999 lines
42 KiB
C#
Raw Normal View History

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 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<List<TeacherNameModel>> 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<List<TeacherListByGradeIdModel>> GetTeacherNamesByGradeId(TeacherListByGradeIdParam paramDto)
{
var query = from t in _teacherRepository.DbContext.Set<S_Teacher>()
join a in _teacherRepository.DbContext.Set<S_ClassAssocTeacher>()
on t.Id equals a.TeacherId into assoc
from a in assoc.DefaultIfEmpty()
join c in _teacherRepository.DbContext.Set<S_Class>()
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<List<TeacherPageListModel>> GetTeacherList(TeacherExportParam paramDto)
{
var res = new PageDataDto<TeacherPageListModel>();
// 主查询,仅查询教师表
var query = _teacherRepository.DbContext.Set<S_Teacher>()
.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<S_ClassAssocTeacher>()
.Where(at => at.TeacherId == item.Id);
// 学生数量
item.StudentCount = (from at in assocTeachers
join s in _teacherRepository.DbContext.Set<S_Student>() 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<S_Class>() 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<PageDataDto<TeacherPageListModel>> GetTeacherPageList(TeacherPageListParam paramDto)
{
var res = new PageDataDto<TeacherPageListModel>();
// 主查询,仅查询教师表
var query = _teacherRepository.DbContext.Set<S_Teacher>()
.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<S_ClassAssocTeacher>()
.Where(at => at.TeacherId == item.Id);
// 学生数量
item.StudentCount = (from at in assocTeachers
join s in _teacherRepository.DbContext.Set<S_Student>() 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<S_Class>() 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<S_Teacher>(paramDto);
teacherEntity.TeacherStatus = TeacherStatus.Normal;
teacherEntity.SchoolCode = UserContext.Current.TenantId;
teacherEntity.Creator = UserContext.Current.UserId;
teacherEntity.CreateDate = DateTime.Now;
2025-06-06 16:55:14 +08:00
teacherEntity.AppletPwd = "000000";
2025-06-06 16:00:39 +08:00
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 = 3,
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<S_ClassAssocTeacher>();
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);
}
2025-06-06 16:55:14 +08:00
await _sys_UserRepository.SaveChangesAsync();
2025-06-06 16:00:39 +08:00
// 提交事务
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<S_Teacher>() { teacherEntity })
//}));
await _iotDataSyncService.DataSyncCallBack(new DataSyncCallBackParam
{
EventType = EventType.Add.GetDisplayName(),
DataType = IOTDataSyncType.Teacher.GetDisplayName(),
//Json = JsonConvert.SerializeObject(new List<S_Teacher>() { 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<S_ClassAssocTeacher>();
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("未找到该用户");
2025-06-06 16:55:14 +08:00
user.UserPwd = teacher.AppletPwd;
2025-06-06 16:00:39 +08:00
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)
});
}
/// <summary>
/// 上传老师头像
/// </summary>
public async Task<string> 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("未找到该用户");
2025-06-06 16:55:14 +08:00
user.UserPwd = AppSetting.OriginalPwd;
2025-06-06 16:00:39 +08:00
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)
});
}
/// <summary>
/// 教师统计
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public async Task<TeacherDataStatsModel> 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<S_Teacher>().First().Id;
}
var teacherModel = await (from t in _teacherRepository.DbContext.Set<S_Teacher>()
join a in _teacherRepository.DbContext.Set<S_ClassAssocTeacher>() on t.Id equals a.TeacherId
join s in _teacherRepository.DbContext.Set<S_Student>() 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<S_Grade>()
join c in _classRepository.DbContext.Set<S_Class>() 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();
2025-06-06 16:55:14 +08:00
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;
2025-06-06 16:00:39 +08:00
if (res != null)
{
// 计算并调整最后一个百分比确保总和为100%
double sum = (res.ExcellentRate + res.FineRate + res.PassRate + res.FailRate);
double adjustment = 100 - sum;
2025-06-06 16:55:14 +08:00
res.FailRate += (totalCount > 0 ? adjustment : 0);
2025-06-06 16:00:39 +08:00
}
float CalculatePassRate(int passed, int total) => total > 0 ? (float)Math.Truncate((double)passed / total * 100) / 100 : 0;
2025-06-06 16:55:14 +08:00
// 获取所有班级的学生人数
var classStudentCounts = await (
from s in _classRepository.DbContext.Set<S_Student>()
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<Ai_ClassRoomRecord>()
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);
2025-06-06 16:00:39 +08:00
foreach (var c in grades)
{
2025-06-06 16:55:14 +08:00
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;
2025-06-06 16:00:39 +08:00
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),
2025-06-06 16:55:14 +08:00
OverallValue = CalculatePassRate(totalPassedOverall, totalStudentsInClass)
2025-06-06 16:00:39 +08:00
};
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,
2025-06-06 16:55:14 +08:00
Count = totalStudentsInClass,
2025-06-06 16:00:39 +08:00
TeacherName = teacherModel.TeacherName,
2025-06-06 16:55:14 +08:00
AccumulatedTeachingTimes = accumulatedTeachingTimes,
PassRate = CalculatePassRate(totalPassedOverall, totalStudentsInClass)
2025-06-06 16:00:39 +08:00
});
}
return res;
}
/// <summary>
/// 班级体测平均成绩
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<Dictionary<string, float>> 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;
}
);
}
/// <summary>
/// 各班级授课次数占比
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<Dictionary<string, float>> ClassTeachingCountRatio(QuarterlyParam paramDto)
{
var res = new Dictionary<string, float>();
var now = DateTime.Now;
var currentQuarter = (now.Month - 1) / 3 + 1;
var currentYear = now.Year;
// 定义 sportsTestResults 变量
List<SportsTestValueModel> sportsTestResults = new List<SportsTestValueModel>();
// 根据 QuarterlyCycle 选择不同的查询范围
var quarterOffset = paramDto.QuarterlyCycle switch
{
QuarterlyCycleEnum.ForTheQuarter => 0,
QuarterlyCycleEnum.PreviousQuarter => 1,
QuarterlyCycleEnum.LastQuarter => 2,
_ => 0
};
2025-06-06 16:55:14 +08:00
// 计算目标季度和年份
var targetQuarter = currentQuarter - quarterOffset;
var targetYear = currentYear;
2025-06-06 16:00:39 +08:00
2025-06-06 16:55:14 +08:00
// 处理跨年情况
if (targetQuarter < 1)
{
targetQuarter += 4; // 调整季度
targetYear--; // 调整年份
2025-06-06 16:00:39 +08:00
}
2025-06-06 16:55:14 +08:00
else if (targetQuarter > 4)
{
targetQuarter -= 4; // 调整季度
targetYear++; // 调整年份
}
var startDate = Tool.GetQuarterStartDate(targetQuarter, targetYear);
var endDate = Tool.GetQuarterEndDate(targetQuarter, targetYear);
2025-06-06 16:00:39 +08:00
2025-06-06 16:55:14 +08:00
sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.TeacherId == paramDto.TeacherId &&
x.SchoolCode == UserContext.Current.TenantId &&
x.ScoreTime >= startDate &&
x.ScoreTime <= endDate, paramDto.Mode);
2025-06-06 16:00:39 +08:00
// 分组并计算每个分组的 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;
}
}
}