using AutoMapper; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using System.Collections.Generic; using System.Security.Cryptography; using VOL.Entity.DomainModels.XinWei; 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.Open; using YD_XinWei.Commons.Dto.School; using YD_XinWei.Commons; using YD_XinWei.Commons.Utils; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; using Newtonsoft.Json.Linq; using VOL.Entity.Enum; using System.Text.Json; namespace YD_XinWei.Api.Services.Impl { /// /// 服务实现 /// public class XinWeiService : IXinWeiService { public SmartSportsContext _sportsContext; private readonly IMapper _mapper; /// /// 构造 /// /// /// public XinWeiService(SmartSportsContext sportsContext, IMapper mapper) { _sportsContext = sportsContext; _mapper = mapper; } /// /// 获取设备信息 /// /// /// /// public async Task DeviceInfo(string deviceSerial) { var deviceInfo = await _sportsContext.XW_Device.FirstOrDefaultAsync(x => x.DeviceSerial == deviceSerial); var res = _mapper.Map(deviceInfo); return res; } /// /// 项目模式 /// /// public async Task> SportsModelTypeList() { var res = await _sportsContext.XW_ProjectMode.Select(x => new ProjectModeDto() { Id = x.Id, ProjectKind = x.ProjectKind, Name = x.Name }).ToListAsync(); return res; } /// /// 体育项目 /// /// 学校Id /// public async Task> OrgSportsProjectList(int orgId) { //var res = await _sportsContext.XW_TestingProject.Where(x => x.OrgId == orgId).Select(x => new TestingProjectDto() var res = await _sportsContext.XW_TestingProject.Where(x => x.OrgId == orgId).Select(x => new TestingProjectDto() { ProjectId = x.ProjectId, IsOpen = x.IsOpen, IsShow = x.IsShow, ProjectName = x.ProjectName }).ToListAsync(); return res; } /// /// 获取学生信息 /// /// 学校Id /// public async Task> MinimumOfStudentInfoList(int orgId) { string schoolCode = await _sportsContext.School.Where(x => x.Id == orgId).Select(x => x.SchoolCode).FirstAsync(); var res = await (from s in _sportsContext.Student join c in _sportsContext.Class on s.ClassId equals c.Id join g in _sportsContext.Grade on c.GradeId equals g.Id where s.SchoolCode == schoolCode select new StudentInfoDto() { Id = s.Id, OrgId = orgId, UserId = s.Id, StudentNo = s.StudentNo, Name = s.StudentName, Sex = s.Sex == 1 ? "男" : "女", ClassId = c.Id, GradeId = g.Id, RuleGradeId = g.Id, ClassName = c.ClassName, GradeName = g.GradeName, CardNo = new List() }).ToListAsync(); return res; } /// /// 获取名单列表 /// /// /// /// /// public async Task> SportsRosterList(int orgId, int userId = 0) { string schoolCode = await _sportsContext.School.Where(x => x.Id == orgId).Select(x => x.SchoolCode).FirstAsync(); var studentList = await _sportsContext.Student.Where(x => x.SchoolCode == schoolCode).ToListAsync(); var list = await (from c in _sportsContext.Class join g in _sportsContext.Grade on c.GradeId equals g.Id where c.SchoolCode == schoolCode select new SportsRosterDto() { Id = c.Id, OrgId = orgId, Name = c.ClassName, GradeId = g.Id, RuleGradeId = g.Id, GradeName = g.GradeName, Status = 1, Type = 1 }).ToListAsync(); foreach (var item in list) { var stuList = studentList.Where(x => x.ClassId == item.Id).ToList(); List sportList1 = new List(); foreach (var sport in stuList) { sportList1.Add(new SportsRosterStudentDto() { Id = sport.Id, GroupName = item.Name, ClassId = item.Id, ClassName = item.Name, GradeId = item.GradeId, GradeName = item.GradeName, OrgId = orgId, Sex = sport.Sex, UserName = sport.StudentName, StudentNo = sport.StudentNo, UserId = sport.Id, GroupOrder = sport.Id, RosterId = 1, }); } item.RosterGroupList.Add(new SportsRosterGroupDto() { GroupName = item.Name, RosterStudentList = sportList1 }); } //var list1 = list.Select(x => new SportsRosterDto //{ // Type = 2 //}).ToList(); //var mergedList = list.Concat(list1).ToList(); return list; } /// /// 获取人脸信息 /// /// /// public async Task FaceListOfDevicePage(FaceListOfDeviceDto dto) { //Console.WriteLine($"获取人脸信息 OrgId:{dto.OrgId}"); string schoolCode = await _sportsContext.School.Where(x => x.Id == dto.OrgId).Select(x => x.SchoolCode).FirstAsync(); var total = await _sportsContext.Student.CountAsync(x => x.SchoolCode == schoolCode && !string.IsNullOrWhiteSpace(x.Photo)); var list = await _sportsContext.Student .Where(x => x.SchoolCode == schoolCode && !string.IsNullOrWhiteSpace(x.Photo)) .Select(s => new DeviceUserFaceVo() { FaceId = s.StudentNo, FaceType = 1, FaceUrl = s.Photo, PersonId = s.StudentNo, UserId = s.Id }) .Skip((dto.PageNo - 1) * dto.PageSize) .Take(dto.PageSize) .ToListAsync(); //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(list, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/获取人脸信息{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); return new FaceListOfDevicePageDto() { Total = total, PageNum = dto.PageNo, PageSize = dto.PageSize, Pages = (int)Math.Ceiling((double)total / dto.PageSize), List = list }; } /// /// 获取训练评分规则 /// /// 学校Id /// public async Task> ScoreRules(int orgId) { var scoreRules = await _sportsContext.HealthStandards.ToListAsync(); var projects = await _sportsContext.XW_TestingProject.ToListAsync(); var projectModels = await _sportsContext.XW_ProjectMode.ToListAsync(); var scoreRuleItemsByType = scoreRules .GroupBy(x => x.CategoryEnum) .ToDictionary(g => g.Key, g => g.ToList()); List res = new List(); foreach (var projectModel in projectModels) { var project = projects.FirstOrDefault(x => x.ProjectId == projectModel.ProjectKind); if (project == null) continue; // Skip if project doesn't exist var scoreRuleTypes = projectModel.CategoryEnum.Split(','); var scoreRuleItems = scoreRuleTypes.SelectMany(type => scoreRuleItemsByType.GetValueOrDefault(type, new List())).ToList(); var groupedItemsData = scoreRuleItems.GroupBy(x => x.GradeId).ToList(); foreach (var grouped in groupedItemsData) { var maleItems = grouped.Where(x => x.Sex == 1).ToList(); var femaleItems = grouped.Where(x => x.Sex == 2).ToList(); var maleMax = maleItems.DefaultIfEmpty().Max(x => x?.MaxValue ?? 0); var maleMin = maleItems.DefaultIfEmpty().Max(x => x?.MinValue ?? 0); var maleMaxScore = maleItems.DefaultIfEmpty().Max(x => x?.Score ?? 0); var femaleMax = femaleItems.DefaultIfEmpty().Max(x => x?.MaxValue ?? 0); var femaleMin = femaleItems.DefaultIfEmpty().Max(x => x?.MinValue ?? 0); var femaleMaxScore = femaleItems.DefaultIfEmpty().Max(x => x?.Score ?? 0); ScoreRuleDto scoreRuleDto = new ScoreRuleDto { GradeId = grouped.Key, OrgId = orgId, ProjectKind = project.ProjectId, ProjectName = project.ProjectName, ModelType = projectModel.Id, RuleType = 1, ProjectId = project.ProjectId, Items = grouped.Select(item => new ScoreRuleItemDto { Id = item.Id, Sex = item.Sex, Name = item.Score.GetRank().ToString(), Score = item.Score, Max = item.MaxValue, Min = item.MinValue }).OrderByDescending(x => x.Min).ToList() }; scoreRuleDto.Pluses = new List { //new ScoreRulePlusDto() //{ // Id = grouped.FirstOrDefault()?.Id ?? 0, // Sex = 1, // Max = maleMax, // Min = maleMin, // MaxScore = maleMaxScore, // Score = maleMaxScore //}, //new ScoreRulePlusDto() //{ // Id = grouped.FirstOrDefault()?.Id ?? 0, // Sex = 2, // Max = femaleMax, // Min = femaleMin, // MaxScore = femaleMaxScore, // Score = femaleMaxScore //} }; scoreRuleDto.ScoreScopes = new List { //new ScoreScope() //{ // Gender = 1, // MaxScore = maleMaxScore, // MaxCount = maleMax //}, //new ScoreScope() //{ // Gender = 2, // MaxScore = femaleMaxScore, // MaxCount = femaleMax //} }; res.Add(scoreRuleDto); } } return res; } /// /// 新增训练 /// /// /// public async Task AddTrain(AddTrainDto dto) { var jsonOptions = new JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/add_train_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); var logDir = "/tmp/xwlog"; Directory.CreateDirectory(logDir); await File.WriteAllTextAsync( $"{logDir}/add_train_{DateTime.Now:yyyyMMdd_HHmmss}.json", dtoJson ); var xwProject = await _sportsContext.XW_TestingProject.Where(x => x.OrgId == dto.OrgId).ToListAsync(); var healthStandards = await _sportsContext.HealthStandards.ToListAsync(); try { var entity = _mapper.Map(dto); entity.StudentList.ForEach(stu => { var project = xwProject.Where(x => x.ProjectId == dto.ProjectId).FirstOrDefault(); string categoryEnum = project?.CategoryEnum ?? ""; if (dto.ProjectId == 4 || dto.ProjectId == 11) { // 只除以10,保留两位小数 stu.Achievement = stu.Achievement.HasValue ? Math.Round(stu.Achievement.Value / 10.0, 2) : 0; stu.Achievement2 = stu.Achievement2.HasValue ? Math.Round(stu.Achievement2.Value / 10.0, 2) : 0; } if (dto.ProjectId == 3) { // 除以1000再除以10,即除以10000,保留两位小数 stu.Achievement = stu.Achievement.HasValue ? Math.Round(stu.Achievement.Value / 1000.0, 2) - 0.5 : 0; } if (dto.ProjectId == 2) { // 除以1000再除以60,保留两位小数 stu.Achievement = stu.Achievement.HasValue ? Math.Round(stu.Achievement.Value / 1000.0 / 60, 2) : 0; } if (dto.ProjectId == 21) { var value = stu.Achievement2; var standard = healthStandards.Where(x => x.CategoryEnum.Equals(categoryEnum) && x.GradeId == x.GradeId && x.Sex == x.Sex && value >= x.MinValue && value < x.MaxValue ).FirstOrDefault(); if (standard != null) { stu.Score = standard.Score; stu.Rating = standard.Score.GetRank(); } } else { var standard = healthStandards.Where(x => x.CategoryEnum.Equals(categoryEnum) && x.GradeId == x.GradeId && x.Sex == x.Sex && stu.Achievement >= x.MinValue && stu.Achievement < x.MaxValue ).FirstOrDefault(); if (standard != null) { stu.Score = standard.Score; stu.Rating = standard.Score.GetRank(); } } stu.OrgId = dto.OrgId; stu.CreateTime = dto.EndTime; }); await _sportsContext.XW_SportsTestData.AddAsync(entity); var sportsTestValueEntinys = await AddSportsTestValueEntity(dto, healthStandards, xwProject); await _sportsContext.SportsTestValue.AddRangeAsync(sportsTestValueEntinys); await _sportsContext.SaveChangesAsync(); var res = _mapper.Map(entity); Console.WriteLine("新增成功"); return res; } catch (Exception e) { Console.WriteLine(e.Message + "/n" + e.InnerException); } return null; } private async Task> AddSportsTestValueEntity(AddTrainDto dto, List healthStandards, List xwProject) { var sportsTestValueEntinys = new List(); string schoolCode = await _sportsContext.School.Where(x => x.Id == dto.OrgId).Select(x => x.SchoolCode).FirstAsync(); var nowTime = DateTime.Now; foreach (var data in dto.StudentList) { if (data.Achievement == 0) continue; var sportsTestValueEntity = new N_SportsTestValue(); var project = xwProject.Where(x => x.ProjectId == dto.ProjectId).FirstOrDefault(); string categoryEnum = project?.CategoryEnum ?? ""; float value = data.Achievement ?? 0; if (dto.ProjectId == 4 || dto.ProjectId == 11) { // 只除以10,保留两位小数 value = (float)(data.Achievement.HasValue ? Math.Round(data.Achievement.Value / 10.0, 2) : 0); } if (dto.ProjectId == 3) { // 除以1000再除以10,即除以10000,保留两位小数,再减0.5 value = (float)((data.Achievement.HasValue ? Math.Round(data.Achievement.Value / 1000.0, 2) : 0) - 0.5); } if (dto.ProjectId == 21) { // Achievement2 除以10,保留两位小数 value = (float)(data.Achievement2.HasValue ? Math.Round(data.Achievement2.Value / 10.0, 2) : 0); // Height 除以10 sportsTestValueEntity.Height = (float)(data.Achievement.HasValue ? Math.Round(data.Achievement.Value / 10.0, 2) : 0); // Weight 除以100 sportsTestValueEntity.Weight = (float)(data.OriginalAchievement.HasValue ? Math.Round(data.OriginalAchievement.Value / 100.0, 2) : 0); } //中长跑 if (dto.ProjectId == 2) { // 只除以1000,保留两位小数 value = (float)(data.Achievement.HasValue ? Math.Round(data.Achievement.Value / 1000.0, 2) : 0); if (data.Sex == 1) categoryEnum = "MeterRun_1000"; else categoryEnum = "MeterRun_800"; } var standard = healthStandards.Where(x => x.CategoryEnum.Equals(categoryEnum) && x.GradeId == data.GradeId && x.Sex == data.Sex && value >= x.MinValue && value < x.MaxValue ).FirstOrDefault(); if (standard != null) { sportsTestValueEntity.Score = standard.Score; sportsTestValueEntity.Rank = standard.Rank; } sportsTestValueEntity.Score = (float)(data.Score ?? 0); sportsTestValueEntity.SchoolCode = schoolCode; sportsTestValueEntity.Sex = (int)data.Sex; sportsTestValueEntity.DataSource = DataSource.XW; sportsTestValueEntity.CategoryValue = project?.CategoryValue ?? 0; sportsTestValueEntity.CategoryEnum = categoryEnum; sportsTestValueEntity.GradeId = data.GradeId; sportsTestValueEntity.GradeName = data.GradeName; sportsTestValueEntity.ClassId = data.ClassId; sportsTestValueEntity.ClassName = data.ClassName; sportsTestValueEntity.TeacherId = data.TeacherId ?? 0; sportsTestValueEntity.TeacherName = data.Teacher; sportsTestValueEntity.StudentNo = data.StudentNo; sportsTestValueEntity.StudentName = data.StudentName; sportsTestValueEntity.Value = value; sportsTestValueEntity.Creator = dto.OrgId ?? 0; sportsTestValueEntity.CreateDate = nowTime; sportsTestValueEntity.ScoreTime = Convert.ToDateTime(dto.EndTime); sportsTestValueEntity.AdditionalScore = (float)(data.PlusesScore ?? 0); SemesterDto semesterDto = new SemesterDto(); sportsTestValueEntity.Year = semesterDto.Year; sportsTestValueEntity.Semester = semesterDto.Semester; sportsTestValueEntity.IsDisplay = true; sportsTestValueEntinys.Add(sportsTestValueEntity); } return sportsTestValueEntinys; } /// /// 年级排名 /// /// /// /// public async Task GradeRankingGet(int studentId, int trainId) { var res = new GradeRankingVo(); Console.WriteLine($"GradeRankingGet:{studentId},{trainId}"); var now = DateTime.UtcNow; var firstDayOfMonth = new DateTime(now.Year, now.Month, 1); var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); var studentResults = await _sportsContext.XW_TrainStudents.Where(x => x.XW_SportsTestDataId == trainId) .Select(x => new { x.GradeId, x.StudentId, x.StudentName, x.Rating, x.CreateTime, x.Score }).ToListAsync(); var student = studentResults.FirstOrDefault(x => x.StudentId == studentId); if (student == null) { return res; } var rankedResults = studentResults .Where(x => x.GradeId == student.GradeId && DateTime.TryParse(x.CreateTime, out var createTime) && createTime >= firstDayOfMonth && createTime <= lastDayOfMonth) .OrderByDescending(x => x.Score) .Select((x, index) => new { x.StudentId, Rank = index + 1 }) .ToList(); var studentRank = rankedResults.FirstOrDefault(x => x.StudentId == student.StudentId)?.Rank.ToString() ?? "无数据"; res.Rating = student.Score.GetRankStr(); res.MonthRanking = studentRank; res.Score = student.Score.ToString(); return res; } /// /// 全部历史记录 /// /// /// public async Task HistoryGet(HistoryGetDto dto) { //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/HistoryGet_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); var res = new TrainHistoryResponseDto(); var trainList = await _sportsContext.XW_SportsTestData .Where(x => x.OrgId == dto.OrgId && x.ProjectKind == Convert.ToInt32(dto.ProjectKind) && x.ModelType == dto.ModelType) .ToListAsync(); if (trainList == null || !trainList.Any()) return res; var trainIds = trainList.Select(t => t.Id).ToList(); var query = _sportsContext.XW_TrainStudents .Where(x => trainIds.Contains(x.XW_SportsTestDataId)) .AsQueryable(); // 动态排序 if (!string.IsNullOrEmpty(dto.OrderByColumn)) { bool isAsc = dto.IsAsc?.ToLower() == "asc"; query = isAsc ? query.OrderBy(x => EF.Property(x, dto.OrderByColumn)) : query.OrderByDescending(x => EF.Property(x, dto.OrderByColumn)); } else { query = query.OrderByDescending(x => x.Id); // 默认排序 } var total = await query.CountAsync(); var list = await query .Skip((dto.PageNo - 1) * dto.PageSize) .Take(dto.PageSize) .Select(x => new TrainHistoryVo() { TrainStudentId = x.Id, ClassName = x.ClassName, CreateTime = x.CreateTime ?? "无数据", Achievement = x.Achievement.ToString() ?? "无数据", Rating = x.Score.GetRankStr(), Score = x.Score.ToString() ?? "无数据", Sex = x.Sex == 1 ? "男" : "女", StudentName = x.StudentName, TouristFlag = false, DoubleFlag = false, }).OrderByDescending(x => x.CreateTime) .ToListAsync(); res.Total = total; res.Rows = list; return res; } /// /// 历史记录详细 /// /// /// public async Task TrainAnalysisHistoryDetail(HistoryDetailRequestDto dto) { //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/TrainAnalysisHistoryDetail_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); var trainModel = await _sportsContext.XW_TrainStudents.Where(x => x.Id == dto.TrainStudentId).Select(x => new { x.XW_SportsTestDataId, x.StudentId, x.GradeId }).FirstAsync(); var stuHeadUrl = await _sportsContext.Student.Where(x => x.Id == trainModel.StudentId).Select(x => x.Photo).FirstOrDefaultAsync(); var train = await _sportsContext.XW_SportsTestData.Where(x => x.Id == trainModel.XW_SportsTestDataId).Select(x => new { x.ProjectName, x.ProjectId }).FirstOrDefaultAsync(); var now = DateTime.UtcNow; var firstDayOfMonth = new DateTime(now.Year, now.Month, 1); var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); var trainIds = await _sportsContext.XW_SportsTestData.Where(x => x.ProjectId == train.ProjectId).Select(x => x.Id).ToListAsync(); var studentResults = await _sportsContext.XW_TrainStudents.Where(x => trainIds.Contains(x.XW_SportsTestDataId)) .Select(x => new { x.Id, x.GradeId, x.StudentId, x.StudentName, x.Rating, x.CreateTime, x.Score, x.Achievement }).ToListAsync(); var student = studentResults.Where(x => x.Id == dto.TrainStudentId).OrderByDescending(x => x.Score).FirstOrDefault(); if (student == null) { return null; } var rankedResults = studentResults .Where(x => x.GradeId == student.GradeId && DateTime.TryParse(x.CreateTime, out var createTime) && createTime >= firstDayOfMonth && createTime <= lastDayOfMonth) .OrderByDescending(x => x.Achievement) .Select((x, index) => new { x.Id, Rank = index + 1 }) .ToList(); var studentRank = rankedResults.FirstOrDefault(x => x.Id == dto.TrainStudentId)?.Rank.ToString() ?? "无数据"; var res = await _sportsContext.XW_TrainStudents.Where(x => x.Id == dto.TrainStudentId).Include(x => x.TrainViolationsBO).Select(x => new TrainAnalusisVo() { Achievement = x.Achievement.ToString() ?? "无数据", ClassName = x.ClassName, GradeRanking = studentRank, HeadImgUrl = stuHeadUrl, ProjectName = train.ProjectName, Rating = x.Score.GetRankStr(), Score = x.Score.ToString() ?? "无数据", Sex = x.Sex == 1 ? "男" : "女", StudentName = x.StudentName, Times = x.Times.ToString(), VideoRecord = x.VideoRecord, ViolationsNumber = x.TrainViolationsBO.Count(), TrainViolationsVo = x.TrainViolationsBO.Select(x => new TrainViolationsVo() { CreateTime = x.TrainTime, TrainTime = x.TrainTime, PicUrl = x.PicUrl, ViolationId = x.ViolationId, ViolationName = "", }).ToList(), }).FirstOrDefaultAsync(); return res; } /// /// 个人历史记录-历史成绩 /// /// /// public async Task HistoryPersonGet(HistoryPersonGetDto dto) { //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/HistoryPersonGet_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); // 1️⃣ 获取该机构、项目、模型类型的训练数据 var trainList = await _sportsContext.XW_SportsTestData .Where(x => x.OrgId == dto.OrgId && x.ProjectKind == Convert.ToInt32(dto.ProjectKind) && x.ModelType == dto.ModelType) .ToListAsync(); if (!trainList.Any()) return new TrainHistoryPersonVo(); var trainIds = trainList.Select(t => t.Id).ToList(); // 2️⃣ 获取学生成绩数据 var studentResults = await _sportsContext.XW_TrainStudents .Where(x => trainIds.Contains(x.XW_SportsTestDataId)) .Select(x => new { x.Id, x.ClassName, x.StudentId, x.StudentName, x.GradeId, x.Sex, x.Score, x.Achievement, x.CreateTime }) .ToListAsync(); if (!studentResults.Any()) return new TrainHistoryPersonVo(); // ⭐ 是否按升序(ModelType = 7) bool isAsc = dto.ModelType == 7; // 3️⃣ 获取指定学生(按成绩最优的一次) var student = isAsc ? studentResults .Where(x => x.StudentId == dto.StudentId) .OrderBy(x => x.Achievement) .FirstOrDefault() : studentResults .Where(x => x.StudentId == dto.StudentId) .OrderByDescending(x => x.Achievement) .FirstOrDefault(); if (student == null) return new TrainHistoryPersonVo(); // 4️⃣ 历史排名 var rankedResults = (isAsc ? studentResults .Where(x => x.GradeId == student.GradeId) .OrderBy(x => x.Achievement) : studentResults .Where(x => x.GradeId == student.GradeId) .OrderByDescending(x => x.Achievement) ) .Select((x, index) => new { x, Rank = index + 1 }) .ToList(); // 5️⃣ 当月数据 var monthList = studentResults .Where(x => DateTime.TryParse(x.CreateTime, out var date) && date.Year == DateTime.Now.Year && date.Month == DateTime.Now.Month) .ToList(); // 6️⃣ 当月排名 var monthRankedResults = (isAsc ? monthList .Where(x => x.GradeId == student.GradeId) .OrderBy(x => x.Achievement) : monthList .Where(x => x.GradeId == student.GradeId) .OrderByDescending(x => x.Achievement) ) .Select((x, index) => new { x, Rank = index + 1 }) .ToList(); // 7️⃣ 获取学生头像 var stuHeadUrl = await _sportsContext.Student .Where(x => x.Id == student.StudentId) .Select(x => x.Photo) .FirstOrDefaultAsync(); // 8️⃣ 历史最佳成绩 var bestStudentOverall = isAsc ? studentResults.OrderBy(x => x.Achievement).FirstOrDefault() : studentResults.OrderByDescending(x => x.Achievement).FirstOrDefault(); // 9️⃣ 当月最佳成绩 var bestStudentMonth = isAsc ? monthList.OrderBy(x => x.Achievement).FirstOrDefault() : monthList.OrderByDescending(x => x.Achievement).FirstOrDefault(); // 🔟 构建返回对象 return new TrainHistoryPersonVo { ClassName = student.ClassName, HeadImgUrl = stuHeadUrl, Sex = student.Sex == 1 ? "男" : "女", StudentName = student.StudentName, // 历史最佳成绩 HistoryBestScore = bestStudentOverall?.Achievement?.ToString("0.###") ?? "无数据", // 历史排名 HistoryRanking = rankedResults .FirstOrDefault(x => x.x.Id == student.Id)?.Rank.ToString() ?? "1", // 当月最佳成绩 MonthBestScore = bestStudentMonth?.Achievement?.ToString("0.###") ?? "无数据", // 当月排名 MonthRanking = monthRankedResults .FirstOrDefault(x => x.x.Id == student.Id)?.Rank.ToString() ?? "1" }; } /// /// 个人历史记录-记录列表 /// /// /// public async Task HistoryPersonListGet(HistoryPersonListDto dto) { //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/HistoryPersonListGet_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); var res = new TableDataPersonAnalysisVo(); var trainList = await _sportsContext.XW_SportsTestData .Where(x => x.OrgId == dto.OrgId && x.ProjectKind == Convert.ToInt32(dto.ProjectKind) && x.ModelType == dto.ModelType) .ToListAsync(); if (trainList == null || !trainList.Any()) return res; var trainIds = trainList.Select(t => t.Id).ToList(); var query = _sportsContext.XW_TrainStudents .Where(x => trainIds.Contains(x.XW_SportsTestDataId) && x.StudentId == dto.StudentId) .AsQueryable(); // 动态排序 if (!string.IsNullOrEmpty(dto.OrderByColumn)) { bool isAsc = dto.IsAsc?.ToLower() == "asc"; query = isAsc ? query.OrderBy(x => EF.Property(x, dto.OrderByColumn)) : query.OrderByDescending(x => EF.Property(x, dto.OrderByColumn)); } else { query = query.OrderByDescending(x => x.Id); // 默认排序 } var total = await query.CountAsync(); var list = await query .Skip((dto.PageNo - 1) * dto.PageSize) .Take(dto.PageSize) .Select(x => new PersonAnalysisVo() { TrainStudentId = x.Id, ClassName = x.ClassName, CreateTime = x.CreateTime ?? "无数据", //Achievement = dto.ModelType == 7 ? (x.Achievement.HasValue ? $"{x.Achievement.Value / 1000}.{x.Achievement.Value % 1000}" : "无数据") : (x.Achievement.HasValue ? x.Achievement.Value.ToString() : "无数据"), Achievement = x.Achievement.HasValue ? x.Achievement.Value.ToString("0.###") : "无数据", Rating = x.Score.GetRankStr(), Score = x.Score.ToString() ?? "无数据", Sex = x.Sex == 1 ? "男" : "女", StudentName = x.StudentName, DoubleFlag = false, }).OrderByDescending(x => x.CreateTime) .ToListAsync(); res.Total = total; res.Rows = list; return res; } /// /// 成绩榜-年级列表 /// /// /// public async Task> GetGradeListByOrg(int orgId) { string schoolCode = await _sportsContext.School.Where(x => x.Id == orgId).Select(x => x.SchoolCode).FirstAsync(); var res = await (from s in _sportsContext.SchoolAssocGrade join g in _sportsContext.Grade on s.GradeId equals g.Id where s.SchoolCode == schoolCode select new GradeVo() { GradeId = g.Id, GradeName = g.GradeName }).ToListAsync(); return res; } /// /// 成绩榜-年级列表 /// /// /// public async Task ScoreRanking(ScoreRankingDto dto) { //var jsonOptions = new JsonSerializerOptions //{ // WriteIndented = true, // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping //}; //string dtoJson = System.Text.Json.JsonSerializer.Serialize(dto, jsonOptions); //await File.WriteAllTextAsync( // $"/tmp/xwlog/ScoreRanking_{DateTime.Now:yyyyMMdd_HHmmss}.json", // dtoJson //); var res = new ScoreRankingVo(); var trainList = await _sportsContext.XW_SportsTestData .Where(x => x.OrgId == dto.OrgId && x.ProjectKind == Convert.ToInt32(dto.ProjectKind) && x.ModelType == Convert.ToInt32(dto.ModelType)) .ToListAsync(); var trainIds = trainList.Select(t => t.Id).ToList(); var today = DateTime.UtcNow.Date; var (startDate, endDate) = dto.Times switch { "1" => (today.AddDays(-(int)today.DayOfWeek + 1), today.AddDays(7 - (int)today.DayOfWeek)), "2" => (new DateTime(today.Year, today.Month, 1), new DateTime(today.Year, today.Month, 1).AddMonths(1).AddDays(-1)), "3" => today.Month >= 9 || today.Month <= 2 ? (new DateTime(today.Month >= 9 ? today.Year : today.Year - 1, 9, 1), new DateTime(today.Month >= 9 ? today.Year + 1 : today.Year, 2, DateTime.DaysInMonth(today.Month >= 9 ? today.Year + 1 : today.Year, 2))) : (new DateTime(today.Year, 3, 1), new DateTime(today.Year, 8, DateTime.DaysInMonth(today.Year, 8))), _ => (DateTime.MinValue, DateTime.MaxValue) }; // 基础数据 var rawResults = await _sportsContext.XW_TrainStudents .Where(x => trainIds.Contains(x.XW_SportsTestDataId) && x.GradeId == dto.GradeId) .Select(x => new { x.StudentName, x.ClassName, x.Achievement, x.CreateTime, x.Sex }) .ToListAsync(); // 过滤时间与性别 var filtered = rawResults .Where(x => DateTime.TryParse(x.CreateTime, out var date) && date >= startDate && date <= endDate) .Where(x => dto.Sex == "1" ? x.Sex == 1 : dto.Sex == "2" ? x.Sex == 2 : true) .ToList(); // ⭐ 根据 ModelType == 7 决定排序方式 ⭐ var sorted = (dto.ModelType == 7 ? filtered.OrderBy(x => x.Achievement ?? int.MaxValue) : filtered.OrderByDescending(x => x.Achievement ?? int.MinValue) ).ToList(); if (dto.ProjectKind == 3) { sorted = filtered.OrderBy(x => x.Achievement ?? int.MaxValue).ToList(); } // ⭐ 输出格式处理 ⭐ var studentResults = sorted.Select(x => new RankingVo() { Name = x.StudentName, ClassName = x.ClassName, //Achievement = dto.ModelType == 7 // ? (x.Achievement.HasValue // ? $"{x.Achievement.Value / 1000}" // : "无数据") // : (x.Achievement.HasValue // ? x.Achievement.Value.ToString() // : "无数据") Achievement = x.Achievement.HasValue ? x.Achievement.Value.ToString("0.###") : "无数据" }).ToList(); res.Times = dto.Times; res.Lists = studentResults; return res; } /// /// 获取成绩榜中显示的体育项目 /// /// /// public async Task> GetProjectInfo(int orgId) { var res = await _sportsContext.XW_ProjectMode.Select(x => new ProjectVo() { ProjectKind = x.ProjectKind, ProjectId = x.ProjectKind, ModelType = x.Id, ProjectName = x.Name }).ToListAsync(); return res; } /// /// 设备端人脸录入 /// /// /// public async Task AddWeBankUserFace(AddWeBankUserFaceDto dto) { if (dto == null || dto.UserId <= 0 || dto.PicUrlA == null || dto.PicUrlA.Length == 0) return false; var student = await _sportsContext.Student .FirstOrDefaultAsync(x => x.Id == dto.UserId); if (student == null) return false; var gradeId = await _sportsContext.Class .Where(x => x.Id == student.ClassId) .Select(x => x.GradeId) .FirstOrDefaultAsync(); // 文件名:学号 + 时间戳 + GUID(防重名) var fileName = $"{student.StudentNo}_{DateTime.Now:yyyyMMddHHmmssfff}"; var folder = $"Upload/{student.SchoolCode}/Photo/Student/{gradeId}/{student.ClassId}/"; var url = ALiYunOss.Upload(dto.PicUrlA, folder, fileName); student.Photo = url; _sportsContext.Student.Update(student); return await _sportsContext.SaveChangesAsync() > 0; } /// /// 上传抓拍图片 /// /// /// public async Task UploadDeviceImage(UploadDeviceImageDto dto) { if (dto == null || dto.File == null || dto.File.Length == 0) return false; var folder = NormalizeOssFolder(dto.Path); var fileName = BuildDeviceImageFileName(dto); var imageUrl = ALiYunOss.Upload(dto.File, folder, fileName); var entity = new XW_DeviceImage { Path = dto.Path, ImageUrl = imageUrl, DeviceNum = dto.DeviceNum, Timestamp = dto.Timestamp, StudentId = dto.StudentId, FileName = dto.File.FileName, CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; await _sportsContext.XW_DeviceImage.AddAsync(entity); return await _sportsContext.SaveChangesAsync() > 0; } private static string NormalizeOssFolder(string? path) { if (string.IsNullOrWhiteSpace(path)) return "Upload/DeviceImage/"; var folder = path.Trim().Replace("\\", "/").Trim('/'); return $"{folder}/"; } private static string BuildDeviceImageFileName(UploadDeviceImageDto dto) { var deviceNum = NormalizeFileNamePart(dto.DeviceNum, "device"); var studentId = NormalizeFileNamePart(dto.StudentId, "student"); var timestamp = NormalizeFileNamePart(dto.Timestamp, DateTime.Now.ToString("yyyyMMddHHmmssfff")); return $"{deviceNum}_{studentId}_{timestamp}_{Guid.NewGuid():N}"; } private static string NormalizeFileNamePart(string? value, string defaultValue) { if (string.IsNullOrWhiteSpace(value)) return defaultValue; var invalidChars = Path.GetInvalidFileNameChars(); var chars = value.Trim().Select(x => invalidChars.Contains(x) ? '_' : x).ToArray(); return new string(chars); } /// /// 阳光跑-数据推送 /// /// /// public async Task AddSunshineRunTrain(List dto) { if (dto == null || !dto.Any()) return false; var entities = dto.Select(x => new XW_SunshineRunTrain { OrgId = x.OrgId, GradeName = x.GradeName, GradeId = x.GradeId, ClassName = x.ClassName, ClassId = x.ClassId, StudentId = x.StudentId, StudentName = x.StudentName, Sex = x.Sex, EffectiveDistance = x.EffectiveDistance, InvalidDistance = x.InvalidDistance, Times = x.Times, InvalidTimes = x.InvalidTimes, CreateTime = string.IsNullOrWhiteSpace(x.CreateTime) ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : x.CreateTime, DetailInfo = x.DetailInfo?.Select(d => new XW_SunshineRunTrainDetail { Point = d.Point, Distance = d.Distance, LapSpeed = d.LapSpeed, CameraName = d.CameraName, Trigger = d.Trigger, SnapPath = d.SnapPath, ValidFlag = d.ValidFlag, Remark = d.Remark }).ToList() ?? new List() }).ToList(); await _sportsContext.XW_SunshineRunTrain.AddRangeAsync(entities); return await _sportsContext.SaveChangesAsync() > 0; } /// /// 上传阳光跑经过点位信息 /// /// /// public async Task UploadSunshinePoint(UploadSunshinePointDto dto) { if (dto == null || dto.Image == null || dto.Image.Length == 0) return false; var folder = "Upload/SunshineRun/Point/"; var fileName = BuildSunshinePointFileName(dto); var imageUrl = ALiYunOss.Upload(dto.Image, folder, fileName); var entity = new XW_SunshinePoint { ImageUrl = imageUrl, FileName = dto.Image.FileName, DeviceNum = dto.DeviceNum, Timestamp = dto.Timestamp, StudentNo = dto.StudentNo, PointName = dto.PointName, CameraIp = dto.CameraIp, EffectiveDistance = dto.EffectiveDistance, SumDistance = dto.SumDistance, CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; await _sportsContext.XW_SunshinePoint.AddAsync(entity); return await _sportsContext.SaveChangesAsync() > 0; } private static string BuildSunshinePointFileName(UploadSunshinePointDto dto) { var deviceNum = NormalizeFileNamePart(dto.DeviceNum, "device"); var studentNo = NormalizeFileNamePart(dto.StudentNo, "student"); var pointName = NormalizeFileNamePart(dto.PointName, "point"); var timestamp = NormalizeFileNamePart(dto.Timestamp, DateTime.Now.ToString("yyyyMMddHHmmssfff")); return $"{deviceNum}_{studentNo}_{pointName}_{timestamp}_{Guid.NewGuid():N}"; } } }