using AutoMapper; using Castle.DynamicProxy.Generators; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.ML; using OfficeOpenXml.ConditionalFormatting; using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using SkiaSharp; using StackExchange.Redis; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using VOL.Business.IServices; using VOL.Business.IServices.Norm; using VOL.Core.CacheManager; using VOL.Core.Configuration; using VOL.Core.Extensions; using VOL.Core.Extensions.AutofacManager; using VOL.Core.ManageUser; using VOL.Core.Utilities; using VOL.Entity.DomainModels; using VOL.Entity.DomainModels.Business.People; using VOL.Entity.Enum; using VOL.Model; using VOL.Model.Ai; using VOL.Model.Norm.Request; using VOL.Model.Norm.Response; using VOL.Model.School.Response; using VOL.System.IRepositories; using VOL.System.Repositories; using static Dapper.SqlMapper; using static System.Formats.Asn1.AsnWriter; namespace VOL.Business.Services { public class N_SportsTestResultService : IN_SportsTestResultService, IDependency { #region 初始化 private readonly IMapper _mapper; private readonly ICacheService _cacheService; private readonly ICacheQueryService _cacheQueryService; private readonly IN_SportsTestResultRepository _sportsTestResultRepository; private readonly IS_GradeRepository _gradeRepository; private readonly IS_ClassRepository _classRepository; private readonly IS_StudentRepository _studentRepository; private readonly IN_HealthStandardsRepository _healthStandardsRepository; private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository; //private readonly IG_ActivitiesRepository _activitiesRepository; [ActivatorUtilitiesConstructor] public N_SportsTestResultService(IMapper mapper, ICacheService cacheService, ICacheQueryService cacheQueryService, IN_SportsTestResultRepository sportsTestResultRepository, IS_GradeRepository gradeRepository, IS_ClassRepository classRepository, IS_StudentRepository studentRepository, IN_HealthStandardsRepository healthStandardsRepository, IN_SportsTestCategoryRepository sportsTestCategoryRepository //IG_ActivitiesRepository activitiesRepository ) { _mapper = mapper; _cacheService = cacheService; _cacheQueryService = cacheQueryService; _sportsTestResultRepository = sportsTestResultRepository; _gradeRepository = gradeRepository; _classRepository = classRepository; _studentRepository = studentRepository; _healthStandardsRepository = healthStandardsRepository; _sportsTestCategoryRepository = sportsTestCategoryRepository; //_activitiesRepository = activitiesRepository; } #endregion /// /// 导入学生体测成绩 /// /// /// /// public async Task ImportStudentsTestData(IFormFile file) { 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(); // 将时间变量移到外部,避免重复赋值 var timeNow = DateTime.Now; foreach (var data in dataObjects) { if (!studentsDict.TryGetValue(data.StudentNo, out var student)) { throw new Exception($"未找到此编号为{data.StudentNo}的学生"); } // 为每个学生添加多条体测数据 if (data.BMIValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.BMI, student, timeNow)); if (data.Sit_And_ReachValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.Sit_And_Reach, student, timeNow)); if (data.VitalCapacityValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.VitalCapacity, student, timeNow)); if (data.MeterRun_50Value > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.MeterRun_50, student, timeNow)); if (data.OneMinuteJumpRopeValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.OneMinuteJumpRope, student, timeNow)); if (data.One_Minute_Sit_UpValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.One_Minute_Sit_Up, student, timeNow)); if (data.ShuttleRun_50x8Value > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.ShuttleRun_50x8, student, timeNow)); if (data.StandingLongJumpValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.StandingLongJump, student, timeNow)); if (data.MeterRun_800Value > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.MeterRun_800, student, timeNow)); if (data.MeterRun_1000Value > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.MeterRun_1000, student, timeNow)); if (data.Pull_UpValue > 0) entityList.Add(await AddEnumStudentsTestData(data, SportsTestItemType.Pull_Up, student, timeNow)); } // 批量添加数据 await _sportsTestResultRepository.AddRangeAsync(entityList); // 一次性保存所有修改 await _sportsTestResultRepository.SaveChangesAsync(); } public async Task AddEnumStudentsTestData(ImportStudentsTestDataParam data, SportsTestItemType type, dynamic student, DateTime timeNow) { var entity = new N_SportsTestValue { TeacherId = 1, DataSource = DataSource.IOT, ScoreTime = timeNow, GradeId = student.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 }; int gradeId = student.GradeId; SexType sexType = student.Sex; 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 LargeScreenDataStat(LargeScreenDataStatParam paramDto) { if (paramDto.StartTime == DateTime.MinValue || paramDto.EndTime == DateTime.MinValue) { throw new Exception($"请求参数不可为空"); } var res = new LargeScreenDataStatModel(); //学校信息 res.SchoolInfo = await GetLargeScreenSchoolInfo(paramDto); //体测情况(体质健康监控) res.TestSituations = await GetLargeScreenTestSituations(paramDto); //体质监测排行榜 res.PhysicalMonitoringRanking = await GetLargeScreenPhysicalMonitoringRanking(paramDto); //赛事活动情况 res.ActivitiesSituation = await GetLargeScreenActivitiesSituation(paramDto); //肥胖情况 res.ObesitySituation = await GetLargeScreenObesitySituation(paramDto); //视力情况 res.DicVisionSituation = await GetLargeScreenDicVisionSituation(paramDto); //年级体测项目成绩监控 res.GradeSportsTestTranscript = await GetLargeScreenGradeSportsTestTranscript(paramDto); return res; } #region 大屏信息 /// /// 大屏学校信息 /// /// /// public async Task GetLargeScreenSchoolInfo(LargeScreenDataStatParam paramDto) { var schoolInfo = new SchoolInfoModel(); var tenantId = UserContext.Current.TenantId; //年级信息 var gradeModels = await ( from g in _gradeRepository.DbContext.Set() join a in _gradeRepository.DbContext.Set() on g.Id equals a.GradeId join c in _gradeRepository.DbContext.Set() on g.Id equals c.GradeId into classGroup from c in classGroup.DefaultIfEmpty() where a.SchoolCode.Equals(tenantId) && c.SchoolCode.Equals(tenantId) group new { c } by new { g.Id, g.GradeName } into groupedData select new { groupedData.Key.Id, groupedData.Key.GradeName, ClassCount = groupedData.Select(x => x.c.Id).Distinct().Count(), ClassIds = groupedData.Select(x => x.c.Id).Distinct().ToList() }).ToListAsync(); //老师信息 var teacherCountModels = await ( from t in _gradeRepository.DbContext.Set() join a in _gradeRepository.DbContext.Set() on t.Id equals a.TeacherId into assocTeachers from at in assocTeachers.DefaultIfEmpty() where t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart group new { t, at } by new { t.Id } into groupedData select new TeacherPageListModel() { Id = groupedData.Key.Id }).ToListAsync(); var tCount = teacherCountModels.Count; //老师信息 var teacherDetailsModels = from a in _gradeRepository.DbContext.Set() join t in _gradeRepository.DbContext.Set() on a.TeacherId equals t.Id into teacher from tea in teacher.DefaultIfEmpty() where tea.SchoolCode.Equals(UserContext.Current.TenantId) && tea.TeacherStatus != TeacherStatus.Depart group new { a } by new { a.ClassId } into groupedData select new { ClassId = groupedData.Key.ClassId, TeacherIds = groupedData .Where(x => x.a.ClassId == groupedData.Key.ClassId) .Select(x => x.a.TeacherId) }; var teacherModels = await teacherDetailsModels.ToListAsync(); ///班级信息 gradeModels.ForEach(x => { schoolInfo.ClassData.Add(new ClassModel() { GradeName = x.GradeName, ClassCount = x.ClassCount }); var teachers = teacherModels.Where(t => x.ClassIds.Contains(t.ClassId)).ToList(); schoolInfo.TeacherData.Add(new TeacherModel() { GradeName = x.GradeName, TeacherCount = teachers .SelectMany(c => c.TeacherIds) .Distinct() .Count() }); }); var classIds = gradeModels.SelectMany(x => x.ClassIds).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 s.SchoolCode.Equals(tenantId) && s.StudentStatus == StudentStatus.Normal select s ).ToListAsync(); schoolInfo.StudentData.MaleCount = students.Where(x => x.Sex == SexType.Male).Count(); schoolInfo.StudentData.FemaleCount = students.Where(x => x.Sex == SexType.Female).Count(); schoolInfo.TotalClassCount = gradeModels.Select(x => x.ClassCount).Sum(); schoolInfo.TotalTeacherCount = tCount; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); // 运动时长 schoolInfo.AccumulatedTrainDuration = sportsTestResults.Select(x => x.MotionDuration).Sum(); var largeScreenTrainingDataModel = new List(); largeScreenTrainingDataModel.Add(await GeLargeScreenTrainingInfo(1, paramDto)); largeScreenTrainingDataModel.Add(await GeLargeScreenTrainingInfo(2, paramDto)); largeScreenTrainingDataModel.Add(await GeLargeScreenTrainingInfo(3, paramDto)); schoolInfo.LargeScreenTrainingDataModel = largeScreenTrainingDataModel; return schoolInfo; } /// /// 大屏训练数据 /// /// 时间类型(1:今日,2:本周,3:本月) /// /// public async Task GeLargeScreenTrainingInfo(int dateType, LargeScreenDataStatParam paramDto) { var largeScreenTrainingDataModel = new LargeScreenTrainingDataModel(); var tenantId = UserContext.Current.TenantId; DateTime startTime; DateTime endTime; if (dateType == 1) { startTime = DateTime.Today; endTime = DateTime.Today.AddDays(1).AddSeconds(-1); largeScreenTrainingDataModel.DataType = "今日"; } else if (dateType == 2) { // 获取本周的开始和结束时间 int diff = (7 + (DateTime.Now.DayOfWeek - DayOfWeek.Monday)) % 7; startTime = DateTime.Now.AddDays(-1 * diff).Date; endTime = startTime.AddDays(7).AddSeconds(-1); largeScreenTrainingDataModel.DataType = "本周"; } else if (dateType == 3) { // 获取本月的开始和结束时间 startTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1); endTime = startTime.AddMonths(1).AddSeconds(-1); largeScreenTrainingDataModel.DataType = "本月"; } else { return largeScreenTrainingDataModel; } // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= startTime && x.ScoreTime <= endTime); var testCategorys = sportsTestResults.Select(x => x.CategoryEnum).Distinct().ToList(); #region 总数据 // 训练次数 largeScreenTrainingDataModel.TrainingCount = sportsTestResults.Count(); // 上课节数 largeScreenTrainingDataModel.AttendClassCount = sportsTestResults.GroupBy(x => x.ClassRoomRecordId).Count(); // 上课时长 largeScreenTrainingDataModel.AttendClassDuration = sportsTestResults.Select(x => x.MotionDuration).Sum(); // 赛事参与人数 var activitiestDatas = await (from ad in _gradeRepository.DbContext.Set() join a in _gradeRepository.DbContext.Set() on ad.ActivitiesId equals a.Id into activitiesDto from a in activitiesDto.DefaultIfEmpty() where a.SchoolCode == tenantId && a.StartDate >= startTime && a.EndDate <= endTime && a.ActivitiesStatus == ActivitiesStatus.Completed select ad).ToListAsync(); largeScreenTrainingDataModel.EventParticipationBodCount = activitiestDatas.GroupBy(x => x.StudentNo).Count(); // 项目测试人数 largeScreenTrainingDataModel.ProjectTestingBodCount = sportsTestResults.GroupBy(x => x.StudentNo).Count(); #endregion #region 平均数据 (全校学生不区分训练/体测/AI赛事/考级测评/作业) // 运动人数 largeScreenTrainingDataModel.AccumulatedTrainCount = sportsTestResults.GroupBy(x => x.StudentNo).Count(); // 平均跳绳次数(跳绳个数少于10个不计算在内) var sportsTestCategorys = await _sportsTestCategoryRepository .FindAsIQueryable(x => x.StatisticType >= 0 && testCategorys.Contains(x.CategoryEnum)) .Select(x => new SportsTestCategoryModel() { Id = x.Id, CategoryEnum = x.CategoryEnum, CategoryName = x.CategoryName, StatisticType = x.StatisticType, IsRopeSkip = x.IsRopeSkip }).ToListAsync(); var ropeSkipCategorys = sportsTestCategorys.Where(x => x.IsRopeSkip).Select(x => x.CategoryEnum).ToList(); var avgRopeSkips = sportsTestResults.Where(x => x.CategoryValue.Equals((int)SportsTestItemType.OneMinuteJumpRope) && ropeSkipCategorys.Contains(x.CategoryEnum)).ToList(); largeScreenTrainingDataModel.AvgRopeSkipCount = avgRopeSkips.Count > 0 ? (int)avgRopeSkips.Where(x => x.Value > 10).Average(x => x.Value) : 0; // 平均运动强度 largeScreenTrainingDataModel.AvgStrength = sportsTestResults.Count > 0 ? (int)sportsTestResults.Average(x => x.Strength) : 0; // 运动时长 largeScreenTrainingDataModel.MotionDuration = sportsTestResults.Select(x => x.MotionDuration).Sum(); // 平均心率 //var avgHeartRates = sportsTestResults.Where(x => x.CategoryEnum.Equals("HeartRate")).ToList(); largeScreenTrainingDataModel.AvgHeartRate = sportsTestResults.Count > 0 ? (int)sportsTestResults.Average(x => x.Value) : 0; // 平均消耗 largeScreenTrainingDataModel.AvgConsume = sportsTestResults.Count > 0 ? (int)sportsTestResults.Average(x => x.Consumption) : 0; #endregion return largeScreenTrainingDataModel; } /// /// 大屏体测情况(体质健康监控) /// /// /// public async Task GetLargeScreenTestSituations(LargeScreenDataStatParam paramDto) { var testSituations = new TestSituations(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); var monitorList = sportsTestResults .GroupBy(x => new { x.StudentNo }) .Select(g => new { g.Key.StudentNo, g.First().Sex, Score = (g.Sum(x => x.Score) + g.Sum(x => x.AdditionalScore)) / (g.Select(x => x.CategoryEnum).Distinct().Count()) }) .Select(x => new { x.StudentNo, x.Sex, x.Score, Rank = x.Score.GetRank() }).ToList(); var rankValues = new List { "优秀", "良好", "及格", "不及格" }; // 计算每个 Rank 的百分比和性别统计 var rankStats = rankValues.ToDictionary(rank => rank, rank => new { Count = monitorList.Count(x => x.Rank == rank), MaleCount = monitorList.Count(x => x.Sex == SexType.Male && x.Rank == rank), FemaleCount = monitorList.Count(x => x.Sex == SexType.Female && x.Rank == rank) }); // 计算总人数 float totalCount = monitorList.Count(x => rankValues.Contains(x.Rank)); // 计算各个 Rank 的百分比 var rankPercentages = rankStats.ToDictionary( rank => rank.Key, rank => (float)Math.Round((rank.Value.Count / totalCount) * 100) ); // 计算并调整最后一个百分比(确保总和为100%) float sum = rankPercentages.Values.Sum(); float adjustment = 100 - sum; rankPercentages["不及格"] += adjustment; // 更新 TestSituation,保留男女人数和百分比 var dicMonitorList = rankValues.Select(rank => new TestSituation() { Title = $"{rank}率", Value = rankPercentages[rank], MaleCount = rankStats[rank].MaleCount, FemaleCount = rankStats[rank].FemaleCount }).ToList(); // 计算 "优秀率" 和 "良好率" 的总和 var goodValues = dicMonitorList.Where(x => x.Title == "优秀率" || x.Title == "良好率").ToList(); testSituations.ExcellentRate = goodValues.Sum(x => x.Value); testSituations.ExcellentCount = goodValues.Sum(x => x.MaleCount) + goodValues.Sum(x => x.FemaleCount); // 填充 TestSituationsData testSituations.TestSituationsData = dicMonitorList; return testSituations; } /// /// 获取视力统计数据 /// /// 查询参数 /// 左右眼统计数据 public async Task> GetLargeScreenDicVisionSituation(LargeScreenDataStatParam paramDto) { var sportsProportionDataList = new List(); // 获取指定时间范围内的视力记录 var visionData = await _sportsTestCategoryRepository.DbContext.Set() .Where(v => v.SchoolCode == UserContext.Current.TenantId) .ToListAsync(); // 定义维度范围 var visionCategories = new List<(string Title, float Min, float Max)> { ("正常", float.MinValue, 50), // 正常 (<50°) ("轻度", 50, 300), // 轻度 (50-300°) ("中度", 300, 600), // 中度 (300-600°) ("高度", 600, float.MaxValue) // 高度 (>600°) }; // 左右眼统计 var leftEyeStats = new List(); var rightEyeStats = new List(); foreach (var category in visionCategories) { // 左眼统计 var leftCount = visionData .Count(v => v.VisionLeft >= category.Min && v.VisionLeft < category.Max); leftEyeStats.Add(new StudentSportsProportionData { Title = category.Title, Value = leftCount }); // 右眼统计 var rightCount = visionData .Count(v => v.VisionReight >= category.Min && v.VisionReight < category.Max); rightEyeStats.Add(new StudentSportsProportionData { Title = category.Title, Value = rightCount }); } // 组装结果 sportsProportionDataList.Add(new SportsProportionData { Name = "右眼", Datas = rightEyeStats }); sportsProportionDataList.Add(new SportsProportionData { Name = "左眼", Datas = leftEyeStats }); return sportsProportionDataList; } /// /// 大屏肥胖情况 /// /// /// public async Task> GetLargeScreenObesitySituation(LargeScreenDataStatParam paramDto) { var sportsProportionDataList = new List(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.CategoryEnum != null && x.Rank != null && x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); var obesityTitles = new List() { "偏瘦", "正常", "超重", "肥胖" }; var m_Obesity = new List(); var f_Obesity = new List(); foreach (var title in obesityTitles) { var maleCount = sportsTestResults.Where(x => x.Sex == SexType.Male && x.CategoryEnum.Equals("BMI") && x.Rank.Equals(title)).Count(); var femaleCount = sportsTestResults.Where(x => x.Sex == SexType.Female && x.CategoryEnum.Equals("BMI") && x.Rank.Equals(title)).Count(); if (m_Obesity.Any(c => c.Title == title) || f_Obesity.Any(c => c.Title == title)) { m_Obesity.First().Value += maleCount; f_Obesity.First().Value += femaleCount; } else { m_Obesity.Add(new StudentSportsProportionData() { Title = title, Value = maleCount, }); f_Obesity.Add(new StudentSportsProportionData() { Title = title, Value = femaleCount, }); } } sportsProportionDataList.Add(new SportsProportionData() { Name = "男", Datas = m_Obesity }); sportsProportionDataList.Add(new SportsProportionData() { Name = "女", Datas = f_Obesity }); return sportsProportionDataList; } /// /// 大屏赛事活动情况 /// /// /// public async Task GetLargeScreenActivitiesSituation(LargeScreenDataStatParam paramDto) { var activitiesSituation = new ActivitiesSituation(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); var activities = await _gradeRepository.DbContext.Set().Where(x => x.SchoolCode == tenantId).ToListAsync(); activitiesSituation.ActivitiesTotalCount = activities.Count(); activitiesSituation.CompletedCount = activities.Where(x => x.ActivitiesStatus == ActivitiesStatus.Completed).Count(); activitiesSituation.AfootCount = activities.Where(x => x.ActivitiesStatus == ActivitiesStatus.Afoot).Count(); activitiesSituation.NotStartedCount = activities.Where(x => x.ActivitiesStatus == ActivitiesStatus.NotStarted).Count(); activitiesSituation.ParticipationRate = activitiesSituation.ActivitiesTotalCount > 0 ? activitiesSituation.CompletedCount / activitiesSituation.ActivitiesTotalCount * 100 : 0; return activitiesSituation; } /// /// 大屏体质监测排行榜 /// /// /// public async Task> GetLargeScreenPhysicalMonitoringRanking(LargeScreenDataStatParam paramDto) { var physicalMonitoringList = new List(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); // 年级-班级信息 var gradeClassQuery = from c in _classRepository.DbContext.Set() join g in _classRepository.DbContext.Set() on c.GradeId equals g.Id join a in _classRepository.DbContext.Set() on g.Id equals a.GradeId where a.SchoolCode == tenantId && c.SchoolCode.Equals(tenantId) select new { GradeAndClassName = $"{g.GradeName}-{c.ClassName}", }; var gradeClassModels = await gradeClassQuery.ToListAsync(); var monitorList = sportsTestResults .GroupBy(x => new { x.StudentNo }) .Select(g => new { g.Key.StudentNo, g.First().Sex, g.First().ClassId, g.First().GradeId, g.First().ClassName, g.First().GradeName, Score = (g.Sum(x => x.Score) + g.Sum(x => x.AdditionalScore)) / (g.Select(x => x.CategoryEnum).Distinct().Count()) }) .Select(x => new { x.StudentNo, x.Sex, x.ClassId, x.GradeId, x.ClassName, x.GradeName, x.Score, Rank = x.Score.GetRank() }).ToList(); physicalMonitoringList = monitorList .GroupBy(x => new { x.ClassId, x.GradeId, x.ClassName, x.GradeName }) .Select(g => new { GradeAndClassName = $"{g.Key.GradeName}-{g.Key.ClassName}", MaleValue = (float)g.Count(x => x.Sex == SexType.Male && x.Score >= 80) / g.Count() * 100, FemaleValue = (float)g.Count(x => x.Sex == SexType.Female && x.Score >= 80) / g.Count() * 100, OverallValue = (float)g.Where(x => x.Rank == "优秀" || x.Rank == "良好").Count(x => x.Score >= 80) / g.Count() * 100, }) .OrderByDescending(g => g.OverallValue) .Select((g, index) => new PhysicalMonitoring { GradeAndClassName = g.GradeAndClassName, ExcellentRate = Math.Round(g.OverallValue), Ranking = index + 1 }) .ToList(); var ranking = physicalMonitoringList.Count + 1; foreach (var gradeClass in gradeClassModels) { var physicalMonitoring = physicalMonitoringList.FirstOrDefault(c => c.GradeAndClassName == gradeClass.GradeAndClassName); if (physicalMonitoring == null) { physicalMonitoringList.Add(new PhysicalMonitoring() { GradeAndClassName = gradeClass.GradeAndClassName, ExcellentRate = 0, Ranking = ranking++ }); } } return physicalMonitoringList; } /// /// 大屏 年级体测项目成绩监控 /// /// /// public async Task> GetLargeScreenGradeSportsTestTranscript(LargeScreenDataStatParam paramDto) { var gradeSportsTestTranscriptList = new List(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); // 年级-班级信息 var gradeClassQuery = from g in _classRepository.DbContext.Set() join a in _classRepository.DbContext.Set() on g.Id equals a.GradeId where a.SchoolCode == tenantId select new { GradeId = g.Id, GradeAndClassName = $"{g.GradeName}", }; var gradeCategory = await ( from g in _gradeRepository.DbContext.Set() join s in _gradeRepository.DbContext.Set() on g.CategoryValue equals s.CategoryValue select new { GradeId = g.GradeId, CategoryId = g.CategoryValue, CategoryName = s.CategoryName, }).ToListAsync(); var gradeClassModels = await gradeClassQuery.ToListAsync(); var sportsTestList = sportsTestResults.GroupBy(x => new { x.GradeId, x.GradeName }).OrderBy(g => g.Key.GradeId).ToList(); foreach (var gradeClass in gradeClassModels) { var sportsProportionDataList = new List(); var sportsTest = sportsTestList.FirstOrDefault(s => s.Key.GradeId == gradeClass.GradeId); foreach (var type in gradeCategory.Where(c => c.GradeId == gradeClass.GradeId).OrderBy(c => c.CategoryId)) { var typeSportsTestResults = sportsTest != null ? sportsTest.Where(x => x.CategoryValue == type.CategoryId).ToList() : new List(); // 计算平均值 var maleAverage = typeSportsTestResults.Where(x => x.Sex == SexType.Male).Any() ? typeSportsTestResults.Where(x => x.Sex == SexType.Male).Average(x => x.Score) : 0; var femaleAverage = typeSportsTestResults.Where(x => x.Sex == SexType.Female).Any() ? typeSportsTestResults.Where(x => x.Sex == SexType.Female).Average(x => x.Score) : 0; var sumAverage = typeSportsTestResults.Any() ? typeSportsTestResults.Average(x => x.Score) : 0; var studentSportsProportionData = new List { new StudentSportsProportionData { Title = "总分数", Value = (int)sumAverage }, new StudentSportsProportionData { Title = "男生", Value = (int)maleAverage }, new StudentSportsProportionData { Title = "女生", Value = (int)femaleAverage } }; sportsProportionDataList.Add(new SportsProportionData { Name = type.CategoryName, Datas = studentSportsProportionData }); } gradeSportsTestTranscriptList.Add(new GradeSportsTestTranscript() { GradeAndClassName = gradeClass.GradeAndClassName, Datas = sportsProportionDataList }); } return gradeSportsTestTranscriptList; } /// /// 大屏 班级平均运动强度监控 /// /// /// public async Task> LargeScreenAverageClassExerciseIntensity(LargeScreenAverageClassExerciseIntensityParam paramDto) { if (paramDto.GradeId < 0 || paramDto.StartTime == DateTime.MinValue || paramDto.EndTime == DateTime.MinValue) { throw new Exception($"请求参数不可为空"); } var sportsProportionDataList = new List(); var tenantId = UserContext.Current.TenantId; // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.GradeId == paramDto.GradeId && x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime); // 班级信息 var classQuery = from c in _classRepository.DbContext.Set() join g in _classRepository.DbContext.Set() on c.GradeId equals g.Id join a in _classRepository.DbContext.Set() on g.Id equals a.GradeId where a.SchoolCode == tenantId && c.SchoolCode == tenantId && g.Id == paramDto.GradeId select c; var classModels = await classQuery.ToListAsync(); if (classModels == null || classModels.Count == 0) throw new ArgumentNullException("未找到班级数据"); var totalStrength = sportsTestResults.Any() ? sportsTestResults.Select(x => x.Strength).Average(x => x) : 0; var classGroups = sportsTestResults.Any() ? sportsTestResults.GroupBy(c => new { c.ClassId, c.ClassName }).ToList() : null; foreach (var classModel in classModels) { var classGroup = classGroups?.FirstOrDefault(c => c.Key.ClassId == classModel.Id && c.Key.ClassName == classModel.ClassName); if (classGroup != null) { // 计算该班级的平均强度 var averageStrength = classGroup.Select(x => x.Strength).Average(x => x); var percentage = averageStrength == 0 ? 0 : (averageStrength / totalStrength) * 100; var studentSportsProportionData = new List { new StudentSportsProportionData { Title = "平均强度", Value =((int)Math.Round(percentage) > 100 ? 100 : (int)Math.Round(percentage)) } }; sportsProportionDataList.Add(new SportsProportionData { Name = classGroup.Key.ClassName, Datas = studentSportsProportionData }); } else { var studentSportsProportionData = new List { new StudentSportsProportionData { Title = "平均强度", Value = 0 } }; sportsProportionDataList.Add(new SportsProportionData { Name = classModel.ClassName, Datas = studentSportsProportionData }); } } return sportsProportionDataList; } #endregion public async Task> GetSportsTestCategoryPageList(StudentsTestDataListParam paramDto) { var res = new PageDataDto(); var query = from r in _healthStandardsRepository.DbContext.Set() join c in _healthStandardsRepository.DbContext.Set() on r.CategoryEnum equals c.CategoryEnum where r.SchoolCode.Equals(UserContext.Current.TenantId) select new StudentsTestDataModel() { Id = r.Id, GradeId = r.Id, GradeName = r.GradeName, StudentNo = r.StudentNo, CategoryName = c.CategoryName, ClassId = r.ClassId, ClassName = r.ClassName, ClassRank = r.ClassRank, Consumption = r.Consumption, MotionDuration = r.MotionDuration, Strength = r.Strength, TeacherId = r.TeacherId, Remarks = r.Remarks, Value = r.Value, Rank = r.Rank, CategoryId = c.Id, Score = r.Score, Sex = r.Sex == SexType.Male ? "男" : "女" }; if (!string.IsNullOrWhiteSpace(paramDto.StudentNo)) { query = query.Where(x => x.StudentNo.Equals(paramDto.StudentNo)); } if (paramDto.TeacherId > 0) { query = query.Where(x => x.TeacherId == paramDto.TeacherId); } if (paramDto.GradeId > 0) { query = query.Where(x => x.GradeId == paramDto.GradeId); } if (paramDto.ClassId > 0) { query = query.Where(x => x.ClassId == paramDto.ClassId); } if (paramDto.CategoryId > 0) { query = query.Where(x => x.CategoryId == paramDto.CategoryId); } if (paramDto.Sex > 0) { query = query.Where(x => x.Sex.Equals(paramDto.Sex == SexType.Male ? "男" : "女")); } res.Total = await query.CountAsync(); var list = await query .Skip((paramDto.PageIndex - 1) * paramDto.PageSize) .Take(paramDto.PageSize) .ToListAsync(); res.Datas = list; return res; } public async Task> GetStudentsTestDataList(StudentsTestDataListParam paramDto) { var query = from r in _healthStandardsRepository.DbContext.Set() join c in _healthStandardsRepository.DbContext.Set() on r.CategoryEnum equals c.CategoryEnum where r.SchoolCode.Equals(UserContext.Current.TenantId) select new StudentsTestDataModel() { Id = r.Id, GradeId = r.Id, GradeName = r.GradeName, StudentNo = r.StudentNo, CategoryName = c.CategoryName, ClassId = r.ClassId, ClassName = r.ClassName, ClassRank = r.ClassRank, Consumption = r.Consumption, MotionDuration = r.MotionDuration, Strength = r.Strength, TeacherId = r.TeacherId, Remarks = r.Remarks, Value = r.Value, Rank = r.Rank, CategoryId = c.Id, Score = r.Score, Sex = r.Sex == SexType.Male ? "男" : "女" }; if (!string.IsNullOrWhiteSpace(paramDto.StudentNo)) { query = query.Where(x => x.StudentNo.Equals(paramDto.StudentNo)); } if (paramDto.TeacherId > 0) { query = query.Where(x => x.TeacherId == paramDto.TeacherId); } if (paramDto.GradeId > 0) { query = query.Where(x => x.GradeId == paramDto.GradeId); } if (paramDto.ClassId > 0) { query = query.Where(x => x.ClassId == paramDto.ClassId); } if (paramDto.CategoryId > 0) { query = query.Where(x => x.CategoryId == paramDto.CategoryId); } if (paramDto.Sex > 0) { query = query.Where(x => x.Sex.Equals(paramDto.Sex == SexType.Male ? "男" : "女")); } var list = await query.ToListAsync(); return list; } public async Task AddStudentsTestData(List paramDto) { var studentNos = paramDto.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 g.SchoolCode.Equals(UserContext.Current.TenantId) select new { Id = s.Id, Age = s.Age, ClassId = s.ClassId, ClassName = c.ClassName, GradeId = g.Id, GradeName = g.GradeName, Sex = s.Sex, StudentNo = s.StudentNo, }).ToListAsync(); var distinctCategoryTypes = paramDto.Select(c => c.CategoryEnum).Distinct().ToList(); var healthStandards = await _healthStandardsRepository.FindAsync(x => distinctCategoryTypes.Contains(x.CategoryEnum)); var entityList = new List(); var timeNow = DateTime.Now; foreach (var data in paramDto) { var entity = _mapper.Map(data); var student = students.FirstOrDefault(x => x.StudentNo.Equals(data.StudentNo)); if (student == null) throw new Exception($"未找到此编号为{data.StudentNo}的学生"); var standard = healthStandards.Where(x => x.CategoryEnum.Equals(data.CategoryEnum) && x.GradeId == student.GradeId && x.Sex == student.Sex && data.Value >= x.MinValue && data.Value < x.MaxValue ).FirstOrDefault(); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } entity.GradeId = student.GradeId; entity.ClassId = student.ClassId; entity.GradeName = student.GradeName; entity.ClassName = student.ClassName; entity.Sex = student.Sex; entity.Creator = UserContext.Current.UserId; entity.CreateDate = timeNow; entityList.Add(entity); } await _sportsTestResultRepository.AddRangeAsync(entityList); await _sportsTestResultRepository.SaveChangesAsync(); var ranks = await _sportsTestResultRepository.FindAsIQueryable(x => distinctCategoryTypes.Contains(x.CategoryEnum)) .Select(x => x.Score) .Distinct() .OrderByDescending(value => value) .ToListAsync(); foreach (var entity in entityList) { if (entity.Score > 0) { entity.ClassRank = ranks.IndexOf(entity.Score) + 1; } } _sportsTestResultRepository.UpdateRange(entityList); await _sportsTestResultRepository.SaveChangesAsync(); } public async Task ModifyStudentsTestData(List paramDto) { var studentNos = paramDto.Select(x => x.StudentNo).Distinct().ToList(); var entityList = await _sportsTestResultRepository.FindAsync(x => studentNos.Contains(x.StudentNo)); var distinctCategoryTypes = paramDto.Select(c => c.CategoryEnum).Distinct().ToList(); var healthStandards = await _healthStandardsRepository.FindAsync(x => distinctCategoryTypes.Contains(x.CategoryEnum)); var timeNow = DateTime.Now; foreach (var data in paramDto) { var entity = entityList.FirstOrDefault(x => x.StudentNo.Equals(data.StudentNo)); if (entity == null) throw new Exception($"未找到要更新的数据"); entity = _mapper.Map(data); var standard = healthStandards.Where(x => x.CategoryEnum.Equals(data.CategoryEnum) && x.GradeId == entity.GradeId && x.Sex == entity.Sex && data.Value >= x.MinValue && data.Value < x.MaxValue ).FirstOrDefault(); if (standard != null) { entity.Score = standard.Score; entity.Rank = standard.Rank; } entity.Modifier = UserContext.Current.UserId; entity.ModifyDate = timeNow; entityList.Add(entity); } _sportsTestResultRepository.UpdateRange(entityList); await _sportsTestResultRepository.SaveChangesAsync(); var ranks = await _sportsTestResultRepository.FindAsIQueryable(x => distinctCategoryTypes.Contains(x.CategoryEnum)) .Select(x => x.Score) .Distinct() .OrderByDescending(value => value) .ToListAsync(); foreach (var entity in entityList) { if (entity.Score > 0) { entity.ClassRank = ranks.IndexOf(entity.Score) + 1; } } _sportsTestResultRepository.UpdateRange(entityList); await _sportsTestResultRepository.SaveChangesAsync(); } public async Task DeleteStudentsTestData(List Ids) { var model = await _sportsTestResultRepository.FindAsync(x => Ids.Contains(x.Id)); if (model == null) throw new ArgumentNullException($"未找到删除的数据"); _sportsTestResultRepository.DbContext.RemoveRange(model); await _sportsTestResultRepository.SaveChangesAsync(); } } }