From c032d937a3849e4b8fac32217f135cf073f92ee0 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Wed, 1 Jul 2026 15:13:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dto/DeviceSync/DeviceSyncGradeOrderDto.cs | 32 +++++ YD_YiLu/Controllers/DeviceSyncController.cs | 53 ++++--- YD_YiLu/Services/Impl/DeviceSyncService.cs | 131 ++++++++++++++++++ .../Services/Interface/IDeviceSyncService.cs | 4 + 4 files changed, 203 insertions(+), 17 deletions(-) diff --git a/YD_YiLu.Commons/Dto/DeviceSync/DeviceSyncGradeOrderDto.cs b/YD_YiLu.Commons/Dto/DeviceSync/DeviceSyncGradeOrderDto.cs index 42ecb36..2d5cf1b 100644 --- a/YD_YiLu.Commons/Dto/DeviceSync/DeviceSyncGradeOrderDto.cs +++ b/YD_YiLu.Commons/Dto/DeviceSync/DeviceSyncGradeOrderDto.cs @@ -39,4 +39,36 @@ namespace YD_YiLu.Commons.Dto.DeviceSync public int CourseId { get; set; } public string CourseName { get; set; } } + + public class DeviceSyncOrganizationGradeOrderItemDto + { + public string OrganizationName { get; set; } + public string SumScore { get; set; } + public string AvgScore { get; set; } + public int SumCount { get; set; } + public int CourseId { get; set; } + public string CourseName { get; set; } + } + + public class DeviceSyncBestScoreRequestDto + { + public int DeviceType { get; set; } + public string DeviceSerial { get; set; } + public int CourseId { get; set; } + public int Gender { get; set; } + public string UserSerial { get; set; } + public int Range { get; set; } + } + + public class DeviceSyncBestScoreDto + { + public string UserSerial { get; set; } + public string Nickname { get; set; } + public int OrganizationId { get; set; } + public string OrganizationName { get; set; } + public int Gender { get; set; } + public string Score { get; set; } + public int CourseId { get; set; } + public string CourseName { get; set; } + } } diff --git a/YD_YiLu/Controllers/DeviceSyncController.cs b/YD_YiLu/Controllers/DeviceSyncController.cs index 57704d3..b38076e 100644 --- a/YD_YiLu/Controllers/DeviceSyncController.cs +++ b/YD_YiLu/Controllers/DeviceSyncController.cs @@ -7,7 +7,7 @@ using YD_YiLu.Commons.Dto.DeviceSync; namespace YD_YiLu.Api.Controllers { /// - /// 壹动站/壹体屏/活力课间屏 设备同步 HTTP 接口(小站登录与心跳见 WebSocket:/ws/camera/device) + /// 设备同步 HTTP 接口。 /// [AllowAnonymous] public class DeviceSyncController : ControllerBase @@ -22,7 +22,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 人员拉取(全量,与 user_page 共用服务逻辑;活力课间屏) + /// 人员全量拉取。 /// [HttpPost] [Route("/device/sync/user")] @@ -32,7 +32,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 人员分页下发(壹动站/壹体屏;弃用的 user_trace 请改用本接口) + /// 人员分页下发。 /// [HttpPost] [Route("/device/sync/user_page")] @@ -48,14 +48,14 @@ namespace YD_YiLu.Api.Controllers } /// - /// 弃用地址别名:device/sync/user_trace → 与 user_page 相同 + /// user_trace 别名。 /// [HttpPost] [Route("/device/sync/user_trace")] public Task UserTrace([FromBody] DeviceSyncUserPageRequestDto dto) => UserPullPage(dto); /// - /// 获取人员详情(壹动站协议) + /// 获取人员详情。 /// [HttpGet] [Route("/device/sync/user/detail")] @@ -65,7 +65,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 组织信息下发 + /// 组织信息下发。 /// [HttpPost] [Route("/device/sync/organization")] @@ -74,9 +74,8 @@ namespace YD_YiLu.Api.Controllers return await _deviceSyncService.OrganizationAsync(dto); } - /// - /// 人员上次成绩(按人员序列号批量) + /// 人员上次成绩。 /// [HttpPost] [Route("/device/sync/grade/last_grade")] @@ -88,7 +87,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 成绩批量上报 + /// 成绩批量上报。 /// [HttpPost] [Route("/device/sync/train_push")] @@ -106,7 +105,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 获取训练详情 + /// 获取训练详情。 /// [HttpGet] [Route("/device/sync/train/details")] @@ -117,7 +116,7 @@ namespace YD_YiLu.Api.Controllers } /// - /// 个人成绩排行榜 + /// 个人成绩排行榜。 /// [HttpPost] [Route("/device/sync/grade/order")] @@ -128,7 +127,29 @@ namespace YD_YiLu.Api.Controllers } /// - /// 查询运动记录 + /// 组织(班级)成绩排行榜。 + /// + [HttpPost] + [Route("/device/sync/organization_grade/order")] + public async Task OrganizationGradeOrder([FromBody] DeviceSyncGradeOrderRequestDto dto) + { + var res = await _deviceSyncService.OrganizationGradeOrderAsync(dto); + return res; + } + + /// + /// 查询挑战最好成绩。 + /// + [HttpPost] + [Route("/device/sync/grade/best_score")] + public async Task BestScore([FromBody] DeviceSyncBestScoreRequestDto dto) + { + var res = await _deviceSyncService.BestScoreAsync(dto); + return res; + } + + /// + /// 查询运动记录。 /// [HttpPost] [Route("/applet/train/record_app")] @@ -139,10 +160,10 @@ namespace YD_YiLu.Api.Controllers } /// - /// 查询我的运动 + /// 查询我的运动。 /// [HttpPost] - [Route("/device/sync/train/my_record/ytp")] + [Route("/device/sync/grade/my_record")] public async Task MyRecord([FromBody] DeviceSyncMyRecordRequestDto dto) { var res = await _deviceSyncService.MyRecordAsync(dto); @@ -150,10 +171,8 @@ namespace YD_YiLu.Api.Controllers } /// - /// 视频回放上传 + /// 视频回放上传。 /// - /// - /// [HttpPost("device/sync/video/upload")] public async Task UploadVideo([FromForm] DeviceVideoUploadRequestDto dto) { diff --git a/YD_YiLu/Services/Impl/DeviceSyncService.cs b/YD_YiLu/Services/Impl/DeviceSyncService.cs index 04b76a8..eaf2c8e 100644 --- a/YD_YiLu/Services/Impl/DeviceSyncService.cs +++ b/YD_YiLu/Services/Impl/DeviceSyncService.cs @@ -564,6 +564,137 @@ namespace YD_YiLu.Api.Services.Impl }).ToList(); } + public async Task> OrganizationGradeOrderAsync(DeviceSyncGradeOrderRequestDto dto) + { + var orgId = await GetOrgIdByDeviceAsync(dto.DeviceSerial); + if (!orgId.HasValue) return new List(); + + string schoolCode = await _context.School + .Where(x => x.Id == orgId) + .Select(x => x.SchoolCode) + .FirstAsync(); + + var (start, end) = GetDateRange(dto.DateType, dto.QueryStartTime, dto.QueryEndTime); + + var query = from t in _context.YL_Train + join s in _context.Student on t.UserSerial equals s.StudentNo + where s.SchoolCode == schoolCode + && t.Score.HasValue + && t.Score.Value > 0 + select new { t, s }; + + if (dto.CourseId.HasValue) + query = query.Where(x => x.t.CourseId == dto.CourseId.Value); + + if (dto.Gender > 0) + query = query.Where(x => x.s.Sex == dto.Gender.Value); + + if (dto.OrganizeId != null && dto.OrganizeId.Count > 0 && dto.OrganizeId[0] > 0) + query = query.Where(x => dto.OrganizeId.Contains(x.s.ClassId ?? 0)); + + if (start.HasValue) + query = query.Where(x => x.t.StartTime >= start.Value); + + if (end.HasValue) + query = query.Where(x => x.t.StartTime < end.Value.AddDays(1)); + + var page = dto.Page < 1 ? 1 : dto.Page; + var size = dto.Size < 1 ? 10 : dto.Size; + + var grouped = await query + .GroupBy(x => new { x.s.ClassId, x.s.ClassName, x.t.CourseId }) + .Select(g => new + { + g.Key.ClassName, + g.Key.CourseId, + SumScore = g.Sum(x => x.t.Score ?? 0), + AvgScore = g.Average(x => x.t.Score ?? 0), + SumCount = g.Count() + }) + .OrderByDescending(x => x.SumScore) + .Skip((page - 1) * size) + .Take(size) + .ToListAsync(); + + return grouped.Select(x => new DeviceSyncOrganizationGradeOrderItemDto + { + OrganizationName = x.ClassName ?? "", + SumScore = Math.Round(x.SumScore).ToString("0"), + AvgScore = x.AvgScore.ToString("0.##"), + SumCount = x.SumCount, + CourseId = x.CourseId, + CourseName = Util.GetEnumDescription(x.CourseId) + }).ToList(); + } + + public async Task BestScoreAsync(DeviceSyncBestScoreRequestDto dto) + { + var orgId = await GetOrgIdByDeviceAsync(dto.DeviceSerial); + if (!orgId.HasValue) return null; + + string schoolCode = await _context.School + .Where(x => x.Id == orgId) + .Select(x => x.SchoolCode) + .FirstAsync(); + + var currentStudent = await _context.Student + .FirstOrDefaultAsync(x => x.SchoolCode == schoolCode && x.StudentNo == dto.UserSerial); + + if (currentStudent == null) + return null; + + var currentClass = currentStudent.ClassId.HasValue + ? await _context.Class.FirstOrDefaultAsync(x => x.Id == currentStudent.ClassId.Value) + : null; + + var query = from t in _context.YL_Train + join s in _context.Student on t.UserSerial equals s.StudentNo + where s.SchoolCode == schoolCode + && t.CourseId == dto.CourseId + && s.Sex == dto.Gender + && t.Score.HasValue + && t.Score.Value > 0 + select new { t, s }; + + if (dto.Range == 1) + { + query = query.Where(x => x.s.ClassId == currentStudent.ClassId); + } + else if (dto.Range == 2 && currentClass != null) + { + var classIds = await _context.Class + .Where(x => x.SchoolCode == schoolCode && x.GradeId == currentClass.GradeId) + .Select(x => x.Id) + .ToListAsync(); + + query = query.Where(x => classIds.Contains(x.s.ClassId ?? 0)); + } + + var best = await query + .OrderByDescending(x => x.t.Score) + .ThenBy(x => x.t.StartTime) + .FirstOrDefaultAsync(); + + if (best == null) + return null; + + var bestClass = best.s.ClassId.HasValue + ? await _context.Class.FirstOrDefaultAsync(x => x.Id == best.s.ClassId.Value) + : null; + + return new DeviceSyncBestScoreDto + { + UserSerial = best.s.StudentNo, + Nickname = best.s.StudentName, + OrganizationId = best.s.ClassId ?? 0, + OrganizationName = bestClass?.ClassName ?? best.s.ClassName ?? "", + Gender = best.s.Sex, + Score = best.t.Score?.ToString("0.##") ?? "0", + CourseId = best.t.CourseId, + CourseName = Util.GetEnumDescription(best.t.CourseId) + }; + } + public async Task> LastGradeAsync(DeviceSyncLastGradeRequestDto dto) { if (dto?.UserSerials == null || dto.UserSerials.Count == 0) diff --git a/YD_YiLu/Services/Interface/IDeviceSyncService.cs b/YD_YiLu/Services/Interface/IDeviceSyncService.cs index 52daf19..471a8ea 100644 --- a/YD_YiLu/Services/Interface/IDeviceSyncService.cs +++ b/YD_YiLu/Services/Interface/IDeviceSyncService.cs @@ -38,6 +38,10 @@ namespace YD_YiLu.Api.Services.Interface /// Task> GradeOrderAsync(DeviceSyncGradeOrderRequestDto dto); + Task> OrganizationGradeOrderAsync(DeviceSyncGradeOrderRequestDto dto); + + Task BestScoreAsync(DeviceSyncBestScoreRequestDto dto); + /// /// 人员上次成绩(按人员序列号批量查询) ///