884 lines
38 KiB
C#
884 lines
38 KiB
C#
using AutoMapper;
|
||
using Castle.DynamicProxy.Generators;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Newtonsoft.Json;
|
||
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
|
||
using System.Threading;
|
||
using VOL.Business.IServices.Norm;
|
||
using VOL.Business.IServices.School;
|
||
using VOL.Core.CacheManager;
|
||
using VOL.Core.Configuration;
|
||
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;
|
||
using VOL.Model.Ai;
|
||
using VOL.Model.IOT.Request;
|
||
using VOL.Model.Norm.Response;
|
||
using VOL.Model.School.Request;
|
||
using VOL.Model.School.Response;
|
||
using VOL.System.IRepositories;
|
||
using VOL.System.Repositories;
|
||
using static Dapper.SqlMapper;
|
||
using static System.Formats.Asn1.AsnWriter;
|
||
|
||
namespace VOL.Business.Services.School
|
||
{
|
||
public class S_ClassService : IS_ClassService, IDependency
|
||
{
|
||
#region 初始化
|
||
private readonly IMapper _mapper;
|
||
private readonly ICacheService _cacheService;
|
||
private readonly ICacheQueryService _cacheQueryService;
|
||
private readonly IotDataSyncService _iotDataSyncService;
|
||
private readonly IS_ClassRepository _classRepository;
|
||
private readonly IS_StudentRepository _studentRepository;
|
||
private readonly IS_ClassAssocTeacherRepository _classAssocTeacherRepository;
|
||
private readonly IN_SportsTestResultRepository _sportsTestResultRepository;
|
||
private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository;
|
||
|
||
[ActivatorUtilitiesConstructor]
|
||
public S_ClassService(IMapper mapper,
|
||
ICacheService cacheService,
|
||
ICacheQueryService cacheQueryService,
|
||
IS_ClassRepository classRepository,
|
||
IS_ClassAssocTeacherRepository classAssocTeacherRepository,
|
||
IS_StudentRepository studentRepository,
|
||
IN_SportsTestResultRepository sportsTestResultRepository,
|
||
IN_SportsTestCategoryRepository sportsTestCategoryRepository,
|
||
IotDataSyncService iotDataSyncService)
|
||
{
|
||
_mapper = mapper;
|
||
_cacheService = cacheService;
|
||
_classRepository = classRepository;
|
||
_cacheQueryService = cacheQueryService;
|
||
_classAssocTeacherRepository = classAssocTeacherRepository;
|
||
_studentRepository = studentRepository;
|
||
_sportsTestResultRepository = sportsTestResultRepository;
|
||
_sportsTestCategoryRepository = sportsTestCategoryRepository;
|
||
_iotDataSyncService = iotDataSyncService;
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 根据年级Id获取所有班级
|
||
/// </summary>
|
||
/// <param name="gradeId"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<List<ClassNameModel>> GetClassNames(int gradeId)
|
||
{
|
||
var list = await (
|
||
from g in _classRepository.DbContext.Set<S_Class>()
|
||
where g.SchoolCode.Equals(UserContext.Current.TenantId) && g.GradeId == gradeId
|
||
select new ClassNameModel()
|
||
{
|
||
Id = g.Id,
|
||
ClassName = g.ClassName
|
||
}).OrderBy(x => x.Id).ToListAsync();
|
||
return list;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取班级列表
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<PageDataDto<ClassPageListModel>> GetClassPageList(ClassPageListParam paramDto)
|
||
{
|
||
var tenantId = UserContext.Current.TenantId;
|
||
|
||
var teacherClassIds = new List<int>();
|
||
var isTeacher = (UserContext.Current.RoleId == 3);
|
||
|
||
if (isTeacher)
|
||
{
|
||
var teacher = await _classRepository.DbContext.Set<S_Teacher>().FirstOrDefaultAsync(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
|
||
|
||
var teacherClassList = await (
|
||
from at in _classRepository.DbContext.Set<S_ClassAssocTeacher>()
|
||
join c in _classRepository.DbContext.Set<S_Class>() on at.ClassId equals c.Id
|
||
where at.TeacherId == teacher.Id
|
||
select c).ToListAsync();
|
||
|
||
teacherClassIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
|
||
}
|
||
|
||
// 基础班级和年级查询
|
||
var query = (from c in _classRepository.DbContext.Set<S_Class>()
|
||
join g in _classRepository.DbContext.Set<S_Grade>() on c.GradeId equals g.Id
|
||
join a in _classRepository.DbContext.Set<S_SchoolAssocGrade>() on g.Id equals a.GradeId
|
||
where c.SchoolCode == tenantId && (!isTeacher || teacherClassIds.Contains(c.Id))
|
||
select new
|
||
{
|
||
c.Id,
|
||
c.ClassName,
|
||
c.GradeId,
|
||
g.GradeName
|
||
}).Distinct();
|
||
|
||
// 条件筛选
|
||
if (!string.IsNullOrWhiteSpace(paramDto.ClassName))
|
||
{
|
||
query = query.Where(x => x.ClassName.Contains(paramDto.ClassName));
|
||
}
|
||
if (paramDto.GradeId > 0)
|
||
{
|
||
query = query.Where(x => x.GradeId == paramDto.GradeId);
|
||
}
|
||
|
||
// 获取班级ID列表
|
||
var classIds = await query.Select(x => x.Id).ToListAsync();
|
||
|
||
// 查询教师信息并拼接 TeacherName 和 TeacherPhoneNo
|
||
var teacherInfos = await (from assoc in _classRepository.DbContext.Set<S_ClassAssocTeacher>()
|
||
join t in _classRepository.DbContext.Set<S_Teacher>() on assoc.TeacherId equals t.Id
|
||
where classIds.Contains(assoc.ClassId) && t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart
|
||
group t by assoc.ClassId into groupedTeachers
|
||
select new
|
||
{
|
||
ClassId = groupedTeachers.Key,
|
||
TeacherNames = string.Join(", ", groupedTeachers.Select(t => t.TeacherName)),
|
||
TeacherPhoneNos = string.Join(", ", groupedTeachers.Select(t => t.TeacherPhoneNo))
|
||
}).ToListAsync();
|
||
|
||
// 查询学生数量
|
||
var studentCounts = await (from s in _classRepository.DbContext.Set<S_Student>()
|
||
where classIds.Contains(s.ClassId) && s.SchoolCode == tenantId
|
||
group s by s.ClassId into groupedStudents
|
||
select new
|
||
{
|
||
ClassId = groupedStudents.Key,
|
||
Count = groupedStudents.Count()
|
||
}).ToListAsync();
|
||
|
||
// 合并数据
|
||
var list = await query
|
||
.OrderBy(x => x.Id)
|
||
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
|
||
.Take(paramDto.PageSize)
|
||
.ToListAsync();
|
||
|
||
var result = list.Select(x =>
|
||
{
|
||
var teacherInfo = teacherInfos.FirstOrDefault(t => t.ClassId == x.Id);
|
||
var studentCount = studentCounts.FirstOrDefault(s => s.ClassId == x.Id)?.Count ?? 0;
|
||
|
||
return new ClassPageListModel
|
||
{
|
||
Id = x.Id,
|
||
ClassName = x.ClassName,
|
||
GradeId = x.GradeId,
|
||
GradeName = x.GradeName,
|
||
TeacherName = teacherInfo?.TeacherNames ?? "无",
|
||
TeacherPhoneNo = teacherInfo?.TeacherPhoneNos ?? "无",
|
||
StudentCount = studentCount
|
||
};
|
||
}).ToList();
|
||
|
||
// 结果包装
|
||
return new PageDataDto<ClassPageListModel>
|
||
{
|
||
Total = classIds.Count,
|
||
Datas = result
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出班级
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<List<ClassPageListModel>> GetClassList(ClassPageListExportParam paramDto)
|
||
{
|
||
var tenantId = UserContext.Current.TenantId;
|
||
|
||
var teacherClassIds = new List<int>();
|
||
var isTeacher = (UserContext.Current.RoleId == 3);
|
||
|
||
if (isTeacher)
|
||
{
|
||
var teacher = await _classRepository.DbContext.Set<S_Teacher>().FirstOrDefaultAsync(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
|
||
|
||
var teacherClassList = await (
|
||
from at in _classRepository.DbContext.Set<S_ClassAssocTeacher>()
|
||
join c in _classRepository.DbContext.Set<S_Class>() on at.ClassId equals c.Id
|
||
where at.TeacherId == teacher.Id
|
||
select c).ToListAsync();
|
||
|
||
teacherClassIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
|
||
}
|
||
var query = (from c in _classRepository.DbContext.Set<S_Class>()
|
||
join g in _classRepository.DbContext.Set<S_Grade>() on c.GradeId equals g.Id
|
||
join a in _classRepository.DbContext.Set<S_SchoolAssocGrade>() on g.Id equals a.GradeId
|
||
where c.SchoolCode == tenantId && (!isTeacher || teacherClassIds.Contains(c.Id))
|
||
select new
|
||
{
|
||
c.Id,
|
||
c.ClassName,
|
||
c.GradeId,
|
||
g.GradeName
|
||
}).Distinct();
|
||
|
||
// 条件筛选
|
||
if (!string.IsNullOrWhiteSpace(paramDto.ClassName))
|
||
{
|
||
query = query.Where(x => x.ClassName.Contains(paramDto.ClassName));
|
||
}
|
||
if (paramDto.GradeId > 0)
|
||
{
|
||
query = query.Where(x => x.GradeId == paramDto.GradeId);
|
||
}
|
||
|
||
// 获取班级ID列表
|
||
var classIds = await query.Select(x => x.Id).ToListAsync();
|
||
|
||
// 查询教师信息并拼接 TeacherName 和 TeacherPhoneNo
|
||
var teacherInfos = await (from assoc in _classRepository.DbContext.Set<S_ClassAssocTeacher>()
|
||
join t in _classRepository.DbContext.Set<S_Teacher>() on assoc.TeacherId equals t.Id
|
||
where classIds.Contains(assoc.ClassId) && t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart
|
||
group t by assoc.ClassId into groupedTeachers
|
||
select new
|
||
{
|
||
ClassId = groupedTeachers.Key,
|
||
TeacherNames = string.Join(", ", groupedTeachers.Select(t => t.TeacherName)),
|
||
TeacherPhoneNos = string.Join(", ", groupedTeachers.Select(t => t.TeacherPhoneNo))
|
||
}).ToListAsync();
|
||
|
||
// 查询学生数量
|
||
var studentCounts = await (from s in _classRepository.DbContext.Set<S_Student>()
|
||
where classIds.Contains(s.ClassId) && s.SchoolCode == tenantId
|
||
group s by s.ClassId into groupedStudents
|
||
select new
|
||
{
|
||
ClassId = groupedStudents.Key,
|
||
Count = groupedStudents.Count()
|
||
}).ToListAsync();
|
||
|
||
|
||
var list = await query.OrderBy(x => x.Id).ToListAsync();
|
||
|
||
var result = list.Select(x =>
|
||
{
|
||
var teacherInfo = teacherInfos.FirstOrDefault(t => t.ClassId == x.Id);
|
||
var studentCount = studentCounts.FirstOrDefault(s => s.ClassId == x.Id)?.Count ?? 0;
|
||
|
||
return new ClassPageListModel
|
||
{
|
||
Id = x.Id,
|
||
ClassName = x.ClassName,
|
||
GradeId = x.GradeId,
|
||
GradeName = x.GradeName,
|
||
TeacherName = teacherInfo?.TeacherNames ?? "无",
|
||
TeacherPhoneNo = teacherInfo?.TeacherPhoneNos ?? "无",
|
||
StudentCount = studentCount
|
||
};
|
||
}).ToList();
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 班级详情
|
||
/// </summary>
|
||
/// <param name="classId"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<ClassDetailsModel> GetClassDetails(int classId)
|
||
{
|
||
var classModel = await _classRepository.FindAsyncFirst(x => x.Id == classId);
|
||
|
||
if (classModel == null)
|
||
throw new ArgumentNullException("数据不存在");
|
||
|
||
var teacherList = await _classAssocTeacherRepository.FindAsIQueryable(x => x.ClassId == classId).Select(x => x.TeacherId).ToListAsync();
|
||
|
||
return new ClassDetailsModel()
|
||
{
|
||
ClassId = classModel.Id,
|
||
GradeId = classModel.GradeId,
|
||
TeacherIds = teacherList
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新班级
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task ModifyClass(ModifyClassParam paramDto)
|
||
{
|
||
var tenantId = UserContext.Current.TenantId;
|
||
var classModel = await _classRepository.FindAsyncFirst(x => x.Id == paramDto.ClassId);
|
||
|
||
if (classModel == null)
|
||
throw new ArgumentNullException("未找到要更新得数据");
|
||
var exist = await _classRepository.ExistsAsync(
|
||
x => x.SchoolCode == tenantId
|
||
&& x.ClassName == paramDto.ClassName
|
||
&& x.GradeId == paramDto.GradeId
|
||
&& 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);
|
||
|
||
await _classRepository.SaveChangesAsync();
|
||
|
||
// 提交事务
|
||
transaction.Commit();
|
||
|
||
|
||
//调用回调函数,同步数据到IOT
|
||
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
//{
|
||
// EventType = EventType.Update.GetDisplayName(),
|
||
// DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
// Json = JsonConvert.SerializeObject(classModel)
|
||
//}));
|
||
|
||
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
{
|
||
EventType = EventType.Update.GetDisplayName(),
|
||
DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
Json = JsonConvert.SerializeObject(classModel)
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 发生错误,回滚事务
|
||
transaction.Rollback();
|
||
Console.WriteLine("事务回滚:" + ex.Message);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加班级
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task AddClass(AddClassParam paramDto)
|
||
{
|
||
var tenantId = UserContext.Current.TenantId;
|
||
|
||
var exist = await _classRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.ClassName == paramDto.ClassName && x.GradeId == paramDto.GradeId);
|
||
if (exist)
|
||
throw new Exception("班级名称已存在");
|
||
|
||
var classEntity = new S_Class()
|
||
{
|
||
GradeId = paramDto.GradeId,
|
||
GradeName = paramDto.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);
|
||
|
||
await _classRepository.SaveChangesAsync();
|
||
|
||
// 提交事务
|
||
transaction.Commit();
|
||
|
||
//调用回调函数,同步数据到IOT
|
||
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
//{
|
||
// EventType = EventType.Add.GetDisplayName(),
|
||
// DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
// Json = JsonConvert.SerializeObject(new List<S_Class>() { classEntity })
|
||
//}));
|
||
|
||
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
{
|
||
EventType = EventType.Add.GetDisplayName(),
|
||
DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
Json = JsonConvert.SerializeObject(new List<S_Class>() { classEntity })
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 发生错误,回滚事务
|
||
transaction.Rollback();
|
||
Console.WriteLine("事务回滚:" + ex.Message);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量添加班级
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task BatchAddClass(BatchAddClassParam paramDto)
|
||
{
|
||
var tenantId = UserContext.Current.TenantId;
|
||
|
||
var classEntityList = new List<S_Class>();
|
||
var nowTime = DateTime.Now;
|
||
|
||
for (int i = 0; i < paramDto.ClassCount; i++)
|
||
{
|
||
var className = $"{(i + 1).ToChineseNumber()}班";
|
||
|
||
var exist = await _classRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.ClassName == className && x.GradeId == paramDto.GradeId);
|
||
if (exist) continue;
|
||
|
||
var classEntity = new S_Class()
|
||
{
|
||
GradeId = paramDto.GradeId,
|
||
GradeName = paramDto.GradeName,
|
||
ClassName = className,
|
||
SchoolCode = UserContext.Current.TenantId,
|
||
Creator = UserContext.Current.UserId,
|
||
CreateDate = nowTime
|
||
};
|
||
|
||
classEntityList.Add(classEntity);
|
||
}
|
||
|
||
using (var transaction = _classRepository.DbContext.Database.BeginTransaction())
|
||
{
|
||
try
|
||
{
|
||
await _classRepository.AddRangeAsync(classEntityList);
|
||
await _classRepository.SaveChangesAsync();
|
||
|
||
var addedRecordIds = classEntityList.Select(c => c.Id).ToList();
|
||
|
||
var classAssocTeacherList = new List<S_ClassAssocTeacher>();
|
||
|
||
addedRecordIds.ForEach(c =>
|
||
{
|
||
paramDto.TeacherIds.ForEach(x =>
|
||
{
|
||
classAssocTeacherList.Add(new S_ClassAssocTeacher()
|
||
{
|
||
ClassId = c,
|
||
TeacherId = x,
|
||
Creator = UserContext.Current.UserId,
|
||
CreateDate = DateTime.Now,
|
||
Modifier = UserContext.Current.UserId,
|
||
ModifyDate = DateTime.Now,
|
||
});
|
||
});
|
||
});
|
||
|
||
await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList);
|
||
|
||
await _classRepository.SaveChangesAsync();
|
||
|
||
// 提交事务
|
||
transaction.Commit();
|
||
|
||
//调用回调函数,同步数据到IOT
|
||
//_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
//{
|
||
// EventType = EventType.Update.GetDisplayName(),
|
||
// DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
// Json = JsonConvert.SerializeObject(classEntityList)
|
||
//}));
|
||
|
||
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
|
||
{
|
||
EventType = EventType.Update.GetDisplayName(),
|
||
DataType = IOTDataSyncType.Class.GetDisplayName(),
|
||
Json = JsonConvert.SerializeObject(classEntityList)
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 发生错误,回滚事务
|
||
transaction.Rollback();
|
||
Console.WriteLine("事务回滚:" + ex.Message);
|
||
}
|
||
}
|
||
}
|
||
|
||
public async Task<ClassDataStatsModel> ClassWholeDataStats(ClassDataStatsParam paramDto)
|
||
{
|
||
var res = new ClassDataStatsModel();
|
||
var tenantId = UserContext.Current.TenantId;
|
||
|
||
if (!paramDto.ClassId.HasValue || paramDto.ClassId <= 0)
|
||
{
|
||
paramDto.ClassId = this._classRepository.DbContext.Set<S_Class>().First().Id;
|
||
}
|
||
|
||
var classModel = await (from c in _classRepository.DbContext.Set<S_Class>()
|
||
join a in _classRepository.DbContext.Set<S_ClassAssocTeacher>() on c.Id equals a.ClassId into associations
|
||
from association in associations.DefaultIfEmpty()
|
||
join t in _classRepository.DbContext.Set<S_Teacher>() on association.TeacherId equals t.Id into teachers
|
||
from teacher in teachers.DefaultIfEmpty()
|
||
|
||
where c.Id == paramDto.ClassId && c.SchoolCode.Equals(UserContext.Current.TenantId)
|
||
|
||
select new ClassPageListModel()
|
||
{
|
||
Id = c.Id,
|
||
GradeId = c.GradeId,
|
||
GradeName = c.GradeName,
|
||
ClassName = c.ClassName,
|
||
TeacherName = teacher.TeacherName,
|
||
TeacherPhoneNo = teacher.TeacherPhoneNo
|
||
}).FirstOrDefaultAsync();
|
||
|
||
|
||
if (classModel == null)
|
||
throw new ArgumentNullException("未找到班级数据");
|
||
|
||
res.TeacherName = classModel.TeacherName;
|
||
res.ClassName = classModel.ClassName;
|
||
|
||
var students = await _studentRepository.FindAsIQueryable(x => x.ClassId == paramDto.ClassId)
|
||
.Select(x => new
|
||
{
|
||
x.ClassId,
|
||
x.StudentNo,
|
||
x.StudentName,
|
||
x.ClassName,
|
||
x.Sex,
|
||
x.Age
|
||
}).ToListAsync();
|
||
|
||
res.StudentCount = students.Count;
|
||
|
||
// 从缓存中获取数据
|
||
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync
|
||
(
|
||
x => x.SchoolCode.Equals(tenantId) && x.ClassId == paramDto.ClassId
|
||
, paramDto.Mode
|
||
);
|
||
|
||
var monitorList = sportsTestResults
|
||
.GroupBy(x => new { x.StudentNo })
|
||
.Select(g => new
|
||
{
|
||
g.Key.StudentNo,
|
||
g.First().Sex,
|
||
g.First().ClassId,
|
||
g.First().ClassName,
|
||
g.First().GradeId,
|
||
g.First().GradeName,
|
||
g.First().TeacherId,
|
||
g.First().TeacherName,
|
||
g.First().ClassRoomRecordId,
|
||
Score = (g.Sum(x => x.Score) + g.Sum(x => x.AdditionalScore)) / (g.Select(x => x.CategoryEnum).Distinct().Count())
|
||
})
|
||
.Select(x => new
|
||
{
|
||
x.StudentNo,
|
||
x.Sex,
|
||
x.ClassId,
|
||
x.ClassName,
|
||
x.GradeId,
|
||
x.GradeName,
|
||
x.TeacherId,
|
||
x.TeacherName,
|
||
x.ClassRoomRecordId,
|
||
x.Score,
|
||
Rank = x.Score.GetRank()
|
||
}).ToList();
|
||
|
||
// 计算总人数
|
||
double totalCount = monitorList.Count();
|
||
res.ExcellentRate = Math.Round((monitorList.Count(x => x.Rank == "优秀") / totalCount) * 100);
|
||
res.FineRate = Math.Round((monitorList.Count(x => x.Rank == "良好") / totalCount) * 100);
|
||
res.PassRate = Math.Round((monitorList.Count(x => x.Rank == "及格") / totalCount) * 100);
|
||
//res.FailRate = totalCount > 0 ? Math.Round((monitorList.Count(x => x.Rank == "不及格") / totalCount) * 100) : 0;
|
||
|
||
if (res != null)
|
||
{
|
||
// 计算并调整最后一个百分比(确保总和为100%)
|
||
double sum = (res.ExcellentRate + res.FineRate + res.PassRate + res.FailRate);
|
||
double adjustment = 100 - sum;
|
||
res.FailRate += totalCount > 0 ? adjustment : 0;
|
||
}
|
||
|
||
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 }
|
||
};
|
||
|
||
res.TestResultAvg = 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)
|
||
.ToList();
|
||
return filteredResults.Any() ? filteredResults.Average() : 0;
|
||
}
|
||
);
|
||
|
||
foreach (var stu in students)
|
||
{
|
||
var stuResults = monitorList.FirstOrDefault(c => c.StudentNo == stu.StudentNo);
|
||
|
||
res.ClassDetailsList.Add(new ClassDetails()
|
||
{
|
||
StudentNo = stu.StudentNo,
|
||
StudentName = stu.StudentName,
|
||
Sex = stu.Sex == SexType.Male ? "男" : "女",
|
||
GradeAndClassName = $"{classModel.GradeName}-{classModel.ClassName}",
|
||
GradeId = classModel.GradeId,
|
||
ClassId = classModel.Id,
|
||
Rank = stuResults.Rank
|
||
});
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 各体测项目等级占比
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<Dictionary<string, float>> CategoryRankRatio(CategoryParam paramDto)
|
||
{
|
||
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
||
x.GradeId == paramDto.GradeId &&
|
||
x.ClassId == paramDto.ClassId &&
|
||
x.SchoolCode.Equals(UserContext.Current.TenantId) &&
|
||
x.CategoryValue == paramDto.CategoryValue
|
||
, paramDto.Mode);
|
||
|
||
var result = sportsTestResults
|
||
.Where(x => x.CategoryValue == paramDto.CategoryValue)
|
||
.GroupBy(x => x.Rank)
|
||
.ToDictionary(g => g.Key, g => (float)g.Count());
|
||
|
||
int totalCount = sportsTestResults.Count(x => x.CategoryValue == paramDto.CategoryValue);
|
||
|
||
if (totalCount == 0)
|
||
{
|
||
return new Dictionary<string, float>();
|
||
}
|
||
|
||
// 计算比例
|
||
foreach (var key in result.Keys.ToList())
|
||
{
|
||
result[key] = (float)Math.Round(result[key] / totalCount, 2);
|
||
}
|
||
|
||
if (result != null)
|
||
{
|
||
// 计算并调整最后一个百分比(确保总和为100%)
|
||
var sum = result.Values.Sum(c => c);
|
||
var adjustment = 1 - sum;
|
||
var lastKey = result.Keys.Last();
|
||
result[lastKey] += adjustment;
|
||
|
||
// 如果最后一个值小于0.01,移除这一项
|
||
if (result[lastKey] < 0.01)
|
||
{
|
||
result.Remove(lastKey);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 班级体侧等级占比人数
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<ExcellentRateChart> TestResultRankRate(ClassResultRankRate paramDto)
|
||
{
|
||
var res = new ExcellentRateChart();
|
||
|
||
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
||
x.GradeId == paramDto.GradeId &&
|
||
x.ClassId == paramDto.ClassId &&
|
||
x.RankEnum == paramDto.Rank &&
|
||
x.SchoolCode.Equals(UserContext.Current.TenantId)
|
||
, 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 == paramDto.GradeId
|
||
select new
|
||
{
|
||
n.CategoryValue,
|
||
n.CategoryName
|
||
}).ToListAsync();
|
||
|
||
foreach (var category in categoryList)
|
||
{
|
||
var categoryResults = sportsTestResults.Where(x => x.CategoryValue == category.CategoryValue).ToList();
|
||
|
||
var sexAndOverall = new SexAndOverall
|
||
{
|
||
FemaleValue = categoryResults.Count(x => x.Sex == SexType.Female),
|
||
MaleValue = categoryResults.Count(x => x.Sex == SexType.Male),
|
||
OverallValue = categoryResults.Count()
|
||
};
|
||
|
||
res.AxisX.Add(category.CategoryName);
|
||
res.AxisY.Add(sexAndOverall);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 成绩趋势
|
||
/// </summary>
|
||
/// <param name="paramDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public async Task<VariousSportsProportion> ResultTrends(ClassResultTrendsParam paramDto)
|
||
{
|
||
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
|
||
x.GradeId == paramDto.GradeId &&
|
||
x.ClassId == paramDto.ClassId &&
|
||
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;
|
||
}
|
||
}
|
||
} |