2025-06-27 15:37:57 +08:00

314 lines
13 KiB
C#

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.LargeScreen;
using YD_AllHeartRates.Commons.MemoryCaches;
using YD_AllHeartRates.Commons.Utils;
namespace YD_AllHeartRates.Api.Services.Impl
{
/// <summary>
/// 服务实现
/// </summary>
public class HeartRateReportService : IHeartRateReportService
{
public SmartSportsContext _sportsContext;
public UserContext _userContext;
private readonly ICaching _caching;
private readonly LoginContext _loginContext;
private string cacheKey;
private string schoolCode;
/// <summary>
/// 构造
/// </summary>
public HeartRateReportService(SmartSportsContext sportsContext, UserContext userContext, ICaching caching, LoginContext loginContext)
{
_sportsContext = sportsContext;
_userContext = userContext;
_caching = caching;
_loginContext = loginContext;
schoolCode = _loginContext.SchoolCode;
cacheKey = $"HeartRateData_{schoolCode}";
}
/// <summary>
/// 学校心率报告
/// </summary>
/// <returns></returns>
public async Task<SchoolHeartRateReportDataDto> 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<List<HeartRateData>>(cacheKey);
if (data == null)
{
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;
}
/// <summary>
/// 年级心率报告
/// </summary>
/// <returns></returns>
public async Task<GradeHeartRateReportDataDto> 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<List<HeartRateData>>(cacheKey);
if (allData == null)
{
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;
}
/// <summary>
/// 班级心率报告
/// </summary>
/// <param name="classId"></param>
/// <param name="scoreTime"></param>
/// <returns></returns>
public async Task<ClassHeartRateReportDataDto> 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<List<HeartRateData>>(cacheKey);
if (allData == null)
{
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;
}
/// <summary>
/// 学生心率报告
/// </summary>
/// <param name="studentNo"></param>
/// <param name="scoreTime"></param>
public async Task<StudentHeartRateReportDataDto> StudentHeartRateReport(string studentNo, DateTime? scoreTime = null)
{
var (dayStart, dayEnd, _) = HeartRateReportHelper.GetTimeRange(scoreTime);
// 先尝试从缓存读取
var allData = _caching.Get<List<HeartRateData>>(cacheKey);
if (allData == null)
{
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)
};
}
}
}