using AutoMapper; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using VOL.Business.IServices; using VOL.Business.IServices.Norm; using VOL.Business.IServices.School; using VOL.Business.IServices.SportsHall; using VOL.Business.Services.School; using VOL.Core.CacheManager; using VOL.Core.Extensions.AutofacManager; using VOL.Core.ManageUser; using VOL.Core.Utilities; using VOL.Entity.DomainModels; using VOL.Entity.Enum; using VOL.Model; using VOL.Model.IOT.Request; using VOL.Model.School.Request; using VOL.Model.School.Response; using VOL.Model.SportsHall; using VOL.System.IRepositories; using VOL.System.Repositories; namespace VOL.Business.Services.SportsHall { /// /// 运动馆 /// public class SportsHallService : ISportsHallService, IDependency { #region 初始化 private readonly IS_ClassRepository _classRepository; private readonly IS_GradeRepository _gradeRepository; private readonly IS_ClassAssocTeacherRepository _classAssocTeacherRepository; private readonly IS_StudentRepository _studentRepository; private readonly IotDataSyncService _iotDataSyncService; IN_HealthStandardsRepository _healthStandardsRepository; IN_SportsTestResultRepository _sportsTestResultRepository; [ActivatorUtilitiesConstructor] public SportsHallService(IS_ClassRepository classRepository, IS_GradeRepository gradeRepository, IS_ClassAssocTeacherRepository classAssocTeacherRepository, IS_StudentRepository studentRepository, IotDataSyncService iotDataSyncService, IN_HealthStandardsRepository healthStandardsRepository, IN_SportsTestResultRepository sportsTestResultRepository) { _classRepository = classRepository; _gradeRepository = gradeRepository; _classAssocTeacherRepository = classAssocTeacherRepository; _studentRepository = studentRepository; _iotDataSyncService = iotDataSyncService; _healthStandardsRepository = healthStandardsRepository; _sportsTestResultRepository = sportsTestResultRepository; } #endregion /// /// 新增班级 /// /// /// public async Task AddClass(AddSportsHallClassParam paramDto) { var tenantId = UserContext.Current.TenantId; var grade = await _gradeRepository.FindAsIQueryable(x => x.GradeName == "运动馆").Select(x => new { x.Id, x.GradeName }).FirstAsync(); var exist = await _classRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.ClassName == paramDto.ClassName && x.GradeId == grade.Id); if (exist) throw new Exception("班级名称已存在"); var classEntity = new S_Class() { GradeId = grade.Id, GradeName = grade.GradeName, ClassName = paramDto.ClassName, Creator = UserContext.Current.UserId, SchoolCode = UserContext.Current.TenantId, CreateDate = DateTime.Now }; using (var transaction = _classRepository.DbContext.Database.BeginTransaction()) { try { await _classRepository.AddAsync(classEntity); await _classRepository.SaveChangesAsync(); int classId = classEntity.Id; var classAssocTeacherList = new List(); paramDto.TeacherIds.ForEach(x => { classAssocTeacherList.Add(new S_ClassAssocTeacher() { ClassId = classId, TeacherId = x, Creator = UserContext.Current.UserId, CreateDate = DateTime.Now, Modifier = UserContext.Current.UserId, ModifyDate = DateTime.Now, }); }); await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList); var students = await _studentRepository.FindAsync(x => paramDto.studentNoList.Contains(x.StudentNo)); foreach (var student in students) { student.ClassId = classId; int index = paramDto.studentNoList.IndexOf(student.StudentNo); student.OrderNo = index >= 0 ? index + 1 : 0; } _studentRepository.UpdateRange(students); await _studentRepository.SaveChangesAsync(); // 提交事务 transaction.Commit(); } catch (Exception ex) { // 发生错误,回滚事务 transaction.Rollback(); Console.WriteLine("事务回滚:" + ex.Message); } } } /// /// 更新班级 /// /// /// public async Task ModifyClass(ModifySportsHallClassParam paramDto) { var tenantId = UserContext.Current.TenantId; var classModel = await _classRepository.FindAsyncFirst(x => x.Id == paramDto.ClassId); var grade = await _gradeRepository.FindAsIQueryable(x => x.GradeName == "运动馆").Select(x => new { x.Id, x.GradeName }).FirstAsync(); if (classModel == null) throw new ArgumentNullException("未找到要更新得数据"); var exist = await _classRepository.ExistsAsync( x => x.SchoolCode == tenantId && x.ClassName == paramDto.ClassName && x.GradeId == grade.Id && x.Id != paramDto.ClassId); if (exist) throw new Exception("班级名称已存在"); classModel.ClassName = paramDto.ClassName; using (var transaction = _classRepository.DbContext.Database.BeginTransaction()) { try { _classRepository.Update(classModel); var teacherList = await _classAssocTeacherRepository.FindAsync(x => x.ClassId == paramDto.ClassId); _classAssocTeacherRepository.DbContext.RemoveRange(teacherList); var classAssocTeacherList = new List(); paramDto.TeacherIds.ForEach(x => { classAssocTeacherList.Add(new S_ClassAssocTeacher() { ClassId = paramDto.ClassId, TeacherId = x, Creator = UserContext.Current.UserId, CreateDate = DateTime.Now, Modifier = UserContext.Current.UserId, ModifyDate = DateTime.Now, }); }); await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList); var oldStudents = await _studentRepository.FindAsync(x => x.ClassId == classModel.Id); var newStudents = await _studentRepository.FindAsync(x => paramDto.studentNoList.Contains(x.StudentNo)); var allStudents = oldStudents.UnionBy(newStudents, s => s.Id).ToList(); foreach (var student in allStudents) { if (paramDto.studentNoList.Contains(student.StudentNo)) { student.ClassId = classModel.Id; student.OrderNo = paramDto.studentNoList.IndexOf(student.StudentNo) + 1; } else { student.ClassId = 0; student.OrderNo = 0; } } _studentRepository.UpdateRange(allStudents); await _classRepository.SaveChangesAsync(); transaction.Commit(); } catch (Exception ex) { // 发生错误,回滚事务 transaction.Rollback(); Console.WriteLine("事务回滚:" + ex.Message); } } } public async Task BatchDeleteClasses(List ids) { var user = UserContext.Current; var classIds = ids?.Where(id => id > 0).Distinct().ToList() ?? new List(); if (classIds.Count == 0 || classIds.Count > 100) throw new ArgumentException("请选择1至100个班级"); if (user.RoleId != 1) { var menuIds = await _classRepository.DbContext.Set() .Where(menu => menu.Url == "/sportsHallClass" && menu.ParentId > 0) .Select(menu => menu.Menu_Id) .ToListAsync(); if (!user.Permissions.Any(permission => menuIds.Contains(permission.Menu_Id) && permission.UserAuthArr.Contains("BatchDelete"))) throw new Exception("没有批量删除运动馆班级的权限"); } var gradeId = await _gradeRepository.FindAsIQueryable(grade => grade.GradeName == "运动馆") .Select(grade => grade.Id) .FirstOrDefaultAsync(); if (gradeId == 0) throw new Exception("未找到运动馆年级"); await using var transaction = await _classRepository.DbContext.Database.BeginTransactionAsync(IsolationLevel.Serializable); var classes = await _classRepository.DbContext.Set() .Where(item => item.SchoolCode == user.TenantId && item.GradeId == gradeId && classIds.Contains(item.Id)) .ToListAsync(); if (classes.Count != classIds.Count) throw new Exception("部分班级不存在或不属于当前学校,请刷新列表后重试"); var occupiedClassIds = await _classRepository.DbContext.Set() .Where(student => classIds.Contains(student.ClassId)) .Select(student => student.ClassId) .Distinct() .ToListAsync(); if (occupiedClassIds.Count > 0) { var names = classes.Where(item => occupiedClassIds.Contains(item.Id)).Select(item => item.ClassName); throw new Exception($"班级 {string.Join("、", names)} 仍有学生,请先调整学生后再删除"); } var teacherAssociations = await _classRepository.DbContext.Set() .Where(item => classIds.Contains(item.ClassId)) .ToListAsync(); _classRepository.DbContext.RemoveRange(teacherAssociations); _classRepository.DbContext.RemoveRange(classes); await _classRepository.SaveChangesAsync(); await transaction.CommitAsync(); foreach (var item in classes) { await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Delete.GetDisplayName(), DataType = IOTDataSyncType.Class.GetDisplayName(), Json = JsonConvert.SerializeObject(item) }); } return classes.Count; } /// /// 导入运动馆学生体测成绩 /// /// /// /// public async Task SportsHallImportStudentsTestData(IFormFile file) { await ImportStudentsTestData(file, false); } /// /// 导入体测成绩管理学生体测成绩 /// /// /// /// public async Task SummaryOfGradesImportStudentsTestData(IFormFile file) { await ImportStudentsTestData(file, true); } private async Task ImportStudentsTestData(IFormFile file, bool isDisplay) { if (file == null || file.Length <= 0) throw new Exception("操作失败"); List dataObjects; using (var fileStream = file.OpenReadStream()) { dataObjects = Tool.ConvertExcelToList(fileStream); } var studentNos = dataObjects.Select(x => x.StudentNo).Distinct().ToList(); // 用一个查询一次性获取所有学生信息 var students = await (from s in _studentRepository.DbContext.Set() join c in _studentRepository.DbContext.Set() on s.ClassId equals c.Id join g in _studentRepository.DbContext.Set() on c.GradeId equals g.Id where c.SchoolCode.Equals(UserContext.Current.TenantId) select new { s.Id, s.Age, s.ClassId, c.ClassName, s.StudentName, GradeId = g.Id, g.GradeName, s.Sex, s.StudentNo }).ToListAsync(); // 将学生信息存入字典,快速查找 var studentsDict = students.ToDictionary(x => x.StudentNo); var entityList = new List(); foreach (var data in dataObjects) { if (!studentsDict.TryGetValue(data.StudentNo, out var student)) { throw new Exception($"未找到此编号为{data.StudentNo}的学生"); } var timeNow = data.ScoreTime; // 为每个学生添加多条体测数据 if (data.BMIValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.BMI, student, data.GradeId, timeNow, isDisplay)); //if (data.Sit_And_ReachValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.Sit_And_Reach, student, data.GradeId, timeNow, isDisplay)); if (data.VitalCapacityValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.VitalCapacity, student, data.GradeId, timeNow, isDisplay)); if (data.MeterRun_50Value > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.MeterRun_50, student, data.GradeId, timeNow, isDisplay)); if (data.OneMinuteJumpRopeValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.OneMinuteJumpRope, student, data.GradeId, timeNow, isDisplay)); if (data.One_Minute_Sit_UpValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.One_Minute_Sit_Up, student, data.GradeId, timeNow, isDisplay)); if (data.ShuttleRun_50x8Value > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.ShuttleRun_50x8, student, data.GradeId, timeNow, isDisplay)); if (data.StandingLongJumpValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.StandingLongJump, student, data.GradeId, timeNow, isDisplay)); if (data.MeterRun_800Value > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.MeterRun_800, student, data.GradeId, timeNow, isDisplay)); if (data.MeterRun_1000Value > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.MeterRun_1000, student, data.GradeId, timeNow, isDisplay)); if (data.Pull_UpValue > 0) entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.Pull_Up, student, data.GradeId, timeNow, isDisplay)); } // 批量添加数据 await _sportsTestResultRepository.AddRangeAsync(entityList); // 一次性保存所有修改 await _sportsTestResultRepository.SaveChangesAsync(); } public async Task AddSportsHallEnumStudentsTestData(SportsHallImportStudentsTestDataParam data, SportsTestItemType type, dynamic student, int gradeId, DateTime timeNow, bool isDisplay = false) { var entity = new N_SportsTestValue { TeacherId = 0, TeacherName = "", DataSource = DataSource.Im, ScoreTime = timeNow, GradeId = gradeId, ClassId = student.ClassId, GradeName = student.GradeName, ClassName = student.ClassName, SchoolCode = UserContext.Current.TenantId, Sex = student.Sex, StudentNo = data.StudentNo, StudentName = student.StudentName, Creator = UserContext.Current.UserId, CreateDate = timeNow, IsDisplay = isDisplay }; SexType sexType = student.Sex; string sex = sexType == SexType.Male ? "男" : "女"; string typeStr = type.ToString(); // 根据测试项目类型设置对应的值 if (type == SportsTestItemType.BMI) { entity.Value = data.BMIValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.BMIValue >= x.MinValue && data.BMIValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.Sit_And_Reach) { entity.Value = data.Sit_And_ReachValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.Sit_And_ReachValue >= x.MinValue && data.Sit_And_ReachValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.VitalCapacity) { entity.Value = data.VitalCapacityValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.VitalCapacityValue >= x.MinValue && data.VitalCapacityValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.MeterRun_50) { entity.Value = data.MeterRun_50Value; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.MeterRun_50Value >= x.MinValue && data.MeterRun_50Value < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.OneMinuteJumpRope) { entity.Value = data.OneMinuteJumpRopeValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.OneMinuteJumpRopeValue >= x.MinValue && data.OneMinuteJumpRopeValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.One_Minute_Sit_Up) { entity.Value = data.One_Minute_Sit_UpValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.One_Minute_Sit_UpValue >= x.MinValue && data.One_Minute_Sit_UpValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.ShuttleRun_50x8) { entity.Value = data.ShuttleRun_50x8Value; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.ShuttleRun_50x8Value >= x.MinValue && data.ShuttleRun_50x8Value < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.StandingLongJump) { entity.Value = data.StandingLongJumpValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.StandingLongJumpValue >= x.MinValue && data.StandingLongJumpValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.MeterRun_800) { entity.Value = data.MeterRun_800Value; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.MeterRun_800Value >= x.MinValue && data.MeterRun_800Value < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.MeterRun_1000) { entity.Value = data.MeterRun_1000Value; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.MeterRun_1000Value >= x.MinValue && data.MeterRun_1000Value < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } else if (type == SportsTestItemType.Pull_Up) { entity.Value = data.Pull_UpValue; // 根据测试项目查找标准 var standard = await _healthStandardsRepository.FindFirstAsync(x => x.CategoryEnum.Equals(typeStr) && x.GradeId == gradeId && x.Sex == sexType && data.Pull_UpValue >= x.MinValue && data.Pull_UpValue < x.MaxValue ); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } } entity.CategoryEnum = type.ToString(); entity.CategoryValue = (int)type; return entity; } /// /// 运动馆体测成绩分页列表 /// /// /// /// public async Task> GetSportsHallStudentsTestPageList(SportsHallStudentsTestParam paramDto) { var result = new PageDataDto(); // 基础查询(带租户过滤) var baseQuery = _sportsTestResultRepository.DbContext.Set() .Where(s => s.SchoolCode == UserContext.Current.TenantId); // 公共条件过滤(时间、班级、性别、等级) if (paramDto.StartTime.HasValue) baseQuery = baseQuery.Where(x => x.CreateDate >= paramDto.StartTime.Value); if (paramDto.EndTime.HasValue) baseQuery = baseQuery.Where(x => x.CreateDate <= paramDto.EndTime.Value); if (paramDto.ClassId.HasValue) baseQuery = baseQuery.Where(x => x.ClassId == paramDto.ClassId.Value); if (paramDto.Sex.HasValue) baseQuery = baseQuery.Where(x => (int)x.Sex == paramDto.Sex); if (!string.IsNullOrWhiteSpace(paramDto.ResultLevel)) baseQuery = baseQuery.Where(x => x.Rank == paramDto.ResultLevel); // Step 1: 获取每个学生最新的 ScoreTime(子查询) var latestScoreTimeQuery = baseQuery .GroupBy(x => x.StudentNo) .Select(g => new { StudentNo = g.Key, MaxScoreTime = g.Max(x => x.ScoreTime) }); // Step 2: 关联原表,只保留每个学生最新测试时间的记录 var latestRecordsQuery = from record in baseQuery join latest in latestScoreTimeQuery on new { record.StudentNo, record.ScoreTime } equals new { latest.StudentNo, ScoreTime = latest.MaxScoreTime } select record; // Step 3: 获取符合条件的学生总数(去重) result.Total = await latestRecordsQuery .Select(x => x.StudentNo) .Distinct() .CountAsync(); // Step 4: 分页获取当前页的学生学号列表(先按最新测试时间倒序排) var pagedStudentNos = await latestRecordsQuery .OrderByDescending(x => x.ScoreTime) // 按最新测试时间整体倒序 .Select(x => x.StudentNo) .Distinct() .Skip((paramDto.PageIndex - 1) * paramDto.PageSize) .Take(paramDto.PageSize) .ToListAsync(); if (!pagedStudentNos.Any()) { result.Datas = new List(); return result; } // Step 5: 获取当前页学生的最新测试所有项目记录(包含 pivot 需要的数据) var pageRecords = await (from record in latestRecordsQuery where pagedStudentNos.Contains(record.StudentNo) select new { record.Id, record.StudentNo, record.StudentName, record.Sex, record.ClassName, record.GradeName, record.ScoreTime, record.IsDisplay, record.CategoryValue, record.Value }).ToListAsync(); // Step 6: 在内存中完成 pivot(因为同一学生同一测试时间的项目数量有限,性能可接受) var grouped = pageRecords .GroupBy(x => new { x.StudentNo, x.StudentName, x.Sex, x.ClassName, x.GradeName, x.ScoreTime, x.IsDisplay }) .Select(g => new SportsHallStudentsTestModel { Id = g.Max(x => x.Id), // 取组内最大 Id(或任意一个,仅作标识) StudentNo = g.Key.StudentNo, StudentName = g.Key.StudentName, SexName = g.Key.Sex.GetDescription(), GradeAndClass = $"{g.Key.GradeName} {g.Key.ClassName}", ScoreTime = g.Key.ScoreTime, IsDisplay = g.Key.IsDisplay, // 各项指标:根据 CategoryValue 匹配,使用 Max(如果同一个 CategoryValue 有多条,取最大值) BMIValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.BMI) .Max(x => (float?)x.Value) ?? 0, VitalCapacityValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.VitalCapacity) .Max(x => (float?)x.Value) ?? 0, Sit_And_ReachValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.Sit_And_Reach) .Max(x => (float?)x.Value) ?? 0, OneMinuteJumpRopeValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.OneMinuteJumpRope) .Max(x => (int?)x.Value) ?? 0, One_Minute_Sit_UpValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.One_Minute_Sit_Up) .Max(x => (int?)x.Value) ?? 0, StandingLongJumpValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.StandingLongJump) .Max(x => (float?)x.Value) ?? 0 }) .OrderByDescending(x => x.ScoreTime) // 按测试时间倒序 .ToList(); result.Datas = grouped; return result; } /// /// 体测成绩管理分页列表(全部体测项目) /// /// /// public async Task> GetSummaryOfGradesStudentsTestPageList(SportsHallStudentsTestParam paramDto) { var result = new PageDataDto(); paramDto.PageIndex = paramDto.PageIndex <= 0 ? 1 : paramDto.PageIndex; paramDto.PageSize = paramDto.PageSize <= 0 ? 10 : paramDto.PageSize; var baseQuery = _sportsTestResultRepository.DbContext.Set() .Where(s => s.SchoolCode == UserContext.Current.TenantId); if (paramDto.StartTime.HasValue) baseQuery = baseQuery.Where(x => x.CreateDate >= paramDto.StartTime.Value); if (paramDto.EndTime.HasValue) baseQuery = baseQuery.Where(x => x.CreateDate <= paramDto.EndTime.Value); if (paramDto.ClassId.HasValue) baseQuery = baseQuery.Where(x => x.ClassId == paramDto.ClassId.Value); if (paramDto.Sex.HasValue) baseQuery = baseQuery.Where(x => (int)x.Sex == paramDto.Sex); if (!string.IsNullOrWhiteSpace(paramDto.ResultLevel)) baseQuery = baseQuery.Where(x => x.Rank == paramDto.ResultLevel); var filteredQuery = baseQuery; if (paramDto.ItemType.HasValue && paramDto.ItemType.Value > 0) filteredQuery = filteredQuery.Where(x => x.CategoryValue == paramDto.ItemType.Value); var latestScoreTimeQuery = filteredQuery .GroupBy(x => x.StudentNo) .Select(g => new { StudentNo = g.Key, MaxScoreTime = g.Max(x => x.ScoreTime) }); var latestRecordsQuery = from record in filteredQuery join latest in latestScoreTimeQuery on new { record.StudentNo, record.ScoreTime } equals new { latest.StudentNo, ScoreTime = latest.MaxScoreTime } select record; result.Total = await latestRecordsQuery .Select(x => x.StudentNo) .Distinct() .CountAsync(); var pagedStudentKeys = await latestRecordsQuery .OrderByDescending(x => x.ScoreTime) .Select(x => new { x.StudentNo, x.ScoreTime }) .Distinct() .Skip((paramDto.PageIndex - 1) * paramDto.PageSize) .Take(paramDto.PageSize) .ToListAsync(); if (!pagedStudentKeys.Any()) { result.Datas = new List(); return result; } var pagedStudentNos = pagedStudentKeys.Select(x => x.StudentNo).Distinct().ToList(); var pageRecords = await baseQuery .Where(x => pagedStudentNos.Contains(x.StudentNo)) .Select(x => new { x.Id, x.StudentNo, x.StudentName, x.Sex, x.ClassName, x.GradeName, x.ScoreTime, x.IsDisplay, x.CategoryValue, x.CategoryEnum, x.Value, x.Score, x.Rank }) .ToListAsync(); pageRecords = pageRecords .Where(x => pagedStudentKeys.Any(k => k.StudentNo == x.StudentNo && k.ScoreTime == x.ScoreTime)) .ToList(); var sportsTestItemTypes = Enum.GetValues(typeof(SportsTestItemType)) .Cast() .OrderBy(x => (int)x) .ToList(); result.Datas = pageRecords .GroupBy(x => new { x.StudentNo, x.StudentName, x.Sex, x.ClassName, x.GradeName, x.ScoreTime, x.IsDisplay }) .Select(g => { var records = g .GroupBy(x => x.CategoryValue) .ToDictionary(x => x.Key, x => x.OrderByDescending(r => r.Id).First()); return new SummaryOfGradesStudentsTestModel { Id = g.Max(x => x.Id), StudentNo = g.Key.StudentNo, StudentName = g.Key.StudentName, SexName = g.Key.Sex.GetDescription(), GradeAndClass = $"{g.Key.GradeName} {g.Key.ClassName}", ScoreTime = g.Key.ScoreTime, IsDisplay = g.Key.IsDisplay, Items = sportsTestItemTypes.Select(itemType => { records.TryGetValue((int)itemType, out var record); return new SummaryOfGradesSportsTestItemModel { ItemType = (int)itemType, ItemCode = itemType.ToString(), ItemName = itemType.GetDisplayName(), Unit = itemType.GetDisplayDescription(), Value = record?.Value, Score = record?.Score, Rank = record?.Rank ?? string.Empty }; }).ToList() }; }) .OrderByDescending(x => x.ScoreTime) .ToList(); return result; } /// /// 成绩推送 /// /// /// /// public async Task ScorePush(SportsHallStudentsTestParam paramDto) { var sql = new StringBuilder(); sql.Append("UPDATE N_SportsTestValue SET IsDisplay = 1 WHERE SchoolCode = {0} "); var parameters = new List { UserContext.Current.TenantId }; if (paramDto.StartTime.HasValue) { sql.Append(" AND CreateDate >= {1} "); parameters.Add(paramDto.StartTime.Value); } if (paramDto.EndTime.HasValue) { sql.Append(" AND CreateDate <= {2} "); parameters.Add(paramDto.EndTime.Value); } if (paramDto.ClassId > 0) { sql.Append(" AND ClassId = {3} "); parameters.Add(paramDto.ClassId.Value); } if (paramDto.Sex > 0) { sql.Append(" AND Sex = {4} "); parameters.Add(paramDto.Sex.Value); } if (!string.IsNullOrWhiteSpace(paramDto.ResultLevel)) { sql.Append(" AND Rank = {5} "); parameters.Add(paramDto.ResultLevel); } await _sportsTestResultRepository.DbContext.Database .ExecuteSqlRawAsync(sql.ToString(), parameters.ToArray()); } /// /// 成绩修成 /// /// /// public async Task ScoreUpdate(SportsHallStudentsTestDataUpdateParam paramDto) { // 获取该学生每个项目的最新一条记录 var list = await _sportsTestResultRepository.DbContext.Set() .Where(s => s.SchoolCode == UserContext.Current.TenantId && s.StudentNo == paramDto.StudentNo) .GroupBy(x => x.CategoryValue) .Select(g => g.OrderByDescending(x => x.ScoreTime).FirstOrDefault()) .ToListAsync(); if (list == null || !list.Any()) throw new Exception($"未找到要修改得数据"); foreach (var entity in list) { switch (entity.CategoryValue) { case (int)SportsTestItemType.BMI: entity.Value = paramDto.BMIValue; break; case (int)SportsTestItemType.VitalCapacity: entity.Value = paramDto.VitalCapacityValue; break; case (int)SportsTestItemType.Sit_And_Reach: entity.Value = paramDto.Sit_And_ReachValue; break; case (int)SportsTestItemType.OneMinuteJumpRope: entity.Value = paramDto.OneMinuteJumpRopeValue; break; case (int)SportsTestItemType.One_Minute_Sit_Up: entity.Value = paramDto.One_Minute_Sit_UpValue; break; case (int)SportsTestItemType.StandingLongJump: entity.Value = paramDto.StandingLongJumpValue; break; } } _sportsTestResultRepository.UpdateRange(list); await _sportsTestResultRepository.DbContext.SaveChangesAsync(); } } }