ExamScoreExportService.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using NPOI.HSSF.UserModel;
  2. using NPOI.SS.UserModel;
  3. using YBEE.EQM.Core;
  4. namespace YBEE.EQM.Application;
  5. /// <summary>
  6. /// 导出成绩服务
  7. /// </summary>
  8. /// <param name="rep"></param>
  9. /// <param name="sqlRep"></param>
  10. /// <param name="examGradeService"></param>
  11. /// <param name="examCourseService"></param>
  12. /// <param name="exportExcelService"></param>
  13. public class ExamScoreExportService(IRepository<ExamScore> rep, ISqlRepository sqlRep, IExamGradeService examGradeService, IExamCourseService examCourseService, IExportExcelService exportExcelService) : IExamScoreExportService, ITransient
  14. {
  15. /// <summary>
  16. /// 导出TQES输入文件
  17. /// </summary>
  18. /// <param name="input"></param>
  19. /// <returns></returns>
  20. /// <exception cref="Exception"></exception>
  21. public async Task<(string, byte[])> ExportTqesFile(ExamScoreExportTqesFileInput input)
  22. {
  23. var examPlan = await rep.Change<ExamPlan>().DetachedEntities.ProjectToType<ExamPlanOutput>().FirstOrDefaultAsync(t => t.Id == input.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
  24. var examGrades = await examGradeService.GetListByExamPlanId(input.ExamPlanId);
  25. var examCourses = await examCourseService.GetListByExamPlanId(input.ExamPlanId);
  26. // 临时存放目录
  27. string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
  28. Directory.CreateDirectory(fileRoot);
  29. string filePath = Path.Combine(fileRoot, $"{examPlan.Name}-TQES导入数据");
  30. Directory.CreateDirectory(filePath);
  31. try
  32. {
  33. foreach (var examGrade in examGrades)
  34. {
  35. for (int i = 0; i < 2; i++)
  36. {
  37. HSSFWorkbook wb = new();
  38. var cellStyles = exportExcelService.GetCellStyle(wb);
  39. ExamSampleType? examSampleType = i == 1 ? ExamSampleType.DISTRICT : null;
  40. var ecs = examCourses.Where(t => t.ExamGradeId == examGrade.Id).OrderBy(t => t.CourseId).ToList();
  41. await ExportToTqesSheet(input, examGrade.GradeId, examSampleType, ecs, wb, cellStyles);
  42. MemoryStream ms = new();
  43. wb.Write(ms, false);
  44. ms.Flush();
  45. string fn = i == 0 ? "全员" : "抽测";
  46. await File.WriteAllBytesAsync(Path.Combine(filePath, $"{examGrade.EducationStage.GetDescription()}-{examGrade.Grade.Name2}-监测成绩-{fn}.xls"), ms.ToArray());
  47. if (!examGrade.IsRequiredSample)
  48. {
  49. break;
  50. }
  51. }
  52. }
  53. string outFileName = $"{examPlan.Name}-TQES导入数据-{DateTime.Now.Ticks}.zip";
  54. string outFilePath = Path.Combine(fileRoot, outFileName);
  55. ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
  56. zip.CreateZip(outFilePath, filePath, true, string.Empty);
  57. var retBytes = await File.ReadAllBytesAsync(outFilePath);
  58. return (outFileName, retBytes);
  59. }
  60. catch (Exception ex)
  61. {
  62. throw new Exception("导出错误", ex);
  63. }
  64. finally
  65. {
  66. Directory.Delete(fileRoot, true);
  67. }
  68. }
  69. #region 导出方法
  70. /// <summary>
  71. /// 导出到表格
  72. /// </summary>
  73. /// <param name="input"></param>
  74. /// <param name="gradeId"></param>
  75. /// <param name="examSampleType"></param>
  76. /// <param name="examCourses"></param>
  77. /// <param name="wb"></param>
  78. /// <param name="cellStyles"></param>
  79. /// <returns></returns>
  80. private async Task ExportToTqesSheet(ExamScoreExportTqesFileInput input, short gradeId, ExamSampleType? examSampleType, List<ExamCourseOutput> examCourses, HSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  81. {
  82. ISheet sheet = wb.CreateSheet("监测成绩");
  83. sheet.DisplayGridlines = false;
  84. sheet.CreateFreezePane(0, 1);
  85. int rowNum = 0;
  86. #region 列头
  87. IRow headerRow = sheet.CreateRow(rowNum++);
  88. int ci = 0;
  89. exportExcelService.AddCell("学校", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  90. exportExcelService.AddCell("学校ID", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  91. exportExcelService.AddCell("考号", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 13);
  92. exportExcelService.AddCell("年级", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  93. exportExcelService.AddCell("班级", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  94. exportExcelService.AddCell("身份证号码", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  95. exportExcelService.AddCell("姓名", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 18);
  96. foreach (var c in examCourses)
  97. {
  98. exportExcelService.AddCell(c.Course.Name, headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  99. }
  100. #endregion
  101. #region 数据
  102. var totalList = await GetTotalList(input.ExamPlanId, gradeId, examSampleType);
  103. var scoreList = await GetScoreList(input.ExamPlanId, gradeId, examSampleType);
  104. foreach (var stu in totalList)
  105. {
  106. IRow row = sheet.CreateRow(rowNum++);
  107. ci = 0;
  108. exportExcelService.AddCell(stu.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  109. exportExcelService.AddCell(stu.TqesId, row, ci++, cellStyles.CenterCellStyle);
  110. exportExcelService.AddCell(stu.ExamNumber, row, ci++, cellStyles.CenterCellStyle);
  111. exportExcelService.AddCell(stu.GradeNumber, row, ci++, cellStyles.CenterCellStyle);
  112. exportExcelService.AddCell(stu.ClassNumber, row, ci++, cellStyles.CenterCellStyle);
  113. exportExcelService.AddCell(stu.IdNumber, row, ci++, cellStyles.CenterCellStyle);
  114. exportExcelService.AddCell(stu.Name, row, ci++, cellStyles.CenterCellStyle);
  115. var scores = scoreList.Where(t => t.ExamStudentId == stu.ExamStudentId).GroupBy(d => d.CourseId).Select(d => new ExamScoreExportTqesDto
  116. {
  117. CourseId = d.Key,
  118. Score = d.Max(s => s.Score),
  119. IsSpecial = d.FirstOrDefault()?.IsSpecial ?? false,
  120. IsAbsent = d.FirstOrDefault()?.IsAbsent ?? false,
  121. }).ToDictionary(d => d.CourseId);
  122. foreach (var c in examCourses)
  123. {
  124. if (stu.IsSpecial)
  125. {
  126. exportExcelService.AddCell("特殊", row, ci++, cellStyles.CenterCellStyle);
  127. continue;
  128. }
  129. if (scores.TryGetValue(c.CourseId, out ExamScoreExportTqesDto score))
  130. {
  131. if (score.IsSpecial)
  132. {
  133. exportExcelService.AddCell("特殊", row, ci++, cellStyles.CenterCellStyle);
  134. }
  135. else if ((score.IsAbsent || input.IsZeroToAbsent) && score.Score == 0)
  136. {
  137. exportExcelService.AddCell("缺考", row, ci++, cellStyles.CenterCellStyle);
  138. }
  139. else
  140. {
  141. exportExcelService.AddCell(score.Score, row, ci++, cellStyles.CenterCellStyle);
  142. }
  143. }
  144. else
  145. {
  146. exportExcelService.AddCell(null, row, ci++, cellStyles.CenterCellStyle);
  147. }
  148. }
  149. }
  150. #endregion
  151. }
  152. #endregion
  153. #region 获取数据
  154. /// <summary>
  155. /// 获取学生列表
  156. /// </summary>
  157. /// <param name="examPlanId"></param>
  158. /// <param name="gradeId"></param>
  159. /// <param name="examSampleType"></param>
  160. /// <returns></returns>
  161. private async Task<List<ExamScoreTotalExportTqesDto>> GetTotalList(int examPlanId, short gradeId, ExamSampleType? examSampleType)
  162. {
  163. var items = await sqlRep.SqlQueriesAsync<ExamScoreTotalExportTqesDto>($@"
  164. SELECT
  165. T1.sys_org_id,
  166. T2.`name` AS sys_org_name,
  167. T2.tqes_id,
  168. T1.exam_number,
  169. T3.grade_number,
  170. T1.class_number,
  171. CASE WHEN T4.id_number = '' OR T4.id_number IS NULL THEN RIGHT(CONCAT('00000000', T1.exam_plan_id, '-', T1.exam_number), 18) ELSE T4.id_number END AS id_number,
  172. T4.`name`,
  173. T1.exam_student_id,
  174. T1.exam_sample_type,
  175. T1.is_special
  176. FROM exam_score_total AS T1
  177. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  178. JOIN base_grade AS T3 ON T1.grade_id = T3.id
  179. JOIN exam_student AS T4 ON T1.exam_student_id = T4.id
  180. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  181. ORDER BY T1.sys_org_id, T1.grade_id, T1.class_number
  182. ;", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = examSampleType.HasValue ? (short)examSampleType : 0 });
  183. return items;
  184. }
  185. /// <summary>
  186. /// 获取成绩列表
  187. /// </summary>
  188. /// <param name="examPlanId"></param>
  189. /// <param name="gradeId"></param>
  190. /// <param name="examSampleType"></param>
  191. /// <returns></returns>
  192. private async Task<List<ExamScoreExportTqesDto>> GetScoreList(int examPlanId, short gradeId, ExamSampleType? examSampleType)
  193. {
  194. var items = await sqlRep.SqlQueriesAsync<ExamScoreExportTqesDto>($@"
  195. SELECT T1.exam_student_id, T1.course_id, T1.is_absent, T1.is_special, T1.score
  196. FROM exam_score AS T1
  197. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  198. ;", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = examSampleType.HasValue ? (short)examSampleType : 0 });
  199. return items;
  200. }
  201. #endregion
  202. }