2026-04-27 16:01:33 +08:00

639 lines
25 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 AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VOL.Business.IServices;
using VOL.Business.IServices.Norm;
using VOL.Business.IServices.School;
using VOL.Business.IServices.SportsHall;
using VOL.Business.Services.School;
using VOL.Core.CacheManager;
using VOL.Core.Extensions.AutofacManager;
using VOL.Core.ManageUser;
using VOL.Core.Utilities;
using VOL.Entity.DomainModels;
using VOL.Entity.Enum;
using VOL.Model;
using VOL.Model.IOT.Request;
using VOL.Model.School.Request;
using VOL.Model.School.Response;
using VOL.Model.SportsHall;
using VOL.System.IRepositories;
using VOL.System.Repositories;
namespace VOL.Business.Services.SportsHall
{
/// <summary>
/// 运动馆
/// </summary>
public class SportsHallService : ISportsHallService, IDependency
{
#region 初始化
private readonly IS_ClassRepository _classRepository;
private readonly IS_GradeRepository _gradeRepository;
private readonly IS_ClassAssocTeacherRepository _classAssocTeacherRepository;
private readonly IS_StudentRepository _studentRepository;
IN_HealthStandardsRepository _healthStandardsRepository;
IN_SportsTestResultRepository _sportsTestResultRepository;
[ActivatorUtilitiesConstructor]
public SportsHallService(IS_ClassRepository classRepository,
IS_GradeRepository gradeRepository,
IS_ClassAssocTeacherRepository classAssocTeacherRepository,
IS_StudentRepository studentRepository,
IN_HealthStandardsRepository healthStandardsRepository,
IN_SportsTestResultRepository sportsTestResultRepository)
{
_classRepository = classRepository;
_gradeRepository = gradeRepository;
_classAssocTeacherRepository = classAssocTeacherRepository;
_studentRepository = studentRepository;
_healthStandardsRepository = healthStandardsRepository;
_sportsTestResultRepository = sportsTestResultRepository;
}
#endregion
/// <summary>
/// 新增班级
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task AddClass(AddSportsHallClassParam paramDto)
{
var tenantId = UserContext.Current.TenantId;
var grade = await _gradeRepository.FindAsIQueryable(x => x.GradeName == "运动馆").Select(x => new { x.Id, x.GradeName }).FirstAsync();
var exist = await _classRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.ClassName == paramDto.ClassName && x.GradeId == grade.Id);
if (exist)
throw new Exception("班级名称已存在");
var classEntity = new S_Class()
{
GradeId = grade.Id,
GradeName = grade.GradeName,
ClassName = paramDto.ClassName,
Creator = UserContext.Current.UserId,
SchoolCode = UserContext.Current.TenantId,
CreateDate = DateTime.Now
};
using (var transaction = _classRepository.DbContext.Database.BeginTransaction())
{
try
{
await _classRepository.AddAsync(classEntity);
await _classRepository.SaveChangesAsync();
int classId = classEntity.Id;
var classAssocTeacherList = new List<S_ClassAssocTeacher>();
paramDto.TeacherIds.ForEach(x =>
{
classAssocTeacherList.Add(new S_ClassAssocTeacher()
{
ClassId = classId,
TeacherId = x,
Creator = UserContext.Current.UserId,
CreateDate = DateTime.Now,
Modifier = UserContext.Current.UserId,
ModifyDate = DateTime.Now,
});
});
await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList);
var students = await _studentRepository.FindAsync(x => paramDto.studentNoList.Contains(x.StudentNo));
foreach (var student in students)
{
student.ClassId = classId;
int index = paramDto.studentNoList.IndexOf(student.StudentNo);
student.OrderNo = index >= 0 ? index + 1 : 0;
}
_studentRepository.UpdateRange(students);
await _studentRepository.SaveChangesAsync();
// 提交事务
transaction.Commit();
}
catch (Exception ex)
{
// 发生错误,回滚事务
transaction.Rollback();
Console.WriteLine("事务回滚:" + ex.Message);
}
}
}
/// <summary>
/// 更新班级
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task ModifyClass(ModifySportsHallClassParam paramDto)
{
var tenantId = UserContext.Current.TenantId;
var classModel = await _classRepository.FindAsyncFirst(x => x.Id == paramDto.ClassId);
var grade = await _gradeRepository.FindAsIQueryable(x => x.GradeName == "运动馆").Select(x => new { x.Id, x.GradeName }).FirstAsync();
if (classModel == null)
throw new ArgumentNullException("未找到要更新得数据");
var exist = await _classRepository.ExistsAsync(
x => x.SchoolCode == tenantId
&& x.ClassName == paramDto.ClassName
&& x.GradeId == grade.Id
&& x.Id != paramDto.ClassId);
if (exist)
throw new Exception("班级名称已存在");
classModel.ClassName = paramDto.ClassName;
using (var transaction = _classRepository.DbContext.Database.BeginTransaction())
{
try
{
_classRepository.Update(classModel);
var teacherList = await _classAssocTeacherRepository.FindAsync(x => x.ClassId == paramDto.ClassId);
_classAssocTeacherRepository.DbContext.RemoveRange(teacherList);
var classAssocTeacherList = new List<S_ClassAssocTeacher>();
paramDto.TeacherIds.ForEach(x =>
{
classAssocTeacherList.Add(new S_ClassAssocTeacher()
{
ClassId = paramDto.ClassId,
TeacherId = x,
Creator = UserContext.Current.UserId,
CreateDate = DateTime.Now,
Modifier = UserContext.Current.UserId,
ModifyDate = DateTime.Now,
});
});
await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList);
var oldStudents = await _studentRepository.FindAsync(x => x.ClassId == classModel.Id);
var newStudents = await _studentRepository.FindAsync(x => paramDto.studentNoList.Contains(x.StudentNo));
var allStudents = oldStudents.UnionBy(newStudents, s => s.Id).ToList();
foreach (var student in allStudents)
{
if (paramDto.studentNoList.Contains(student.StudentNo))
{
student.ClassId = classModel.Id;
student.OrderNo = paramDto.studentNoList.IndexOf(student.StudentNo) + 1;
}
else
{
student.ClassId = 0;
student.OrderNo = 0;
}
}
_studentRepository.UpdateRange(allStudents);
await _classRepository.SaveChangesAsync();
transaction.Commit();
}
catch (Exception ex)
{
// 发生错误,回滚事务
transaction.Rollback();
Console.WriteLine("事务回滚:" + ex.Message);
}
}
}
/// <summary>
/// 导入运动馆学生体测成绩
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task SportsHallImportStudentsTestData(IFormFile file)
{
if (file == null || file.Length <= 0)
throw new Exception("操作失败");
List<SportsHallImportStudentsTestDataParam> dataObjects;
using (var fileStream = file.OpenReadStream())
{
dataObjects = Tool.ConvertExcelToList<SportsHallImportStudentsTestDataParam>(fileStream);
}
var studentNos = dataObjects.Select(x => x.StudentNo).Distinct().ToList();
// 用一个查询一次性获取所有学生信息
var students = await (from s in _studentRepository.DbContext.Set<S_Student>()
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
join g in _studentRepository.DbContext.Set<S_Grade>() on c.GradeId equals g.Id
where c.SchoolCode.Equals(UserContext.Current.TenantId)
select new
{
s.Id,
s.Age,
s.ClassId,
c.ClassName,
s.StudentName,
GradeId = g.Id,
g.GradeName,
s.Sex,
s.StudentNo
}).ToListAsync();
// 将学生信息存入字典,快速查找
var studentsDict = students.ToDictionary(x => x.StudentNo);
var entityList = new List<N_SportsTestValue>();
foreach (var data in dataObjects)
{
if (!studentsDict.TryGetValue(data.StudentNo, out var student))
{
throw new Exception($"未找到此编号为{data.StudentNo}的学生");
}
var timeNow = Convert.ToDateTime(data.ScoreTime);
// 为每个学生添加多条体测数据
if (data.BMIValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.BMI, student, data.GradeId, timeNow));
if (data.Sit_And_ReachValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.Sit_And_Reach, student, data.GradeId, timeNow));
if (data.VitalCapacityValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.VitalCapacity, student, data.GradeId, timeNow));
if (data.OneMinuteJumpRopeValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.OneMinuteJumpRope, student, data.GradeId, timeNow));
if (data.One_Minute_Sit_UpValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.One_Minute_Sit_Up, student, data.GradeId, timeNow));
if (data.StandingLongJumpValue > 0)
entityList.Add(await AddSportsHallEnumStudentsTestData(data, SportsTestItemType.StandingLongJump, student, data.GradeId, timeNow));
}
// 批量添加数据
await _sportsTestResultRepository.AddRangeAsync(entityList);
// 一次性保存所有修改
await _sportsTestResultRepository.SaveChangesAsync();
}
public async Task<N_SportsTestValue> AddSportsHallEnumStudentsTestData(SportsHallImportStudentsTestDataParam data, SportsTestItemType type, dynamic student, int gradeId, DateTime timeNow)
{
var entity = new N_SportsTestValue
{
TeacherId = 0,
TeacherName = "",
DataSource = DataSource.IOT,
ScoreTime = timeNow,
GradeId = student.GradeId,
ClassId = student.ClassId,
GradeName = student.GradeName,
ClassName = student.ClassName,
SchoolCode = UserContext.Current.TenantId,
Sex = student.Sex,
StudentNo = data.StudentNo,
StudentName = student.StudentName,
Creator = UserContext.Current.UserId,
CreateDate = timeNow,
IsDisplay = false
};
SexType sexType = student.Sex;
string sex = sexType == SexType.Male ? "男" : "女";
string typeStr = type.ToString();
// 根据测试项目类型设置对应的值
if (type == SportsTestItemType.BMI)
{
entity.Value = data.BMIValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.BMIValue >= x.MinValue &&
data.BMIValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
else if (type == SportsTestItemType.Sit_And_Reach)
{
entity.Value = data.Sit_And_ReachValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.Sit_And_ReachValue >= x.MinValue &&
data.Sit_And_ReachValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
else if (type == SportsTestItemType.VitalCapacity)
{
entity.Value = data.VitalCapacityValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.VitalCapacityValue >= x.MinValue &&
data.VitalCapacityValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
else if (type == SportsTestItemType.OneMinuteJumpRope)
{
entity.Value = data.OneMinuteJumpRopeValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.OneMinuteJumpRopeValue >= x.MinValue &&
data.OneMinuteJumpRopeValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
else if (type == SportsTestItemType.One_Minute_Sit_Up)
{
entity.Value = data.One_Minute_Sit_UpValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.One_Minute_Sit_UpValue >= x.MinValue &&
data.One_Minute_Sit_UpValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
else if (type == SportsTestItemType.StandingLongJump)
{
entity.Value = data.StandingLongJumpValue;
// 根据测试项目查找标准
var standard = await _healthStandardsRepository.FindFirstAsync(x =>
x.CategoryEnum.Equals(typeStr) &&
x.GradeId == gradeId &&
x.Sex == sexType &&
data.StandingLongJumpValue >= x.MinValue &&
data.StandingLongJumpValue < x.MaxValue
);
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
}
}
entity.CategoryEnum = type.ToString();
entity.CategoryValue = (int)type;
return entity;
}
/// <summary>
/// 运动馆体测成绩分页列表
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<PageDataDto<SportsHallStudentsTestModel>> GetSportsHallStudentsTestPageList(SportsHallStudentsTestParam paramDto)
{
var res = new PageDataDto<SportsHallStudentsTestModel>();
var query = _sportsTestResultRepository.DbContext.Set<N_SportsTestValue>()
.Where(s => s.SchoolCode == UserContext.Current.TenantId);
// 条件过滤
if (paramDto.StartTime.HasValue)
query = query.Where(x => x.CreateDate >= paramDto.StartTime.Value);
if (paramDto.EndTime.HasValue)
query = query.Where(x => x.CreateDate <= paramDto.EndTime.Value);
if (paramDto.ClassId.HasValue)
query = query.Where(x => x.ClassId == paramDto.ClassId.Value);
if (paramDto.Sex.HasValue)
query = query.Where(x => (int)x.Sex == paramDto.Sex);
if (!string.IsNullOrWhiteSpace(paramDto.ResultLevel))
query = query.Where(x => x.Rank == paramDto.ResultLevel);
// ✅ 正确分组(去掉 Id)
var groupedQuery = query.GroupBy(x => new
{
x.StudentNo,
x.StudentName,
x.Sex,
x.ClassName,
x.GradeName,
x.ScoreTime,
x.IsDisplay
});
res.Total = await groupedQuery.CountAsync();
var list = await groupedQuery
.Select(g => new SportsHallStudentsTestModel
{
// 👉 可以取组内任意一条的Id
Id = g.Max(x => x.Id),
StudentNo = g.Key.StudentNo,
StudentName = g.Key.StudentName,
SexName = g.Key.Sex.GetDescription(),
GradeAndClass = g.Key.GradeName + " " + g.Key.ClassName,
ScoreTime = g.Key.ScoreTime,
IsDisplay = g.Key.IsDisplay,
// 👇 pivot(用 Max 比 FirstOrDefault 更稳)
BMIValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.BMI)
.Max(x => (float?)x.Value) ?? 0,
VitalCapacityValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.VitalCapacity)
.Max(x => (float?)x.Value) ?? 0,
Sit_And_ReachValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.Sit_And_Reach)
.Max(x => (float?)x.Value) ?? 0,
OneMinuteJumpRopeValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.OneMinuteJumpRope)
.Max(x => (int?)x.Value) ?? 0,
One_Minute_Sit_UpValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.One_Minute_Sit_Up)
.Max(x => (int?)x.Value) ?? 0,
StandingLongJumpValue = g.Where(x => x.CategoryValue == (int)SportsTestItemType.StandingLongJump)
.Max(x => (float?)x.Value) ?? 0
})
.OrderByDescending(x => x.ScoreTime)
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.ToListAsync();
res.Datas = list;
return res;
}
/// <summary>
/// 成绩推送
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task ScorePush(SportsHallStudentsTestParam paramDto)
{
var sql = new StringBuilder();
sql.Append("UPDATE N_SportsTestValue SET IsDisplay = 1 WHERE SchoolCode = {0} ");
var parameters = new List<object>
{
UserContext.Current.TenantId
};
if (paramDto.StartTime.HasValue)
{
sql.Append(" AND CreateDate >= {1} ");
parameters.Add(paramDto.StartTime.Value);
}
if (paramDto.EndTime.HasValue)
{
sql.Append(" AND CreateDate <= {2} ");
parameters.Add(paramDto.EndTime.Value);
}
if (paramDto.ClassId > 0)
{
sql.Append(" AND ClassId = {3} ");
parameters.Add(paramDto.ClassId.Value);
}
if (paramDto.Sex > 0)
{
sql.Append(" AND Sex = {4} ");
parameters.Add(paramDto.Sex.Value);
}
if (!string.IsNullOrWhiteSpace(paramDto.ResultLevel))
{
sql.Append(" AND Rank = {5} ");
parameters.Add(paramDto.ResultLevel);
}
await _sportsTestResultRepository.DbContext.Database
.ExecuteSqlRawAsync(sql.ToString(), parameters.ToArray());
}
/// <summary>
/// 成绩修成
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task ScoreUpdate(SportsHallStudentsTestDataUpdateParam paramDto)
{
// 获取该学生每个项目的最新一条记录
var list = await _sportsTestResultRepository.DbContext.Set<N_SportsTestValue>()
.Where(s => s.SchoolCode == "202506170049" && s.StudentNo == paramDto.StudentNo)
.GroupBy(x => x.CategoryValue)
.Select(g => g.OrderByDescending(x => x.ScoreTime).FirstOrDefault())
.ToListAsync();
if (list == null || !list.Any())
return;
foreach (var entity in list)
{
switch (entity.CategoryValue)
{
case (int)SportsTestItemType.BMI:
entity.Value = paramDto.BMIValue;
break;
case (int)SportsTestItemType.VitalCapacity:
entity.Value = paramDto.VitalCapacityValue;
break;
case (int)SportsTestItemType.Sit_And_Reach:
entity.Value = paramDto.Sit_And_ReachValue;
break;
case (int)SportsTestItemType.OneMinuteJumpRope:
entity.Value = paramDto.OneMinuteJumpRopeValue;
break;
case (int)SportsTestItemType.One_Minute_Sit_Up:
entity.Value = paramDto.One_Minute_Sit_UpValue;
break;
case (int)SportsTestItemType.StandingLongJump:
entity.Value = paramDto.StandingLongJumpValue;
break;
}
}
_sportsTestResultRepository.UpdateRange(list);
await _sportsTestResultRepository.DbContext.SaveChangesAsync();
}
}
}