using Furion.DatabaseAccessor.Extensions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using YBEE.EQM.Core;
namespace YBEE.EQM.Application;
///
/// 缺测替补抽样服务
///
public class ExamSampleReplaceService(IRepository replaceRep, IRepository stuRep, IExportExcelService exportExcelService) : IExamSampleReplaceService, ITransient
{
///
/// 抽取
///
///
///
public async Task Sample(SampleExamSampleReplaceInput input)
{
var orgId = CurrentSysUserInfo.SysOrgId;
// 缺测学生
var absentStu = await stuRep.DetachedEntities
.Include(t => t.ExamStudent).Include(t => t.ExamSample)
.FirstOrDefaultAsync(t => t.Id == input.AbsentExamSampleStudentId && t.ExamStudent.SysOrgId == orgId)
?? throw Oops.Oh(ErrorCode.E2001);
// 替补列表
var rps = await replaceRep.DetachedEntities
.Where(t => t.ExamSampleId == absentStu.ExamSampleId && t.SysOrgId == orgId && t.IsDeleted == false)
.Select(t => new { t.AbsentExamSampleStudentId, t.ReplaceExamSampleStudentId, t.IsReplaceAbsent })
.ToListAsync();
// 如果已添加缺测,并且替补未被标记为缺测的情况则报错
if (rps.Any(t => t.AbsentExamSampleStudentId == absentStu.Id && t.IsReplaceAbsent == false))
{
throw Oops.Oh(ErrorCode.E3010);
}
// 所有学生
var stus = await stuRep.DetachedEntities.Where(t => t.ExamSampleId == absentStu.ExamSampleId &&
t.ExamStudent.SysOrgId == orgId &&
t.ExamStudent.GradeId == absentStu.ExamStudent.GradeId &&
t.ExamStudent.SchoolClassId == absentStu.ExamStudent.SchoolClassId &&
t.IsSpecialStudent == false)
.OrderByDescending(t => t.PreTotalScore).ThenBy(t => t.ExamStudentId)
.ToListAsync();
if (stus.Count == 0)
{
throw Oops.Oh(ErrorCode.E3009);
}
// 去掉已抽为替补的
var ers = rps.Select(t => t.ReplaceExamSampleStudentId).ToList();
stus = stus.Where(t => !ers.Contains(t.Id)).ToList();
ExamSampleStudent rstu = null;
// 有前置成绩按顺序抽取,无随机抽取
if (stus.Any(t => t.PreTotalScore > 0))
{
// 向上找紧挨的1位
rstu = stus.Where(t => t.PreTotalScore >= absentStu.PreTotalScore && t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).LastOrDefault();
// 向上未找到则向下找紧挨的1位
rstu ??= stus.Where(t => t.PreTotalScore <= absentStu.PreTotalScore && t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).FirstOrDefault();
}
else
{
var nstus = stus.Where(t => t.Id != absentStu.Id && t.ExamSampleType == ExamSampleType.SCHOOL_EXAM).ToList();
if (nstus.Count > 0)
{
var rand = new Random();
var si = rand.Next(0, nstus.Count);
rstu = nstus[si];
}
}
if (rstu is null)
{
throw Oops.Oh(ErrorCode.E3009);
}
// 同一缺测生抽的其他替补缺测锁定
var rps2 = await replaceRep.Where(t => t.ExamSampleId == absentStu.ExamSampleId &&
t.SysOrgId == CurrentSysUserInfo.SysOrgId &&
t.AbsentExamSampleStudentId == absentStu.Id &&
t.IsDeleted == false &&
t.IsReplaceAbsent == true
).ToListAsync();
foreach (var rp in rps2)
{
rp.IsReplaceAbsentLocked = true;
await rp.UpdateIncludeAsync([nameof(rp.IsReplaceAbsentLocked)]);
}
ExamSampleReplace item = new()
{
ExamSampleId = absentStu.ExamSampleId,
ExamPlanId = absentStu.ExamSample.ExamPlanId,
SysOrgId = absentStu.ExamStudent.SysOrgId,
SysOrgBranchId = absentStu.ExamStudent.SysOrgBranchId,
SchoolClassId = absentStu.ExamStudent.SchoolClassId,
ExamGradeId = absentStu.ExamStudent.ExamGradeId,
GradeId = absentStu.ExamStudent.GradeId,
ClassNumber = absentStu.ExamStudent.ClassNumber,
AbsentExamSampleStudentId = absentStu.Id,
ReplaceExamSampleStudentId = rstu.Id,
Remark = input.Remark,
IsReplaceAbsent = false,
IsReplaceAbsentLocked = false,
};
await replaceRep.InsertAsync(item);
}
///
/// 标记替补为缺测
///
///
///
public async Task MarkedReplaceAbsent(int id)
{
var item = await replaceRep.FirstOrDefaultAsync(t => t.Id == id && t.IsReplaceAbsentLocked == false) ?? throw Oops.Oh(ErrorCode.E2001);
item.IsReplaceAbsent = !item.IsReplaceAbsent;
await item.UpdateIncludeAsync([nameof(item.IsReplaceAbsent)]);
// 同一缺测生抽的其他替补缺测解除锁定
var rps2 = await replaceRep.Where(t => t.ExamSampleId == item.ExamSampleId &&
t.SysOrgId == CurrentSysUserInfo.SysOrgId &&
t.AbsentExamSampleStudentId == item.AbsentExamSampleStudentId &&
t.Id != item.Id &&
t.IsDeleted == false &&
t.IsReplaceAbsent == true
).ToListAsync();
foreach (var rp in rps2)
{
rp.IsReplaceAbsentLocked = !item.IsReplaceAbsent;
await rp.UpdateIncludeAsync([nameof(rp.IsReplaceAbsentLocked)]);
}
}
///
/// 软删除
///
///
///
public async Task FakeDelete(BaseId input)
{
var item = await replaceRep.FirstOrDefaultAsync(t => t.Id == input.Id && t.IsDeleted == false) ?? throw Oops.Oh(ErrorCode.E2001);
item.IsDeleted = true;
await item.UpdateIncludeNowAsync([nameof(item.IsDeleted)]);
}
///
/// 导出缺测替补名单
///
///
///
public async Task<(string fileName, byte[] fileBytes)> ExportToOrg(int examPlanId)
{
var res = await QueryOrgPageList(new()
{
ExamPlanId = examPlanId,
IsReplaceAbsent = false,
PageIndex = 1,
PageSize = 9999
});
var hasBranch = res.Items.Any(t => t.SysOrgBranchId.HasValue && t.SysOrgBranchId > 0);
XSSFWorkbook wb = new();
ISheet sheet = wb.CreateSheet();
sheet.DisplayGridlines = false;
// 获取样式
var cellStyle = exportExcelService.GetCellStyle(wb);
#region 表头
int rowNum = 0;
IRow headerRow1 = sheet.CreateRow(rowNum);
headerRow1.Height = ExportExcelCellStyle.DefaultRowHeight;
int ci = 0;
exportExcelService.AddCell("序", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 6);
if (hasBranch)
{
exportExcelService.AddCell("校区", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
}
exportExcelService.AddCell("年级", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 10);
exportExcelService.AddCell("班级", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 8);
exportExcelService.AddCell("缺测学生", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
//exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 20);
exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 14);
sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, ci - 2, ci - 1));
exportExcelService.AddCell("替补学生", headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 12);
//exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 20);
exportExcelService.AddCell(null, headerRow1, ci++, cellStyle.ColumnHeaderStyle, sheet, 14);
sheet.AddMergedRegion(new CellRangeAddress(rowNum, rowNum, ci - 2, ci - 1));
exportExcelService.AddCell("抽取时间", headerRow1, ci, cellStyle.ColumnHeaderStyle, sheet, 20);
IRow headerRow2 = sheet.CreateRow(++rowNum);
headerRow2.Height = ExportExcelCellStyle.DefaultRowHeight;
int lci = hasBranch ? 4 : 3;
for (ci = 0; ci < lci; ci++)
{
exportExcelService.AddCell(null, headerRow2, ci, cellStyle.ColumnHeaderStyle);
sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum, ci, ci));
}
exportExcelService.AddCell("姓名", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
//exportExcelService.AddCell("证件号码", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
exportExcelService.AddCell("监测号", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
exportExcelService.AddCell("姓名", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
//exportExcelService.AddCell("证件号码", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
exportExcelService.AddCell("监测号", headerRow2, ci++, cellStyle.ColumnHeaderStyle);
exportExcelService.AddCell(null, headerRow2, ci, cellStyle.ColumnHeaderStyle);
sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum, ci, ci));
sheet.CreateFreezePane(0, 2);
#endregion
int rn = 0;
foreach (var item in res.Items)
{
IRow row = sheet.CreateRow(++rowNum);
row.Height = ExportExcelCellStyle.DefaultRowHeight;
int rci = 0;
ICellStyle cstyle = cellStyle.CenterCellStyle;
exportExcelService.AddCell(++rn, row, rci++, cstyle);
if (hasBranch)
{
exportExcelService.AddCell(item.SysOrgBranch?.Name ?? "", row, rci++, cstyle);
}
exportExcelService.AddCell(item.ExamGrade.Grade.Name, row, rci++, cstyle);
exportExcelService.AddCell(item.ClassNumber, row, rci++, cstyle);
exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamStudent.Name, row, rci++, cstyle);
//exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamStudent.IdNumber, row, rci++, cstyle);
exportExcelService.AddCell(item.AbsentExamSampleStudent.ExamNumber, row, rci++, cstyle);
exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamStudent.Name, row, rci++, cstyle);
//exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamStudent.IdNumber, row, rci++, cstyle);
exportExcelService.AddCell(item.ReplaceExamSampleStudent.ExamNumber, row, rci++, cstyle);
exportExcelService.AddCell(item.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), row, rci++, cstyle);
}
MemoryStream ms = new();
wb.Write(ms, false);
ms.Flush();
return ($"监测替补抽取表{DateTime.Now:yyyyMMddHHmmss}.xlsx", ms.ToArray());
}
///
/// 分页查询缺测替补抽样列表
///
///
///
public async Task> QueryOrgPageList(ExamSampleReplacePageInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var idNumber = !string.IsNullOrEmpty(input.IdNumber?.Trim());
var examNumber = !string.IsNullOrEmpty(input.ExamNumber?.Trim());
var ret = await replaceRep.DetachedEntities
.Where(t => t.ExamPlanId == input.ExamPlanId && t.SysOrgId == CurrentSysUserInfo.SysOrgId && t.IsDeleted == false)
.Where((name, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamStudent.Name, $"%{input.Name.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamStudent.Name, $"%{input.Name.Trim()}%")))
.Where((idNumber, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamStudent.IdNumber, $"%{input.IdNumber.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamStudent.IdNumber, $"%{input.IdNumber.Trim()}%")))
.Where((examNumber, u => EF.Functions.Like(u.AbsentExamSampleStudent.ExamNumber, $"%{input.ExamNumber.Trim()}%") || EF.Functions.Like(u.ReplaceExamSampleStudent.ExamNumber, $"%{input.ExamNumber.Trim()}%")))
.Where(input.GradeId.HasValue, t => t.GradeId == input.GradeId)
.Where(input.ClassNumber.HasValue, t => t.ClassNumber == input.ClassNumber)
.Where(input.SysOrgBranchId.HasValue, t => t.SysOrgBranchId == input.SysOrgBranchId)
.Where(input.IsReplaceAbsent.HasValue, t => t.IsReplaceAbsent == input.IsReplaceAbsent)
.Where(input.IsReplaceAbsentLocked.HasValue, t => t.IsReplaceAbsentLocked == input.IsReplaceAbsentLocked)
.ProjectToType()
.OrderBy(t => t.SysOrgBranchId).ThenBy(t => t.GradeId).ThenBy(t => t.ClassNumber).ThenByDescending(t => t.CreateTime)
.ToADPagedListAsync(input.PageIndex, input.PageSize);
return ret;
}
}