This commit is contained in:
tanglong 2025-09-01 10:49:30 +08:00
parent 42d5d0aa64
commit c527820dc7
6 changed files with 459 additions and 10 deletions

View File

@ -22,6 +22,12 @@ namespace VOL.Business.IServices
Task<LargeScreenDataStatModel> LargeScreenDataStat(LargeScreenDataStatParam paramDto);
/// <summary>
/// 智慧操场
/// </summary>
/// <returns></returns>
Task<SmartPlaygroundModel> SmartPlayground();
/// <summary>
/// 学期数据
/// </summary>

View File

@ -33,6 +33,7 @@ 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.Request;
using VOL.Model.Norm.Response;
using VOL.Model.School.Response;
@ -506,6 +507,343 @@ namespace VOL.Business.Services
return res;
}
#region
/// <summary>
/// 智慧操场
/// </summary>
/// <returns></returns>
public async Task<SmartPlaygroundModel> SmartPlayground()
{
var res = new SmartPlaygroundModel();
var tenantId = UserContext.Current.TenantId;
//学校信息
var smartPlaygroundSchoolInfoKey = $"SmartPlaygroundSchoolInfo_{tenantId}";
var schoolInfo = _cacheService.Get<SchoolInfoModel>(smartPlaygroundSchoolInfoKey);
if (schoolInfo == null)
{
schoolInfo = await SmartPlaygroundSchoolInfo(tenantId);
_cacheService.AddObject(smartPlaygroundSchoolInfoKey, schoolInfo, 600);
}
res.SchoolInfo = schoolInfo;
var today = DateTime.Today;
// 先确定当前学期的时间范围
DateTime startDate, endDate;
// 规则:上学期 9月1日 次年2月1日下学期 2月1日 9月1日
if (today.Month >= 9 || today.Month < 2) // 上学期
{
startDate = new DateTime(today.Month >= 9 ? today.Year : today.Year - 1, 1, 1);
endDate = new DateTime(today.Month >= 9 ? today.Year + 1 : today.Year, 2, 1);
}
else // 下学期
{
startDate = new DateTime(today.Year, 2, 1);
endDate = new DateTime(today.Year, 10, 1);
}
var SmartPlaygroundKey = $"SmartPlayground_{tenantId}";
// 从缓存中获取数据
var sportsTestResults = _cacheService.Get<List<SportsTestValueModel>>(SmartPlaygroundKey);
if (sportsTestResults == null)
{
// 查询条件加上 ScoreTime 范围
var testResults = await _sportsTestResultRepository
.FindAsIQueryable(x => x.SchoolCode == tenantId
&& x.IsDisplay
&& x.DataSource == DataSource.XW
&& x.ScoreTime >= startDate
&& x.ScoreTime < endDate)
.ToListAsync();
sportsTestResults = _mapper.Map<List<SportsTestValueModel>>(testResults);
_cacheService.AddObject(smartPlaygroundSchoolInfoKey, schoolInfo, 600);
}
var classListKey = $"ClassList_{tenantId}";
var classList = _cacheService.Get<List<S_Class>>(classListKey);
if (classList == null || classList.Count == 0)
{
classList = await _classRepository.FindAsIQueryable(x => x.SchoolCode == tenantId).ToListAsync();
_cacheService.AddObject(classListKey, classList, 3600);
}
// 统计逻辑
var rankValues = new List<string> { "优秀", "良好", "及格", "不及格" };
var totalCount = sportsTestResults.Count;
var totalMale = sportsTestResults.Count(x => x.Sex == SexType.Male);
var totalFemale = sportsTestResults.Count(x => x.Sex == SexType.Female);
var tempStats = new List<RankStatsDto>();
for (int i = 0; i < rankValues.Count; i++)
{
var rank = rankValues[i];
var count = sportsTestResults.Count(x => x.Score.GetRank() == rank);
var maleCount = sportsTestResults.Count(x => x.Sex == SexType.Male && x.Score.GetRank() == rank);
var femaleCount = sportsTestResults.Count(x => x.Sex == SexType.Female && x.Score.GetRank() == rank);
int rate = totalCount == 0 ? 0 : (i < rankValues.Count - 1 ? (int)Math.Round((double)count / totalCount * 100) : 100 - tempStats.Sum(s => s.Rate));
int maleRate = totalMale == 0 ? 0 : (i < rankValues.Count - 1 ? (int)Math.Round((double)maleCount / totalMale * 100) : 100 - tempStats.Sum(s => s.MaleRate));
int femaleRate = totalFemale == 0 ? 0 : (i < rankValues.Count - 1 ? (int)Math.Round((double)femaleCount / totalFemale * 100) : 100 - tempStats.Sum(s => s.FemaleRate));
tempStats.Add(new RankStatsDto
{
Rank = rank,
Count = count,
MaleCount = maleCount,
FemaleCount = femaleCount,
Rate = rate,
MaleRate = maleRate,
FemaleRate = femaleRate
});
}
res.RankStatsList = tempStats;
res.ResultSituationDic = sportsTestResults
.GroupBy(x => x.CategoryValue)
.ToDictionary(
g => ((SportsTestItemType)g.Key).GetDisplayName(),
g =>
{
var result = new ResultSituation();
var categoryTotal = g.Count();
var categoryMaleTotal = g.Count(x => x.Sex == SexType.Male);
var categoryFemaleTotal = g.Count(x => x.Sex == SexType.Female);
int sumRate = 0, sumMaleRate = 0, sumFemaleRate = 0;
var tempStats = new List<TestSituationsModel>();
for (int i = 0; i < rankValues.Count; i++)
{
var rank = rankValues[i];
var count = g.Count(x => x.Score.GetRank() == rank);
var maleCount = g.Count(x => x.Sex == SexType.Male && x.Score.GetRank() == rank);
var femaleCount = g.Count(x => x.Sex == SexType.Female && x.Score.GetRank() == rank);
int rate = categoryTotal == 0 ? 0 : (i < rankValues.Count - 1
? (int)Math.Round((double)count / categoryTotal * 100)
: 100 - sumRate);
int activityLevel = categoryTotal == 0 ? 0 : (int)Math.Round((double)categoryTotal / sportsTestResults.Count * 100);
tempStats.Add(new TestSituationsModel
{
Rank = rank,
Count = count,
MaleCount = maleCount,
FemaleCount = femaleCount,
Rate = rate,
ActivityLevel = activityLevel
});
}
result.TestSituationsList = tempStats;
var maleData = g.Where(x => x.Sex == SexType.Male).ToList();
var femaleData = g.Where(x => x.Sex == SexType.Female).ToList();
var maleProportion = GetMonthlyAverageScore(maleData);
var femaleProportion = GetMonthlyAverageScore(femaleData);
result.AvgTestResultDic.Add("男生", maleProportion);
result.AvgTestResultDic.Add("女生", femaleProportion);
result.Vitality = (int)Math.Round((double)g.Count() / sportsTestResults.Count() * 100);
return result;
});
res.ClassSportsRankList = sportsTestResults
.GroupBy(x => new { x.ClassId })
.Select(g => new ClassSportsRankingDto
{
ClassId = g.Key.ClassId,
ClassName = classList.Where(c => c.Id == g.Key.ClassId).Select(x => $"{x.GradeName}-{x.ClassName}").FirstOrDefault() ?? "",
Count = g.Count()
})
.OrderByDescending(x => x.Count)
.Take(10)
.Select((x, index) =>
{
x.Rank = index + 1;
return x;
})
.ToList();
res.GradeExcellentRateDic = classList
.GroupBy(x => x.GradeId)
.ToDictionary(
g => g.First().GradeName,
g =>
{
var gradeStudents = sportsTestResults.Where(x => x.GradeId == g.Key).ToList();
int total = gradeStudents.Count;
var goodRanks = new[] { "优秀", "良好" };
int femaleTotal = gradeStudents.Count(x => x.Sex == SexType.Female);
int maleTotal = gradeStudents.Count(x => x.Sex == SexType.Male);
var result = new GradeExcellentRate()
{
// 优良率(全体)
Rate = total == 0 ? 0 :
(int)Math.Round((double)gradeStudents.Count(x => goodRanks.Contains(x.Score.GetRank())) / total * 100),
// 女生优良率
FemaleRate = femaleTotal == 0 ? 0 :
(int)Math.Round((double)gradeStudents.Count(x => x.Sex == SexType.Female && goodRanks.Contains(x.Score.GetRank())) / femaleTotal * 100),
// 男生优良率
MaleRate = maleTotal == 0 ? 0 :
(int)Math.Round((double)gradeStudents.Count(x => x.Sex == SexType.Male && goodRanks.Contains(x.Score.GetRank())) / maleTotal * 100)
};
return result;
});
return res;
}
/// <summary>
/// 智慧操场学校信息
/// </summary>
/// <returns></returns>
public async Task<SchoolInfoModel> SmartPlaygroundSchoolInfo(string tenantId)
{
var schoolInfo = new SchoolInfoModel();
// 使用单个查询获取年级、班级和教师信息
var gradeModels = await (
from g in _gradeRepository.DbContext.Set<S_Grade>()
join a in _gradeRepository.DbContext.Set<S_SchoolAssocGrade>() on g.Id equals a.GradeId
where a.SchoolCode == tenantId
select new
{
g.Id,
g.GradeName,
Classes = _gradeRepository.DbContext.Set<S_Class>()
.Where(c => c.GradeId == g.Id && c.SchoolCode == tenantId)
.Select(c => new { c.Id })
.ToList()
})
.Select(x => new
{
x.Id,
x.GradeName,
ClassCount = x.Classes.Count,
ClassIds = x.Classes.Select(c => c.Id).ToList()
})
.ToListAsync();
// 获取教师总数(简化查询)
var tCount = await _gradeRepository.DbContext.Set<S_Teacher>()
.Where(t => t.SchoolCode == tenantId && t.TeacherStatus != TeacherStatus.Depart)
.CountAsync();
// 获取班级与教师的关联信息
var classTeacherMap = await (
from a in _gradeRepository.DbContext.Set<S_ClassAssocTeacher>()
join t in _gradeRepository.DbContext.Set<S_Teacher>() on a.TeacherId equals t.Id
where t.SchoolCode == tenantId && t.TeacherStatus != TeacherStatus.Depart
group a.TeacherId by a.ClassId into g
select new { ClassId = g.Key, TeacherIds = g.Distinct().ToList() })
.ToDictionaryAsync(x => x.ClassId, x => x.TeacherIds);
// 获取学生信息(简化查询)
var studentCounts = await _studentRepository.DbContext.Set<S_Student>()
.Where(s => s.SchoolCode == tenantId && s.StudentStatus == StudentStatus.Normal)
.GroupBy(s => s.Sex)
.Select(g => new { Sex = g.Key, Count = g.Count() })
.ToListAsync();
// 处理数据
var allClassIds = gradeModels.SelectMany(x => x.ClassIds).ToList();
var gradeTeacherCounts = new Dictionary<Guid, int>(); // 假设年级ID类型为Guid
foreach (var grade in gradeModels)
{
schoolInfo.ClassData.Add(new ClassModel
{
GradeName = grade.GradeName,
ClassCount = grade.ClassCount
});
// 计算该年级的教师数量
var teacherIds = new HashSet<int>(); // 假设教师ID类型为Guid
foreach (var classId in grade.ClassIds)
{
if (classTeacherMap.TryGetValue(classId, out var classTeacherIds))
{
foreach (var teacherId in classTeacherIds)
{
teacherIds.Add(teacherId);
}
}
}
schoolInfo.TeacherData.Add(new TeacherModel
{
GradeName = grade.GradeName,
TeacherCount = teacherIds.Count
});
}
// 设置学生数据
schoolInfo.StudentData.MaleCount = studentCounts.FirstOrDefault(x => x.Sex == SexType.Male)?.Count ?? 0;
schoolInfo.StudentData.FemaleCount = studentCounts.FirstOrDefault(x => x.Sex == SexType.Female)?.Count ?? 0;
// 设置总计数据
schoolInfo.TotalClassCount = gradeModels.Sum(x => x.ClassCount);
schoolInfo.TotalTeacherCount = tCount;
return schoolInfo;
}
/// <summary>
/// 月份分组
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public VariousSportsProportion GetMonthlyAverageScore(List<SportsTestValueModel> data)
{
var result = new VariousSportsProportion();
// 按月份分组
var monthGroups = data
.GroupBy(x => x.ScoreTime.Month)
.OrderBy(g => g.Key); // 按月份排序
foreach (var g in monthGroups)
{
result.AxisX.Add($"{g.Key}月"); // 1月、2月...
// 计算平均值,如果没有数据就填 0
var avg = g.Any() ? (float)Math.Round(g.Average(x => x.Score), 2) : 0;
result.AxisY.Add(avg);
}
return result;
}
#endregion
#region
/// <summary>
@ -618,16 +956,16 @@ namespace VOL.Business.Services
var sportsTestResults = await _cacheQueryService.GeSportsTestDataCacheAsync(x => x.ScoreTime >= paramDto.StartTime && x.ScoreTime <= paramDto.EndTime);
// 运动时长
schoolInfo.AccumulatedTrainDuration = sportsTestResults.Select(x => x.MotionDuration).Sum();
//schoolInfo.AccumulatedTrainDuration = sportsTestResults.Select(x => x.MotionDuration).Sum();
var largeScreenTrainingDataModel = new List<LargeScreenTrainingDataModel>
{
await GeLargeScreenTrainingInfo(1, paramDto),
await GeLargeScreenTrainingInfo(2, paramDto),
await GeLargeScreenTrainingInfo(3, paramDto)
};
//var largeScreenTrainingDataModel = new List<LargeScreenTrainingDataModel>
//{
// await GeLargeScreenTrainingInfo(1, paramDto),
// await GeLargeScreenTrainingInfo(2, paramDto),
// await GeLargeScreenTrainingInfo(3, paramDto)
//};
schoolInfo.LargeScreenTrainingDataModel = largeScreenTrainingDataModel;
//schoolInfo.LargeScreenTrainingDataModel = largeScreenTrainingDataModel;
return schoolInfo;
}

