2026-10-13 14:07:26 +08:00

1345 lines
52 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 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
{
/// <summary>
/// 服务实现
/// </summary>
public class XinWeiService : IXinWeiService
{
public SmartSportsContext _sportsContext;
private readonly IMapper _mapper;
/// <summary>
/// 构造
/// </summary>
/// <param name="sportsContext"></param>
/// <param name="mapper"></param>
public XinWeiService(SmartSportsContext sportsContext, IMapper mapper)
{
_sportsContext = sportsContext;
_mapper = mapper;
}
/// <summary>
/// 获取设备信息
/// </summary>
/// <param name="deviceSerial"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<DeviceInfoDto> DeviceInfo(string deviceSerial)
{
var deviceInfo = await _sportsContext.XW_Device.FirstOrDefaultAsync(x => x.DeviceSerial == deviceSerial);
var res = _mapper.Map<DeviceInfoDto>(deviceInfo);
return res;
}
/// <summary>
/// 项目模式
/// </summary>
/// <returns></returns>
public async Task<List<ProjectModeDto>> SportsModelTypeList()
{
var res = await _sportsContext.XW_ProjectMode.Select(x => new ProjectModeDto()
{
Id = x.Id,
ProjectKind = x.ProjectKind,
Name = x.Name
}).ToListAsync();
return res;
}
/// <summary>
/// 体育项目
/// </summary>
/// <param name="orgId">学校Id</param>
/// <returns></returns>
public async Task<List<TestingProjectDto>> 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;
}
/// <summary>
/// 获取学生信息
/// </summary>
/// <param name="orgId">学校Id</param>
/// <returns></returns>
public async Task<List<StudentInfoDto>> 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,
GradeName = g.GradeName,
ClassName = $"{g.GradeName}-{c.ClassName}",
CardNo = new List<string>()
}).ToListAsync();
return res;
}
/// <summary>
/// 获取名单列表
/// </summary>
/// <param name="orgId"></param>
/// <param name="userId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<List<SportsRosterDto>> 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<SportsRosterStudentDto> sportList1 = new List<SportsRosterStudentDto>();
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;
}
/// <summary>
/// 获取人脸信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<FaceListOfDevicePageDto> 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
};
}
/// <summary>
/// 获取训练评分规则
/// </summary>
/// <param name="orgId">学校Id</param>
/// <returns></returns>
public async Task<List<ScoreRuleDto>> 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<ScoreRuleDto> res = new List<ScoreRuleDto>();
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<N_HealthStandards>())).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<ScoreRulePlusDto>
{
//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<ScoreScope>
{
//new ScoreScope()
//{
// Gender = 1,
// MaxScore = maleMaxScore,
// MaxCount = maleMax
//},
//new ScoreScope()
//{
// Gender = 2,
// MaxScore = femaleMaxScore,
// MaxCount = femaleMax
//}
};
res.Add(scoreRuleDto);
}
}
return res;
}
/// <summary>
/// 新增训练
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TrainDto> 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<XW_SportsTestData>(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<TrainDto>(entity);
Console.WriteLine("新增成功");
return res;
}
catch (Exception e)
{
Console.WriteLine(e.Message + "/n" + e.InnerException);
}
return null;
}
private async Task<List<N_SportsTestValue>> AddSportsTestValueEntity(AddTrainDto dto, List<N_HealthStandards> healthStandards, List<XW_TestingProject> xwProject)
{
var sportsTestValueEntinys = new List<N_SportsTestValue>();
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;
}
/// <summary>
/// 年级排名
/// </summary>
/// <param name="studentId"></param>
/// <param name="trainId"></param>
/// <returns></returns>
public async Task<GradeRankingVo> 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;
}
/// <summary>
/// 全部历史记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TrainHistoryResponseDto> 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<object>(x, dto.OrderByColumn))
: query.OrderByDescending(x => EF.Property<object>(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;
}
/// <summary>
/// 历史记录详细
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TrainAnalusisVo> 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;
}
/// <summary>
/// 个人历史记录-历史成绩
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TrainHistoryPersonVo> 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"
};
}
/// <summary>
/// 个人历史记录-记录列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TableDataPersonAnalysisVo> 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<object>(x, dto.OrderByColumn))
: query.OrderByDescending(x => EF.Property<object>(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;
}
/// <summary>
/// 成绩榜-年级列表
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<List<GradeVo>> 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;
}
/// <summary>
/// 成绩榜-年级列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<ScoreRankingVo> 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;
}
/// <summary>
/// 获取成绩榜中显示的体育项目
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<List<ProjectVo>> 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;
}
/// <summary>
/// 设备端人脸录入
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<bool> 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;
}
/// <summary>
/// 上传抓拍图片
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<bool> 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);
}
/// <summary>
/// 阳光跑-数据推送
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<TrainDto> AddSunshineRunTrain(List<AddSunshineRunTrainDto> dto)
{
if (dto == null || !dto.Any())
return null;
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<XW_SunshineRunTrainDetail>()
}).ToList();
await _sportsContext.XW_SunshineRunTrain.AddRangeAsync(entities);
await _sportsContext.SaveChangesAsync();
var entity = entities[0];
var res = _mapper.Map<TrainDto>(entity);
Console.WriteLine("新增成功");
return res;
}
/// <summary>
/// 上传阳光跑经过点位信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<bool> 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}";
}
}
}