YD_XinWei.Api/Server/YD_XinWei/Services/Impl/LargeScreenService.cs
2025-08-26 10:11:06 +08:00

324 lines
13 KiB
C#

using AutoMapper;
using Microsoft.EntityFrameworkCore;
using VOL.Entity.DomainModels.XinWei;
using VOL.Entity.Enum;
using YD_XinWei.Api.Context;
using YD_XinWei.Api.Services.Interface;
using YD_XinWei.Api.SmartSportsEntitys;
using YD_XinWei.Api.Utilities;
using YD_XinWei.Commons.Dto.Common;
using YD_XinWei.Commons.Dto.LargeScreen;
using YD_XinWei.Commons.Enum;
using YD_XinWei.Commons.MemoryCaches;
namespace YD_XinWei.Api.Services.Impl
{
/// <summary>
/// 大屏服务
/// </summary>
public class LargeScreenService : ILargeScreenService
{
public SmartSportsContext _sportsContext;
private readonly IMapper _mapper;
private readonly ICacheService _caching;
/// <summary>
/// 构造
/// </summary>
/// <param name="sportsContext"></param>
/// <param name="mapper"></param>
public LargeScreenService(SmartSportsContext sportsContext, IMapper mapper, ICacheService caching)
{
_sportsContext = sportsContext;
_mapper = mapper;
_caching = caching;
}
/// <summary>
/// 获取项目列表
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<List<SportsProjectListDto>> SportsProjectList(int orgId)
{
var testingProjectKey = $"TestingProject_{orgId}";
var res = _caching.Get<List<SportsProjectListDto>>(testingProjectKey);
if (res == null || res.Count == 0)
{
res = await _sportsContext.XW_TestingProject.Where(x => x.OrgId == orgId)
.Select(x => new SportsProjectListDto()
{
ProjectId = x.ProjectId,
ProjectName = x.ProjectName,
CategoryValue = x.CategoryValue,
CategoryEnum = x.CategoryEnum,
Unit = ((SportsTestItemType)x.CategoryValue).GetUnit()
}).ToListAsync();
}
return res;
}
/// <summary>
/// 项目成绩大屏数据
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<LargeScreenDto> ItemResultLargeScreenData(int orgId)
{
var res = new LargeScreenDto();
var schoolKeyKey = $"School_{orgId}";
var schoolCode = _caching.Get(schoolKeyKey);
if (string.IsNullOrWhiteSpace(schoolCode))
{
schoolCode = await _sportsContext.School.Where(x => x.Id == orgId).Select(x => x.SchoolCode).FirstAsync();
_caching.Add(schoolKeyKey, schoolCode);
}
var sportsTestDataKey = $"SportsTestData_{schoolCode}";
var studentListKey = $"StudentList_{schoolCode}";
var studentList = _caching.Get<List<StudentListDto>>(studentListKey);
if (studentList == null || studentList.Count == 0)
{
studentList = await _sportsContext.Student.Where(x => x.SchoolCode == schoolCode).Select(x => new StudentListDto
{
StudentNo = x.StudentNo,
Sex = x.Sex,
Photo = x.Photo ?? ""
}).ToListAsync();
_caching.AddObject(studentListKey, studentList, 3600);
}
res.BasicInfo.FemaleCount = studentList.Count(x => x.Sex == 2);
res.BasicInfo.MaleCount = studentList.Count(x => x.Sex == 1);
var sportsTestData = _caching.Get<List<SportsTestDataDto>>(sportsTestDataKey);
if (sportsTestData == null || sportsTestData.Count == 0)
{
sportsTestData = await _sportsContext.SportsTestValue.Where(x => x.SchoolCode == schoolCode && x.DataSource == DataSource.XW)
.Select(x => new SportsTestDataDto()
{
SchoolCode = x.SchoolCode,
CategoryValue = x.CategoryValue,
ClassId = x.ClassId,
ClassName = x.ClassName,
GradeId = x.GradeId,
GradeName = x.GradeName,
TeacherId = x.TeacherId,
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Sex = x.Sex,
ScoreTime = x.ScoreTime,
Height = x.Height,
Weight = x.Weight,
Value = x.Value,
Score = x.Score,
Rank = x.Rank
}).ToListAsync();
_caching.AddObject(sportsTestDataKey, sportsTestData, 60);
}
var bestScoreData = sportsTestData
.GroupBy(x => x.StudentNo)
.Select(g => g.OrderByDescending(x => x.Score).First())
.ToList();
var projectResults = bestScoreData
.GroupBy(x => x.CategoryValue)
.Select(g => new ItemDataDto
{
CategoryValue = g.Key,
// 优良率计算:分数 > 80 的人数 ÷ 总人数
ExcellentRate = (int)Math.Round(sportsTestData.Count(x => x.CategoryValue == g.Key && x.Score > 80) * 100.0 / Math.Max(sportsTestData.Count(x => x.CategoryValue == g.Key), 1), MidpointRounding.AwayFromZero),
// 男生前十名
MaleRankList = g.Where(x => x.Sex == 1)
.OrderByDescending(x => x.Value)
.Take(10)
.Select((x, index) => new StudentScoreRankingDto
{
Rank = index + 1,
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Value = x.CategoryValue == 1 ? $"{x.Height}cm/{x.Weight}kg" : $"{x.Value}{((SportsTestItemType)x.CategoryValue).GetUnit()}"
}).ToList(),
// 女生前十名
FemaleRankList = g.Where(x => x.Sex == 2)
.OrderByDescending(x => x.Value)
.Take(10)
.Select((x, index) => new StudentScoreRankingDto
{
Rank = index + 1,
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Value = x.CategoryValue == 1 ? $"{x.Height}cm/{x.Weight}kg" : $"{x.Value}{((SportsTestItemType)x.CategoryValue).GetUnit()}"
}).ToList()
})
.ToList();
res.ItemDatalist = projectResults;
var classListKey = $"ClassList_{schoolCode}";
var classList = _caching.Get<List<S_Class>>(classListKey);
if (classList == null || classList.Count == 0)
{
classList = await _sportsContext.Class.Where(x => x.SchoolCode == schoolCode).ToListAsync();
_caching.AddObject(classListKey, classList, 3600);
}
var classRankingList = sportsTestData
.GroupBy(x => new { x.ClassId })
.Select(g => new ClassSportsRankingDto
{
ClassId = g.Key.ClassId,
ClassName = classList.Where(c => c.Id == g.Key.ClassId).Select(x => $"{x.GradeName}-{x.ClassName}").FirstOrDefault() ?? "",
Count = g.Count()
})
.OrderByDescending(x => x.Count)
.Take(5)
.Select((x, index) =>
{
x.Rank = index + 1;
return x;
})
.ToList();
res.ClassSportsRankList = classRankingList;
//var testingProjectKey = $"TestingProject_{orgId}";
//var testingProjects = _caching.Get<List<SportsProjectListDto>>(testingProjectKey) ?? new List<SportsProjectListDto>();
//var projectNameDict = testingProjects.ToDictionary(t => t.CategoryValue, t => t.ProjectName);
//var studentPhotoDict = studentList.Where(s => !string.IsNullOrEmpty(s.StudentNo)).ToDictionary(s => s.StudentNo, s => s.Photo ?? string.Empty);
//var latestPerStudent = sportsTestData
// .GroupBy(x => x.StudentNo)
// .Select(g => g.OrderByDescending(x => x.ScoreTime).First())
// .ToList();
//var realTimeResults = latestPerStudent
// .OrderByDescending(x => x.ScoreTime)
// .Take(50)
// .Select(x => new ItemRealTimeResultDto
// {
// StudentNo = x.StudentNo,
// StudentName = x.StudentName,
// Photo = studentPhotoDict.TryGetValue(x.StudentNo, out var photo) ? photo : string.Empty,
// GradeAndClassName = $"{x.GradeName}-{x.ClassName}",
// //ProjectName = projectNameDict.TryGetValue(x.CategoryValue, out var projectName) ? projectName : string.Empty,
// Value = x.Value,
// Score = x.Score,
// Rank = x.Rank ?? "不及格",
// ScoreTime = x.ScoreTime.ToString("yyyy-MM-dd HH:mm:ss")
// }).Reverse().ToList();
//res.ItemRealTimeResultList = realTimeResults;
var today = DateTime.Today;
// 本周一
var startOfThisWeek = today.AddDays(-(int)today.DayOfWeek + 1);
// 下周一(= 本周结束时间)
var startOfNextWeek = startOfThisWeek.AddDays(7);
// 上周一
var startOfLastWeek = startOfThisWeek.AddDays(-7);
// 本周数据
var thisWeekCount = sportsTestData
.Count(x => x.ScoreTime >= startOfThisWeek && x.ScoreTime < startOfNextWeek);
// 上周数据
var lastWeekCount = sportsTestData
.Count(x => x.ScoreTime >= startOfLastWeek && x.ScoreTime < startOfThisWeek);
res.BasicInfo.WeekTrainingCount = thisWeekCount;
res.BasicInfo.Increase = thisWeekCount - lastWeekCount;
return res;
}
/// <summary>
/// 各项目实时成绩
/// </summary>
/// <param name="orgId"></param>
/// <param name="lastTime"></param>
/// <returns></returns>
/// <summary>
/// 各项目实时成绩
/// </summary>
public async Task<List<ItemRealTimeResultDto>> ItemRealTimeResult(int orgId, DateTime? lastTime = null)
{
var schoolKeyKey = $"School_{orgId}";
var schoolCode = _caching.Get(schoolKeyKey);
var testingProjectKey = $"TestingProject_{orgId}";
var studentListKey = $"StudentList_{schoolCode}";
var studentList = _caching.Get<List<StudentListDto>>(studentListKey);
var testingProjects = _caching.Get<List<SportsProjectListDto>>(testingProjectKey) ?? new List<SportsProjectListDto>();
var projectNameDict = testingProjects.ToDictionary(t => t.CategoryValue, t => t.ProjectName);
var studentPhotoDict = studentList
.Where(s => !string.IsNullOrEmpty(s.StudentNo))
.ToDictionary(s => s.StudentNo, s => s.Photo ?? string.Empty);
// 1. 查询时只拉取需要的字段
var sourceData = await _sportsContext.SportsTestValue
.Where(s => s.SchoolCode == schoolCode && s.DataSource == DataSource.XW
&& (!lastTime.HasValue || s.ScoreTime > lastTime.Value))
.OrderByDescending(s => s.ScoreTime)
.Select(s => new
{
s.StudentNo,
s.StudentName,
s.GradeName,
s.ClassName,
s.CategoryValue,
s.Value,
s.Weight,
s.Height,
s.Score,
s.Rank,
s.ScoreTime
})
.Take(50)
.ToListAsync();
// 2. 内存分组取每个学生最新一条
var latestPerStudent = sourceData.OrderBy(x => x.ScoreTime).ToList();
// 3. 映射到 DTO
var realTimeResults = latestPerStudent
.Select(x => new ItemRealTimeResultDto
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Photo = studentPhotoDict.TryGetValue(x.StudentNo, out var photo) ? photo : string.Empty,
GradeAndClassName = $"{x.GradeName}-{x.ClassName}",
CategoryValue = x.CategoryValue,
Value = x.CategoryValue == 1 ? $"{x.Height}cm/{x.Weight}kg" : $"{x.Value}{((SportsTestItemType)x.CategoryValue).GetUnit()}",
Score = x.Score,
Rank = string.IsNullOrEmpty(x.Rank) ? "不及格" : x.Rank,
ScoreTime = x.ScoreTime.ToString("yyyy-MM-dd HH:mm:ss")
})
.ToList();
return realTimeResults;
}
}
}