using AutoMapper;
using DlibDotNet;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using VOL.Business.IServices;
using VOL.Business.IServices.Norm;
using VOL.Business.IServices.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.DomainModels.Business.People;
using VOL.Entity.Enum;
using VOL.Model;
using VOL.Model.Ai;
using VOL.Model.Norm.Response;
using VOL.Model.School.Request;
using VOL.Model.School.Response;
using VOL.System.IRepositories;
using VOL.System.Repositories;
namespace VOL.Business.Services.School
{
public class S_GradeService : IS_GradeService, IDependency
{
#region 初始化
private readonly IMapper _mapper;
private readonly ICacheService _cacheService;
private readonly ICacheQueryService _cacheQueryService;
private readonly IS_GradeRepository _gradeRepository;
private readonly IN_SportsTestResultRepository _sportsTestResultRepository;
private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository;
private readonly IS_ClassRepository _classRepository;
private readonly IS_TeacherRepository _teacherRepository;
[ActivatorUtilitiesConstructor]
public S_GradeService(IMapper mapper,
ICacheService cacheService,
ICacheQueryService cacheQueryService,
IS_GradeRepository gradeRepository,
IN_SportsTestResultRepository sportsTestResultRepository,
IN_SportsTestCategoryRepository sportsTestCategoryRepository,
IS_ClassRepository classRepository,
IS_TeacherRepository teacherRepository)
{
_mapper = mapper;
_cacheService = cacheService;
_gradeRepository = gradeRepository;
_cacheQueryService = cacheQueryService;
_sportsTestResultRepository = sportsTestResultRepository;
_sportsTestCategoryRepository = sportsTestCategoryRepository;
_classRepository = classRepository;
_teacherRepository = teacherRepository;
}
#endregion
///
/// 获取年级列表
///
///
public async Task> GetGradeList()
{
var tenantId = UserContext.Current.TenantId;
var teacherClassIds = new List();
var gradeIds = new List();
var isTeacher = (UserContext.Current.RoleId == 3);
if (isTeacher)
{
var teacher = await _teacherRepository.FindAsyncFirst(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
var teacherClassList = await (
from at in _teacherRepository.DbContext.Set()
join c in _teacherRepository.DbContext.Set() on at.ClassId equals c.Id
where at.TeacherId == teacher.Id
select c).ToListAsync();
gradeIds = teacherClassList.Select(c => c.GradeId).Distinct().ToList();
teacherClassIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
}
var list = await (
from g in _gradeRepository.DbContext.Set()
join a in _gradeRepository.DbContext.Set() on g.Id equals a.GradeId
where a.SchoolCode == tenantId && (!isTeacher || gradeIds.Contains(g.Id))
select new GradeListModel()
{
Id = g.Id,
GradeName = g.GradeName,
})
.OrderBy(x => x.Id)
.ToListAsync();
var classList = await _gradeRepository.DbContext.Set().Where(x => x.SchoolCode == tenantId && (!isTeacher || teacherClassIds.Contains(x.Id))).ToListAsync();
var studentList = await _gradeRepository.DbContext.Set().Where(x => x.SchoolCode == tenantId).ToListAsync();
foreach (var item in list)
{
var classIds = classList.Where(x => x.GradeId == item.Id).Select(x => x.Id).ToList();
var studentCount = studentList.Where(x => classIds.Contains(x.ClassId)).Count();
item.ClassCount = classIds.Count;
item.StudentCount = studentCount;
}
return list;
}
///
/// 获取所有年级名称
///
///
public async Task> GetGradeNames()
{
var tenantId = UserContext.Current.TenantId;
var teacherClassIds = new List();
var gradeIds = new List();
var isTeacher = (UserContext.Current.RoleId == 3);
if (isTeacher)
{
var teacher = await _teacherRepository.FindAsyncFirst(x => x.SchoolCode == tenantId && x.TeacherPhoneNo == UserContext.Current.UserInfo.PhoneNo);
var teacherClassList = await (
from at in _teacherRepository.DbContext.Set()
join c in _teacherRepository.DbContext.Set() on at.ClassId equals c.Id
where at.TeacherId == teacher.Id
select c).ToListAsync();
gradeIds = teacherClassList.Select(c => c.GradeId).Distinct().ToList();
teacherClassIds = teacherClassList.Select(c => c.Id).Distinct().ToList();
}
var list = await (
from g in _gradeRepository.DbContext.Set()
join a in _gradeRepository.DbContext.Set() on g.Id equals a.GradeId
where a.SchoolCode == tenantId && (!isTeacher || gradeIds.Contains(g.Id))
select new GradeNameModel()
{
Id = g.Id,
GradeName = g.GradeName,
})
.OrderBy(x => x.Id)
.ToListAsync();
var classList = await _gradeRepository.DbContext.Set().Where(x => x.SchoolCode == tenantId && (!isTeacher || teacherClassIds.Contains(x.Id))).ToListAsync();
foreach (var item in list)
{
var classIds = classList.Where(x => x.GradeId == item.Id).Select(x => x.Id).ToList();
item.Class = classList.Where(x => x.GradeId == item.Id).Select(x => new ClassNameModel()
{
Id = x.Id,
ClassName = x.ClassName
}).ToList();
}
return list;
}
///
/// 获取所有性质年级名称
///
///
public async Task> GetNatureGradeNames()
{
var grades = await (
from g in _gradeRepository.DbContext.Set()
join a in _gradeRepository.DbContext.Set() on g.Id equals a.GradeId
join snag in _gradeRepository.DbContext.Set() on g.Id equals snag.GradeId into snagGroup
from snag in snagGroup.DefaultIfEmpty()
join sn in _gradeRepository.DbContext.Set() on snag.NatureId equals sn.Id into snGroup
from sn in snGroup.DefaultIfEmpty()
join c in _gradeRepository.DbContext.Set() on g.Id equals c.GradeId into classGroup
from c in classGroup.DefaultIfEmpty()
where g.SchoolCode.Equals(UserContext.Current.TenantId)
group new { g, c, sn } by new { sn.Id, sn.NatureName } into natureGroup
select new NatureGradeNameModel
{
Id = natureGroup.Key.Id,
NatureName = natureGroup.Key.NatureName ?? string.Empty,
GradeList = natureGroup
.Where(g => g.g != null)
.GroupBy(g => new { g.g.Id, g.g.GradeName })
.Select(gradeGroup => new GradeNameModel
{
Id = gradeGroup.Key.Id,
GradeName = gradeGroup.Key.GradeName,
Class = gradeGroup.Where(g => g.c != null).Select(g => new ClassNameModel
{
Id = g.c.Id,
ClassName = g.c.ClassName
}).ToList()
}).ToList()
}).ToListAsync();
return grades;
}
public async Task GradeWholeDataStats(GradeDataStatsParam paramDto)
{
var res = new GradeDataStatsModel();
var tenantId = UserContext.Current.TenantId;
var baseQuery = await (
from c in _gradeRepository.DbContext.Set()
join s in _gradeRepository.DbContext.Set() on c.Id equals s.ClassId into studentGroup
from s in studentGroup.DefaultIfEmpty()
where (!paramDto.GradeId.HasValue || paramDto.GradeId <= 0 || c.GradeId == paramDto.GradeId) && c.SchoolCode == tenantId
select new
{
c.Id,
c.ClassName,
c.GradeId,
c.GradeName,
StudentId = (int?)s.Id
}).ToListAsync();
var gradeModel = baseQuery
.GroupBy(x => x.GradeId)
.Select(g => new
{
GradeId = g.Key,
ClassCount = g.Select(x => x.Id).Distinct().Count(),
StudentCount = g.Where(x => x.StudentId != null).Select(x => x.StudentId).Distinct().Count(),
ClassList = g
.GroupBy(x => x.Id)
.Select(cg => new
{
Id = cg.Key,
ClassName = cg.First().ClassName,
GradeId = cg.First().GradeId,
GradeName = cg.First().GradeName,
StudentCount = cg.Where(x => x.StudentId != null).Select(x => x.StudentId).Distinct().Count()
})
.Distinct()
.ToList()
}).ToList();
if (gradeModel.Count == 0)
return res;
res.ClassCount = gradeModel.SelectMany(x => x.ClassList).Count();
res.StudentCount = gradeModel.Sum(x => x.StudentCount);
// 从缓存中获取数据
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
x.SchoolCode.Equals(tenantId) &&
(!paramDto.GradeId.HasValue || paramDto.GradeId <= 0 || x.GradeId == paramDto.GradeId)
, 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();
// 计算总人数
float 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);
}
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;
}
);
float CalculatePassRate(int passed, int total) => total > 0 ? (float)Math.Truncate((double)passed / total * 100) / 100 : 0;
var teacherIds = sportsTestResults.Select(x => x.TeacherId).Distinct().ToList();
var teacherList = await _teacherRepository.FindAsync(x => teacherIds.Contains(x.Id));
var classList = gradeModel.SelectMany(x => x.ClassList).ToList();
var classTeachingTimes = await (
from r in _gradeRepository.DbContext.Set()
where classList.Select(c => c.Id).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 classList)
{
// 从班级中获取学生人数
var studentCount = gradeModel
.SelectMany(g => g.ClassList)
.FirstOrDefault(cl => cl.Id == c.Id)?
.StudentCount ?? 0;
// 从字典中获取当前班级的教学次数
var accumulatedTeachingTimes = classTeachingTimes.TryGetValue(c.Id, out var teachingTimes) ? teachingTimes : 0;
var classResults = monitorList.Where(x => x.ClassId == c.Id).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 totalClassResults = classResults.Count();
var sexAndOverall = new SexAndOverall
{
FemaleValue = CalculatePassRate(totalPassedFemale, totalFemale),
MaleValue = CalculatePassRate(totalPassedMale, totalMale),
OverallValue = CalculatePassRate(totalPassedOverall, totalClassResults)
};
res.TestResultPassRate.AxisX.Add($"{c.GradeName}{c.ClassName}");
res.TestResultPassRate.AxisY.Add(sexAndOverall);
var classResult = classResults.FirstOrDefault();
var teacher = teacherList.FirstOrDefault(x => x.Id == classResult?.TeacherId);
res.GradeDetailsList.Add(new GradeDetails()
{
ClassId = c.Id,
GradeAndClassName = $"{c.GradeName}-{c.ClassName}",
Count = studentCount,
TeacherName = teacher?.TeacherName,
AccumulatedTeachingTimes = accumulatedTeachingTimes,
PassRate = CalculatePassRate(totalPassedOverall, totalClassResults)
});
}
return res;
}
///
/// 获取年级关联的测试项目
///
///
///
public async Task> GetCategoryList(GradeDataStatsParam paramDto)
{
var res = await (
from g in _gradeRepository.DbContext.Set()
join s in _gradeRepository.DbContext.Set() on g.CategoryValue equals s.CategoryValue
where g.GradeId == paramDto.GradeId
select new CategoryModel
{
CategoryId = g.CategoryValue,
CategoryName = s.CategoryName,
}).ToListAsync();
return res;
}
///
/// 各体测项目等级占比
///
///
///
///
public async Task> CategoryRankRatio(CategoryParam paramDto)
{
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
x.SchoolCode.Equals(UserContext.Current.TenantId) &&
(paramDto.GradeId <= 0 || x.GradeId == paramDto.GradeId) &&
x.CategoryValue == paramDto.CategoryValue
, paramDto.Mode);
var result = sportsTestResults
.Where(x => x.CategoryValue == paramDto.CategoryValue && !string.IsNullOrWhiteSpace(x.Rank))
.GroupBy(x => x.Rank)
.ToDictionary(g => g.Key, g => (float)g.Count());
int totalCount = sportsTestResults.Count();
if (totalCount == 0)
{
return new Dictionary();
}
if (paramDto.CategoryValue == (int)SportsTestItemType.BMI)
{
var newResult = new Dictionary();
foreach (var kvp in result)
{
string newRank = kvp.Key switch
{
"正常" => "优秀",
"超重" => "及格",
"偏瘦" => "良好",
"肥胖" => "不及格",
_ => kvp.Key
};
if (newResult.ContainsKey(newRank))
{
newResult[newRank] += kvp.Value;
}
else
{
newResult[newRank] = kvp.Value;
}
}
result = newResult;
}
// 计算比例
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;
}
return result;
}
///
/// 成绩趋势
///
///
///
///
public async Task ResultTrends(GradeResultTrendsParam paramDto)
{
// 从缓存中获取数据
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x =>
(paramDto.GradeId <= 0 || x.GradeId == paramDto.GradeId) &&
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;
}
}
}