using Learn.Archives.API.Controllers.Dto; using Learn.Archives.API.Expand; using Learn.Archives.Core.Common; using Learn.Archives.Core.Common.Expand; using Learn.Archives.Core.Model; using Learn.Archives.Core.Model.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; using System.Buffers.Text; using System.Collections.Generic; using System.Diagnostics; using System.Security.Claims; using UserCenter.Model; using UserCenter.Model.Common; using UserCenter.Model.Enum; using static System.Net.Mime.MediaTypeNames; namespace Learn.Archives.API.Controllers { /// /// 年级控制器 /// public class ExamClassInfoController : BackController { readonly Repository baseService; readonly Repository examService; readonly Repository schoolService; readonly Repository tagService; readonly Repository classTagService; readonly Repository examUserInfoService; readonly LiveUserInfo userInfo; readonly IHttpContextAccessor accessor; public ExamClassInfoController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContext, Repository examUserInfoService, Repository examService, Repository schoolService, Repository tagService, Repository classTagService) : base(baseService) { this.baseService = baseService; this.userInfo = userInfo; this.accessor = httpContext; this.examUserInfoService = examUserInfoService; this.examService = examService; this.schoolService = schoolService; this.tagService = tagService; this.classTagService = classTagService; } /// /// 导入考试信息 /// /// /// [HttpPost, ResultIgnore] [HttpLogEnable] public async Task Import(ImportExamDto dto) { var exam = await examService.GetByIdAsync(dto.eId); var fl = dto.File!=null? dto .File: accessor.HttpContext?.Request.Form.Files[0]; if (exam == null || fl == null) Oh.ModelError("为传入有效的数据"); if (!Path.GetExtension(fl.FileName).Equals(".xlsx", StringComparison.OrdinalIgnoreCase)) Oh.ModelError("请选择导入文件为.xlsx的后缀名!"); //分析excel IEnumerable dataList; using var stream = new MemoryStream(); { await fl.CopyToAsync(stream); dataList = stream.Query() .Where(s => !string.IsNullOrEmpty(s.Student)); } if (dataList == null || dataList.Count() == 0) Oh.ModelError("导入失败:无有效数据"); //处理数据 var errorExcelInfo = new List(); var insertUserInfo = new List(); var insertClassInfo = new List(); var oldUidAr = await examUserInfoService.AsQueryable() .Where(s => s.ExamId == exam.Id) .Select(s=>s.UserId) .ToArrayAsync(); var oldUidHash = oldUidAr.ToHashSet(); var db = schoolService.Context; foreach (var schoolArr in dataList.GroupBy(s => s.School)) { var school = await schoolService.GetFirstAsync(s => s.Name == schoolArr.Key); if (school == null) Oh.ModelError($"导入失败:学校 [{schoolArr.Key}] 未找到!"); var gradeInfo = GradeHelper.GetStudentGradeBaseByGrade(schoolArr.First().Grade); if (gradeInfo == null) Oh.ModelError($"导入失败:学校 [{schoolArr.Key}] 年级[{schoolArr.First().Grade}]不符合规范!"); if(exam.GradeLevel != gradeInfo.GradeLevel || exam.GradeYear != gradeInfo.GradeYear) Oh.ModelError($"导入失败:导入年级[{schoolArr.First().Grade}] 与 考试年级不匹配!"); //学校下的所属班级 var classArr = await db.Queryable().Where(c => c.SchoolId == school.Id && c.GradeLevel == gradeInfo.GradeLevel && c.GraduationYear == gradeInfo.GradeYear && !c.DeleteState).ToArrayAsync(); var userDic = await db.Queryable() .LeftJoin((s, c) => c.SchoolId == s.Id) .LeftJoin((s, c, p) => p.ClassId == c.Id) .LeftJoin((s, c, p, pr) => pr.PositionId == p.Id) .LeftJoin((s, c, p, pr, u) => u.Id == pr.UserId) .Where((s, c, p, pr, u) => s.Id == school.Id && c.GradeLevel == gradeInfo.GradeLevel && c.GraduationYear == gradeInfo.GradeYear && u.UserType == UserTypeEnum.学生.GetHashCode() && s.Enable && p.Enable && pr.Enable && !p.DeleteState && !c.DeleteState && !u.DeleteState && !s.DeleteState) .Select((s, c, p, pr, u) => new { Name = c.Name + u.RealName, u.Id, }).ToDictionaryAsync(s => s.Name, s => s.Id); //处理学生成绩数据 var userList = schoolArr.Select(s => { var classInfo = classArr .FirstOrDefault(x => x.Name == s.Class ); if (classInfo == null) { s.Error = "未能匹配班级"; errorExcelInfo.Add(s); return null; } var grade = GradeHelper.GetStudentGradeBaseByGrade(s.Grade); var sub = ImportExamInfoSubjectDic(s); var name = s.Class + s.Student; if (!userDic.ContainsKey(name)) { s.Error = "未能匹配到年级班级下对应的学生"; errorExcelInfo.Add(s); return null; } var uid = userDic[name]; if(oldUidHash!=null &&oldUidHash.Contains((long)uid)) { s.Error = "此学生已经在考试中已经录入过成绩"; errorExcelInfo.Add(s); return null; } return new ExamUserInfo() { ExamId = exam.Id, ExamName = exam.Name, GradeYear = grade.GradeYear, GradeLevel = grade.GradeLevel, TestPaperType = exam.TestPaperType, UserId = (long)userDic[name], UserName = s.Student, ClassId = classInfo.Id, SchoolId = classInfo.SchoolId, Type = exam.Type, SubjectDic = sub, AssignScore = s.赋分后总分, AssignRanking = s.资源校排名, }; }).ToList(); insertUserInfo.AddRange(userList); insertUserInfo = insertUserInfo.Where(s => s != null).ToList(); }; if (errorExcelInfo.Count != 0) return File(errorExcelInfo.ExportExcel(), "application/ms-excel", $"错误考试信息{DateTime.Now.ToString("MMddHHmm")}.xlsx"); //写入数据库 await examUserInfoService.AsInsertable(insertUserInfo).ExecuteCommandAsync(); await CalculatingTestResults(exam,examUserInfoService,schoolService, tagService); return Ok(); } /// /// 下载导入成绩模板 /// /// [HttpGet, ResultIgnore, AllowAnonymous] public IActionResult DwImportTemplate() { List resultList = new List() { new ImportExamInfo() { School="例子学校[导入时候请删除]", Class="测试班级", Grade="高2028", Student="学生姓名", 语文=80.5m, 化学=80.5m, 数学=80m, 历史 = 80m, 地理 = 80m, 政治 = 80m, 物理 = 80m, 生物 = 80m, 英语 = 80.5m, 赋分后总分=721.5m } }; return File(resultList.ExportExcel(), "application/ms-excel", $"导入成绩模板{DateTime.Now.ToString("MMddHHmm")}.xlsx"); } /// /// 删除班级考试信息 /// /// /// [HttpPost] public async Task DeleteExamInfo(RestartEntryDto dto) { if (dto.ClassId == 0 || dto.ExamId == 0) Oh.ModelError("传入了异常参数"); var exam = examService.GetById(dto.ExamId); if (exam is null) Oh.ModelError("传入了无效的考试"); var dCount = await baseService.AsDeleteable().Where(s => s.ClassId == dto.ClassId) .ExecuteCommandAsync(); var dCount1 = await examUserInfoService.AsDeleteable().Where(s => s.ClassId == dto.ClassId) .ExecuteCommandAsync(); return Ok(); } public override Task PageList([FromBody] QueryRequestBase model) { var c = model.Conditions.FirstOrDefault(s => s.FieldName == "Grade"); if (c != null) { var gInfo = GradeHelper.GetStudentGradeBaseByGrade(c.FieldValue); if (gInfo != null) { model.Conditions = model.Conditions.Where(s => s != c).ToList(); model.Conditions.Add(new SqlSugar.ConditionalModel() { FieldName = "GradeLevel", FieldValue = gInfo.GradeLevel, }); model.Conditions.Add(new SqlSugar.ConditionalModel() { FieldName = "GradeYear", FieldValue = gInfo.GradeYear.ToString(), CSharpTypeName="int" }); } else { Oh.ModelError($"传入了无法识别的 年级 => {c.FieldValue}"); } } return base.PageList(model); } /// /// 重新计算考试成绩排名 /// /// 考试id /// [HttpGet] public async Task RecalculateExamRankings(long examId) { if (examId == 0) Oh.ModelError("传入了异常参数"); var exam = examService.GetById(examId); if (exam is null) Oh.ModelError("无效的考试"); await CalculatingTestResults(exam, examUserInfoService, schoolService, tagService); return Ok(); } /// /// 获取班级考试分段排名 /// /// 考试id /// [HttpGet] public async Task> ClassRanking(long examId,long classId) { if (examId == 0) Oh.ModelError("传入异常参数"); if (! await examService.IsAnyAsync(s => s.Id == examId)) Oh.ModelError("无效的考试"); var arr = await classTagService.AsQueryable().Where(s => s.ExamId == examId && s.ClassId == classId) .Select() .ToArrayAsync(); return arr.OrderBy(s => s.SubjectId); } [NonAction] private Dictionary? ImportExamInfoSubjectDic(ImportExamInfo info) { return new Dictionary() { { SubjectEnum.语文, info.语文}, { SubjectEnum.数学, info.数学}, { SubjectEnum.英语, info.英语}, { SubjectEnum.物理, info.物理}, { SubjectEnum.化学, info.化学}, { SubjectEnum.生物, info.生物}, { SubjectEnum.政治, info.政治}, { SubjectEnum.历史, info.历史}, { SubjectEnum.地理, info.地理}, }; } [NonAction] private static decimal? SubjectScore(ExamUserInfo info, SubjectEnum? s) { switch (s) { case null: return info.AssignScore; default: if (info?.SubjectDic is null) return -999; return info.SubjectDic.ContainsKey(s.Value) ? info.SubjectDic?[s.Value] : -999; } } /// /// 计算考试成绩 /// /// /// /// /// [NonAction] public static async Task CalculatingTestResults(Exam exam, Repository eUService, Repository sService, Repository tagService) { var userInfoArr = await eUService.AsQueryable() .Where(s => s.ExamId == exam.Id) .ToArrayAsync(); var insertTotalClassInfo = new List(); var insertTotalClassTag = new List(); var eTagArr = await tagService.AsQueryable().Where(s => s.ExamId == exam.Id) .ToArrayAsync(); var db = sService.Context; foreach (var schoolArr in userInfoArr.GroupBy(s => s.SchoolId)) { var school = await sService.GetFirstAsync(s => s.Id == schoolArr.Key); var classArr = await db.Queryable() .Where(c => c.SchoolId == school.Id && c.GradeLevel == exam.GradeLevel && c.GraduationYear == exam.GradeYear && !c.DeleteState) .ToArrayAsync(); foreach (var classUserArr in userInfoArr.Where(s=>s.SchoolId == school.Id).GroupBy(s => s.ClassId)) { var classInfo = classArr.FirstOrDefault(s => s.Id == classUserArr.Key); if (classInfo is null) continue; var eCInfo = new ExamClassInfo() { ExamId = exam.Id, ExamName = exam.Name, SchoolId = classInfo.SchoolId, SchoolName = school.Name, ClassId = classInfo.Id, ClassName = classInfo.Name, GradeLevel = classInfo.GradeLevel, GradeYear = classInfo.GraduationYear, PeopleCount = classUserArr.Count(), MinScore = int.MaxValue, MaxScore = -99, BaseSchoolScore = exam.BaseSchoolScore, TestPaperType = exam.TestPaperType, Type = exam.Type, Average1 = exam.BaseSchoolScore, }; insertTotalClassInfo.Add(eCInfo); var avgTotal = 0m; //todo 班级分段分析 var classTagDic = new Dictionary(); foreach (var eUserInfo in classUserArr) { //上线人数 foreach (var item in eTagArr) { var subV = SubjectScore(eUserInfo, item.SubjectId); if (!classTagDic.TryGetValue(item.Id, out ExamClassTag? tag)) { tag = new ExamClassTag() { MaxScore = item.MaxScore, MinScore = item.MinScore, ClassId = classInfo.Id, PeopleCount = eCInfo.PeopleCount, ExamId = exam.Id, ExamTagId = item.Id, Name = item.TagName, SubjectId = item.SubjectId }; insertTotalClassTag.Add(tag); classTagDic.Add(item.Id, tag); } //总分分段 if (subV >= item.MinScore && subV <= item.MaxScore) { tag.OnLineCount++; } } var v = eUserInfo.AssignScore; //最大小分 if (v < eCInfo.MinScore) eCInfo.MinScore = v; if (v > eCInfo.MaxScore) eCInfo.MaxScore = v; avgTotal += v;//追加得分 } //总分平均分 eCInfo.Average = avgTotal / eCInfo.PeopleCount; } } { var i = 0; //计算班级上线率排名 foreach (var examTagArr in insertTotalClassTag.GroupBy(s => s.ExamTagId)) { i = 0; foreach (var item in examTagArr.OrderByDescending(s => s.OnLineRate) .GroupBy(s => s.OnLineRate)) { foreach (var u in item) u.OnLineRanking = ++i; } } //计算年级平均分排名 i = 0; foreach (var item in insertTotalClassInfo.OrderByDescending(s => s.Average) .GroupBy(s => s.Average)) { foreach (var u in item) u.AverageRank = ++i; } } //写入数据库 var baseDB = eUService.Context; baseDB.Ado.BeginTran(); try { await baseDB.Deleteable().Where(s => s.ExamId == exam.Id).ExecuteCommandAsync(); await baseDB.Deleteable().Where(s => s.ExamId == exam.Id).ExecuteCommandAsync(); await baseDB.Insertable(insertTotalClassInfo).ExecuteCommandAsync(); await baseDB.Insertable(insertTotalClassTag).ExecuteCommandAsync(); baseDB.Ado.CommitTran(); } catch (Exception ex) { baseDB.Ado.RollbackTran(); Oh.ModelError($"导入失败:写入数据时候出现了异常 [{ex.Message}] !"); } } } }