View File

@ -40,12 +40,12 @@ namespace VOL.Model.Norm.Response
/// <summary>
/// 累计训练时长
/// </summary>
public int AccumulatedTrainDuration { get; set; }
//public int AccumulatedTrainDuration { get; set; }
/// <summary>
/// 大屏训练数据
/// </summary>
public List<LargeScreenTrainingDataModel> LargeScreenTrainingDataModel { get; set; } = new List<LargeScreenTrainingDataModel> { };
//public List<LargeScreenTrainingDataModel> LargeScreenTrainingDataModel { get; set; } = new List<LargeScreenTrainingDataModel> { };
}
/// <summary>

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VOL.Model.Norm.Response
{
/// <summary>
/// 智慧操场
/// </summary>
public class SmartPlaygroundModel
{
/// <summary>
/// 学校信息
/// </summary>
public SchoolInfoModel SchoolInfo { get; set; } = new SchoolInfoModel();
/// <summary>
/// 等级占比
/// </summary>
public List<RankStatsDto> RankStatsList { get; set; } = new List<RankStatsDto>();
/// <summary>
/// 各项目成绩情况
/// </summary>
public Dictionary<string, ResultSituation> ResultSituationDic { get; set; } = new Dictionary<string, ResultSituation>();
/// <summary>
/// 班级运动榜
/// </summary>
public List<ClassSportsRankingDto> ClassSportsRankList { get; set; } = new List<ClassSportsRankingDto>();
/// <summary>
/// 年级优良率
/// </summary>
public Dictionary<string, GradeExcellentRate> GradeExcellentRateDic { get; set; } = new Dictionary<string, GradeExcellentRate>();
}
public class ResultSituation
{
public int Vitality { get; set; }
public List<TestSituationsModel> TestSituationsList { get; set; } = new List<TestSituationsModel>();
public Dictionary<string, VariousSportsProportion> AvgTestResultDic { get; set; } = new Dictionary<string, VariousSportsProportion>();
}
public class RankStatsDto : TestSituationsModel
{
public int MaleRate { get; set; }
public int FemaleRate { get; set; }
}
public class TestSituationsModel
{
public string Rank { get; set; }
public int Count { get; set; }
public int Rate { get; set; }
public int MaleCount { get; set; }
public int FemaleCount { get; set; }
public int ActivityLevel { get; set; }
}
public class ClassSportsRankingDto
{
/// <summary>
/// 排名
/// </summary>
public int Rank { get; set; }
/// <summary>
/// 班级Id
/// </summary>
public int ClassId { get; set; }
/// <summary>
/// 班级名称
/// </summary>
public string ClassName { get; set; }
/// <summary>
/// 运动次数
/// </summary>
public int Count { get; set; }
}
public class GradeExcellentRate
{
public int Rate { get; set; }
public int MaleRate { get; set; }
public int FemaleRate { get; set; }
}
}

View File

@ -68,6 +68,7 @@ namespace VOL.Model.School.Response
/// <summary>
///类别枚举值
/// </summary>
//public SportsTestItemType CategoryValue { get; set; }
public int CategoryValue { get; set; }
/// <summary>

View File

@ -66,5 +66,15 @@ namespace VOL.WebApi.Controllers.Business
{
return await _sportsTestResultService.LargeScreenAverageClassExerciseIntensity(paramDto);
}
/// <summary>
/// 智慧操场
/// </summary>
/// <returns></returns>
[HttpGet(nameof(SmartPlayground))]
public async Task<SmartPlaygroundModel> SmartPlayground()
{
return await _sportsTestResultService.SmartPlayground();
}
}
}