ExamReportingAvgRangeService.cs 46 KB

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