ExamReportingAvgRangeService.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. using NPOI.SS.Formula.Functions;
  2. using NPOI.SS.UserModel;
  3. using NPOI.SS.Util;
  4. using NPOI.XSSF.UserModel;
  5. using System.Data;
  6. using YBEE.EQM.Core;
  7. namespace YBEE.EQM.Application;
  8. /// <summary>
  9. /// 统计报表之分数段报表服务
  10. /// </summary>
  11. public class ExamReportingAvgRangeService(
  12. IRepository rep,
  13. ISqlRepository sqlRep,
  14. IExamScoreRangeService examScoreRangeService,
  15. IExamGradeService examGradeService,
  16. IExamOrgService examOrgService,
  17. IExamCourseService examCourseService,
  18. IExportExcelService exportExcelService) : IExamReportingAvgRangeService, ITransient
  19. {
  20. #region 导出
  21. /// <summary>
  22. /// 导出全区分数段统计表
  23. /// </summary>
  24. /// <param name="examPlanId"></param>
  25. /// <returns></returns>
  26. public async Task<(string, byte[])> ExportTotal(int examPlanId)
  27. {
  28. var examPlan = await rep.Change<ExamPlan>().DetachedEntities.ProjectToType<ExamPlanOutput>().FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
  29. var scoreRanges = await examScoreRangeService.GetList();
  30. var examGrades = await examGradeService.GetListByExamPlanId(examPlanId);
  31. var examCourses = await examCourseService.GetListByExamPlanId(examPlanId);
  32. // 临时存放目录
  33. string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
  34. Directory.CreateDirectory(fileRoot);
  35. string filePath = Path.Combine(fileRoot, $"{examPlan.Name}-区县报告-平均分及分数段统计");
  36. Directory.CreateDirectory(filePath);
  37. try
  38. {
  39. foreach (var examGrade in examGrades)
  40. {
  41. for (int i = 0; i < 2; i++)
  42. {
  43. string fn = i == 0 ? "区校合并" : "区级监测";
  44. string titlePrefix = $"{examPlan.Name}{fn}{examGrade.Grade.Name}";
  45. XSSFWorkbook wb = new();
  46. var cellStyles = exportExcelService.GetCellStyle(wb);
  47. ExamSampleType? sampleType = i == 1 ? ExamSampleType.DISTRICT : null;
  48. await ExportTotalTotalRange(titlePrefix, examPlanId, sampleType, examGrade.GradeId, scoreRanges.Where(t => t.Type == examGrade.ExamScoreRangeType).ToList(), wb, cellStyles);
  49. await ExportTotalCourseRange(titlePrefix, examPlanId, sampleType, examGrade.GradeId, scoreRanges, examCourses.Where(t => t.GradeId == examGrade.GradeId).ToList(), wb, cellStyles);
  50. await ExportTotalCourseAvgScore(titlePrefix, examPlanId, sampleType, examGrade.GradeId, examCourses.Where(t => t.GradeId == examGrade.GradeId).ToList(), wb, cellStyles);
  51. MemoryStream ms = new();
  52. wb.Write(ms, false);
  53. ms.Flush();
  54. await File.WriteAllBytesAsync(Path.Combine(filePath, $"{examGrade.Grade.Name}-{fn}-平均分及分数段统计.xlsx"), ms.ToArray());
  55. if (!examGrade.IsRequiredSample)
  56. {
  57. break;
  58. }
  59. }
  60. }
  61. string outFileName = $"{examPlan.Name}-区县报告-平均分及分数段统计-{DateTime.Now.Ticks}.zip";
  62. string outFilePath = Path.Combine(fileRoot, outFileName);
  63. ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
  64. zip.CreateZip(outFilePath, filePath, true, string.Empty);
  65. var retBytes = await File.ReadAllBytesAsync(outFilePath);
  66. return (outFileName, retBytes);
  67. }
  68. catch (Exception ex)
  69. {
  70. throw new Exception("导出错误", ex);
  71. }
  72. finally
  73. {
  74. Directory.Delete(fileRoot, true);
  75. }
  76. }
  77. /// <summary>
  78. /// 导出各校分数段统计表
  79. /// </summary>
  80. /// <param name="examPlanId"></param>
  81. /// <returns></returns>
  82. /// <exception cref="Exception"></exception>
  83. public async Task<(string, byte[])> ExportOrg(int examPlanId)
  84. {
  85. var examPlan = await rep.Change<ExamPlan>().DetachedEntities.ProjectToType<ExamPlanOutput>().FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
  86. var scoreRanges = await examScoreRangeService.GetList();
  87. var examGrades = await examGradeService.GetListByExamPlanId(examPlanId);
  88. var examCourses = await examCourseService.GetListByExamPlanId(examPlanId);
  89. var examOrgs = (await examOrgService.GetListByExamPlanId(examPlanId)).Where(t => t.IsRequiredExam).ToList();
  90. // 临时存放目录
  91. string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
  92. Directory.CreateDirectory(fileRoot);
  93. string filePath = Path.Combine(fileRoot, $"{examPlan.Name}-学校报告-平均分及分数段统计");
  94. Directory.CreateDirectory(filePath);
  95. // 学段目录
  96. string eduStageFilePath = Path.Combine(filePath, examPlan.EducationStage.GetDescription());
  97. Directory.CreateDirectory(eduStageFilePath);
  98. try
  99. {
  100. // 年级
  101. foreach (var examGrade in examGrades)
  102. {
  103. var ecourses = examCourses.Where(t => t.GradeId == examGrade.GradeId).OrderBy(t => t.CourseId).ToList();
  104. for (int i = 0; i < 2; i++)
  105. {
  106. string fn = i == 0 ? "区校合并" : "区级监测";
  107. string titlePrefix = $"{fn}{examGrade.Grade.Name}";
  108. ExamSampleType? sampleType = i == 1 ? ExamSampleType.DISTRICT : null;
  109. // 平均分
  110. var listAvgScore = await GetAvgScoreList(examPlanId, examGrade.GradeId, sampleType);
  111. // 总分分数段
  112. var listTotalScoreRange = await GetTotalRangeList(examPlanId, examGrade.GradeId, sampleType);
  113. // 学科分数段
  114. var listCourseScoreRange = await GetCourseRangeList(examPlanId, examGrade.GradeId, sampleType);
  115. // 学校
  116. foreach (var org in examOrgs)
  117. {
  118. // 学校目录
  119. string orgFilePath = Path.Combine(eduStageFilePath, org.SysOrg.FullName, "学校报告", "1-6 平均分及分数段统计");
  120. if (!Directory.Exists(orgFilePath))
  121. {
  122. Directory.CreateDirectory(orgFilePath);
  123. }
  124. XSSFWorkbook wb = new();
  125. var cellStyles = exportExcelService.GetCellStyle(wb);
  126. // 平均分
  127. var orgAvgScoreList = listAvgScore.Where(t => t.SysOrgId == org.SysOrgId || t.DataScopeType > 1 || (t.DataScopeType == 1 && t.UrbanRuralType == UrbanRuralType.NONE))
  128. .OrderBy(t => t.DataScopeType).ThenBy(t => t.SysOrgId).ThenBy(t => t.UrbanRuralType).ThenBy(t => t.CourseId)
  129. .ToList();
  130. ExportOrgCourseAvgScore(titlePrefix, org.SysOrg.Name, orgAvgScoreList, ecourses, wb, cellStyles);
  131. // 总分分数段
  132. var orgTotalScoreRangeList = listTotalScoreRange.Where(t => t.SysOrgId == org.SysOrgId).OrderByDescending(t => t.ExamScoreRangeId).ToList();
  133. ExportOrgScoreRange(titlePrefix, org.SysOrg.Name, "总分", orgTotalScoreRangeList, scoreRanges.Where(t => t.Type == examGrade.ExamScoreRangeType).ToList(), wb, cellStyles);
  134. // 学科分数段
  135. foreach (var ecourse in ecourses)
  136. {
  137. var orgCourseScoreRangeList = listCourseScoreRange.Where(t => t.SysOrgId == org.SysOrgId && t.CourseId == ecourse.CourseId).ToList();
  138. ExportOrgScoreRange(titlePrefix, org.SysOrg.Name, ecourse.Course.Name, orgCourseScoreRangeList, scoreRanges.Where(t => t.Type == ecourse.ExamScoreRangeType).ToList(), wb, cellStyles);
  139. }
  140. MemoryStream ms = new();
  141. wb.Write(ms, false);
  142. ms.Flush();
  143. await File.WriteAllBytesAsync(Path.Combine(orgFilePath, $"{examGrade.Grade.Name}-{fn}-平均分及分数段统计.xlsx"), ms.ToArray());
  144. }
  145. if (!examGrade.IsRequiredSample)
  146. {
  147. break;
  148. }
  149. }
  150. }
  151. string outFileName = $"{examPlan.Name}-学校报告-平均分及分数段统计-{DateTime.Now.Ticks}.zip";
  152. string outFilePath = Path.Combine(fileRoot, outFileName);
  153. ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
  154. zip.CreateZip(outFilePath, filePath, true, string.Empty);
  155. var retBytes = await File.ReadAllBytesAsync(outFilePath);
  156. return (outFileName, retBytes);
  157. }
  158. catch (Exception ex)
  159. {
  160. throw new Exception("导出错误", ex);
  161. }
  162. finally
  163. {
  164. Directory.Delete(fileRoot, true);
  165. }
  166. }
  167. #endregion
  168. #region 导出全区表格私有方法
  169. /// <summary>
  170. /// 导出总分分数段
  171. /// </summary>
  172. /// <param name="titlePrefix"></param>
  173. /// <param name="examPlanId"></param>
  174. /// <param name="sampleType"></param>
  175. /// <param name="gradeId"></param>
  176. /// <param name="ranges"></param>
  177. /// <param name="wb"></param>
  178. /// <param name="cellStyles"></param>
  179. /// <returns></returns>
  180. private async Task ExportTotalTotalRange(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamScoreRangeOutput> ranges, XSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  181. {
  182. ISheet sheet = wb.CreateSheet("总分分数段");
  183. sheet.DisplayGridlines = false;
  184. sheet.CreateFreezePane(0, 2);
  185. int rowNum = 0;
  186. #region 标题
  187. IRow titleRow = sheet.CreateRow(rowNum++);
  188. titleRow.HeightInPoints = 40;
  189. int ci = 0;
  190. exportExcelService.AddCell($"{titlePrefix}总分分数段统计", titleRow, ci++, cellStyles.TitleStyle);
  191. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  192. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  193. foreach (var r in ranges)
  194. {
  195. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  196. }
  197. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  198. #endregion
  199. #region 列头
  200. IRow headerRow = sheet.CreateRow(rowNum++);
  201. headerRow.HeightInPoints = 30;
  202. ci = 0;
  203. exportExcelService.AddCell("学校", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  204. exportExcelService.AddCell("类别", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  205. exportExcelService.AddCell("实考\r\n人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  206. foreach (var r in ranges)
  207. {
  208. exportExcelService.AddCell(r.NickName, headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  209. }
  210. #endregion
  211. #region 数据
  212. var list = await GetTotalRangeList(examPlanId, gradeId, sampleType);
  213. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key).ToList();
  214. foreach (var urg in urGroup)
  215. {
  216. var orgGroup = urg.GroupBy(t => new { t.SysOrgId, t.TotalCount }).ToList();//.OrderByDescending(t => t.Key.TotalCount).ToList();
  217. foreach (var orgg in orgGroup)
  218. {
  219. IRow row = sheet.CreateRow(rowNum++);
  220. row.HeightInPoints = 20;
  221. ci = 0;
  222. var forg = orgg.First();
  223. exportExcelService.AddCell(forg.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  224. exportExcelService.AddCell(forg.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  225. exportExcelService.AddCell(forg.TotalCount, row, ci++, cellStyles.CenterCellStyle);
  226. foreach (var r in ranges)
  227. {
  228. var sr = orgg.FirstOrDefault(t => t.ExamScoreRangeId == r.Id);
  229. exportExcelService.AddCell(sr?.RangeCount, row, ci++, cellStyles.CenterCellStyle);
  230. }
  231. }
  232. }
  233. #endregion
  234. #region 合计
  235. IRow totalRow = sheet.CreateRow(rowNum++);
  236. totalRow.HeightInPoints = 20;
  237. ci = 0;
  238. exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  239. exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  240. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  241. exportExcelService.AddCell($"SUM(C3:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  242. foreach (var r in ranges)
  243. {
  244. var cn = ExcelUtil.GetColumnNameByIndex(ci);
  245. exportExcelService.AddCell($"SUM({cn}3:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  246. }
  247. #endregion
  248. }
  249. /// <summary>
  250. /// 导出学科分数段
  251. /// </summary>
  252. /// <param name="titlePrefix"></param>
  253. /// <param name="examPlanId"></param>
  254. /// <param name="sampleType"></param>
  255. /// <param name="gradeId"></param>
  256. /// <param name="ranges"></param>
  257. /// <param name="examCourses"></param>
  258. /// <param name="wb"></param>
  259. /// <param name="cellStyles"></param>
  260. /// <returns></returns>
  261. private async Task ExportTotalCourseRange(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamScoreRangeOutput> ranges, List<ExamCourseOutput> examCourses, XSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  262. {
  263. ISheet sheet = wb.CreateSheet("学科分数段");
  264. sheet.DisplayGridlines = false;
  265. int rowNum = 0;
  266. foreach (var examCourse in examCourses)
  267. {
  268. var list = await GetCourseRangeList(examPlanId, gradeId, examCourse.CourseId, sampleType);
  269. if (list.Count == 0)
  270. {
  271. continue;
  272. }
  273. var courseRanges = ranges.Where(t => t.Type == examCourse.ExamScoreRangeType).ToList();
  274. #region 标题
  275. IRow titleRow = sheet.CreateRow(rowNum++);
  276. titleRow.HeightInPoints = 40;
  277. int ci = 0;
  278. exportExcelService.AddCell($"{titlePrefix}{examCourse.Course.Name}分数段统计", titleRow, ci++, cellStyles.TitleStyle);
  279. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  280. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  281. foreach (var r in courseRanges)
  282. {
  283. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  284. }
  285. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  286. #endregion
  287. #region 列头
  288. IRow headerRow = sheet.CreateRow(rowNum++);
  289. headerRow.HeightInPoints = 20;
  290. ci = 0;
  291. exportExcelService.AddCell("学校", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  292. exportExcelService.AddCell("类别", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 6);
  293. exportExcelService.AddCell("实考人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  294. foreach (var r in courseRanges)
  295. {
  296. exportExcelService.AddCell(r.NickName, headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  297. }
  298. #endregion
  299. #region 数据
  300. int rc = 0;
  301. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key).ToList();
  302. foreach (var urg in urGroup)
  303. {
  304. var orgGroup = urg.GroupBy(t => new { t.SysOrgId, t.TotalCount }).ToList();//.OrderByDescending(t => t.Key.TotalCount).ToList();
  305. foreach (var orgg in orgGroup)
  306. {
  307. rc++;
  308. IRow row = sheet.CreateRow(rowNum++);
  309. row.HeightInPoints = 20;
  310. ci = 0;
  311. var forg = orgg.First();
  312. exportExcelService.AddCell(forg.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  313. exportExcelService.AddCell(forg.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  314. exportExcelService.AddCell(forg.TotalCount, row, ci++, cellStyles.CenterCellStyle);
  315. foreach (var r in courseRanges)
  316. {
  317. var sr = orgg.FirstOrDefault(t => t.ExamScoreRangeId == r.Id);
  318. exportExcelService.AddCell(sr?.RangeCount, row, ci++, cellStyles.CenterCellStyle);
  319. }
  320. }
  321. }
  322. #endregion
  323. #region 合计
  324. IRow totalRow = sheet.CreateRow(rowNum++);
  325. totalRow.HeightInPoints = 20;
  326. ci = 0;
  327. exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  328. exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  329. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  330. exportExcelService.AddCell($"SUM(C{rowNum - rc}:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  331. foreach (var r in courseRanges)
  332. {
  333. var cn = ExcelUtil.GetColumnNameByIndex(ci);
  334. exportExcelService.AddCell($"SUM({cn}{rowNum - rc}:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  335. }
  336. #endregion
  337. rowNum += 2;
  338. }
  339. }
  340. /// <summary>
  341. /// 导出学科平均分
  342. /// </summary>
  343. /// <param name="titlePrefix"></param>
  344. /// <param name="examPlanId"></param>
  345. /// <param name="sampleType"></param>
  346. /// <param name="gradeId"></param>
  347. /// <param name="examCourses"></param>
  348. /// <param name="wb"></param>
  349. /// <param name="cellStyles"></param>
  350. /// <returns></returns>
  351. private async Task ExportTotalCourseAvgScore(string titlePrefix, int examPlanId, ExamSampleType? sampleType, short gradeId, List<ExamCourseOutput> examCourses, XSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  352. {
  353. var courses = examCourses.Select(t => t.Course).ToList();
  354. courses.Insert(0, new CourseLiteOutput() { Id = 0, Name = "总分" });
  355. ISheet sheet = wb.CreateSheet("学科平均分");
  356. sheet.DisplayGridlines = false;
  357. //sheet.CreateFreezePane(0, 2);
  358. var cellStyleNumberP2Blod = wb.CreateCellStyle();
  359. cellStyleNumberP2Blod.CloneStyleFrom(cellStyles.NumberCellStyleP2);
  360. //var font = cellStyleNumberP2Blod.GetFont(wb);
  361. //font.IsBold = true;
  362. IFont bFont = wb.CreateFont();
  363. bFont.IsBold = true;
  364. bFont.FontName = cellStyles.TitleFontName;
  365. bFont.FontHeightInPoints = 10;
  366. cellStyleNumberP2Blod.SetFont(bFont);
  367. int rowNum = 0;
  368. #region 标题
  369. IRow titleRow = sheet.CreateRow(rowNum++);
  370. titleRow.HeightInPoints = 40;
  371. int ci = 0;
  372. exportExcelService.AddCell($"{titlePrefix}各学科均分统计", titleRow, ci++, cellStyles.TitleStyle);
  373. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  374. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  375. foreach (var r in courses)
  376. {
  377. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  378. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  379. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  380. }
  381. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  382. #endregion
  383. #region 列头
  384. IRow headerRow1 = sheet.CreateRow(rowNum++);
  385. headerRow1.HeightInPoints = 20;
  386. ci = 0;
  387. exportExcelService.AddCell("学校", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  388. exportExcelService.AddCell("类别", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  389. exportExcelService.AddCell("实考\r\n人数", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  390. foreach (var r in courses)
  391. {
  392. exportExcelService.AddCell(r.Name, headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  393. exportExcelService.AddCell("", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  394. exportExcelService.AddCell("", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  395. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, ci - 3, ci - 1));
  396. }
  397. IRow headerRow2 = sheet.CreateRow(rowNum++);
  398. headerRow2.HeightInPoints = 30;
  399. ci = 0;
  400. exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  401. exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  402. exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  403. foreach (var r in courses)
  404. {
  405. exportExcelService.AddCell("平均分", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  406. exportExcelService.AddCell("名次", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 5);
  407. exportExcelService.AddCell("与最高\r\n分差距", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  408. }
  409. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 0, 0));
  410. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 1, 1));
  411. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 2, 2));
  412. #endregion
  413. #region 数据
  414. var list = await GetAvgScoreList(examPlanId, gradeId, sampleType);
  415. // 过滤掉最高分
  416. list = list.Where(t => !(t.DataScopeType == 1 && t.UrbanRuralType == UrbanRuralType.NONE)).ToList();
  417. // 城乡分组
  418. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key == UrbanRuralType.NONE ? 99 : (short)t.Key).ToList();
  419. foreach (var urg in urGroup)
  420. {
  421. // 机构
  422. //var orgGroup = urg.GroupBy(t => t.SysOrgId).OrderBy(t => t.FirstOrDefault()?.DataScopeType).ThenByDescending(t => t.FirstOrDefault(c => c.CourseId == 0)?.TotalCount).ToList();
  423. var orgGroup = urg.GroupBy(t => t.SysOrgId).OrderBy(t => t.FirstOrDefault()?.DataScopeType).ThenBy(t => t.FirstOrDefault(c => c.CourseId == 0)?.OrderInTotal).ToList();
  424. foreach (var orgg in orgGroup)
  425. {
  426. IRow row = sheet.CreateRow(rowNum++);
  427. row.HeightInPoints = 20;
  428. ci = 0;
  429. var forg = orgg.FirstOrDefault(t => t.CourseId == 0);
  430. if (forg.DataScopeType > 1)
  431. {
  432. var txt = forg?.SysOrgName ?? "";
  433. if (forg?.UrbanRuralType != UrbanRuralType.NONE)
  434. {
  435. txt = $"{forg?.UrbanRuralType?.GetDescription()}{txt}";
  436. }
  437. exportExcelService.AddCell(txt, row, ci++, cellStyles.ColumnHeaderStyle);
  438. exportExcelService.AddCell("", row, ci++, cellStyles.ColumnHeaderStyle);
  439. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, ci - 2, ci - 1));
  440. }
  441. else
  442. {
  443. exportExcelService.AddCell(forg?.SysOrgName, row, ci++, cellStyles.LeftCellStyle);
  444. exportExcelService.AddCell(forg?.UrbanRuralType.GetDescription(), row, ci++, cellStyles.CenterCellStyle);
  445. }
  446. exportExcelService.AddCell(forg?.TotalCount, row, ci++, forg?.DataScopeType != 1 ? cellStyles.ColumnHeaderStyle : cellStyles.CenterCellStyle);
  447. foreach (var r in courses)
  448. {
  449. var sr = orgg.FirstOrDefault(t => t.CourseId == r.Id);
  450. exportExcelService.AddCell(sr?.AvgScore, row, ci++, (sr?.DataScopeType != 1 || sr.AvgScoreDiff == 0) ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  451. exportExcelService.AddCell(sr?.DataScopeType != 1 ? null : sr?.OrderInTotal, row, ci++, cellStyles.CenterCellStyle);
  452. exportExcelService.AddCell(sr?.AvgScoreDiff, row, ci++, sr?.DataScopeType != 1 ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  453. }
  454. }
  455. }
  456. #endregion
  457. //#region 合计
  458. //IRow totalRow = sheet.CreateRow(rowNum++);
  459. //totalRow.HeightInPoints = 20;
  460. //ci = 0;
  461. //exportExcelService.AddCell("合计", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  462. //exportExcelService.AddCell("", totalRow, ci++, cellStyles.ColumnHeaderStyle);
  463. //sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(rowNum - 1, rowNum - 1, 0, 1));
  464. //exportExcelService.AddCell($"SUM(C3:C{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  465. //foreach (var r in ranges)
  466. //{
  467. // var cn = ExcelUtil.GetColumnNameByIndex(ci);
  468. // exportExcelService.AddCell($"SUM({cn}3:{cn}{rowNum - 1})", totalRow, ci++, cellStyles.ColumnHeaderStyle, cellType: CellType.Formula);
  469. //}
  470. //#endregion
  471. }
  472. #endregion
  473. #region 导出学校表格私有方法
  474. /// <summary>
  475. /// 导出学校学科平均分统计表
  476. /// </summary>
  477. /// <param name="titlePrefix"></param>
  478. /// <param name="orgName"></param>
  479. /// <param name="list"></param>
  480. /// <param name="examCourses"></param>
  481. /// <param name="wb"></param>
  482. /// <param name="cellStyles"></param>
  483. /// <returns></returns>
  484. private void ExportOrgCourseAvgScore(string titlePrefix, string orgName, List<ExamScoreAvgExportDto> list, List<ExamCourseOutput> examCourses, XSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  485. {
  486. var courses = examCourses.Select(t => t.Course).ToList();
  487. courses.Insert(0, new CourseLiteOutput() { Id = 0, Name = "总分" });
  488. ISheet sheet = wb.CreateSheet("学科平均分");
  489. sheet.DisplayGridlines = false;
  490. var cellStyleNumberP2Blod = wb.CreateCellStyle();
  491. cellStyleNumberP2Blod.CloneStyleFrom(cellStyles.NumberCellStyleP2);
  492. IFont bFont = wb.CreateFont();
  493. bFont.IsBold = true;
  494. bFont.FontName = cellStyles.TitleFontName;
  495. bFont.FontHeightInPoints = 10;
  496. cellStyleNumberP2Blod.SetFont(bFont);
  497. int rowNum = 0;
  498. #region 标题
  499. IRow titleRow = sheet.CreateRow(rowNum++);
  500. titleRow.HeightInPoints = 60;
  501. int ci = 0;
  502. exportExcelService.AddCell($"{orgName}{titlePrefix}各学科均分统计", titleRow, ci++, cellStyles.TitleStyle);
  503. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  504. foreach (var r in courses)
  505. {
  506. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  507. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  508. }
  509. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  510. #endregion
  511. #region 列头
  512. IRow headerRow1 = sheet.CreateRow(rowNum++);
  513. headerRow1.HeightInPoints = 20;
  514. ci = 0;
  515. exportExcelService.AddCell("学校", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 20);
  516. exportExcelService.AddCell("实考\r\n人数", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  517. foreach (var r in courses)
  518. {
  519. exportExcelService.AddCell(r.Name, headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  520. exportExcelService.AddCell("", headerRow1, ci++, cellStyles.ColumnFillHeaderStyle);
  521. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, ci - 2, ci - 1));
  522. }
  523. IRow headerRow2 = sheet.CreateRow(rowNum++);
  524. headerRow2.HeightInPoints = 30;
  525. ci = 0;
  526. exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  527. exportExcelService.AddCell("", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle);
  528. foreach (var r in courses)
  529. {
  530. exportExcelService.AddCell("平均分", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  531. exportExcelService.AddCell("与最高\r\n分差距", headerRow2, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 8);
  532. }
  533. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 0, 0));
  534. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 2, rowNum - 1, 1, 1));
  535. #endregion
  536. #region 数据
  537. // 城乡分组
  538. var urGroup = list.GroupBy(t => t.UrbanRuralType).OrderBy(t => t.Key).ToList();
  539. foreach (var urg in urGroup)
  540. {
  541. // 机构
  542. var orgGroup = urg.GroupBy(t => t.SysOrgId).OrderBy(t => t.FirstOrDefault()?.DataScopeType).ThenBy(t => t.Key).ThenBy(t => t.FirstOrDefault().UrbanRuralType).ToList();
  543. foreach (var orgg in orgGroup)
  544. {
  545. IRow row = sheet.CreateRow(rowNum++);
  546. row.HeightInPoints = 20;
  547. ci = 0;
  548. var forg = orgg.FirstOrDefault(t => t.CourseId == 0);
  549. var isMax = forg.DataScopeType == 1 && forg.UrbanRuralType == UrbanRuralType.NONE;
  550. if (forg.DataScopeType > 1)
  551. {
  552. var txt = forg?.SysOrgName ?? "";
  553. if (forg?.UrbanRuralType != UrbanRuralType.NONE)
  554. {
  555. txt = $"{forg?.UrbanRuralType?.GetDescription()}学校";
  556. }
  557. exportExcelService.AddCell(txt, row, ci++, cellStyles.LeftCellStyle);
  558. }
  559. else
  560. {
  561. exportExcelService.AddCell(forg?.SysOrgName, row, ci++, isMax ? cellStyles.ColumnHeaderStyle : cellStyles.LeftCellStyle);
  562. }
  563. exportExcelService.AddCell(isMax ? null : forg?.TotalCount, row, ci++, isMax ? cellStyles.ColumnHeaderStyle : cellStyles.CenterCellStyle);
  564. foreach (var r in courses)
  565. {
  566. var sr = orgg.FirstOrDefault(t => t.CourseId == r.Id);
  567. exportExcelService.AddCell(sr?.AvgScore, row, ci++, isMax ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  568. exportExcelService.AddCell((isMax || forg.DataScopeType > 1) ? null : sr?.AvgScoreDiff, row, ci++, isMax ? cellStyleNumberP2Blod : cellStyles.NumberCellStyleP2);
  569. }
  570. }
  571. }
  572. #endregion
  573. }
  574. /// <summary>
  575. /// 导出学校学科分数段统计图表
  576. /// </summary>
  577. /// <param name="titlePrefix"></param>
  578. /// <param name="orgName"></param>
  579. /// <param name="courseName"></param>
  580. /// <param name="list"></param>
  581. /// <param name="ranges"></param>
  582. /// <param name="wb"></param>
  583. /// <param name="cellStyles"></param>
  584. private void ExportOrgScoreRange(string titlePrefix, string orgName, string courseName, List<ExamScoreRangeExportDto> list, List<ExamScoreRangeOutput> ranges, XSSFWorkbook wb, ExportExcelCellStyle cellStyles)
  585. {
  586. ISheet sheet = wb.CreateSheet(courseName);
  587. sheet.DisplayGridlines = false;
  588. int rowNum = 0;
  589. #region 标题
  590. IRow titleRow = sheet.CreateRow(rowNum++);
  591. titleRow.HeightInPoints = 60;
  592. int ci = 0;
  593. exportExcelService.AddCell($"{orgName}{titlePrefix}{courseName}分数段统计", titleRow, ci++, cellStyles.TitleStyle);
  594. for (; ci < 15; ci++)
  595. {
  596. exportExcelService.AddCell("", titleRow, ci++, cellStyles.TitleStyle);
  597. }
  598. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, ci - 1));
  599. #endregion
  600. #region 列头
  601. IRow headerRow = sheet.CreateRow(rowNum++);
  602. ci = 0;
  603. exportExcelService.AddCell("分数段", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 14);
  604. exportExcelService.AddCell("本段人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  605. exportExcelService.AddCell("累计人数", headerRow, ci++, cellStyles.ColumnFillHeaderStyle, sheet, 10);
  606. #endregion
  607. #region 数据
  608. decimal scount = 0;
  609. foreach (var r in ranges)
  610. {
  611. var sr = list.FirstOrDefault(t => t.ExamScoreRangeId == r.Id);
  612. IRow row = sheet.CreateRow(rowNum++);
  613. int rci = 0;
  614. exportExcelService.AddCell(r.NickName, row, rci++, cellStyles.CenterCellStyle);
  615. scount += sr?.RangeCount ?? 0;
  616. exportExcelService.AddCell(sr?.RangeCount ?? 0, row, rci++, cellStyles.CenterCellStyle);
  617. exportExcelService.AddCell(scount, row, rci++, cellStyles.CenterCellStyle);
  618. }
  619. #endregion
  620. #region 图表
  621. const int CHART_ROW_COUNT = 18;
  622. XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch();
  623. int startRow = 1;
  624. int endRow = startRow + CHART_ROW_COUNT;
  625. XSSFClientAnchor anchor = (XSSFClientAnchor)drawing.CreateAnchor(0, 0, 0, 0, ci + 1, startRow, ci + 12, endRow);
  626. exportExcelService.CreateBarChart(sheet, drawing, anchor, 2, ranges.Count + 1, 1, $"{titlePrefix}{courseName}分数段统计", orgName, "人数", "分数段");
  627. #endregion
  628. }
  629. #endregion
  630. #region 数据获取私有方法
  631. /// <summary>
  632. /// 获取总分分数段数据
  633. /// </summary>
  634. /// <param name="examPlanId"></param>
  635. /// <param name="gradeId"></param>
  636. /// <param name="sampleType"></param>
  637. /// <returns></returns>
  638. private async Task<List<ExamScoreRangeExportDto>> GetTotalRangeList(int examPlanId, short gradeId, ExamSampleType? sampleType)
  639. {
  640. var items = await sqlRep.SqlQueriesAsync<ExamScoreRangeExportDto>($@"
  641. SELECT T2.urban_rural_type, T2.`name` AS sys_org_name, T3.`name` AS exam_score_range_name, T3.nick_name AS exam_score_range_nick_name, T1.*
  642. FROM
  643. (
  644. 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
  645. FROM
  646. (
  647. 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
  648. FROM exam_score_total AS T1
  649. WHERE exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  650. ) AS T1
  651. GROUP BY T1.sys_org_id, T1.exam_score_range_id
  652. ) AS T1
  653. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  654. JOIN exam_score_range AS T3 ON T1.exam_score_range_id = T3.id
  655. ;
  656. ", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  657. return items;
  658. }
  659. /// <summary>
  660. /// 获取单科分数段数据
  661. /// </summary>
  662. /// <param name="examPlanId"></param>
  663. /// <param name="gradeId"></param>
  664. /// <param name="courseId"></param>
  665. /// <param name="sampleType"></param>
  666. /// <returns></returns>
  667. private async Task<List<ExamScoreRangeExportDto>> GetCourseRangeList(int examPlanId, short gradeId, short courseId, ExamSampleType? sampleType)
  668. {
  669. var items = await sqlRep.SqlQueriesAsync<ExamScoreRangeExportDto>($@"
  670. SELECT T2.urban_rural_type, T2.`name` AS sys_org_name, T3.`name` AS exam_score_range_name, T3.nick_name AS exam_score_range_nick_name, T1.*
  671. FROM
  672. (
  673. 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
  674. FROM
  675. (
  676. 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
  677. FROM exam_score AS T1
  678. WHERE exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.course_id = @courseId AND T1.is_excluded = 0 AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  679. ) AS T1
  680. GROUP BY T1.sys_org_id, T1.exam_score_range_id
  681. ) AS T1
  682. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  683. JOIN exam_score_range AS T3 ON T1.exam_score_range_id = T3.id
  684. ;
  685. ", new { ExamPlanId = examPlanId, GradeId = gradeId, CourseId = courseId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  686. return items;
  687. }
  688. /// <summary>
  689. /// 获取单科分数段数据
  690. /// </summary>
  691. /// <param name="examPlanId"></param>
  692. /// <param name="gradeId"></param>
  693. /// <param name="sampleType"></param>
  694. /// <returns></returns>
  695. private async Task<List<ExamScoreRangeExportDto>> GetCourseRangeList(int examPlanId, short gradeId, ExamSampleType? sampleType)
  696. {
  697. var items = await sqlRep.SqlQueriesAsync<ExamScoreRangeExportDto>($@"
  698. SELECT T2.urban_rural_type, T2.`name` AS sys_org_name, T3.`name` AS exam_score_range_name, T3.nick_name AS exam_score_range_nick_name, T1.*
  699. FROM
  700. (
  701. SELECT T1.sys_org_id, T1.course_id, T1.exam_score_range_id, COUNT(T1.exam_student_id) AS range_count, MAX(T1.total_count) AS total_count
  702. FROM
  703. (
  704. SELECT T1.sys_org_id, T1.course_id, T1.exam_score_range_id, T1.exam_student_id, COUNT(T1.exam_student_id) OVER (PARTITION BY T1.sys_org_id, T1.course_id) AS total_count
  705. FROM exam_score AS T1
  706. WHERE exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  707. ) AS T1
  708. GROUP BY T1.sys_org_id, T1.course_id, T1.exam_score_range_id
  709. ) AS T1
  710. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  711. JOIN exam_score_range AS T3 ON T1.exam_score_range_id = T3.id
  712. ;
  713. ", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  714. return items;
  715. }
  716. /// <summary>
  717. /// 获取平均分数据
  718. /// </summary>
  719. /// <param name="examPlanId"></param>
  720. /// <param name="gradeId"></param>
  721. /// <param name="sampleType"></param>
  722. /// <returns></returns>
  723. private async Task<List<ExamScoreAvgExportDto>> GetAvgScoreList(int examPlanId, short gradeId, ExamSampleType? sampleType)
  724. {
  725. var items = await sqlRep.SqlQueriesAsync<ExamScoreAvgExportDto>($@"
  726. SELECT T1.*
  727. FROM
  728. (
  729. -- 总分
  730. SELECT 1 AS data_scope_type, T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*,
  731. RANK() OVER(ORDER BY T1.avg_score DESC) AS order_in_total,
  732. RANK() OVER(PARTITION BY T2.urban_rural_type ORDER BY T1.avg_score DESC) AS order_in_same,
  733. T1.avg_score - T3.score_max AS avg_score_diff,
  734. T3.score_max
  735. FROM
  736. (
  737. SELECT sys_org_id, 0 AS course_id, COUNT(exam_student_id) AS total_count, AVG(score) AS avg_score
  738. FROM exam_score_total
  739. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  740. GROUP BY sys_org_id
  741. ) AS T1
  742. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  743. JOIN
  744. (
  745. SELECT MAX(T.avg_score) AS score_max
  746. FROM
  747. (
  748. SELECT sys_org_id, AVG(score) AS avg_score
  749. FROM exam_score_total
  750. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  751. GROUP BY sys_org_id
  752. ) AS T
  753. ) AS T3
  754. -- 单科
  755. UNION ALL
  756. SELECT 1 AS data_scope_type, T2.urban_rural_type, T2.`name` AS sys_org_name, T1.*,
  757. RANK() OVER(PARTITION BY T1.course_id ORDER BY T1.avg_score DESC) AS order_in_total,
  758. RANK() OVER(PARTITION BY T1.course_id, T2.urban_rural_type ORDER BY T1.avg_score DESC) AS order_in_same,
  759. T1.avg_score - T3.score_max AS avg_score_diff,
  760. T3.score_max
  761. FROM
  762. (
  763. SELECT sys_org_id, course_id, COUNT(exam_student_id) AS total_count, AVG(score) AS avg_score
  764. FROM exam_score
  765. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  766. GROUP BY sys_org_id, course_id
  767. ) AS T1
  768. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  769. JOIN
  770. (
  771. SELECT T.course_id, MAX(T.avg_score) AS score_max
  772. FROM
  773. (
  774. SELECT sys_org_id, course_id, AVG(score) AS avg_score
  775. FROM exam_score
  776. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  777. GROUP BY sys_org_id, course_id
  778. ) AS T
  779. GROUP BY T.course_id
  780. ) AS T3 ON T1.course_id = T3.course_id
  781. -- 同类小计
  782. UNION ALL
  783. SELECT 2 AS data_scope_type, T1.*,
  784. NULL AS order_in_total,
  785. NULL AS order_in_same,
  786. T1.avg_score - T3.score_max AS avg_score_diff,
  787. T3.score_max
  788. FROM
  789. (
  790. 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
  791. FROM exam_score_total AS T1
  792. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  793. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  794. GROUP BY T2.urban_rural_type
  795. ) AS T1
  796. JOIN
  797. (
  798. SELECT MAX(score) AS score_max FROM exam_score_total WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  799. ) AS T3
  800. -- 单科同类小计
  801. UNION ALL
  802. SELECT 2 AS data_scope_type, T1.*,
  803. NULL AS order_in_total,
  804. NULL AS order_in_same,
  805. T1.avg_score - T3.score_max AS avg_score_diff,
  806. T3.score_max
  807. FROM
  808. (
  809. 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
  810. FROM exam_score AS T1
  811. JOIN sys_org AS T2 ON T1.sys_org_id = T2.id
  812. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (T1.exam_sample_type = @examSampleType OR @examSampleType = 0)
  813. GROUP BY T2.urban_rural_type, T1.course_id
  814. ) AS T1
  815. JOIN (
  816. SELECT course_id, MAX(score) AS score_max
  817. FROM exam_score
  818. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  819. GROUP BY course_id
  820. ) AS T3 ON T1.course_id = T3.course_id
  821. -- 全部合计
  822. UNION ALL
  823. SELECT 3 AS data_scope_type, T1.*,
  824. NULL AS order_in_total,
  825. NULL AS order_in_same,
  826. T1.avg_score - T3.score_max AS avg_score_diff,
  827. T3.score_max
  828. FROM
  829. (
  830. 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
  831. FROM exam_score_total AS T1
  832. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  833. ) AS T1
  834. JOIN
  835. (
  836. SELECT MAX(score) AS score_max FROM exam_score_total WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  837. ) AS T3
  838. -- 单科全部合计
  839. UNION ALL
  840. SELECT 3 AS data_scope_type, T1.*,
  841. NULL AS order_in_total,
  842. NULL AS order_in_same,
  843. T1.avg_score - T3.score_max AS avg_score_diff,
  844. T3.score_max
  845. FROM
  846. (
  847. 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
  848. FROM exam_score AS T1
  849. WHERE T1.exam_plan_id = @examPlanId AND T1.grade_id = @gradeId AND T1.is_excluded = 0 AND T1.score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  850. GROUP BY T1.course_id
  851. ) AS T1
  852. JOIN (
  853. SELECT course_id, MAX(score) AS score_max
  854. FROM exam_score
  855. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  856. GROUP BY course_id
  857. ) AS T3 ON T1.course_id = T3.course_id
  858. -- 总分最高平均分
  859. UNION ALL
  860. SELECT 1 AS data_scope_type,
  861. 0 AS urban_rural_type,
  862. '全区最高分' AS sys_org_name,
  863. 99999999 AS sys_org_id,
  864. 0 AS course_id,
  865. NULL AS total_count,
  866. MAX(T.avg_score) AS avg_score,
  867. NULL AS order_in_total,
  868. NULL AS order_in_same,
  869. NULL AS avg_score_diff,
  870. NULL AS score_max
  871. FROM
  872. (
  873. SELECT sys_org_id, AVG(score) AS avg_score
  874. FROM exam_score_total
  875. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  876. GROUP BY sys_org_id
  877. ) AS T
  878. -- 单科最高平均分
  879. UNION ALL
  880. SELECT 1 AS data_scope_type,
  881. 0 AS urban_rural_type,
  882. '全区最高分' AS sys_org_name,
  883. 99999999 AS sys_org_id,
  884. T.course_id,
  885. NULL AS total_count,
  886. MAX(T.avg_score) AS avg_score,
  887. NULL AS order_in_total,
  888. NULL AS order_in_same,
  889. NULL AS avg_score_diff,
  890. NULL AS score_max
  891. FROM
  892. (
  893. SELECT sys_org_id, course_id, AVG(score) AS avg_score
  894. FROM exam_score
  895. WHERE exam_plan_id = @examPlanId AND grade_id = @gradeId AND is_excluded = 0 AND score > 0 AND (exam_sample_type = @examSampleType OR @examSampleType = 0)
  896. GROUP BY sys_org_id, course_id
  897. ) AS T
  898. GROUP BY T.course_id
  899. ) AS T1
  900. ORDER BY T1.data_scope_type, T1.urban_rural_type, T1.course_id, T1.order_in_total, T1.sys_org_id, T1.total_count
  901. ;
  902. ", new { ExamPlanId = examPlanId, GradeId = gradeId, ExamSampleType = sampleType.HasValue ? (short)sampleType : 0 });
  903. return items;
  904. }
  905. #endregion
  906. }