using Furion.DatabaseAccessor.Extensions;
using NPOI.SS.UserModel;
using YBEE.EQM.Core;
namespace YBEE.EQM.Application;
///
/// 监测教师任教科目管理服务
///
public class ExamTeacherCourseService : IExamTeacherCourseService, ITransient
{
private readonly IRepository _rep;
private readonly ISysDictDataService _sysDictDataService;
private readonly IExamGradeService _examGradeService;
private readonly ISchoolClassService _schoolClassService;
private readonly ICourseService _courseService;
private readonly IExportExcelService _exportExcelService;
public ExamTeacherCourseService(IRepository rep, ISysDictDataService sysDictDataService, IExamGradeService examGradeService, ISchoolClassService schoolClassService, ICourseService courseService, IExportExcelService exportExcelService)
{
_rep = rep;
_sysDictDataService = sysDictDataService;
_examGradeService = examGradeService;
_schoolClassService = schoolClassService;
_courseService = courseService;
_exportExcelService = exportExcelService;
}
#region 批量导入
///
/// 上传监测教师批量导入文件
///
///
///
///
public async Task> Upload(string filePath, int examPlanId)
{
UploadExamDataOutput result = new();
try
{
using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
IWorkbook workbook = ExcelUtil.GetWorkbook(filePath, fs);
var sheet = workbook.GetSheetAt(0);
var rows = sheet.GetRowEnumerator();
#region 验证表结构
// 少于2行验证
if (sheet.LastRowNum < 2)
{
result.ErrorMessage.Add("第一行应为填写说明,第二行应为标题行,请勿修改模板结构。");
return result;
}
// 跳过第一行
rows.MoveNext();
// 读取表头
rows.MoveNext();
IRow headerRow = (IRow)rows.Current;
int index = 0;
int GRADE_INDEX = index++;
int CLASS_INDEX = index++;
int COURSE_INDEX = index++;
int NAME_INDEX = index++;
int CERT_TYPE_INDEX = index++;
int ID_NUM_INDEX = index++;
int REMARK_INDEX = index;
Dictionary headers = new()
{
{ GRADE_INDEX, "年级号" },
{ CLASS_INDEX, "班级号" },
{ COURSE_INDEX, "科目" },
{ NAME_INDEX, "教师姓名" },
{ CERT_TYPE_INDEX, "证件类型" },
{ ID_NUM_INDEX, "证件号码" },
{ REMARK_INDEX, "备注" },
};
List headerErrors = new();
for (int i = 0; i <= 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($"第2行标题行{columnErrors}列名错误。从A列开始依次应为年级号、班级号、科目、教师姓名、证件类型、证件号码和备注。");
return result;
}
result.StructureCorrect = true;
#endregion
#region 处理数据
// 监测年级
var examGrades = await _examGradeService.GetListByExamPlanId(examPlanId);
// 获取证件类型
var cts = await _sysDictDataService.GetListByDictTypeId(304);
var certificateTypes = cts.ToDictionary(x => x.Name, y => y.Value);
// 学科
var cs = await _courseService.GetAllLiteList();
var courses = cs.ToDictionary(x => x.Name, y => y.Id);
// 读取数据
List data = new();
int rn = 0;
while (rows.MoveNext())
{
IRow row = (IRow)rows.Current;
string rv = row.GetCell(0)?.ToString().Trim() ?? "";
rv += row.GetCell(1)?.ToString().Trim() ?? "";
if (rv == "")
{
break;
}
UploadExamTeacherCourseOutput item = new() { RowNumber = ++rn };
// 年级
if (short.TryParse(row.GetCell(GRADE_INDEX)?.ToString(), out short gradeNumber))
{
var g = examGrades.FirstOrDefault(t => t.Grade.GradeNumber == gradeNumber);
if (g == null)
{
item.ErrorMessage.Add($"{headers[GRADE_INDEX]}与监测年级不符");
}
else
{
item.ExamGradeId = g.Id;
item.GradeId = g.GradeId;
}
}
else
{
item.ErrorMessage.Add(headers[CLASS_INDEX]);
}
// 班级
if (short.TryParse(row.GetCell(CLASS_INDEX)?.ToString(), out short classNumber))
{
item.ClassNumber = classNumber;
if (item.ClassNumber < 1 || item.ClassNumber > 35)
{
item.ErrorMessage.Add($"{headers[CLASS_INDEX]}超限");
}
}
else
{
item.ErrorMessage.Add(headers[CLASS_INDEX]);
}
// 科目
item.CourseName = StringUtil.ClearWhite(row.GetCell(COURSE_INDEX)?.ToString() ?? "");
if (item.CourseName == null)
{
item.ErrorMessage.Add($"{headers[COURSE_INDEX]}未填");
}
else
{
var cn = item.CourseName;
if (cn.Contains("政治") || cn.Contains("道法") || cn.Contains("道德") || cn.Contains("法治"))
{
cn = "政治";
}
if (courses.TryGetValue(cn, out short value))
{
item.CourseId = value;
}
else
{
item.ErrorMessage.Add($"{headers[COURSE_INDEX]}错误");
}
}
// 姓名
item.Name = StringUtil.ClearWhite(row.GetCell(NAME_INDEX)?.ToString() ?? "");
if (item.Name == "" || item.Name.Length > 100)
{
item.ErrorMessage.Add(headers[NAME_INDEX]);
}
if (item.Name.Length > 100) { item.Name = item.Name[..100]; }
// 证件类型
item.CertificateTypeName = StringUtil.ClearWhite(row.GetCell(CERT_TYPE_INDEX)?.ToString() ?? "");
if (!(item.CertificateTypeName != "" && certificateTypes.ContainsKey(item.CertificateTypeName)))
{
item.ErrorMessage.Add(headers[CERT_TYPE_INDEX]);
}
else
{
item.CertificateType = (CertificateType)certificateTypes[item.CertificateTypeName];
}
// 证件号码
item.IdNumber = StringUtil.ClearWhite(row.GetCell(ID_NUM_INDEX)?.ToString() ?? "").ToUpper();
if (item.CertificateType == CertificateType.ID_CARD)
{
var idNumberValidate = CertificateNumberValidator.ValidateIdCard(item.IdNumber);
if (!idNumberValidate.Success)
{
item.ErrorMessage.Add($"{item.CertificateTypeName}{idNumberValidate.ErrorMessage}");
}
}
if (item.IdNumber.Length > 100) { item.IdNumber = item.IdNumber[..100]; }
// 备注
item.Remark = StringUtil.ClearWhite(row.GetCell(REMARK_INDEX)?.ToString() ?? "");
if (item.Remark.Length > 200) { item.Remark = item.Remark[..200]; }
// 行是否验证通过
item.IsSuccess = item.ErrorMessage.Count == 0;
data.Add(item);
result.TotalRowCount++;
if (!item.IsSuccess)
{
result.ErrorRowCount++;
}
}
result.Rows = data;
#endregion
workbook.Close();
fs.Close();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
File.Delete(filePath);
}
return result;
}
///
/// 批量导入监测教师
///
///
///
public async Task Import(ImportExamTeacherCourseInput input)
{
var orgId = input.SysOrgId ?? CurrentSysUserInfo.SysOrgId;
// 删除同一监测计划中监测机构内所有数据
await _rep.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId && t.SysOrgBranchId == input.SysOrgBranchId).ExecuteDeleteAsync();
List items = new();
var gs = input.Items.Select(t => t.ExamGradeId).Distinct().ToList();
var examGrades = await _rep.Change().DetachedEntities.Where(t => gs.Contains(t.Id)).Select(t => t.Adapt()).ToListAsync();
foreach (var eg in examGrades)
{
var classNumbers = input.Items.Where(t => t.ExamGradeId == eg.Id).Select(t => t.ClassNumber).Distinct().ToList();
var classDict = await _schoolClassService.GetImportSchoolClassList(new()
{
SysOrgId = orgId,
SysOrgBranchId = input.SysOrgBranchId,
ExamGrade = eg,
ClassNumberList = classNumbers,
});
var citems = input.Items.Where(t => t.ExamGradeId == eg.Id).ToList();
foreach (var ni in citems)
{
var item = ni.Adapt();
item.ExamPlanId = input.ExamPlanId;
item.SysOrgId = orgId;
item.SysOrgBranchId = input.SysOrgBranchId;
item.SchoolClassId = classDict[ni.ClassNumber];
item.Name = StringUtil.ClearWhite(item.Name);
item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
items.Add(item);
}
}
await _rep.InsertAsync(items);
}
#endregion
#region 批量导出
///
/// 导出TQES导入文件格式文件包
///
///
///
///
public async Task<(string, byte[])> ExportTqesFile(int examPlanId)
{
var examPlan = await _rep.Change().DetachedEntities.ProjectToType().FirstOrDefaultAsync(t => t.Id == examPlanId) ?? throw Oops.Oh(ErrorCode.E2001);
// 临时存放目录
string fileRoot = Path.Combine(FileUtil.GetTempFileRoot(), $"{Guid.NewGuid()}");
Directory.CreateDirectory(fileRoot);
string filePath = Path.Combine(fileRoot, $"{examPlan.Name}-教师任教科目");
Directory.CreateDirectory(filePath);
try
{
// 定义EXCEL列
List> noCols = new()
{
new() { Name = "年级号", Width = 8, GetCellValue = (r) => r.ExamGrade.Grade.GradeNumber2 },
new() { Name = "班级号", Width = 8, GetCellValue = (r) => r.ClassNumber },
new() { Name = "科目", Width = 8, GetCellValue = (r) => r.Course.Name },
new() { Name = "身份证号码", Width = 20, GetCellValue = (r) => r.IdNumber },
new() { Name = "姓名", Width = 20, GetCellValue = (r) => r.Name },
};
var items = await _rep.DetachedEntities.Where(t => t.ExamPlanId == examPlanId).ProjectToType().ToListAsync();
var orgs = items.Select(t => new { t.SysOrg.Id, t.SysOrg.Name, t.SysOrg.TqesCode }).Distinct().ToList();
foreach (var org in orgs)
{
var orgData = items.Where(t => t.SysOrgId == org.Id).ToList();
var bs = _exportExcelService.ExportExcel(new ExportExcelDto()
{
IsXlsx = false,
Title = null,
Columns = noCols,
Items = orgData,
IncludeExportTime = false,
});
await File.WriteAllBytesAsync(Path.Combine(filePath, $"T-{org.TqesCode}-{org.Name}-教师任教科目.xls"), bs);
}
string outFileName = $"{examPlan.Name}-教师任教科目.zip";
string outFilePath = Path.Combine(fileRoot, outFileName);
ICSharpCode.SharpZipLib.Zip.FastZip zip = new();
zip.CreateZip(outFilePath, filePath, true, string.Empty);
var retBytes = await File.ReadAllBytesAsync(outFilePath);
return (outFileName, retBytes);
}
catch (Exception ex)
{
throw new Exception("导出错误", ex);
}
finally
{
Directory.Delete(fileRoot, true);
}
}
#endregion
#region 创建编辑
///
/// 添加监测教师
///
///
///
public async Task Add(AddExamTeacherCourseInput input)
{
var orgId = input.SysOrgId ?? CurrentSysUserInfo.SysOrgId;
// 检测同年级同班同科目是否已经有教师
var sameItems = await _rep.DetachedEntities
.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId && t.GradeId == input.GradeId && t.ClassNumber == input.ClassNumber && t.CourseId == input.CourseId)
.ProjectToType().ToListAsync();
if (sameItems.Any())
{
throw Oops.Oh(ErrorCode.E2003);
}
var examGrade = await _examGradeService.GetById(input.ExamGradeId);
var schoolClass = await _schoolClassService.GetSchoolClass(orgId, input.SysOrgBranchId, examGrade, input.ClassNumber);
var item = input.Adapt();
item.SysOrgId = orgId;
item.SchoolClassId = schoolClass.Id;
item.Name = StringUtil.ClearWhite(item.Name);
item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
await item.InsertAsync();
}
///
/// 更新监测教师
///
///
///
public async Task Update(UpdateExamTeacherCourseInput input)
{
var orgId = input.SysOrgId ?? CurrentSysUserInfo.SysOrgId;
var oitem = await _rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
// 检测同年级同班同科目是否已经有教师
var sameItems = await _rep.DetachedEntities
.Where(t => t.Id != input.Id && t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId && t.GradeId == input.GradeId && t.ClassNumber == input.ClassNumber && t.CourseId == input.CourseId)
.ProjectToType().ToListAsync();
if (sameItems.Any())
{
throw Oops.Oh(ErrorCode.E2003);
}
var examGrade = await _examGradeService.GetById(oitem.ExamGradeId);
var schoolClass = await _schoolClassService.GetSchoolClass(oitem.SysOrgId, input.SysOrgBranchId, examGrade, input.ClassNumber);
var item = input.Adapt();
item.SchoolClassId = schoolClass.Id;
item.Name = StringUtil.ClearWhite(item.Name);
item.IdNumber = StringUtil.ClearIdNumber(item.IdNumber);
await item.UpdateIncludeNowAsync(new[] {
nameof(item.Name),
nameof(item.CertificateType),
nameof(item.IdNumber),
nameof(item.ExamGradeId),
nameof(item.GradeId),
nameof(item.SchoolClassId),
nameof(item.SysOrgBranchId),
nameof(item.ClassNumber),
nameof(item.CourseId),
nameof(item.Remark),
});
var items = await _rep.Where(t => t.Id != input.Id && t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId && t.Name == oitem.Name && t.CertificateType == input.CertificateType && t.IdNumber == input.IdNumber).ToListAsync();
if (items.Any())
{
foreach (var uitem in items)
{
uitem.Name = input.Name;
uitem.CertificateType = input.CertificateType;
uitem.IdNumber = input.IdNumber;
await uitem.UpdateIncludeNowAsync(new[] { nameof(uitem.Name), nameof(uitem.CertificateType), nameof(uitem.IdNumber) });
}
}
}
///
/// 删除监测教师
///
///
///
public async Task Del(BaseId input)
{
var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
await item.DeleteAsync();
}
///
/// 清空监测教师
///
///
///
public async Task Clear(ClearExamTeacherCourseInput input)
{
var orgId = CurrentSysUserInfo.SysOrgId;
await _rep.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == orgId).ExecuteDeleteAsync();
}
#endregion
#region 查询统计
///
/// 分页查询监测教师列表
///
///
///
public async Task> QueryPageList(ExamTeacherCoursePageInput input)
{
var query = GetQueryBase(input);
query = query.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == CurrentSysUserInfo.SysOrgId);
var ret = await query.OrderBy(t => t.Id).ProjectToType().ToADPagedListAsync(input.PageIndex, input.PageSize);
return ret;
}
#endregion
#region 私有方法
///
/// 构建查询
///
///
///
private IQueryable GetQueryBase(ExamTeacherCoursePageInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var idNumber = !string.IsNullOrEmpty(input.IdNumber?.Trim());
var query = _rep.DetachedEntities.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
.Where((idNumber, u => EF.Functions.Like(u.IdNumber, $"%{input.IdNumber.Trim()}%")))
.Where(input.CertificateType.HasValue, t => t.CertificateType == input.CertificateType)
.Where(input.ClassNumber.HasValue, t => t.ClassNumber == input.ClassNumber)
.Where(input.SysOrgBranchId.HasValue, t => t.SysOrgBranchId == input.SysOrgBranchId)
.Where(input.GradeId.HasValue, t => t.GradeId == input.GradeId)
.Where(input.CourseId.HasValue, t => t.CourseId == input.CourseId);
return query;
}
#endregion
}