ExamTeacherService.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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. item.Name = StringUtil.ClearWhite(item.Name);
  199. item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
  200. }
  201. var items = input.Items.Select(t => t.Adapt<ExamTeacher>()).ToList();
  202. await _rep.InsertAsync(items);
  203. }
  204. #endregion
  205. #region 创建编辑
  206. /// <summary>
  207. /// 添加监测教师
  208. /// </summary>
  209. /// <param name="input"></param>
  210. /// <returns></returns>
  211. public async Task Add(AddExamTeacherInput input)
  212. {
  213. var orgId = input.SysOrgId ?? CurrentSysUserInfo.SysOrgId;
  214. // 检测同一监测计划中同机构内是否有相同证件号码的教师
  215. 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();
  216. if (sameItems.Any())
  217. {
  218. throw Oops.Oh(ErrorCode.E2003, "", "证件号码");
  219. }
  220. var item = input.Adapt<ExamTeacher>();
  221. if (!input.SysOrgId.HasValue)
  222. {
  223. item.SysOrgId = orgId;
  224. }
  225. item.Name = StringUtil.ClearWhite(item.Name);
  226. item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
  227. await item.InsertAsync();
  228. }
  229. /// <summary>
  230. /// 更新监测教师
  231. /// </summary>
  232. /// <param name="input"></param>
  233. /// <returns></returns>
  234. public async Task Update(UpdateExamTeacherInput input)
  235. {
  236. var oitem = await _rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
  237. var item = input.Adapt<ExamTeacher>();
  238. item.Name = StringUtil.ClearWhite(item.Name);
  239. item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
  240. await item.UpdateIncludeAsync(new[] {
  241. nameof(item.Name),
  242. nameof(item.CertificateType),
  243. nameof(item.SchoolJobTitle),
  244. nameof(item.IdNumber),
  245. nameof(item.Gender),
  246. nameof(item.BirthDate),
  247. nameof(item.Mobile),
  248. nameof(item.Email),
  249. nameof(item.Remark),
  250. });
  251. }
  252. /// <summary>
  253. /// 删除监测教师
  254. /// </summary>
  255. /// <param name="input"></param>
  256. /// <returns></returns>
  257. public async Task Del(BaseId input)
  258. {
  259. var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
  260. await item.DeleteAsync();
  261. }
  262. /// <summary>
  263. /// 清空监测教师
  264. /// </summary>
  265. /// <param name="input"></param>
  266. /// <returns></returns>
  267. public async Task Clear(ClearExamTeacherInput input)
  268. {
  269. var orgId = CurrentSysUserInfo.SysOrgId;
  270. await _rep.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId).ExecuteDeleteAsync();
  271. }
  272. #endregion
  273. #region 查询统计
  274. /// <summary>
  275. /// 分页查询监测教师列表
  276. /// </summary>
  277. /// <param name="input"></param>
  278. /// <returns></returns>
  279. public async Task<PageResult<ExamTeacherOutput>> QueryPageList(ExamTeacherPageInput input)
  280. {
  281. var query = GetQueryBase(input);
  282. query = query.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == CurrentSysUserInfo.SysOrgId);
  283. var ret = await query.OrderBy(t => t.Id).ProjectToType<ExamTeacherOutput>().ToADPagedListAsync(input.PageIndex, input.PageSize);
  284. return ret;
  285. }
  286. #endregion
  287. #region 私有方法
  288. /// <summary>
  289. /// 构建查询
  290. /// </summary>
  291. /// <param name="input"></param>
  292. /// <returns></returns>
  293. private IQueryable<ExamTeacher> GetQueryBase(ExamTeacherPageInput input)
  294. {
  295. var name = !string.IsNullOrEmpty(input.Name?.Trim());
  296. var idNumber = !string.IsNullOrEmpty(input.IdNumber?.Trim());
  297. var mobile = !string.IsNullOrEmpty(input.Mobile?.Trim());
  298. var email = !string.IsNullOrEmpty(input.Email?.Trim());
  299. var query = _rep.DetachedEntities.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
  300. .Where((idNumber, u => EF.Functions.Like(u.IdNumber, $"%{input.IdNumber.Trim()}%")))
  301. .Where((mobile, u => EF.Functions.Like(u.Mobile, $"%{input.Mobile.Trim()}%")))
  302. .Where((email, u => EF.Functions.Like(u.Email, $"%{input.Email.Trim()}%")))
  303. .Where(input.CertificateType.HasValue, t => t.CertificateType == input.CertificateType)
  304. .Where(input.SchoolJobTitle.HasValue, t => t.SchoolJobTitle == input.SchoolJobTitle)
  305. .Where(input.Gender.HasValue, t => t.Gender == input.Gender);
  306. return query;
  307. }
  308. #endregion
  309. }