2026-03-16 10:33:46 +08:00

296 lines
9.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System;
using YD_AllHeartRates.Api.Context;
using YD_AllHeartRates.Api.Entitys;
using YD_AllHeartRates.Api.Services.Interface;
using YD_AllHeartRates.Api.Utilities;
using YD_AllHeartRates.Commons.Dto.DataPush;
using YD_AllHeartRates.Commons.MemoryCaches;
using YD_AllHeartRates.Commons.Utils;
namespace YD_AllHeartRates.Api.Middlewares
{
public class SportsRoutineReportJob : IHostedService, IDisposable
{
private readonly IDataPushService _dataPushService;
private Timer _timer;
private readonly ICaching _caching;
public UserContext _userContext;
private static string[] ChangXing = new string[]
{
"曹贺淳",
"陈思禹",
"陈朱海",
"代宇若",
"丁淑雅",
"董静思",
"樊玉顺",
"方欣玥",
"宫恩佑",
"郭依诺",
//"黄子熙",
//"计永安",
//"蒋圣煊",
//"李锦娴",
//"李语汐",
//"李梓皓",
//"刘家耀",
//"龙正柏瑀",
//"罗昕",
//"聂新淼",
//"彭禹凡",
//"宋皓晨",
//"孙海翔",
//"唐瀚宇",
//"陶柔紫若",
//"万欣怡",
//"王心语",
//"王梓皓",
//"王梓煜",
//"徐子琪",
//"许穆晨",
//"杨温馨",
//"杨子墨",
//"袁洛霏",
//"张宸溪",
//"张羽曦",
//"赵昊辰",
//"周舒洋"
};
private static string[] ZhengDa = new string[]
{
"沈懿轩",
"周宇飛",
"范昊瑜",
"张翼成",
"卫宇航",
"董书涵",
"吴彦君",
"朱灏辰",
"王叡之",
"施梓俊",
//"龚怡豪",
//"张子赫",
//"仇泽宇",
//"陈歆蕾",
//"朱旖文",
//"郭笑笑",
//"陈瑾萱",
//"蔡乐妮",
//"侯熙琰",
//"龚思涵",
//"朱瑾萱",
//"宋依涵",
//"顾渝尧",
//"张赛文",
//"沈美夕",
//"吴佳凤",
//"蒋添依",
//"茅苹苹",
//"李腾宇",
//"林少熙",
//"史梓晨",
//"丁文洁",
//"董利妍",
//"朱紫芸"
};
private readonly List<StudentCacheInfo> _students = new();
public SportsRoutineReportJob(IDataPushService dataPushService, UserContext userContext, ICaching caching)
{
_dataPushService = dataPushService;
_userContext = userContext;
_caching = caching;
// Redis 中缓存键
const string redisKey = "StudentEduInfos";
// 1. 尝试从 Redis 获取缓存
var cachedList = _caching.Get<List<StudentCacheInfo>>(redisKey);
if (cachedList != null && cachedList.Count > 0)
{
_students = cachedList;
Console.WriteLine("从 Redis 加载学生信息完成。");
return;
}
// 2. 缓存不存在 → 调用接口获取
var result = new List<StudentCacheInfo>();
// 正大中学
foreach (var name in ZhengDa)
{
var student = _dataPushService
.GetStudentEduId("上海市崇明区正大中学", "七年级", "5班", name, "")
.Result?
.FirstOrDefault();
if (student != null)
{
result.Add(new StudentCacheInfo
{
School = "上海市崇明区正大中学",
Grade = "七年级",
ClassName = "5班",
Name = name,
EduId = student.EduId
});
Console.WriteLine($"正大中学:[{DateTime.Now}] {name}: {student.EduId}");
}
}
// 长兴小学
foreach (var name in ChangXing)
{
var student = _dataPushService
.GetStudentEduId("上海市崇明区长兴小学", "二年级", "二年级2班", name, "")
.Result?
.FirstOrDefault();
if (student != null)
{
result.Add(new StudentCacheInfo
{
School = "上海市崇明区长兴小学",
Grade = "二年级",
ClassName = "二年级2班",
Name = name,
EduId = student.EduId
});
Console.WriteLine($"长兴小学:[{DateTime.Now}] {name}: {student.EduId}");
}
}
// 写入 Redis(直接存对象,不再存 json 字符串)
_caching.AddObject(redisKey, result);
_students = result;
Console.WriteLine("学生信息已缓存到 Redis。");
}
public Task StartAsync(CancellationToken cancellationToken)
{
// 每分钟检查一次时间,看是否在8-16点并整点执行
_timer = new Timer(async _ => await DoWork(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
private async Task DoWork()
{
var now = DateTime.Now;
// 每天 8:30 ~ 18:30 每小时30分执行
if (now.Hour < 8 || now.Hour > 18 || now.Minute != 23)
return;
var startTime = now.AddHours(-1);
var endTime = now;
Console.WriteLine($"[{now}] 开始统计心率数据 {startTime} ~ {endTime}");
try
{
// 1 查询1小时心率数据,并按分钟计算平均值
var list = await _userContext.HeartRateData
.Where(x => x.SchoolCode == AppSettings.SchoolCode && x.ScoreTime >= startTime && x.ScoreTime <= endTime)
.GroupBy(x => new
{
x.StudentNo,
Minute = new DateTime(
x.ScoreTime.Year,
x.ScoreTime.Month,
x.ScoreTime.Day,
x.ScoreTime.Hour,
x.ScoreTime.Minute,
0)
})
.Select(g => new
{
g.Key.StudentNo,
Minute = g.Key.Minute,
AvgValue = g.Average(x => x.Value),
AvgInstant = g.Average(x => x.InstantValue)
})
.ToListAsync();
// 2 按学生分组(提高查找效率)
var groups = list
.GroupBy(x => x.StudentNo)
.ToDictionary(
x => x.Key,
x => x.OrderBy(t => t.Minute).ToList()
);
var data = new List<StudentData>();
// 3 生成推送数据
for (int i = 0; i < _students.Count; i++)
{
var stu = _students[i];
if (!groups.TryGetValue(stu.EduId, out var stuData))
continue;
var hdsjList = stuData.Select(x => new HeartRateRecordDto
{
SJSJ = x.Minute.ToString("yyyy-MM-dd HH:mm:ss"),
XL = (int)Math.Round(x.AvgValue),
JXXL = (int)Math.Round(x.AvgInstant)
}).ToList();
// 保证每个学生60条数据
if (hdsjList.Count == 0)
continue;
var student = new StudentData
{
SBID = (i + 1).ToString("D2"),
XSEDUID = stu.EduId,
HDSJ = hdsjList
};
data.Add(student);
Console.WriteLine(
$"生成数据 → {stu.School}-{stu.Grade}-{stu.ClassName}-{stu.Name} | EduId:{stu.EduId} | {hdsjList.Count}条");
}
// 4 推送数据
if (data.Count > 0)
{
var res = await _dataPushService.SportsRoutineReport(data);
Console.WriteLine(
$"[{DateTime.Now}] HeartRate job code:{res?.code} msg:{res?.msg} 学生数:{data.Count}");
}
else
{
Console.WriteLine($"[{DateTime.Now}] 无心率数据需要推送");
}
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now}] HeartRate job error:{ex.Message}");
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
}