Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3411db6188 | |||
| 278652ee2f | |||
| 78ac4ae259 | |||
|
|
f3a1abe8bf | ||
|
|
d2773b9686 | ||
|
|
50ab8a8ab1 | ||
| 07c5933b06 | |||
| 3559a3ae7f | |||
| a8926683df | |||
| f30386d596 | |||
| a980826100 | |||
| c65fd9d3c2 | |||
| 4627cf9bb5 | |||
| 9986c4db76 | |||
| 02b7b0ba9a | |||
| b2ee87d2d2 | |||
| 81e1533243 | |||
|
|
ce3991d082 | ||
| d3bd78ad01 | |||
| 9a4b68310a | |||
| 05f24cd395 |
@ -33,6 +33,7 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public int UserId { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public int OrganizationId { get; set; }
|
||||
public string GradeName { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
public int Gender { get; set; }
|
||||
public string Score { get; set; }
|
||||
@ -65,6 +66,7 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public string UserSerial { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public int OrganizationId { get; set; }
|
||||
public string GradeName { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
public int Gender { get; set; }
|
||||
public string Score { get; set; }
|
||||
|
||||
@ -71,4 +71,40 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public string AvgGrade { get; set; }
|
||||
public string WorstGrade { get; set; }
|
||||
}
|
||||
|
||||
public class DeviceSyncYtpMyRecordRequestDto
|
||||
{
|
||||
public int DeviceType { get; set; }
|
||||
public string DeviceSerial { get; set; }
|
||||
public string UserSerial { get; set; }
|
||||
public int DateType { get; set; }
|
||||
public string QueryStartTime { get; set; }
|
||||
public string QueryEndTime { get; set; }
|
||||
}
|
||||
|
||||
public class DeviceSyncYtpMyRecordResponseDto
|
||||
{
|
||||
public UserSyncDto UserSync { get; set; }
|
||||
public int TrainCourseSum { get; set; }
|
||||
public int TrainDays { get; set; }
|
||||
public string TrainHours { get; set; } = "0.00";
|
||||
public int TrainCounts { get; set; }
|
||||
public List<YtpGradeListItem> GradeList { get; set; } = new List<YtpGradeListItem>();
|
||||
public List<YtpGraphTrainListItem> GraphTrainList { get; set; } = new List<YtpGraphTrainListItem>();
|
||||
}
|
||||
|
||||
public class YtpGradeListItem
|
||||
{
|
||||
public string Time { get; set; }
|
||||
public string Duration { get; set; }
|
||||
public string Count { get; set; }
|
||||
}
|
||||
|
||||
public class YtpGraphTrainListItem
|
||||
{
|
||||
public int CourseId { get; set; }
|
||||
public string CourseName { get; set; }
|
||||
public string Time { get; set; }
|
||||
public string Count { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public int Gender { get; set; }
|
||||
public string FacePictureUrl { get; set; }
|
||||
public string Score { get; set; }
|
||||
public string ShowResult { get; set; }
|
||||
public string StartTime { get; set; }
|
||||
public string EndTime { get; set; }
|
||||
public int CourseId { get; set; }
|
||||
|
||||
@ -53,11 +53,12 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
/// </summary>
|
||||
public class DeviceSyncUserPageResponseDto
|
||||
{
|
||||
public long SyncId { get; set; }
|
||||
public int Current { get; set; }
|
||||
public int Pages { get; set; }
|
||||
public int Total { get; set; }
|
||||
public long? SyncId { get; set; }
|
||||
public List<DeviceSyncUserDto> UserList { get; set; } = new List<DeviceSyncUserDto>();
|
||||
public int Total { get; set; }
|
||||
public int Size { get; set; }
|
||||
public int Pages { get; set; }
|
||||
public int Current { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -68,16 +69,23 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public int UserId { get; set; }
|
||||
public int UserType { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public int OrganizationId { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
public int GradeId { get; set; }
|
||||
public string GradeName { get; set; }
|
||||
public string FacePictureUrl { get; set; }
|
||||
public string Identity { get; set; }
|
||||
public string Gender { get; set; }
|
||||
public string Birthday { get; set; }
|
||||
public string UserSerial { get; set; }
|
||||
public List<TrainLimitValueDto> TrainLimitValues { get; set; } = new List<TrainLimitValueDto>();
|
||||
public string Height { get; set; }
|
||||
public string Weight { get; set; }
|
||||
public List<TrainLimitValueDto> TrainLimitValues { get; set; }
|
||||
public int Classification { get; set; }
|
||||
public int IsActive { get; set; }
|
||||
public int IsDelete { get; set; }
|
||||
}
|
||||
|
||||
public class TrainLimitValueDto
|
||||
|
||||
24
YD_YiLu.Commons/Dto/DeviceSync/ErrorUserCallbackDto.cs
Normal file
24
YD_YiLu.Commons/Dto/DeviceSync/ErrorUserCallbackDto.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
{
|
||||
/// <summary>
|
||||
/// 信息下发失败回调请求
|
||||
/// </summary>
|
||||
public class ErrorUserCallbackRequestDto
|
||||
{
|
||||
public long SyncId { get; set; }
|
||||
public int DeviceType { get; set; }
|
||||
public string DeviceSerial { get; set; }
|
||||
public List<ErrorUserCallbackItemDto> ErrorUsers { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 失败人员信息
|
||||
/// </summary>
|
||||
public class ErrorUserCallbackItemDto
|
||||
{
|
||||
public string UserSerial { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
32
YD_YiLu.Commons/Dto/Running/RunningDtos.cs
Normal file
32
YD_YiLu.Commons/Dto/Running/RunningDtos.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YD_YiLu.Commons.Dto.Running
|
||||
{
|
||||
/// <summary>
|
||||
/// 阳光跑跑道人员状态上报。
|
||||
/// </summary>
|
||||
public class RunningStageRequestDto
|
||||
{
|
||||
public string DeviceSerial { get; set; }
|
||||
public List<RunUserRecognizeDto> RunUserList { get; set; } = new List<RunUserRecognizeDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 阳光跑跑道识别人员。
|
||||
/// </summary>
|
||||
public class RunUserRecognizeDto
|
||||
{
|
||||
public string UserSerial { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public string Base64face { get; set; }
|
||||
public int Status { get; set; }
|
||||
public int TrackIndex { get; set; }
|
||||
public int Result { get; set; }
|
||||
public int Medal { get; set; }
|
||||
public int GroupId { get; set; }
|
||||
public int Uid { get; set; }
|
||||
public int QuizId { get; set; }
|
||||
public string DeviceSerial { get; set; }
|
||||
public int ErrorStatus { get; set; }
|
||||
}
|
||||
}
|
||||
@ -153,6 +153,7 @@ namespace YD_YiLu.Api.Controllers
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/device/sync/grade/record")]
|
||||
[Route("/applet/train/record_app")]
|
||||
public async Task<object> RecordApp([FromBody] DeviceSyncRecordAppRequestDto dto)
|
||||
{
|
||||
var res = await _deviceSyncService.RecordAppAsync(dto);
|
||||
@ -170,6 +171,19 @@ namespace YD_YiLu.Api.Controllers
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询我的运动(壹体屏)
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("/device/sync/train/my_record/ytp")]
|
||||
public async Task<object> YtpMyRecord([FromBody] DeviceSyncYtpMyRecordRequestDto dto)
|
||||
{
|
||||
var res = await _deviceSyncService.YtpMyRecordAsync(dto);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视频回放上传。
|
||||
/// </summary>
|
||||
|
||||
189
YD_YiLu/Controllers/RunningController.cs
Normal file
189
YD_YiLu/Controllers/RunningController.cs
Normal file
@ -0,0 +1,189 @@
|
||||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YD_YiLu.Api.WebSockets;
|
||||
using YD_YiLu.Commons.Dto.Running;
|
||||
|
||||
namespace YD_YiLu.Api.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 阳光跑接口。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class RunningController : ControllerBase
|
||||
{
|
||||
private readonly ICameraDeviceSocketRegistry _registry;
|
||||
private readonly ILogger<RunningController> _logger;
|
||||
|
||||
public RunningController(ICameraDeviceSocketRegistry registry, ILogger<RunningController> logger)
|
||||
{
|
||||
_registry = registry;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跑道人员变更状态。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/running/result/stage")]
|
||||
public object ResultStage([FromBody] RunningStageRequestDto dto)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Running stage changed. DeviceSerial: {DeviceSerial}, UserCount: {UserCount}",
|
||||
dto?.DeviceSerial,
|
||||
dto?.RunUserList?.Count ?? 0);
|
||||
|
||||
return new { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平台指定用户下发人脸通知(旧)。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/station:user:push")]
|
||||
public async Task<object> StationUserPush([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("station:user:push", body, deviceSerial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平台下发人脸通知。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/station:remind")]
|
||||
public async Task<object> StationRemind([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("station:remind", body, deviceSerial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跑步小组重启。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/running:group:restart")]
|
||||
public async Task<object> RunningGroupRestart([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("running:group:restart", body, deviceSerial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跑步小组推送。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/running:group:push")]
|
||||
public async Task<object> RunningGroupPush([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("running:group:push", body, deviceSerial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跑步小组自动分组。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/running:group:auto")]
|
||||
public async Task<object> RunningGroupAuto([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("running:group:auto", body, deviceSerial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备指令。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/device:instruct")]
|
||||
public async Task<object> DeviceInstruct([FromBody] JObject body, [FromQuery] string deviceSerial = "")
|
||||
{
|
||||
return await SendSocketMessageAsync("device:instruct", body, deviceSerial);
|
||||
}
|
||||
|
||||
private async Task<object> SendSocketMessageAsync(string request, JObject body, string deviceSerial)
|
||||
{
|
||||
body ??= new JObject();
|
||||
|
||||
var serial = FirstNotEmpty(deviceSerial, FindString(body, "serialNumber"), FindString(body, "deviceSerial"));
|
||||
if (string.IsNullOrWhiteSpace(serial))
|
||||
{
|
||||
return new
|
||||
{
|
||||
success = false,
|
||||
message = "deviceSerial不能为空"
|
||||
};
|
||||
}
|
||||
|
||||
if (!_registry.TryGet(serial, out var socket) || socket.State != WebSocketState.Open)
|
||||
{
|
||||
return new
|
||||
{
|
||||
success = false,
|
||||
serialNumber = serial,
|
||||
message = "设备不在线"
|
||||
};
|
||||
}
|
||||
|
||||
var message = NormalizeSocketMessage(request, body);
|
||||
await CameraDeviceWebSocketHandler.SendAsync(
|
||||
socket,
|
||||
message.ToString(Formatting.None),
|
||||
HttpContext.RequestAborted);
|
||||
|
||||
_logger.LogInformation("Running socket message sent. Request: {Request}, SerialNumber: {SerialNumber}", request, serial);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
serialNumber = serial
|
||||
};
|
||||
}
|
||||
|
||||
private static JObject NormalizeSocketMessage(string request, JObject body)
|
||||
{
|
||||
var message = (JObject)body.DeepClone();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message["msgId"]?.ToString()))
|
||||
message["msgId"] = Guid.NewGuid().ToString("N");
|
||||
|
||||
message["request"] = request;
|
||||
message["code"] ??= 0;
|
||||
message["msg"] ??= "";
|
||||
message["data"] ??= new JObject();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private static string FirstNotEmpty(params string[] values)
|
||||
{
|
||||
return values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)) ?? "";
|
||||
}
|
||||
|
||||
private static string FindString(JToken token, string name)
|
||||
{
|
||||
if (token == null)
|
||||
return "";
|
||||
|
||||
if (token.Type == JTokenType.Object)
|
||||
{
|
||||
var obj = (JObject)token;
|
||||
foreach (var property in obj.Properties())
|
||||
{
|
||||
if (string.Equals(property.Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var value = property.Value?.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in token.Children())
|
||||
{
|
||||
var value = FindString(child, name);
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
return value;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,21 @@ namespace YD_YiLu.Api.Controllers
|
||||
return await _deviceSyncService.AppVerifyAsync(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 信息下发失败回调
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("/setting/callback/error_user")]
|
||||
public object ErrorUserCallback([FromBody] ErrorUserCallbackRequestDto dto)
|
||||
{
|
||||
return new
|
||||
{
|
||||
code = 0,
|
||||
msg = "OK",
|
||||
data = 1
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增app版本
|
||||
/// </summary>
|
||||
|
||||
@ -19,7 +19,7 @@ namespace YD_YiLu.Api
|
||||
{
|
||||
serverOptions.Limits.MaxRequestBodySize = 10485760;
|
||||
});
|
||||
webBuilder.UseKestrel().UseUrls("http://*:9997");
|
||||
webBuilder.UseKestrel().UseUrls("http://*:8001");
|
||||
webBuilder.UseIIS();
|
||||
webBuilder.UseStartup<Startup>();
|
||||
}).UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
|
||||
@ -56,16 +56,23 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
{
|
||||
UserId = s.Id,
|
||||
UserType = 2,
|
||||
Username = s.StudentNo,
|
||||
Username = null,
|
||||
Password = null,
|
||||
Nickname = s.StudentName,
|
||||
GradeName = "",
|
||||
OrganizationId = (int)orgId,
|
||||
OrganizationId = s.ClassId ?? 0,
|
||||
OrganizationName = s.ClassName ?? "",
|
||||
FacePictureUrl = s.Photo ?? "",
|
||||
Identity = "学生",
|
||||
Gender = s.Sex == 1 ? "男" : "女",
|
||||
Birthday = s.Birthday.HasValue ? s.Birthday.Value.ToString("yyyy-MM-dd") : "",
|
||||
UserSerial = s.StudentNo,
|
||||
Height = null,
|
||||
Weight = null,
|
||||
TrainLimitValues = null,
|
||||
Classification = 2,
|
||||
IsActive = 1,
|
||||
IsDelete = 0
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
@ -74,18 +81,20 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
var c = await _context.Class.FindAsync(u.OrganizationId);
|
||||
if (c != null)
|
||||
{
|
||||
u.OrganizationName = c.ClassName ?? "";
|
||||
u.OrganizationName = FormatOrganizationName(c, u.OrganizationName);
|
||||
u.GradeId = c.GradeId;
|
||||
u.GradeName = c.GradeName ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
return new DeviceSyncUserPageResponseDto
|
||||
{
|
||||
SyncId = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
Current = fullSync ? 1 : pageIndex,
|
||||
Pages = fullSync ? 1 : (total == 0 ? 0 : (int)Math.Ceiling(total / (double)Math.Max(pageSize, 1))),
|
||||
SyncId = null,
|
||||
UserList = list,
|
||||
Total = total,
|
||||
UserList = list
|
||||
Size = pageSize,
|
||||
Pages = fullSync ? 1 : (total == 0 ? 0 : (int)Math.Ceiling(total / (double)Math.Max(pageSize, 1))),
|
||||
Current = fullSync ? 1 : pageIndex
|
||||
};
|
||||
}
|
||||
|
||||
@ -140,7 +149,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
OrganizationId = c.Id,
|
||||
ParentId = g.Id,
|
||||
ParentName = g.GradeName,
|
||||
OrganizationName = c.ClassName ?? "",
|
||||
OrganizationName = FormatOrganizationName(c, c.ClassName ?? ""),
|
||||
OrganizationType = "班级",
|
||||
ChildrenIds = new List<int>()
|
||||
}).ToList(),
|
||||
@ -243,11 +252,11 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
// 👉 4. 批量入库(关键优化点)
|
||||
if (newEntities.Any())
|
||||
{
|
||||
var sportsTestValueEntities = await AddSportsTestValueEntity(orgId.Value, newEntities);
|
||||
//var sportsTestValueEntities = await AddSportsTestValueEntity(orgId.Value, newEntities);
|
||||
|
||||
_context.YL_Train.AddRange(newEntities);
|
||||
if (sportsTestValueEntities.Any())
|
||||
_context.SportsTestValue.AddRange(sportsTestValueEntities);
|
||||
//if (sportsTestValueEntities.Any())
|
||||
// _context.SportsTestValue.AddRange(sportsTestValueEntities);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
@ -472,6 +481,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
|
||||
// 5. 课程名称(枚举描述)
|
||||
string courseName = Util.GetEnumDescription<CourseType>(train.CourseId);
|
||||
var trainClass = await _context.Class.FindAsync(student.ClassId);
|
||||
|
||||
// 6. 返回 DTO
|
||||
return new DeviceSyncTrainDetailsDto
|
||||
@ -483,7 +493,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
FacePictureUrl = student.Photo ?? "",
|
||||
Identity = "学生",
|
||||
OrganizationId = (int)student.ClassId,
|
||||
OrganizationName = student.ClassName,
|
||||
OrganizationName = FormatOrganizationName(trainClass, student.ClassName),
|
||||
Gender = student.Sex == 1 ? "男" : "女",
|
||||
Score = train.Score?.ToString("0") ?? "0",
|
||||
UnStandardCounts = train.UnStandardCounts,
|
||||
@ -521,7 +531,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
select new { t, s };
|
||||
|
||||
// 5️⃣ 条件过滤
|
||||
if (dto.CourseId.HasValue)
|
||||
if (dto.CourseId > 0)
|
||||
query = query.Where(x => x.t.CourseId == dto.CourseId.Value);
|
||||
|
||||
if (dto.Gender > 0)
|
||||
@ -550,12 +560,31 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
.ToListAsync(); // ✅ 确保在此处完成
|
||||
|
||||
// 7️⃣ 返回结果
|
||||
var classIds = grouped
|
||||
.Select(x => x.Key.ClassId ?? 0)
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var classMap = await _context.Class
|
||||
.Where(x => classIds.Contains(x.Id))
|
||||
.ToDictionaryAsync(x => x.Id, x => new
|
||||
{
|
||||
x.GradeName,
|
||||
x.ClassName
|
||||
});
|
||||
|
||||
return grouped.Select(x => new DeviceSyncGradeOrderItemDto
|
||||
{
|
||||
UserId = x.Key.Id,
|
||||
Nickname = x.Key.StudentName,
|
||||
OrganizationId = x.Key.ClassId ?? 0,
|
||||
OrganizationName = x.Key.ClassName ?? "",
|
||||
GradeName = classMap.TryGetValue(x.Key.ClassId ?? 0, out var gradeClassInfo)
|
||||
? gradeClassInfo.GradeName ?? ""
|
||||
: "",
|
||||
OrganizationName = classMap.TryGetValue(x.Key.ClassId ?? 0, out var classInfo)
|
||||
? classInfo.ClassName ?? ""
|
||||
: x.Key.ClassName ?? "",
|
||||
Gender = x.Key.Sex,
|
||||
Score = Math.Round(x.Score).ToString("0"),
|
||||
CourseId = x.Key.CourseId,
|
||||
@ -605,6 +634,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
.GroupBy(x => new { x.s.ClassId, x.s.ClassName, x.t.CourseId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.ClassId,
|
||||
g.Key.ClassName,
|
||||
g.Key.CourseId,
|
||||
SumScore = g.Sum(x => x.t.Score ?? 0),
|
||||
@ -616,9 +646,21 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
.Take(size)
|
||||
.ToListAsync();
|
||||
|
||||
var organizationClassIds = grouped
|
||||
.Select(x => x.ClassId ?? 0)
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var organizationClassMap = await _context.Class
|
||||
.Where(x => organizationClassIds.Contains(x.Id))
|
||||
.ToDictionaryAsync(x => x.Id);
|
||||
|
||||
return grouped.Select(x => new DeviceSyncOrganizationGradeOrderItemDto
|
||||
{
|
||||
OrganizationName = x.ClassName ?? "",
|
||||
OrganizationName = organizationClassMap.TryGetValue(x.ClassId ?? 0, out var classInfo)
|
||||
? FormatOrganizationName(classInfo, x.ClassName ?? "")
|
||||
: x.ClassName ?? "",
|
||||
SumScore = Math.Round(x.SumScore).ToString("0"),
|
||||
AvgScore = x.AvgScore.ToString("0.##"),
|
||||
SumCount = x.SumCount,
|
||||
@ -687,7 +729,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
UserSerial = best.s.StudentNo,
|
||||
Nickname = best.s.StudentName,
|
||||
OrganizationId = best.s.ClassId ?? 0,
|
||||
OrganizationName = bestClass?.ClassName ?? best.s.ClassName ?? "",
|
||||
OrganizationName = FormatOrganizationName(bestClass, best.s.ClassName ?? ""),
|
||||
Gender = best.s.Sex,
|
||||
Score = best.t.Score?.ToString("0.##") ?? "0",
|
||||
CourseId = best.t.CourseId,
|
||||
@ -711,6 +753,14 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
.ToListAsync();
|
||||
|
||||
var studentSerials = students.Select(s => s.StudentNo).ToList();
|
||||
var lastGradeClassIds = students
|
||||
.Select(x => x.ClassId ?? 0)
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var lastGradeClassMap = await _context.Class
|
||||
.Where(x => lastGradeClassIds.Contains(x.Id))
|
||||
.ToDictionaryAsync(x => x.Id);
|
||||
|
||||
var trains = await _context.YL_Train
|
||||
.Where(t => studentSerials.Contains(t.UserSerial))
|
||||
@ -743,13 +793,16 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
Username = st?.StudentNo ?? serial,
|
||||
Nickname = st?.StudentName ?? "",
|
||||
OrganizationId = st?.ClassId ?? 0,
|
||||
OrganizationName = st?.ClassName ?? "",
|
||||
OrganizationName = st != null && lastGradeClassMap.TryGetValue(st.ClassId ?? 0, out var lastGradeClass)
|
||||
? FormatOrganizationName(lastGradeClass, st.ClassName ?? "")
|
||||
: st?.ClassName ?? "",
|
||||
GradeName = "", // 没有就空
|
||||
FacePictureUrl = st?.Photo ?? "",
|
||||
Identity = "", // 可选
|
||||
Gender = st == null ? "" : (st.Sex == 1 ? "男" : "女"),
|
||||
Birthday = st?.Birthday?.ToString("yyyy-MM-dd") ?? "",
|
||||
UserSerial = serial
|
||||
UserSerial = serial,
|
||||
TrainLimitValues = new List<TrainLimitValueDto>()
|
||||
};
|
||||
|
||||
// 👉 构造 trainLimitValues
|
||||
@ -803,7 +856,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
select new { t, s };
|
||||
|
||||
// 👉 4. 条件过滤
|
||||
if (dto.CourseId.HasValue)
|
||||
if (dto.CourseId > 0)
|
||||
query = query.Where(x => x.t.CourseId == dto.CourseId.Value);
|
||||
|
||||
if (dto.Gender > 0)
|
||||
@ -842,6 +895,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
FacePictureUrl = x.s.Photo,
|
||||
|
||||
Score = x.t.Score.HasValue ? x.t.Score.Value.ToString("0.##") : "0",
|
||||
ShowResult = x.t.Score.HasValue ? x.t.Score.Value.ToString("0.##") : "0",
|
||||
|
||||
StartTime = x.t.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
EndTime = x.t.EndTime.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
@ -850,6 +904,22 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var recordClassIds = list
|
||||
.Select(x => x.OrganizationId)
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var recordClassMap = await _context.Class
|
||||
.Where(x => recordClassIds.Contains(x.Id))
|
||||
.ToDictionaryAsync(x => x.Id);
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (recordClassMap.TryGetValue(item.OrganizationId, out var recordClass))
|
||||
item.OrganizationName = FormatOrganizationName(recordClass, item.OrganizationName);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -878,7 +948,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
UserType = 2,
|
||||
Nickname = student.StudentName,
|
||||
OrganizationId = student.ClassId ?? 0,
|
||||
OrganizationName = c?.ClassName ?? "",
|
||||
OrganizationName = FormatOrganizationName(c, ""),
|
||||
FacePictureUrl = student.Photo,
|
||||
Identity = "学生",
|
||||
Gender = student.Sex == 1 ? "男" : "女",
|
||||
@ -891,6 +961,9 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
// 👉 2. 时间范围
|
||||
var (start, end) = GetDateRange(dto.DateType, dto.QueryStartTime, dto.QueryEndTime);
|
||||
|
||||
result.UserSync.Identity = "学生";
|
||||
result.UserSync.Gender = student.Sex == 1 ? "男" : "女";
|
||||
|
||||
var query = _context.YL_Train
|
||||
.Where(x => x.UserSerial == dto.UserSerial
|
||||
&& x.DeviceSerial == dto.DeviceSerial
|
||||
@ -909,7 +982,15 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
else if (dto.CourseType > 0)
|
||||
query = query.Where(x => x.CourseId == dto.CourseType);
|
||||
|
||||
var trains = await query.ToListAsync();
|
||||
var trains = await query
|
||||
.Select(x => new
|
||||
{
|
||||
x.CourseId,
|
||||
x.StartTime,
|
||||
x.Duration,
|
||||
x.Score
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
if (!trains.Any())
|
||||
return result;
|
||||
@ -963,7 +1044,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
UserType = 2,
|
||||
Nickname = student.StudentName,
|
||||
OrganizationId = student.ClassId ?? 0,
|
||||
OrganizationName = c?.ClassName ?? "",
|
||||
OrganizationName = FormatOrganizationName(c, ""),
|
||||
FacePictureUrl = student.Photo,
|
||||
Identity = "学生",
|
||||
Gender = student.Sex == 1 ? "男" : "女",
|
||||
@ -990,6 +1071,147 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<DeviceSyncYtpMyRecordResponseDto> YtpMyRecordAsync(DeviceSyncYtpMyRecordRequestDto dto)
|
||||
{
|
||||
var result = new DeviceSyncYtpMyRecordResponseDto();
|
||||
|
||||
var orgId = await GetOrgIdByDeviceAsync(dto.DeviceSerial);
|
||||
if (!orgId.HasValue) return result;
|
||||
|
||||
var schoolCode = await _context.School
|
||||
.Where(x => x.Id == orgId)
|
||||
.Select(x => x.SchoolCode)
|
||||
.FirstAsync();
|
||||
|
||||
var student = await _context.Student
|
||||
.FirstOrDefaultAsync(x => x.SchoolCode == schoolCode && x.StudentNo == dto.UserSerial);
|
||||
|
||||
if (student == null) return result;
|
||||
|
||||
var studentClass = await _context.Class.FindAsync(student.ClassId);
|
||||
result.UserSync = new UserSyncDto
|
||||
{
|
||||
UserId = student.Id,
|
||||
UserType = 2,
|
||||
Nickname = student.StudentName,
|
||||
OrganizationId = student.ClassId ?? 0,
|
||||
OrganizationName = FormatOrganizationName(studentClass, student.ClassName ?? ""),
|
||||
FacePictureUrl = student.Photo ?? "",
|
||||
Identity = "学生",
|
||||
Gender = student.Sex == 1 ? "男" : "女",
|
||||
Birthday = student.Birthday?.ToString("yyyy-MM-dd") ?? "",
|
||||
UserSerial = student.StudentNo,
|
||||
Height = "0",
|
||||
Weight = "0"
|
||||
};
|
||||
result.UserSync.Identity = "学生";
|
||||
result.UserSync.Gender = student.Sex == 1 ? "男" : "女";
|
||||
|
||||
var (start, end) = GetDateRange(dto.DateType, dto.QueryStartTime, dto.QueryEndTime);
|
||||
|
||||
var query = _context.YL_Train
|
||||
.AsNoTracking()
|
||||
.Where(x => x.UserSerial == dto.UserSerial
|
||||
&& x.DeviceSerial == dto.DeviceSerial
|
||||
&& x.DeviceType == dto.DeviceType);
|
||||
|
||||
if (start.HasValue)
|
||||
query = query.Where(x => x.StartTime >= start.Value);
|
||||
|
||||
if (end.HasValue)
|
||||
query = query.Where(x => x.StartTime < end.Value.AddDays(1));
|
||||
|
||||
var trains = await query
|
||||
.Select(x => new
|
||||
{
|
||||
x.CourseId,
|
||||
x.StartTime,
|
||||
x.Duration
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
if (!trains.Any())
|
||||
{
|
||||
if (IsYtpSummaryDateType(dto.DateType))
|
||||
{
|
||||
result.GradeList = new List<YtpGradeListItem>
|
||||
{
|
||||
new YtpGradeListItem
|
||||
{
|
||||
Time = GetYtpSummaryTime(dto.DateType),
|
||||
Duration = SecondsToIntegerHours(0),
|
||||
Count = "0"
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
var today = DateTime.Now.Date;
|
||||
result.GradeList = Enumerable.Range(0, 7)
|
||||
.Select(i => today.AddDays(i - 6))
|
||||
.Select(day => new YtpGradeListItem
|
||||
{
|
||||
Time = day.ToString("MM-dd"),
|
||||
Duration = SecondsToIntegerHours(0),
|
||||
Count = "0"
|
||||
})
|
||||
.ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
var totalSeconds = trains.Sum(x => x.Duration);
|
||||
result.TrainCourseSum = trains.Select(x => x.CourseId).Distinct().Count();
|
||||
result.TrainDays = trains.Select(x => x.StartTime.Date).Distinct().Count();
|
||||
result.TrainHours = (totalSeconds / 3600.0).ToString("0.00");
|
||||
result.TrainCounts = trains.Count;
|
||||
|
||||
if (IsYtpSummaryDateType(dto.DateType))
|
||||
{
|
||||
result.GradeList = new List<YtpGradeListItem>
|
||||
{
|
||||
new YtpGradeListItem
|
||||
{
|
||||
Time = GetYtpSummaryTime(dto.DateType),
|
||||
Duration = SecondsToIntegerHours(totalSeconds),
|
||||
Count = trains.Count.ToString()
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var graphEnd = end?.Date ?? DateTime.Now.Date;
|
||||
var graphStart = graphEnd.AddDays(-6);
|
||||
result.GradeList = Enumerable.Range(0, 7)
|
||||
.Select(i => graphStart.AddDays(i))
|
||||
.Select(day =>
|
||||
{
|
||||
var dayTrains = trains.Where(x => x.StartTime.Date == day).ToList();
|
||||
var daySeconds = dayTrains.Sum(x => x.Duration);
|
||||
return new YtpGradeListItem
|
||||
{
|
||||
Time = day.ToString("MM-dd"),
|
||||
Duration = SecondsToIntegerHours(daySeconds),
|
||||
Count = dayTrains.Count.ToString()
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
result.GraphTrainList = trains
|
||||
.GroupBy(x => x.CourseId)
|
||||
.Select(g => new YtpGraphTrainListItem
|
||||
{
|
||||
CourseId = g.Key,
|
||||
CourseName = Util.GetEnumDescription<CourseType>(g.Key),
|
||||
Time = SecondsToWholeHours(g.Sum(x => x.Duration)),
|
||||
Count = g.Count().ToString()
|
||||
})
|
||||
.OrderByDescending(x => double.TryParse(x.Time, out var hours) ? hours : 0)
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<TeacherLoginResponseDto> TeacherLoginAsync(TeacherLoginRequestDto dto)
|
||||
{
|
||||
var pwdHash = MD5Helper.MD5Encrypt32(dto.Password);
|
||||
@ -1190,7 +1412,7 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
var c = await _context.Class.FindAsync(dto.OrganizationId);
|
||||
if (c != null)
|
||||
{
|
||||
dto.OrganizationName = c.ClassName ?? "";
|
||||
dto.OrganizationName = FormatOrganizationName(c, dto.OrganizationName);
|
||||
dto.GradeName = c.GradeName ?? "";
|
||||
}
|
||||
return dto;
|
||||
@ -1228,20 +1450,98 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
return "AUTO-" + MD5Helper.MD5Encrypt32(value);
|
||||
}
|
||||
|
||||
private static (DateTime? start, DateTime? end) GetDateRange(int dateType, string queryStart, string queryEnd)
|
||||
private static string SecondsToHours(int seconds)
|
||||
{
|
||||
return (seconds / 3600.0).ToString("0.####");
|
||||
}
|
||||
|
||||
private static string SecondsToWholeHours(int seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
return "0";
|
||||
|
||||
return Math.Ceiling(seconds / 3600.0).ToString("0");
|
||||
}
|
||||
|
||||
private static string SecondsToIntegerHours(int seconds)
|
||||
{
|
||||
return ((int)(seconds / 3600.0)).ToString();
|
||||
}
|
||||
|
||||
private static bool IsYtpSummaryDateType(int dateType)
|
||||
{
|
||||
return dateType >= 0 && dateType <= 9;
|
||||
}
|
||||
|
||||
private static string GetYtpSummaryTime(int dateType)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
return dateType switch
|
||||
{
|
||||
1 => (now.Date, now.Date),
|
||||
2 => (now.AddDays(-(int)now.DayOfWeek + 1).Date, now.Date),
|
||||
3 => (new DateTime(now.Year, now.Month, 1), now.Date),
|
||||
4 => (now.Month >= 9 ? new DateTime(now.Year, 9, 1) : new DateTime(now.Year - 1, 9, 1), now.Date),
|
||||
5 => (DateTime.TryParse(queryStart, out var s) ? s : (DateTime?)null, DateTime.TryParse(queryEnd, out var e) ? e : (DateTime?)null),
|
||||
_ => (null, null)
|
||||
0 => "\u5168\u90e8",
|
||||
1 => "\u4eca\u65e5",
|
||||
2 => "\u672c\u5468",
|
||||
3 => "\u672c\u6708",
|
||||
4 => "\u672c\u5b66\u671f",
|
||||
5 => "\u6307\u5b9a\u65f6\u95f4",
|
||||
6 => "\u4e0a\u5468",
|
||||
7 => "\u4e0a\u6708",
|
||||
8 => "\u6700\u8fd1\u4e09\u4e2a\u6708",
|
||||
9 => "\u672c\u5e74\u5ea6",
|
||||
_ => ""
|
||||
};
|
||||
}
|
||||
|
||||
private static DateTime GetWeekStart(DateTime today)
|
||||
{
|
||||
return today.AddDays(-(((int)today.DayOfWeek + 6) % 7));
|
||||
}
|
||||
|
||||
private static (DateTime start, DateTime end) GetCurrentSemesterRange(DateTime today)
|
||||
{
|
||||
if (today.Month >= 2 && today.Month <= 8)
|
||||
return (new DateTime(today.Year, 2, 1), new DateTime(today.Year, 8, 31));
|
||||
|
||||
if (today.Month >= 9)
|
||||
return (new DateTime(today.Year, 9, 1), new DateTime(today.Year + 1, 1, 31));
|
||||
|
||||
return (new DateTime(today.Year - 1, 9, 1), new DateTime(today.Year, 1, 31));
|
||||
}
|
||||
|
||||
private static (DateTime? start, DateTime? end) GetDateRange(int dateType, string queryStart, string queryEnd)
|
||||
{
|
||||
var today = DateTime.Now.Date;
|
||||
var weekStart = GetWeekStart(today);
|
||||
var monthStart = new DateTime(today.Year, today.Month, 1);
|
||||
(DateTime? start, DateTime? end) result = dateType switch
|
||||
{
|
||||
0 => ((DateTime?)null, (DateTime?)null),
|
||||
1 => (today, today),
|
||||
2 => (weekStart, weekStart.AddDays(6)),
|
||||
3 => (monthStart, monthStart.AddMonths(1).AddDays(-1)),
|
||||
4 => GetCurrentSemesterRange(today),
|
||||
5 => (DateTime.TryParse(queryStart, out var s) ? s : (DateTime?)null, DateTime.TryParse(queryEnd, out var e) ? e : (DateTime?)null),
|
||||
6 => (weekStart.AddDays(-7), weekStart.AddDays(-1)),
|
||||
7 => (monthStart.AddMonths(-1), monthStart.AddDays(-1)),
|
||||
8 => (today.AddMonths(-3), today),
|
||||
9 => (new DateTime(today.Year, 1, 1), new DateTime(today.Year, 12, 31)),
|
||||
_ => ((DateTime?)null, (DateTime?)null)
|
||||
};
|
||||
|
||||
if (result.start.HasValue && result.end.HasValue && result.start.Value > result.end.Value)
|
||||
result = (result.end, result.start);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string FormatOrganizationName(S_Class classInfo, string fallback = "")
|
||||
{
|
||||
if (classInfo == null)
|
||||
return fallback ?? "";
|
||||
|
||||
var name = $"{classInfo.GradeName ?? ""}{classInfo.ClassName ?? ""}";
|
||||
return string.IsNullOrWhiteSpace(name) ? fallback ?? "" : name;
|
||||
}
|
||||
|
||||
private int CompareVersion(string v1, string v2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2))
|
||||
|
||||
@ -57,6 +57,8 @@ namespace YD_YiLu.Api.Services.Interface
|
||||
/// </summary>
|
||||
Task<DeviceSyncMyRecordResponseDto> MyRecordAsync(DeviceSyncMyRecordRequestDto dto);
|
||||
|
||||
Task<DeviceSyncYtpMyRecordResponseDto> YtpMyRecordAsync(DeviceSyncYtpMyRecordRequestDto dto);
|
||||
|
||||
/// <summary>
|
||||
/// 老师登录
|
||||
/// </summary>
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
"DbConnectionString": " Data Source=rm-uf64b8oxdt19pfyxfao.sqlserver.rds.aliyuncs.com;Database=SmartSportsServerDB_QA;User ID=qa;Password=Admin@123;Connect Timeout=500;",
|
||||
"SmartSportsString": " Data Source=rm-uf64b8oxdt19pfyxfao.sqlserver.rds.aliyuncs.com;Database=SmartSportsServerDB_QA;User ID=qa;Password=Admin@123;Connect Timeout=500;",
|
||||
"CorsUrls": "http://localhost:8081,http://localhost:8080,http://localhost:7080,http://localhost:9980,http://127.0.0.1:9994,http://localhost:9994",
|
||||
"CorsUrls": "http://localhost:8081,http://localhost:8080,http://localhost:7080,http://localhost:9980,http://127.0.0.1:9997,http://localhost:9997",
|
||||
|
||||
"WeChat": {
|
||||
"Appid": "xxx",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user