314 lines
13 KiB
C#
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)
|
|
};
|
|
}
|
|
}
|
|
}
|