123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- using Furion.ClayObject.Extensions;
- using Furion.DatabaseAccessor.Extensions;
- using NPOI.OpenXmlFormats.Spreadsheet;
- using NPOI.SS.UserModel;
- using NPOI.XSSF.UserModel;
- using System;
- using System.IO.Compression;
- using YBEE.EQM.Core;
- namespace YBEE.EQM.Application;
- /// <summary>
- /// 学生成绩导入服务
- /// </summary>
- public class ExamScoreImportService : IExamScoreImportService, ITransient
- {
- private readonly IRepository<ExamScore> _rep;
- private readonly ISchoolClassService _schoolClassService;
- private readonly IExamGradeService _examGradeService;
- private readonly IExportExcelService _exportExcelService;
- public ExamScoreImportService(IRepository<ExamScore> rep, ISchoolClassService schoolClassService, IExamGradeService examGradeService, IExportExcelService exportExcelService)
- {
- _rep = rep;
- _schoolClassService = schoolClassService;
- _examGradeService = examGradeService;
- _exportExcelService = exportExcelService;
- }
- /// <summary>
- /// 批量导入前期未上报学生名单的各科成绩(初始化)
- /// </summary>
- /// <param name="filePath"></param>
- /// <param name="examPlanId"></param>
- /// <returns></returns>
- public async Task UploadImportWithoutStudentTotalScore(string filePath, int examPlanId)
- {
- /* ----------------------------------------------------------
- * 处理步骤:
- * 1.验证表头
- * 2.读取数据
- * 3.处理班级
- * 4.处理学生
- * 5.批量插入
- ---------------------------------------------------------- */
- try
- {
- #region 1.验证表结构
- using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
- IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
- var sheet = workbook.GetSheetAt(0);
- var rows = sheet.GetRowEnumerator();
- // 少于2行验证
- if (sheet.LastRowNum < 2)
- {
- //"第一行应为填写说明,第二行应为标题行,请勿修改模板结构。"
- return;
- }
- // 读取表头
- rows.MoveNext();
- IRow headerRow = (IRow)rows.Current;
- int index = 0;
- int SAMPLE_TYPE_INDEX = index++;
- int SCHOOL_ID_INDEX = index++;
- int SCHOOL_NAME_INDEX = index++;
- int SCHOOL_BID_INDEX = index++;
- int SCHOOL_BNAME_INDEX = index++;
- int NAME_INDEX = index++;
- int CERT_TYPE_INDEX = index++;
- int ID_NUM_INDEX = index++;
- int EXAM_NUMBER_INDEX = index++;
- int COURSE_COMB_INDEX = index++;
- int GRADE_INDEX = index++;
- int CLASS_INDEX = index++;
- int COURSE_START_INDEX = index;
- Dictionary<int, string> headers = new()
- {
- { SAMPLE_TYPE_INDEX, "抽样类型" },
- { SCHOOL_ID_INDEX, "学校ID" },
- { SCHOOL_NAME_INDEX, "学校" },
- { SCHOOL_BID_INDEX, "校区ID" },
- { SCHOOL_BNAME_INDEX, "校区" },
- { NAME_INDEX, "姓名" },
- { CERT_TYPE_INDEX, "证件类型" },
- { ID_NUM_INDEX, "证件号码" },
- { EXAM_NUMBER_INDEX, "考号" },
- { COURSE_COMB_INDEX, "选科组合" },
- { GRADE_INDEX, "年级" },
- { CLASS_INDEX, "班级" },
- };
- List<string> headerErrors = new();
- for (int i = 0; i < COURSE_START_INDEX; i++)
- {
- if (headerRow.GetCell(i)?.ToString() != headers[i])
- {
- char letter = (char)('A' + i);
- headerErrors.Add(letter.ToString());
- }
- }
- if (headerErrors.Any())
- {
- string columnErrors = string.Join("、", headerErrors);
- //result.ErrorMessage.Add($"第1行标题行{columnErrors}列名错误。从A列开始依次应为抽样类型、学校ID、学校、姓名、证件类型、证件号码、考号、年级、班级。");
- //return result;
- return;
- }
- //result.StructureCorrect = true;
- #endregion
- #region 2.读取数据
- var examPlan = await _rep.Change<ExamPlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
- var examGradeDict = (await _examGradeService.GetListByExamPlanId(examPlanId)).ToDictionary(t => t.Grade.GradeNumber);
- var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
- // 获取需要导入的科目列表
- Dictionary<int, Course> courses = new();
- for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
- {
- var cn = headerRow.GetCell(gi).ToString() ?? "";
- if (string.IsNullOrEmpty(cn))
- {
- continue;
- }
- courses.Add(gi, courseDict[cn]);
- }
- int rn = 1;
- List<ExamScoreImportDto> sourceItems = new();
- while (rows.MoveNext())
- {
- rn++;
- IRow row = (IRow)rows.Current;
- string rv = row.GetCell(0)?.ToString().Trim() ?? "";
- if (rv == "")
- {
- break;
- }
- for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
- {
- var course = courses[gi];
- var sbid = row.GetCell(SCHOOL_BID_INDEX)?.NumericCellValue;
- var ccid = row.GetCell(COURSE_COMB_INDEX)?.NumericCellValue;
- ExamScoreImportDto item = new()
- {
- RowNumber = rn,
- SampleType = (ExamSampleType)row.GetCell(SAMPLE_TYPE_INDEX).NumericCellValue,
- SysOrgId = (short)row.GetCell(SCHOOL_ID_INDEX).NumericCellValue,
- SysOrgBranchId = sbid > 0 ? (short)sbid : null,
- StudentName = StringUtil.ClearWhite(row.GetCell(NAME_INDEX)?.ToString() ?? ""),
- CertificateType = (CertificateType)row.GetCell(CERT_TYPE_INDEX).NumericCellValue,
- IdNumber = StringUtil.ClearIdNumber(row.GetCell(ID_NUM_INDEX)?.ToString() ?? ""),
- ExamNumber = row.GetCell(EXAM_NUMBER_INDEX)?.ToString() ?? "",
- GradeNumber = (short)row.GetCell(GRADE_INDEX).NumericCellValue,
- ClassNumber = (short)row.GetCell(CLASS_INDEX).NumericCellValue,
- CourseId = course.Id,
- NceeCourseCombId = ccid > 0 ? (short)ccid : null,
- };
- string scoreStr = row.GetCell(gi)?.ToString() ?? "";
- if (decimal.TryParse(scoreStr, out decimal score))
- {
- item.Score = score;
- }
- else if (scoreStr.Contains('特') || scoreStr.Contains('缺'))
- {
- item.IsExcluded = true;
- }
- if (item.IdNumber.Length == 18)
- {
- if (int.TryParse(item.IdNumber.AsSpan(16, 1), out int gn))
- {
- item.Gender = (short)(gn % 2 == 0 ? 2 : 1);
- }
- }
- var eg = examGradeDict[item.GradeNumber];
- item.ExamGradeId = eg.Id;
- item.GradeId = eg.GradeId;
- item.GradeBeginYear = eg.GradeBeginYear;
- item.GradeEndYear = eg.GradeEndYear;
- item.SchoolClassId = _schoolClassService.GetSchoolClassId(examPlan.EducationStage, item.SysOrgId, eg.GradeBeginYear, item.ClassNumber);
- sourceItems.Add(item);
- }
- }
- workbook.Close();
- fs.Close();
- #endregion
- #region 3.处理班级
- var scs = sourceItems.GroupBy(t => t.SchoolClassId).Select(t => new BatchInsertSchoolClassInput
- {
- Id = t.Key,
- SysOrgId = t.FirstOrDefault().SysOrgId,
- SysOrgBranchId = t.FirstOrDefault().SysOrgBranchId,
- EducationStage = examPlan.EducationStage,
- EducationYears = examPlan.EducationYears,
- ClassNumber = t.FirstOrDefault().ClassNumber,
- GradeBeginYear = t.FirstOrDefault().GradeBeginYear,
- GradeEndYear = t.FirstOrDefault().GradeEndYear,
- }).Distinct().ToList();
- // 插入没有的班级
- await _schoolClassService.BatchInsert(scs);
- #endregion
- #region 4.处理学生
- // 清理学生
- string deleteStudentSql = $"DELETE FROM exam_student WHERE exam_plan_id = {examPlanId};";
- await _rep.SqlNonQueryAsync(deleteStudentSql);
- var uid = CurrentSysUserInfo.SysUserId;
- if (uid == 0)
- {
- uid = 1;
- }
- var stus = sourceItems.GroupBy(t => t.RowNumber).Select(t => t.FirstOrDefault()).ToList();
- List<string> insertValues = new();
- int scount = stus.Count;
- for (int i = 0; i < scount; i++)
- {
- var t = stus[i];
- string sobid = "NULL";
- if (t.SysOrgBranchId.HasValue)
- {
- sobid = t.SysOrgBranchId.ToString();
- }
- insertValues.Add($"({examPlanId}, {t.SysOrgId}, {sobid}, {t.ExamGradeId}, {t.GradeId}, {t.SchoolClassId}, {t.ClassNumber}, '{t.ExamNumber}', '{t.StudentName}', {(short)t.CertificateType}, '{t.IdNumber}', {t.Gender}, {uid}, CURRENT_TIMESTAMP, 0)");
- if ((i + 1) % 1000 == 0 || i == scount - 1)
- {
- string insertSql = $@"
- INSERT INTO exam_student(exam_plan_id, sys_org_id, sys_org_branch_id, exam_grade_id, grade_id, school_class_id, class_number, exam_number, `name`, certificate_type, id_number, gender, create_sys_user_id, create_time, is_deleted) VALUES
- {string.Join(",", insertValues)}
- ";
- await _rep.SqlNonQueryAsync(insertSql);
- insertValues.Clear();
- }
- }
- #endregion
- #region 5.批量导入
- // 清理成绩
- string deleteScoreSql = $"DELETE FROM exam_score WHERE exam_plan_id = {examPlanId};";
- await _rep.SqlNonQueryAsync(deleteScoreSql);
- List<string> selects = new();
- scount = sourceItems.Count;
- for (int i = 0; i < scount; i++)
- {
- var t = sourceItems[i];
- string sobid = "NULL";
- if (t.SysOrgBranchId.HasValue)
- {
- sobid = t.SysOrgBranchId.ToString();
- }
- string ccid = "NULL";
- if (t.NceeCourseCombId.HasValue)
- {
- ccid = t.NceeCourseCombId.ToString();
- }
- var ied = t.IsExcluded ? 1 : 0;
- selects.Add($@"
- SELECT {(short)t.SampleType} est, {t.SysOrgId} soid, {sobid} as sobid, {t.GradeId} gid, {examPlan.SemesterId} smid, {t.SchoolClassId} scid, {t.ClassNumber} cn, '{t.ExamNumber}' en, {ccid} ccid, {t.CourseId} cid, {t.Score} s, {ied} ied ");
- if ((i + 1) % 500 == 0 || i == scount - 1)
- {
- string insertSql = $@"
- INSERT INTO exam_score(exam_plan_id, education_stage, exam_sample_type, sys_org_id, sys_org_branch_id, grade_id, semester_id, school_class_id, class_number, exam_student_id, ncee_course_comb_id, course_id, score, is_excluded)
- SELECT {examPlanId}, {(short)examPlan.EducationStage}, T1.est, T1.soid, T1.sobid, T1.gid, T1.smid, T1.scid, T1.cn, T2.id, T1.ccid, T1.cid, T1.s, T1.ied
- FROM
- ({string.Join("UNION ALL", selects)}
- ) AS T1
- JOIN (SELECT id, school_class_id, exam_number FROM exam_student WHERE exam_plan_id = {examPlanId}) AS T2
- ON T1.scid = T2.school_class_id AND T1.en = T2.exam_number
- ";
- await _rep.SqlNonQueryAsync(insertSql);
- selects.Clear();
- }
- }
- #endregion
- }
- catch (Exception ex)
- {
- throw new Exception(ex.Message);
- }
- finally
- {
- File.Delete(filePath);
- }
- }
- /// <summary>
- /// 批量导入学生总成绩
- /// </summary>
- /// <param name="filePath"></param>
- /// <param name="examPlanId"></param>
- /// <returns></returns>
- public async Task UploadImportStudentTotalScore(string filePath, int examPlanId)
- {
- /* ----------------------------------------------------------
- * 处理步骤:
- * 1.验证表头
- * 2.读取数据
- * 3.处理班级
- * 4.批量插入
- ---------------------------------------------------------- */
- try
- {
- #region 1.验证表结构
- using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
- IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
- var sheet = workbook.GetSheetAt(0);
- var rows = sheet.GetRowEnumerator();
- // 少于2行验证
- if (sheet.LastRowNum < 2)
- {
- //"第一行应为填写说明,第二行应为标题行,请勿修改模板结构。"
- return;
- }
- // 读取表头
- rows.MoveNext();
- IRow headerRow = (IRow)rows.Current;
- int index = 0;
- int SAMPLE_TYPE_INDEX = index++;
- int SCHOOL_ID_INDEX = index++;
- int SCHOOL_NAME_INDEX = index++;
- int SCHOOL_BID_INDEX = index++;
- int SCHOOL_BNAME_INDEX = index++;
- int NAME_INDEX = index++;
- int CERT_TYPE_INDEX = index++;
- int ID_NUM_INDEX = index++;
- int EXAM_NUMBER_INDEX = index++;
- int COURSE_COMB_INDEX = index++;
- int GRADE_INDEX = index++;
- int CLASS_INDEX = index++;
- int COURSE_START_INDEX = index;
- Dictionary<int, string> headers = new()
- {
- { SAMPLE_TYPE_INDEX, "抽样类型" },
- { SCHOOL_ID_INDEX, "学校ID" },
- { SCHOOL_NAME_INDEX, "学校" },
- { SCHOOL_BID_INDEX, "校区ID" },
- { SCHOOL_BNAME_INDEX, "校区" },
- { NAME_INDEX, "姓名" },
- { CERT_TYPE_INDEX, "证件类型" },
- { ID_NUM_INDEX, "证件号码" },
- { EXAM_NUMBER_INDEX, "考号" },
- { COURSE_COMB_INDEX, "选科组合" },
- { GRADE_INDEX, "年级" },
- { CLASS_INDEX, "班级" },
- };
- List<string> headerErrors = new();
- for (int i = 0; i < COURSE_START_INDEX; i++)
- {
- if (headerRow.GetCell(i)?.ToString() != headers[i])
- {
- char letter = (char)('A' + i);
- headerErrors.Add(letter.ToString());
- }
- }
- if (headerErrors.Any())
- {
- string columnErrors = string.Join("、", headerErrors);
- //result.ErrorMessage.Add($"第1行标题行{columnErrors}列名错误。从A列开始依次应为抽样类型、学校ID、学校、姓名、证件类型、证件号码、考号、年级、班级。");
- //return result;
- return;
- }
- //result.StructureCorrect = true;
- #endregion
- #region 2.读取数据
- var examPlan = await _rep.Change<ExamPlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
- var examGradeDict = (await _examGradeService.GetListByExamPlanId(examPlanId)).ToDictionary(t => t.Grade.GradeNumber);
- var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
- var examCourses = await _rep.Change<ExamCourse>().DetachedEntities.Where(t => t.ExamPlanId == examPlanId).Select(t => new { t.GradeId, t.CourseId }).ToListAsync();
- // 获取需要导入的科目列表
- Dictionary<int, Course> courses = new();
- for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
- {
- var cn = headerRow.GetCell(gi).ToString() ?? "";
- if (string.IsNullOrEmpty(cn))
- {
- continue;
- }
- courses.Add(gi, courseDict[cn]);
- }
- int rn = 1;
- List<ExamScoreImportDto> sourceItems = new();
- while (rows.MoveNext())
- {
- rn++;
- IRow row = (IRow)rows.Current;
- string rv = row.GetCell(0)?.ToString().Trim() ?? "";
- if (rv == "")
- {
- break;
- }
- var gradeNumber = (short)row.GetCell(GRADE_INDEX).NumericCellValue;
- for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
- {
- var course = courses[gi];
- var eg = examGradeDict[gradeNumber];
- // 跳过未监测科目
- if (!examCourses.Any(t => t.GradeId == eg.GradeId && t.CourseId == course.Id))
- {
- continue;
- }
- var sbid = row.GetCell(SCHOOL_BID_INDEX)?.NumericCellValue;
- var ccid = row.GetCell(COURSE_COMB_INDEX)?.NumericCellValue;
- ExamScoreImportDto item = new()
- {
- RowNumber = rn,
- SampleType = (ExamSampleType)row.GetCell(SAMPLE_TYPE_INDEX).NumericCellValue,
- SysOrgId = (short)row.GetCell(SCHOOL_ID_INDEX).NumericCellValue,
- SysOrgBranchId = sbid > 0 ? (short)sbid : null,
- StudentName = StringUtil.ClearWhite(row.GetCell(NAME_INDEX)?.ToString() ?? ""),
- CertificateType = (CertificateType)row.GetCell(CERT_TYPE_INDEX).NumericCellValue,
- IdNumber = StringUtil.ClearIdNumber(row.GetCell(ID_NUM_INDEX)?.ToString() ?? ""),
- ExamNumber = row.GetCell(EXAM_NUMBER_INDEX)?.ToString() ?? "",
- GradeNumber = gradeNumber,
- ClassNumber = (short)row.GetCell(CLASS_INDEX).NumericCellValue,
- CourseId = course.Id,
- NceeCourseCombId = ccid > 0 ? (short)ccid : null,
- };
- string scoreStr = row.GetCell(gi)?.ToString() ?? "";
- if (decimal.TryParse(scoreStr, out decimal score))
- {
- item.Score = score;
- }
- else if (scoreStr.Contains('特') || scoreStr.Contains('缺'))
- {
- item.IsExcluded = true;
- }
- if (item.IdNumber.Length == 18)
- {
- if (int.TryParse(item.IdNumber.AsSpan(16, 1), out int gn))
- {
- item.Gender = (short)(gn % 2 == 0 ? 2 : 1);
- }
- }
- item.ExamGradeId = eg.Id;
- item.GradeId = eg.GradeId;
- item.GradeBeginYear = eg.GradeBeginYear;
- item.GradeEndYear = eg.GradeEndYear;
- item.SchoolClassId = _schoolClassService.GetSchoolClassId(examPlan.EducationStage, item.SysOrgId, eg.GradeBeginYear, item.ClassNumber);
- sourceItems.Add(item);
- }
- }
- workbook.Close();
- fs.Close();
- #endregion
- #region 3.处理班级
- var scs = sourceItems.GroupBy(t => t.SchoolClassId).Select(t => new BatchInsertSchoolClassInput
- {
- Id = t.Key,
- SysOrgId = t.FirstOrDefault().SysOrgId,
- SysOrgBranchId = t.FirstOrDefault().SysOrgBranchId,
- EducationStage = examPlan.EducationStage,
- EducationYears = examPlan.EducationYears,
- ClassNumber = t.FirstOrDefault().ClassNumber,
- GradeBeginYear = t.FirstOrDefault().GradeBeginYear,
- GradeEndYear = t.FirstOrDefault().GradeEndYear,
- }).Distinct().ToList();
- // 插入没有的班级
- await _schoolClassService.BatchInsert(scs);
- #endregion
- #region 4.批量导入
- // 清理成绩
- string deleteScoreSql = $"DELETE FROM exam_score WHERE exam_plan_id = {examPlanId};";
- await _rep.SqlNonQueryAsync(deleteScoreSql);
- List<string> selects = new();
- var scount = sourceItems.Count;
- for (int i = 0; i < scount; i++)
- {
- var t = sourceItems[i];
- string sobid = "NULL";
- if (t.SysOrgBranchId.HasValue)
- {
- sobid = t.SysOrgBranchId.ToString();
- }
- string ccid = "NULL";
- if (t.NceeCourseCombId.HasValue)
- {
- ccid = t.NceeCourseCombId.ToString();
- }
- var ied = t.IsExcluded ? 1 : 0;
- selects.Add($@"
- SELECT {(short)t.SampleType} est, {t.SysOrgId} soid, {sobid} as sobid, {t.GradeId} gid, {examPlan.SemesterId} smid, {t.SchoolClassId} scid, {t.ClassNumber} cn, '{t.ExamNumber}' en, {ccid} ccid, {t.CourseId} cid, {t.Score} s, {ied} ied ");
- if ((i + 1) % 500 == 0 || i == scount - 1)
- {
- string insertSql = $@"
- SET @examSampleId = (SELECT MAX(id) FROM exam_sample WHERE exam_plan_id = {examPlanId} AND is_selected = 1);
- INSERT INTO exam_score(exam_plan_id, education_stage, exam_sample_type, sys_org_id, sys_org_branch_id, grade_id, semester_id, school_class_id, class_number, exam_student_id, ncee_course_comb_id, course_id, score, is_excluded)
- SELECT {examPlanId}, {(short)examPlan.EducationStage}, T1.est, T1.soid, T1.sobid, T1.gid, T1.smid, T1.scid, T1.cn, T2.id, T1.ccid, T1.cid, T1.s, T1.ied
- FROM
- ({string.Join("UNION ALL", selects)}
- ) AS T1
- JOIN (SELECT exam_number, exam_student_id AS id FROM exam_sample_student WHERE exam_sample_id = @examSampleId) AS T2
- ON T1.en = T2.exam_number;
- ";
- await _rep.SqlNonQueryAsync(insertSql);
- selects.Clear();
- }
- }
- #endregion
- #region 5.计算总分和分段
- await $@"
- -- 更新各科成绩分段
- UPDATE exam_score AS T1
- JOIN
- (
- SELECT T1.id, T1.grade_id, T1.course_id, get_exam_score_range_id(T2.exam_score_range_type, T1.score) AS exam_score_range_id
- FROM exam_score AS T1
- JOIN exam_course AS T2 ON T1.exam_plan_id = T2.exam_plan_id AND T1.grade_id = T2.grade_id AND T1.course_id = T2.course_id
- WHERE T1.exam_plan_id = {examPlanId}
- ) AS T2 ON T1.id = T2.id
- SET T1.exam_score_range_id = T2.exam_score_range_id
- WHERE T1.exam_plan_id = {examPlanId};
- -- 更新总成绩和分段
- DELETE FROM exam_score_total WHERE exam_plan_id = {examPlanId};
- INSERT INTO exam_score_total(
- exam_plan_id,
- education_stage,
- exam_sample_type,
- sys_org_id,
- sys_org_branch_id,
- grade_id, semester_id,
- school_class_id,
- class_number,
- exam_student_id,
- ncee_course_comb_id,
- course_count,
- score,
- exam_score_range_id,
- is_excluded
- )
- SELECT {examPlanId}, T1.*, get_exam_score_range_id(T2.exam_score_range_type, T1.score) AS exam_score_range_id, 0
- FROM
- (
- SELECT
- education_stage,
- exam_sample_type,
- sys_org_id,
- sys_org_branch_id,
- grade_id, semester_id,
- school_class_id,
- class_number,
- exam_student_id,
- ncee_course_comb_id,
- COUNT(1) AS course_count,
- SUM(IFNULL(score, 0)) AS score
- FROM exam_score
- WHERE exam_plan_id = {examPlanId}
- GROUP BY education_stage, exam_sample_type, sys_org_id, sys_org_branch_id, grade_id, semester_id, school_class_id, class_number, exam_student_id, ncee_course_comb_id
- ) AS T1
- JOIN
- (
- SELECT grade_id, exam_score_range_type FROM exam_grade WHERE exam_plan_id = {examPlanId}
- ) AS T2 ON T1.grade_id = T2.grade_id;
- ".SetCommandTimeout(6000).SqlNonQueryAsync();
- #endregion
- }
- catch (Exception ex)
- {
- throw new Exception(ex.Message);
- }
- finally
- {
- File.Delete(filePath);
- }
- }
- /// <summary>
- /// 上传并合并小题成绩导出
- /// </summary>
- /// <param name="filePath"></param>
- /// <param name="examPlanId"></param>
- /// <returns></returns>
- /// <exception cref="Exception"></exception>
- public async Task<(string, byte[])> UploadImportStudentMinorScore(string filePath, int examPlanId)
- {
- var examPlan = await _rep.Change<ExamPlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划");
- // 解压输出目录
- string unzipPath = Path.Combine(FileUtil.GetTempFileRoot(), Path.GetFileNameWithoutExtension(filePath));
- Directory.CreateDirectory(unzipPath);
- // 临时存放目录
- string outputRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
- Directory.CreateDirectory(outputRoot);
- string outputFilePath = Path.Combine(outputRoot, $"{examPlan.Name}-区校合并-小题成绩");
- Directory.CreateDirectory(outputFilePath);
- try
- {
- List<ExamScoreMinorFileInfo> fileInfos = new();
- var gradeDict = (await _rep.Change<Grade>().DetachedEntities.Where(t => t.EducationStage == examPlan.EducationStage).ToListAsync()).ToDictionary(t => t.Name);
- var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
- #region 1.读取文件信息
- ZipFile.ExtractToDirectory(filePath, unzipPath, System.Text.Encoding.GetEncoding("GBK"), true);
- var sampleTypePaths = Directory.GetDirectories(unzipPath);
- foreach (var stpath in sampleTypePaths)
- {
- var tn = Path.GetFileNameWithoutExtension(stpath).Split("-");
- ExamSampleType est;
- if (int.TryParse(tn[0], out int estInt))
- {
- est = (ExamSampleType)estInt;
- }
- else
- {
- throw new ArgumentException("监测抽样类型错误");
- }
- var scoreFiles = Directory.GetFiles(stpath).Where(t => t.EndsWith(".xlsx") || t.EndsWith(".xls")).Where(t => !t.StartsWith("~"));
- foreach (var scoreFile in scoreFiles)
- {
- var fn = Path.GetFileNameWithoutExtension(scoreFile).Split("-");
- var gradeName = fn[1];
- var grade = gradeDict[gradeName];
- var courseName = fn[2];
- var course = courseDict[courseName];
- fileInfos.Add(new()
- {
- ExamSampleType = est,
- FilePath = scoreFile,
- GradeId = grade.Id,
- Grade = grade,
- CourseId = course.Id,
- Course = course,
- });
- }
- }
- #endregion
- #region 2.合并文件
- var cfiles = fileInfos.Where(t => t.ExamSampleType == ExamSampleType.DISTRICT);
- foreach (var cfile in cfiles)
- {
- var qminors = await _rep.Change<ExamPaperQuestionMinor>().DetachedEntities.Where(t => t.ExamPaper.ExamPlanId == examPlanId && t.ExamPaper.CourseId == cfile.CourseId && t.ExamPaper.GradeId == cfile.GradeId && t.IsLeaf == true).ToListAsync();
- var examSampleStudents = await _rep.Change<ExamSampleStudent>().DetachedEntities
- .Where(t => t.ExamSample.ExamPlanId == examPlanId && t.ExamSample.IsSelected == true && t.ExamStudent.GradeId == cfile.GradeId)
- .Select(t => new { t.ExamNumber, t.ExamStudentId, t.ExamStudent.SysOrgId })
- .ToListAsync();
- var sfile = fileInfos.FirstOrDefault(t => t.ExamSampleType == ExamSampleType.SCHOOL_EXAM && t.GradeId == cfile.GradeId && t.CourseId == cfile.CourseId) ?? throw new ArgumentNullException($"{cfile}对应校考小题成绩未找到");
- // 区文件
- using FileStream c_fs = new(cfile.FilePath, FileMode.Open, FileAccess.Read);
- IWorkbook c_workbook = ExcelUtil.GetWorkbook(filePath, c_fs);
- var c_sheet = c_workbook.GetSheetAt(0);
- var c_rows = c_sheet.GetRowEnumerator();
- // 输出文件
- IWorkbook o_workbook = new XSSFWorkbook();
- ISheet o_sheet = o_workbook.CreateSheet();
- var o_cellStyles = _exportExcelService.GetCellStyle(o_workbook);
- int o_rowNum = 0;
- IRow o_headerRow = o_sheet.CreateRow(o_rowNum++);
- // 标题行
- Dictionary<int, ExamPaperQuestionMinor> colMinor = new();
- c_rows.MoveNext();
- var c_headerRow = (IRow)c_rows.Current;
- int colNum = c_headerRow.LastCellNum;
- for (int ci = 0; ci < colNum; ci++)
- {
- string colName = c_headerRow.GetCell(ci)?.ToString() ?? "";
- _exportExcelService.AddCell(c_headerRow.GetCell(ci)?.ToString(), o_headerRow, ci, o_cellStyles.CenterCellStyle, o_sheet, width: ci == 1 ? 12 : ci == 2 ? 30 : null);
- var qm = qminors.FirstOrDefault(t => t.ColumnName == colName);
- if (qm != null)
- {
- colMinor.Add(ci, qm);
- }
- }
- // 所有小题成绩列表
- List<ExamScoreMinorImportDto> items = new();
- // 区文件写入
- while (c_rows.MoveNext())
- {
- var row = (IRow)c_rows.Current;
- var c0 = row.GetCell(0);
- if (c0 == null || c0.ToString() == "")
- {
- break;
- }
- ExamScoreMinorImportDto item = new();
- IRow o_row = o_sheet.CreateRow(o_rowNum++);
- for (int tci = 0; tci < colNum; tci++)
- {
- var cell = row.GetCell(tci);
- if (cell == null)
- {
- continue;
- }
- if (cell.CellType == CellType.Numeric)
- {
- _exportExcelService.AddCell(cell.NumericCellValue, o_row, tci, o_cellStyles.CenterCellStyle, o_sheet);
- }
- else
- {
- _exportExcelService.AddCell(cell.ToString(), o_row, tci, o_cellStyles.CenterCellStyle, o_sheet);
- }
- if (tci == 1)
- {
- var examNumber = cell.ToString();
- var stu = examSampleStudents.FirstOrDefault(t => t.ExamNumber == examNumber);
- if (stu != null)
- {
- item.ExamStudentId = stu.ExamStudentId;
- item.SysOrgId = stu.SysOrgId;
- }
- }
- else if (tci >= 7 && colMinor.TryGetValue(tci, out ExamPaperQuestionMinor cm))
- {
- if (cell.CellType == CellType.Numeric)
- {
- item.Score = (decimal)cell.NumericCellValue;
- }
- else
- {
- if (decimal.TryParse(cell.ToString(), out decimal s))
- {
- item.Score = s;
- }
- }
- item.StarScore = cm.Score == 0 ? 0 : item.Score / cm.Score;
- if (item.ExamStudentId != 0)
- {
- items.Add(new()
- {
- ExamStudentId = item.ExamStudentId,
- SysOrgId = item.SysOrgId,
- ExamPaperQuestionMinorId = cm.Id,
- Score = item.Score,
- StarScore = item.StarScore
- });
- }
- }
- }
- }
- // 校文件
- using FileStream s_fs = new(sfile.FilePath, FileMode.Open, FileAccess.Read);
- IWorkbook s_workbook = ExcelUtil.GetWorkbook(sfile.FilePath, s_fs);
- var s_sheet = s_workbook.GetSheetAt(0);
- var s_rows = s_sheet.GetRowEnumerator();
- s_rows.MoveNext();
- // 校文件写入
- while (s_rows.MoveNext())
- {
- var row = (IRow)s_rows.Current;
- var c0 = row.GetCell(0);
- if (c0 == null || c0.ToString() == "")
- {
- break;
- }
- ExamScoreMinorImportDto item = new();
- IRow o_row = o_sheet.CreateRow(o_rowNum++);
- for (int tci = 0; tci < colNum; tci++)
- {
- var cell = row.GetCell(tci);
- if (cell == null)
- {
- continue;
- }
- if (cell.CellType == CellType.Numeric)
- {
- _exportExcelService.AddCell(cell.NumericCellValue, o_row, tci, o_cellStyles.CenterCellStyle, o_sheet);
- }
- else
- {
- _exportExcelService.AddCell(cell.ToString(), o_row, tci, o_cellStyles.CenterCellStyle, o_sheet);
- }
- if (tci == 1)
- {
- var examNumber = cell.ToString();
- var stu = examSampleStudents.FirstOrDefault(t => t.ExamNumber == examNumber);
- if (stu != null)
- {
- item.ExamStudentId = stu.ExamStudentId;
- item.SysOrgId = stu.SysOrgId;
- }
- }
- else if (tci >= 7 && colMinor.TryGetValue(tci, out ExamPaperQuestionMinor cm))
- {
- if (cell.CellType == CellType.Numeric)
- {
- item.Score = (decimal)cell.NumericCellValue;
- }
- else
- {
- if (decimal.TryParse(cell.ToString(), out decimal s))
- {
- item.Score = s;
- }
- }
- item.StarScore = cm.Score == 0 ? 0 : item.Score / cm.Score;
- if (item.ExamStudentId != 0)
- {
- items.Add(new()
- {
- ExamStudentId = item.ExamStudentId,
- SysOrgId = item.SysOrgId,
- ExamPaperQuestionMinorId = cm.Id,
- Score = item.Score,
- StarScore = item.StarScore
- });
- }
- }
- }
- }
- o_sheet.CreateFreezePane(0, 1);
- MemoryStream o_ms = new();
- o_workbook.Write(o_ms, false);
- o_ms.Flush();
- await File.WriteAllBytesAsync(Path.Combine(outputFilePath, $"{examPlan.EducationStage.GetDescription()}-{cfile.Grade.Name}-{cfile.Course.Name}-小题成绩.xlsx"), o_ms.ToArray());
- #region 批量导入
- // 清理成绩
- string deleteScoreSql = $"DELETE FROM exam_score_minor WHERE exam_plan_id = {examPlanId} AND grade_id = {cfile.GradeId} AND course_id = {cfile.CourseId};";
- //await _rep.SqlNonQueryAsync(deleteScoreSql);
- await deleteScoreSql.SetCommandTimeout(60000).SqlNonQueryAsync();
- List<string> values = new();
- var scount = items.Count;
- for (int i = 0; i < scount; i++)
- {
- var item = items[i];
- values.Add($"({examPlanId},{item.SysOrgId},{item.ExamStudentId},{cfile.GradeId},{cfile.CourseId},{item.ExamPaperQuestionMinorId},{item.Score},{item.StarScore})");
- if ((i + 1) % 2000 == 0 || i == scount - 1)
- {
- string insertSql = $"INSERT INTO exam_score_minor(exam_plan_id,sys_org_id,exam_student_id,grade_id,course_id,exam_paper_question_minor_id,score,star_score) VALUES {string.Join(",", values)}";
- //await _rep.SqlNonQueryAsync(insertSql);
- await insertSql.SetCommandTimeout(60000).SqlNonQueryAsync();
- values.Clear();
- }
- }
- #endregion
- }
- #endregion
- string outFileName = $"{examPlan.Name}-区校合并-小题成绩.zip";
- string oFilePath = Path.Combine(outputRoot, outFileName);
- ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
- zip.CreateZip(oFilePath, outputFilePath, true, string.Empty);
- var retBytes = await File.ReadAllBytesAsync(oFilePath);
- return (outFileName, retBytes);
- }
- catch (Exception ex)
- {
- throw new Exception(ex.Message);
- }
- finally
- {
- File.Delete(filePath);
- Directory.Delete(unzipPath, true);
- Directory.Delete(outputRoot, true);
- }
- }
- }
|