using AutoMapper; using Microsoft.EntityFrameworkCore; using System.Drawing; using YD_AllHeartRates.Api.Context; using YD_AllHeartRates.Api.Entitys; using YD_AllHeartRates.Api.Services.Interface; using YD_AllHeartRates.Api.SmartSportsEntitys; using YD_AllHeartRates.Api.Utilities; using YD_AllHeartRates.Commons.Dto; using YD_AllHeartRates.Commons.Dto.HeartRateRepor; using YD_AllHeartRates.Commons.MemoryCaches; using YD_AllHeartRates.Commons.Utils; namespace YD_AllHeartRates.Api.Services.Impl { /// /// 服务实现 /// public class HeartRateReportService : IHeartRateReportService { public SmartSportsContext _sportsContext; public UserContext _userContext; private readonly ICaching _caching; private readonly LoginContext _loginContext; private string cacheKey; private string schoolCode; /// /// 构造 /// public HeartRateReportService(SmartSportsContext sportsContext, UserContext userContext, ICaching caching, LoginContext loginContext) { _sportsContext = sportsContext; _userContext = userContext; _caching = caching; _loginContext = loginContext; schoolCode = _loginContext.SchoolCode; cacheKey = $"HeartRateData_{schoolCode}"; } /// /// 学校心率报告 /// /// public async Task SchoolHeartRateReport(DateTime? scoreTime = null) { var (dayStart, dayEnd, effective) = HeartRateReportHelper.GetTimeRange(scoreTime); var res = new SchoolHeartRateReportDataDto { StartEndTime = effective.ToString() }; var gradeList = await _sportsContext.Grade .Join(_sportsContext.SchoolAssocGrade, g => g.Id, a => a.GradeId, (g, a) => new { g.Id, g.GradeName, a.SchoolCode }) .Where(x => x.SchoolCode == schoolCode) .Select(x => new GradeListDto { GradeId = x.Id, GradeName = x.GradeName }) .OrderBy(x => x.GradeId).ToListAsync(); var studentList = await ( from s in _sportsContext.Student join c in _sportsContext.Class on s.ClassId equals c.Id where s.SchoolCode == schoolCode select new { s.StudentNo, s.Sex, c.GradeId }).ToListAsync(); res.GradeCount = gradeList.Count; res.MaleCount = studentList.Count(x => x.Sex == 1); res.FemaleCount = studentList.Count(x => x.Sex == 2); // 先尝试从缓存读取 var cacheKey = $"HeartRateData_{schoolCode}"; var data = _caching.Get>(cacheKey); if (data == null || data.Count == 0) { data = await _userContext.HeartRateData .Where(x => x.SchoolCode == schoolCode && x.ScoreTime > dayStart && x.ScoreTime <= dayEnd) .ToListAsync(); // 写入缓存,过期时间 8 小时 = 28800 秒 _caching.AddObject(cacheKey, data, 28800); } res.SportsData.OnlineDevicesCount = data.GroupBy(x => x.StudentNo).Count(); res.SportsData.AvgHeartRate = (int)data.Select(x => x.Value).DefaultIfEmpty(0).Average(); var durationsByStudent = HeartRateReportHelper.CalculateDurations( data, x => (x.StudentNo, x.Sex, x.GradeId)); res.SportsData.AvgDuration = (int)durationsByStudent.Values.DefaultIfEmpty(0).Average(); var reachMale = durationsByStudent.Count(x => x.Key.Sex == 1 && x.Value >= 7200); var reachFemale = durationsByStudent.Count(x => x.Key.Sex == 2 && x.Value >= 7200); res.SportsData.ReachCount = reachMale + reachFemale; res.SportsData.NotReachCount = res.StudentCount - res.SportsData.ReachCount; res.SportsData.HeartRateTrend = HeartRateReportHelper.BuildHeartRateTrend(data); foreach (var grade in gradeList) { var total = studentList.Count(x => x.GradeId == grade.GradeId); var maleReach = durationsByStudent.Count(kv => kv.Key.GradeId == grade.GradeId && kv.Key.Sex == 1 && kv.Value >= 7200); var femaleReach = durationsByStudent.Count(kv => kv.Key.GradeId == grade.GradeId && kv.Key.Sex == 2 && kv.Value >= 7200); res.GradeList.Add(new GradeListDto { GradeId = grade.GradeId, GradeName = grade.GradeName, StudentCount = total, MaleReachCount = maleReach, FemaleReachCount = femaleReach, ReachRate = HeartRateReportHelper.CalculateReachRate(maleReach + femaleReach, total) }); } return res; } /// /// 年级心率报告 /// /// public async Task GradeHeartRateReport(int gradeId, DateTime? scoreTime = null) { var (dayStart, dayEnd, effective) = HeartRateReportHelper.GetTimeRange(scoreTime); var res = new GradeHeartRateReportDataDto { StartEndTime = effective.ToString() }; var classList = await ( from c in _sportsContext.Class join s in _sportsContext.ClassAssocTeacher on c.Id equals s.ClassId join t in _sportsContext.Teacher on s.TeacherId equals t.Id where c.SchoolCode == schoolCode && c.GradeId == gradeId select new ClassListDto { ClassId = c.Id, ClassName = c.ClassName, GradeName = c.GradeName, TeacherName = t.TeacherName }).OrderBy(x => x.ClassId).ToListAsync(); var studentList = await ( from s in _sportsContext.Student join c in _sportsContext.Class on s.ClassId equals c.Id where c.SchoolCode == schoolCode && c.GradeId == gradeId select new { s.StudentNo, s.Sex, ClassId = c.Id }).ToListAsync(); res.GradeName = classList.FirstOrDefault()?.GradeName ?? ""; res.ClassCount = classList.Count; res.MaleCount = studentList.Count(x => x.Sex == 1); res.FemaleCount = studentList.Count(x => x.Sex == 2); // 先尝试从缓存读取 var allData = _caching.Get>(cacheKey); if (allData == null|| allData.Count==0) { allData = await _userContext.HeartRateData .Where(x => x.SchoolCode == schoolCode && x.ScoreTime > dayStart && x.ScoreTime <= dayEnd) .ToListAsync(); // 写入缓存,过期时间 8 小时 = 28800 秒 _caching.AddObject(cacheKey, allData, 28800); } var data = allData.Where(x => x.GradeId == gradeId).ToList(); res.SportsData.OnlineDevicesCount = data.GroupBy(x => x.StudentNo).Count(); res.SportsData.AvgHeartRate = (int)data.Select(x => x.Value).DefaultIfEmpty(0).Average(); var durationsByStudent = HeartRateReportHelper.CalculateDurations(data, x => (x.StudentNo, x.Sex, x.ClassId)); res.SportsData.AvgDuration = (int)durationsByStudent.Values.DefaultIfEmpty(0).Average(); var maleReach = durationsByStudent.Count(kv => kv.Key.Sex == 1 && kv.Value >= 7200); var femaleReach = durationsByStudent.Count(kv => kv.Key.Sex == 2 && kv.Value >= 7200); res.SportsData.ReachCount = maleReach + femaleReach; res.SportsData.NotReachCount = res.StudentCount - res.SportsData.ReachCount; res.SportsData.HeartRateTrend = HeartRateReportHelper.BuildHeartRateTrend(data); foreach (var cls in classList) { var total = studentList.Count(x => x.ClassId == cls.ClassId); var maleQualified = durationsByStudent.Count(kv => kv.Key.ClassId == cls.ClassId && kv.Key.Sex == 1 && kv.Value >= 7200); var femaleQualified = durationsByStudent.Count(kv => kv.Key.ClassId == cls.ClassId && kv.Key.Sex == 2 && kv.Value >= 7200); res.ClassList.Add(new ClassListDto { ClassId = cls.ClassId, ClassName = cls.ClassName, TeacherName = cls.TeacherName, GradeName = cls.GradeName, StudentCount = total, MaleReachCount = maleQualified, FemaleReachCount = femaleQualified, ReachRate = HeartRateReportHelper.CalculateReachRate(maleQualified + femaleQualified, total) }); } return res; } /// /// 班级心率报告 /// /// /// /// public async Task ClassHeartRateReport(int classId, DateTime? scoreTime = null) { var (dayStart, dayEnd, effective) = HeartRateReportHelper.GetTimeRange(scoreTime); var res = new ClassHeartRateReportDataDto { StartEndTime = effective.ToString() }; var classInfo = await ( from c in _sportsContext.Class join s in _sportsContext.ClassAssocTeacher on c.Id equals s.ClassId join t in _sportsContext.Teacher on s.TeacherId equals t.Id where c.SchoolCode == schoolCode && c.Id == classId select new { c.GradeName, c.ClassName, t.TeacherName }).FirstOrDefaultAsync(); var studentList = await _sportsContext.Student .Where(s => s.SchoolCode == schoolCode && s.ClassId == classId) .Select(s => new { s.StudentNo, s.StudentName, s.Age, s.Sex, s.Photo }).ToListAsync(); res.GradeName = classInfo?.GradeName ?? ""; res.ClassName = classInfo?.ClassName ?? ""; res.MaleCount = studentList.Count(x => x.Sex == 1); res.FemaleCount = studentList.Count(x => x.Sex == 2); // 先尝试从缓存读取 var allData = _caching.Get>(cacheKey); if (allData == null || allData.Count == 0) { allData = await _userContext.HeartRateData .Where(x => x.SchoolCode == schoolCode && x.ScoreTime > dayStart && x.ScoreTime <= dayEnd) .ToListAsync(); // 写入缓存,过期时间 8 小时 = 28800 秒 _caching.AddObject(cacheKey, allData, 28800); } var data = allData.Where(x => x.GradeId == classId).ToList(); res.SportsData.OnlineDevicesCount = data.GroupBy(x => x.StudentNo).Count(); res.SportsData.AvgHeartRate = (int)data.Select(x => x.Value).DefaultIfEmpty(0).Average(); var durationsByStudent = HeartRateReportHelper.CalculateDurations(data, x => x.StudentNo); res.SportsData.AvgDuration = (int)durationsByStudent.Values.DefaultIfEmpty(0).Average(); res.SportsData.ReachCount = durationsByStudent.Count(kv => kv.Value >= 7200); res.SportsData.NotReachCount = res.StudentCount - res.SportsData.ReachCount; res.SportsData.HeartRateTrend = HeartRateReportHelper.BuildHeartRateTrend(data); foreach (var student in studentList) { int duration = durationsByStudent.TryGetValue(student.StudentNo, out var d) ? d : 0; res.StudentList.Add(new StudentListDto { StudentNo = student.StudentNo, StudentName = student.StudentName, Age = student.Age, Sex = student.Sex, Photo = student.Photo ?? "", Duration = duration, IsReach = duration >= 7200 }); } return res; } /// /// 学生心率报告 /// /// /// public async Task StudentHeartRateReport(string studentNo, DateTime? scoreTime = null) { var (dayStart, dayEnd, _) = HeartRateReportHelper.GetTimeRange(scoreTime); // 先尝试从缓存读取 var allData = _caching.Get>(cacheKey); if (allData == null || allData.Count == 0) { allData = await _userContext.HeartRateData .Where(x => x.SchoolCode == schoolCode && x.ScoreTime > dayStart && x.ScoreTime <= dayEnd) .ToListAsync(); // 写入缓存,过期时间 8 小时 = 28800 秒 _caching.AddObject(cacheKey, allData, 28800); } var data = allData.Where(x => x.StudentNo == studentNo).ToList(); return new StudentHeartRateReportDataDto { HeartRateTrend = HeartRateReportHelper.BuildHeartRateTrend(data) }; } } }