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;
///
/// 学生成绩导入服务
///
public class ExamScoreImportService : IExamScoreImportService, ITransient
{
private readonly IRepository _rep;
private readonly ISchoolClassService _schoolClassService;
private readonly IExamGradeService _examGradeService;
private readonly IExportExcelService _exportExcelService;
public ExamScoreImportService(IRepository rep, ISchoolClassService schoolClassService, IExamGradeService examGradeService, IExportExcelService exportExcelService)
{
_rep = rep;
_schoolClassService = schoolClassService;
_examGradeService = examGradeService;
_exportExcelService = exportExcelService;
}
///
/// 批量导入前期未上报学生名单的各科成绩(初始化)
///
///
///
///
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 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 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().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().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
// 获取需要导入的科目列表
Dictionary 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 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 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 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);
}
}
///
/// 批量导入学生总成绩
///
///
///
///
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 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 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().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().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
var examCourses = await _rep.Change().DetachedEntities.Where(t => t.ExamPlanId == examPlanId).Select(t => new { t.GradeId, t.CourseId }).ToListAsync();
// 获取需要导入的科目列表
Dictionary 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 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 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);
}
}
///
/// 上传并合并小题成绩导出
///
///
///
///
///
public async Task<(string, byte[])> UploadImportStudentMinorScore(string filePath, int examPlanId)
{
var examPlan = await _rep.Change().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 fileInfos = new();
var gradeDict = (await _rep.Change().DetachedEntities.Where(t => t.EducationStage == examPlan.EducationStage).ToListAsync()).ToDictionary(t => t.Name);
var courseDict = (await _rep.Change().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().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().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 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 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 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);
}
}
}