YD_SmartSports.Api/VOL.Ai/Services/AiSportsStationService.cs
ltx 9266080356 fix: 兼容壹路运动站有效次数的线上列类型
线上 YL_Train.StandardCounts 为 int,QA 为 float,原实体按 double 读取导致 GetAiSportsStationList 在生产环境抛出 Int32 到 Double 的转换异常。

在列表查询投影中使用 Convert.ToDouble,使 EF 生成 CONVERT(float, [StandardCounts]),同时兼容两种数据库列类型,不修改表结构或其他接口。

验证:WebApi Release 构建通过;EF SQL 探针确认生成 CONVERT(float);QA 和生产库只读查询均成功。线上服务仍需部署本提交并重启后生效。
2026-10-21 13:46:47 +08:00

204 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using VOL.Ai.IServices;
using VOL.Core.EFDbContext;
using VOL.Core.Extensions.AutofacManager;
using VOL.Core.ManageUser;
using VOL.Entity.DomainModels;
using VOL.Entity.DomainModels.YiLu;
using VOL.Entity.Enum;
using VOL.Model;
using VOL.Model.Ai.Request;
using VOL.Model.Ai.Response;
namespace VOL.Ai.Services
{
public class AiSportsStationService : IAiSportsStationService, IDependency
{
private readonly VOLContext _dbContext;
private static readonly Dictionary<int, string> CourseNames = new()
{
{ 3, "引体向上" },
{ 7, "立定跳远" },
{ 8, "跳绳" },
{ 9, "开合跳" },
{ 10, "高抬腿" },
{ 11, "深蹲" },
{ 23, "坐位体前屈" },
{ 24, "单脚跳" },
{ 25, "动态臀桥" },
{ 26, "左右跳" },
{ 27, "打地鼠" },
{ 28, "七上八下" },
{ 29, "消灭蚊子" },
{ 30, "石头剪刀布" },
{ 31, "变脸" },
{ 32, "打钉子" },
{ 1001, "体重系数(BMI)" },
{ 1002, "肺活量" }
};
public AiSportsStationService(VOLContext dbContext)
{
_dbContext = dbContext;
}
public async Task<PageDataDto<AiSportsStationDto>> GetAiSportsStationList(AiSportsStationListRequest paramDto)
{
paramDto.PageIndex = paramDto.PageIndex <= 0 ? 1 : paramDto.PageIndex;
paramDto.PageSize = paramDto.PageSize <= 0 ? 10 : paramDto.PageSize;
var currentUser = UserContext.Current;
var isAdmin = currentUser?.IsSuperAdmin == true
|| string.Equals(currentUser?.UserName, "admin", StringComparison.OrdinalIgnoreCase);
var schoolCode = currentUser?.TenantId;
int? schoolId = null;
if (!isAdmin)
{
schoolId = await _dbContext.Set<S_School>()
.Where(x => x.SchoolCode == schoolCode)
.Select(x => (int?)x.Id)
.FirstOrDefaultAsync();
if (schoolId == null)
{
return new PageDataDto<AiSportsStationDto>
{
Total = 0,
TotalPages = 0,
PageIndex = paramDto.PageIndex,
PageSize = paramDto.PageSize,
Datas = new List<AiSportsStationDto>()
};
}
}
var studentName = paramDto.StudentName?.Trim();
var projectName = paramDto.ProjectName?.Trim();
var sex = GetSexType(paramDto.Sex);
var query =
from train in _dbContext.Set<YL_Train>().AsNoTracking()
join device in _dbContext.Set<YL_Device>().AsNoTracking()
on train.DeviceSerial equals device.SerialNumber
join school in _dbContext.Set<S_School>().AsNoTracking()
on device.OrgId equals (int?)school.Id into schoolGroup
from school in schoolGroup.DefaultIfEmpty()
join student in _dbContext.Set<S_Student>().AsNoTracking()
on new { StudentNo = train.UserSerial, SchoolCode = school.SchoolCode }
equals new { student.StudentNo, student.SchoolCode } into studentGroup
from student in studentGroup.DefaultIfEmpty()
join studentClass in _dbContext.Set<S_Class>().AsNoTracking()
on new { Id = student.ClassId, student.SchoolCode }
equals new { studentClass.Id, studentClass.SchoolCode } into classGroup
from studentClass in classGroup.DefaultIfEmpty()
select new
{
train.Id,
device.OrgId,
DeviceSchoolCode = school.SchoolCode,
train.CourseId,
train.StartTime,
train.EndTime,
train.Score,
// QA stores this as float, while production still uses int.
StandardCounts = Convert.ToDouble(train.StandardCounts),
train.UnStandardCounts,
train.TimeKeeping,
train.Duration,
train.Distance,
train.UserSerial,
StudentName = student == null ? train.UserSerial : student.StudentName,
Sex = student == null ? (SexType?)null : student.Sex,
ClassId = student == null ? 0 : student.ClassId,
ClassName = studentClass == null || studentClass.ClassName == null || studentClass.ClassName == ""
? (student == null ? string.Empty : student.ClassName)
: (studentClass.GradeName ?? string.Empty) + studentClass.ClassName
};
if (!isAdmin)
{
var currentSchoolId = schoolId.GetValueOrDefault();
query = query.Where(x => x.OrgId == currentSchoolId && x.DeviceSchoolCode == schoolCode);
}
if (!string.IsNullOrWhiteSpace(studentName))
query = query.Where(x => x.StudentName.Contains(studentName));
if (sex.HasValue)
query = query.Where(x => x.Sex == sex.Value);
if (paramDto.ClassId > 0)
query = query.Where(x => x.ClassId == paramDto.ClassId);
if (paramDto.StartTime.HasValue)
query = query.Where(x => x.StartTime >= paramDto.StartTime.Value);
if (paramDto.EndTime.HasValue)
query = query.Where(x => x.StartTime <= paramDto.EndTime.Value);
var rows = await query
.OrderByDescending(x => x.StartTime)
.ThenByDescending(x => x.Id)
.ToListAsync();
if (!string.IsNullOrWhiteSpace(projectName))
rows = rows.Where(x => GetCourseName(x.CourseId).Contains(projectName)).ToList();
var total = rows.Count;
var datas = rows
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.Select((x, index) => new AiSportsStationDto
{
Ind = (paramDto.PageIndex - 1) * paramDto.PageSize + index + 1,
StudentName = x.StudentName,
Sex = GetSexName(x.Sex),
ClassName = string.IsNullOrWhiteSpace(x.ClassName) ? "未匹配班级" : x.ClassName,
ProjectName = GetCourseName(x.CourseId),
CourseId = x.CourseId,
Score = x.Score,
StandardCounts = x.StandardCounts,
UnStandardCounts = x.UnStandardCounts,
TimeKeeping = x.TimeKeeping,
Duration = x.Duration,
Distance = x.Distance,
StartTime = x.StartTime,
EndTime = x.EndTime
})
.ToList();
return new PageDataDto<AiSportsStationDto>
{
Total = total,
TotalPages = (int)Math.Ceiling(total / (double)paramDto.PageSize),
PageIndex = paramDto.PageIndex,
PageSize = paramDto.PageSize,
Datas = datas
};
}
private static string GetCourseName(int courseId)
{
return CourseNames.TryGetValue(courseId, out var name) ? name : $"待确认课目(编号{courseId})";
}
private static SexType? GetSexType(string sex)
{
return sex switch
{
"男" => SexType.Male,
"女" => SexType.Female,
_ => null
};
}
private static string GetSexName(SexType? sex)
{
if (sex == null) return string.Empty;
return sex == SexType.Male ? "男" : "女";
}
}
}