ExamReportingAvgRangeService.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. using NPOI.SS.UserModel;
  2. using NPOI.SS.Util;
  3. using NPOI.XSSF.UserModel;
  4. using System.Data;
  5. using YBEE.EQM.Core;
  6. namespace YBEE.EQM.Application;
  7. /// <summary>
  8. /// 统计报表之分数段报表服务
  9. /// </summary>
  10. public class ExamReportingAvgRangeService : IExamReportingAvgRangeService, ITransient
  11. {
  12. private readonly IRepository _rep;
  13. private readonly ISqlRepository _sqlRep;
  14. private readonly IExamScoreRangeService _examScoreRangeService;
  15. private readonly IExamGradeService _examGradeService;
  16. private readonly IExamCourseService _examCourseService;
  17. private readonly IExportExcelService _exportExcelService;
  18. public ExamReportingAvgRangeService(IRepository rep, ISqlRepository sqlRep, IExamScoreRangeService examScoreRangeService, IExamGradeService examGradeService, IExamCourseService examCourseService, ExportExcelService exportExcelService)
  19. {
  20. _rep = rep;
  21. _sqlRep = sqlRep;
  22. _examScoreRangeService = examScoreRangeService;
  23. _examGradeService = examGradeService;
  24. _examCourseService = examCourseService;
  25. _exportExcelService = exportExcelService;
  26. }
  27. /// <summary>
  28. /// 导出分数段统计表
  29. /// </summary>
  30. /// <param name="examPlanId"></param>
  31. /// <returns></returns>
  32. public async Task<(string, byte[])> Export(int examPlanId)
  33. {
  34. var examPlan = await _rep.Change<ExamPlan>().DetachedEntities.ProjectToType<ExamPlanOutput>().FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
  35. var scoreRanges = await _examScoreRangeService.GetList();
  36. var examGrades = await _examGradeService.GetListByExamPlanId(examPlanId);
  37. var examCourses = await _examCourseService.GetListByExamPlanId(examPlanId);
  38. // 临时存放目录
  39. string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
  40. Directory.CreateDirectory(fileRoot);
  41. string filePath = Path.Combine(fileRoot, $"{examPlan.Name}-分数段平均分统计");
  42. Directory.CreateDirectory(filePath);
  43. try
  44. {
  45. foreach (var examGrade in examGrades)
  46. {
  47. string titlePrefix = $"{examPlan.Semester.Name}{examGrade.Grade.Name}期末";
  48. for (int i = 0; i < 2; i++)
  49. {
  50. IWorkbook wb = new XSSFWorkbook();
  51. var cellStyles = _exportExcelService.GetCellStyle(wb);
  52. ExamSampleType? sampleType = i == 1 ? ExamSampleType.DISTRICT : null;
  53. await ExportTotalRange(titlePrefix, examPlanId, sampleType, examGrade.GradeId, scoreRanges.Where(t => t.Type == examGrade.ExamScoreRangeType).ToList(), wb, cellStyles);
  54. await ExportCourseRange(titlePrefix, examPlanId, sampleType, examGrade.GradeId, scoreRanges, examCourses.Where(t => t.GradeId == examGrade.GradeId).ToList(), wb, cellStyles);
  55. await ExportCourseAvgScore(titlePrefix, examPlanId, sampleType, examGrade.GradeId, examCourses.Where(t => t.GradeId == examGrade.GradeId).ToList(), wb, cellStyles);
  56. MemoryStream ms = new();
  57. wb.Write(ms, false);
  58. ms.Flush();
  59. string fn = i == 0 ? "全员" : "抽测";
  60. await File.WriteAllBytesAsync(Path.Combine(filePath, $"{examGrade.Grade.Name}-{fn}-分数段统计.xlsx"), ms.ToArray());
  61. if (!examGrade.IsRequiredSample)
  62. {
  63. break;
  64. }
  65. }
  66. }
  67. string outFileName = $"{examPlan.Name}-分数段平均分统计-{DateTime.Now.Ticks}.zip";
  68. string outFilePath = Path.Combine(fileRoot, outFileName);
  69. ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
  70. zip.CreateZip(outFilePath, filePath, true, string.Empty);
  71. var retBytes = await File.ReadAllBytesAsync(outFilePath);
  72. return (outFileName, retBytes);
  73. }
  74. catch (Exception ex)
  75. {
  76. throw new Exception("导出错误", ex);
  77. }
  78. finally
  79. {
  80. Directory.Delete(fileRoot, true);
  81. }
  82. }
  83. /// <summary>
  84. /// 导出总分分数段
  85. /// </summary>
  86. /// <param name="titlePrefix"></param>
  87. /// <param name="examPlanId"></param>
  88. /// <param name="sampleType"></param>
  89. /// <param name="gradeId"></param>
  90. /// <param name="ranges"></param>
  91. /// <param name="wb"></param>
  92. /// <param name="cellStyles"></param>
  93. /// <returns></returns>
  94. private async Task ExportTotalRange(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamScoreRangeOutput> ranges, IWorkbook wb, ExportExcelCellStyle cellStyles)
  95. {
  96. ISheet sheet = wb.CreateSheet("总分分数段");
  97. sheet.DisplayGridlines = false;
  98. sheet.CreateFreezePane(0, 2);
  99. int rowNum = 0;
  100. #region 标题
  101. IRow titleRow = sheet.CreateRow(rowNum++);
  102. titleRow.HeightInPoints = 40;
  103. int ci = 0;
  104. _exportExcelService.AddCell($"{titlePrefix}总分分数段统计", titleRow, ci++, cellStyles.TitleStyle);
  105. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  106. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  107. foreach (var r in ranges)
  108. {
  109. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  110. }
  111. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  112. #endregion
  113. #region 列头
  114. IRow headerRow = sheet.CreateRow(rowNum++);
  115. headerRow.HeightInPoints = 30;
  116. ci = 0;
  117. _exportExcelService.AddCell("学校", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  118. _exportExcelService.AddCell("类别", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  119. _exportExcelService.AddCell("实考\r\n人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  120. foreach (var r in ranges)
  121. {
  122. _exportExcelService.AddCell(r.NickName, headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  123. }
  124. #endregion
  125. #region 数据
  126. var list = await GetTotalRangeList(examPlanId, gradeId, sampleType);
  127. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key).ToList();
  128. foreach (var urg in urGroup)
  129. {
  130. var orgGroup = urg.GroupBy(t => new { t.SysOrgId, t.TotalCount }).ToList();//.OrderByDescending(t => t.Key.TotalCount).ToList();
  131. foreach (var orgg in orgGroup)
  132. {
  133. IRow row = sheet.CreateRow(rowNum++);
  134. row.HeightInPoints = 20;
  135. ci = 0;
  136. var forg = orgg.First();
  137. _exportExcelService.AddCell(forg.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  138. _exportExcelService.AddCell(forg.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  139. _exportExcelService.AddCell(forg.TotalCount, row, ci++, cellStyles.CenterCellStyle);
  140. foreach (var r in ranges)
  141. {
  142. var sr = orgg.FirstOrDefault(t => t.ExamScoreRangeId == r.Id);
  143. _exportExcelService.AddCell(sr?.RangeCount, row, ci++, cellStyles.CenterCellStyle);
  144. }
  145. }
  146. }
  147. #endregion
  148. #region 合计
  149. IRow totalRow = sheet.CreateRow(rowNum++);
  150. totalRow.HeightInPoints = 20;
  151. ci = 0;
  152. _exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  153. _exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  154. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  155. _exportExcelService.AddCell($"SUM(C3:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  156. foreach (var r in ranges)
  157. {
  158. var cn = ExcelUtil.GetColumnNameByIndex(ci);
  159. _exportExcelService.AddCell($"SUM({cn}3:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  160. }
  161. #endregion
  162. }
  163. /// <summary>
  164. /// 导出学科分数段
  165. /// </summary>
  166. /// <param name="titlePrefix"></param>
  167. /// <param name="examPlanId"></param>
  168. /// <param name="sampleType"></param>
  169. /// <param name="gradeId"></param>
  170. /// <param name="ranges"></param>
  171. /// <param name="examCourses"></param>
  172. /// <param name="wb"></param>
  173. /// <param name="cellStyles"></param>
  174. /// <returns></returns>
  175. private async Task ExportCourseRange(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamScoreRangeOutput> ranges, List<ExamCourseOutput> examCourses, IWorkbook wb, ExportExcelCellStyle cellStyles)
  176. {
  177. ISheet sheet = wb.CreateSheet("学科分数段");
  178. sheet.DisplayGridlines = false;
  179. int rowNum = 0;
  180. foreach (var examCourse in examCourses)
  181. {
  182. var list = await GetCourseRangeList(examPlanId, gradeId, examCourse.CourseId, sampleType);
  183. if (!list.Any())
  184. {
  185. continue;
  186. }
  187. var courseRanges = ranges.Where(t => t.Type == examCourse.ExamScoreRangeType).ToList();
  188. #region 标题
  189. IRow titleRow = sheet.CreateRow(rowNum++);
  190. titleRow.HeightInPoints = 40;
  191. int ci = 0;
  192. _exportExcelService.AddCell($"{titlePrefix}{examCourse.Course.Name}分数段统计", titleRow, ci++, cellStyles.TitleStyle);
  193. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  194. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  195. foreach (var r in courseRanges)
  196. {
  197. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  198. }
  199. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  200. #endregion
  201. #region 列头
  202. IRow headerRow = sheet.CreateRow(rowNum++);
  203. headerRow.HeightInPoints = 20;
  204. ci = 0;
  205. _exportExcelService.AddCell("学校", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  206. _exportExcelService.AddCell("类别", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  207. _exportExcelService.AddCell("实考人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  208. foreach (var r in courseRanges)
  209. {
  210. _exportExcelService.AddCell(r.NickName, headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  211. }
  212. #endregion
  213. #region 数据
  214. int rc = 0;
  215. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key).ToList();
  216. foreach (var urg in urGroup)
  217. {
  218. var orgGroup = urg.GroupBy(t => new { t.SysOrgId, t.TotalCount }).ToList();//.OrderByDescending(t => t.Key.TotalCount).ToList();
  219. foreach (var orgg in orgGroup)
  220. {
  221. rc++;
  222. IRow row = sheet.CreateRow(rowNum++);
  223. row.HeightInPoints = 20;
  224. ci = 0;
  225. var forg = orgg.First();
  226. _exportExcelService.AddCell(forg.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  227. _exportExcelService.AddCell(forg.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  228. _exportExcelService.AddCell(forg.TotalCount, row, ci++, cellStyles.CenterCellStyle);
  229. foreach (var r in courseRanges)
  230. {
  231. var sr = orgg.FirstOrDefault(t => t.ExamScoreRangeId == r.Id);
  232. _exportExcelService.AddCell(sr?.RangeCount, row, ci++, cellStyles.CenterCellStyle);
  233. }
  234. }
  235. }
  236. #endregion
  237. #region 合计
  238. IRow totalRow = sheet.CreateRow(rowNum++);
  239. totalRow.HeightInPoints = 20;
  240. ci = 0;
  241. _exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  242. _exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  243. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  244. _exportExcelService.AddCell($"SUM(C{rowNum - rc}:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  245. foreach (var r in courseRanges)
  246. {
  247. var cn = ExcelUtil.GetColumnNameByIndex(ci);
  248. _exportExcelService.AddCell($"SUM({cn}{rowNum - rc}:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  249. }
  250. #endregion
  251. rowNum += 2;
  252. }
  253. }
  254. /// <summary>
  255. /// 导出学科平均分
  256. /// </summary>
  257. /// <param name="titlePrefix"></param>
  258. /// <param name="examPlanId"></param>
  259. /// <param name="sampleType"></param>
  260. /// <param name="gradeId"></param>
  261. /// <param name="examCourses"></param>
  262. /// <param name="wb"></param>
  263. /// <param name="cellStyles"></param>
  264. /// <returns></returns>
  265. private async Task ExportCourseAvgScore(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamCourseOutput> examCourses, IWorkbook wb, ExportExcelCellStyle cellStyles)
  266. {
  267. var courses = examCourses.Select(t => t.Course).ToList();
  268. courses.Insert(0, new CourseLiteOutput() { Id = 0, Name = "总分" });
  269. ISheet sheet = wb.CreateSheet("学科平均分");
  270. sheet.DisplayGridlines = false;
  271. //sheet.CreateFreezePane(0, 2);
  272. var cellStyleNumberP2Blod = wb.CreateCellStyle();
  273. cellStyleNumberP2Blod.CloneStyleFrom(cellStyles.NumberCellStyleP2);
  274. //var font = cellStyleNumberP2Blod.GetFont(wb);
  275. //font.IsBold = true;
  276. IFont bFont = wb.CreateFont();
  277. bFont.IsBold = true;
  278. bFont.FontName = cellStyles.TitleFontName;
  279. bFont.FontHeightInPoints = 10;
  280. cellStyleNumberP2Blod.SetFont(bFont);
  281. int rowNum = 0;
  282. #region 标题
  283. IRow titleRow = sheet.CreateRow(rowNum++);
  284. titleRow.HeightInPoints = 40;
  285. int ci = 0;
  286. _exportExcelService.AddCell($"{titlePrefix}各学科均分统计", titleRow, ci++, cellStyles.TitleStyle);
  287. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  288. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  289. foreach (var r in courses)
  290. {
  291. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  292. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  293. _exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  294. }
  295. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  296. #endregion
  297. #region 列头
  298. IRow headerRow1 = sheet.CreateRow(rowNum++);
  299. headerRow1.HeightInPoints = 20;
  300. ci = 0;
  301. _exportExcelService.AddCell("学校", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  302. _exportExcelService.AddCell("类别", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  303. _exportExcelService.AddCell("实考\r\n人数", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  304. foreach (var r in courses)
  305. {
  306. _exportExcelService.AddCell(r.Name, headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  307. _exportExcelService.AddCell("", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  308. _exportExcelService.AddCell("", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  309. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, ci - 3, ci - 1));
  310. }
  311. IRow headerRow2 = sheet.CreateRow(rowNum++);
  312. headerRow2.HeightInPoints = 30;
  313. ci = 0;
  314. _exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  315. _exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  316. _exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  317. foreach (var r in courses)
  318. {
  319. _exportExcelService.AddCell("平均分", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  320. _exportExcelService.AddCell("名次", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 5);
  321. _exportExcelService.AddCell("与第一\r\n名分差", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  322. }
  323. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 0, 0));
  324. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 1, 1));
  325. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 2, 2));
  326. #endregion
  327. #region 数据
  328. var list = await GetAvgScoreList(examPlanId, gradeId, sampleType);
  329. // 城乡分组
  330. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key == UrbanRuralType.NONE ? 99 : (short)t.Key).ToList();
  331. foreach (var urg in urGroup)
  332. {
  333. // 机构
  334. var orgGroup = urg.GroupBy(t => t.SysOrgId).OrderBy(t => t.FirstOrDefault()?.DataScopeType).ThenByDescending(t => t.FirstOrDefault(c => c.CourseId == 0)?.TotalCount).ToList();
  335. foreach (var orgg in orgGroup)
  336. {
  337. IRow row = sheet.CreateRow(rowNum++);
  338. row.HeightInPoints = 20;
  339. ci = 0;
  340. var forg = orgg.FirstOrDefault(t => t.CourseId == 0);
  341. if (forg.DataScopeType > 1)
  342. {
  343. var txt = forg?.SysOrgName ?? "";
  344. if (forg?.UrbanRuralType != UrbanRuralType.NONE)
  345. {
  346. txt = $"{forg?.UrbanRuralType?.GetDescription()}{txt}";
  347. }
  348. _exportExcelService.AddCell(txt, row, ci++, cellStyles.ColumnHeaderStyle);
  349. _exportExcelService.AddCell("", row, ci++, cellStyles.ColumnHeaderStyle);
  350. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, ci - 2, ci - 1));
  351. }
  352. else
  353. {
  354. _exportExcelService.AddCell(forg?.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  355. _exportExcelService.AddCell(forg?.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  356. }
  357. _exportExcelService.AddCell(forg?.TotalCount, row, ci++, forg?.DataScopeType != 1 ? cellStyles.ColumnHeaderStyle : cellStyles.CenterCellStyle);
  358. foreach (var r in courses)
  359. {
  360. var sr = orgg.FirstOrDefault(t => t.CourseId == r.Id);
  361. _exportExcelService.AddCell(sr?.AvgScore, row, ci++, sr?.DataScopeType != 1 ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  362. _exportExcelService.AddCell(sr?.DataScopeType != 1 ? null : sr?.OrderInTotal, row, ci++, cellStyles.CenterCellStyle);
  363. _exportExcelService.AddCell(sr?.AvgScoreDiff, row, ci++, sr?.DataScopeType != 1 ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  364. }
  365. }
  366. }
  367. #endregion
  368. //#region 合计
  369. //IRow totalRow = sheet.CreateRow(rowNum++);
  370. //totalRow.HeightInPoints = 20;
  371. //ci = 0;
  372. //_exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  373. //_exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  374. //sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  375. //_exportExcelService.AddCell($"SUM(C3:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  376. //foreach (var r in ranges)
  377. //{
  378. // var cn = ExcelUtil.GetColumnNameByIndex(ci);
  379. // _exportExcelService.AddCell($"SUM({cn}3:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  380. //}
  381. //#endregion
  382. }
  383. #region 数据获取
  384. /// <summary>
  385. /// 获取总分分数段数据
  386. /// </summary>
  387. /// <param name="examPlanId"></param>
  388. /// <param name="gradeId"></param>
  389. /// <param name="sampleType"></param>
  390. /// <returns></returns>
  391. private async Task<List<ExamScoreRangeExportDto>> GetTotalRangeList(int examPlanId, short gradeId, ExamSampleType? sampleType)
  392. {
  393. var items = await _sqlRep.SqlQueriesAsync<ExamScoreRangeExportDto>($@"
  394. SELECT T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*
  395. FROM
  396. (
  397. SELECT T1.sys_org_id, T1.exam_score_range_id, COUNT(T1.exam_student_id) AS range_count, MAX(T1.total_count) AS total_count
  398. FROM
  399. (
  400. SELECT T1.sys_org_id, T1.exam_score_range_id, T1.exam_student_id, COUNT(T1.exam_student_id) OVER (PARTITION BY T1.sys_org_id) AS total_count
  401. FROM exam_score_total AS T1
  402. WHERE exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  403. ) AS T1
  404. GROUP BY T1.sys_org_id, T1.exam_score_range_id
  405. ) AS T1
  406. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  407. ", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  408. return items;
  409. }
  410. /// <summary>
  411. /// 获取单科分数段数据
  412. /// </summary>
  413. /// <param name="examPlanId"></param>
  414. /// <param name="gradeId"></param>
  415. /// <param name="courseId"></param>
  416. /// <param name="sampleType"></param>
  417. /// <returns></returns>
  418. private async Task<List<ExamScoreRangeExportDto>> GetCourseRangeList(int examPlanId, short gradeId, short courseId, ExamSampleType? sampleType)
  419. {
  420. var items = await _sqlRep.SqlQueriesAsync<ExamScoreRangeExportDto>($@"
  421. SELECT T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*
  422. FROM
  423. (
  424. SELECT T1.sys_org_id, T1.exam_score_range_id, COUNT(T1.exam_student_id) AS range_count, MAX(T1.total_count) AS total_count
  425. FROM
  426. (
  427. SELECT T1.sys_org_id, T1.exam_score_range_id, T1.exam_student_id, COUNT(T1.exam_student_id) OVER (PARTITION BY T1.sys_org_id) AS total_count
  428. FROM exam_score AS T1
  429. WHERE exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.course_id = @courseId AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  430. ) AS T1
  431. GROUP BY T1.sys_org_id, T1.exam_score_range_id
  432. ) AS T1
  433. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  434. ", new { ExamPlanId = examPlanId, GradeId = gradeId, CourseId = courseId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  435. return items;
  436. }
  437. /// <summary>
  438. /// 获取平均分数据
  439. /// </summary>
  440. /// <param name="examPlanId"></param>
  441. /// <param name="gradeId"></param>
  442. /// <param name="sampleType"></param>
  443. /// <returns></returns>
  444. private async Task<List<ExamScoreAvgExportDto>> GetAvgScoreList(int examPlanId, short gradeId, ExamSampleType? sampleType)
  445. {
  446. var items = await _sqlRep.SqlQueriesAsync<ExamScoreAvgExportDto>($@"
  447. SELECT T1.*
  448. FROM
  449. (
  450. -- 总分
  451. SELECT 1 AS data_scope_type, T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*,
  452. RANK() OVER(ORDER BY T1.avg_score DESC) AS order_in_total,
  453. RANK() OVER(PARTITION BY T2.urban_rural_type ORDER BY T1.avg_score DESC) AS order_in_same,
  454. T3.score_max - T1.avg_score AS avg_score_diff,
  455. T3.score_max
  456. FROM
  457. (
  458. SELECT sys_org_id, 0 AS course_id, COUNT(exam_student_id) AS total_count, AVG(score) AS avg_score
  459. FROM exam_score_total
  460. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  461. GROUP BY sys_org_id
  462. ) AS T1
  463. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  464. JOIN
  465. (
  466. SELECT MAX(T.avg_score) AS score_max
  467. FROM
  468. (
  469. SELECT sys_org_id, AVG(score) AS avg_score
  470. FROM exam_score_total
  471. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  472. GROUP BY sys_org_id
  473. ) AS T
  474. ) AS T3
  475. -- 单科
  476. UNION ALL
  477. SELECT 1 AS data_scope_type, T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*,
  478. RANK() OVER(PARTITION BY T1.course_id ORDER BY T1.avg_score DESC) AS order_in_total,
  479. RANK() OVER(PARTITION BY T1.course_id, T2.urban_rural_type ORDER BY T1.avg_score DESC) AS order_in_same,
  480. T3.score_max - T1.avg_score AS avg_score_diff,
  481. T3.score_max
  482. FROM
  483. (
  484. SELECT sys_org_id, course_id, COUNT(exam_student_id) AS total_count, AVG(score) AS avg_score
  485. FROM exam_score
  486. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  487. GROUP BY sys_org_id, course_id
  488. ) AS T1
  489. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  490. JOIN
  491. (
  492. SELECT T.course_id, MAX(T.avg_score) AS score_max
  493. FROM
  494. (
  495. SELECT sys_org_id, course_id, AVG(score) AS avg_score
  496. FROM exam_score
  497. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  498. GROUP BY sys_org_id, course_id
  499. ) AS T
  500. GROUP BY T.course_id
  501. ) AS T3 ON T1.course_id = T3.course_id
  502. -- 同类小计
  503. UNION ALL
  504. SELECT 2 AS data_scope_type, T1.*,
  505. NULL AS order_in_total,
  506. NULL AS order_in_same,
  507. T3.score_max - T1.avg_score AS avg_score_diff,
  508. T3.score_max
  509. FROM
  510. (
  511. SELECT T2.urban_rural_type, '小计' AS sys_org_name, 99999998 AS sys_org_id, 0 AS course_id, COUNT(T1.exam_student_id) AS total_count, AVG(T1.score) AS avg_score
  512. FROM exam_score_total AS T1
  513. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  514. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  515. GROUP BY T2.urban_rural_type
  516. ) AS T1
  517. JOIN
  518. (
  519. SELECT MAX(score) AS score_max FROM exam_score_total WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  520. ) AS T3
  521. -- 单科同类小计
  522. UNION ALL
  523. SELECT 2 AS data_scope_type, T1.*,
  524. NULL AS order_in_total,
  525. NULL AS order_in_same,
  526. T3.score_max - T1.avg_score AS avg_score_diff,
  527. T3.score_max
  528. FROM
  529. (
  530. SELECT T2.urban_rural_type, '小计' AS sys_org_name, 99999998 AS sys_org_id, T1.course_id, COUNT(T1.exam_student_id) AS total_count, AVG(T1.score) AS avg_score
  531. FROM exam_score AS T1
  532. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  533. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  534. GROUP BY T2.urban_rural_type, T1.course_id
  535. ) AS T1
  536. JOIN (
  537. SELECT course_id, MAX(score) AS score_max
  538. FROM exam_score
  539. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  540. GROUP BY course_id
  541. ) AS T3 ON T1.course_id = T3.course_id
  542. -- 全部合计
  543. UNION ALL
  544. SELECT 3 AS data_scope_type, T1.*,
  545. NULL AS order_in_total,
  546. NULL AS order_in_same,
  547. T3.score_max - T1.avg_score AS avg_score_diff,
  548. T3.score_max
  549. FROM
  550. (
  551. SELECT 0 AS urban_rural_type, '合计' AS sys_org_name, 99999999 AS sys_org_id, 0 AS course_id, COUNT(T1.exam_student_id) AS total_count, AVG(T1.score) AS avg_score
  552. FROM exam_score_total AS T1
  553. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  554. ) AS T1
  555. JOIN
  556. (
  557. SELECT MAX(score) AS score_max FROM exam_score_total WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  558. ) AS T3
  559. -- 单科全部合计
  560. UNION ALL
  561. SELECT 3 AS data_scope_type, T1.*,
  562. NULL AS order_in_total,
  563. NULL AS order_in_same,
  564. T3.score_max - T1.avg_score AS avg_score_diff,
  565. T3.score_max
  566. FROM
  567. (
  568. SELECT 0 AS urban_rural_type, '合计' AS sys_org_name, 99999999 AS sys_org_id, T1.course_id, COUNT(T1.exam_student_id) AS total_count, AVG(T1.score) AS avg_score
  569. FROM exam_score AS T1
  570. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  571. GROUP BY T1.course_id
  572. ) AS T1
  573. JOIN (
  574. SELECT course_id, MAX(score) AS score_max
  575. FROM exam_score
  576. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  577. GROUP BY course_id
  578. ) AS T3 ON T1.course_id = T3.course_id
  579. ) AS T1
  580. ORDER BY T1.urban_rural_type, T1.data_scope_type, T1.sys_org_id, T1.total_count;
  581. ", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  582. return items;
  583. }
  584. #endregion
  585. }