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 System.Linq; 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; 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.Ai; using VOL.Model.IOT.Request; using VOL.Model.Norm.Response; using VOL.Model.School.Response; using VOL.System.IRepositories; using VOL.System.Repositories; using static Dapper.SqlMapper; namespace VOL.Business.Services.School { public class S_TeacherService : IS_TeacherService, IDependency { #region 初始化 private readonly IMapper _mapper; private readonly ICacheService _cacheService; private readonly ICacheQueryService _cacheQueryService; private readonly IotDataSyncService _iotDataSyncService; private readonly IS_TeacherRepository _teacherRepository; private readonly ISys_UserRepository _sys_UserRepository; private readonly IS_ClassAssocTeacherRepository _classAssocTeacherRepository; private readonly IS_StudentRepository _studentRepository; private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository; private readonly IN_SportsTestResultRepository _sportsTestResultRepository; private readonly IS_ClassRepository _classRepository; [ActivatorUtilitiesConstructor] public S_TeacherService(IMapper mapper, ICacheService cacheService, ICacheQueryService cacheQueryService, IS_TeacherRepository teacherRepository, ISys_UserRepository sys_UserRepository, IS_ClassAssocTeacherRepository classAssocTeacherRepository, IS_StudentRepository studentRepository, IN_SportsTestCategoryRepository sportsTestCategoryRepository, IN_SportsTestResultRepository sportsTestResultRepository, IS_ClassRepository classRepository, IotDataSyncService iotDataSyncService) { _mapper = mapper; _cacheService = cacheService; _cacheQueryService = cacheQueryService; _teacherRepository = teacherRepository; _sys_UserRepository = sys_UserRepository; _classAssocTeacherRepository = classAssocTeacherRepository; _studentRepository = studentRepository; _sportsTestCategoryRepository = sportsTestCategoryRepository; _sportsTestResultRepository = sportsTestResultRepository; _classRepository = classRepository; _iotDataSyncService = iotDataSyncService; } #endregion public async Task> GetTeacherNames() { var isTeacher = (UserContext.Current.RoleId == 3); var list = await _teacherRepository.FindAsIQueryable(x => x.SchoolCode.Equals(UserContext.Current.TenantId) && x.TeacherStatus != TeacherStatus.Depart && (!isTeacher || x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo) ).Select(x => new TeacherNameModel() { Id = x.Id, TeacherPhone = x.TeacherPhoneNo, TeacherName = x.TeacherName }).ToListAsync(); return list; } public async Task> GetTeacherNamesByGradeId(TeacherListByGradeIdParam paramDto) { var query = from t in _teacherRepository.DbContext.Set() join a in _teacherRepository.DbContext.Set() on t.Id equals a.TeacherId into assoc from a in assoc.DefaultIfEmpty() join c in _teacherRepository.DbContext.Set() on a.ClassId equals c.Id into classes from c in classes.DefaultIfEmpty() where t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart select new TeacherListByGradeIdModel() { Id = t.Id, TeacherName = t.TeacherName, TeacherPhone = t.TeacherPhoneNo, GradeId = c != null ? c.GradeId : 0, ClassId = c != null ? c.Id : 0 }; if (paramDto.GradeId.HasValue) { query = query.Where(x => x.GradeId == paramDto.GradeId); } if (paramDto.ClassId.HasValue) { query = query.Where(x => x.ClassId == paramDto.ClassId); } var list = await query .GroupBy(x => x.Id) .Select(g => new TeacherListByGradeIdModel { Id = g.Key, TeacherName = g.FirstOrDefault().TeacherName, TeacherPhone = g.FirstOrDefault().TeacherPhone, GradeId = g.FirstOrDefault().GradeId, ClassId = g.FirstOrDefault().ClassId }) .OrderBy(x => x.Id) .ToListAsync(); return list; } public async Task> GetTeacherList(TeacherExportParam paramDto) { var res = new PageDataDto(); // 主查询,仅查询教师表 var query = _teacherRepository.DbContext.Set() .Where(t => t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart) .Select(t => new TeacherPageListModel() { Id = t.Id, PhoneNo = t.TeacherPhoneNo, TeacherName = t.TeacherName, TeacherStatus = t.TeacherStatus, InductionDate = t.InductionDate, Sex = t.Sex }); // 过滤条件 if (!string.IsNullOrWhiteSpace(paramDto.TeacherName)) { query = query.Where(x => x.TeacherName.Contains(paramDto.TeacherName)); } if (!string.IsNullOrWhiteSpace(paramDto.PhoneNo)) { query = query.Where(x => x.PhoneNo.Contains(paramDto.PhoneNo)); } if (paramDto.Sex > 0) { query = query.Where(x => x.Sex == paramDto.Sex); } if (paramDto.TeacherStatus > 0) { query = query.Where(x => x.TeacherStatus == paramDto.TeacherStatus); } var list = await query.OrderBy(x => x.Id).ToListAsync(); // 统计每个教师的班级和学生数量 foreach (var item in list) { var assocTeachers = _teacherRepository.DbContext.Set() .Where(at => at.TeacherId == item.Id); // 学生数量 item.StudentCount = (from at in assocTeachers join s in _teacherRepository.DbContext.Set() on at.ClassId equals s.ClassId select s.Id).Distinct().Count(); // 班级数量 item.ClassCount = assocTeachers.Select(at => at.ClassId).Distinct().Count(); // 按年级分组的班级信息 var groupedClassIdsByGrade = (from at in assocTeachers join c in _teacherRepository.DbContext.Set() on at.ClassId equals c.Id group at.ClassId by c.GradeId into groupedData select new { GradeId = groupedData.Key, ClassIds = groupedData.ToList() }).ToList(); item.GradeAndClassIds = groupedClassIdsByGrade.ToDictionary(x => x.GradeId, x => x.ClassIds); } return list; } public async Task> GetTeacherPageList(TeacherPageListParam paramDto) { var res = new PageDataDto(); // 主查询,仅查询教师表 var query = _teacherRepository.DbContext.Set() .Where(t => t.SchoolCode.Equals(UserContext.Current.TenantId) && t.TeacherStatus != TeacherStatus.Depart) .Select(t => new TeacherPageListModel() { Id = t.Id, PhoneNo = t.TeacherPhoneNo, TeacherName = t.TeacherName, TeacherStatus = t.TeacherStatus, InductionDate = t.InductionDate, Sex = t.Sex, TeacherPhoto = t.TeacherPhoto, }); // 过滤条件 if (!string.IsNullOrWhiteSpace(paramDto.TeacherName)) { query = query.Where(x => x.TeacherName.Contains(paramDto.TeacherName)); } if (!string.IsNullOrWhiteSpace(paramDto.PhoneNo)) { query = query.Where(x => x.PhoneNo.Contains(paramDto.PhoneNo)); } if (paramDto.Sex > 0) { query = query.Where(x => x.Sex == paramDto.Sex); } if (paramDto.TeacherStatus > 0) { query = query.Where(x => x.TeacherStatus == paramDto.TeacherStatus); } // 获取总数用于分页 res.Total = await query.CountAsync(); // 分页数据 var list = await query .Skip((paramDto.PageIndex - 1) * paramDto.PageSize) .Take(paramDto.PageSize) .OrderBy(x => x.Id) .ToListAsync(); // 统计每个教师的班级和学生数量 foreach (var item in list) { var assocTeachers = _teacherRepository.DbContext.Set() .Where(at => at.TeacherId == item.Id); // 学生数量 item.StudentCount = (from at in assocTeachers join s in _teacherRepository.DbContext.Set() on at.ClassId equals s.ClassId select s.Id).Distinct().Count(); // 班级数量 item.ClassCount = assocTeachers.Select(at => at.ClassId).Distinct().Count(); // 按年级分组的班级信息 var groupedClassIdsByGrade = (from at in assocTeachers join c in _teacherRepository.DbContext.Set() on at.ClassId equals c.Id group at.ClassId by c.GradeId into groupedData select new { GradeId = groupedData.Key, ClassIds = groupedData.ToList() }).ToList(); item.GradeAndClassIds = groupedClassIdsByGrade.ToDictionary(x => x.GradeId, x => x.ClassIds); } res.Datas = list; return res; } public async Task AddTeacher(AddTeacherParam paramDto) { var tenantId = UserContext.Current.TenantId; var teacherExists = await _teacherRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == paramDto.TeacherPhoneNo); if (teacherExists) throw new Exception($"手机号{paramDto.TeacherPhoneNo}的老师已存在!"); var teacherEntity = _mapper.Map(paramDto); teacherEntity.TeacherStatus = TeacherStatus.Normal; teacherEntity.SchoolCode = UserContext.Current.TenantId; teacherEntity.Creator = UserContext.Current.UserId; teacherEntity.CreateDate = DateTime.Now; teacherEntity.AppletPwd = "000000"; var roleId = UserContext.Current.RoleId; using (var transaction = _teacherRepository.DbContext.Database.BeginTransaction()) { try { await _teacherRepository.AddAsync(teacherEntity); await _teacherRepository.SaveChangesAsync(); int teacherId = teacherEntity.Id; var userEntity = new Sys_User() { Role_Id = roleId == 2 ? 3 : 5, RoleName = "学校老师", PhoneNo = paramDto.TeacherPhoneNo, SchoolName = UserContext.Current.SchoolName, UserTrueName = paramDto.TeacherName, Enable = 1, UserName = paramDto.TeacherPhoneNo, //UserPwd = paramDto.Password.EncryptDES(AppSetting.Secret.User), UserPwd = paramDto.Password, CreateID = teacherId, SchoolCode = tenantId, CreateDate = DateTime.Now }; await _sys_UserRepository.AddAsync(userEntity); var classAssocTeacherList = new List(); if (paramDto.ClassIds != null && paramDto.ClassIds.Count > 0) { paramDto.ClassIds.ForEach(x => { classAssocTeacherList.Add(new S_ClassAssocTeacher() { ClassId = x, TeacherId = teacherId, Creator = UserContext.Current.UserId, CreateDate = DateTime.Now, Modifier = UserContext.Current.UserId, ModifyDate = DateTime.Now, }); }); await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherList); } await _sys_UserRepository.SaveChangesAsync(); // 提交事务 transaction.Commit(); var teacherJson = new object[] { new { teacherEntity.Id, teacherEntity.SchoolCode, teacherEntity.TeacherName, teacherEntity.Age, teacherEntity.Sex, teacherEntity.TeacherStatus, teacherEntity.TeacherPhoto, teacherEntity.InductionDate, teacherEntity.TeacherPhoneNo, teacherEntity.Ology, teacherEntity.AccumulatedTeachingTimes, teacherEntity.AppletPwd, teacherEntity.Remarks, paramDto.ClassIds } }; //调用回调函数,同步数据到IOT //_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam //{ // EventType = EventType.Add.GetDisplayName(), // DataType = IOTDataSyncType.Teacher.GetDisplayName(), // Json = JsonConvert.SerializeObject(new List() { teacherEntity }) //})); await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Add.GetDisplayName(), DataType = IOTDataSyncType.Teacher.GetDisplayName(), //Json = JsonConvert.SerializeObject(new List() { teacherJson }), Json = JsonConvert.SerializeObject(teacherJson) }); } catch (Exception ex) { transaction.Rollback(); throw new Exception($"新增失败:{ex.Message}"); } } } public async Task ModifyTeacher(AddTeacherParam paramDto) { var tenantId = UserContext.Current.TenantId; var teacherModel = await _teacherRepository.FindAsyncFirst(x => x.Id == paramDto.Id); if (teacherModel == null) throw new ArgumentNullException("未找到要更新的数据"); var teacherExists = await _teacherRepository.ExistsAsync(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == paramDto.TeacherPhoneNo && x.Id != teacherModel.Id); if (teacherExists) throw new Exception($"手机号{paramDto.TeacherPhoneNo}的老师已存在!"); teacherModel.TeacherName = paramDto.TeacherName; teacherModel.TeacherPhoneNo = paramDto.TeacherPhoneNo; teacherModel.TeacherPhoto = paramDto.TeacherPhoto; teacherModel.InductionDate = paramDto.InductionDate; teacherModel.Sex = paramDto.Sex; teacherModel.Modifier = UserContext.Current.UserId; teacherModel.ModifyDate = DateTime.Now; //var userModel = await _sys_UserRepository.FindAsyncFirst(x => x.PhoneNo == paramDto.TeacherPhoneNo); //if (userModel == null) // throw new ArgumentNullException("未找到要更新的数据"); //userModel.UserPwd = paramDto.Password; //userModel.PhoneNo = paramDto.TeacherPhoneNo; var classAssocTeacherList = await _classAssocTeacherRepository.FindAsync(x => x.TeacherId == teacherModel.Id); using (var transaction = _teacherRepository.DbContext.Database.BeginTransaction()) { try { _teacherRepository.Update(teacherModel); //_sys_UserRepository.Update(userModel); if (classAssocTeacherList.Count > 0) _classAssocTeacherRepository.DbContext.RemoveRange(classAssocTeacherList); var classAssocTeacherEntitys = new List(); paramDto.ClassIds.ForEach(x => { classAssocTeacherEntitys.Add(new S_ClassAssocTeacher() { ClassId = x, TeacherId = teacherModel.Id, Creator = teacherModel.Id, CreateDate = DateTime.Now, Modifier = teacherModel.Id, ModifyDate = DateTime.Now, }); }); if (classAssocTeacherEntitys.Count > 0) await _classAssocTeacherRepository.AddRangeAsync(classAssocTeacherEntitys); await _teacherRepository.SaveChangesAsync(); // 提交事务 transaction.Commit(); var teacherJson = new { teacherModel.Id, teacherModel.SchoolCode, teacherModel.TeacherName, teacherModel.Age, teacherModel.Sex, teacherModel.TeacherStatus, teacherModel.TeacherPhoto, teacherModel.InductionDate, teacherModel.TeacherPhoneNo, teacherModel.Ology, teacherModel.AccumulatedTeachingTimes, teacherModel.AppletPwd, teacherModel.Remarks, paramDto.ClassIds }; //调用回调函数,同步数据到IOT //_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam //{ // EventType = EventType.Update.GetDisplayName(), // DataType = IOTDataSyncType.Teacher.GetDisplayName(), // Json = JsonConvert.SerializeObject(teacherJson) //})); await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Update.GetDisplayName(), DataType = IOTDataSyncType.Teacher.GetDisplayName(), Json = JsonConvert.SerializeObject(teacherJson) }); } catch (Exception ex) { // 发生错误,回滚事务 transaction.Rollback(); throw new Exception("事务回滚:" + ex.Message); } } } public async Task UpdateTeacherStatus(UpdateTeacherStatusParam paramDto) { var teacher = await _teacherRepository.FindAsyncFirst(x => x.TeacherPhoneNo == paramDto.TeacherPhoneNo); if (teacher == null) throw new ArgumentNullException("未找到要更新的数据"); teacher.TeacherStatus = paramDto.Status; _studentRepository.Update(teacher); await _studentRepository.SaveChangesAsync(); var classIds = await _classAssocTeacherRepository.FindAsIQueryable(x => x.TeacherId == teacher.Id).Select(x => x.ClassId).ToListAsync(); var teacherJson = new { teacher.Id, teacher.SchoolCode, teacher.TeacherName, teacher.Age, teacher.Sex, teacher.TeacherStatus, teacher.TeacherPhoto, teacher.InductionDate, teacher.TeacherPhoneNo, teacher.Ology, teacher.AccumulatedTeachingTimes, teacher.AppletPwd, teacher.Remarks, ClassIds = classIds }; //调用回调函数,同步数据到IOT //_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam //{ // EventType = EventType.Update.GetDisplayName(), // DataType = IOTDataSyncType.Teacher.GetDisplayName(), // Json = JsonConvert.SerializeObject(teacherJson) //})); await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Update.GetDisplayName(), DataType = IOTDataSyncType.Teacher.GetDisplayName(), Json = JsonConvert.SerializeObject(teacherJson) }); } public async Task UpdateTeacherPwd(UpdateTeacherPwdParam paramDto) { var teacher = await _teacherRepository.FindAsyncFirst(x => x.TeacherPhoneNo == paramDto.TeacherPhoneNo); if (teacher == null) throw new ArgumentNullException("未找到要更新的数据"); teacher.AppletPwd = AppSetting.OriginalPwd; var user = await _sys_UserRepository.FindAsyncFirst(x => x.UserName == paramDto.TeacherPhoneNo); if (user == null) throw new ArgumentNullException("未找到该用户"); user.UserPwd = teacher.AppletPwd; user.LastLoginDate = DateTime.Now; _sys_UserRepository.Update(user); _studentRepository.Update(teacher); await _studentRepository.SaveChangesAsync(); await _sys_UserRepository.SaveChangesAsync(); var classIds = await _classAssocTeacherRepository.FindAsIQueryable(x => x.TeacherId == teacher.Id).Select(x => x.ClassId).ToListAsync(); var teacherJson = new { teacher.Id, teacher.SchoolCode, teacher.TeacherName, teacher.Age, teacher.Sex, teacher.TeacherStatus, teacher.TeacherPhoto, teacher.InductionDate, teacher.TeacherPhoneNo, teacher.Ology, teacher.AccumulatedTeachingTimes, teacher.AppletPwd, teacher.Remarks, ClassIds = classIds }; //调用回调函数,同步数据到IOT //_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam //{ // EventType = EventType.Update.GetDisplayName(), // DataType = IOTDataSyncType.Teacher.GetDisplayName(), // Json = JsonConvert.SerializeObject(teacherJson) //})); await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Update.GetDisplayName(), DataType = IOTDataSyncType.Teacher.GetDisplayName(), Json = JsonConvert.SerializeObject(teacherJson) }); } /// /// 上传老师头像 /// public async Task UploadTeacherPhoto(IFormFile file, string teacherPhoneNo, string teacherName) { // 设置照片最大允许大小 (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/Teacher/", teacherPhoneNo); var faceEntityWithRequest = new FaceEntityWithDto() { EntityId = teacherPhoneNo, SchoolCode = UserContext.Current.TenantId, IsStudent = false }; 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 = teacherName, IsStudent = false, EntityId = teacherPhoneNo, ImageUrl = url }); if (!faceWithReuslt) { throw new Exception("上传失败"); } return url; } public async Task ResetPassword(int teacherId) { var user = await _sys_UserRepository.FindAsyncFirst(x => x.User_Id == teacherId); if (user == null) throw new ArgumentNullException("未找到该用户"); user.UserPwd = AppSetting.OriginalPwd; user.LastLoginDate = DateTime.Now; _sys_UserRepository.Update(user); await _sys_UserRepository.SaveChangesAsync(); var teacher = await _teacherRepository.FindAsyncFirst(x => x.Id == teacherId); if (teacher == null) throw new ArgumentNullException("未找到要更新的数据"); var classIds = await _classAssocTeacherRepository.FindAsIQueryable(x => x.TeacherId == teacher.Id).Select(x => x.ClassId).ToListAsync(); var teacherJson = new { teacher.Id, teacher.SchoolCode, teacher.TeacherName, teacher.Age, teacher.Sex, teacher.TeacherStatus, teacher.TeacherPhoto, teacher.InductionDate, teacher.TeacherPhoneNo, teacher.Ology, teacher.AccumulatedTeachingTimes, AppletPwd = "123456", teacher.Remarks, ClassIds = classIds }; //调用回调函数,同步数据到IOT //_ = Task.Run(() => _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam //{ // EventType = EventType.Update.GetDisplayName(), // DataType = IOTDataSyncType.Teacher.GetDisplayName(), // Json = JsonConvert.SerializeObject(user) //})); await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam { EventType = EventType.Update.GetDisplayName(), DataType = IOTDataSyncType.Teacher.GetDisplayName(), Json = JsonConvert.SerializeObject(user) }); } /// /// 教师统计 /// /// /// /// public async Task TeacherDataStats(TeacherDataStatsParam paramDto) { var res = new TeacherDataStatsModel(); var tenantId = UserContext.Current.TenantId; if (!paramDto.TeacherId.HasValue || paramDto.TeacherId <= 0) { paramDto.TeacherId = this._teacherRepository.DbContext.Set().First().Id; } var teacherModel = await (from t in _teacherRepository.DbContext.Set() join a in _teacherRepository.DbContext.Set() on t.Id equals a.TeacherId join s in _teacherRepository.DbContext.Set() on a.ClassId equals s.ClassId where t.Id == paramDto.TeacherId && t.SchoolCode.Equals(UserContext.Current.TenantId) group new { t, a } by new { t.Id } into groupedData select new { groupedData.Key.Id, TeacherName = groupedData.Select(x => x.t.TeacherName).FirstOrDefault(), AccumulatedTeachingTimes = groupedData.Select(x => x.t.AccumulatedTeachingTimes).FirstOrDefault(), StudentCount = groupedData.Count(), ClassCount = groupedData.Select(x => x.a.ClassId).Distinct().Count(), ClassIds = groupedData.Select(x => x.a.ClassId).Distinct().ToList() }).FirstOrDefaultAsync(); if (teacherModel == null) throw new ArgumentNullException("未找到老师数据"); res.TeacherName = teacherModel.TeacherName; res.ClassCount = teacherModel.ClassCount; res.StudentCount = teacherModel.StudentCount; var grades = await ( from g in _classRepository.DbContext.Set() join c in _classRepository.DbContext.Set() on g.Id equals c.GradeId into classGroup from c in classGroup.DefaultIfEmpty() where teacherModel.ClassIds.Contains(c.Id) && c.SchoolCode.Equals(UserContext.Current.TenantId) select new GradeAndClassNames { ClassId = c.Id, GradeAndClassName = $"{g.GradeName}{c.ClassName}" }).ToListAsync(); var classIds = grades.Select(x => x.ClassId).ToList(); // 从缓存中获取数据 var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.SchoolCode.Equals(tenantId) && x.TeacherId == paramDto.TeacherId && classIds.Contains(x.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, g.First().ScoreTime, 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, x.ScoreTime, Rank = x.Score.GetRank() }).ToList(); // 计算总人数 double totalCount = monitorList.Count(); res.ExcellentRate = totalCount > 0 ? Math.Round((monitorList.Count(x => x.Rank == "优秀") / totalCount) * 100) : 0; res.FineRate = totalCount > 0 ? Math.Round((monitorList.Count(x => x.Rank == "良好") / totalCount) * 100) : 0; res.PassRate = totalCount > 0 ? Math.Round((monitorList.Count(x => x.Rank == "及格") / totalCount) * 100) : 0; 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); } float CalculatePassRate(int passed, int total) => total > 0 ? (float)Math.Truncate((double)passed / total * 100) / 100 : 0; // 获取所有班级的学生人数 var classStudentCounts = await ( from s in _classRepository.DbContext.Set() where teacherModel.ClassIds.Contains(s.ClassId) group s by s.ClassId into g select new { ClassId = g.Key, StudentCount = g.Count() } ).ToDictionaryAsync(x => x.ClassId, x => x.StudentCount); // 获取所有班级的教学次数 var classTeachingTimes = await ( from r in _classRepository.DbContext.Set() where r.TeacherId == paramDto.TeacherId && teacherModel.ClassIds.Contains(r.ClassId) group r by r.ClassId into g select new { ClassId = g.Key, TeachingTimes = g.Count() } ).ToDictionaryAsync(x => x.ClassId, x => x.TeachingTimes); foreach (var c in grades) { if (!c.ClassId.HasValue) continue; // 从字典中获取当前班级的学生人数 var totalStudentsInClass = classStudentCounts.TryGetValue(c.ClassId.Value, out var count) ? count : 0; // 从字典中获取当前班级的教学次数 var accumulatedTeachingTimes = classTeachingTimes.TryGetValue(c.ClassId.Value, out var teachingTimes) ? teachingTimes : 0; var classResults = monitorList.Where(x => x.ClassId == c.ClassId).ToList(); var totalFemale = classResults.Count(x => x.Sex == SexType.Female); var totalMale = classResults.Count(x => x.Sex == SexType.Male); var totalPassedFemale = classResults.Count(x => x.Sex == SexType.Female && x.Rank != "不及格"); var totalPassedMale = classResults.Count(x => x.Sex == SexType.Male && x.Rank != "不及格"); var totalPassedOverall = classResults.Count(x => x.Rank != "不及格"); var sexAndOverall = new SexAndOverall { FemaleValue = CalculatePassRate(totalPassedFemale, totalFemale), MaleValue = CalculatePassRate(totalPassedMale, totalMale), OverallValue = CalculatePassRate(totalPassedOverall, totalStudentsInClass) }; res.TestResultPassRate.AxisX.Add(c.GradeAndClassName); res.TestResultPassRate.AxisY.Add(sexAndOverall); var classResult = classResults.FirstOrDefault(); var result = classResults .GroupBy(c => c.ScoreTime) .Select(g => new { ScoreTime = g.Key, Count = g.Count() }) .ToList(); res.TeacherDetailsList.Add(new TeacherDetails() { ClassId = c.ClassId, GradeAndClassName = c.GradeAndClassName, Count = totalStudentsInClass, TeacherName = teacherModel.TeacherName, AccumulatedTeachingTimes = accumulatedTeachingTimes, PassRate = CalculatePassRate(totalPassedOverall, totalStudentsInClass) }); } return res; } /// /// 班级体测平均成绩 /// /// /// public async Task> TestResultAvg(ClassDataStatsParam paramDto) { var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ClassId == paramDto.ClassId && x.SchoolCode.Equals(UserContext.Current.TenantId), 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) .ToList(); return filteredResults.Any() ? filteredResults.Average() : 0; } ); } /// /// 各班级授课次数占比 /// /// /// /// public async Task> ClassTeachingCountRatio(QuarterlyParam paramDto) { var res = new Dictionary(); var now = DateTime.Now; var currentQuarter = (now.Month - 1) / 3 + 1; var currentYear = now.Year; // 定义 sportsTestResults 变量 List sportsTestResults = new List(); // 根据 QuarterlyCycle 选择不同的查询范围 var quarterOffset = paramDto.QuarterlyCycle switch { QuarterlyCycleEnum.ForTheQuarter => 0, QuarterlyCycleEnum.PreviousQuarter => 1, QuarterlyCycleEnum.LastQuarter => 2, _ => 0 }; // 计算目标季度和年份 var targetQuarter = currentQuarter - quarterOffset; var targetYear = currentYear; // 处理跨年情况 if (targetQuarter < 1) { targetQuarter += 4; // 调整季度 targetYear--; // 调整年份 } else if (targetQuarter > 4) { targetQuarter -= 4; // 调整季度 targetYear++; // 调整年份 } var startDate = Tool.GetQuarterStartDate(targetQuarter, targetYear); var endDate = Tool.GetQuarterEndDate(targetQuarter, targetYear); sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.TeacherId == paramDto.TeacherId && x.SchoolCode == UserContext.Current.TenantId && x.ScoreTime >= startDate && x.ScoreTime <= endDate, paramDto.Mode); // 分组并计算每个分组的 Count var groupResult = sportsTestResults .GroupBy(c => new { c.ClassId, c.ClassName, c.GradeName }) .Select(g => new { g.Key.ClassId, g.Key.ClassName, g.Key.GradeName, Count = g.DistinctBy(x => x.ScoreTime).Count() }) .ToList(); // 计算总数 var totalCount = groupResult.Sum(gr => gr.Count); // 生成结果字典 res = groupResult.ToDictionary( gr => $"{gr.GradeName}{gr.ClassName}", gr => totalCount > 0 ? (float)gr.Count / totalCount : 0f ); return res; } } }