2025-06-06 16:00:39 +08:00
|
|
|
|
using AutoMapper;
|
|
|
|
|
using Castle.DynamicProxy.Generators;
|
|
|
|
|
using Google.Protobuf.Collections;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using VOL.Business.IRepositories;
|
|
|
|
|
using VOL.Business.IServices.Norm;
|
|
|
|
|
using VOL.Business.IServices.School;
|
|
|
|
|
using VOL.Core.CacheManager;
|
|
|
|
|
using VOL.Core.Extensions;
|
|
|
|
|
using VOL.Core.Extensions.AutofacManager;
|
|
|
|
|
using VOL.Core.ManageUser;
|
|
|
|
|
using VOL.Core.Utilities;
|
|
|
|
|
using VOL.Entity.DomainModels;
|
|
|
|
|
using VOL.Entity.DomainModels.Business.People;
|
|
|
|
|
using VOL.Entity.Enum;
|
|
|
|
|
using VOL.Model;
|
2025-06-06 16:55:14 +08:00
|
|
|
|
using VOL.Model.Ai;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
using VOL.Model.IOT.Request;
|
|
|
|
|
using VOL.Model.School.Request;
|
|
|
|
|
using VOL.Model.School.Response;
|
|
|
|
|
using VOL.System.IRepositories;
|
|
|
|
|
using VOL.System.Repositories;
|
2025-06-06 16:55:14 +08:00
|
|
|
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
|
|
|
|
namespace VOL.Business.Services.School
|
|
|
|
|
{
|
|
|
|
|
public class S_StudentService : IS_StudentService, IDependency
|
|
|
|
|
{
|
|
|
|
|
#region 初始化
|
|
|
|
|
private readonly IMapper _mapper;
|
|
|
|
|
private readonly ICacheService _cacheService;
|
|
|
|
|
private readonly ICacheQueryService _cacheQueryService;
|
|
|
|
|
private readonly IotDataSyncService _iotDataSyncService;
|
|
|
|
|
private readonly IS_StudentRepository _studentRepository;
|
|
|
|
|
private readonly IS_GradeRepository _gradeRepository;
|
|
|
|
|
private readonly IS_ClassRepository _classRepository;
|
|
|
|
|
private readonly IN_SportsTestResultRepository _sportsTestResultRepository;
|
|
|
|
|
private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[ActivatorUtilitiesConstructor]
|
|
|
|
|
public S_StudentService(IMapper mapper,
|
|
|
|
|
ICacheService cacheService,
|
|
|
|
|
ICacheQueryService cacheQueryService,
|
|
|
|
|
IS_StudentRepository studentRepository,
|
|
|
|
|
IS_GradeRepository gradeRepository,
|
|
|
|
|
IS_ClassRepository classRepository,
|
|
|
|
|
IN_SportsTestResultRepository sportsTestResultRepository,
|
|
|
|
|
IN_SportsTestCategoryRepository sportsTestCategoryRepository,
|
|
|
|
|
IotDataSyncService iotDataSyncService)
|
|
|
|
|
{
|
|
|
|
|
_mapper = mapper;
|
|
|
|
|
_cacheService = cacheService;
|
|
|
|
|
_cacheQueryService = cacheQueryService;
|
|
|
|
|
_studentRepository = studentRepository;
|
|
|
|
|
_gradeRepository = gradeRepository;
|
|
|
|
|
_classRepository = classRepository;
|
|
|
|
|
_sportsTestResultRepository = sportsTestResultRepository;
|
|
|
|
|
_sportsTestCategoryRepository = sportsTestCategoryRepository;
|
|
|
|
|
_iotDataSyncService = iotDataSyncService;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public async Task<PageDataDto<StudentPageListModel>> GetStudentPageList(StudentPageListParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var res = new PageDataDto<StudentPageListModel>();
|
|
|
|
|
|
|
|
|
|
var classIds = new List<int>();
|
|
|
|
|
var isTeacher = (UserContext.Current.RoleId == 3);
|
|
|
|
|
|
|
|
|
|
if (isTeacher)
|
|
|
|
|
{
|
|
|
|
|
var teacher = await _studentRepository.DbContext.Set<S_Teacher>().FirstOrDefaultAsync(x => x.SchoolCode == UserContext.Current.TenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
|
|
|
|
|
|
|
|
|
|
var teacherClassList = await (
|
|
|
|
|
from at in _studentRepository.DbContext.Set<S_ClassAssocTeacher>()
|
|
|
|
|
join c in _studentRepository.DbContext.Set<S_Class>() on at.ClassId equals c.Id
|
|
|
|
|
where at.TeacherId == teacher.Id
|
|
|
|
|
select c).ToListAsync();
|
|
|
|
|
|
|
|
|
|
classIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
|
2025-06-06 16:00:39 +08:00
|
|
|
|
var query = from s in _studentRepository.DbContext.Set<S_Student>()
|
2025-07-14 14:20:13 +08:00
|
|
|
|
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id into sc
|
|
|
|
|
from c in sc.DefaultIfEmpty()
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
|
|
|
|
where s.SchoolCode.Equals(UserContext.Current.TenantId) && s.StudentStatus == StudentStatus.Normal && (!isTeacher || classIds.Contains(c.Id))
|
|
|
|
|
|
|
|
|
|
select new StudentPageListModel()
|
|
|
|
|
{
|
|
|
|
|
Id = s.Id,
|
|
|
|
|
Age = s.Age,
|
|
|
|
|
ClassId = s.ClassId,
|
|
|
|
|
ClassName = c.ClassName,
|
|
|
|
|
GradeName = c.GradeName,
|
|
|
|
|
HeartRateFrontNo = s.HeartRateFrontNo,
|
|
|
|
|
HeartRateId = s.HeartRateId,
|
|
|
|
|
Photo = s.Photo,
|
|
|
|
|
RopeSkipQRCode = s.RopeSkipQRCode,
|
|
|
|
|
SchoolRollNo = s.SchoolRollNo,
|
|
|
|
|
Sex = s.Sex,
|
|
|
|
|
StudentName = s.StudentName,
|
|
|
|
|
StudentNo = s.StudentNo,
|
2025-06-06 16:55:14 +08:00
|
|
|
|
OrderNo = s.OrderNo,
|
2025-06-06 16:00:39 +08:00
|
|
|
|
StudentStatus = s.StudentStatus
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentName))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentName.Contains(paramDto.StudentName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentNo.Contains(paramDto.StudentNo));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.Sex > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.Sex == paramDto.Sex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.ClassId > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.ClassId == paramDto.ClassId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.StudentStatus > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentStatus == paramDto.StudentStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.Total = await query.CountAsync();
|
|
|
|
|
|
|
|
|
|
var list = await query
|
2025-06-06 16:55:14 +08:00
|
|
|
|
.OrderBy(x => x.Id)
|
2025-06-06 16:00:39 +08:00
|
|
|
|
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
|
|
|
|
|
.Take(paramDto.PageSize)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
res.Datas = list;
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<List<StudentPageListModel>> GetStudentList(StudentExportParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var classIds = new List<int>();
|
|
|
|
|
var isTeacher = (UserContext.Current.RoleId == 3);
|
|
|
|
|
|
|
|
|
|
if (isTeacher)
|
|
|
|
|
{
|
|
|
|
|
var teacher = await _studentRepository.DbContext.Set<S_Teacher>().FirstOrDefaultAsync(x => x.SchoolCode == UserContext.Current.TenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
|
|
|
|
|
|
|
|
|
|
var teacherClassList = await (
|
|
|
|
|
from at in _studentRepository.DbContext.Set<S_ClassAssocTeacher>()
|
|
|
|
|
join c in _studentRepository.DbContext.Set<S_Class>() on at.ClassId equals c.Id
|
|
|
|
|
where at.TeacherId == teacher.Id
|
|
|
|
|
select c).ToListAsync();
|
|
|
|
|
|
|
|
|
|
classIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var query = from s in _studentRepository.DbContext.Set<S_Student>()
|
|
|
|
|
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
|
|
|
|
|
|
|
|
|
|
where c.SchoolCode.Equals(UserContext.Current.TenantId) && s.StudentStatus == StudentStatus.Normal && s.StudentStatus == StudentStatus.Normal && (!isTeacher || classIds.Contains(c.Id))
|
|
|
|
|
|
|
|
|
|
select new StudentPageListModel()
|
|
|
|
|
{
|
|
|
|
|
Id = s.Id,
|
|
|
|
|
Age = s.Age,
|
|
|
|
|
ClassId = s.ClassId,
|
|
|
|
|
ClassName = c.ClassName,
|
|
|
|
|
GradeName = c.GradeName,
|
|
|
|
|
HeartRateFrontNo = s.HeartRateFrontNo,
|
|
|
|
|
HeartRateId = s.HeartRateId,
|
|
|
|
|
Photo = s.Photo,
|
|
|
|
|
RopeSkipQRCode = s.RopeSkipQRCode,
|
|
|
|
|
SchoolRollNo = s.SchoolRollNo,
|
|
|
|
|
Sex = s.Sex,
|
|
|
|
|
StudentName = s.StudentName,
|
|
|
|
|
StudentNo = s.StudentNo,
|
|
|
|
|
StudentStatus = s.StudentStatus
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentName))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentName.Contains(paramDto.StudentName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentNo.Contains(paramDto.StudentNo));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.Sex > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.Sex == paramDto.Sex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.ClassId > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.ClassId == paramDto.ClassId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.StudentStatus > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentStatus == paramDto.StudentStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var list = await query.OrderBy(x => x.Id).ToListAsync();
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取场馆学员
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
public async Task<List<StudentPageListModel>> GetStudentListBySportsHall(StudentExportParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var query = from s in _studentRepository.DbContext.Set<S_Student>()
|
|
|
|
|
|
|
|
|
|
where s.SchoolCode.Equals(UserContext.Current.TenantId) && s.StudentStatus == StudentStatus.Normal && s.StudentStatus == StudentStatus.Normal
|
|
|
|
|
|
|
|
|
|
select new StudentPageListModel()
|
|
|
|
|
{
|
|
|
|
|
Id = s.Id,
|
|
|
|
|
Age = s.Age,
|
|
|
|
|
ClassId = s.ClassId,
|
|
|
|
|
ClassName = s.ClassName,
|
|
|
|
|
GradeName = "运动馆",
|
|
|
|
|
HeartRateFrontNo = s.HeartRateFrontNo,
|
|
|
|
|
HeartRateId = s.HeartRateId,
|
|
|
|
|
Photo = s.Photo,
|
|
|
|
|
RopeSkipQRCode = s.RopeSkipQRCode,
|
|
|
|
|
SchoolRollNo = s.SchoolRollNo,
|
|
|
|
|
Sex = s.Sex,
|
|
|
|
|
StudentName = s.StudentName,
|
|
|
|
|
StudentNo = s.StudentNo,
|
|
|
|
|
StudentStatus = s.StudentStatus,
|
|
|
|
|
OrderNo = s.OrderNo,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentName))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentName.Contains(paramDto.StudentName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentNo.Contains(paramDto.StudentNo));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.Sex > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.Sex == paramDto.Sex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.ClassId > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.ClassId == paramDto.ClassId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.StudentStatus > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.Where(x => x.StudentStatus == paramDto.StudentStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var list = await query.OrderBy(x => x.OrderNo).ToListAsync();
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:00:39 +08:00
|
|
|
|
public async Task AddStudent(AddStudentParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var stuExists = await _studentRepository.ExistsAsync(x => x.StudentNo == paramDto.StudentNo);
|
|
|
|
|
if (stuExists)
|
|
|
|
|
throw new Exception($"学号{paramDto.StudentNo}的学生已存在!");
|
|
|
|
|
|
|
|
|
|
var studentEntity = _mapper.Map<S_Student>(paramDto);
|
|
|
|
|
studentEntity.StudentStatus = StudentStatus.Normal;
|
|
|
|
|
studentEntity.SchoolCode = UserContext.Current.TenantId;
|
|
|
|
|
studentEntity.Creator = UserContext.Current.UserId;
|
|
|
|
|
studentEntity.CreateDate = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
await _studentRepository.AddAsync(studentEntity);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
//调用回调函数,同步数据到IOT
|
|
|
|
|
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
//{
|
|
|
|
|
// EventType = EventType.Add.GetDisplayName(),
|
|
|
|
|
// DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
// Json = JsonConvert.SerializeObject(new List<S_Student>() { studentEntity })
|
|
|
|
|
//}));
|
|
|
|
|
|
|
|
|
|
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
{
|
|
|
|
|
EventType = EventType.Add.GetDisplayName(),
|
|
|
|
|
DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
Json = JsonConvert.SerializeObject(new List<S_Student>() { studentEntity })
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
public async Task ModifyStudent(AddStudentParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
if (paramDto == null || string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("学生学号必填");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var student = await _studentRepository.FindAsyncFirst(x => x.Id == paramDto.Id);
|
|
|
|
|
if (student == null)
|
|
|
|
|
throw new ArgumentNullException("未找到要更新的数据");
|
|
|
|
|
|
|
|
|
|
var stuExists = await _studentRepository.ExistsAsync(x => x.StudentNo == paramDto.StudentNo && x.Id != student.Id);
|
|
|
|
|
if (stuExists)
|
|
|
|
|
throw new Exception($"学号{paramDto.StudentNo}的学生已存在!");
|
|
|
|
|
|
|
|
|
|
student.StudentName = paramDto.StudentName; // 学生名称
|
|
|
|
|
student.ClassId = paramDto.ClassId; // 班级
|
|
|
|
|
student.Sex = paramDto.Sex; // 性别
|
|
|
|
|
student.Age = paramDto.Age; // 年龄
|
|
|
|
|
student.Birthday = paramDto.Birthday; // 出生日期
|
|
|
|
|
student.IDCard = paramDto.IDCard; // 身份证
|
|
|
|
|
student.Photo = paramDto.Photo; // 学生照片
|
|
|
|
|
student.HeartRateFrontNo = paramDto.HeartRateFrontNo; // 心率设备正面编号
|
|
|
|
|
student.HeartRateId = paramDto.HeartRateId; // 心率设备ID
|
|
|
|
|
student.RunChipNo = paramDto.RunChipNo; // 中长跑芯片编号
|
|
|
|
|
student.RopeSkipNo = paramDto.RopeSkipQRCode; // 跳绳编号
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
student.OrderNo = 1;
|
|
|
|
|
|
2025-06-06 16:00:39 +08:00
|
|
|
|
student.Creator = UserContext.Current.UserId;
|
|
|
|
|
student.Modifier = UserContext.Current.UserId;
|
|
|
|
|
student.ModifyDate = DateTime.Now;
|
|
|
|
|
student.StudentStatus = StudentStatus.Normal;
|
|
|
|
|
_studentRepository.Update(student);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
//调用回调函数,同步数据到IOT
|
|
|
|
|
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
//{
|
|
|
|
|
// EventType = EventType.Update.GetDisplayName(),
|
|
|
|
|
// DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
// Json = JsonConvert.SerializeObject(student)
|
|
|
|
|
//}));
|
|
|
|
|
|
|
|
|
|
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
{
|
|
|
|
|
EventType = EventType.Update.GetDisplayName(),
|
|
|
|
|
DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
Json = JsonConvert.SerializeObject(student)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task UpdateStudentStatus(UpdateStudentStatusParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var student = await _studentRepository.FindAsyncFirst(x => x.StudentNo == paramDto.StudentNo);
|
|
|
|
|
if (student == null)
|
|
|
|
|
throw new ArgumentNullException("未找到要更新的数据");
|
|
|
|
|
student.StudentStatus = paramDto.Status;
|
|
|
|
|
|
|
|
|
|
_studentRepository.Update(student);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
//调用回调函数,同步数据到IOT
|
|
|
|
|
_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
{
|
|
|
|
|
EventType = EventType.Update.GetDisplayName(),
|
|
|
|
|
DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
Json = JsonConvert.SerializeObject(student)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task UpdateStudentPwd(UpdateStudentPwdParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var student = await _studentRepository.FindAsyncFirst(x => x.StudentNo == paramDto.StudentNo);
|
|
|
|
|
if (student == null)
|
|
|
|
|
throw new ArgumentNullException("未找到要更新的数据");
|
|
|
|
|
student.AppletPwd = paramDto.StudentNo;
|
|
|
|
|
|
|
|
|
|
_studentRepository.Update(student);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
//调用回调函数,同步数据到IOT
|
|
|
|
|
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
//{
|
|
|
|
|
// EventType = EventType.Update.GetDisplayName(),
|
|
|
|
|
// DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
// Json = JsonConvert.SerializeObject(student)
|
|
|
|
|
//}));
|
|
|
|
|
|
|
|
|
|
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
{
|
|
|
|
|
EventType = EventType.Update.GetDisplayName(),
|
|
|
|
|
DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
Json = JsonConvert.SerializeObject(student)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task ChangeClasses(string studentNo, int classId)
|
|
|
|
|
{
|
|
|
|
|
var student = await _studentRepository.FindAsyncFirst(x => x.StudentNo == studentNo);
|
|
|
|
|
|
|
|
|
|
if (student == null)
|
|
|
|
|
throw new ArgumentNullException("未找到更新的数据");
|
|
|
|
|
|
|
|
|
|
var classModel = await _classRepository.FindAsyncFirst(x => x.Id == student.ClassId);
|
|
|
|
|
|
|
|
|
|
var sportsTestResults = await _sportsTestResultRepository.FindAsIQueryable(x => x.ClassId == student.ClassId && x.StudentNo == studentNo).ToListAsync();
|
|
|
|
|
|
|
|
|
|
using (var transaction = _studentRepository.DbContext.Database.BeginTransaction())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
student.ClassId = classId;
|
|
|
|
|
student.ClassName = classModel.ClassName;
|
|
|
|
|
student.Modifier = UserContext.Current.UserId;
|
|
|
|
|
student.ModifyDate = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
_studentRepository.Update(student);
|
|
|
|
|
|
|
|
|
|
foreach (var item in sportsTestResults)
|
|
|
|
|
{
|
|
|
|
|
item.ClassId = classId;
|
|
|
|
|
item.ClassName = classModel.ClassName;
|
|
|
|
|
item.Modifier = UserContext.Current.UserId;
|
|
|
|
|
item.ModifyDate = DateTime.Now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_sportsTestResultRepository.UpdateRange(sportsTestResults);
|
|
|
|
|
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
// 提交事务
|
|
|
|
|
transaction.Commit();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// 发生错误,回滚事务
|
|
|
|
|
transaction.Rollback();
|
|
|
|
|
throw new Exception("操作失败");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task ImportStudents(IFormFile file)
|
|
|
|
|
{
|
|
|
|
|
if (file == null || file.Length <= 0)
|
|
|
|
|
throw new Exception("操作失败");
|
|
|
|
|
var tenantId = UserContext.Current.TenantId;
|
|
|
|
|
|
|
|
|
|
var dataObjects = new List<ImportStudentParam>();
|
|
|
|
|
|
|
|
|
|
using (var fileStream = file.OpenReadStream())
|
|
|
|
|
{
|
|
|
|
|
dataObjects = Tool.ConvertExcelToList<ImportStudentParam>(fileStream);
|
|
|
|
|
}
|
|
|
|
|
var greadNames = dataObjects.Select(x => x.GradeName).Distinct().ToList();
|
|
|
|
|
|
|
|
|
|
var greadList = await _gradeRepository.FindAsync(x => greadNames.Contains(x.GradeName));
|
|
|
|
|
|
|
|
|
|
var greadIds = greadList.Select(x => x.Id).ToList();
|
|
|
|
|
|
|
|
|
|
var classList = await _classRepository.FindAsync(x => greadIds.Contains(x.GradeId) && x.SchoolCode == tenantId);
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
var groupedStudentNos = dataObjects.GroupBy(x => x.StudentNo);
|
|
|
|
|
foreach (var group in groupedStudentNos)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(group.Key) && group.Count() > 1)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception($"存在重复的学号:{group.Key},请检查后重新操作");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stuNos = groupedStudentNos.Select(g => g.Key).ToList();
|
|
|
|
|
var stuList = await _studentRepository.FindAsync(x => x.SchoolCode == tenantId && stuNos.Contains(x.StudentNo));
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
|
|
|
|
var entitys = new List<S_Student>();
|
|
|
|
|
|
|
|
|
|
foreach (var data in dataObjects)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(data.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("学号不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stuExists = stuList.Exists(x => x.StudentNo == data.StudentNo);
|
|
|
|
|
if (stuExists)
|
|
|
|
|
throw new Exception($"学号{data.StudentNo}的学生已存在!");
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(data.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("姓名不能为空");
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrWhiteSpace(data.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("性别不能为空");
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrWhiteSpace(data.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("年龄不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var greadModel = greadList.Where(x => x.GradeName.Equals(data.GradeName)).FirstOrDefault();
|
|
|
|
|
if (greadModel == null)
|
|
|
|
|
throw new Exception("未找到年级信息");
|
|
|
|
|
|
|
|
|
|
var classModel = classList.Where(x => x.GradeId == greadModel.Id && x.ClassName == data.ClassName).FirstOrDefault();
|
|
|
|
|
if (classModel == null)
|
|
|
|
|
throw new Exception("未找到班级信息");
|
|
|
|
|
|
|
|
|
|
var studentEntity = new S_Student()
|
|
|
|
|
{
|
|
|
|
|
SchoolCode = tenantId,
|
|
|
|
|
StudentName = data.StudentName,
|
|
|
|
|
StudentNo = data.StudentNo,
|
|
|
|
|
ClassId = classModel.Id,
|
|
|
|
|
Age = data.Age,
|
|
|
|
|
IDCard = data.IDCard,
|
|
|
|
|
Birthday = data.Birthday,
|
|
|
|
|
HeartRateFrontNo = data.HeartRateFrontNo,
|
|
|
|
|
HeartRateId = data.HeartRateId,
|
|
|
|
|
RopeSkipQRCode = data.RopeSkipQRCode,
|
|
|
|
|
Sex = data.Sex == "男" ? SexType.Male : SexType.Female,
|
|
|
|
|
RunChipNo = data.RunChipNo,
|
|
|
|
|
StudentStatus = StudentStatus.Normal,
|
|
|
|
|
Creator = UserContext.Current.UserId,
|
|
|
|
|
CreateDate = DateTime.Now
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
entitys.Add(studentEntity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _studentRepository.AddRangeAsync(entitys);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
//调用回调函数,同步数据到IOT
|
|
|
|
|
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
//{
|
|
|
|
|
// EventType = EventType.Add.GetDisplayName(),
|
|
|
|
|
// DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
// Json = JsonConvert.SerializeObject(entitys)
|
|
|
|
|
//}));
|
|
|
|
|
|
|
|
|
|
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
|
|
|
|
{
|
|
|
|
|
EventType = EventType.Add.GetDisplayName(),
|
|
|
|
|
DataType = IOTDataSyncType.Student.GetDisplayName(),
|
|
|
|
|
Json = JsonConvert.SerializeObject(entitys)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 上传学生头像
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<string> UploadPhoto(IFormFile file, int gradeId, int classId, string studentNo, string studentName)
|
|
|
|
|
{
|
|
|
|
|
//var student = _studentRepository.FindFirst(c => c.StudentNo == studentNo);
|
|
|
|
|
//if (student == null)
|
|
|
|
|
//{
|
|
|
|
|
// throw new Exception("学生未找到");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// 设置照片最大允许大小 (5MB)
|
|
|
|
|
const long MaxFileSize = 2 * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
// 检查文件大小是否超过限制
|
|
|
|
|
if (file.Length > MaxFileSize)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("上传的照片文件大小不能超过2MB");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var url = ALiYunOss.Upload(file, $"Upload/{UserContext.Current.TenantId}/Photo/Student/{gradeId}/{classId}/", studentNo);
|
|
|
|
|
|
|
|
|
|
var faceEntityWithRequest = new FaceEntityWithDto()
|
|
|
|
|
{
|
|
|
|
|
EntityId = studentNo,
|
|
|
|
|
SchoolCode = UserContext.Current.TenantId,
|
|
|
|
|
IsStudent = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await ALiYunFace.DeleteFaceEntityWith(faceEntityWithRequest);
|
|
|
|
|
|
|
|
|
|
var faceEntityWithReuslt = await ALiYunFace.FaceEntityWith(faceEntityWithRequest);
|
|
|
|
|
if (!faceEntityWithReuslt)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("上传失败");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var faceWithReuslt = await ALiYunFace.FaceWith(new FaceWithDto()
|
|
|
|
|
{
|
|
|
|
|
SchoolCode = UserContext.Current.TenantId,
|
|
|
|
|
Name = studentName,
|
|
|
|
|
IsStudent = true,
|
|
|
|
|
EntityId = studentNo,
|
|
|
|
|
ImageUrl = url
|
|
|
|
|
});
|
|
|
|
|
if (!faceWithReuslt)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("上传失败");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task BatchUploadPhoto(IFormFile zipFile)
|
|
|
|
|
{
|
|
|
|
|
// 解压缩 zip 文件
|
|
|
|
|
var uploadPath = Path.Combine("Upload", "Student");
|
|
|
|
|
Directory.CreateDirectory(uploadPath);
|
|
|
|
|
var zipFilePath = Path.Combine(uploadPath, zipFile.FileName);
|
|
|
|
|
|
|
|
|
|
using (var fileStream = new FileStream(zipFilePath, FileMode.Create))
|
|
|
|
|
{
|
|
|
|
|
await zipFile.CopyToAsync(fileStream);
|
|
|
|
|
}
|
|
|
|
|
// 解压后的目录路径
|
|
|
|
|
var extractPath = Path.Combine(uploadPath, Path.GetFileNameWithoutExtension(zipFile.FileName));
|
|
|
|
|
|
|
|
|
|
// 检查并删除已存在的目录
|
|
|
|
|
if (Directory.Exists(extractPath))
|
|
|
|
|
{
|
|
|
|
|
Directory.Delete(extractPath, true);
|
|
|
|
|
}
|
|
|
|
|
ZipFile.ExtractToDirectory(zipFilePath, extractPath);
|
|
|
|
|
|
|
|
|
|
// 递归获取所有文件
|
|
|
|
|
var files = Directory.GetFiles(extractPath, "*.*", SearchOption.AllDirectories)
|
|
|
|
|
.Select(filePath => new FileInfo(filePath))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
// 获取学号列表
|
|
|
|
|
var studentNos = files.Select(x => Path.GetFileNameWithoutExtension(x.Name)).ToList();
|
|
|
|
|
|
|
|
|
|
var students = await _studentRepository.FindAsync(x => studentNos.Contains(x.StudentNo) && x.SchoolCode.Equals(UserContext.Current.TenantId));
|
|
|
|
|
var classList = await _classRepository.FindAsync(x => x.SchoolCode.Equals(UserContext.Current.TenantId));
|
|
|
|
|
|
|
|
|
|
var studentEntitys = new List<S_Student>();
|
|
|
|
|
foreach (var fileInfo in files)
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
try
|
2025-06-06 16:00:39 +08:00
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
using (var stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
|
2025-06-06 16:00:39 +08:00
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
var formFile = new FormFile(stream, 0, stream.Length, fileInfo.FullName, fileInfo.Name);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
var stuNo = Path.GetFileNameWithoutExtension(fileInfo.Name);
|
|
|
|
|
var student = students.FirstOrDefault(x => x.StudentNo.Equals(stuNo, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (student != null)
|
|
|
|
|
{
|
|
|
|
|
var cla = classList.FirstOrDefault(x => x.Id == student.ClassId);
|
|
|
|
|
|
|
|
|
|
var url = await UploadPhoto(formFile, cla == null ? 0 : cla.GradeId, student.ClassId, stuNo, student.StudentName);
|
|
|
|
|
student.Photo = url;
|
|
|
|
|
studentEntitys.Add(student);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//throw new Exception($"未找到学号:{stuNo}");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
2025-06-06 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_studentRepository.UpdateRange(studentEntitys);
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
// 删除临时文件和目录
|
|
|
|
|
Directory.Delete(extractPath, true);
|
|
|
|
|
File.Delete(zipFilePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 学生数据统计
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
public async Task<StudentDataStatsModel> StudentWholeDataStats(StudentDataStatsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var res = new StudentDataStatsModel();
|
|
|
|
|
var tenantId = UserContext.Current.TenantId;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
paramDto.StudentNo = _studentRepository.DbContext.Set<S_Student>().First().StudentNo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var student = await (from s in _studentRepository.DbContext.Set<S_Student>()
|
|
|
|
|
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
|
|
|
|
|
where s.StudentNo == paramDto.StudentNo && s.SchoolCode.Equals(UserContext.Current.TenantId)
|
|
|
|
|
select new StudentPageListModel()
|
|
|
|
|
{
|
|
|
|
|
StudentName = s.StudentName,
|
|
|
|
|
GradeId = c.GradeId,
|
|
|
|
|
ClassId = s.ClassId,
|
|
|
|
|
ClassName = c.ClassName,
|
|
|
|
|
GradeName = c.GradeName,
|
|
|
|
|
Photo = s.Photo,
|
|
|
|
|
Age = s.Age,
|
|
|
|
|
Sex = s.Sex
|
|
|
|
|
}).FirstOrDefaultAsync();
|
|
|
|
|
|
|
|
|
|
if (student == null)
|
|
|
|
|
throw new ArgumentNullException("未找到学生数据");
|
|
|
|
|
|
|
|
|
|
res.StudentName = student.StudentName;
|
|
|
|
|
res.ClassName = student.ClassName;
|
|
|
|
|
res.GradeName = student.GradeName;
|
|
|
|
|
res.Photo = student.Photo;
|
|
|
|
|
res.Sex = student.Sex.Description();
|
|
|
|
|
res.Age = student.Age;
|
|
|
|
|
|
|
|
|
|
var heartRateData = await (from t in _studentRepository.DbContext.Set<I_TrainingData>()
|
|
|
|
|
join s in _studentRepository.DbContext.Set<I_TrainRanking>() on t.Id equals s.TrainingDataId
|
|
|
|
|
where
|
|
|
|
|
t.SchoolCode.Equals(UserContext.Current.TenantId)
|
|
|
|
|
&& t.ItemType == (int)TrainingItemType.HeartRate
|
|
|
|
|
&& s.StudentNo == paramDto.StudentNo
|
|
|
|
|
select new
|
|
|
|
|
{
|
|
|
|
|
t.Id,
|
|
|
|
|
s.HighHeartRate
|
|
|
|
|
})
|
|
|
|
|
.OrderByDescending(x => x.Id)
|
|
|
|
|
.Take(5)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var keys = new[] { "一", "二", "三", "四", "五" };
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
if (heartRateData.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
res.HeartRateData = keys.ToDictionary(key => key, key => 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
res.HeartRateData = heartRateData
|
|
|
|
|
.Select((x, index) => new { Key = keys[index], Value = x.HighHeartRate ?? 0 })
|
|
|
|
|
.ToDictionary(x => x.Key, x => x.Value);
|
|
|
|
|
}
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
|
|
|
|
// 从缓存中获取数据
|
|
|
|
|
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.SchoolCode.Equals(tenantId) && x.StudentNo == paramDto.StudentNo, paramDto.Mode);
|
|
|
|
|
|
|
|
|
|
var categoryList = await (from s in _sportsTestResultRepository.DbContext.Set<S_GradeAssocCategory>()
|
|
|
|
|
join n in _sportsTestResultRepository.DbContext.Set<N_SportsTestCategory>()
|
|
|
|
|
on s.CategoryValue equals n.CategoryValue
|
|
|
|
|
where s.GradeId == student.GradeId
|
|
|
|
|
select new
|
|
|
|
|
{
|
|
|
|
|
n.CategoryValue,
|
|
|
|
|
n.CategoryName
|
|
|
|
|
}).ToListAsync();
|
|
|
|
|
|
|
|
|
|
foreach (var category in categoryList)
|
|
|
|
|
{
|
|
|
|
|
var categoryResults = sportsTestResults.Where(x => x.CategoryValue == category.CategoryValue).ToList();
|
|
|
|
|
|
|
|
|
|
var maxValue = categoryResults.Any() ? categoryResults.Max(x => x.Score + x.AdditionalScore) : 0f;
|
|
|
|
|
var lastValue = categoryResults.OrderByDescending(x => x.ScoreTime).Select(x => x.Score + x.AdditionalScore).FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
var resultContrast = new ResultContrast
|
|
|
|
|
{
|
|
|
|
|
MaxValue = maxValue,
|
|
|
|
|
LastValue = lastValue
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
res.TestResultCompare.AxisX.Add(category.CategoryName);
|
|
|
|
|
res.TestResultCompare.AxisY.Add(resultContrast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 各项目平均成绩
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
public async Task<Dictionary<string, float>> TestResultAvg(StudentCategoryParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
|
|
|
|
x.StudentNo == paramDto.StudentNo &&
|
|
|
|
|
x.SchoolCode.Equals(UserContext.Current.TenantId) &&
|
|
|
|
|
x.CategoryValue == paramDto.CategoryValue
|
|
|
|
|
, paramDto.Mode);
|
|
|
|
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
var currentQuarter = (now.Month - 1) / 3 + 1;
|
|
|
|
|
var currentYear = now.Year;
|
|
|
|
|
|
|
|
|
|
// 处理跨年情况
|
|
|
|
|
var quarters = new[]
|
|
|
|
|
{
|
|
|
|
|
new { Label = "本季度", Quarter = currentQuarter, Year = currentYear },
|
|
|
|
|
new { Label = "上季度", Quarter = currentQuarter - 1 == 0 ? 4 : currentQuarter - 1, Year = currentQuarter == 1 ? currentYear - 1 : currentYear },
|
|
|
|
|
new { Label = "前季度", Quarter = currentQuarter - 2 == 0 ? 4 : currentQuarter - 2 == -1 ? 3 : currentQuarter - 2, Year = currentQuarter <= 2 ? currentYear - 1 : currentYear }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return quarters.ToDictionary(
|
|
|
|
|
q => q.Label,
|
|
|
|
|
q =>
|
|
|
|
|
{
|
|
|
|
|
var filteredResults = sportsTestResults
|
|
|
|
|
.Where(x => x.ScoreTime >= Tool.GetQuarterStartDate(q.Quarter, q.Year) && x.ScoreTime <= Tool.GetQuarterEndDate(q.Quarter, q.Year))
|
|
|
|
|
.Select(x => x.Score + x.AdditionalScore)
|
|
|
|
|
.ToList();
|
|
|
|
|
return filteredResults.Any() ? (float)Math.Round(filteredResults.Average(), 2) : 0;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 各项目成绩对比
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
public async Task<Dictionary<string, float>> TestResultContrast(StudentCategoryParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
// 获取指定班级和类别的测试结果
|
|
|
|
|
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
|
|
|
|
x.ClassId == paramDto.ClassId &&
|
|
|
|
|
x.SchoolCode.Equals(UserContext.Current.TenantId) &&
|
|
|
|
|
x.CategoryValue == paramDto.CategoryValue
|
|
|
|
|
, paramDto.Mode);
|
|
|
|
|
|
|
|
|
|
// 提取班级所有成绩
|
|
|
|
|
var filteredResults = sportsTestResults
|
|
|
|
|
.Select(x => x.Score + x.AdditionalScore)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
// 提取个人成绩
|
|
|
|
|
var personalScore = sportsTestResults
|
|
|
|
|
.Where(x => x.StudentNo == paramDto.StudentNo)
|
|
|
|
|
.Select(x => x.Score + x.AdditionalScore)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
var quarters = new[]
|
|
|
|
|
{
|
|
|
|
|
new { Label = "个人成绩", Value = personalScore },
|
|
|
|
|
new { Label = "班级平均分", Value = filteredResults.Any() ? (float)Math.Round(filteredResults.Average(),2) : 0f },
|
|
|
|
|
new { Label = "班级中位数", Value = filteredResults.Any() ? (float)Math.Round(filteredResults.Median(),2) : 0f },
|
|
|
|
|
new { Label = "班级最高分", Value = filteredResults.Any() ? (float)Math.Round(filteredResults.Max(),2) : 0f }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return quarters.ToDictionary(q => q.Label, q => q.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 成绩趋势
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
|
|
|
|
|
public async Task<VariousSportsProportion> ResultTrends(StudentResultTrendsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
|
|
|
|
x.StudentNo == paramDto.StudentNo &&
|
|
|
|
|
x.SchoolCode.Equals(UserContext.Current.TenantId) &&
|
|
|
|
|
x.CategoryValue == paramDto.CategoryValue
|
|
|
|
|
, paramDto.Mode);
|
|
|
|
|
|
|
|
|
|
var result = new VariousSportsProportion();
|
|
|
|
|
|
|
|
|
|
DateTime currentDate = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
switch (paramDto.CycleTime)
|
|
|
|
|
{
|
|
|
|
|
case CycleTimeEnum.InThePastWeek:
|
|
|
|
|
// 近一周:按天展示
|
|
|
|
|
var pastWeekResults = sportsTestResults
|
|
|
|
|
.Where(x => x.ScoreTime >= currentDate.AddDays(-7))
|
|
|
|
|
.GroupBy(x => x.ScoreTime.Date);
|
|
|
|
|
|
|
|
|
|
foreach (var group in pastWeekResults)
|
|
|
|
|
{
|
|
|
|
|
result.AxisX.Add(group.Key.ToString("yyyy-MM-dd"));
|
|
|
|
|
var score = (group.Sum(x => x.Score) + group.Sum(x => x.AdditionalScore)) / (group.Select(x => x.StudentNo).Distinct().Count());
|
|
|
|
|
result.AxisY.Add((float)Math.Round(score));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CycleTimeEnum.InThePastTwoWeeks:
|
|
|
|
|
// 近两周:按天展示
|
|
|
|
|
var pastTwoWeeksResults = sportsTestResults
|
|
|
|
|
.Where(x => x.ScoreTime >= currentDate.AddDays(-14))
|
|
|
|
|
.GroupBy(x => x.ScoreTime.Date);
|
|
|
|
|
|
|
|
|
|
foreach (var group in pastTwoWeeksResults)
|
|
|
|
|
{
|
|
|
|
|
result.AxisX.Add(group.Key.ToString("yyyy-MM-dd"));
|
|
|
|
|
var score = (group.Sum(x => x.Score) + group.Sum(x => x.AdditionalScore)) / (group.Select(x => x.StudentNo).Distinct().Count());
|
|
|
|
|
result.AxisY.Add((float)Math.Round(score));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CycleTimeEnum.InThePastMonth:
|
|
|
|
|
// 近一月:每5天一个阶段展示
|
|
|
|
|
var pastMonthResults = sportsTestResults
|
|
|
|
|
.Where(x => x.ScoreTime >= currentDate.AddDays(-30))
|
|
|
|
|
.GroupBy(x => (currentDate - x.ScoreTime).Days / 5);
|
|
|
|
|
|
|
|
|
|
foreach (var group in pastMonthResults)
|
|
|
|
|
{
|
|
|
|
|
var minDate = group.Min(r => r.ScoreTime).ToString("yyyy-MM-dd");
|
|
|
|
|
var maxDate = group.Max(r => r.ScoreTime).ToString("yyyy-MM-dd");
|
|
|
|
|
result.AxisX.Add($"{minDate} - {maxDate}");
|
|
|
|
|
var score = (group.Sum(x => x.Score) + group.Sum(x => x.AdditionalScore)) / (group.Select(x => x.StudentNo).Distinct().Count());
|
|
|
|
|
result.AxisY.Add((float)Math.Round(score));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CycleTimeEnum.InThePastYear:
|
|
|
|
|
// 近一年:按月展示
|
|
|
|
|
var pastYearResults = sportsTestResults
|
|
|
|
|
.Where(x => x.ScoreTime >= currentDate.AddYears(-1))
|
|
|
|
|
.GroupBy(x => new { x.ScoreTime.Year, x.ScoreTime.Month });
|
|
|
|
|
|
|
|
|
|
foreach (var group in pastYearResults)
|
|
|
|
|
{
|
|
|
|
|
result.AxisX.Add($"{group.Key.Year}-{group.Key.Month:D2}");
|
|
|
|
|
var score = (group.Sum(x => x.Score) + group.Sum(x => x.AdditionalScore)) / (group.Select(x => x.StudentNo).Distinct().Count());
|
|
|
|
|
result.AxisY.Add((float)Math.Round(score));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// 默认处理,如果没有匹配的情况
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 训练记录
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
2025-06-06 16:55:14 +08:00
|
|
|
|
public async Task<PageDataDto<StudentTrainingRecordsModel>> TrainingRecords(StudentTrainingRecordsParam paramDto)
|
2025-06-06 16:00:39 +08:00
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
var res = new PageDataDto<StudentTrainingRecordsModel>();
|
2025-06-06 16:00:39 +08:00
|
|
|
|
var tenantId = UserContext.Current.TenantId;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
paramDto.StudentNo = _studentRepository.DbContext.Set<S_Student>().First(s => s.SchoolCode == tenantId).StudentNo;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 查询 I_TrainingData 表
|
|
|
|
|
var iotQuery = from student in _studentRepository.DbContext.Set<I_TrainRanking>()
|
|
|
|
|
join trainingData in _studentRepository.DbContext.Set<I_TrainingData>()
|
|
|
|
|
on student.TrainingDataId equals trainingData.Id
|
|
|
|
|
where trainingData.IsDisplay && trainingData.SchoolCode == tenantId
|
|
|
|
|
select new StudentTrainingRecordsModel
|
|
|
|
|
{
|
|
|
|
|
StudentNo = student.StudentNo,
|
|
|
|
|
// 训练模式
|
|
|
|
|
TrainType = trainingData.ModelType ?? 0,
|
|
|
|
|
//项目模式
|
|
|
|
|
ModelName = trainingData.ModelName,
|
|
|
|
|
CategoryValue = (TrainingItemType)trainingData.ItemType,
|
|
|
|
|
// 成绩
|
|
|
|
|
Result = (float?)trainingData.ClassNumAvg,
|
|
|
|
|
ResultLevel = trainingData.ClassScore,
|
|
|
|
|
// 训练时间
|
|
|
|
|
ScoreTime = trainingData.InsertTime,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 查询 Ai_SportsTestData 表
|
|
|
|
|
var aiQuery = from trainingData in _studentRepository.DbContext.Set<Ai_SportsTestData>()
|
|
|
|
|
where trainingData.IsDisplay && trainingData.SchoolCode == tenantId
|
|
|
|
|
select new StudentTrainingRecordsModel
|
|
|
|
|
{
|
|
|
|
|
StudentNo = trainingData.StudentNo,
|
|
|
|
|
TrainType = (int)trainingData.ModeType,
|
|
|
|
|
ModelName = trainingData.ModelName,
|
|
|
|
|
CategoryValue = (TrainingItemType)trainingData.CategoryValue,
|
|
|
|
|
Result = (float?)trainingData.Value,
|
|
|
|
|
ResultLevel = (int?)trainingData.RankEnum,
|
|
|
|
|
ScoreTime = trainingData.ScoreTime
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 合并两个查询结果
|
|
|
|
|
var combinedQuery = iotQuery.Concat(aiQuery);
|
|
|
|
|
|
|
|
|
|
// 根据学号过滤
|
2025-06-06 16:00:39 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
combinedQuery = combinedQuery.Where(x => x.StudentNo == paramDto.StudentNo);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 根据开始时间过滤
|
2025-06-06 16:00:39 +08:00
|
|
|
|
if (paramDto.StartTime.HasValue)
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
combinedQuery = combinedQuery.Where(x => x.ScoreTime >= paramDto.StartTime);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 根据结束时间过滤
|
2025-06-06 16:00:39 +08:00
|
|
|
|
if (paramDto.EndTime.HasValue)
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
combinedQuery = combinedQuery.Where(x => x.ScoreTime <= paramDto.EndTime);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 根据项目类型过滤
|
2025-06-06 16:00:39 +08:00
|
|
|
|
if (paramDto.CategoryValue > 0)
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
combinedQuery = combinedQuery.Where(x => x.CategoryValue == (TrainingItemType)paramDto.CategoryValue);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 根据成绩等级过滤
|
2025-06-06 16:00:39 +08:00
|
|
|
|
if (paramDto.Rank > 0)
|
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
combinedQuery = combinedQuery.Where(x => x.ResultLevel == (int)paramDto.Rank);
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 获取总数
|
|
|
|
|
res.Total = await combinedQuery.CountAsync();
|
2025-06-06 16:00:39 +08:00
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 分页和排序
|
|
|
|
|
var list = await combinedQuery
|
|
|
|
|
.OrderBy(c => c.ScoreTime) // 按训练时间排序
|
|
|
|
|
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
|
|
|
|
|
.Take(paramDto.PageSize)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
foreach (var item in list)
|
|
|
|
|
{
|
|
|
|
|
// 项目
|
|
|
|
|
item.CategoryName = item.CategoryValue.GetDisplayName();
|
|
|
|
|
var grade = ((AchievementRank)(item.ResultLevel.HasValue && item.ResultLevel.Value > 0 ? item.ResultLevel.Value : 4));
|
|
|
|
|
var gradeName = grade.GetDescription();
|
|
|
|
|
// 等级
|
|
|
|
|
item.Grade = gradeName;
|
|
|
|
|
var trainName = ((Ai_ModeEnum)(item.TrainType)).GetDescription();
|
|
|
|
|
// 训练模式
|
|
|
|
|
item.TrainName = trainName;
|
|
|
|
|
}
|
|
|
|
|
res.Datas = list;
|
|
|
|
|
|
|
|
|
|
return res;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 体测记录
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
2025-06-06 16:55:14 +08:00
|
|
|
|
public async Task<PageDataDto<StudentTestRecordsModel>> TestRecords(StudentTrainingRecordsParam paramDto)
|
2025-06-06 16:00:39 +08:00
|
|
|
|
{
|
2025-06-06 16:55:14 +08:00
|
|
|
|
var res = new PageDataDto<StudentTestRecordsModel>();
|
|
|
|
|
|
2025-06-06 16:00:39 +08:00
|
|
|
|
var tenantId = UserContext.Current.TenantId;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
paramDto.StudentNo = this._studentRepository.DbContext.Set<S_Student>().First().StudentNo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化基础查询条件
|
|
|
|
|
Expression<Func<SportsTestValueModel, bool>> query = (x => x.SchoolCode == tenantId);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(paramDto.StudentNo))
|
|
|
|
|
{
|
|
|
|
|
query = query.And(x => x.StudentNo == paramDto.StudentNo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.StartTime.HasValue)
|
|
|
|
|
{
|
|
|
|
|
query = query.And(x => x.ScoreTime >= paramDto.StartTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.EndTime.HasValue)
|
|
|
|
|
{
|
|
|
|
|
query = query.And(x => x.ScoreTime <= paramDto.StartTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.CategoryValue > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.And(x => x.CategoryValue == paramDto.CategoryValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paramDto.Rank > 0)
|
|
|
|
|
{
|
|
|
|
|
query = query.And(x => x.RankEnum == paramDto.Rank);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(query.Compile(), paramDto.TrainType);
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
// 获取总数
|
|
|
|
|
res.Total = sportsTestResults.Count();
|
|
|
|
|
|
|
|
|
|
// 分页和排序
|
|
|
|
|
res.Datas = sportsTestResults
|
|
|
|
|
.OrderBy(c => c.ScoreTime) // 按训练时间排序
|
2025-06-06 16:00:39 +08:00
|
|
|
|
.Select(c => new StudentTestRecordsModel
|
|
|
|
|
{
|
|
|
|
|
TrainName = c.ModeType.GetDescription(),
|
|
|
|
|
CategoryValue = (SportsTestItemType)c.CategoryValue,
|
|
|
|
|
CategoryName = ((SportsTestItemType)c.CategoryValue).GetDisplayName(),
|
|
|
|
|
Result = c.Value,
|
|
|
|
|
Score = (c.Score + c.AdditionalScore),
|
|
|
|
|
Rank = c.Rank,
|
|
|
|
|
ScoreTime = c.ScoreTime
|
|
|
|
|
})
|
2025-06-06 16:55:14 +08:00
|
|
|
|
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
|
|
|
|
|
.Take(paramDto.PageSize)
|
2025-06-06 16:00:39 +08:00
|
|
|
|
.ToList();
|
2025-06-06 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
return res;
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建课堂模式查询
|
|
|
|
|
private IQueryable<StudentTrainingRecordsModel> BuildClassRoomModeQuery(string tenantId, string studentNo, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var query = from td in _studentRepository.DbContext.Set<I_TrainingData>()
|
|
|
|
|
join tr in _studentRepository.DbContext.Set<I_TrainRanking>() on td.Id equals tr.TrainingDataId into ranking
|
|
|
|
|
from r in ranking.DefaultIfEmpty()
|
|
|
|
|
where td.SchoolCode == tenantId && r.StudentNo == studentNo
|
|
|
|
|
select new StudentTrainingRecordsModel
|
|
|
|
|
{
|
|
|
|
|
TrainName = "课堂模式",
|
|
|
|
|
ModelName = td.ModelName,
|
|
|
|
|
CategoryValue = (TrainingItemType)td.ItemType,
|
|
|
|
|
CategoryName = ((TrainingItemType)td.ItemType).GetDescription(),
|
|
|
|
|
Result = r.JumpValue,
|
|
|
|
|
ResultLevel = r.ResultLevel,
|
|
|
|
|
ScoreTime = td.InsertTime
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return ApplyFilters(query, paramDto);
|
|
|
|
|
}
|
|
|
|
|
// 构建自由模式查询
|
|
|
|
|
private IQueryable<StudentTrainingRecordsModel> BuildFreeModeQuery(string tenantId, string studentNo, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var query = from a in _studentRepository.DbContext.Set<Ai_TrainingData>()
|
|
|
|
|
join n in _studentRepository.DbContext.Set<N_SportsTestValue>() on a.StartTime equals n.ScoreTime
|
|
|
|
|
where a.SchoolCode == tenantId && a.StudentNo == studentNo
|
|
|
|
|
select new StudentTrainingRecordsModel
|
|
|
|
|
{
|
|
|
|
|
TrainName = "自由模式",
|
|
|
|
|
ScoreTime = a.StartTime,
|
|
|
|
|
CategoryValue = a.CategoryValue,
|
|
|
|
|
CategoryName = ((TrainingItemType)Convert.ToInt32(a.CategoryValue)).GetDescription(),
|
|
|
|
|
ModelName = "/",
|
|
|
|
|
Grade = n.Rank
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return ApplyFilters(query, paramDto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通用过滤逻辑
|
|
|
|
|
private IQueryable<StudentTrainingRecordsModel> ApplyFilters(IQueryable<StudentTrainingRecordsModel> query, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
return query
|
|
|
|
|
.WhereIF(paramDto.StartTime.HasValue, x => x.ScoreTime >= paramDto.StartTime)
|
|
|
|
|
.WhereIF(paramDto.EndTime.HasValue, x => x.ScoreTime <= paramDto.EndTime)
|
|
|
|
|
.WhereIF(paramDto.CategoryValue > 0, x => x.CategoryValue == (TrainingItemType)paramDto.CategoryValue)
|
|
|
|
|
.WhereIF(paramDto.Rank > 0, x => x.ResultLevel == (int)paramDto.Rank);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 构建体测课堂模式查询
|
|
|
|
|
private IQueryable<StudentTestRecordsModel> BuildTestRoomModeQuery(string tenantId, string studentNo, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var query = from a in _studentRepository.DbContext.Set<I_SportsTestData>()
|
|
|
|
|
join r in _studentRepository.DbContext.Set<N_SportsTestValue>() on a.ScoreTime equals r.ScoreTime
|
|
|
|
|
where a.SchoolCode == tenantId && r.StudentNo == studentNo
|
|
|
|
|
select new StudentTestRecordsModel
|
|
|
|
|
{
|
|
|
|
|
TrainName = "课堂模式",
|
|
|
|
|
CategoryValue = (SportsTestItemType)r.CategoryValue,
|
|
|
|
|
CategoryName = ((SportsTestItemType)r.CategoryValue).GetDescription(),
|
|
|
|
|
Result = r.Value,
|
|
|
|
|
Rank = r.Rank,
|
|
|
|
|
Score = r.Score,
|
|
|
|
|
ScoreTime = r.ScoreTime
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return TestApplyFilters(query, paramDto);
|
|
|
|
|
}
|
|
|
|
|
private IQueryable<StudentTestRecordsModel> BuildTestFreeModeQuery(string tenantId, string studentNo, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
var query = from a in _studentRepository.DbContext.Set<Ai_TrainingData>()
|
|
|
|
|
join n in _studentRepository.DbContext.Set<N_SportsTestValue>() on a.StartTime equals n.ScoreTime
|
|
|
|
|
where a.SchoolCode == tenantId && a.StudentNo == studentNo
|
|
|
|
|
select new StudentTestRecordsModel
|
|
|
|
|
{
|
|
|
|
|
TrainName = "自由模式",
|
|
|
|
|
ScoreTime = a.StartTime,
|
|
|
|
|
CategoryValue = (SportsTestItemType)a.CategoryValue,
|
|
|
|
|
CategoryName = ((TrainingItemType)Convert.ToInt32(a.CategoryValue)).GetDescription(),
|
|
|
|
|
Rank = n.Rank,
|
|
|
|
|
Score = n.Score,
|
|
|
|
|
Result = n.Value
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return TestApplyFilters(query, paramDto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 通用过滤逻辑
|
|
|
|
|
private IQueryable<StudentTestRecordsModel> TestApplyFilters(IQueryable<StudentTestRecordsModel> query, StudentTrainingRecordsParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
return query
|
|
|
|
|
.WhereIF(paramDto.StartTime.HasValue, x => x.ScoreTime >= paramDto.StartTime)
|
|
|
|
|
.WhereIF(paramDto.EndTime.HasValue, x => x.ScoreTime <= paramDto.EndTime)
|
|
|
|
|
.WhereIF(paramDto.CategoryValue > 0, x => x.CategoryValue == (SportsTestItemType)paramDto.CategoryValue)
|
|
|
|
|
.WhereIF(paramDto.Rank > 0, x => x.Rank == paramDto.RankStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 学生名单列表
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="paramDto"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
|
|
|
public async Task<List<StudentNamePageListModel>> GetStudentNamePageList(StudentNamePageListParam paramDto)
|
|
|
|
|
{
|
|
|
|
|
// 查询学生信息
|
|
|
|
|
var students = await _studentRepository.FindAsIQueryable(x => x.ClassId == paramDto.ClassId)
|
|
|
|
|
.Select(x => new StudentNamePageListModel()
|
|
|
|
|
{
|
|
|
|
|
StudentNo = x.StudentNo,
|
|
|
|
|
StudentName = x.StudentName,
|
|
|
|
|
Photo = x.Photo
|
|
|
|
|
})
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
// 查询两个数据源的成绩并合并
|
|
|
|
|
var combinedResults = await _sportsTestResultRepository.FindAsIQueryable(x =>
|
|
|
|
|
x.ClassId == paramDto.ClassId &&
|
|
|
|
|
x.SchoolCode.Equals(UserContext.Current.TenantId)
|
|
|
|
|
)
|
|
|
|
|
.Select(x => new { x.StudentNo, x.Score })
|
|
|
|
|
.Concat(
|
|
|
|
|
_sportsTestResultRepository.DbContext.Set<Ai_SportsTestData>().Where(x =>
|
|
|
|
|
x.ClassId == paramDto.ClassId &&
|
|
|
|
|
x.SchoolCode.Equals(UserContext.Current.TenantId)
|
|
|
|
|
)
|
|
|
|
|
.Select(x => new { x.StudentNo, x.Score })
|
|
|
|
|
)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
// 使用字典来存储平均成绩,便于后续查找
|
|
|
|
|
var averageScoresDict = combinedResults
|
|
|
|
|
.GroupBy(x => x.StudentNo)
|
|
|
|
|
.ToDictionary(g => g.Key, g => g.Average(x => x.Score));
|
|
|
|
|
|
|
|
|
|
// 遍历学生信息,为每个学生分配 Rank
|
|
|
|
|
students.ForEach(student =>
|
|
|
|
|
{
|
|
|
|
|
if (averageScoresDict.TryGetValue(student.StudentNo, out var averageScore))
|
|
|
|
|
{
|
|
|
|
|
student.Rank = averageScore.GetRank(); // 有平均成绩的情况
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
student.Rank = 0.GetRank(); // 没有成绩时默认处理
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return students;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 16:55:14 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 更换班级
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="studentNoList"></param>
|
|
|
|
|
/// <param name="classId"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task ReplaceClasses(List<string> studentNoList, int classId)
|
|
|
|
|
{
|
|
|
|
|
var students = await _studentRepository.FindAsync(x => studentNoList.Contains(x.StudentNo));
|
|
|
|
|
|
|
|
|
|
if (students == null || students.Count == 0)
|
|
|
|
|
throw new ArgumentNullException("未找到更新的数据");
|
|
|
|
|
|
|
|
|
|
foreach (var student in students)
|
|
|
|
|
{
|
|
|
|
|
student.ClassId = classId;
|
|
|
|
|
student.Modifier = UserContext.Current.UserId;
|
|
|
|
|
student.ModifyDate = DateTime.Now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_studentRepository.UpdateRange(students);
|
|
|
|
|
|
|
|
|
|
await _studentRepository.SaveChangesAsync();
|
|
|
|
|
}
|
2025-06-06 16:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
}
|