diff --git a/WeChatApplet/Controllers/PatriarchController.cs b/WeChatApplet/Controllers/PatriarchController.cs index 8232412..42cb810 100644 --- a/WeChatApplet/Controllers/PatriarchController.cs +++ b/WeChatApplet/Controllers/PatriarchController.cs @@ -63,9 +63,9 @@ namespace YD_WeChatApplet.Api.Controllers /// /// [HttpGet("StudentReport")] - public async Task StudentReport() + public async Task StudentReport(string studentNo) { - var res = await _patriarchService.StudentReport(); + var res = await _patriarchService.StudentReport(studentNo); return res; } diff --git a/WeChatApplet/Controllers/TeacherController.cs b/WeChatApplet/Controllers/TeacherController.cs index b7520e1..f1dbd9d 100644 --- a/WeChatApplet/Controllers/TeacherController.cs +++ b/WeChatApplet/Controllers/TeacherController.cs @@ -1,9 +1,10 @@ -using System.ComponentModel.Design; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.Design; using YD_WeChatApplet.Api.Services.Impl; using YD_WeChatApplet.Commons.Dto; using YD_WeChatApplet.Commons.Dto.ClassRoomRecord; using YD_WeChatApplet.Commons.Dto.HomeWork; +using YD_WeChatApplet.Commons.Dto.Patriarch; using YD_WeChatApplet.Commons.Dto.Resource; using YD_WeChatApplet.Commons.Dto.Teacher; using YD_WeChatApplet.Services; @@ -122,13 +123,23 @@ namespace YD_WeChatApplet.Api.Controllers /// /// 学员课堂报告 /// - /// - /// + /// /// [HttpGet("StudentClassRoomReport")] - public async Task StudentClassRoomReport(int classRoomId, string studentNo) + public async Task StudentClassRoomReport([FromQuery] StudentClassRoomReportRequestDto dto) { - var res = await _teacherService.StudentClassRoomReport(classRoomId, studentNo); + var res = await _teacherService.StudentClassRoomReport(dto); + return res; + } + + /// + /// 班级报告 + /// + /// + [HttpGet("ClassReport")] + public async Task ClassReport(int classId) + { + var res = await _teacherService.ClassReport(classId); return res; } } diff --git a/WeChatApplet/Services/Impl/PatriarchService.cs b/WeChatApplet/Services/Impl/PatriarchService.cs index 22a1f4e..9054977 100644 --- a/WeChatApplet/Services/Impl/PatriarchService.cs +++ b/WeChatApplet/Services/Impl/PatriarchService.cs @@ -184,11 +184,14 @@ namespace YD_WeChatApplet.Api.Services.Impl /// 学生报告 /// /// - public async Task StudentReport() + public async Task StudentReport(string studentNo) { var res = new StudentReportDto(); - var studentNo = UserLoginContext.Current.UserNo; + if (string.IsNullOrWhiteSpace(studentNo)) + return res; + + //var studentNo = UserLoginContext.Current.UserNo; var student = await _sportsContext.Student.Where(x => x.StudentNo == studentNo).FirstOrDefaultAsync(); @@ -218,68 +221,71 @@ namespace YD_WeChatApplet.Api.Services.Impl if (categorys == null) return res; - var iotQuery = await _sportsContext.SportsTestValue.Where(x => x.IsDisplay && x.SchoolCode == student.SchoolCode && x.StudentNo == studentNo).Select(x => new SportsTestValueModel() + var sportsTestResults = await _sportsContext.SportsTestValue.Where(x => x.IsDisplay && x.SchoolCode == student.SchoolCode).Select(x => new SportsTestValueModel() { StudentNo = x.StudentNo, Value = x.Value, Score = x.Score, AdditionalScore = x.AdditionalScore, - CategoryValue = x.CategoryValue + CategoryValue = x.CategoryValue, + ScoreTime = x.ScoreTime }).ToListAsync(); - var aiQuery = await _sportsContext.SportsTestData.Where(x => x.IsDisplay && x.SchoolCode == student.SchoolCode && x.StudentNo == studentNo).Select(x => new SportsTestValueModel() - { - StudentNo = x.StudentNo, - Value = (float)x.Value, - Score = x.Score, - AdditionalScore = x.AdditionalScore, - CategoryValue = x.CategoryValue - }).ToListAsync(); - - var sportsTestResults = iotQuery.Concat(aiQuery).ToList(); - res.CategoryScoreList = new List(); - double totalScore = 0; - var currentYear = DateTime.Now.Year; - var currentMonth = DateTime.Now.Month; - foreach (var item in categorys) { - var maxResultInMonth = sportsTestResults - .Where(x => x.CategoryValue == item.Id - && x.ScoreTime?.Year == currentYear - && x.ScoreTime?.Month == currentMonth) - .OrderByDescending(x => x.Value) - .FirstOrDefault(); + // ✅ 当前学生数据 + var studentResults = sportsTestResults + .Where(x => x.CategoryValue == item.Id && x.StudentNo == studentNo) + .OrderByDescending(x => x.ScoreTime) // 倒序:最新在前 + .ToList(); - var maxResult = maxResultInMonth ?? sportsTestResults + var lastResult = studentResults.FirstOrDefault(); + var prevResult = studentResults.Skip(1).FirstOrDefault(); + + var maxScore = studentResults.Any() + ? studentResults.Max(x => x.Score) + : 0; + + var classAvgScore = studentResults.Any() + ? studentResults.Average(x => x.Score) + : 0; + + var schoolAvgScore = sportsTestResults .Where(x => x.CategoryValue == item.Id) - .OrderByDescending(x => x.Value) - .FirstOrDefault(); + .Select(x => x.Score) + .DefaultIfEmpty(0) + .Average(); - if (item.CategoryName == "BMI") - res.BodyShape = maxResult?.Score ?? 0; + // 🚀 趋势图(关键:反转集合) + var trendList = studentResults + .OrderBy(x => x.ScoreTime) + .ToList(); - if (item.CategoryName == "肺活量") - res.BodyFunction = maxResult?.Score ?? 0; + var trendChart = new ScoreTrendChartDataDto + { + AxisX = trendList.Select(x => x.ScoreTime.ToString("MM-dd HH:mm")).ToList(), + AxisY = trendList.Select(x => x.Score ?? 0f).ToList() + }; res.CategoryScoreList.Add(new CategoryScoreDto() { + CategoryValue = item.CategoryValue, Name = item.CategoryName, - Score = maxResult?.Score ?? 0, + + PrevScore = prevResult?.Score ?? 0, + LastScore = lastResult?.Score ?? 0, + + MaxScore = (float)maxScore, + ClassAvgScore = (float)classAvgScore, + SchoolAvgScore = (float)schoolAvgScore, + + ScoreTrendChart = trendChart, + ImgUrl = $"{AppSettings.ALiYunOSS.CategoryImgUrl}{item.CategoryEnum}.png" }); - - double currentScore = maxResult?.Score ?? 0; - double currentAdditionalScore = maxResult?.AdditionalScore ?? 0; - - totalScore += ((currentScore + currentAdditionalScore) * item.Weight); } - res.TotalScore = (float)totalScore; - - var bodyFunction = res.CategoryScoreList.Where(x => x.Name != "BMI" && x.Name != "肺活量").ToList(); - res.BodyFunction = bodyFunction.Average(x => x.Score); return res; } diff --git a/WeChatApplet/Services/Impl/TeacherService.cs b/WeChatApplet/Services/Impl/TeacherService.cs index 36fc993..d8a1bd0 100644 --- a/WeChatApplet/Services/Impl/TeacherService.cs +++ b/WeChatApplet/Services/Impl/TeacherService.cs @@ -428,6 +428,9 @@ namespace YD_WeChatApplet.Services res.HighIntensity = heartRateDataList.Where(x => x.Strength > 50).Sum(x => x.MotionDuration); + double totalDuration = heartRateDataList.Sum(x => x.MotionDuration); + res.HighIntensityDurationRatio = totalDuration > 0 ? res.HighIntensity / totalDuration : 0; + var studentTrainingRecordList = classRoom.ClassroomStudentRecord.ToList(); List studentList = new List(); @@ -469,36 +472,62 @@ namespace YD_WeChatApplet.Services /// /// 学员报告 /// - /// - /// - /// - public async Task StudentClassRoomReport(int classRoomId, string studentNo) + public async Task StudentClassRoomReport(StudentClassRoomReportRequestDto dto) { var res = new StudentClassRoomReportDto(); - - if (string.IsNullOrWhiteSpace(studentNo)) + // 处理学号:若未传则从登录上下文获取 + string studentNo = dto.StudentNo.ToString(); + if (string.IsNullOrWhiteSpace(studentNo) || studentNo == "0") studentNo = UserLoginContext.Current.UserNo; - var query = from hrd in _sportsContext.Ai_HeartRateData - where hrd.ClassRoomRecordId == classRoomId && - hrd.StudentNo == studentNo - select hrd; + // 构建心跳数据查询 + IQueryable heartRateQuery = _sportsContext.Ai_HeartRateData; - var heartRateDataList = await query.ToListAsync(); + if (dto.Type == 1) // 按课堂查询 + { + heartRateQuery = heartRateQuery.Where(x => x.ClassRoomRecordId == dto.classRoomId && x.StudentNo == studentNo); + } + else if (dto.Type == 2) // 按时间范围查询 + { + if (dto.StartTime.HasValue) + heartRateQuery = heartRateQuery.Where(x => x.ScoreTime >= dto.StartTime.Value); + if (dto.EndTime.HasValue) + heartRateQuery = heartRateQuery.Where(x => x.ScoreTime <= dto.EndTime.Value); + heartRateQuery = heartRateQuery.Where(x => x.StudentNo == studentNo); + } + else + { + return res; // 无效类型 + } + var heartRateDataList = await heartRateQuery.ToListAsync(); if (heartRateDataList.Count == 0) return res; - var student = heartRateDataList[0]; - + // 基本心率指标 res.MaxHR = (int)heartRateDataList.Max(x => x.Value); res.MinHR = (int)heartRateDataList.Min(x => x.Value); res.AvgHR = (int)heartRateDataList.Average(x => x.Value); res.HighIntensity = heartRateDataList.Where(x => x.Strength > 50).Sum(x => x.MotionDuration); + double totalDuration = heartRateDataList.Sum(x => x.MotionDuration); + res.HighIntensityDurationRatio = totalDuration > 0 ? res.HighIntensity / totalDuration : 0; - var baseTime = await _sportsContext.Ai_HeartRateData.Where(x => x.ClassRoomRecordId == classRoomId).MinAsync(x => x.ScoreTime); + // 计算时间轴(每分钟平均心率) + DateTime baseTime; + if (dto.Type == 1) + { + // 课堂模式:以该课堂所有心跳数据的最早时间为基准 + baseTime = await _sportsContext.Ai_HeartRateData + .Where(x => x.ClassRoomRecordId == dto.classRoomId) + .MinAsync(x => x.ScoreTime); + } + else + { + // 时间模式:以当前查询到的心跳数据的最早时间为基准 + baseTime = heartRateDataList.Min(x => x.ScoreTime); + } var heartRateWithMinutes = heartRateDataList .Select(data => new @@ -508,8 +537,7 @@ namespace YD_WeChatApplet.Services }) .ToList(); - var maxMinute = heartRateWithMinutes.Max(x => x.MinuteBucket); - + int maxMinute = heartRateWithMinutes.Max(x => x.MinuteBucket); for (int minute = 0; minute <= maxMinute; minute++) { var minuteData = heartRateWithMinutes @@ -524,16 +552,37 @@ namespace YD_WeChatApplet.Services } } - - var jumpRecordList = await _sportsContext.Ai_FastJumpRopeData.Where(x => x.ClassRoomRecordId == classRoomId && x.StudentNo == studentNo).OrderByDescending(x => x.ScoreTime).Select(x => new JumpRecordListDto() + // 查询跳绳数据 + IQueryable jumpQuery = _sportsContext.Ai_FastJumpRopeData; + if (dto.Type == 1) { - TotalCount = x.JumpValue ?? 0, - TotalDuration = x.MotionDuration ?? 0, + jumpQuery = jumpQuery.Where(x => x.ClassRoomRecordId == dto.classRoomId && x.StudentNo == studentNo); + } + else + { + if (dto.StartTime.HasValue) + jumpQuery = jumpQuery.Where(x => x.ScoreTime >= dto.StartTime.Value); + if (dto.EndTime.HasValue) + jumpQuery = jumpQuery.Where(x => x.ScoreTime <= dto.EndTime.Value); + jumpQuery = jumpQuery.Where(x => x.StudentNo == studentNo); + } - }).ToListAsync(); + var jumpRecordList = await jumpQuery + .OrderByDescending(x => x.ScoreTime) + .Select(x => new JumpRecordListDto() + { + TotalCount = x.JumpValue ?? 0, + TotalDuration = x.MotionDuration ?? 0 + }) + .ToListAsync(); res.JumpRecordList = jumpRecordList; + int totalSeconds = jumpRecordList.Sum(x => x.TotalDuration); + res.TotalCount = jumpRecordList.Sum(x => x.TotalCount); + res.TotalDuration = totalSeconds / 60; // 秒转分钟,向下取整 + res.AvgSpeed = totalSeconds > 0 ? (res.TotalCount * 60) / totalSeconds : 0; + return res; } @@ -578,5 +627,90 @@ namespace YD_WeChatApplet.Services return result; } + + /// + /// 班级报告 + /// + /// + /// + /// + public async Task ClassReport(int classId) + { + var res = new ClassReportDto(); + + var classInfo = await _sportsContext.Class + .FirstOrDefaultAsync(x => x.Id == classId); + + if (classInfo == null) + return res; + + var students = await _sportsContext.Student + .Where(x => x.ClassId == classId) + .ToListAsync(); + + var studentNos = students.Select(x => x.StudentNo).ToList(); + + var categorys = await ( + from g in _sportsContext.GradeAssocCategory + join s in _sportsContext.SportsTestCategory + on g.CategoryValue equals s.CategoryValue + where g.GradeId == classInfo.GradeId + select new + { + s.Id, + s.CategoryValue, + s.CategoryName, + s.CategoryEnum + }).ToListAsync(); + + var allResults = await _sportsContext.SportsTestValue + .Where(x => x.IsDisplay + && studentNos.Contains(x.StudentNo) + && x.SchoolCode == classInfo.SchoolCode) + .Select(x => new SportsTestValueModel + { + StudentNo = x.StudentNo, + Score = x.Score, + CategoryValue = x.CategoryValue, + ScoreTime = x.ScoreTime + }) + .ToListAsync(); + + res.ClassCategoryScoreList = new List(); + + foreach (var item in categorys) + { + var categoryResults = allResults + .Where(x => x.CategoryValue == item.Id) + .OrderByDescending(x => x.ScoreTime) + .ToList(); + + var last = categoryResults.FirstOrDefault(); + var prev = categoryResults.Skip(1).FirstOrDefault(); + + res.ClassCategoryScoreList.Add(new ClassCategoryScoreDto + { + CategoryValue = item.CategoryValue, + Name = item.CategoryName, + + PrevScore = prev?.Score ?? 0, + LastScore = last?.Score ?? 0, + + ImgUrl = $"{AppSettings.ALiYunOSS.CategoryImgUrl}{item.CategoryEnum}.png" + }); + } + + // 学生列表(保留) + res.StudentTrainingRecordList = students.Select(x => new StudentTrainingRecordDto + { + StudentNo = x.StudentNo, + StudentName = x.StudentName, + Sex = x.Sex, + Age = x.Age, + Photo = x.Photo + }).ToList(); + + return res; + } } } diff --git a/WeChatApplet/Services/Interface/IPatriarchService.cs b/WeChatApplet/Services/Interface/IPatriarchService.cs index 7426712..5013d90 100644 --- a/WeChatApplet/Services/Interface/IPatriarchService.cs +++ b/WeChatApplet/Services/Interface/IPatriarchService.cs @@ -42,7 +42,7 @@ namespace YD_WeChatApplet.Services /// 学生报告 /// /// - Task StudentReport(); + Task StudentReport(string studentNo); /// /// 训练记录 diff --git a/WeChatApplet/Services/Interface/ITeacherService.cs b/WeChatApplet/Services/Interface/ITeacherService.cs index f1670d0..e58b9bc 100644 --- a/WeChatApplet/Services/Interface/ITeacherService.cs +++ b/WeChatApplet/Services/Interface/ITeacherService.cs @@ -6,6 +6,7 @@ using YD_WeChatApplet.Commons; using YD_WeChatApplet.Commons.Dto; using YD_WeChatApplet.Commons.Dto.ClassRoomRecord; using YD_WeChatApplet.Commons.Dto.HomeWork; +using YD_WeChatApplet.Commons.Dto.Patriarch; using YD_WeChatApplet.Commons.Dto.Teacher; namespace YD_WeChatApplet.Services @@ -68,8 +69,15 @@ namespace YD_WeChatApplet.Services /// /// 学员课堂报告 /// - /// + /// /// - Task StudentClassRoomReport(int classRoomId, string studentNo); + Task StudentClassRoomReport(StudentClassRoomReportRequestDto dto); + + + /// + /// 班级体测报告 + /// + /// + Task ClassReport(int classId); } } diff --git a/WeChatApplet/SmartSportsEntitys/N_SportsTestValue.cs b/WeChatApplet/SmartSportsEntitys/N_SportsTestValue.cs index e192e66..45d0bce 100644 --- a/WeChatApplet/SmartSportsEntitys/N_SportsTestValue.cs +++ b/WeChatApplet/SmartSportsEntitys/N_SportsTestValue.cs @@ -137,7 +137,7 @@ namespace YD_WeChatApplet.Api.SmartSportsEntitys [Comment("测试时间")] [Column(TypeName = "datetime")] [Editable(true)] - public DateTime? ScoreTime { get; set; } + public DateTime ScoreTime { get; set; } /// /// 是否显示 diff --git a/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/ClassReportDetailsDto.cs b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/ClassReportDetailsDto.cs index c046587..35e4018 100644 --- a/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/ClassReportDetailsDto.cs +++ b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/ClassReportDetailsDto.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using YD_WeChatApplet.Commons.Dto.Patriarch; namespace YD_WeChatApplet.Commons.Dto.ClassRoomRecord { @@ -31,6 +32,11 @@ namespace YD_WeChatApplet.Commons.Dto.ClassRoomRecord /// public int HighIntensity { get; set; } + /// + /// 中高强度时长占比 + /// + public double HighIntensityDurationRatio { get; set; } + /// /// 学生运动记录 /// @@ -69,4 +75,52 @@ namespace YD_WeChatApplet.Commons.Dto.ClassRoomRecord public string Photo { get; set; } } + + /// + /// 班级报告 + /// + public class ClassReportDto + { + /// + /// 各项目得分 + /// + public List ClassCategoryScoreList { get; set; } + + /// + /// 学生运动记录 + /// + public List StudentTrainingRecordList { get; set; } = new List(); + } + + /// + /// 各项目得分 + /// + public class ClassCategoryScoreDto + { + /// + /// 项目枚举 + /// + public int CategoryValue { get; set; } + + /// + /// 项目名称 + /// + public string Name { get; set; } + + /// + /// 上一次得分 + /// + public float PrevScore { get; set; } + + /// + /// 最新得分 + /// + public float LastScore { get; set; } + + + /// + /// 图片地址 + /// + public string ImgUrl { get; set; } + } } diff --git a/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportDto.cs b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportDto.cs index 4f9117f..a65c4d0 100644 --- a/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportDto.cs +++ b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportDto.cs @@ -31,6 +31,28 @@ namespace YD_WeChatApplet.Commons.Dto.ClassRoomRecord /// public int HighIntensity { get; set; } + + /// + /// 中高强度时长占比 + /// + public double HighIntensityDurationRatio { get; set; } + + + /// + /// 总个数 + /// + public int TotalCount { get; set; } + + /// + /// 总时长 + /// + public int TotalDuration { get; set; } + + /// + /// 平均速度 + /// + public int AvgSpeed { get; set; } + /// /// 跳绳记录 /// diff --git a/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportRequestDto.cs b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportRequestDto.cs new file mode 100644 index 0000000..0e7d137 --- /dev/null +++ b/YD_WeChatApplet.Commons/Dto/ClassRoomRecord/StudentClassRoomReportRequestDto.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace YD_WeChatApplet.Commons.Dto.ClassRoomRecord +{ + /// + /// 学生报告 + /// + public class StudentClassRoomReportRequestDto + { + /// + /// 查询类型[1:课堂/2:时间] + /// + public int Type { get; set; } + /// + /// 课堂Id + /// + public int classRoomId { get; set; } + + /// + /// 学号 + /// + public int StudentNo { get; set; } + + /// + /// 开始时间 + /// + public DateTime? StartTime { get; set; } + + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + } +} diff --git a/YD_WeChatApplet.Commons/Dto/Patriarch/StudentReportDto.cs b/YD_WeChatApplet.Commons/Dto/Patriarch/StudentReportDto.cs index f5fe49f..a88f358 100644 --- a/YD_WeChatApplet.Commons/Dto/Patriarch/StudentReportDto.cs +++ b/YD_WeChatApplet.Commons/Dto/Patriarch/StudentReportDto.cs @@ -11,26 +11,6 @@ namespace YD_WeChatApplet.Commons.Dto.Patriarch /// public class StudentReportDto { - /// - /// 身体形态 - /// - public float BodyShape { get; set; } - - /// - /// 身体机能 - /// - public float BodyFunction { get; set; } - - /// - /// 身体素质 - /// - public float BodyQuality { get; set; } - - /// - /// 总得分 - /// - public float TotalScore { get; set; } - /// /// 各项目得分 /// @@ -42,19 +22,62 @@ namespace YD_WeChatApplet.Commons.Dto.Patriarch /// public class CategoryScoreDto { + /// + /// 项目枚举 + /// + public int CategoryValue { get; set; } + /// /// 项目名称 /// public string Name { get; set; } /// - /// 得分 + /// 上一次得分 /// - public float Score { get; set; } + public float PrevScore { get; set; } + + /// + /// 最新得分 + /// + public float LastScore { get; set; } + + /// + /// 最好得分 + /// + public float MaxScore { get; set; } + + /// + /// 班级平均得分 + /// + public float ClassAvgScore { get; set; } + + /// + /// 学校平均得分 + /// + public float SchoolAvgScore { get; set; } /// /// 图片地址 /// public string ImgUrl { get; set; } + + /// + /// 成绩趋势图 + /// + public ScoreTrendChartDataDto ScoreTrendChart { get; set; } + } + + public class ScoreTrendChartDataDto + { + /// + /// X轴 + /// + public List AxisX { get; set; } + + /// + /// Y轴 + /// + public List AxisY { get; set; } } } diff --git a/YD_WeChatApplet.Commons/Dto/SportsTest/SportsTestValueModel.cs b/YD_WeChatApplet.Commons/Dto/SportsTest/SportsTestValueModel.cs index 62c545e..b9dc3ce 100644 --- a/YD_WeChatApplet.Commons/Dto/SportsTest/SportsTestValueModel.cs +++ b/YD_WeChatApplet.Commons/Dto/SportsTest/SportsTestValueModel.cs @@ -83,7 +83,7 @@ namespace YD_WeChatApplet.Commons.Dto.SportsTest /// ///测试时间 /// - public DateTime? ScoreTime { get; set; } + public DateTime ScoreTime { get; set; } } }