ExamSampleReplaceService.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using Furion.DatabaseAccessor.Extensions;
  2. using NPOI.SS.UserModel;
  3. using NPOI.SS.Util;
  4. using NPOI.XSSF.UserModel;
  5. using YBEE.EQM.Core;
  6. namespace YBEE.EQM.Application;
  7. /// <summary>
  8. /// 缺测替补抽样服务
  9. /// </summary>
  10. public class ExamSampleReplaceService(IRepository<ExamSampleReplace> replaceRep, IRepository<ExamSampleStudent> stuRep, IExportExcelService exportExcelService) : IExamSampleReplaceService, ITransient
  11. {
  12. /// <summary>
  13. /// 抽取
  14. /// </summary>
  15. /// <param name="input"></param>
  16. /// <returns></returns>
  17. public async Task Sample(SampleExamSampleReplaceInput input)
  18. {
  19. var orgId = CurrentSysUserInfo.SysOrgId;
  20. // 缺测学生
  21. var absentStu = await stuRep.DetachedEntities
  22. .Include(t => t.ExamStudent).Include(t => t.ExamSample)
  23. .FirstOrDefaultAsync(t => t.Id == input.AbsentExamSampleStudentId && t.ExamStudent.SysOrgId == orgId)
  24. ?? throw Oops.Oh(ErrorCode.E2001);
  25. // 替补列表
  26. var rps = await replaceRep.DetachedEntities
  27. .Where(t => t.ExamSampleId == absentStu.ExamSampleId && t.SysOrgId == orgId && t.IsDeleted == false)
  28. .Select(t => new { t.AbsentExamSampleStudentId, t.ReplaceExamSampleStudentId, t.IsReplaceAbsent })
  29. .ToListAsync();
  30. // 如果已添加缺测,并且替补未被标记为缺测的情况则报错
  31. if (rps.Any(t => t.AbsentExamSampleStudentId == absentStu.Id && t.IsReplaceAbsent == false))
  32. {
  33. throw Oops.Oh(ErrorCode.E3010);
  34. }
  35. // 所有学生
  36. var stus = await stuRep.DetachedEntities.Where(t => t.ExamSampleId == absentStu.ExamSampleId &&
  37. t.ExamStudent.SysOrgId == orgId &&
  38. t.ExamStudent.GradeId == absentStu.ExamStudent.GradeId &&
  39. t.ExamStudent.SchoolClassId == absentStu.ExamStudent.SchoolClassId &&
  40. t.IsSpecialStudent == false)
  41. .OrderByDescending(t => t.PreTotalScore).ThenBy(t => t.ExamStudentId)
  42. .ToListAsync();
  43. if (stus.Count == 0)
  44. {
  45. throw Oops.Oh(ErrorCode.E3009);
  46. }
  47. // 去掉已抽为替补的
  48. var ers = rps.Select(t => t.ReplaceExamSampleStudentId).ToList();
  49. stus = stus.Where(t => !ers.Contains(t.Id)).ToList();
  50. ExamSampleStudent rstu = null;
  51. // 有前置成绩按顺序抽取,无随机抽取
  52. if (stus.Any(t => t.PreTotalScore > 0))
  53. {
  54. // 向上找紧挨的1位
  55. rstu = stus.Where(t => t.PreTotalScore >= absentStu.PreTotalScore && t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).LastOrDefault();
  56. // 向上未找到则向下找紧挨的1位
  57. rstu ??= stus.Where(t => t.PreTotalScore <= absentStu.PreTotalScore && t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).FirstOrDefault();
  58. }
  59. else
  60. {
  61. var nstus = stus.Where(t => t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).ToList();
  62. if (nstus.Count > 0)
  63. {
  64. var rand = new Random();
  65. var si = rand.Next(0, nstus.Count);
  66. rstu = nstus[si];
  67. }
  68. }
  69. if (rstu is null)
  70. {
  71. throw Oops.Oh(ErrorCode.E3009);
  72. }
  73. // 同一缺测生抽的其他替补缺测锁定
  74. var rps2 = await replaceRep.Where(t => t.ExamSampleId == absentStu.ExamSampleId &&
  75. t.SysOrgId == CurrentSysUserInfo.SysOrgId &&
  76. t.AbsentExamSampleStudentId == absentStu.Id &&
  77. t.IsDeleted == false &&
  78. t.IsReplaceAbsent == true
  79. ).ToListAsync();
  80. foreach (var rp in rps2)
  81. {
  82. rp.IsReplaceAbsentLocked = true;
  83. await rp.UpdateIncludeAsync([nameof(rp.IsReplaceAbsentLocked)]);
  84. }
  85. ExamSampleReplace item = new()
  86. {
  87. ExamSampleId = absentStu.ExamSampleId,
  88. ExamPlanId = absentStu.ExamSample.ExamPlanId,
  89. SysOrgId = absentStu.ExamStudent.SysOrgId,
  90. SysOrgBranchId = absentStu.ExamStudent.SysOrgBranchId,
  91. SchoolClassId = absentStu.ExamStudent.SchoolClassId,
  92. ExamGradeId = absentStu.ExamStudent.ExamGradeId,
  93. GradeId = absentStu.ExamStudent.GradeId,
  94. ClassNumber = absentStu.ExamStudent.ClassNumber,
  95. AbsentExamSampleStudentId = absentStu.Id,
  96. ReplaceExamSampleStudentId = rstu.Id,
  97. Remark = input.Remark,
  98. IsReplaceAbsent = false,
  99. IsReplaceAbsentLocked = false,
  100. };
  101. await replaceRep.InsertAsync(item);
  102. }
  103. /// <summary>
  104. /// 标记替补为缺测
  105. /// </summary>
  106. /// <param name="id"></param>
  107. /// <returns></returns>
  108. public async Task MarkedReplaceAbsent(int id)
  109. {
  110. var item = await replaceRep.FirstOrDefaultAsync(t => t.Id == id && t.IsReplaceAbsentLocked == false) ?? throw Oops.Oh(ErrorCode.E2001);
  111. item.IsReplaceAbsent = !item.IsReplaceAbsent;
  112. await item.UpdateIncludeAsync([nameof(item.IsReplaceAbsent)]);
  113. // 同一缺测生抽的其他替补缺测解除锁定
  114. var rps2 = await replaceRep.Where(t => t.ExamSampleId == item.ExamSampleId &&
  115. t.SysOrgId == CurrentSysUserInfo.SysOrgId &&
  116. t.AbsentExamSampleStudentId == item.AbsentExamSampleStudentId &&
  117. t.Id != item.Id &&
  118. t.IsDeleted == false &&
  119. t.IsReplaceAbsent == true
  120. ).ToListAsync();
  121. foreach (var rp in rps2)
  122. {
  123. rp.IsReplaceAbsentLocked = !item.IsReplaceAbsent;
  124. await rp.UpdateIncludeAsync([nameof(rp.IsReplaceAbsentLocked)]);
  125. }
  126. }
  127. /// <summary>
  128. /// 软删除
  129. /// </summary>
  130. /// <param name="input"></param>
  131. /// <returns></returns>
  132. public async Task FakeDelete(BaseId input)
  133. {
  134. var item = await replaceRep.FirstOrDefaultAsync(t => t.Id == input.Id && t.IsDeleted == false) ?? throw Oops.Oh(ErrorCode.E2001);
  135. item.IsDeleted = true;
  136. await item.UpdateIncludeNowAsync([nameof(item.IsDeleted)]);
  137. }
  138. /// <summary>
  139. /// 导出缺测替补名单
  140. /// </summary>
  141. /// <param name="examPlanId"></param>
  142. /// <returns></returns>
  143. public async Task<(string fileName, byte[] fileBytes)> ExportToOrg(int examPlanId)
  144. {
  145. var res = await QueryOrgPageList(new()
  146. {
  147. ExamPlanId = examPlanId,
  148. IsReplaceAbsent = false,
  149. PageIndex = 1,
  150. PageSize = 9999
  151. });
  152. var hasBranch = res.Items.Any(t => t.SysOrgBranchId.HasValue && t.SysOrgBranchId > 0);
  153. XSSFWorkbook wb = new();
  154. ISheet sheet = wb.CreateSheet();
  155. sheet.DisplayGridlines = false;
  156. // 获取样式
  157. var cellStyle = exportExcelService.GetCellStyle(wb);
  158. #region 表头
  159. int rowNum = 0;
  160. IRow headerRow1 = sheet.CreateRow(rowNum);
  161. headerRow1.Height = ExportExcelCellStyle.DefaultRowHeight;
  162. int ci = 0;
  163. exportExcelService.AddCell("序", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 6);
  164. if (hasBranch)
  165. {
  166. exportExcelService.AddCell("校区", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
  167. }
  168. exportExcelService.AddCell("年级", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 10);
  169. exportExcelService.AddCell("班级", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 8);
  170. exportExcelService.AddCell("缺测学生", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
  171. //exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 20);
  172. exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 14);
  173. sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, ci - 2, ci - 1));
  174. exportExcelService.AddCell("替补学生", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
  175. //exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 20);
  176. exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 14);
  177. sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, ci - 2, ci - 1));
  178. exportExcelService.AddCell("抽取时间", headerRow1, ci, cellStyle.ColumnHeaderStyle, sheet, 20);
  179. IRow headerRow2 = sheet.CreateRow(++rowNum);
  180. headerRow2.Height = ExportExcelCellStyle.DefaultRowHeight;
  181. int lci = hasBranch ? 4 : 3;
  182. for (ci = 0; ci < lci; ci++)
  183. {
  184. exportExcelService.AddCell(null, headerRow2, ci, cellStyle.ColumnHeaderStyle);
  185. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum, ci, ci));
  186. }
  187. exportExcelService.AddCell("姓名", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  188. //exportExcelService.AddCell("证件号码", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  189. exportExcelService.AddCell("监测号", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  190. exportExcelService.AddCell("姓名", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  191. //exportExcelService.AddCell("证件号码", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  192. exportExcelService.AddCell("监测号", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
  193. exportExcelService.AddCell(null, headerRow2, ci, cellStyle.ColumnHeaderStyle);
  194. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum, ci, ci));
  195. sheet.CreateFreezePane(0, 2);
  196. #endregion
  197. int rn = 0;
  198. foreach (var item in res.Items)
  199. {
  200. IRow row = sheet.CreateRow(++rowNum);
  201. row.Height = ExportExcelCellStyle.DefaultRowHeight;
  202. int rci = 0;
  203. ICellStyle cstyle = cellStyle.CenterCellStyle;
  204. exportExcelService.AddCell(++rn, row, rci++, cstyle);
  205. if (hasBranch)
  206. {
  207. exportExcelService.AddCell(item.SysOrgBranch?.Name ?? "", row, rci++, cstyle);
  208. }
  209. exportExcelService.AddCell(item.ExamGrade.Grade.Name, row, rci++, cstyle);
  210. exportExcelService.AddCell(item.ClassNumber, row, rci++, cstyle);
  211. exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamStudent.Name, row, rci++, cstyle);
  212. //exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamStudent.IdNumber, row, rci++, cstyle);
  213. exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamNumber, row, rci++, cstyle);
  214. exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamStudent.Name, row, rci++, cstyle);
  215. //exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamStudent.IdNumber, row, rci++, cstyle);
  216. exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamNumber, row, rci++, cstyle);
  217. exportExcelService.AddCell(item.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), row, rci++, cstyle);
  218. }
  219. MemoryStream ms = new();
  220. wb.Write(ms, false);
  221. ms.Flush();
  222. return ($"监测替补抽取表{DateTime.Now:yyyyMMddHHmmss}.xlsx", ms.ToArray());
  223. }
  224. /// <summary>
  225. /// 分页查询缺测替补抽样列表
  226. /// </summary>
  227. /// <param name="input"></param>
  228. /// <returns></returns>
  229. public async Task<PageResult<ExamSampleReplaceOutput>> QueryOrgPageList(ExamSampleReplacePageInput input)
  230. {
  231. var name = !string.IsNullOrEmpty(input.Name?.Trim());
  232. var idNumber = !string.IsNullOrEmpty(input.IdNumber?.Trim());
  233. var examNumber = !string.IsNullOrEmpty(input.ExamNumber?.Trim());
  234. var ret = await replaceRep.DetachedEntities
  235. .Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == CurrentSysUserInfo.SysOrgId && t.IsDeleted == false)
  236. .Where((name, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamStudent.Name, $"%{input.Name.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamStudent.Name, $"%{input.Name.Trim()}%")))
  237. .Where((idNumber, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamStudent.IdNumber, $"%{input.IdNumber.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamStudent.IdNumber, $"%{input.IdNumber.Trim()}%")))
  238. .Where((examNumber, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamNumber, $"%{input.ExamNumber.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamNumber, $"%{input.ExamNumber.Trim()}%")))
  239. .Where(input.GradeId.HasValue, t => t.GradeId == input.GradeId)
  240. .Where(input.ClassNumber.HasValue, t => t.ClassNumber == input.ClassNumber)
  241. .Where(input.SysOrgBranchId.HasValue, t => t.SysOrgBranchId == input.SysOrgBranchId)
  242. .Where(input.IsReplaceAbsent.HasValue, t => t.IsReplaceAbsent == input.IsReplaceAbsent)
  243. .Where(input.IsReplaceAbsentLocked.HasValue, t => t.IsReplaceAbsentLocked == input.IsReplaceAbsentLocked)
  244. .ProjectToType<ExamSampleReplaceOutput>()
  245. .OrderBy(t => t.SysOrgBranchId).ThenBy(t => t.GradeId).ThenBy(t => t.ClassNumber).ThenByDescending(t => t.CreateTime)
  246. .ToADPagedListAsync(input.PageIndex, input.PageSize);
  247. return ret;
  248. }
  249. }