2025-07-29 11:15:06 +08:00

242 lines
8.9 KiB
C#

using AutoMapper;
using Microsoft.EntityFrameworkCore;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using System.Drawing;
using System.Reflection.PortableExecutable;
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.LargeScreen;
using YD_AllHeartRates.Commons.MemoryCaches;
namespace YD_AllHeartRates.Api.Services.Impl
{
/// <summary>
/// 服务实现
/// </summary>
public class LargeScreenService : ILargeScreenService
{
public SmartSportsContext _sportsContext;
public UserContext _userContext;
private readonly LoginContext _loginContext;
private string schoolCode;
private readonly ICaching _caching;
/// <summary>
/// 构造
/// </summary>
public LargeScreenService(SmartSportsContext sportsContext, UserContext userContext, LoginContext loginContext, ICaching caching)
{
_sportsContext = sportsContext;
_userContext = userContext;
_loginContext = loginContext;
_caching = caching;
schoolCode = _loginContext.SchoolCode;
}
/// <summary>
/// 获取学校数据
/// </summary>
/// <returns></returns>
public async Task<SchoolDto> SchoolInfo()
{
var res = new SchoolDto();
res.SchoolCode = schoolCode;
res.Name = _loginContext.UserName;
res.FlushTime = Convert.ToInt32(AppSettings.FlushTime);
var gIds = await _sportsContext.SchoolAssocGrade.Where(x => x.SchoolCode == schoolCode).Select(x => x.GradeId).ToListAsync();
var grades = await (
from g in _sportsContext.Grade
join c in _sportsContext.Class on g.Id equals c.GradeId into classGroup
from c in classGroup.DefaultIfEmpty()
where gIds.Contains(g.Id) && c.SchoolCode == schoolCode
group c by new { g.Id, g.GradeName } into gradeGroup
select new Grades()
{
Id = gradeGroup.Key.Id,
Name = gradeGroup.Key.GradeName,
Class = gradeGroup.Where(c => c != null).Select(c => new Classes
{
Id = c.Id,
Name = c.ClassName
}).ToList()
}).ToListAsync();
res.Grade = grades;
var devices = await _sportsContext.Device.Where(x => x.SchoolCode == schoolCode).ToListAsync();
var heartRateDevices = devices.Where(x => x.DeviceType == 1).ToList();
var jumpingRopeDevices = devices.Where(x => x.DeviceType == 2).ToList();
res.HeartRateAllCount = heartRateDevices.Count();
res.JumpingRopeAllCount = jumpingRopeDevices.Count();
return res;
}
/// <summary>
/// 根据班级Id获取学生列表
/// </summary>
/// <param name="classId"></param>
/// <returns></returns>
public async Task<List<StudentDto>> StudentList(int classId)
{
var res = await _sportsContext.Student.Where(x => x.SchoolCode == schoolCode && x.ClassId == classId && x.StudentStatus == 1).Select(x => new StudentDto
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Photo = x.Photo ?? "",
Sex = x.Sex
}).ToListAsync();
return res;
}
/// <summary>
/// 心率数据
/// </summary>
/// <returns></returns>
public async Task<HeartRateDataDto> HeartRateData(int classId)
{
var res = new HeartRateDataDto();
var now = DateTime.Now;
var tenMinutesAgo = now.AddMinutes(-1);
// 在线心率设备
int onlineHeartRateCount = RedisHelper.Keys("heartRate:*").Length;
res.HeartRateOnlineCount = onlineHeartRateCount;
// 在线跳绳设备
int onlineJumpRopeCount = RedisHelper.Keys("jumpRope:raw:*").Length;
res.JumpingRopeOnLineCount = onlineJumpRopeCount;
// 1. 构建缓存 key
string studentListKey = $"students:{schoolCode}:{classId}";
// 2. 尝试从缓存获取
var studentList = _caching.Get<List<StudentDto>>(studentListKey);
// 3. 如果缓存没有 → 查询数据库 + 写入缓存
if (studentList == null || studentList.Count == 0)
{
studentList = await _sportsContext.Student
.Where(x => x.ClassId == classId && x.SchoolCode == schoolCode && x.StudentStatus == 1)
.Select(x => new StudentDto
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Sex = x.Sex,
Photo = x.Photo ?? ""
})
.ToListAsync();
_caching.AddObject(studentListKey, studentList, 60); // 缓存 8 小时
}
int warmUp = 0, low = 0, medium = 0, high = 0, warning = 0;
foreach (var student in studentList)
{
var heartRateKey = $"heartRate:{student.StudentNo}";
//var jumpRopeKey = $"jumpRope:{student.StudentNo}";
//string jumpRopeKey = $"{student.StudentNo}_{DateTime.Now:yyyyMMdd}";
var jumpRopeKey = $"jumpRope:active:{student.StudentNo}:{DateTime.Now:yyyyMMdd}";
// 先从缓存拿
var heartRate = _caching.Get<HeartRateData>(heartRateKey);
var jumpRope = _caching.Get<JumpRopeData>(jumpRopeKey);
if (jumpRope == null)
{
jumpRope = await _userContext.JumpRopeData.Where(x => x.StudentNo == student.StudentNo).FirstOrDefaultAsync();
}
// ❗心率缓存未命中 → 单独查数据库
//if (heartRate == null)
//{
// heartRate = await _userContext.HeartRateData
// .Where(x => x.StudentNo == student.StudentNo && x.ScoreTime >= tenMinutesAgo)
// .OrderByDescending(x => x.ScoreTime)
// .FirstOrDefaultAsync();
// //if (heartRate != null)
// // _caching.AddObject(heartRateKey, heartRate, 60);
//}
// ❗跳绳缓存未命中 → 单独查数据库
//if (jumpRope == null)
//{
// jumpRope = await _userContext.JumpRopeData
// .Where(x => x.StudentNo == student.StudentNo && x.ScoreTime.Date == now.Date)
// .OrderByDescending(x => x.ScoreTime)
// .FirstOrDefaultAsync();
// if (jumpRope != null)
// _caching.AddObject(jumpRopeKey, jumpRope, 60);
//}
// 心率强度判断
int strength = heartRate?.Strength ?? 0;
switch (strength)
{
case > 0 and < 50:
warmUp++;
break;
case >= 50 and < 60:
low++;
break;
case >= 60 and < 70:
medium++;
break;
case >= 70 and < 85:
high++;
break;
case >= 85:
warning++;
break;
}
res.StudentList.Add(new StudentDto
{
StudentName = student.StudentName,
StudentNo = student.StudentNo,
Photo = student.Photo ?? "",
Sex = student.Sex,
HeartRate = heartRate?.Value ?? 0,
JumpingRope = jumpRope?.JumpValue ?? 0,
Strength = strength,
HeartRateQuantityOfElectricity = heartRate?.QuantityOfElectricity ?? 0,
JumpingRopeQuantityOfElectricity = jumpRope?.QuantityOfElectricity ?? 0
});
}
int total = RedisHelper.Keys("heartRate:*").Length;
int CalcPercentage(int count) =>
total == 0 ? 0 : (int)Math.Round(count * 100.0 / total);
res.WarmUp = CalcPercentage(warmUp);
res.Low = CalcPercentage(low);
res.Medium = CalcPercentage(medium);
res.High = CalcPercentage(high);
res.Warning = CalcPercentage(warning);
return res;
}
}
}