ExtractRuleService.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. using DocumentFormat.OpenXml.Office2010.Excel;
  2. using Exam.Application.Interface.Exam;
  3. using Exam.Infrastructure.Data.Entity;
  4. using Exam.Infrastructure.Enums;
  5. using Exam.Infrastructure.Extensions;
  6. using Exam.Share.ViewResponses.Exam;
  7. using Hotline.Application.Exam.Core.Extensions;
  8. using Hotline.Application.Exam.Core.Utilities;
  9. using Hotline.Application.Exam.Extensions;
  10. using Hotline.Application.Exam.Interface.ExamManages;
  11. using Hotline.Application.Exam.QueryExtensions.ExamManages;
  12. using Hotline.Exams.ExamManages;
  13. using Hotline.Exams.Questions;
  14. using Hotline.Repository.SqlSugar;
  15. using Hotline.Repository.SqlSugar.DataPermissions;
  16. using Hotline.Repository.SqlSugar.Exam.Core.Constants;
  17. using Hotline.Repository.SqlSugar.Exam.Extensions;
  18. using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
  19. using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
  20. using Hotline.Repository.SqlSugar.Exam.Repositories;
  21. using Hotline.Repository.SqlSugar.Exam.Service;
  22. using Hotline.Share.Dtos.TestPapers;
  23. using Hotline.Share.Requests.Exam;
  24. using Hotline.Share.Tools;
  25. using Hotline.Share.ViewResponses.Exam;
  26. using JiebaNet.Segmenter.Common;
  27. using MapsterMapper;
  28. using SqlSugar;
  29. using XF.Domain.Authentications;
  30. using XF.Domain.Dependency;
  31. using ExamQuestion = Hotline.Exams.Questions.ExamQuestion;
  32. namespace Hotline.Application.Exam.Service.ExamManages
  33. {
  34. public class ExtractRuleService : ApiService<ExamExtractRule, AddExtractRuleDto, UpdateExtractRuleDto, HotlineDbContext>, IExtractRuleService, IScopeDependency
  35. {
  36. private readonly IExtractRuleRepository _repository;
  37. private readonly ITagQuestionRepository _tagQuestionRepository;
  38. private readonly IRuleTagRepository _ruleTagRepository;
  39. private readonly ISessionContext _sessionContext;
  40. private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
  41. private readonly IServiceProvider _serviceProvider;
  42. private readonly IMapper _mapper;
  43. private AddExtractRuleDto _addExtractRuleDto;
  44. public ExtractRuleService(IExtractRuleRepository repository,
  45. ITagQuestionRepository tagQuestionRepository,
  46. IRuleTagRepository ruleTagRepository,
  47. ISessionContext sessionContext,
  48. IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
  49. IMapper mapper) : base(repository, mapper, sessionContext)
  50. {
  51. _repository = repository;
  52. _tagQuestionRepository = tagQuestionRepository;
  53. _ruleTagRepository = ruleTagRepository;
  54. this._sessionContext = sessionContext;
  55. _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
  56. _serviceProvider = serviceProvider;
  57. _mapper = mapper;
  58. }
  59. #region public method
  60. /// <summary>
  61. ///
  62. /// </summary>
  63. /// <param name="entityQueryRequest"></param>
  64. /// <returns></returns>
  65. public async Task<ExtractRuleDto> GetAsync(EntityQueryRequest entityQueryRequest)
  66. {
  67. var entity = await _repository.GetAsync(entityQueryRequest.Id);
  68. var extractRuleDto = _mapper.Map<ExtractRuleDto>(entity);
  69. if (extractRuleDto != null)
  70. {
  71. extractRuleDto.RuleTagDtos = await GetRuleTagDtos(entityQueryRequest);
  72. extractRuleDto.TagQuestionDtos = await GetTagQuestions(entityQueryRequest);
  73. }
  74. return extractRuleDto;
  75. }
  76. public async Task<(int, List<ExtractRuleViewResponse>)> GetListAsync(ExtractRulePagedRequest queryRequest)
  77. {
  78. queryRequest.Status = queryRequest.Status ?? Share.Enums.Exams.EPublicStatus.Valid;
  79. ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);
  80. var result = await queryable.ToListAsync();
  81. var total = await queryable.CountAsync();
  82. return (total, result);
  83. }
  84. public async Task<PageViewResponse<ExtractRuleViewResponse>> GetPagedListAsync(ExtractRulePagedRequest queryRequest)
  85. {
  86. ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);
  87. var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
  88. var total = await queryable.CountAsync();
  89. var result = new ExtractRulePageViewResponse
  90. {
  91. Items = list,
  92. Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
  93. };
  94. return result;
  95. }
  96. /// <summary>
  97. /// 新增抽题规则
  98. /// </summary>
  99. /// <param name="actionRequest"></param>
  100. /// <param name="cancellationToken"></param>
  101. /// <returns></returns>
  102. public override async Task<string> AddAsync(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
  103. {
  104. base.StartTran();
  105. var id = await base.AddAsync(actionRequest, cancellationToken);
  106. ResolveExtractRuleId(actionRequest, id);
  107. base.Entity.RuleTags = await AddRuleTags(actionRequest, cancellationToken);
  108. base.Entity.TagQuestions = await AddTagQuestions(actionRequest, cancellationToken);
  109. await base.Complete(base.Entity, OperationConstant.Create);
  110. return id;
  111. }
  112. /// <summary>
  113. /// 修改抽题规则
  114. /// </summary>
  115. /// <param name="actionRequest"></param>
  116. /// <param name="cancellationToken"></param>
  117. /// <returns></returns>
  118. public override async Task UpdateAsync(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
  119. {
  120. base.StartTran();
  121. await base.UpdateAsync(actionRequest, cancellationToken);
  122. ResolveExtractRuleId(actionRequest, actionRequest.Id);
  123. ResolveAddExtractRuleDto(actionRequest);
  124. base.Entity.RuleTags = await ModifyRuleTags(actionRequest, cancellationToken);
  125. base.Entity.TagQuestions = await ModifyTagQuestions(actionRequest, cancellationToken);
  126. await base.Complete(base.Entity, OperationConstant.Update);
  127. }
  128. private void ResolveAddExtractRuleDto(UpdateExtractRuleDto actionRequest)
  129. {
  130. _addExtractRuleDto = _mapper.Map<AddExtractRuleDto>(actionRequest);
  131. //_addExtractRuleDto.RuleTagDtos = new List<AddRuleTagDto>();
  132. //actionRequest.RuleTagDtos.ToList().ForEach(item =>
  133. //{
  134. // _addExtractRuleDto.RuleTagDtos.Add(_mapper.Map<AddRuleTagDto>(item));
  135. //});
  136. //_addExtractRuleDto.TagQuestionDtos = new List<AddTagQuestionDto>();
  137. //actionRequest.TagQuestionDtos.ToList().ForEach(item =>
  138. //{
  139. // _addExtractRuleDto.TagQuestionDtos.Add(_mapper.Map<AddTagQuestionDto>(item));
  140. //});
  141. }
  142. /// <summary>
  143. /// 删除抽题规则
  144. /// </summary>
  145. /// <param name="entityQueryRequest"></param>
  146. /// <param name="cancellationToken"></param>
  147. /// <returns></returns>
  148. public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
  149. {
  150. await base.DeleteAsync(entityQueryRequest, cancellationToken);
  151. var tempEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamTagQuestion>()
  152. .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
  153. .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<ExamTagQuestion>();
  154. await DeleteTagQuestions(tempEntityQueryRequest, cancellationToken);
  155. tempEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamRuleTag>()
  156. .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
  157. .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<ExamRuleTag>();
  158. await DeleteRuleTags(tempEntityQueryRequest, cancellationToken);
  159. }
  160. /// <summary>
  161. /// 获取标题试题数
  162. /// </summary>
  163. /// <param name="tagQuestionRequest"></param>
  164. /// <returns></returns>
  165. public async Task<List<TagQuestionViewResponse>> GetTagQuestionCount(TagQuestionRequest tagQuestionRequest)
  166. {
  167. if (tagQuestionRequest.TagIds.IsNullOrEmpty()) return new List<TagQuestionViewResponse>();
  168. var expression = tagQuestionRequest.GetExpression();
  169. var questionTagTable = new ExamRepository<ExamQuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
  170. var questionTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
  171. var queryable = questionTagTable.LeftJoin(questionTable, (t, q) => t.QuestionId == q.Id)
  172. .GroupBy((t, q) => new { t.TagId, q.QuestionType })
  173. .OrderBy((t, q) => t.TagId)
  174. .Select((t, q) => new TagQuestionViewResponse
  175. {
  176. TagId = t.TagId,
  177. QuestionType = q.QuestionType,
  178. TotalCount = SqlFunc.AggregateCount(q.Id!=null)
  179. });
  180. return await queryable.ToListAsync();
  181. }
  182. #endregion
  183. #region private method
  184. /// <summary>
  185. /// 获取查询结果
  186. /// </summary>
  187. /// <param name="queryRequest"></param>
  188. /// <returns></returns>
  189. private ISugarQueryable<ExtractRuleViewResponse> QueryResult(ExtractRulePagedRequest queryRequest)
  190. {
  191. var expression = queryRequest.GetExpression();
  192. var query = _repository.Queryable().Where(expression);
  193. var querable = query.OrderBy(o => o.SortIndex).Select(m => new ExtractRuleViewResponse
  194. {
  195. Name = m.Name,
  196. Id = m.Id,
  197. Status = m.Status,
  198. Code = m.Code,
  199. SortIndex = m.SortIndex,
  200. Remark = m.Remark,
  201. ExamType = m.RuleType
  202. });
  203. return querable;
  204. }
  205. /// <summary>
  206. /// 新增标签试题
  207. /// </summary>
  208. /// <param name="actionRequest"></param>
  209. /// <param name="cancellationToken"></param>
  210. /// <returns></returns>
  211. private async Task<List<ExamTagQuestion>> AddTagQuestions(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
  212. {
  213. if (actionRequest.TagQuestionDtos == null) return null;
  214. var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Add && x.Count!=null).ToList();
  215. var tagQuestions = _mapper.Map<List<ExamTagQuestion>>(tagQuestionDtos);
  216. tagQuestions.ToInsert(_sessionContext);
  217. await _tagQuestionRepository.ValidateAddAsync(tagQuestions, cancellationToken);
  218. return tagQuestions;
  219. }
  220. /// <summary>
  221. /// 新增规则标签
  222. /// </summary>
  223. /// <param name="actionRequest"></param>
  224. /// <param name="cancellationToken"></param>
  225. /// <returns></returns>
  226. private async Task<List<ExamRuleTag>> AddRuleTags(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
  227. {
  228. if (actionRequest.RuleTagDtos == null) return null;
  229. actionRequest.RuleTagDtos.ResolveOperationStatus();
  230. var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
  231. var ruleTags = _mapper.Map<List<ExamRuleTag>>(ruleTagDtos);
  232. ruleTags.ToInsert(_sessionContext);
  233. await _ruleTagRepository.ValidateAddAsync(ruleTags, cancellationToken);
  234. return ruleTags;
  235. }
  236. /// <summary>
  237. /// 修改标签试题数
  238. /// </summary>
  239. /// <param name="actionRequest"></param>
  240. /// <param name="cancellationToken"></param>
  241. /// <returns></returns>
  242. /// <exception cref="NotImplementedException"></exception>
  243. private async Task<List<ExamTagQuestion>> ModifyTagQuestions(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
  244. {
  245. if (actionRequest.TagQuestionDtos == null) return null;
  246. //var all = await _tagQuestionRepository.Queryable().Where(m => m.RuleId == actionRequest.Id).ToListAsync();
  247. //actionRequest.TagQuestionDtos.ResolveOperationStatus(all);
  248. var entityQueyRequest = ExpressionableUtility.CreateExpression<ExamTagQuestion>()
  249. .AndIF(actionRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == actionRequest.Id).ToEntityQueryRequest<ExamTagQuestion>();
  250. await DeleteTagQuestions(entityQueyRequest, cancellationToken);
  251. var tagQuestions = new List<ExamTagQuestion>();
  252. tagQuestions.AddRangeExt(await AddTagQuestions(_addExtractRuleDto, cancellationToken));
  253. //tagQuestions.AddRangeExt(await UpdateTagQuestions(actionRequest, all, cancellationToken));
  254. //var ruleTagDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
  255. //var ids = ruleTagDtos.Select(x => x.Id).ToList();
  256. //var entityQueyRequest = ExpressionableUtility.CreateExpression<TagQuestion>()
  257. // .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<TagQuestion>();
  258. await DeleteTagQuestions(entityQueyRequest, cancellationToken);
  259. return tagQuestions;
  260. }
  261. /// <summary>
  262. /// 修改标签试题数
  263. /// </summary>
  264. /// <param name="actionRequest"></param>
  265. /// <param name="cancellationToken"></param>
  266. /// <returns></returns>
  267. /// <exception cref="NotImplementedException"></exception>
  268. private async Task<List<ExamTagQuestion>> UpdateTagQuestions(UpdateExtractRuleDto actionRequest, List<ExamTagQuestion> all, CancellationToken cancellationToken)
  269. {
  270. if (actionRequest.TagQuestionDtos == null) return null;
  271. var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
  272. var ids = tagQuestionDtos.Select(x => x.Id).ToList();
  273. var tagQuestions = all.Where(x => ids.Contains(x.Id)).ToList();
  274. var entitys = new List<ExamTagQuestion>();
  275. tagQuestionDtos.ForEach(x =>
  276. {
  277. var entity = tagQuestions.FirstOrDefault(x => x.Id == x.Id);
  278. entity = _mapper.Map(x, entity);
  279. if (entity != null)
  280. {
  281. entity.RuleId = actionRequest.Id;
  282. entitys.Add(entity);
  283. }
  284. });
  285. entitys.ToUpdate(_sessionContext);
  286. await _tagQuestionRepository.ValidateUpdateAsync(entitys, cancellationToken);
  287. return entitys;
  288. }
  289. /// <summary>
  290. /// 修改规则标签
  291. /// </summary>
  292. /// <param name="actionRequest"></param>
  293. /// <param name="cancellationToken"></param>
  294. /// <returns></returns>
  295. /// <exception cref="NotImplementedException"></exception>
  296. private async Task<List<ExamRuleTag>> ModifyRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
  297. {
  298. var ruleId = actionRequest.Id;
  299. if (actionRequest.RuleTagDtos == null) return null;
  300. //var all = await _ruleTagRepository.Queryable().Where(x => x.RuleId == actionRequest.Id).ToListAsync();
  301. //actionRequest.RuleTagDtos.ResolveOperationStatus(all);
  302. var ruleTags = new List<ExamRuleTag>();
  303. var entityQueyRequest = ExpressionableUtility.CreateExpression<ExamRuleTag>()
  304. .AndIF(ruleId.IsNotNullOrEmpty(), x => x.RuleId == ruleId).ToEntityQueryRequest<ExamRuleTag>();
  305. await DeleteRuleTags(entityQueyRequest, cancellationToken);
  306. ruleTags.AddRangeExt(await AddRuleTags(_addExtractRuleDto, cancellationToken));
  307. //ruleTags.AddRangeExt(await UpdateRuleTags(actionRequest, cancellationToken));
  308. //var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
  309. //var ids = ruleTagDtos.Select(x => x.Id).ToList();
  310. //var entityQueyRequest = ExpressionableUtility.CreateExpression<RuleTag>()
  311. // .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<RuleTag>();
  312. return ruleTags;
  313. }
  314. private async Task<List<ExamRuleTag>> UpdateRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
  315. {
  316. if (actionRequest.RuleTagDtos == null) return null;
  317. var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
  318. var ids = ruleTagDtos.Select(x => x.Id).ToList();
  319. var ruleTags = await _ruleTagRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
  320. var entitys = new List<ExamRuleTag>();
  321. ruleTagDtos.ForEach(x =>
  322. {
  323. var entity = ruleTags.FirstOrDefault(x => x.Id == x.Id);
  324. entity = _mapper.Map(x, entity);
  325. if (entity != null)
  326. {
  327. entity.RuleId = actionRequest.Id;
  328. entitys.Add(entity);
  329. }
  330. });
  331. entitys.ToUpdate(_sessionContext);
  332. await _ruleTagRepository.ValidateUpdateAsync(entitys, cancellationToken);
  333. return entitys;
  334. }
  335. /// <summary>
  336. /// 删除规则标签
  337. /// </summary>
  338. /// <param name="tempEntityQueryRequest"></param>
  339. /// <param name="cancellationToken"></param>
  340. /// <returns></returns>
  341. private async Task DeleteRuleTags(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
  342. {
  343. await _ruleTagRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
  344. }
  345. /// <summary>
  346. /// 删除标签试题数
  347. /// </summary>
  348. /// <param name="tempEntityQueryRequest"></param>
  349. /// <param name="cancellationToken"></param>
  350. /// <returns></returns>
  351. private async Task DeleteTagQuestions(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
  352. {
  353. await _tagQuestionRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
  354. }
  355. /// <summary>
  356. /// 获取规则标签
  357. /// </summary>
  358. /// <param name="entityQueryRequest"></param>
  359. /// <returns></returns>
  360. /// <exception cref="NotImplementedException"></exception>
  361. private async Task<List<RuleTagDto>> GetRuleTagDtos(EntityQueryRequest entityQueryRequest)
  362. {
  363. var ruleTagTable = _ruleTagRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);
  364. var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
  365. var queryable = ruleTagTable.LeftJoin(examTagTable, (r, e) => r.TagId == e.Id).Select((r, e) => new RuleTagDto
  366. {
  367. Id = r.Id,
  368. TagId = r.TagId,
  369. Tag = e.Name
  370. });
  371. return await queryable.ToListAsync();
  372. }
  373. /// <summary>
  374. /// 获取标签试题数
  375. /// </summary>
  376. /// <param name="entityQueryRequest"></param>
  377. /// <returns></returns>
  378. /// <exception cref="NotImplementedException"></exception>
  379. private async Task<List<TagQuestionDto>> GetTagQuestions(EntityQueryRequest entityQueryRequest)
  380. {
  381. var tagQuestionTable = _tagQuestionRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);
  382. var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
  383. var tagQuestionDtos = tagQuestionTable.LeftJoin(examTagTable, (t, e) => t.TagId == e.Id).Select((t, e) => new TagQuestionDto
  384. {
  385. Id = t.Id,
  386. TagId = t.TagId,
  387. QuestionType = t.QuestionType,
  388. Count = t.Count,
  389. Tag = e.Name
  390. });
  391. return await tagQuestionDtos.ToListAsync();
  392. }
  393. /// <summary>
  394. /// 处理抽题规则Id
  395. /// </summary>
  396. /// <param name="actionRequest"></param>
  397. /// <param name="id"></param>
  398. /// <exception cref="NotImplementedException"></exception>
  399. private void ResolveExtractRuleId(AddExtractRuleDto actionRequest, string id)
  400. {
  401. actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);
  402. actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
  403. }
  404. /// <summary>
  405. /// 处理抽题规则Id
  406. /// </summary>
  407. /// <param name="actionRequest"></param>
  408. /// <param name="id"></param>
  409. /// <exception cref="NotImplementedException"></exception>
  410. private void ResolveExtractRuleId(UpdateExtractRuleDto actionRequest, string id)
  411. {
  412. actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);
  413. actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
  414. }
  415. #endregion
  416. }
  417. }