Compare commits

...

21 Commits
main ... dev

Author SHA1 Message Date
3411db6188 size 2026-09-15 17:26:12 +08:00
278652ee2f Merge branch 'dev' of http://8.153.108.90:3000/YD/YD_YiLu.Api into dev 2026-08-21 09:29:22 +08:00
78ac4ae259 ww 2026-08-21 09:29:20 +08:00
tanglong
f3a1abe8bf 回调 2026-08-20 15:14:34 +08:00
tanglong
d2773b9686 d 2026-08-18 15:58:55 +08:00
tanglong
50ab8a8ab1 kuayu 2026-08-18 15:57:39 +08:00
07c5933b06 public string Score { get; set; } 2026-07-17 17:19:35 +08:00
3559a3ae7f 年级 2026-07-17 16:24:06 +08:00
a8926683df . 2026-07-17 15:45:26 +08:00
f30386d596 time 2026-07-17 14:44:18 +08:00
a980826100 嗯嗯 2026-07-17 14:16:08 +08:00
c65fd9d3c2 time 2026-07-17 14:07:58 +08:00
4627cf9bb5 时间 2026-07-17 13:51:36 +08:00
9986c4db76 小数 2026-07-17 13:30:14 +08:00
02b7b0ba9a 时长 2026-07-17 11:17:27 +08:00
b2ee87d2d2 年级班级 2026-07-15 15:38:03 +08:00
81e1533243 年级班级 2026-07-15 15:32:07 +08:00
tanglong
ce3991d082 tiaojian 2026-07-15 15:22:11 +08:00
d3bd78ad01 zhushi 2026-07-15 15:04:23 +08:00
9a4b68310a 新增接口 2026-07-15 14:46:25 +08:00
05f24cd395 tijiao 2026-07-15 14:35:01 +08:00
13 changed files with 661 additions and 38 deletions

View File

@ -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; }

View File

@ -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; }
}
}

View File

@ -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; }

View File

@ -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

View 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; }
}
}

View 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; }
}
}

View File

@ -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>

View 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 "";
}
}
}

View File

@ -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>

View File

@ -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());

View File

@ -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))

View File

@ -57,6 +57,8 @@ namespace YD_YiLu.Api.Services.Interface
/// </summary>
Task<DeviceSyncMyRecordResponseDto> MyRecordAsync(DeviceSyncMyRecordRequestDto dto);
Task<DeviceSyncYtpMyRecordResponseDto> YtpMyRecordAsync(DeviceSyncYtpMyRecordRequestDto dto);
/// <summary>
/// 老师登录
/// </summary>

View File

@ -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",