NceeScoreService.cs 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633
  1. using Furion.JsonSerialization;
  2. using NPOI.SS.UserModel;
  3. using YBEE.EQM.Core;
  4. namespace YBEE.EQM.Application;
  5. /// <summary>
  6. /// 高中成绩管理服务
  7. /// </summary>
  8. public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, ITransient
  9. {
  10. private readonly IRepository<NceeScore> _rep = rep;
  11. private readonly List<short> _chooseCourses = [5, 6, 7, 9];
  12. /// <summary>
  13. /// 上传成绩(仅原始分,适用于五区联考或本区独立赋分)
  14. /// </summary>
  15. /// <param name="filePath"></param>
  16. /// <param name="nceePlanId"></param>
  17. /// <returns></returns>
  18. public async Task UploadOnlyRawScore(string filePath, int nceePlanId)
  19. {
  20. /* ----------------------------------------------------------
  21. * 处理步骤:
  22. * 1.验证表头
  23. * 2.读取数据
  24. * 3.处理学生
  25. * 4.批量插入
  26. ---------------------------------------------------------- */
  27. try
  28. {
  29. #region 1.验证表结构
  30. using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
  31. IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
  32. var sheet = workbook.GetSheetAt(0);
  33. var rows = sheet.GetRowEnumerator();
  34. var evaluator = ExcelUtil.GetEvaluator(workbook);
  35. // 少于2行验证
  36. if (sheet.LastRowNum < 2)
  37. {
  38. //"第一行应为填写说明,第二行应为标题行,请勿修改模板结构。"
  39. return;
  40. }
  41. // 读取表头
  42. rows.MoveNext();
  43. IRow headerRow = (IRow)rows.Current;
  44. int index = 0;
  45. int ORG_ID = index++;
  46. int ORG_NAME = index++;
  47. int NAME_INDEX = index++;
  48. int EXAM_NUMBER_INDEX = index++;
  49. int CLASS_INDEX = index++;
  50. int DIRECTION_INDEX = index++;
  51. int DIRECTION_ID_INDEX = index++;
  52. int COURSE_COMB_INDEX = index++;
  53. int COURSE_COMB_ID_INDEX = index++;
  54. int COURSE_START_INDEX = index;
  55. Dictionary<int, string> headers = new()
  56. {
  57. { ORG_ID, "机构ID" },
  58. { ORG_NAME, "机构" },
  59. { NAME_INDEX, "姓名" },
  60. { EXAM_NUMBER_INDEX, "考号" },
  61. { CLASS_INDEX, "班级号" },
  62. { DIRECTION_INDEX, "选择方向" },
  63. { DIRECTION_ID_INDEX, "选择方向科目ID" },
  64. { COURSE_COMB_INDEX, "选科组合" },
  65. { COURSE_COMB_ID_INDEX, "选科组合ID" },
  66. };
  67. List<string> headerErrors = [];
  68. for (int i = 0; i < COURSE_START_INDEX; i++)
  69. {
  70. if (headerRow.GetCell(i)?.ToString() != headers[i])
  71. {
  72. char letter = (char)('A' + i);
  73. headerErrors.Add(letter.ToString());
  74. }
  75. }
  76. if (headerErrors.Count != 0)
  77. {
  78. string columnErrors = string.Join("、", headerErrors);
  79. //result.ErrorMessage.Add($"第1行标题行{columnErrors}列名错误。从A列开始依次应为抽样类型、学校ID、学校、姓名、证件类型、证件号码、考号、年级、班级。");
  80. //return result;
  81. throw new Exception("列头错误");
  82. }
  83. //result.StructureCorrect = true;
  84. #endregion
  85. #region 2.读取数据
  86. var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
  87. var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
  88. //var orgs = await _rep.Change<SysOrg>().DetachedEntities.Where(t => t.EducationStage == EducationStage.SENIOR_HIGH_SCHOOL_STAGE).ToListAsync();
  89. var courseCombs = await _rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
  90. var courseCombDict = courseCombs.ToDictionary(t => t.Id, t => t);
  91. // 获取需要导入的科目列表
  92. Dictionary<int, Course> courses = [];
  93. int validCellNum = COURSE_START_INDEX;
  94. for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
  95. {
  96. var cn = headerRow.GetCell(gi)?.ToString()?.Trim() ?? "";
  97. if (string.IsNullOrEmpty(cn))
  98. {
  99. continue;
  100. }
  101. courses.Add(gi, courseDict[cn]);
  102. validCellNum = gi + 1;
  103. }
  104. int rn = 1;
  105. List<NceeStudentImportDto> students = [];
  106. List<NceeScoreImportDto> scores = [];
  107. while (rows.MoveNext())
  108. {
  109. rn++;
  110. IRow row = (IRow)rows.Current;
  111. string rv = row.GetCell(0)?.ToString().Trim() ?? "";
  112. if (rv == "")
  113. {
  114. break;
  115. }
  116. NceeStudentImportDto stu = new();
  117. // 机构ID
  118. if (short.TryParse(row.GetCell(ORG_ID)?.ToString(), out short orgId))
  119. {
  120. stu.SysOrgId = orgId;
  121. }
  122. else
  123. {
  124. throw new Exception($"[{rn}]机构ID错误");
  125. }
  126. // 姓名
  127. stu.Name = row.GetCell(NAME_INDEX)?.ToString();
  128. // 考号
  129. stu.ExamNumber = row.GetCell(EXAM_NUMBER_INDEX)?.ToString();
  130. // 班号
  131. if (short.TryParse(row.GetCell(CLASS_INDEX)?.ToString(), out short cn))
  132. {
  133. stu.ClassNumber = cn;
  134. }
  135. else
  136. {
  137. throw new Exception($"[{rn}]班号错误");
  138. }
  139. // 方向ID
  140. if (short.TryParse(row.GetCell(DIRECTION_ID_INDEX)?.ToString(), out short directionId))
  141. {
  142. stu.DirectionCourseId = directionId;
  143. }
  144. //else
  145. //{
  146. // throw new Exception($"[{rn}]方向ID错误");
  147. //}
  148. // 组合ID
  149. if (short.TryParse(row.GetCell(COURSE_COMB_ID_INDEX)?.ToString(), out short courseCombId))
  150. {
  151. stu.NceeCourseCombId = courseCombId;
  152. }
  153. //else
  154. //{
  155. // throw new Exception($"[{rn}]组合ID错误");
  156. //}
  157. // 取各科成绩
  158. int sc = 0;
  159. for (int gi = COURSE_START_INDEX; gi < validCellNum; gi++)
  160. {
  161. var cell = row.GetCell(gi);
  162. if (cell == null || cell.ToString().Trim() == "")
  163. {
  164. continue;
  165. }
  166. if (!courses.ContainsKey(gi)) { continue; }
  167. var course = courses[gi];
  168. NceeScoreImportDto item = new()
  169. {
  170. NceeStudentId = stu.Id,
  171. CourseId = course.Id,
  172. };
  173. if (cell.CellType == CellType.Numeric)
  174. {
  175. item.Score = (decimal)cell.NumericCellValue;
  176. }
  177. else if (cell.CellType == CellType.Formula)
  178. {
  179. var cv = evaluator.Evaluate(cell);
  180. if (cv.CellType == CellType.Numeric)
  181. {
  182. item.Score = (decimal)cv.NumberValue;
  183. }
  184. else
  185. {
  186. continue;
  187. }
  188. }
  189. if (item.Score > 0)
  190. {
  191. if (!_chooseCourses.Any(t => t == course.Id))
  192. {
  193. item.ScoreX = item.Score;
  194. }
  195. scores.Add(item);
  196. sc++;
  197. }
  198. }
  199. if (sc > 0)
  200. {
  201. students.Add(stu);
  202. }
  203. }
  204. workbook.Close();
  205. fs.Close();
  206. #endregion
  207. #region 3.处理学生
  208. // 清理学生
  209. string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
  210. await _rep.SqlNonQueryAsync(deleteStudentSql);
  211. var uid = CurrentSysUserInfo.SysUserId;
  212. if (uid == 0)
  213. {
  214. uid = 1;
  215. }
  216. List<string> insertValues = [];
  217. int scount = students.Count;
  218. for (int i = 0; i < scount; i++)
  219. {
  220. var t = students[i];
  221. string dcid = "NULL";
  222. if (t.DirectionCourseId.HasValue)
  223. {
  224. dcid = t.DirectionCourseId.Value.ToString();
  225. }
  226. string nccid = "NULL";
  227. if (t.NceeCourseCombId.HasValue)
  228. {
  229. nccid = t.NceeCourseCombId.Value.ToString();
  230. }
  231. insertValues.Add($"({t.Id}, {nceePlanId}, {t.SysOrgId}, {plan.GradeId}, {t.ClassNumber}, '{t.ExamNumber}', '{t.Name}', {dcid}, {nccid}, 0, 0)");
  232. if ((i + 1) % 1000 == 0 || i == scount - 1)
  233. {
  234. string insertSql = $@"
  235. INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES
  236. {string.Join(",", insertValues)}
  237. ";
  238. await _rep.SqlNonQueryAsync(insertSql);
  239. insertValues.Clear();
  240. }
  241. }
  242. #endregion
  243. #region 4.批量导入
  244. // 清理成绩
  245. string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
  246. await _rep.SqlNonQueryAsync(deleteScoreSql);
  247. insertValues.Clear();
  248. scount = scores.Count;
  249. for (int i = 0; i < scount; i++)
  250. {
  251. var t = scores[i];
  252. insertValues.Add($"({t.Id}, {nceePlanId}, {t.NceeStudentId}, {t.CourseId}, {t.Score}, {t.ScoreX})");
  253. if ((i + 1) % 1000 == 0 || i == scount - 1)
  254. {
  255. string insertSql = $@"
  256. INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, score_x) VALUES
  257. {string.Join(",", insertValues)}
  258. ";
  259. await _rep.SqlNonQueryAsync(insertSql);
  260. insertValues.Clear();
  261. }
  262. }
  263. #endregion
  264. }
  265. catch (Exception ex)
  266. {
  267. throw new Exception(ex.Message);
  268. }
  269. finally
  270. {
  271. File.Delete(filePath);
  272. }
  273. }
  274. /// <summary>
  275. /// 上传成绩(带转换分和等级,适用于六校联考)
  276. /// </summary>
  277. /// <param name="filePath"></param>
  278. /// <param name="nceePlanId"></param>
  279. /// <returns></returns>
  280. /// <exception cref="Exception"></exception>
  281. public async Task UploadWithConvertScore(string filePath, int nceePlanId)
  282. {
  283. /* ----------------------------------------------------------
  284. * 处理步骤:
  285. * 1.验证表头
  286. * 2.读取数据
  287. * 3.处理学生
  288. * 4.批量插入
  289. ---------------------------------------------------------- */
  290. try
  291. {
  292. #region 1.验证表结构
  293. using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
  294. IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
  295. var sheet = workbook.GetSheetAt(0);
  296. var rows = sheet.GetRowEnumerator();
  297. var evaluator = ExcelUtil.GetEvaluator(workbook);
  298. // 少于2行验证
  299. if (sheet.LastRowNum < 2)
  300. {
  301. //"第一行应为填写说明,第二行应为标题行,请勿修改模板结构。"
  302. return;
  303. }
  304. // 读取表头
  305. rows.MoveNext();
  306. IRow headerRow = (IRow)rows.Current;
  307. int index = 0;
  308. int ORG_ID = index++;
  309. int ORG_NAME = index++;
  310. int NAME_INDEX = index++;
  311. int EXAM_NUMBER_INDEX = index++;
  312. int CLASS_INDEX = index++;
  313. int DIRECTION_INDEX = index++;
  314. int DIRECTION_ID_INDEX = index++;
  315. int COURSE_COMB_INDEX = index++;
  316. int COURSE_COMB_ID_INDEX = index++;
  317. int COURSE_START_INDEX = index;
  318. Dictionary<int, string> headers = new()
  319. {
  320. { ORG_ID, "机构ID" },
  321. { ORG_NAME, "机构" },
  322. { NAME_INDEX, "姓名" },
  323. { EXAM_NUMBER_INDEX, "考号" },
  324. { CLASS_INDEX, "班级号" },
  325. { DIRECTION_INDEX, "选择方向" },
  326. { DIRECTION_ID_INDEX, "选择方向科目ID" },
  327. { COURSE_COMB_INDEX, "选科组合" },
  328. { COURSE_COMB_ID_INDEX, "选科组合ID" },
  329. };
  330. List<string> headerErrors = [];
  331. for (int i = 0; i < COURSE_START_INDEX; i++)
  332. {
  333. if (headerRow.GetCell(i)?.ToString() != headers[i])
  334. {
  335. char letter = (char)('A' + i);
  336. headerErrors.Add(letter.ToString());
  337. }
  338. }
  339. if (headerErrors.Count != 0)
  340. {
  341. string columnErrors = string.Join("、", headerErrors);
  342. //result.ErrorMessage.Add($"第1行标题行{columnErrors}列名错误。从A列开始依次应为抽样类型、学校ID、学校、姓名、证件类型、证件号码、考号、年级、班级。");
  343. //return result;
  344. throw new Exception("列头错误");
  345. }
  346. //result.StructureCorrect = true;
  347. #endregion
  348. #region 2.读取数据
  349. var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
  350. var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
  351. //var orgs = await _rep.Change<SysOrg>().DetachedEntities.Where(t => t.EducationStage == EducationStage.SENIOR_HIGH_SCHOOL_STAGE).ToListAsync();
  352. var courseCombs = await _rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
  353. var courseCombDict = courseCombs.ToDictionary(t => t.Id, t => t);
  354. var convertGrades = await _rep.Change<NceeConvertGrade>().DetachedEntities.ToListAsync();
  355. var convertGradeDict = convertGrades.ToDictionary(t => t.Name);
  356. // 获取需要导入的科目列表
  357. Dictionary<int, NceeScoreImportCourseDto> courses = new();
  358. int validCellNum = COURSE_START_INDEX;
  359. for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
  360. {
  361. var cn = headerRow.GetCell(gi)?.ToString()?.Trim() ?? "";
  362. if (string.IsNullOrEmpty(cn))
  363. {
  364. continue;
  365. }
  366. string tm = cn[^1..].ToUpper();
  367. var cnn = cn;
  368. if (tm == "X" || tm == "G")
  369. {
  370. cnn = cn[..^1];
  371. }
  372. courses.Add(gi, new()
  373. {
  374. CourseName = cn,
  375. Course = courseDict[cnn],
  376. IsRawScore = tm != "X" && tm != "G",
  377. IsConvertGrade = tm == "G",
  378. IsConvertScore = tm == "X",
  379. });
  380. validCellNum = gi + 1;
  381. }
  382. int rn = 1;
  383. List<NceeStudentImportDto> students = new();
  384. List<NceeScoreImportDto> scores = new();
  385. while (rows.MoveNext())
  386. {
  387. rn++;
  388. IRow row = (IRow)rows.Current;
  389. string rv = row.GetCell(0)?.ToString().Trim() ?? "";
  390. if (rv == "")
  391. {
  392. break;
  393. }
  394. NceeStudentImportDto stu = new();
  395. // 机构ID
  396. if (short.TryParse(row.GetCell(ORG_ID)?.ToString(), out short orgId))
  397. {
  398. stu.SysOrgId = orgId;
  399. }
  400. else
  401. {
  402. throw new Exception($"[{rn}]机构ID错误");
  403. }
  404. // 姓名
  405. stu.Name = row.GetCell(NAME_INDEX)?.ToString();
  406. // 考号
  407. stu.ExamNumber = row.GetCell(EXAM_NUMBER_INDEX)?.ToString();
  408. // 班号
  409. if (short.TryParse(row.GetCell(CLASS_INDEX)?.ToString(), out short cn))
  410. {
  411. stu.ClassNumber = cn;
  412. }
  413. else
  414. {
  415. throw new Exception($"[{rn}]班号错误");
  416. }
  417. // 方向ID
  418. if (short.TryParse(row.GetCell(DIRECTION_ID_INDEX)?.ToString(), out short directionId))
  419. {
  420. stu.DirectionCourseId = directionId;
  421. }
  422. //else
  423. //{
  424. // throw new Exception($"[{rn}]方向ID错误");
  425. //}
  426. // 组合ID
  427. if (short.TryParse(row.GetCell(COURSE_COMB_ID_INDEX)?.ToString(), out short courseCombId))
  428. {
  429. stu.NceeCourseCombId = courseCombId;
  430. }
  431. //else
  432. //{
  433. // throw new Exception($"[{rn}]组合ID错误");
  434. //}
  435. // 取各科成绩
  436. List<NceeScoreImportDto> courseScores = [];
  437. for (int gi = COURSE_START_INDEX; gi < validCellNum; gi++)
  438. {
  439. var cell = row.GetCell(gi);
  440. if (cell == null || cell.ToString().Trim() == "")
  441. {
  442. continue;
  443. }
  444. if (!courses.ContainsKey(gi)) { continue; }
  445. var course = courses[gi];
  446. NceeScoreImportDto item;
  447. if (course.IsConvertScore || course.IsConvertGrade)
  448. {
  449. item = courseScores.FirstOrDefault(t => t.CourseId == course.Course.Id);
  450. }
  451. else
  452. {
  453. item = new()
  454. {
  455. NceeStudentId = stu.Id,
  456. CourseId = course.Course.Id,
  457. };
  458. }
  459. if (course.IsConvertGrade == false)
  460. {
  461. decimal score = 0;
  462. if (cell.CellType == CellType.Numeric)
  463. {
  464. score = (decimal)cell.NumericCellValue;
  465. }
  466. else if (cell.CellType == CellType.Formula)
  467. {
  468. var cv = evaluator.Evaluate(cell);
  469. if (cv.CellType == CellType.Numeric)
  470. {
  471. score = (decimal)cv.NumberValue;
  472. }
  473. else
  474. {
  475. continue;
  476. }
  477. }
  478. if (score > 0)
  479. {
  480. if (course.IsRawScore == true)
  481. {
  482. item.Score = score;
  483. if (!_chooseCourses.Any(t => t == course.Course.Id))
  484. {
  485. item.ScoreX = score;
  486. }
  487. courseScores.Add(item);
  488. }
  489. else if (course.IsConvertScore == true)
  490. {
  491. item.ScoreX = score;
  492. }
  493. }
  494. }
  495. else
  496. {
  497. var gn = cell.ToString().Trim().ToUpper();
  498. var g = convertGradeDict[gn];
  499. item.NceeConvertGradeId = g.Id;
  500. item.NceeConvertGradeName = g.Name;
  501. }
  502. }
  503. if (courseScores.Count > 0)
  504. {
  505. stu.Score = courseScores.Sum(t => t.Score);
  506. stu.ScoreX = courseScores.Sum(t => t.ScoreX);
  507. students.Add(stu);
  508. scores.AddRange(courseScores);
  509. }
  510. }
  511. workbook.Close();
  512. fs.Close();
  513. #endregion
  514. #region 3.处理学生
  515. // 清理学生
  516. string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
  517. await _rep.SqlNonQueryAsync(deleteStudentSql);
  518. var uid = CurrentSysUserInfo.SysUserId;
  519. if (uid == 0)
  520. {
  521. uid = 1;
  522. }
  523. List<string> insertValues = new();
  524. int scount = students.Count;
  525. for (int i = 0; i < scount; i++)
  526. {
  527. var t = students[i];
  528. string dcid = "NULL";
  529. if (t.DirectionCourseId.HasValue)
  530. {
  531. dcid = t.DirectionCourseId.Value.ToString();
  532. }
  533. string nccid = "NULL";
  534. if (t.NceeCourseCombId.HasValue)
  535. {
  536. nccid = t.NceeCourseCombId.Value.ToString();
  537. }
  538. insertValues.Add($"({t.Id}, {nceePlanId}, {t.SysOrgId}, {plan.GradeId}, {t.ClassNumber}, '{t.ExamNumber}', '{t.Name}', {dcid}, {nccid}, {t.Score}, {t.ScoreX})");
  539. if ((i + 1) % 1000 == 0 || i == scount - 1)
  540. {
  541. string insertSql = $@"
  542. INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES
  543. {string.Join(",", insertValues)}
  544. ";
  545. await _rep.SqlNonQueryAsync(insertSql);
  546. insertValues.Clear();
  547. }
  548. }
  549. #endregion
  550. #region 4.批量导入
  551. // 清理成绩
  552. string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
  553. await _rep.SqlNonQueryAsync(deleteScoreSql);
  554. insertValues.Clear();
  555. scount = scores.Count;
  556. for (int i = 0; i < scount; i++)
  557. {
  558. var t = scores[i];
  559. string ncgid = "NULL";
  560. if (t.NceeConvertGradeId.HasValue)
  561. {
  562. ncgid = t.NceeConvertGradeId.Value.ToString();
  563. }
  564. insertValues.Add($"({t.Id}, {nceePlanId}, {t.NceeStudentId}, {t.CourseId}, {ncgid}, '{t.NceeConvertGradeName}', {t.Score}, {t.ScoreX})");
  565. if ((i + 1) % 1000 == 0 || i == scount - 1)
  566. {
  567. string insertSql = $@"
  568. INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_convert_grade_id, ncee_convert_grade_name, score, score_x) VALUES
  569. {string.Join(",", insertValues)}
  570. ";
  571. await _rep.SqlNonQueryAsync(insertSql);
  572. insertValues.Clear();
  573. }
  574. }
  575. #endregion
  576. }
  577. catch (Exception ex)
  578. {
  579. throw new Exception(ex.Message);
  580. }
  581. finally
  582. {
  583. File.Delete(filePath);
  584. }
  585. }
  586. /// <summary>
  587. /// 上传未选科原始成绩(适用于高一未选科)
  588. /// </summary>
  589. /// <param name="filePath"></param>
  590. /// <param name="nceePlanId"></param>
  591. /// <returns></returns>
  592. /// <exception cref="Exception"></exception>
  593. public async Task UploadNoDirectionCourse(string filePath, int nceePlanId)
  594. {
  595. /* ----------------------------------------------------------
  596. * 处理步骤:
  597. * 1.验证表头
  598. * 2.读取数据
  599. * 3.处理学生
  600. * 4.批量插入
  601. ---------------------------------------------------------- */
  602. try
  603. {
  604. #region 1.验证表结构
  605. using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
  606. IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
  607. var sheet = workbook.GetSheetAt(0);
  608. var rows = sheet.GetRowEnumerator();
  609. var evaluator = ExcelUtil.GetEvaluator(workbook);
  610. // 少于2行验证
  611. if (sheet.LastRowNum < 2)
  612. {
  613. //"第一行应为填写说明,第二行应为标题行,请勿修改模板结构。"
  614. return;
  615. }
  616. // 读取表头
  617. rows.MoveNext();
  618. IRow headerRow = (IRow)rows.Current;
  619. int index = 0;
  620. int ORG_ID = index++;
  621. int ORG_NAME = index++;
  622. int NAME_INDEX = index++;
  623. int EXAM_NUMBER_INDEX = index++;
  624. int CLASS_INDEX = index++;
  625. int COURSE_START_INDEX = index;
  626. Dictionary<int, string> headers = new()
  627. {
  628. { ORG_ID, "机构ID" },
  629. { ORG_NAME, "机构" },
  630. { NAME_INDEX, "姓名" },
  631. { EXAM_NUMBER_INDEX, "考号" },
  632. { CLASS_INDEX, "班级号" },
  633. };
  634. List<string> headerErrors = [];
  635. for (int i = 0; i < COURSE_START_INDEX; i++)
  636. {
  637. if (headerRow.GetCell(i)?.ToString() != headers[i])
  638. {
  639. char letter = (char)('A' + i);
  640. headerErrors.Add(letter.ToString());
  641. }
  642. }
  643. if (headerErrors.Count != 0)
  644. {
  645. string columnErrors = string.Join("、", headerErrors);
  646. //result.ErrorMessage.Add($"第1行标题行{columnErrors}列名错误。从A列开始依次应为抽样类型、学校ID、学校、姓名、证件类型、证件号码、考号、年级、班级。");
  647. //return result;
  648. throw new Exception("列头错误");
  649. }
  650. //result.StructureCorrect = true;
  651. #endregion
  652. #region 2.读取数据
  653. var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
  654. var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
  655. // 获取需要导入的科目列表
  656. Dictionary<int, Course> courses = [];
  657. int validCellNum = COURSE_START_INDEX;
  658. for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
  659. {
  660. var cn = headerRow.GetCell(gi)?.ToString()?.Trim() ?? "";
  661. if (string.IsNullOrEmpty(cn))
  662. {
  663. continue;
  664. }
  665. courses.Add(gi, courseDict[cn]);
  666. validCellNum = gi + 1;
  667. }
  668. int rn = 1;
  669. List<NceeStudentImportDto> students = [];
  670. List<NceeScoreImportDto> scores = [];
  671. while (rows.MoveNext())
  672. {
  673. rn++;
  674. IRow row = (IRow)rows.Current;
  675. string rv = row.GetCell(0)?.ToString().Trim() ?? "";
  676. if (rv == "")
  677. {
  678. break;
  679. }
  680. NceeStudentImportDto stu = new();
  681. // 机构ID
  682. if (short.TryParse(row.GetCell(ORG_ID)?.ToString(), out short orgId))
  683. {
  684. stu.SysOrgId = orgId;
  685. }
  686. else
  687. {
  688. throw new Exception($"[{rn}]机构ID错误");
  689. }
  690. // 姓名
  691. stu.Name = row.GetCell(NAME_INDEX)?.ToString();
  692. // 考号
  693. stu.ExamNumber = row.GetCell(EXAM_NUMBER_INDEX)?.ToString();
  694. // 班号
  695. if (short.TryParse(row.GetCell(CLASS_INDEX)?.ToString(), out short cn))
  696. {
  697. stu.ClassNumber = cn;
  698. }
  699. else
  700. {
  701. throw new Exception($"[{rn}]班号错误");
  702. }
  703. // 取各科成绩
  704. List<NceeScoreImportDto> courseScores = [];
  705. for (int gi = COURSE_START_INDEX; gi < validCellNum; gi++)
  706. {
  707. var cell = row.GetCell(gi);
  708. if (cell == null || cell.ToString().Trim() == "")
  709. {
  710. continue;
  711. }
  712. if (!courses.ContainsKey(gi)) { continue; }
  713. var course = courses[gi];
  714. NceeScoreImportDto item = new()
  715. {
  716. NceeStudentId = stu.Id,
  717. CourseId = course.Id,
  718. };
  719. decimal score = 0;
  720. if (cell.CellType == CellType.Numeric)
  721. {
  722. score = (decimal)cell.NumericCellValue;
  723. }
  724. else if (cell.CellType == CellType.Formula)
  725. {
  726. var cv = evaluator.Evaluate(cell);
  727. if (cv.CellType == CellType.Numeric)
  728. {
  729. score = (decimal)cv.NumberValue;
  730. }
  731. else
  732. {
  733. continue;
  734. }
  735. }
  736. if (score > 0)
  737. {
  738. item.Score = score;
  739. item.ScoreX = score;
  740. courseScores.Add(item);
  741. }
  742. }
  743. if (courseScores.Count > 0)
  744. {
  745. stu.Score = courseScores.Sum(t => t.Score);
  746. stu.ScoreX = courseScores.Sum(t => t.ScoreX);
  747. students.Add(stu);
  748. scores.AddRange(courseScores);
  749. }
  750. }
  751. workbook.Close();
  752. fs.Close();
  753. #endregion
  754. #region 3.处理学生
  755. // 清理学生
  756. string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
  757. await _rep.SqlNonQueryAsync(deleteStudentSql);
  758. var uid = CurrentSysUserInfo.SysUserId;
  759. if (uid == 0)
  760. {
  761. uid = 1;
  762. }
  763. List<string> insertValues = new();
  764. int scount = students.Count;
  765. for (int i = 0; i < scount; i++)
  766. {
  767. var t = students[i];
  768. insertValues.Add($"({t.Id}, {nceePlanId}, {t.SysOrgId}, {plan.GradeId}, {t.ClassNumber}, '{t.ExamNumber}', '{t.Name}', 99, 0, {t.Score}, {t.ScoreX})");
  769. if ((i + 1) % 1000 == 0 || i == scount - 1)
  770. {
  771. string insertSql = $@"
  772. INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES
  773. {string.Join(",", insertValues)}
  774. ";
  775. await _rep.SqlNonQueryAsync(insertSql);
  776. insertValues.Clear();
  777. }
  778. }
  779. #endregion
  780. #region 4.批量导入
  781. // 清理成绩
  782. string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
  783. await _rep.SqlNonQueryAsync(deleteScoreSql);
  784. insertValues.Clear();
  785. scount = scores.Count;
  786. for (int i = 0; i < scount; i++)
  787. {
  788. var t = scores[i];
  789. insertValues.Add($"({t.Id}, {nceePlanId}, {t.NceeStudentId}, {t.CourseId}, NULL, '', {t.Score}, {t.ScoreX})");
  790. if ((i + 1) % 1000 == 0 || i == scount - 1)
  791. {
  792. string insertSql = $@"
  793. INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_convert_grade_id, ncee_convert_grade_name, score, score_x) VALUES
  794. {string.Join(",", insertValues)}
  795. ";
  796. await _rep.SqlNonQueryAsync(insertSql);
  797. insertValues.Clear();
  798. }
  799. }
  800. #endregion
  801. }
  802. catch (Exception ex)
  803. {
  804. throw new Exception(ex.Message);
  805. }
  806. finally
  807. {
  808. File.Delete(filePath);
  809. }
  810. }
  811. /// <summary>
  812. /// 执行模拟划线
  813. /// </summary>
  814. /// <param name="nceePlanId"></param>
  815. /// <returns></returns>
  816. public async Task Execute(int nceePlanId)
  817. {
  818. var nceePlan = await _rep.Change<NceePlan>().FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001);
  819. var nceePlanConfig = JSON.Deserialize<NceePlanConfig>(nceePlan.Config);
  820. // 执行分数转换
  821. if (nceePlanConfig.ConvertEnabled)
  822. {
  823. await ExecuteScoreConvert(nceePlanId);
  824. }
  825. // 更新学生排名
  826. await ExecuteUpdateOrder(nceePlanId);
  827. // 执行划线
  828. await ExecuteLine(nceePlanId, nceePlanConfig);
  829. }
  830. #region 私有方法
  831. /// <summary>
  832. /// 执行划线
  833. /// </summary>
  834. /// <param name="nceePlanId"></param>
  835. /// <param name="config"></param>
  836. /// <returns></returns>
  837. private async Task ExecuteLine(int nceePlanId, NceePlanConfig config)
  838. {
  839. var baseLines = await _rep.Change<NceeBaseLine>().Where(t => t.NceePlanId == nceePlanId).OrderBy(t => t.NceeLineLevel).ToListAsync();
  840. // 单科有效分
  841. List<NceeCourseLineScore> courseLineScores = [];
  842. #region 计算总有效分(通过比例计算)
  843. if (config.CalcTotalLineScoreEnabled)
  844. {
  845. foreach (var line in baseLines)
  846. {
  847. // 计算总有效分
  848. var lineScoreX = await _rep.SqlScalarAsync<decimal>(@$"
  849. SELECT MIN(score_x) AS score_x
  850. FROM
  851. (
  852. SELECT ROW_NUMBER() OVER(ORDER BY score_x DESC) AS rn, score_x
  853. FROM ncee_student
  854. WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId
  855. ) AS T1
  856. WHERE T1.rn <= (SELECT COUNT(1) FROM ncee_student WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId) * @lineRate
  857. ", new { NceePlanId = nceePlanId, line.DirectionCourseId, line.LineRate });
  858. line.LineScoreX = lineScoreX;
  859. }
  860. await _rep.Change<NceeBaseLine>().UpdateNowAsync(baseLines);
  861. }
  862. #endregion
  863. #region 计算单科有效分
  864. if (config.CalcCourseLineScoreEnabled)
  865. {
  866. // 1.总分排序,合并单科独立排序
  867. // 2.取总分上线分对应行的单科成绩为有效分
  868. foreach (var line in baseLines)
  869. {
  870. // 计算单科有效分
  871. var courseLineScoreX = await _rep.SqlQueryAsync<NceeCourseLineScoreCalcDto>($@"
  872. SELECT
  873. MIN(T1.yuwen_score_x) AS yuwen_score_x, -- 语文
  874. MIN(T1.shuxue_score_x) AS shuxue_score_x, -- 数学
  875. MIN(T1.yingyu_score_x) AS yingyu_score_x, -- 英语
  876. MIN(T1.fangxiang_score_x) AS fangxiang_score_x, -- 方向(物理或历史)
  877. MIN(T1.zonghe_score_x) AS zonghe_score_x -- 综合
  878. FROM
  879. (
  880. SELECT T1.rn,
  881. T2.score_x AS yuwen_score_x,
  882. T3.score_x AS shuxue_score_x,
  883. T4.score_x AS yingyu_score_x,
  884. T5.score_x AS fangxiang_score_x,
  885. T6.score_x AS zonghe_score_x,
  886. IFNULL(T2.score_x, 0) + IFNULL(T3.score_x, 0) + IFNULL(T4.score_x, 0) + IFNULL(T5.score_x, 0) + IFNULL(T6.score_x, 0) AS score_x
  887. FROM
  888. (
  889. SELECT ROW_NUMBER() OVER(ORDER BY score_x DESC) AS rn
  890. FROM ncee_student AS T1
  891. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId
  892. ) AS T1
  893. LEFT JOIN
  894. (
  895. SELECT ROW_NUMBER() OVER(ORDER BY T2.score_x DESC) AS rn, T2.score_x
  896. FROM ncee_student AS T1
  897. JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  898. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id = 1
  899. ) AS T2 ON T1.rn = T2.rn
  900. LEFT JOIN
  901. (
  902. SELECT ROW_NUMBER() OVER(ORDER BY T2.score_x DESC) AS rn, T2.score_x
  903. FROM ncee_student AS T1
  904. JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  905. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id = 2
  906. ) AS T3 ON T1.rn = T3.rn
  907. LEFT JOIN
  908. (
  909. SELECT ROW_NUMBER() OVER(ORDER BY T2.score_x DESC) AS rn, T2.score_x
  910. FROM ncee_student AS T1
  911. JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  912. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id = 3
  913. ) AS T4 ON T1.rn = T4.rn
  914. LEFT JOIN
  915. (
  916. SELECT ROW_NUMBER() OVER(ORDER BY T2.score_x DESC) AS rn, T2.score_x
  917. FROM ncee_student AS T1
  918. JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  919. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id = @directionCourseId
  920. ) AS T5 ON T1.rn = T5.rn
  921. LEFT JOIN
  922. (
  923. SELECT ROW_NUMBER() OVER(ORDER BY T1.score_x DESC) AS rn, t1.score_x
  924. FROM
  925. (
  926. SELECT T2.ncee_student_id, SUM(T2.score_x) AS score_x
  927. FROM ncee_student AS T1
  928. JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  929. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id IN(5, 6, 7, 9)
  930. GROUP BY T2.ncee_student_id
  931. ) AS T1
  932. ) AS T6 ON T1.rn = T6.rn
  933. ) AS T1
  934. WHERE T1.score_x >= @totalLineScoreX
  935. ", new { NceePlanId = nceePlanId, line.DirectionCourseId, TotalLineScoreX = line.LineScoreX });
  936. var cline = courseLineScoreX.FirstOrDefault();
  937. if (cline != null)
  938. {
  939. courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 1, LineScoreX = cline.YuwenScoreX });
  940. courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 2, LineScoreX = cline.ShuxueScoreX });
  941. courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 3, LineScoreX = cline.YingyuScoreX });
  942. courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = line.DirectionCourseId, LineScoreX = cline.FangxiangScoreX });
  943. courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 101, LineScoreX = cline.ZongheScoreX });
  944. }
  945. }
  946. await _rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
  947. List<NceeCourseLineScore> totalLines = [];
  948. foreach (var line in baseLines)
  949. {
  950. totalLines.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 100, LineScoreX = line.LineScoreX });
  951. }
  952. await _rep.Change<NceeCourseLineScore>().InsertNowAsync(totalLines);
  953. await _rep.Change<NceeCourseLineScore>().InsertNowAsync(courseLineScores);
  954. }
  955. else
  956. {
  957. courseLineScores = await _rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ToListAsync();
  958. }
  959. #endregion
  960. #region 计算总分上线
  961. // 清除数据
  962. await _rep.Change<NceeLineTotal>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
  963. // 总分划线
  964. foreach (var line in baseLines)
  965. {
  966. // 整体 - 方向
  967. await _rep.SqlNonQueryAsync($@"
  968. INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, grade_id, line_count, total_count, line_rate)
  969. SELECT {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
  970. FROM
  971. (
  972. SELECT
  973. T1.direction_course_id,
  974. T1.grade_id,
  975. COUNT(1) AS line_count,
  976. (
  977. SELECT COUNT(1) AS total_count
  978. FROM ncee_student
  979. WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId AND score_x > 0
  980. ) AS total_count
  981. FROM ncee_student AS T1
  982. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T1.score_x >= @lineScoreX
  983. GROUP BY T1.direction_course_id, T1.grade_id
  984. ) AS T1
  985. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
  986. // 机构 - 方向
  987. await _rep.SqlNonQueryAsync($@"
  988. INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, sys_org_id, grade_id, line_count, total_count, line_rate)
  989. SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
  990. FROM
  991. (
  992. SELECT
  993. T1.direction_course_id,
  994. T1.sys_org_id,
  995. T1.grade_id,
  996. COUNT(1) AS line_count,
  997. T2.total_count
  998. FROM ncee_student AS T1
  999. JOIN
  1000. (
  1001. SELECT sys_org_id, COUNT(1) AS total_count
  1002. FROM ncee_student
  1003. WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId AND score_x > 0
  1004. GROUP BY sys_org_id
  1005. ) AS T2 ON T1.sys_org_id = T2.sys_org_id
  1006. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T1.score_x >= @lineScoreX
  1007. GROUP BY T1.direction_course_id, T1.sys_org_id, T1.grade_id
  1008. ) AS T1
  1009. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
  1010. // 机构 - 方向 - 组合
  1011. await _rep.SqlNonQueryAsync($@"
  1012. INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate)
  1013. SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
  1014. FROM
  1015. (
  1016. SELECT
  1017. T1.direction_course_id,
  1018. T1.ncee_course_comb_id,
  1019. T1.sys_org_id,
  1020. T1.grade_id,
  1021. COUNT(1) AS line_count,
  1022. T2.total_count
  1023. FROM ncee_student AS T1
  1024. JOIN
  1025. (
  1026. SELECT ncee_course_comb_id, sys_org_id, COUNT(1) AS total_count
  1027. FROM ncee_student
  1028. WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId AND score_x > 0
  1029. GROUP BY ncee_course_comb_id, sys_org_id
  1030. ) AS T2 ON T1.sys_org_id = T2.sys_org_id
  1031. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T1.ncee_course_comb_id = T2.ncee_course_comb_id AND T1.score_x >= @lineScoreX
  1032. GROUP BY T1.direction_course_id, T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id
  1033. ) AS T1
  1034. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
  1035. // 机构 - 班级 - 方向 - 组合
  1036. await _rep.SqlNonQueryAsync($@"
  1037. INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate)
  1038. SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
  1039. FROM
  1040. (
  1041. SELECT
  1042. T1.direction_course_id,
  1043. T1.ncee_course_comb_id,
  1044. T1.sys_org_id,
  1045. T1.grade_id,
  1046. T1.class_number,
  1047. COUNT(1) AS line_count,
  1048. T2.total_count
  1049. FROM ncee_student AS T1
  1050. JOIN
  1051. (
  1052. SELECT ncee_course_comb_id, sys_org_id, class_number, COUNT(1) AS total_count
  1053. FROM ncee_student
  1054. WHERE ncee_plan_id = @nceePlanId AND direction_course_id = @directionCourseId AND score_x > 0
  1055. GROUP BY ncee_course_comb_id, sys_org_id, class_number
  1056. ) AS T2 ON T1.sys_org_id = T2.sys_org_id
  1057. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T1.ncee_course_comb_id = T2.ncee_course_comb_id AND T1.class_number = T2.class_number AND T1.score_x >= @lineScoreX
  1058. GROUP BY T1.direction_course_id, T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number
  1059. ) AS T1
  1060. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
  1061. }
  1062. #endregion
  1063. #region 计算科目有效分上线
  1064. // 清除数据
  1065. await _rep.Change<NceeLineCourse>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
  1066. // 总分划线
  1067. foreach (var line in baseLines)
  1068. {
  1069. var courseLines = courseLineScores.Where(t => t.DirectionCourseId == line.DirectionCourseId && t.NceeLineLevel == line.NceeLineLevel).ToList();
  1070. foreach (var courseLine in courseLines)
  1071. {
  1072. string inCourses = courseLine.CourseId.ToString();
  1073. if (courseLine.CourseId == 101)
  1074. {
  1075. inCourses = "5, 6, 7, 9";
  1076. }
  1077. // 单上线:整体 - 方向
  1078. await _rep.SqlNonQueryAsync($@"
  1079. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1080. SELECT {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
  1081. FROM
  1082. (
  1083. SELECT
  1084. T1.grade_id,
  1085. COUNT(1) AS line_count,
  1086. (
  1087. SELECT COUNT(1) AS total_count
  1088. FROM
  1089. (
  1090. SELECT DISTINCT T1.grade_id, T2.ncee_student_id
  1091. FROM ncee_student AS T1
  1092. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1093. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1094. GROUP BY T1.grade_id, T2.ncee_student_id
  1095. ) AS T1
  1096. ) AS total_count
  1097. FROM
  1098. (
  1099. SELECT T1.grade_id, T2.ncee_student_id, SUM(T2.score_x) AS course_score_x
  1100. FROM ncee_student AS T1
  1101. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1102. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1103. GROUP BY T1.grade_id, T2.ncee_student_id
  1104. ) AS T1
  1105. WHERE T1.course_score_x >= @courseLineScoreX
  1106. GROUP BY T1.grade_id
  1107. ) AS T1
  1108. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1109. // 双上线:整体 - 方向
  1110. await _rep.SqlNonQueryAsync($@"
  1111. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1112. SELECT {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
  1113. FROM
  1114. (
  1115. SELECT
  1116. T1.grade_id,
  1117. COUNT(1) AS line_count,
  1118. (
  1119. SELECT COUNT(1) AS total_count
  1120. FROM
  1121. (
  1122. SELECT DISTINCT T1.grade_id, T2.ncee_student_id
  1123. FROM ncee_student AS T1
  1124. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1125. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1126. GROUP BY T1.grade_id, T2.ncee_student_id
  1127. ) AS T1
  1128. ) AS total_count
  1129. FROM
  1130. (
  1131. SELECT T1.grade_id, T2.ncee_student_id, MAX(T1.score_x) AS score_x, SUM(T2.score_x) AS course_score_x
  1132. FROM ncee_student AS T1
  1133. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1134. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1135. GROUP BY T1.grade_id, T2.ncee_student_id
  1136. ) AS T1
  1137. WHERE T1.score_x >= @lineScoreX AND T1.course_score_x >= @courseLineScoreX
  1138. GROUP BY T1.grade_id
  1139. ) AS T1
  1140. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1141. // 单上线:机构 - 方向
  1142. await _rep.SqlNonQueryAsync($@"
  1143. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1144. SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
  1145. FROM
  1146. (
  1147. SELECT
  1148. T1.sys_org_id,
  1149. T1.grade_id,
  1150. COUNT(1) AS line_count,
  1151. T2.total_count
  1152. FROM
  1153. (
  1154. SELECT T1.sys_org_id, T1.grade_id, T2.ncee_student_id, SUM(T2.score_x) AS course_score_x
  1155. FROM ncee_student AS T1
  1156. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1157. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1158. GROUP BY T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1159. ) AS T1
  1160. JOIN
  1161. (
  1162. SELECT T1.sys_org_id, COUNT(1) AS total_count
  1163. FROM
  1164. (
  1165. SELECT DISTINCT T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1166. FROM ncee_student AS T1
  1167. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1168. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1169. GROUP BY T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1170. ) AS T1
  1171. GROUP BY T1.sys_org_id
  1172. ) AS T2 ON T1.sys_org_id = T2.sys_org_id
  1173. WHERE T1.course_score_x >= @courseLineScoreX
  1174. GROUP BY T1.sys_org_id, T1.grade_id
  1175. ) AS T1
  1176. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1177. // 双上线:机构 - 方向
  1178. await _rep.SqlNonQueryAsync($@"
  1179. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1180. SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
  1181. FROM
  1182. (
  1183. SELECT
  1184. T1.sys_org_id,
  1185. T1.grade_id,
  1186. COUNT(1) AS line_count,
  1187. T2.total_count
  1188. FROM
  1189. (
  1190. SELECT T1.sys_org_id, T1.grade_id, T2.ncee_student_id, MAX(T1.score_x) AS score_x, SUM(T2.score_x) AS course_score_x
  1191. FROM ncee_student AS T1
  1192. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1193. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1194. GROUP BY T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1195. ) AS T1
  1196. JOIN
  1197. (
  1198. SELECT T1.sys_org_id, COUNT(1) AS total_count
  1199. FROM
  1200. (
  1201. SELECT DISTINCT T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1202. FROM ncee_student AS T1
  1203. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1204. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1205. GROUP BY T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1206. ) AS T1
  1207. GROUP BY T1.sys_org_id
  1208. ) AS T2 ON T1.sys_org_id = T2.sys_org_id
  1209. WHERE T1.score_x >= @lineScoreX AND T1.course_score_x >= @courseLineScoreX
  1210. GROUP BY T1.sys_org_id, T1.grade_id
  1211. ) AS T1
  1212. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1213. // 单上线:机构 - 方向 - 组合
  1214. await _rep.SqlNonQueryAsync($@"
  1215. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1216. SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
  1217. FROM
  1218. (
  1219. SELECT
  1220. T1.ncee_course_comb_id,
  1221. T1.sys_org_id,
  1222. T1.grade_id,
  1223. COUNT(1) AS line_count,
  1224. T2.total_count
  1225. FROM
  1226. (
  1227. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id, SUM(T2.score_x) AS course_score_x
  1228. FROM ncee_student AS T1
  1229. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1230. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1231. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1232. ) AS T1
  1233. JOIN
  1234. (
  1235. SELECT T1.ncee_course_comb_id, T1.sys_org_id, COUNT(1) AS total_count
  1236. FROM
  1237. (
  1238. SELECT DISTINCT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1239. FROM ncee_student AS T1
  1240. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1241. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1242. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1243. ) AS T1
  1244. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id
  1245. ) AS T2 ON T1.sys_org_id = T2.sys_org_id AND T1.ncee_course_comb_id = T2.ncee_course_comb_id
  1246. WHERE T1.course_score_x >= @courseLineScoreX
  1247. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id
  1248. ) AS T1
  1249. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1250. // 双上线:机构 - 方向 - 组合
  1251. await _rep.SqlNonQueryAsync($@"
  1252. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
  1253. SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
  1254. FROM
  1255. (
  1256. SELECT
  1257. T1.ncee_course_comb_id,
  1258. T1.sys_org_id,
  1259. T1.grade_id,
  1260. COUNT(1) AS line_count,
  1261. T2.total_count
  1262. FROM
  1263. (
  1264. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id, MAX(T1.score_x) AS score_x, SUM(T2.score_x) AS course_score_x
  1265. FROM ncee_student AS T1
  1266. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1267. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1268. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1269. ) AS T1
  1270. JOIN
  1271. (
  1272. SELECT T1.ncee_course_comb_id, T1.sys_org_id, COUNT(1) AS total_count
  1273. FROM
  1274. (
  1275. SELECT DISTINCT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1276. FROM ncee_student AS T1
  1277. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1278. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1279. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T2.ncee_student_id
  1280. ) AS T1
  1281. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id
  1282. ) AS T2 ON T1.sys_org_id = T2.sys_org_id AND T1.ncee_course_comb_id = T2.ncee_course_comb_id
  1283. WHERE T1.score_x >= @lineScoreX AND T1.course_score_x >= @courseLineScoreX
  1284. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id
  1285. ) AS T1
  1286. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1287. // 机构 - 班级 - 方向 - 组合
  1288. await _rep.SqlNonQueryAsync($@"
  1289. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate, is_double_line)
  1290. SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
  1291. FROM
  1292. (
  1293. SELECT
  1294. T1.ncee_course_comb_id,
  1295. T1.sys_org_id,
  1296. T1.grade_id,
  1297. T1.class_number,
  1298. COUNT(1) AS line_count,
  1299. T2.total_count
  1300. FROM
  1301. (
  1302. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id, SUM(T2.score_x) AS course_score_x
  1303. FROM ncee_student AS T1
  1304. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1305. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1306. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1307. ) AS T1
  1308. JOIN
  1309. (
  1310. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.class_number, COUNT(1) AS total_count
  1311. FROM
  1312. (
  1313. SELECT DISTINCT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1314. FROM ncee_student AS T1
  1315. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1316. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1317. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1318. ) AS T1
  1319. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.class_number
  1320. ) AS T2 ON T1.sys_org_id = T2.sys_org_id AND T1.ncee_course_comb_id = T2.ncee_course_comb_id AND T1.class_number = T2.class_number
  1321. WHERE T1.course_score_x >= @courseLineScoreX
  1322. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number
  1323. ) AS T1
  1324. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1325. // 机构 - 班级 - 方向 - 组合
  1326. await _rep.SqlNonQueryAsync($@"
  1327. INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate, is_double_line)
  1328. SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
  1329. FROM
  1330. (
  1331. SELECT
  1332. T1.ncee_course_comb_id,
  1333. T1.sys_org_id,
  1334. T1.grade_id,
  1335. T1.class_number,
  1336. COUNT(1) AS line_count,
  1337. T2.total_count
  1338. FROM
  1339. (
  1340. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id, MAX(T1.score_x) AS score_x, SUM(T2.score_x) AS course_score_x
  1341. FROM ncee_student AS T1
  1342. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1343. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1344. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1345. ) AS T1
  1346. JOIN
  1347. (
  1348. SELECT T1.ncee_course_comb_id, T1.sys_org_id, T1.class_number, COUNT(1) AS total_count
  1349. FROM
  1350. (
  1351. SELECT DISTINCT T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1352. FROM ncee_student AS T1
  1353. LEFT JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
  1354. WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.score > 0 AND T2.course_id IN({inCourses})
  1355. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number, T2.ncee_student_id
  1356. ) AS T1
  1357. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.class_number
  1358. ) AS T2 ON T1.sys_org_id = T2.sys_org_id AND T1.ncee_course_comb_id = T2.ncee_course_comb_id AND T1.class_number = T2.class_number
  1359. WHERE T1.score_x >= @lineScoreX AND T1.course_score_x >= @courseLineScoreX
  1360. GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number
  1361. ) AS T1
  1362. ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
  1363. }
  1364. }
  1365. #endregion
  1366. }
  1367. /// <summary>
  1368. /// 执行分数转换
  1369. /// </summary>
  1370. /// <param name="nceePlanId"></param>
  1371. /// <returns></returns>
  1372. private async Task ExecuteScoreConvert(int nceePlanId)
  1373. {
  1374. await _rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
  1375. var convertGrades = await _rep.Change<NceeConvertGrade>().DetachedEntities.OrderBy(t => t.Sequence).ToListAsync();
  1376. var convertGradeDict = convertGrades.ToDictionary(t => t.Id, t => t);
  1377. // 分科转换
  1378. foreach (var courseId in _chooseCourses)
  1379. {
  1380. var scores = await _rep.DetachedEntities.Where(t => t.NceeStudent.NceePlanId == nceePlanId && t.CourseId == courseId && t.Score > 0).OrderByDescending(t => t.Score).ToListAsync();
  1381. if (scores.Count == 0)
  1382. {
  1383. continue;
  1384. }
  1385. var convertRanges = GetConvertRange(nceePlanId, courseId, convertGrades, scores);
  1386. // 更新转换区间
  1387. await _rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId && t.CourseId == courseId).ExecuteDeleteAsync();
  1388. await _rep.Change<NceeConvertRange>().InsertNowAsync(convertRanges);
  1389. // 更新转换分
  1390. List<string> replaceValues = [];
  1391. int cscount = scores.Count;
  1392. for (int i = 0; i < cscount; i++)
  1393. {
  1394. var s = scores[i];
  1395. var cr = convertRanges.FirstOrDefault(t => s.Score >= t.MinScore && s.Score <= t.MaxScore);
  1396. var cg = convertGradeDict[cr.NceeConvertGradeId];
  1397. var scoreX = GetScoreX(s.Score, cg, cr);
  1398. replaceValues.Add($"({s.Id}, {nceePlanId}, {s.NceeStudentId}, {s.CourseId}, {s.Score}, {scoreX}, {cr.NceeConvertGradeId}, '{cg.Name}')");
  1399. if ((i + 1) % 1000 == 0 || i == cscount - 1)
  1400. {
  1401. string replaceSql = $@"
  1402. REPLACE INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, score_x, ncee_convert_grade_id, ncee_convert_grade_name) VALUES
  1403. {string.Join(",", replaceValues)}
  1404. ";
  1405. await _rep.SqlNonQueryAsync(replaceSql);
  1406. replaceValues.Clear();
  1407. }
  1408. }
  1409. }
  1410. // 更新总分
  1411. await _rep.SqlNonQueryAsync($@"
  1412. -- 更新总分
  1413. UPDATE ncee_student AS T1
  1414. JOIN
  1415. (
  1416. SELECT ncee_student_id, SUM(score) AS score, SUM(score_x) AS score_x
  1417. FROM ncee_score
  1418. WHERE ncee_plan_id = @nceePlanId
  1419. GROUP BY ncee_student_id
  1420. ) AS T2 ON T1.id = T2.ncee_student_id
  1421. SET T1.score = T2.score, T1.score_x = T2.score_x
  1422. WHERE T1.ncee_plan_id = @nceePlanId
  1423. ;", new { NceePlanId = nceePlanId });
  1424. }
  1425. /// <summary>
  1426. /// 更新排名(在整体中的排名和在机构内的排名)
  1427. /// </summary>
  1428. /// <param name="nceePlanId"></param>
  1429. /// <returns></returns>
  1430. private async Task ExecuteUpdateOrder(int nceePlanId)
  1431. {
  1432. await _rep.SqlNonQueryAsync($@"
  1433. UPDATE ncee_student AS T1
  1434. JOIN
  1435. (
  1436. SELECT id,
  1437. RANK() OVER(PARTITION BY direction_course_id ORDER BY score DESC) AS order_in_total,
  1438. RANK() OVER(PARTITION BY direction_course_id ORDER BY score_x DESC) AS order_in_total_x,
  1439. RANK() OVER(PARTITION BY direction_course_id, sys_org_id ORDER BY score DESC) AS order_in_org,
  1440. RANK() OVER(PARTITION BY direction_course_id, sys_org_id ORDER BY score_x DESC) AS order_in_org_x
  1441. FROM ncee_student
  1442. WHERE ncee_plan_id = @nceePlanId
  1443. ) AS T2 ON T1.id = T2.id
  1444. SET T1.order_in_total = T2.order_in_total,
  1445. T1.order_in_total_x = T2.order_in_total_x,
  1446. T1.order_in_org = T2.order_in_org,
  1447. T1.order_in_org_x = T2.order_in_org_x
  1448. ;", new { NceePlanId = nceePlanId });
  1449. }
  1450. /// <summary>
  1451. /// 获取转换区间
  1452. /// </summary>
  1453. /// <param name="nceePlanId"></param>
  1454. /// <param name="courseId"></param>
  1455. /// <param name="convertGrades"></param>
  1456. /// <param name="nceeScores"></param>
  1457. /// <returns></returns>
  1458. private static List<NceeConvertRange> GetConvertRange(int nceePlanId, short courseId, List<NceeConvertGrade> convertGrades, List<NceeScore> nceeScores)
  1459. {
  1460. int scount = nceeScores.Count;
  1461. var totalMaxScore = nceeScores.Select(t => t.Score).Max();
  1462. var totalMinScore = nceeScores.Select(t => t.Score).Min();
  1463. var cgs = convertGrades.OrderBy(t => t.Id).ToList();
  1464. var scores = nceeScores.OrderByDescending(t => t.Score).ToList();
  1465. List<NceeConvertRange> ncrs = [];
  1466. decimal lastMinScore = decimal.MaxValue;
  1467. foreach (var cg in cgs)
  1468. {
  1469. NceeConvertRange range = new()
  1470. {
  1471. NceePlanId = nceePlanId,
  1472. NceeConvertGradeId = cg.Id,
  1473. CourseId = courseId,
  1474. };
  1475. var i = (int)Math.Min(Math.Floor(cg.EndRate * scount), scount - 1);
  1476. if (cg.BeginRate == 0)
  1477. {
  1478. range.MaxScore = totalMaxScore;
  1479. }
  1480. else
  1481. {
  1482. range.MaxScore = scores.Where(t => t.Score < lastMinScore).Select(t => t.Score).Max();
  1483. }
  1484. if (cg.EndRate == 1)
  1485. {
  1486. range.MinScore = totalMinScore;
  1487. }
  1488. else
  1489. {
  1490. range.MinScore = scores[i].Score;
  1491. }
  1492. lastMinScore = range.MinScore;
  1493. ncrs.Add(range);
  1494. }
  1495. return ncrs;
  1496. }
  1497. /// <summary>
  1498. /// 获取转换分
  1499. /// </summary>
  1500. /// <param name="score"></param>
  1501. /// <param name="convertGrade"></param>
  1502. /// <param name="convertRange"></param>
  1503. /// <returns></returns>
  1504. private static decimal GetScoreX(decimal score, NceeConvertGrade convertGrade, NceeConvertRange convertRange)
  1505. {
  1506. // (Y2 - Y) / (Y- Y1) = (X2 - X) / (X - X1)
  1507. // Y1,Y2为原始分区间下限和上限
  1508. // X1,X2为转换分区间下限和上限
  1509. // Y为原始分
  1510. // X为转换分
  1511. // a = Y2 - Y
  1512. var a = (convertRange.MaxScore - score);
  1513. // b = Y - Y1
  1514. var b = (score - convertRange.MinScore);
  1515. return Math.Round((b * convertGrade.MaxScore + a * convertGrade.MinScore) / (a + b));
  1516. }
  1517. #endregion
  1518. }