using Furion.DatabaseAccessor.Extensions; using Furion.JsonSerialization; using NPOI.SS.UserModel; using NPOI.SS.Util; using NPOI.XSSF.UserModel; using YBEE.EQM.Core; namespace YBEE.EQM.Application; /// /// 监测抽样方案管理服务 /// public class ExamSampleService(IRepository rep, IExportExcelService exportExcelService, IExamPlanService examPlanService, ISysDictDataService sysDictDataService) : IExamSampleService, ITransient { #region 方案管理 /// /// 添加监测抽样方案 /// /// /// public async Task Add(AddExamSampleInput input) { var examPlan = await rep.Change().DetachedEntities.FirstOrDefaultAsync(t => t.Id == input.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); var maxSeq = await rep.DetachedEntities.Where(t => t.ExamPlanId == input.ExamPlanId).MaxAsync(t => (short?)t.Sequence); var item = input.Adapt(); item.Sequence = (short)(maxSeq.HasValue ? (maxSeq + 1) : 1); string sampleName = $"学生抽样方案{ConvertUtil.ConvertToChinese(item.Sequence)}"; item.FullName = $"{examPlan.FullName}{sampleName}"; item.Name = $"{examPlan.Name}{sampleName}"; item.ShortName = $"{examPlan.ShortName}{sampleName}"; await item.InsertAsync(); } /// /// 更新监测抽样方案 /// /// /// public async Task Update(UpdateExamSampleInput input) { if (!await rep.AnyAsync(t => t.Id == input.Id)) { throw Oops.Oh(ErrorCode.E2001); } var item = input.Adapt(); await item.UpdateIncludeAsync([ nameof(item.Name), nameof(item.FullName), nameof(item.ShortName), nameof(item.Remark), nameof(item.ExamScoreRefExamPlanId), nameof(item.Config) ]); } /// /// 复制抽样方案信息 /// /// /// public async Task Duplicate(BaseId input) { var item = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var examPlan = await rep.Change().DetachedEntities.FirstOrDefaultAsync(t => t.Id == item.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); var maxSeq = await rep.DetachedEntities.Where(t => t.ExamPlanId == item.ExamPlanId).MaxAsync(t => (short?)t.Sequence); short sequence = (short)(maxSeq.HasValue ? (maxSeq + 1) : 1); string sampleName = $"学生抽样方案{ConvertUtil.ConvertToChinese(sequence)}"; ExamSample newItem = new() { ExamPlanId = item.ExamPlanId, Sequence = sequence, FullName = $"{examPlan.FullName}{sampleName}", Name = $"{examPlan.Name}{sampleName}", ShortName = $"{examPlan.ShortName}{sampleName}", Status = ExamSampleStatus.INITIAL, ExamScoreRefExamPlanId = item.ExamScoreRefExamPlanId, Config = item.Config, Remark = item.Remark, }; await newItem.InsertAsync(); } /// /// 删除监测抽样方案 /// /// /// public async Task Del(BaseId input) { var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var examPlan = await rep.Change().DetachedEntities.FirstOrDefaultAsync(t => t.Id == item.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); if (examPlan.IsFixedExamSample || item.Status == ExamSampleStatus.RUNNING || item.IsSelected == true) { throw Oops.Oh(ErrorCode.E3001); } // 批量删除已抽测学生 await rep.Change().Where(t => t.ExamSampleId == item.Id).ExecuteDeleteAsync(); // 删除抽样方案 await item.DeleteNowAsync(); } /// /// 保存全抽班级ID列表 /// /// /// public async Task SaveExamSampleAllClasses(SaveExamSampleAllClasses input) { var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var config = JSON.Deserialize(item.Config); config.SampleAllSchoolClassIds = input.ClassIds; item.Config = JSON.Serialize(config); await item.UpdateIncludeAsync([nameof(item.Config)]); } /// /// 切换全抽班级 /// /// /// public async Task SwitchExamSampleAllClass(SwitchExamSampleAllClassInput input) { var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var config = JSON.Deserialize(item.Config); if (input.IsAdd && !config.SampleAllSchoolClassIds.Any(t => t == input.SchoolClassId)) { config.SampleAllSchoolClassIds.Add(input.SchoolClassId); } else if (!input.IsAdd && config.SampleAllSchoolClassIds.Any(t => t == input.SchoolClassId)) { config.SampleAllSchoolClassIds.Remove(input.SchoolClassId); } item.Config = JSON.Serialize(config); await item.UpdateIncludeAsync([nameof(item.Config)]); } /// /// 选定方案 /// /// /// public async Task SelectSample(BaseId input) { var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var examPlan = await rep.Change().FirstOrDefaultAsync(t => t.Id == item.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); // 不能多选 if (examPlan.IsFixedExamSample || await rep.AnyAsync(t => t.ExamPlanId == item.ExamPlanId && t.Id != input.Id && t.IsSelected == true)) { throw Oops.Oh(ErrorCode.E3006); } item.IsSelected = true; item.SelectedTime = DateTime.Now; item.SelectedSysUserId = CurrentSysUserInfo.SysUserId; examPlan.IsFixedExamSample = true; await item.UpdateAsync(); await examPlan.UpdateAsync(); await UpdateOrgReportSchoolExamScoreStatus(examPlan.Id); } /// /// 取消选定 /// /// /// public async Task UnselectSample(BaseId input) { var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001); var published = await rep.Change().DetachedEntities.AnyAsync(t => t.ExamPlanId == item.ExamPlanId && ( (t.Type == DataPublishType.STUDENT_SAMPLE_LIST && t.Status == PublishStatus.PUBLISHED) || (t.Type == DataPublishType.STUDENT_SAMPLE_COUNT_LIST && t.Status == PublishStatus.PUBLISHED) )); // 抽样名单已发布不能取消 if (published) { throw Oops.Oh(ErrorCode.E3005); } var examPlan = await rep.Change().FirstOrDefaultAsync(t => t.Id == item.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); item.IsSelected = false; item.SelectedTime = DateTime.Now; item.SelectedSysUserId = CurrentSysUserInfo.SysUserId; examPlan.IsFixedExamSample = false; await item.UpdateAsync(); await examPlan.UpdateAsync(); } /// /// 检查监测计划中是否已有选定的抽样方案 /// /// 监测计划ID /// public async Task CheckSelectedByExamPlanId(int examPlanId) { var plan = await examPlanService.GetById(examPlanId); if (!plan.IsFixedExamSample) { return false; } return await rep.DetachedEntities.AnyAsync(t => t.ExamPlanId == examPlanId && t.IsSelected == true); } /// /// 更新机构是否需要上报校考成绩状态 /// /// 监测计划ID /// public async Task UpdateOrgReportSchoolExamScoreStatus(int examPlanId) { if (!await CheckSelectedByExamPlanId(examPlanId)) { throw Oops.Oh(ErrorCode.E3008); } await rep.SqlNonQueryAsync($@" CREATE TEMPORARY TABLE tmp_selected_orgs AS SELECT DISTINCT T2.sys_org_id FROM exam_sample_student AS T1 JOIN exam_student AS T2 ON T1.exam_student_id = T2.id JOIN exam_sample AS T3 ON T1.exam_sample_id = T3.id WHERE T3.exam_plan_id = @examPlanId AND T1.exam_sample_type = @examSampleType AND T3.is_selected = 1 ; UPDATE exam_org SET is_report_school_exam_score = 0 WHERE exam_plan_id = @examPlanId AND is_required_exam = 1 ; UPDATE exam_org AS eo JOIN tmp_selected_orgs AS selected_orgs ON eo.sys_org_id = selected_orgs.sys_org_id SET eo.is_report_school_exam_score = 1 WHERE eo.exam_plan_id = @examPlanId AND eo.is_required_exam = 1 ; DROP TEMPORARY TABLE tmp_selected_orgs ; ", new { ExamPlanId = examPlanId, ExamSampleType = (short)ExamSampleType.SCHOOL_EXAM }); } /// /// 执行抽样 /// /// 抽样方案ID /// /// public async Task ExecuteSample(BaseId input) { // 抽样方案 var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001, "抽样方案"); // 监测计划 var plan = await rep.Change().DetachedEntities .ProjectToType() .FirstOrDefaultAsync(t => t.Id == item.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); // 监测年级字典 var examGradeDict = plan.ExamGrades.ToDictionary(t => t.GradeId); var examSample = item.Adapt(); try { var sampleItem = item.Adapt(); // 更新状态为【生成中】 item.Status = ExamSampleStatus.RUNNING; await item.UpdateIncludeNowAsync([nameof(item.Status)]); // 获取所有学生信息 var stus = await GetStudentScoreList(sampleItem.Config, item.ExamScoreRefExamPlanId, item.ExamPlanId, plan.ExamGrades.Select(t => t.GradeId).Distinct().ToList()); // 生成监测名单 var sampleStus = BuildSampleList(sampleItem.Config, examGradeDict, stus); // 生成监测号 var finalSampleStus = BuildExamNumber(examSample, examGradeDict, sampleStus); // 删除已存在数据 string deleteSql = $"DELETE FROM exam_sample_student WHERE exam_sample_id = {input.Id}"; await rep.SqlNonQueryAsync(deleteSql); #region 批量写入 int si = 0; int stuCount = finalSampleStus.Count; List insertValues = []; foreach (var stu in finalSampleStus) { si++; var iss = stu.IsSpecialStudent ? 1 : 0; string valueSql = $"({stu.ExamSampleId}, {stu.ExamStudentId}, '{stu.ExamNumber}', {stu.Sequence}, {(short)stu.ExamSampleType}, {iss}, {stu.PreTotalScore}, {stu.CyclicNumber})"; insertValues.Add(valueSql); // 一次写入2000行 if (si % 2000 == 0 || si == stuCount) { string insertSql = $"INSERT INTO exam_sample_student(exam_sample_id, exam_student_id, exam_number, sequence, exam_sample_type, is_special_student, pre_total_score, cyclic_number) VALUES {string.Join(", ", insertValues)}"; await rep.SqlNonQueryAsync(insertSql); insertValues.Clear(); } } #endregion // 更新状态为【已生成】 item.Status = ExamSampleStatus.SUCCESSFUL; item.ExecuteLog = "生成完成"; await item.UpdateIncludeNowAsync([nameof(item.Status), nameof(item.ExecuteLog)]); } catch (Exception ex) { // 更新状态为【已失败】 item.Status = ExamSampleStatus.FAILED; item.ExecuteLog = $"【生成失败】{ex.Message}"; await item.UpdateIncludeNowAsync([nameof(item.Status), nameof(item.ExecuteLog)]); throw new Exception(ex.Message); } } /// /// 启动监测计划所有抽样方案生成 /// /// /// /// public async Task ExecuteSampleByExamPlanId(int examPlanId) { // 监测计划 var plan = await examPlanService.GetById(examPlanId); // 监测计划已选定抽样方案不能再生成 if (plan.IsFixedExamSample) { throw Oops.Oh(ErrorCode.E3004); } // 监测年级字典 var examGradeDict = plan.ExamGrades.ToDictionary(t => t.GradeId); try { // 更新监测计划状态 await rep.SqlNonQueryAsync($"UPDATE exam_plan SET sample_status = {(int)ExamSampleStatus.RUNNING} WHERE id = {examPlanId}"); // 抽样方案列表 var sampleItems = await rep.Where(t => t.ExamPlanId == plan.Id).ToListAsync(); // 更新所有抽样方案状态 await rep.SqlNonQueryAsync($"UPDATE exam_sample SET status = {(int)ExamSampleStatus.RUNNING} WHERE exam_plan_id = {examPlanId}"); foreach (var item in sampleItems) { var examSample = item.Adapt(); try { var sampleItem = item.Adapt(); // 获取所有学生信息 var stus = await GetStudentScoreList(sampleItem.Config, item.ExamScoreRefExamPlanId, item.ExamPlanId, plan.ExamGrades.Select(t => t.GradeId).Distinct().ToList()); // 生成监测名单 var sampleStus = BuildSampleList(sampleItem.Config, examGradeDict, stus); // 生成监测号 var finalSampleStus = BuildExamNumber(examSample, examGradeDict, sampleStus); // 删除已存在数据 rep.Database.SetCommandTimeout(60000); await rep.SqlNonQueryAsync("DELETE FROM exam_sample_student WHERE exam_sample_id = @id", new { item.Id }); #region 批量写入 int si = 0; int stuCount = finalSampleStus.Count; List insertValues = []; foreach (var stu in finalSampleStus) { si++; var iss = stu.IsSpecialStudent ? 1 : 0; string valueSql = $"({stu.ExamSampleId}, {stu.ExamStudentId}, '{stu.ExamNumber}', {stu.Sequence}, {(short)stu.ExamSampleType}, {iss}, {stu.PreTotalScore}, {stu.CyclicNumber})"; insertValues.Add(valueSql); // 一次写入2000行 if (si % 2000 == 0 || si == stuCount) { string insertSql = $"INSERT INTO exam_sample_student(exam_sample_id, exam_student_id, exam_number, sequence, exam_sample_type, is_special_student, pre_total_score, cyclic_number) VALUES {string.Join(", ", insertValues)}"; await rep.SqlNonQueryAsync(insertSql); insertValues.Clear(); } } #endregion // 更新状态为【已生成】 await rep.SqlNonQueryAsync($"UPDATE exam_sample SET status = @status, execute_log = '生成完成' WHERE id = @id", new { item.Id, Status = ExamSampleStatus.SUCCESSFUL }); } catch (Exception ex) { // 更新状态为【已失败】 await rep.SqlNonQueryAsync($"UPDATE exam_sample SET status = @status, execute_log = @log WHERE id = @id", new { item.Id, Status = ExamSampleStatus.FAILED, Log = $"【生成失败】{ex.Message}" }); } } // 更新监测计划状态 await rep.SqlNonQueryAsync($"UPDATE exam_plan SET sample_status={(int)ExamSampleStatus.SUCCESSFUL} WHERE id = {examPlanId}"); } catch { // 更新监测计划状态 await rep.SqlNonQueryAsync($"UPDATE exam_plan SET sample_status={(int)ExamSampleStatus.FAILED} WHERE id = {examPlanId}"); } } #endregion #region 导出抽样数据 /// /// 导出抽样方案存档文件 /// /// /// /// 是否包含特殊学生数 /// public async Task<(string fileName, byte[] fileBytes)> ExportSampleList(int id, bool hideIdNumber = false, bool includeSpecialStudentCount = false) { // 抽样方案 var examSample = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001, "抽样方案"); // 监测计划 var plan = await rep.Change().DetachedEntities .ProjectToType() .FirstOrDefaultAsync(t => t.Id == examSample.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); // 监测年级字典 var examGradeDict = plan.ExamGrades.ToDictionary(t => t.GradeId); // 获取证件类型 var cts = await sysDictDataService.GetListByDictTypeId(304); var certificateTypes = cts.ToDictionary(x => (Core.CertificateType)x.Value, y => y.Name); // 临时存放目录 string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}"); Directory.CreateDirectory(fileRoot); // 抽样文件目录 string sampleFileRoot = Path.Combine(fileRoot, examSample.FullName); string selectStusSql = @$" SELECT T2.sys_org_id, T4.full_name AS sys_org_full_name, T4.`name` AS sys_org_name, T4.`code` AS sys_org_code, T2.sys_org_branch_id, T5.`name` AS sys_org_branch_name, T2.grade_id, T3.grade_number, T3.`name` AS grade_name, T2.school_class_id, T2.class_number, T1.exam_student_id, T2.`name` AS exam_student_name, T2.certificate_type, T2.id_number, T1.exam_number, T1.exam_sample_type FROM exam_sample_student AS T1 JOIN exam_student AS T2 ON T1.exam_student_id = T2.id JOIN base_grade AS T3 ON T2.grade_id = T3.id JOIN sys_org AS T4 ON T2.sys_org_id = T4.id LEFT JOIN sys_org AS T5 ON T2.sys_org_branch_id = T5.id WHERE T1.exam_sample_id = {id} "; // 所有学生 var stus = await rep.SqlQueriesAsync(selectStusSql); // 按年级生成考生文件 var ws = stus.Select(t => new { t.SysOrgId, t.SysOrgFullName, t.SysOrgCode, t.SysOrgBranchId, t.SysOrgBranchName, t.GradeId }) .OrderBy(t => t.SysOrgId).ThenBy(t => t.SysOrgBranchId).ThenBy(t => t.GradeId).Distinct().ToList(); foreach (var w in ws) { // 监测年级 var eg = examGradeDict[w.GradeId]; // 学校目录 string orgDir = Path.Combine(sampleFileRoot, $"{w.SysOrgCode}-{w.SysOrgFullName}"); if (!Directory.Exists(orgDir)) { Directory.CreateDirectory(orgDir); } // 校区名称 string branchName = ""; if (w.SysOrgBranchId != null) { branchName = $"@{w.SysOrgBranchName}"; } #region 导出抽中学生 string sampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.DISTRICT.GetDescription()}-学生检录表"; // 抽中学生列表 var sampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.DISTRICT) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { sampleStus = sampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> cols = new() { new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "监测号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, }; if (w.SysOrgBranchId != null) { cols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } if (!hideIdNumber) { cols.Add(new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }); cols.Add(new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }); } cols.Add(new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }); // 导出EXCEL文件 var ret = exportExcelService.ExportExcel(new ExportExcelDto() { Title = sampleFileName, Columns = cols, Items = sampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{sampleFileName}.xlsx"), ret); #endregion #region 导出未抽中学生 string noSampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.SCHOOL_EXAM.GetDescription()}-学生检录表"; // 抽中学生列表 var noSampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { noSampleStus = noSampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> noCols = [ new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "考号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, ]; if (w.SysOrgBranchId != null) { noCols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } if (!hideIdNumber) { noCols.Add(new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }); noCols.Add(new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }); } noCols.Add(new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }); // 导出EXCEL文件 var noRet = exportExcelService.ExportExcel(new ExportExcelDto() { Title = noSampleFileName, Columns = noCols, Items = noSampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{noSampleFileName}.xlsx"), noRet); #endregion } // 统计表文件 var (fileName, fileBytes) = await ExportSampleCount(id, includeSpecialStudentCount); await File.WriteAllBytesAsync(Path.Combine(sampleFileRoot, fileName), fileBytes); string outFileName = $"{examSample.FullName}.zip"; string outFilePath = Path.Combine(fileRoot, outFileName); ICSharpCode.SharpZipLib.Zip.FastZip zip = new(); zip.CreateZip(outFilePath, sampleFileRoot, true, string.Empty); var retBytes = await File.ReadAllBytesAsync(outFilePath); Directory.Delete(fileRoot, true); return (outFileName, retBytes); } /// /// 导出抽样方案存档文件 /// /// /// /// 是否包含特殊学生数 /// public async Task<(string fileName, byte[] fileBytes)> ExportToArchived(int id, bool hideIdNumber = false, bool includeSpecialStudentCount = false) { // 抽样方案 var examSample = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001, "抽样方案"); // 监测计划 var plan = await rep.Change().DetachedEntities .ProjectToType() .FirstOrDefaultAsync(t => t.Id == examSample.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); // 监测年级字典 var examGradeDict = plan.ExamGrades.ToDictionary(t => t.GradeId); // 获取证件类型 var cts = await sysDictDataService.GetListByDictTypeId(304); var certificateTypes = cts.ToDictionary(x => (Core.CertificateType)x.Value, y => y.Name); // 临时存放目录 string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}"); Directory.CreateDirectory(fileRoot); // 抽样文件目录 string sampleFileRoot = Path.Combine(fileRoot, examSample.FullName); string selectStusSql = @$" SELECT T2.sys_org_id, T4.full_name AS sys_org_full_name, T4.`name` AS sys_org_name, T4.`code` AS sys_org_code, T2.sys_org_branch_id, T5.`name` AS sys_org_branch_name, T2.grade_id, T3.grade_number, T3.`name` AS grade_name, T2.school_class_id, T2.class_number, T1.exam_student_id, T2.`name` AS exam_student_name, T2.certificate_type, T2.id_number, T1.exam_number, T1.exam_sample_type FROM exam_sample_student AS T1 JOIN exam_student AS T2 ON T1.exam_student_id = T2.id JOIN base_grade AS T3 ON T2.grade_id = T3.id JOIN sys_org AS T4 ON T2.sys_org_id = T4.id LEFT JOIN sys_org AS T5 ON T2.sys_org_branch_id = T5.id WHERE T1.exam_sample_id = {id} "; // 所有学生 var stus = await rep.SqlQueriesAsync(selectStusSql); // 按年级生成考生文件 var ws = stus.Select(t => new { t.SysOrgId, t.SysOrgFullName, t.SysOrgCode, t.SysOrgBranchId, t.SysOrgBranchName, t.GradeId }) .OrderBy(t => t.SysOrgId).ThenBy(t => t.SysOrgBranchId).ThenBy(t => t.GradeId).Distinct().ToList(); foreach (var w in ws) { // 监测年级 var eg = examGradeDict[w.GradeId]; // 学校目录 string orgDir = Path.Combine(sampleFileRoot, $"{w.SysOrgCode}-{w.SysOrgFullName}"); if (!Directory.Exists(orgDir)) { Directory.CreateDirectory(orgDir); } // 校区名称 string branchName = ""; if (w.SysOrgBranchId != null) { branchName = $"@{w.SysOrgBranchName}"; } #region 导出抽中学生 string sampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.DISTRICT.GetDescription()}-学生检录表"; // 抽中学生列表 var sampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.DISTRICT) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { sampleStus = sampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> cols = new() { new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "监测号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, }; if (w.SysOrgBranchId != null) { cols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } if (!hideIdNumber) { cols.Add(new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }); cols.Add(new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }); } cols.Add(new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }); // 导出EXCEL文件 var ret = exportExcelService.ExportExcel(new ExportExcelDto() { Title = sampleFileName, Columns = cols, Items = sampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{sampleFileName}.xlsx"), ret); #endregion #region 导出未抽中学生 string noSampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.SCHOOL_EXAM.GetDescription()}-学生检录表"; // 抽中学生列表 var noSampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { noSampleStus = noSampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> noCols = new() { new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "考号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, }; if (w.SysOrgBranchId != null) { noCols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } if (!hideIdNumber) { noCols.Add(new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }); noCols.Add(new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }); } noCols.Add(new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }); // 导出EXCEL文件 var noRet = exportExcelService.ExportExcel(new ExportExcelDto() { Title = noSampleFileName, Columns = noCols, Items = noSampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{noSampleFileName}.xlsx"), noRet); #endregion } // 统计表文件 var (fileName, fileBytes) = await ExportSampleCount(id, includeSpecialStudentCount); await File.WriteAllBytesAsync(Path.Combine(sampleFileRoot, fileName), fileBytes); string outFileName = $"{examSample.FullName}.zip"; string outFilePath = Path.Combine(fileRoot, outFileName); ICSharpCode.SharpZipLib.Zip.FastZip zip = new(); zip.CreateZip(outFilePath, sampleFileRoot, true, string.Empty); var retBytes = await File.ReadAllBytesAsync(outFilePath); Directory.Delete(fileRoot, true); return (outFileName, retBytes); } /// /// 导出给印刷厂和网阅机构文件 /// /// /// public async Task<(string fileName, byte[] fileBytes)> ExportToPrintshop(int id) { //var item = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id && t.Status == ExamSampleStatus.SUCCESSFUL && t.IsSelected == true); //return item == null ? throw Oops.Oh(ErrorCode.E2006) : await ExportToArchived(id, true, false); var ret = await ExportToArchived(id, true, false); ret.fileName = $"印网使用-{ret.fileName}"; return ret; } /// /// 导出给学校 /// /// /// public async Task<(string fileName, byte[] fileBytes)> ExportToOrg(int id) { // 抽样方案 var examSample = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id && t.ExamPlan.IsFixedExamSample == true && t.Status == ExamSampleStatus.SUCCESSFUL && t.IsSelected == true) ?? throw Oops.Oh(ErrorCode.E2001, "抽样方案"); // 监测计划 var plan = await rep.Change().DetachedEntities .ProjectToType() .FirstOrDefaultAsync(t => t.Id == examSample.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测计划"); // 必须发布了的才能下载 var pb = await rep.Change().DetachedEntities.AnyAsync(t => t.ExamPlanId == plan.Id && t.Type == DataPublishType.STUDENT_SAMPLE_LIST && t.Status == PublishStatus.PUBLISHED); if (!pb) { throw Oops.Oh(ErrorCode.E2006); } // 监测年级字典 var examGradeDict = plan.ExamGrades.ToDictionary(t => t.GradeId); // 获取证件类型 var cts = await sysDictDataService.GetListByDictTypeId(304); var certificateTypes = cts.ToDictionary(x => (Core.CertificateType)x.Value, y => y.Name); // 临时存放目录 string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}"); Directory.CreateDirectory(fileRoot); string selectStusSql = @$" SELECT T2.sys_org_id, T4.full_name AS sys_org_full_name, T4.`name` AS sys_org_name, T4.`code` AS sys_org_code, T2.sys_org_branch_id, T5.`name` AS sys_org_branch_name, T2.grade_id, T3.grade_number, T3.`name` AS grade_name, T2.school_class_id, T2.class_number, T1.exam_student_id, T2.`name` AS exam_student_name, T2.certificate_type, T2.id_number, T1.exam_number, T1.exam_sample_type FROM exam_sample_student AS T1 JOIN exam_student AS T2 ON T1.exam_student_id = T2.id JOIN base_grade AS T3 ON T2.grade_id = T3.id JOIN sys_org AS T4 ON T2.sys_org_id = T4.id LEFT JOIN sys_org AS T5 ON T2.sys_org_branch_id = T5.id WHERE T1.exam_sample_id = {id} AND T2.sys_org_id = {CurrentSysUserInfo.SysOrgId} "; // 所有学生 var stus = await rep.SqlQueriesAsync(selectStusSql); // 按年级生成考生文件 var ws = stus.Select(t => new { t.SysOrgId, t.SysOrgFullName, t.SysOrgCode, t.SysOrgBranchId, t.SysOrgBranchName, t.GradeId }) .OrderBy(t => t.SysOrgId).ThenBy(t => t.SysOrgBranchId).ThenBy(t => t.GradeId).Distinct().ToList(); string fileName = ""; foreach (var w in ws) { // 监测年级 var eg = examGradeDict[w.GradeId]; // 学校目录 if (fileName == "") { fileName = $"{w.SysOrgCode}-{w.SysOrgFullName}"; } string orgDir = Path.Combine(fileRoot, fileName); if (!Directory.Exists(orgDir)) { Directory.CreateDirectory(orgDir); } // 校区名称 string branchName = ""; if (w.SysOrgBranchId != null) { branchName = $"@{w.SysOrgBranchName}"; } #region 导出抽中学生 string sampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.DISTRICT.GetDescription()}-学生检录表"; // 抽中学生列表 var sampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.DISTRICT) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { sampleStus = sampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> cols = new() { new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "监测号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }, new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }, new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }, }; if (w.SysOrgBranchId != null) { cols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } // 导出EXCEL文件 var ret = exportExcelService.ExportExcel(new ExportExcelDto() { Title = sampleFileName, Columns = cols, Items = sampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{sampleFileName}.xlsx"), ret); #endregion #region 导出未抽中学生 string noSampleFileName = $"{w.SysOrgCode}-{w.SysOrgFullName}{branchName}-{eg.Grade.Name}-{ExamSampleType.SCHOOL_EXAM.GetDescription()}-学生检录表"; // 抽中学生列表 var noSampleStus = stus.Where(t => t.SysOrgId == w.SysOrgId && t.SysOrgBranchId == w.SysOrgBranchId && t.GradeId == w.GradeId && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM) .OrderBy(t => t.ExamNumber?[^4..]).ToList(); // 自编号按导入顺序 if (eg.IsRequiredSelfExamNumber) { noSampleStus = noSampleStus.OrderBy(t => t.ExamStudentId).ToList(); } // 定义EXCEL列 List> noCols = new() { new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.GradeNumber }, new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber }, new() { Name = "考号", Width = 14, GetCellValue = (r) => r.ExamNumber }, new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.ExamStudentName }, new() { Name = "证件类型", Width = 16, GetCellValue = (r) => certificateTypes[r.CertificateType] }, new() { Name = "证件号码", Width = 20, GetCellValue = (r) => r.IdNumber }, new() { Name = "名单类型", Width = 10, GetCellValue = (r) => r.ExamSampleType.GetDescription() }, }; if (w.SysOrgBranchId != null) { noCols.Insert(0, new() { Name = "校区", Width = 10, GetCellValue = (r) => r.SysOrgBranchName }); } // 导出EXCEL文件 var noRet = exportExcelService.ExportExcel(new ExportExcelDto() { Title = noSampleFileName, Columns = noCols, Items = noSampleStus, IncludeExportTime = false, }); // 写入文件 await File.WriteAllBytesAsync(Path.Combine(orgDir, $"{noSampleFileName}.xlsx"), noRet); #endregion } // 学生目录 var schoolFilePath = Path.Combine(fileRoot, fileName); // 统计表文件 var countFile = await ExportSampleCountToOrg(id); await File.WriteAllBytesAsync(Path.Combine(schoolFilePath, $"{fileName}-监测抽样统计表.xlsx"), countFile.fileBytes); string outFileName = $"{plan.FullName}-{fileName}-监测抽样学生检录表.zip"; string outFilePath = Path.Combine(fileRoot, outFileName); ICSharpCode.SharpZipLib.Zip.FastZip zip = new(); zip.CreateZip(outFilePath, schoolFilePath, true, string.Empty); var retBytes = await File.ReadAllBytesAsync(outFilePath); Directory.Delete(fileRoot, true); return (outFileName, retBytes); } /// /// 导出抽样统计表 /// /// /// 是否包含特殊学生数 /// public async Task<(string fileName, byte[] fileBytes)> ExportSampleCount(int id, bool includeSpecialStudentCount = false) { var examSample = await GetById(id); var items = await GetSampleCountListById(id); XSSFWorkbook wb = new(); ISheet sheet = wb.CreateSheet(); sheet.DisplayGridlines = false; // 获取样式 var cellStyle = exportExcelService.GetCellStyle(wb); #region 表头 int rowNum = 0; IRow headerRow = sheet.CreateRow(rowNum++); headerRow.Height = ExportExcelCellStyle.DefaultRowHeight; int ci = 0; exportExcelService.AddCell("数据类型", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell("学校代码", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell("学校名称", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 20); exportExcelService.AddCell("年级", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell("班级", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); if (includeSpecialStudentCount) { exportExcelService.AddCell("特殊学生", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); } exportExcelService.AddCell(ExamSampleType.DISTRICT.GetDescription(), headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell(ExamSampleType.SCHOOL_EXAM.GetDescription(), headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell("合计", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); sheet.CreateFreezePane(0, 1); #endregion foreach (var item in items) { IRow row = sheet.CreateRow(rowNum); row.Height = ExportExcelCellStyle.DefaultRowHeight; int si = 0; string schoolCode = item.SysOrgCode ?? ""; string schoolName = item.SysOrgName ?? ""; string gradeName = item.GradeName ?? ""; long classNumber = item.ClassNumber; ICellStyle cstyle = cellStyle.CenterCellStyle; switch (item.TypeId) { case 2: si = 3; break; case 3: si = 2; break; case 4: si = 3; schoolCode = ""; break; case 5: si = 2; schoolCode = ""; break; } if (si > 0) { cstyle = cellStyle.FillCellStyle; sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, si, 4)); } int rci = 0; exportExcelService.AddCell(item.TypeName, row, rci++, cstyle); exportExcelService.AddCell(schoolCode, row, rci++, cstyle); exportExcelService.AddCell(schoolName, row, rci++, cstyle); exportExcelService.AddCell(gradeName, row, rci++, cstyle); exportExcelService.AddCell(classNumber, row, rci++, cstyle); if (includeSpecialStudentCount) { exportExcelService.AddCell(item.TotalSpecialStudentCount, row, rci++, cstyle); } exportExcelService.AddCell(item.CenterStudentCount, row, rci++, cstyle); exportExcelService.AddCell(item.SchoolStudentCount, row, rci++, cstyle); exportExcelService.AddCell(item.TotalStudentCount, row, rci++, cstyle); rowNum++; } MemoryStream ms = new(); wb.Write(ms, false); ms.Flush(); return ($"{examSample.FullName}-监测抽样统计表.xlsx", ms.ToArray()); } /// /// 导出学校抽样统计表 /// /// /// public async Task<(string fileName, byte[] fileBytes)> ExportSampleCountToOrg(int id) { var examSample = await rep.DetachedEntities.Include(t => t.ExamPlan).FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001); var items = await GetSampleCountListById(id); items = items.Where(t => t.TypeId < 4 && t.SysOrgId == CurrentSysUserInfo.SysOrgId).ToList(); XSSFWorkbook wb = new(); ISheet sheet = wb.CreateSheet(); sheet.DisplayGridlines = false; // 获取样式 var cellStyle = exportExcelService.GetCellStyle(wb); #region 表头 int rowNum = 0; IRow headerRow = sheet.CreateRow(rowNum++); headerRow.Height = ExportExcelCellStyle.DefaultRowHeight; int ci = 0; exportExcelService.AddCell("数据类型", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell("学校代码", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell("学校名称", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 20); exportExcelService.AddCell("年级", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell("班级", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell("特殊学生", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 10); exportExcelService.AddCell(ExamSampleType.DISTRICT.GetDescription(), headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell(ExamSampleType.SCHOOL_EXAM.GetDescription(), headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); exportExcelService.AddCell("合计", headerRow, ci++, cellStyle.ColumnHeaderStyle, sheet, 12); sheet.CreateFreezePane(0, 1); #endregion foreach (var item in items) { IRow row = sheet.CreateRow(rowNum); row.Height = ExportExcelCellStyle.DefaultRowHeight; int si = 0; string schoolCode = item.SysOrgCode ?? ""; string schoolName = item.SysOrgName ?? ""; string gradeName = item.GradeName ?? ""; long classNumber = item.ClassNumber; ICellStyle cstyle = cellStyle.CenterCellStyle; switch (item.TypeId) { case 2: si = 3; break; case 3: si = 2; break; case 4: si = 3; schoolCode = ""; break; case 5: si = 2; schoolCode = ""; break; } if (si > 0) { cstyle = cellStyle.FillCellStyle; sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, si, 4)); } int rci = 0; exportExcelService.AddCell(item.TypeName, row, rci++, cstyle); exportExcelService.AddCell(schoolCode, row, rci++, cstyle); exportExcelService.AddCell(schoolName, row, rci++, cstyle); exportExcelService.AddCell(gradeName, row, rci++, cstyle); exportExcelService.AddCell(classNumber, row, rci++, cstyle); exportExcelService.AddCell(item.TotalSpecialStudentCount, row, rci++, cstyle); exportExcelService.AddCell(item.CenterStudentCount, row, rci++, cstyle); exportExcelService.AddCell(item.SchoolStudentCount, row, rci++, cstyle); exportExcelService.AddCell(item.TotalStudentCount, row, rci++, cstyle); rowNum++; } MemoryStream ms = new(); wb.Write(ms, false); ms.Flush(); return ($"{examSample.ExamPlan.FullName}-{CurrentSysUserInfo.SysOrgName}-监测抽样统计表.xlsx", ms.ToArray()); } #endregion #region 方案查询 /// /// 根据ID获取抽样方案 /// /// /// public async Task GetById(int id) { var item = await rep.DetachedEntities.ProjectToType().FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001); //item.Config.ExamSampleRefExamPlanId; return item.Adapt(); } /// /// 根据监测计划ID获取全部抽样方案 /// /// /// public async Task> GetListByExamPlanId(int examPlanId) { var items = await rep.DetachedEntities.Where(t => t.ExamPlanId == examPlanId).ProjectToType().ToListAsync(); return items; } /// /// 根据监测计划ID获取全部抽样方案的状态 /// /// /// public async Task> GetStatusListByExamPlanId(int examPlanId) { var items = await rep.DetachedEntities.Where(t => t.ExamPlanId == examPlanId).ProjectToType().ToListAsync(); return items; } /// /// 获取抽样统计表 /// /// /// public async Task> GetSampleCountListById(int id) { var examSample = await GetById(id); string whereSql = $"WHERE T1.exam_plan_id = {examSample.ExamPlanId} AND T2.exam_sample_id = {id}"; string querySql = @$" SELECT ROW_NUMBER() OVER (ORDER BY T2.`code`, T1.grade_id, T1.class_number, T1.type_id) AS id, T1.type_id, T3.`name` AS type_name, T1.sys_org_id, T2.`code` AS sys_org_code, T2.`name` AS sys_org_name, T2.full_name AS sys_org_full_name, T1.grade_id, T4.`name` AS grade_name, T1.school_class_id, T1.class_number, T1.total_student_count, T1.center_student_count, T1.school_student_count, T1.total_special_student_count FROM ( -- 班级汇总 SELECT {(int)ExamSampleCountType.SCHOOL_CLASS} AS type_id, T.sys_org_id, T.grade_id, T.school_class_id, T.class_number, SUM(CASE WHEN T.exam_sample_type = @district THEN T.student_count ELSE 0 END) AS center_student_count, SUM(CASE WHEN T.exam_sample_type = @schoolExam THEN T.student_count ELSE 0 END) AS school_student_count, SUM(T.student_count) AS total_student_count, SUM(T.special_student_count) AS total_special_student_count FROM ( SELECT T1.sys_org_id, T1.grade_id, T1.school_class_id, T1.class_number, T2.exam_sample_type, COUNT(1) AS student_count, SUM(T2.is_special_student) AS special_student_count FROM exam_student AS T1 JOIN exam_sample_student AS T2 ON T1.id = T2.exam_student_id {whereSql} GROUP BY T1.sys_org_id, T1.grade_id, T1.school_class_id, T1.class_number, T2.exam_sample_type ) AS T GROUP BY T.sys_org_id, T.grade_id, T.school_class_id, T.class_number UNION ALL -- 年级汇总 SELECT {(int)ExamSampleCountType.SCHOOL_GRADE} AS type_id, T.sys_org_id, T.grade_id, NULL AS school_class_id, 9999 AS class_number, SUM(CASE WHEN T.exam_sample_type = @district THEN T.student_count ELSE 0 END) AS center_student_count, SUM(CASE WHEN T.exam_sample_type = @schoolExam THEN T.student_count ELSE 0 END) AS school_student_count, SUM(T.student_count) AS total_student_count, SUM(T.special_student_count) AS total_special_student_count FROM ( SELECT T1.sys_org_id, T1.grade_id, T2.exam_sample_type, COUNT(1) AS student_count, SUM(T2.is_special_student) AS special_student_count FROM exam_student AS T1 JOIN exam_sample_student AS T2 ON T1.id = T2.exam_student_id {whereSql} GROUP BY T1.sys_org_id, T1.grade_id, T2.exam_sample_type ) AS T GROUP BY T.sys_org_id, T.grade_id UNION ALL -- 学校汇总 SELECT {(int)ExamSampleCountType.SCHOOL} AS type_id, T.sys_org_id, 9999 AS grade_id, NULL AS school_class_id, 9999 AS class_number, SUM(CASE WHEN T.exam_sample_type = @district THEN T.student_count ELSE 0 END) AS center_student_count, SUM(CASE WHEN T.exam_sample_type = @schoolExam THEN T.student_count ELSE 0 END) AS school_student_count, SUM(T.student_count) AS total_student_count, SUM(T.special_student_count) AS total_special_student_count FROM ( SELECT T1.sys_org_id, T2.exam_sample_type, COUNT(1) AS student_count, SUM(T2.is_special_student) AS special_student_count FROM exam_student AS T1 JOIN exam_sample_student AS T2 ON T1.id = T2.exam_student_id {whereSql} GROUP BY T1.sys_org_id, T2.exam_sample_type ) AS T GROUP BY T.sys_org_id UNION ALL -- 全区年级汇总 SELECT {(int)ExamSampleCountType.GRADE} AS type_id, 9999 AS sys_org_id, T.grade_id, NULL AS school_class_id, 9999 AS class_number, SUM(CASE WHEN T.exam_sample_type = @district THEN T.student_count ELSE 0 END) AS center_student_count, SUM(CASE WHEN T.exam_sample_type = @schoolExam THEN T.student_count ELSE 0 END) AS school_student_count, SUM(T.student_count) AS total_student_count, SUM(T.special_student_count) AS total_special_student_count FROM ( SELECT T1.grade_id, T2.exam_sample_type, COUNT(1) AS student_count, SUM(T2.is_special_student) AS special_student_count FROM exam_student AS T1 JOIN exam_sample_student AS T2 ON T1.id = T2.exam_student_id {whereSql} GROUP BY T1.grade_id, T2.exam_sample_type ) AS T GROUP BY T.grade_id UNION ALL -- 全区汇总 SELECT {(int)ExamSampleCountType.ALL} AS type_id, 9999 AS sys_org_id, 9999 AS grade_id, NULL AS school_class_id, 9999 AS class_number, SUM(CASE WHEN T.exam_sample_type = @district THEN T.student_count ELSE 0 END) AS center_student_count, SUM(CASE WHEN T.exam_sample_type = @schoolExam THEN T.student_count ELSE 0 END) AS school_student_count, SUM(T.student_count) AS total_student_count, SUM(T.special_student_count) AS total_special_student_count FROM ( SELECT T2.exam_sample_type, COUNT(1) AS student_count, SUM(T2.is_special_student) AS special_student_count FROM exam_student AS T1 JOIN exam_sample_student AS T2 ON T1.id = T2.exam_student_id {whereSql} GROUP BY T2.exam_sample_type ) AS T ) AS T1 LEFT JOIN ( SELECT id, `code`, `name`, full_name FROM sys_org WHERE org_type = {(short)OrgType.SCHOOL} UNION ALL SELECT 9999 AS id, '999' AS `code`, '全区' AS `name`, '全区' AS full_name ) AS T2 ON T1.sys_org_id = T2.id JOIN (SELECT id, `value` AS type_id, `name` FROM `sys_dict_data` WHERE sys_dict_type_id = 112) AS T3 ON T1.type_id = T3.type_id LEFT JOIN base_grade AS T4 ON T1.grade_id = T4.id ORDER BY T2.`code`, T1.grade_id, T1.class_number, T1.type_id "; var items = await rep.SqlQueriesAsync(querySql, new { District = (short)ExamSampleType.DISTRICT, SchoolExam = (short)ExamSampleType.SCHOOL_EXAM }); return items; } /// /// 获取学校抽样统计表 /// /// /// public async Task> GetOrgSampleCountListById(int id) { var items = await GetSampleCountListById(id); items = items.Where(t => t.TypeId < 4 && t.SysOrgId == CurrentSysUserInfo.SysOrgId).ToList(); return items; } /// /// 查询已发布抽样 /// /// 监测发布内容ID /// 抽样数据发布类型 /// public async Task GetByExamDataPublishId(int examDataPublishId, DataPublishType type) { if (type != DataPublishType.STUDENT_SAMPLE_LIST && type != DataPublishType.STUDENT_SAMPLE_COUNT_LIST) { throw Oops.Oh(ErrorCode.E1014, "数据发布"); } var pub = await rep.Change().DetachedEntities.FirstOrDefaultAsync(t => t.Id == examDataPublishId && t.Type == type) ?? throw Oops.Oh(ErrorCode.E2001, "反馈内容"); var item = await rep.DetachedEntities.Include(t => t.ExamPlan) .FirstOrDefaultAsync(t => t.ExamPlanId == pub.ExamPlanId && t.ExamPlan.IsFixedExamSample == true && t.Status == ExamSampleStatus.SUCCESSFUL && t.IsSelected == true) ?? throw Oops.Oh(ErrorCode.E2001, "抽样方案"); return item.Adapt(); } #endregion #region 机构查询 #endregion #region 私有方法 /// /// 生成监测号 /// /// /// /// /// private static List BuildExamNumber(ExamSampleOutput examSample, Dictionary examGradeDict, List stus) { // 抽样配置 var config = examSample.Config; // 返回结果集 List retItems = []; // 遍历集合 var ws = stus.Select(t => new { t.SysOrgId, t.SysOrgBranchId, t.GradeId, t.SchoolClassId }).Distinct().ToList(); // 学校 var orgs = ws.Select(t => t.SysOrgId).Distinct().ToList(); foreach (var orgId in orgs) { // 跳过不参与监测的学校 if (config.ExcludeSysOrgIds.Contains(orgId)) { continue; } // 校区 var branches = ws.Where(t => t.SysOrgId == orgId).Select(t => t.SysOrgBranchId).Distinct().ToList(); foreach (var branchId in branches) { // 年级 var grades = ws.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId).Select(t => t.GradeId).Distinct().ToList(); foreach (var gradeId in grades) { var eg = examGradeDict[gradeId]; // 自编号直接写入 if (eg.IsRequiredSelfExamNumber) { var selfList = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.IsSelfExamNumber == true).OrderBy(t => t.ExamNumber?[^4..]).ToList(); retItems.AddRange(selfList.Select((t, i) => new AddExamSampleStudentInput { ExamSampleId = examSample.Id, ExamStudentId = t.ExamStudentId, ExamNumber = t.ExamNumber, ExamSampleType = t.ExamSampleType ?? ExamSampleType.DISTRICT, IsSpecialStudent = t.IsSpecialStudent, PreTotalScore = t.TotalScore ?? 0, Sequence = i + 1, CyclicNumber = t.CyclicNumber, })); continue; } // 区测学生列表 var sampleStus = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.ExamSampleType == ExamSampleType.DISTRICT) .OrderBy(t => t.ClassNumber).ThenBy(t => t.Sequence).ToList(); // 区测学生数量 var sampleCount = sampleStus.Count; // 是否在年级内随机打乱顺序 if (config.IsGradeSeatNumberRandom) { Utils.ListRandom(sampleStus); } // 生成区测学生监测号并插入 retItems.AddRange(sampleStus.Select((t, i) => new AddExamSampleStudentInput { ExamSampleId = examSample.Id, ExamStudentId = t.ExamStudentId, ExamNumber = GetExamNumber(t.SysOrgCode, eg.Grade.GradeNumber, t.ClassNumber, i + 1), ExamSampleType = ExamSampleType.DISTRICT, IsSpecialStudent = t.IsSpecialStudent, PreTotalScore = t.TotalScore ?? 0, Sequence = i + 1, CyclicNumber = t.CyclicNumber, })); // 校测学生列表 var noSampleStus = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM) .OrderBy(t => t.ClassNumber).ThenBy(t => t.Sequence).ToList(); // 是否在年级内随机打乱顺序 if (config.IsGradeSeatNumberRandom) { Utils.ListRandom(noSampleStus); } // 生成校测学生监测号并插入 retItems.AddRange(noSampleStus.Select((t, i) => new AddExamSampleStudentInput { ExamSampleId = examSample.Id, ExamStudentId = t.ExamStudentId, ExamNumber = GetExamNumber(t.SysOrgCode, eg.Grade.GradeNumber, t.ClassNumber, sampleCount + 100 + i + 1), ExamSampleType = ExamSampleType.SCHOOL_EXAM, IsSpecialStudent = t.IsSpecialStudent, PreTotalScore = t.TotalScore ?? 0, Sequence = i + 1, CyclicNumber = t.CyclicNumber, })); } } } return retItems; } /// /// 生成监测号 /// /// 学校代码 /// 年级序号 /// 班级序号 /// 顺序号 /// private static string GetExamNumber(string sysOrgCode, short gradeNumber, short classNumber, int sequence) { return $"{sysOrgCode.PadLeft(3, '0')}{gradeNumber.ToString().PadLeft(2, '0')}{classNumber.ToString().PadLeft(2, '0')}{sequence.ToString().PadLeft(4, '0')}"; } /// /// 生成抽样名单 /// /// 抽样配置 /// 监测年级字典,键为年级ID /// 学生列表 /// private static List BuildSampleList(ExamSampleConfig config, Dictionary examGradeDict, List stus) { // 抽样比例 var sampleRate = config.Percent / 100.0; // 返回结果集 List retItems = []; // 遍历集合 var ws = stus.Select(t => new { t.SysOrgId, t.SysOrgBranchId, t.GradeId, t.SchoolClassId }).Distinct().ToList(); // 学校 var orgs = ws.Select(t => t.SysOrgId).Distinct().ToList(); foreach (var orgId in orgs) { // 跳过不参与监测的学校 if (config.ExcludeSysOrgIds.Contains(orgId)) { continue; } // 校区 var branches = ws.Where(t => t.SysOrgId == orgId).Select(t => t.SysOrgBranchId).Distinct().ToList(); foreach (var branchId in branches) { // 年级 var grades = ws.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId).Select(t => t.GradeId).Distinct().ToList(); foreach (var gradeId in grades) { // 班级 var classes = ws.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId).Select(t => t.SchoolClassId).Distinct().Order().ToList(); var classCount = classes.Count; // 1.年级自编号直接写入 var eg = examGradeDict[gradeId]; if (eg != null && eg.IsRequiredSelfExamNumber) { var items = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId).ToList(); foreach (var item in items) { item.ExamSampleType = ExamSampleType.DISTRICT; item.IsSelfExamNumber = true; item.ExamNumber = item.ExamNumber.Trim().Replace(" ", ""); //if (item.ExamNumber.Length != eg.SelfExamNumberLength) //{ // throw Oops.Oh(ErrorCode.E1013, $"{item.ExamStudentId},{item.ExamStudentName},{item.ExamNumber}"); //} } retItems.AddRange(items.Where(t => t.ExamNumber.Length >= 4)); continue; } // 2.年级只有一个班,处理最低人数限制,满足条件该年级全抽 if (config.IsEnabledOnlyOneClassStudentMin && classCount == 1) { var stuCount = stus.Count(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId); if (stuCount <= config.OnlyOneClassStudentMin) { var items = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId).ToList(); for (int i = 0; i < items.Count; i++) { items[i].ExamSampleType = ExamSampleType.DISTRICT; items[i].Sequence = i + 1; } retItems.AddRange(items); continue; } } // 3.年级多于一个班,处理年级未抽样最低限制,满足条件该年级全抽 if (config.IsEnabledGradeNoSampleStudentMin && classCount > 1) { var classStuCount = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId).GroupBy(t => t.SchoolClassId).Select(t => new { t.Key, Count = t.Count() }).ToList(); if (classStuCount.Sum(t => Math.Ceiling(t.Count * (1 - sampleRate))) <= config.GradeNoSampleStudentMin) { var items = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId).ToList(); for (int i = 0; i < items.Count; i++) { items[i].ExamSampleType = ExamSampleType.DISTRICT; items[i].Sequence = i + 1; } retItems.AddRange(items); continue; } } // 遍历处理各班级 foreach (var classId in classes) { // 班级学生数量 var classStuCount = stus.Count(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.SchoolClassId == classId); if (classStuCount == 0) { continue; } // 4.班级设置为全抽的直接写入 // 5.班级学生人数低于限制 if (config.SampleAllSchoolClassIds.Contains(classId) || (config.IsEnabledClassStudentMin && classStuCount <= config.ClassStudentMin)) { var items = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.SchoolClassId == classId).ToList(); for (int i = 0; i < items.Count; i++) { items[i].ExamSampleType = ExamSampleType.DISTRICT; items[i].Sequence = i + 1; } retItems.AddRange(items); continue; } // ------------------------- // 以下是需要抽样的 // ------------------------- // 班级学生列表 var classStus = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.SchoolClassId == classId).ToList(); // 抽中学生列表 List sampleStus = []; // 该班是否有成绩 var hasScore = classStus.Any(t => t.TotalScore != null && t.TotalScore > 0); // 最大抽样数量 var maxSampleStuCount = (int)Math.Ceiling(classStuCount * sampleRate); // 排除特殊学生 if (config.IsExcludeSpecialStudent) { // 非特殊学生列表 var normalStus = stus.Where(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.SchoolClassId == classId && t.IsSpecialStudent == false).ToList(); // 特殊学生数量 var classSpecialStuCount = stus.Count(t => t.SysOrgId == orgId && t.SysOrgBranchId == branchId && t.GradeId == gradeId && t.SchoolClassId == classId && t.IsSpecialStudent == true); // 特殊学生数量占比大于等于 1-抽样比例,非特殊学生全抽 if (classSpecialStuCount / classStuCount >= (1 - sampleRate)) { // 非特殊学生 for (int i = 0; i < normalStus.Count; i++) { normalStus[i].ExamSampleType = ExamSampleType.DISTRICT; normalStus[i].Sequence = i + 1; } retItems.AddRange(normalStus); } // 仅非特殊学生参与抽样 else { // 有成绩并为等距抽样,开始循环抽样 if (hasScore && !config.IsRandomSampling) { int sq = 1; CyclicSampling(config, normalStus, sampleStus, maxSampleStuCount, sq); retItems.AddRange(sampleStus); } // 无成绩或设置为随机抽样的,开始随机抽样 else { sampleStus = RandomSampling(normalStus, classStuCount, sampleRate); retItems.AddRange(sampleStus); } } } // 不排除特殊学生 else { // 有成绩并为等距抽样,开始循环抽样 if (hasScore && !config.IsRandomSampling) { int sq = 1; CyclicSampling(config, classStus, sampleStus, maxSampleStuCount, sq); retItems.AddRange(sampleStus); } // 无成绩或设置为随机抽样的,开始随机抽样 else { sampleStus = RandomSampling(classStus, classStuCount, sampleRate); retItems.AddRange(sampleStus); } } // 未抽中学生列表 var noSampleStus = (from s in classStus where !(from n in sampleStus select n.ExamStudentId).Contains(s.ExamStudentId) select s).ToList(); for (int i = 0; i < noSampleStus.Count; i++) { noSampleStus[i].ExamSampleType = ExamSampleType.SCHOOL_EXAM; noSampleStus[i].Sequence = i + 1; } retItems.AddRange(noSampleStus); } } } } return retItems; } /// /// 循环抽样 /// /// /// /// /// /// /// 循环次数 private static void CyclicSampling(ExamSampleConfig config, List stus, List sampleStus, int maxStuCount, int sequence, short cyclicNumber = 1) { var interval = config.Interval + 1; for (int i = config.StartPosition >= stus.Count ? 0 : config.StartPosition - 1; i < stus.Count; i += interval) { var stu = stus[i]; var nstus = stu.Adapt(); nstus.Sequence = sequence++; nstus.ExamSampleType = ExamSampleType.DISTRICT; nstus.CyclicNumber = cyclicNumber; sampleStus.Add(nstus); // 如果已抽满结束抽样 if (sampleStus.Count >= maxStuCount) { break; } } if (sampleStus.Count < maxStuCount) { var noSampleStus = (from s in stus where !(from n in sampleStus select n.ExamStudentId).Contains(s.ExamStudentId) select s).ToList(); CyclicSampling(config, noSampleStus, sampleStus, maxStuCount, sequence, ++cyclicNumber); } } /// /// 随机抽样 /// /// /// /// /// private static List RandomSampling(List stus, int classStuCount, double sampleRate) { List sampleStus = []; var sampleIndexList = GetRandomIndex(classStuCount, sampleRate); int sq = 1; for (int i = 0; i < stus.Count; i++) { if (sampleIndexList.Contains(i)) { var item = stus[i].Adapt(); item.ExamSampleType = ExamSampleType.DISTRICT; item.Sequence = sq++; sampleStus.Add(item); } } return sampleStus; } /// /// 获取随机位置列表 /// /// 总数量 /// 抽样比例 /// private static List GetRandomIndex(int totalCount, double sampleRate) { Random random = new(); List samples = []; int i = 0; var sc = Math.Ceiling(totalCount * sampleRate); while (i < sc) { int s = random.Next(totalCount); if (samples.Any(t => t == s)) { continue; } samples.Add(s); i++; } return samples.OrderBy(t => t).ToList(); } /// /// 获取学生并带往期成绩 /// /// 抽样配置 /// 成绩引用监测计划ID /// 抽样监测计划ID /// 年级ID列表 /// private async Task> GetStudentScoreList(ExamSampleConfig config, int? examScoreRefExamPlanId, int examPlanId, List grades) { string gradeWhere = string.Join(" OR ", grades.Select(t => $"T1.grade_id = {t}")); string selectSql = $@" SELECT T1.id AS exam_student_id, T1.`name` AS exam_student_name, T1.certificate_type, UPPER(T1.id_number) AS id_number, T1.sys_org_id, ORG.`code` AS sys_org_code, T1.sys_org_branch_id, T1.grade_id, T1.school_class_id, T1.class_number, T1.exam_number, T2.course_count, T2.total_score, CASE WHEN ISNULL(T3.id) THEN 0 ELSE 1 END as is_special_student FROM exam_student AS T1 LEFT JOIN ( -- 往期总分 SELECT T1.sys_org_id, T1.school_class_id, T2.certificate_type, UPPER(T2.id_number) AS id_number, COUNT(T1.id) AS course_count, SUM(T1.score) AS total_score FROM exam_score AS T1 JOIN exam_student AS T2 ON T1.exam_plan_id = T2.exam_plan_id AND T1.exam_student_id = T2.id WHERE T1.exam_plan_id = @examScoreRefExamPlanId GROUP BY T1.sys_org_id, T1.school_class_id, T2.certificate_type, T2.id_number ) AS T2 -- ON T1.sys_org_id = T2.sys_org_id AND T1.school_class_id = T2.school_class_id AND UPPER(T1.id_number) = T2.id_number ON T1.sys_org_id = T2.sys_org_id AND UPPER(T1.id_number) = T2.id_number JOIN ( SELECT DISTINCT exam_plan_id, sys_org_id FROM exam_org WHERE exam_plan_id = @examPlanId AND is_required_exam = 1 ) AS EO ON T1.exam_plan_id = EO.exam_plan_id AND T1.sys_org_id = EO.sys_org_id JOIN sys_org AS ORG ON T1.sys_org_id = ORG.id LEFT JOIN ( SELECT id, exam_plan_id, sys_org_id, certificate_type, id_number FROM exam_special_student WHERE exam_plan_id = @examPlanId AND `status` = {(short)AuditStatus.APPROVED} ) AS T3 ON T1.exam_plan_id = T3.exam_plan_id AND T1.sys_org_id = T3.sys_org_id AND UPPER(T1.id_number) = UPPER(T3.id_number) WHERE T1.exam_plan_id = @examPlanId AND ({gradeWhere}) ORDER BY T1.sys_org_id, T1.grade_id, T1.school_class_id, T2.total_score DESC, T1.id "; if (!config.SpecialStudentMustApproved) { selectSql = $@" SELECT T1.id AS exam_student_id, T1.`name` AS exam_student_name, T1.certificate_type, UPPER(T1.id_number) AS id_number, T1.sys_org_id, ORG.`code` AS sys_org_code, T1.sys_org_branch_id, T1.grade_id, T1.school_class_id, T1.class_number, T1.exam_number, T2.course_count, T2.total_score, CASE WHEN ISNULL(T3.id) THEN 0 ELSE 1 END as is_special_student FROM exam_student AS T1 LEFT JOIN ( -- 往期总分 SELECT T1.sys_org_id, T1.school_class_id, T2.certificate_type, UPPER(T2.id_number) AS id_number, COUNT(T1.id) AS course_count, SUM(T1.score) AS total_score FROM exam_score AS T1 JOIN exam_student AS T2 ON T1.exam_plan_id = T2.exam_plan_id AND T1.exam_student_id = T2.id WHERE T1.exam_plan_id = @examScoreRefExamPlanId GROUP BY T1.sys_org_id, T1.school_class_id, T2.certificate_type, T2.id_number ) AS T2 -- ON T1.sys_org_id = T2.sys_org_id AND T1.school_class_id = T2.school_class_id AND UPPER(T1.id_number) = T2.id_number ON T1.sys_org_id = T2.sys_org_id AND UPPER(T1.id_number) = T2.id_number JOIN ( SELECT DISTINCT exam_plan_id, sys_org_id FROM exam_org WHERE exam_plan_id = @examPlanId AND is_required_exam = 1 ) AS EO ON T1.exam_plan_id = EO.exam_plan_id AND T1.sys_org_id = EO.sys_org_id JOIN sys_org AS ORG ON T1.sys_org_id = ORG.id LEFT JOIN ( SELECT T1.id, T1.exam_plan_id, T1.sys_org_id, T1.certificate_type, T1.id_number FROM exam_special_student AS T1 LEFT JOIN ( SELECT sys_org_id FROM exam_org_data_report WHERE exam_plan_id = @examPlanId AND type = {(short)DataReportType.SP_STUDENT} AND `status` = {(short)DataReportStatus.REPORTED} ) AS T2 ON T1.sys_org_id = T2.sys_org_id WHERE T1.exam_plan_id = @examPlanId AND ((T2.sys_org_id IS NULL AND T1.`status` = {(short)AuditStatus.APPROVED}) OR (T2.sys_org_id IS NOT NULL AND (T1.`status` = {(short)AuditStatus.AUDIT} OR T1.`status` = {(short)AuditStatus.APPROVED}))) ) AS T3 ON T1.exam_plan_id = T3.exam_plan_id AND T1.sys_org_id = T3.sys_org_id AND UPPER(T1.id_number) = UPPER(T3.id_number) WHERE T1.exam_plan_id = @examPlanId AND ({gradeWhere}) ORDER BY T1.sys_org_id, T1.grade_id, T1.school_class_id, T2.total_score DESC, T1.id "; } var items = await rep.SqlQueriesAsync(selectSql, new { ExamScoreRefExamPlanId = examScoreRefExamPlanId ?? 0, ExamPlanId = examPlanId, }); return items; } #endregion }