296 lines
9.2 KiB
C#
296 lines
9.2 KiB
C#
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();
|
||
}
|
||
}
|
||
}
|