ExamTeacherService.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. using Furion.DatabaseAccessor.Extensions;
  2. using NPOI.SS.UserModel;
  3. using YBEE.EQM.Core;
  4. namespace YBEE.EQM.Application;
  5. /// <summary>
  6. /// 监测教师管理服务
  7. /// </summary>
  8. public class ExamTeacherService : IExamTeacherService, ITransient
  9. {
  10. private readonly IRepository<ExamTeacher> _rep;
  11. private readonly ISysDictDataService _sysDictDataService;
  12. public ExamTeacherService(IRepository<ExamTeacher> rep, ISysDictDataService sysDictDataService)
  13. {
  14. _rep = rep;
  15. _sysDictDataService = sysDictDataService;
  16. }
  17. #region 批量导入
  18. /// <summary>
  19. /// 上传监测教师批量导入文件
  20. /// </summary>
  21. /// <param name="filePath"></param>
  22. /// <param name="examPlanId"></param>
  23. /// <returns></returns>
  24. public async Task<UploadExamDataOutput<UploadExamTeacherOutput>> Upload(string filePath, int examPlanId)
  25. {
  26. UploadExamDataOutput<UploadExamTeacherOutput> result = new();
  27. try
  28. {
  29. using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
  30. IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
  31. var sheet = workbook.GetSheetAt(0);
  32. var rows = sheet.GetRowEnumerator();
  33. #region 验证表结构
  34. // 少于2行验证
  35. if (sheet.LastRowNum < 2)
  36. {
  37. result.ErrorMessage.Add("第一行应为填写说明,第二行应为标题行,请勿修改模板结构。");
  38. return result;
  39. }
  40. // 跳过第一行
  41. rows.MoveNext();
  42. // 读取表头
  43. rows.MoveNext();
  44. IRow headerRow = (IRow)rows.Current;
  45. int index = 0;
  46. int NAME_INDEX = index++;
  47. int CERT_TYPE_INDEX = index++;
  48. int ID_NUM_INDEX = index++;
  49. int GENDER_INDEX = index++;
  50. int JOB_INDEX = index++;
  51. int MOBILE_INDEX = index++;
  52. int EMAIL_INDEX = index++;
  53. int REMARK_INDEX = index;
  54. Dictionary<int, string> headers = new()
  55. {
  56. { NAME_INDEX, "姓名" },
  57. { CERT_TYPE_INDEX, "证件类型" },
  58. { ID_NUM_INDEX, "证件号码" },
  59. { GENDER_INDEX, "性别" },
  60. { JOB_INDEX, "校内职务" },
  61. { MOBILE_INDEX, "手机号码" },
  62. { EMAIL_INDEX, "电子邮箱" },
  63. { REMARK_INDEX, "备注" },
  64. };
  65. List<string> headerErrors = new();
  66. for (int i = 0; i <= index; i++)
  67. {
  68. if (headerRow.GetCell(i)?.ToString() != headers[i])
  69. {
  70. char letter = (char)('A' + i);
  71. headerErrors.Add(letter.ToString());
  72. }
  73. }
  74. if (headerErrors.Any())
  75. {
  76. string columnErrors = string.Join("、", headerErrors);
  77. result.ErrorMessage.Add($"第2行标题行{columnErrors}列名错误。从A列开始依次应为姓名、证件类型、证件号码、性别、校内职务、手机号码、电子邮箱和备注。");
  78. return result;
  79. }
  80. result.StructureCorrect = true;
  81. #endregion
  82. #region 处理数据
  83. // 获取证件类型
  84. var cts = await _sysDictDataService.GetListByDictTypeId(304);
  85. var certificateTypes = cts.ToDictionary(x => x.Name, y => y.Value);
  86. // 获取选科组全
  87. var sjs = await _sysDictDataService.GetListByDictTypeId(306);
  88. var jobs = sjs.ToDictionary(x => x.Name, y => y.Value);
  89. // 读取数据
  90. List<UploadExamTeacherOutput> data = new();
  91. int rn = 0;
  92. while (rows.MoveNext())
  93. {
  94. IRow row = (IRow)rows.Current;
  95. string rv = row.GetCell(0)?.ToString().Trim() ?? "";
  96. rv += row.GetCell(1)?.ToString().Trim() ?? "";
  97. if (rv == "")
  98. {
  99. break;
  100. }
  101. UploadExamTeacherOutput item = new() { RowNumber = ++rn };
  102. // 姓名
  103. item.Name = StringUtil.ClearWhite(row.GetCell(NAME_INDEX)?.ToString() ?? "");
  104. if (item.Name == "")
  105. {
  106. item.ErrorMessage.Add(headers[NAME_INDEX]);
  107. }
  108. // 证件类型
  109. item.CertificateTypeName = StringUtil.ClearWhite(row.GetCell(CERT_TYPE_INDEX)?.ToString() ?? "");
  110. if (item.CertificateTypeName == "" || !certificateTypes.ContainsKey(item.CertificateTypeName))
  111. {
  112. item.ErrorMessage.Add(headers[CERT_TYPE_INDEX]);
  113. }
  114. else
  115. {
  116. item.CertificateType = (CertificateType)certificateTypes[item.CertificateTypeName];
  117. }
  118. // 证件号码
  119. item.IdNumber = StringUtil.ClearWhite(row.GetCell(ID_NUM_INDEX)?.ToString() ?? "").ToUpper();
  120. if (item.CertificateType == CertificateType.ID_CARD)
  121. {
  122. var idNumberValidate = CertificateNumberValidator.ValidateIdCard(item.IdNumber);
  123. if (!idNumberValidate.Success)
  124. {
  125. item.ErrorMessage.Add($"{item.CertificateTypeName}{idNumberValidate.ErrorMessage}");
  126. }
  127. else
  128. {
  129. item.GenderName = idNumberValidate.Gender == Gender.MALE ? "男" : "女";
  130. item.Gender = idNumberValidate.Gender;
  131. }
  132. }
  133. else
  134. {
  135. // 性别
  136. item.GenderName = StringUtil.ClearWhite(row.GetCell(GENDER_INDEX)?.ToString() ?? "");
  137. if (item.CertificateType != CertificateType.ID_CARD)
  138. {
  139. item.Gender = item.GenderName == "男" ? Gender.MALE : item.GenderName == "女" ? Gender.FEMALE : Gender.UNKNOWN;
  140. }
  141. }
  142. // 校内职务
  143. item.SchoolJobTitleName = StringUtil.ClearWhite(row.GetCell(JOB_INDEX)?.ToString() ?? "");
  144. if (item.SchoolJobTitleName == "")
  145. {
  146. item.SchoolJobTitle = SchoolJobTitle.TEACHER;
  147. }
  148. else if (jobs.ContainsKey(item.SchoolJobTitleName))
  149. {
  150. item.SchoolJobTitle = (SchoolJobTitle)jobs[item.SchoolJobTitleName];
  151. }
  152. else
  153. {
  154. item.ErrorMessage.Add(headers[JOB_INDEX]);
  155. }
  156. // 手机号码
  157. item.Mobile = StringUtil.ClearWhite(row.GetCell(MOBILE_INDEX)?.ToString() ?? "");
  158. // 电子邮箱
  159. item.Email = StringUtil.ClearWhite(row.GetCell(EMAIL_INDEX)?.ToString() ?? "");
  160. // 备注
  161. item.Remark = StringUtil.ClearWhite(row.GetCell(REMARK_INDEX)?.ToString() ?? "");
  162. // 行是否验证通过
  163. item.IsSuccess = item.ErrorMessage.Count == 0;
  164. data.Add(item);
  165. result.TotalRowCount++;
  166. if (!item.IsSuccess)
  167. {
  168. result.ErrorRowCount++;
  169. }
  170. }
  171. result.Rows = data;
  172. #endregion
  173. workbook.Close();
  174. fs.Close();
  175. }
  176. catch (Exception ex)
  177. {
  178. throw new Exception(ex.Message);
  179. }
  180. finally
  181. {
  182. File.Delete(filePath);
  183. }
  184. return result;
  185. }
  186. /// <summary>
  187. /// 批量导入监测教师
  188. /// </summary>
  189. /// <param name="input"></param>
  190. /// <returns></returns>
  191. public async Task Import(ImportExamTeacherInput input)
  192. {
  193. // 删除同一监测计划中监测机构内所有数据
  194. await _rep.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == input.SysOrgId).ExecuteDeleteAsync();
  195. foreach (var item in input.Items)
  196. {
  197. item.SysOrgId ??= CurrentSysUserInfo.SysOrgId;
  198. }
  199. var items = input.Items.Select(t => t.Adapt<ExamTeacher>()).ToList();
  200. await _rep.InsertAsync(items);
  201. }
  202. #endregion
  203. #region 创建编辑
  204. /// <summary>
  205. /// 添加监测教师
  206. /// </summary>
  207. /// <param name="input"></param>
  208. /// <returns></returns>
  209. public async Task Add(AddExamTeacherInput input)
  210. {
  211. var orgId = input.SysOrgId ?? CurrentSysUserInfo.SysOrgId;
  212. // 检测同一监测计划中同机构内是否有相同证件号码的教师
  213. var sameItems = await _rep.DetachedEntities.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId && t.CertificateType == input.CertificateType && t.IdNumber == input.IdNumber).ProjectToType<ExamTeacherOutput>().ToListAsync();
  214. if (sameItems.Any())
  215. {
  216. throw Oops.Oh(ErrorCode.E2003, "", "证件号码");
  217. }
  218. var item = input.Adapt<ExamTeacher>();
  219. if (!input.SysOrgId.HasValue)
  220. {
  221. item.SysOrgId = orgId;
  222. }
  223. await item.InsertAsync();
  224. }
  225. /// <summary>
  226. /// 更新监测教师
  227. /// </summary>
  228. /// <param name="input"></param>
  229. /// <returns></returns>
  230. public async Task Update(UpdateExamTeacherInput input)
  231. {
  232. var oitem = await _rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
  233. var item = input.Adapt<ExamTeacher>();
  234. await item.UpdateIncludeAsync(new[] {
  235. nameof(item.Name),
  236. nameof(item.CertificateType),
  237. nameof(item.SchoolJobTitle),
  238. nameof(item.IdNumber),
  239. nameof(item.Gender),
  240. nameof(item.BirthDate),
  241. nameof(item.Mobile),
  242. nameof(item.Email),
  243. nameof(item.Remark),
  244. });
  245. }
  246. /// <summary>
  247. /// 删除监测教师
  248. /// </summary>
  249. /// <param name="input"></param>
  250. /// <returns></returns>
  251. public async Task Del(BaseId input)
  252. {
  253. var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
  254. await item.DeleteAsync();
  255. }
  256. /// <summary>
  257. /// 清空监测教师
  258. /// </summary>
  259. /// <param name="input"></param>
  260. /// <returns></returns>
  261. public async Task Clear(ClearExamTeacherInput input)
  262. {
  263. var orgId = CurrentSysUserInfo.SysOrgId;
  264. await _rep.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId).ExecuteDeleteAsync();
  265. }
  266. #endregion
  267. #region 查询统计
  268. /// <summary>
  269. /// 分页查询监测教师列表
  270. /// </summary>
  271. /// <param name="input"></param>
  272. /// <returns></returns>
  273. public async Task<PageResult<ExamTeacherOutput>> QueryPageList(ExamTeacherPageInput input)
  274. {
  275. var query = GetQueryBase(input);
  276. query = query.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == CurrentSysUserInfo.SysOrgId);
  277. var ret = await query.OrderBy(t => t.Id).ProjectToType<ExamTeacherOutput>().ToADPagedListAsync(input.PageIndex, input.PageSize);
  278. return ret;
  279. }
  280. #endregion
  281. #region 私有方法
  282. /// <summary>
  283. /// 构建查询
  284. /// </summary>
  285. /// <param name="input"></param>
  286. /// <returns></returns>
  287. private IQueryable<ExamTeacher> GetQueryBase(ExamTeacherPageInput input)
  288. {
  289. var name = !string.IsNullOrEmpty(input.Name?.Trim());
  290. var idNumber = !string.IsNullOrEmpty(input.IdNumber?.Trim());
  291. var mobile = !string.IsNullOrEmpty(input.Mobile?.Trim());
  292. var email = !string.IsNullOrEmpty(input.Email?.Trim());
  293. var query = _rep.DetachedEntities.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
  294. .Where((idNumber, u => EF.Functions.Like(u.IdNumber, $"%{input.IdNumber.Trim()}%")))
  295. .Where((mobile, u => EF.Functions.Like(u.Mobile, $"%{input.Mobile.Trim()}%")))
  296. .Where((email, u => EF.Functions.Like(u.Email, $"%{input.Email.Trim()}%")))
  297. .Where(input.CertificateType.HasValue, t => t.CertificateType == input.CertificateType)
  298. .Where(input.SchoolJobTitle.HasValue, t => t.SchoolJobTitle == input.SchoolJobTitle)
  299. .Where(input.Gender.HasValue, t => t.Gender == input.Gender);
  300. return query;
  301. }
  302. #endregion
  303. }