2026-01-28 11:03:33 +08:00

2007 lines
83 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
using EFCore.BulkExtensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using VOL.Ai.IRepositories;
using VOL.Ai.IServices;
using VOL.Core.Configuration;
using VOL.Core.Extensions;
using VOL.Core.Extensions.AutofacManager;
using VOL.Core.Utilities;
using VOL.Entity.DomainModels;
using VOL.Entity.Enum;
using VOL.Model;
using VOL.Model.Ai;
using VOL.Model.Ai.Request;
using VOL.Model.Ai.Response;
using VOL.Model.JingShiTiJiao;
using VOL.Model.Norm.Response;
using VOL.Model.Training.Response;
using VOL.System.IRepositories;
using VOL.System.Repositories;
namespace VOL.Ai.Services
{
public class AiAppService : IAiAppService, IDependency
{
private readonly IMapper _mapper;
private readonly IS_StudentRepository _studentRepository;
private readonly IS_TeacherRepository _teacherRepository;
private readonly IS_ClassRepository _classRepository;
private readonly IN_SportsTestCategoryRepository _sportsTestCategoryRepository;
private readonly IN_HealthStandardsRepository _healthStandardsRepository;
private readonly IScanCodeLoginRepository _scanCodeLoginRepository;
private readonly IAiAppVersionRepository _aiAppVersionRepository;
private readonly IN_SportsTrainingCategoryRepository _sportsTrainingCategoryRepository;
private readonly IClassRoomRecordRepository _classRoomRecordRepository;
private readonly IActivitiestDataRepository _activitiestDataRepository;
private readonly ILevelExamDataRepository _levelExamDataRepository;
private readonly ISportsTestDataRepository _sportsTestDataRepository;
private readonly ITrainingDataRepository _trainingDataRepository;
private readonly IFastJumpRopeDataRepository _fastJumpRopeDataRepository;
private readonly IHeartRateDataRepository _heartRateDataRepository;
private readonly ISys_UserRepository _userRepository;
private readonly IClassroomStageRepository _classroomStageRepository;
private readonly IClassroomSettingRepository _classroomSettingRepository;
private readonly IJingShiTiJiaoService _jingShiTiJiaoService;
private readonly ILogger<AiAppService> _logger;
[ActivatorUtilitiesConstructor]
public AiAppService(IMapper mapper,
IS_StudentRepository studentRepository,
IS_TeacherRepository teacherRepository,
IS_ClassRepository classRepository,
IN_SportsTestCategoryRepository sportsTestCategoryRepository,
IN_HealthStandardsRepository healthStandardsRepository,
IScanCodeLoginRepository scanCodeLoginRepository,
IN_SportsTrainingCategoryRepository sportsTrainingCategoryRepository,
IClassRoomRecordRepository classRoomRecordRepository,
IActivitiestDataRepository activitiestDataRepository,
ILevelExamDataRepository levelExamDataRepository,
ISportsTestDataRepository sportsTestDataRepository,
ITrainingDataRepository trainingDataRepository,
IFastJumpRopeDataRepository fastJumpRopeDataRepository,
IHeartRateDataRepository heartRateDataRepository,
ISys_UserRepository userRepository,
IClassroomStageRepository classroomStageRepository,
IClassroomSettingRepository classroomSettingRepository,
IAiAppVersionRepository aiAppVersionRepository,
IJingShiTiJiaoService jingShiTiJiaoService,
ILogger<AiAppService> logger
)
{
_mapper = mapper;
_studentRepository = studentRepository;
_sportsTestCategoryRepository = sportsTestCategoryRepository;
_healthStandardsRepository = healthStandardsRepository;
_scanCodeLoginRepository = scanCodeLoginRepository;
_teacherRepository = teacherRepository;
_sportsTrainingCategoryRepository = sportsTrainingCategoryRepository;
_classRoomRecordRepository = classRoomRecordRepository;
_activitiestDataRepository = activitiestDataRepository;
_levelExamDataRepository = levelExamDataRepository;
_sportsTestDataRepository = sportsTestDataRepository;
_trainingDataRepository = trainingDataRepository;
_classRepository = classRepository;
_fastJumpRopeDataRepository = fastJumpRopeDataRepository;
_heartRateDataRepository = heartRateDataRepository;
_userRepository = userRepository;
_classroomStageRepository = classroomStageRepository;
_classroomSettingRepository = classroomSettingRepository;
_aiAppVersionRepository = aiAppVersionRepository;
_jingShiTiJiaoService = jingShiTiJiaoService;
_logger = logger;
}
#region New
public async Task<Ai_DeviceDto> Ai_DeviceInfo(AiRequestDto paramDto)
{
var res = await (
from d in _studentRepository.DbContext.Set<Ai_Device>()
join a in _studentRepository.DbContext.Set<Ai_DeviceAssocSchool>() on d.Code equals a.Code
join s in _studentRepository.DbContext.Set<S_School>() on a.SchoolCode equals s.SchoolCode
where d.Code == paramDto.Code
select new Ai_DeviceDto()
{
Code = d.Code,
Name = d.Name,
SchoolName = s.SchoolName,
Status = d.Status,
StartTime = d.StartTime,
EndTime = d.EndTime
}).FirstOrDefaultAsync();
return res;
}
public async Task<Ai_SchoolDto> Ai_SchoolDetail(Ai_SchoolRequest paramDto)
{
// =========================
// 1️⃣ 查询学校信息
// =========================
//var res = await (
// from a in _studentRepository.DbContext.Set<Ai_DeviceAssocSchool>()
// join s in _studentRepository.DbContext.Set<S_School>()
// on a.SchoolCode equals s.SchoolCode
// where a.Code == paramDto.Code
// select new Ai_SchoolDto
// {
// SchoolCode = s.SchoolCode,
// Name = s.SchoolName,
// Type = s.SchoolNatureId,
// Province = s.Province,
// City = s.City
// }
//).FirstOrDefaultAsync();
//if (res == null)
// return new Ai_SchoolDto();
if (string.IsNullOrWhiteSpace(paramDto.Code))
throw new ArgumentException("学校编码不能为空", nameof(paramDto.Code));
var schoolCode = paramDto.Code;
// =========================
// 2️⃣ 调用京师接口获取学校年级班级
// =========================
var schoolClass = await _jingShiTiJiaoService.GetClassBySchoolAsync(schoolCode);
if (schoolClass?.Grades == null || !schoolClass.Grades.Any())
throw new Exception("获取京师年级班级失败!");
var res = new Ai_SchoolDto()
{
SchoolCode = schoolCode,
Name = schoolClass.SchoolName,
Type = 1,
Province = "",
City = ""
};
var gradeDtos = schoolClass.Grades;
// =========================
// 3️⃣ 提取所有 GradeNo / ClassNo
// =========================
var allGradeNos = gradeDtos
.Where(g => !string.IsNullOrWhiteSpace(g.GradeNo))
.Select(g => g.GradeNo)
.ToList();
var allClassNos = gradeDtos
.SelectMany(g => g.Classes ?? new List<JingShiClassDto>())
.Where(c => !string.IsNullOrWhiteSpace(c.ClassNo))
.Select(c => c.ClassNo)
.ToList();
// =========================
// 4️⃣ 查询数据库中已存在的数据(幂等)
// =========================
var existGrades = await _studentRepository.DbContext.Set<JS_Grade>()
.Where(x => x.SchoolCode == schoolCode && allGradeNos.Contains(x.GradeNo))
.ToListAsync();
var existClasses = await _classRepository.DbContext.Set<JS_Class>()
.Where(x => x.SchoolCode == schoolCode && allClassNos.Contains(x.ClassNo))
.ToListAsync();
var existGradeNoSet = existGrades.Select(x => x.GradeNo).ToHashSet();
var existClassNoSet = existClasses.Select(x => x.ClassNo).ToHashSet();
// =========================
// 5️⃣ 构造待插入的年级 + 班级
// =========================
var newGrades = new List<JS_Grade>();
foreach (var g in gradeDtos)
{
if (string.IsNullOrWhiteSpace(g.GradeNo) || existGradeNoSet.Contains(g.GradeNo))
continue;
var grade = new JS_Grade
{
SchoolCode = schoolCode,
Code = paramDto.Code,
GradeNo = g.GradeNo,
GradeName = g.GradeName,
Classes = g.Classes?
.Where(c => !string.IsNullOrWhiteSpace(c.ClassNo) && !existClassNoSet.Contains(c.ClassNo))
.Select(c => new JS_Class
{
SchoolCode = schoolCode,
Code = paramDto.Code,
GradeNo = g.GradeNo,
GradeName = g.GradeName,
ClassNo = c.ClassNo,
ClassName = c.ClassName
}).ToList() ?? new List<JS_Class>()
};
newGrades.Add(grade);
existGradeNoSet.Add(g.GradeNo);
foreach (var c in grade.Classes)
existClassNoSet.Add(c.ClassNo);
}
if (newGrades.Any())
await _studentRepository.DbContext.Set<JS_Grade>().AddRangeAsync(newGrades);
// =========================
// 6️⃣ 保存(并发幂等兜底)
// =========================
try
{
await _studentRepository.DbContext.SaveChangesAsync();
}
catch (DbUpdateException ex) when (ex.InnerException?.Message.Contains("UNIQUE") == true)
{
// 并发下唯一索引冲突可忽略
}
// =========================
// 7️⃣ 回查数据库,组装返回 DTO
// =========================
var dbGrades = await _studentRepository.DbContext.Set<JS_Grade>()
.Where(x => x.SchoolCode == schoolCode && allGradeNos.Contains(x.GradeNo))
.Include(g => g.Classes)
.ToListAsync();
res.Grade = dbGrades
.Where(g => g.Classes != null && g.Classes.Any())
.Select(g => new Grades
{
Id = (int)g.Id,
Name = g.GradeName,
Class = g.Classes.Select(c => new Classes
{
Id = (int)c.Id,
Name = c.ClassName
}).ToList()
})
.ToList();
return res;
//if (string.IsNullOrWhiteSpace(paramDto.Code))
// throw new ArgumentException("学校编码不能为空", nameof(paramDto.Code));
//var schoolCode = paramDto.Code;
//// =========================
//// 1️⃣ 调用京师接口获取学校信息及年级班级
//// =========================
//var schoolClass = await _jingShiTiJiaoService.GetClassBySchoolAsync(schoolCode);
//if (schoolClass?.Grades == null || !schoolClass.Grades.Any())
// throw new Exception("获取京师年级班级失败!");
//// =========================
//// 2️⃣ 组装返回 DTO
//// =========================
//var res = new Ai_SchoolDto()
//{
// SchoolCode = schoolCode,
// Name = schoolClass.SchoolName,
// Type = 1, // 可根据业务修改
// Province = "",
// City = "",
// Grade = schoolClass.Grades.Select(g => new Grades
// {
// Id = 1, // 京师接口没有数据库 Id,可用占位
// Name = g.GradeName,
// Class = g.Classes?.Select(c => new Classes
// {
// Id = 1, // 同上
// Name = c.ClassName
// }).ToList() ?? new List<Classes>()
// }).ToList()
//};
//return res;
}
public Ai_OSSInfoDto OSSInfo(Ai_Request paramDto)
{
return new Ai_OSSInfoDto()
{
Accessid = AppSetting.ALiYunOSS.AccessKeyId,
AccessKeySecret = AppSetting.ALiYunOSS.SecretAccessKey,
Endpoint = AppSetting.ALiYunOSS.Endpoint,
BucketName = AppSetting.YueDongServerOSS.BucketName,
Prefix = AppSetting.YueDongServerOSS.Prefix
};
}
//public async Task<List<Ai_ItemTypeModel>> GetAiCategoryType()
//{
// return await Task.FromResult(Tool.GetEnumDescriptions<Ai_ItemTypeModel, SportsTestItemType>((code, description)
// => new Ai_ItemTypeModel { ItemCode = code, ItemTypeName = description }));
//}
public async Task<ResultFilesDto> ResultFiles(ResultFilesRequest paramDto, IFormFile file)
{
var path = $"Upload/{paramDto.SchoolCode}/Voide/{paramDto.ClassRoomRecordId}/";
var name = Path.GetFileNameWithoutExtension(file.FileName);
var url = ALiYunOss.Upload(file, path, name);
var updateModels = await _levelExamDataRepository.FindAsync(x => x.SchoolCode == paramDto.SchoolCode &&
x.FileName == name);
updateModels.ForEach(x =>
{
x.FileUrl = url;
});
_levelExamDataRepository.UpdateRange(updateModels);
await _levelExamDataRepository.SaveChangesAsync();
return new ResultFilesDto() { Name = name, Url = url };
}
public async Task<List<Ai_ModeTypeModel>> ItemTypeList()
{
var list = await (from t in _sportsTestCategoryRepository.DbContext.Set<N_TrainingAssocCategory>()
join a in _sportsTrainingCategoryRepository.DbContext.Set<N_SportsTrainingCategory>() on t.CategoryValue equals a.CategoryValue
where t.DataSource == DataSource.AI
select new
{
Id = a.CategoryValue,
t.ModeId,
ItemTypeName = a.CategoryName
}).ToListAsync();
List<Ai_ModeTypeModel> result = Enum.GetValues(typeof(Ai_ModeEnum))
.Cast<Ai_ModeEnum>()
.Select(modeEnum => new Ai_ModeTypeModel
{
ModeId = (int)modeEnum,
ModeName = modeEnum.GetDescription(),
ItemTypeList = list
.Where(x => x.ModeId == (int)modeEnum)
.Select(x => new Ai_ItemTypeModel
{
ItemCode = x.Id,
ItemTypeName = x.ItemTypeName
})
.ToList()
})
.Where(model => model.ItemTypeList.Any())
.ToList();
return result;
}
public async Task<List<Ai_ModeTypeModel>> CategoryList()
{
var list = await (from t in _sportsTestCategoryRepository.DbContext.Set<N_TestAssocCategory>()
join a in _sportsTrainingCategoryRepository.DbContext.Set<N_SportsTestCategory>() on t.CategoryValue equals a.CategoryValue
where t.DataSource == DataSource.AI
select new
{
Id = a.CategoryValue,
t.ModeId,
ItemTypeName = a.CategoryName
}).ToListAsync();
List<Ai_ModeTypeModel> result = Enum.GetValues(typeof(Ai_ModeEnum))
.Cast<Ai_ModeEnum>()
.Select(modeEnum => new Ai_ModeTypeModel
{
ModeId = (int)modeEnum,
ModeName = modeEnum.GetDescription(),
ItemTypeList = list
.Where(x => x.ModeId == (int)modeEnum)
.Select(x => new Ai_ItemTypeModel
{
ItemCode = x.Id,
ItemTypeName = x.ItemTypeName
})
.ToList()
})
.Where(model => model.ItemTypeList.Any())
.ToList();
return result;
}
public async Task<Ai_StudentFaceInfo> StudentFace(GetFaceParam paramDto)
{
var faces = await ALiYunFace.SearchFace(paramDto.Base64, true);
foreach (var body in faces)
{
if (body != null && body.Confidence >= 70)
{
var student = await (from s in _studentRepository.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.StudentNo == body.EntityId)
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
select new Ai_StudentFaceInfo
{
ClassId = c.Id,
ClassName = c.ClassName,
Age = s.Age,
Sex = s.Sex,
StudentName = s.StudentName,
StudentCode = s.StudentNo,
GradeId = c.GradeId,
GradeName = c.GradeName,
Photo = s.Photo
}).FirstOrDefaultAsync();
if (student == null)
continue;
return student;
}
}
throw new Exception("未查询到学生信息");
}
public async Task<Ai_TeacherFaceInfo> TeacherFace(GetFaceParam paramDto)
{
var faces = await ALiYunFace.SearchFace(paramDto.Base64, false);
foreach (var body in faces)
{
if (body != null && body.Confidence >= 72.62)
{
var teacher = await _teacherRepository.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.TeacherStatus != TeacherStatus.Depart && x.TeacherPhoneNo == body.EntityId)
.Select(x =>
new Ai_TeacherFaceInfo()
{
Id = x.Id,
Age = x.Age,
//SchoolCode = x.SchoolCode,
Sex = x.Sex,
Phone = x.TeacherPhoneNo,
TeacherName = x.TeacherName,
Photo = x.TeacherPhoto,
}).FirstOrDefaultAsync();
if (teacher == null)
continue;
var grades = await (
from t in _studentRepository.DbContext.Set<S_ClassAssocTeacher>()
join c in _studentRepository.DbContext.Set<S_Class>() on t.ClassId equals c.Id into classGroup
from c in classGroup.DefaultIfEmpty()
where t.TeacherId == teacher.Id && c != null
select new Classes()
{
Id = c.Id,
Name = $"{c.GradeName}-{c.ClassName}",
}).ToListAsync();
teacher.GradeAndClassList = grades;
return teacher;
}
}
throw new Exception("未查询到老师信息");
}
public async Task<List<Ai_StudentListDto>> Ai_StudentList(Ai_StudentListRequest paramDto)
{
var classInfo = await _classRepository.DbContext.Set<JS_Class>().Where(x => x.Id == paramDto.Id).FirstOrDefaultAsync();
var students = await _jingShiTiJiaoService.GetStuListByClass(classInfo?.ClassNo ?? "");
var res = students
.Select((s, index) => new Ai_StudentListDto
{
Code = paramDto.Code,
StudentNo = s.StuNo,
StudentName = s.StuName,
Sex = s.Gender == "男" ? SexType.Male : SexType.Female,
Age = s.Age,
Photo = s.Avatar ?? "",
ClassName = s.ClassName,
GradeName = s.GradeName,
OrderNo = index + 1
})
.OrderBy(x => x.OrderNo)
.ToList();
return res;
}
public async Task<AiStudentDetailsDto> Ai_StudentDetail(Ai_StudentDetailRequest paramDto)
{
// 1️⃣ 调用京师接口获取学生详情
var student = await _jingShiTiJiaoService.GetStudentInfo(paramDto.StudentNo);
if (student == null)
return null;
var schoolCode = student.School;
// 2️⃣ 查询数据库班级(包含 GradeId)
var dbClass = await _classRepository.DbContext.Set<JS_Class>()
.FirstOrDefaultAsync(c => c.SchoolCode == schoolCode && c.ClassNo == student.ClassNo);
// 3️⃣ 映射到 DTO
var res = new AiStudentDetailsDto
{
StudentNo = student.StuNo,
StudentName = student.StuName,
Age = student.Age,
IdCard = student.StuNo,
Sex = student.Gender == "男" ? SexType.Male : SexType.Female,
Photo = "",
ClassId = dbClass?.Id != null ? (int)dbClass.Id : 0,
ClassName = student.ClassName,
// 直接通过班级表关联的年级 Id
GradeId = dbClass?.Grade?.Id != null ? (int)dbClass.Grade.Id : 0,
GradeName = student.GradeName
};
return res;
}
/// <summary>
/// 获取专项教学项目
/// </summary>
public async Task<List<Ai_ItemTypeModel>> TeachingItems(Ai_Request paramDto)
{
var list = await (
from s in _studentRepository.DbContext.Set<Ai_Special>()
select new Ai_ItemTypeModel()
{
ItemCode = s.Id,
ItemTypeName = s.SpecialName
}).ToListAsync();
return list;
}
/// <summary>
/// 获取专项教学项目详情
/// </summary>
public async Task<Ai_TeachingItemsDetailModel> TeachingItemsDetail(Ai_TeachingItemsRequest paramDto)
{
if (paramDto == null || paramDto.ItemCode <= 0)
{
Console.WriteLine("参数为空,跳过...");
return new Ai_TeachingItemsDetailModel();
}
var result = await (
from s in _studentRepository.DbContext.Set<Ai_Special>()
where s.Id == paramDto.ItemCode
select new Ai_TeachingItemsDetailModel
{
BasicSkillList = s.SpecialLevelList
.Select(level => new SpecialLevelBasicSkill
{
Id = level.Id,
Name = level.LevelName,
StartGrade = level.StartGrade,
EndGrade = level.EndGrade,
SpecialActionModes = level.SpecialActionList
.Where(a => a.ActionType == SpecialActionType.BasicSkill)
.Select(a => new SpecialActionBasicSkill
{
Id = a.Id,
Name = a.ActionName,
ActionVideoPath = a.ActionVideoPath,
ExplainVideoPath = a.ExplainVideoPath,
ExerciseVideoPath = a.ExerciseVideoPath,
StandardVideoPath = a.StandardVideoPath
}).ToList()
}).ToList(),
SpecialStaminaList = s.SpecialLevelList
.Select(level => new SpecialLevelSpecialStamina
{
Id = level.Id,
Name = level.LevelName,
StartGrade = level.StartGrade,
EndGrade = level.EndGrade,
SpecialActionModes = level.SpecialActionList
.Where(a => a.ActionType == SpecialActionType.SpecialStamina)
.Select(a => new SpecialActionSpecialStamina
{
Id = a.Id,
Name = a.ActionName,
ActionVideoPath = a.ActionVideoPath
}).ToList()
}).ToList(),
}).FirstOrDefaultAsync();
return result;
}
/// <summary>
/// 获取赛事活动列表
/// </summary>
public async Task<PageDataDto<Ai_ActivitiesListDto>> ActivitiesList(Ai_ActivitiesListRequest paramDto)
{
var res = new PageDataDto<Ai_ActivitiesListDto>();
var query = from s in _studentRepository.DbContext.Set<G_Activities>()
where s.SchoolCode.Equals(paramDto.SchoolCode)
select new Ai_ActivitiesListDto()
{
ActivitiesId = s.Id,
ItemCode = s.CategoryId,
ActivitiesName = s.ActivitiesName,
ItemName = s.CategoryName,
EndDate = s.EndDate,
StartDate = s.StartDate,
ActivitiesStatus = s.ActivitiesStatus,
ImagePath = s.ImagePath,
ModeType = s.ModeType,
DataType = s.DataType
};
if (paramDto.ActivitiesStatus > 0)
{
query = query.Where(x => x.ActivitiesStatus == paramDto.ActivitiesStatus);
}
if (paramDto.ModeType > 0)
{
query = query.Where(x => x.ModeType == paramDto.ModeType);
}
if (paramDto.DataType > 0)
{
query = query.Where(x => x.DataType == paramDto.DataType);
}
res.Total = await query.CountAsync();
res.PageIndex = paramDto.PageIndex;
res.PageSize = paramDto.PageSize;
res.TotalPages = (int)Math.Ceiling((double)res.Total / paramDto.PageSize);
var list = await query
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.ToListAsync();
res.Datas = list;
return res;
}
/// <summary>
/// 获取赛事活动排行
/// </summary>
public async Task<ActivitiesRankingPageDataDto<Ai_ActivitiesRankingDto>> ActivitiesRanking(Ai_ActivitiesRequest paramDto)
{
var res = new ActivitiesRankingPageDataDto<Ai_ActivitiesRankingDto>();
var query = from a in _activitiestDataRepository.DbContext.Set<Ai_ActivitiestData>()
join s in _activitiestDataRepository.DbContext.Set<S_Student>() on a.StudentNo equals s.StudentNo
join c in _activitiestDataRepository.DbContext.Set<S_School>() on s.SchoolCode equals c.SchoolCode
where a.ActivitiesId == paramDto.ActivitiesId
orderby a.Value descending
select new Ai_ActivitiesRankingDto()
{
SchoolName = c.SchoolName,
Score = a.Value.ToString(),
GradeAndClass = $"{a.GradeName}-{a.ClassName}",
Sex = s.Sex,
StudentName = s.StudentName,
Photo = s.Photo,
SportsTestDataType = a.DataType,
CategoryEnumType = a.CategoryValue,
};
res.Total = await query.CountAsync();
res.Name = paramDto.ActivitiesName;
var pagedDataList = await query
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.ToListAsync();
var rankedList = pagedDataList
.Select((item, index) => new Ai_ActivitiesRankingDto
{
SchoolName = item.SchoolName,
Score = item.Score,
GradeAndClass = item.GradeAndClass,
Sex = item.Sex,
StudentName = item.StudentName,
Photo = item.Photo,
SportsTestDataType = item.SportsTestDataType,
CategoryEnumType = item.CategoryEnumType,
Ranking = (paramDto.PageIndex - 1) * paramDto.PageSize + (index + 1)
})
.ToList();
res.Datas = rankedList;
return res;
}
/// <summary>
/// 获取体测排行
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<PageDataDto<Ai_RankingListDto>> SportsTestRanking(Ai_RankingListRequest paramDto)
{
var res = new PageDataDto<Ai_RankingListDto>();
// 构建查询
var query = _sportsTestDataRepository.FindAsIQueryable(x =>
x.CategoryValue == paramDto.ItemCode &&
x.SchoolCode == paramDto.SchoolCode && x.DataType == paramDto.DataType);
if (paramDto.TodayTime.HasValue)
{
var todayStart = paramDto.TodayTime.Value.Date;
var todayEnd = todayStart.AddDays(1).AddTicks(-1);
query = query.Where(x => x.StartTime >= todayStart && x.StartTime <= todayEnd);
}
if (paramDto.GradeId > 0)
{
query = query.Where(x => x.GradeId == paramDto.GradeId);
}
if (paramDto.ClassId > 0)
{
query = query.Where(x => x.ClassId == paramDto.ClassId);
}
// 异步获取数据列表
var rawData = await query.ToListAsync().ConfigureAwait(false);
// 在内存中分组并获取每个分组中最大的值
var data = rawData
.GroupBy(x => x.StudentNo)
.Select(g => g.OrderByDescending(x => x.Value).FirstOrDefault()) // 获取每个学生 Value 最大的数据
.OrderByDescending(x => x.Value)
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.Select(x => new Ai_RankingListDto()
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Value = x.Value,
})
.ToList();
res.Total = data.Count;
// 计算排名
var list = data
.Select((item, index) => new Ai_RankingListDto
{
StudentNo = item.StudentNo,
StudentName = item.StudentName,
Value = item.Value,
Ranking = (paramDto.PageIndex - 1) * paramDto.PageSize + (index + 1)
})
.ToList();
res.Datas = list;
return res;
}
/// <summary>
/// 体测成绩查询
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<PageDataDto<Ai_SportsTestDto>> SportsTestResult(Ai_SportsTestRequest paramDto)
{
var res = new PageDataDto<Ai_SportsTestDto>();
var query = from a in _sportsTestDataRepository.FindAsIQueryable(x =>
x.CategoryValue == paramDto.ItemCode &&
x.SchoolCode == paramDto.SchoolCode)
//.Include(c => c.FileList)
orderby a.ScoreTime descending
select new Ai_SportsTestDto()
{
//IfVideo = a.FileList.Count > 0,
//FileList = _mapper.Map<List<FileList>>(a.FileList),
Value = a.Value,
SportsTestDataType = a.DataType,
CategoryEnumType = a.CategoryValue,
TestTime = a.ScoreTime,
StudentNo = a.StudentNo,
StudentName = a.StudentName
};
res.Total = await query.CountAsync();
var list = await query
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.ToListAsync();
res.Datas = list;
return res;
}
/// <summary>
/// 训练成绩上传
/// </summary>
public async Task TrainingDataUpload(Ai_ResultUploadRequest paramDto)
{
//var trainingDataIds = paramDto.Select(c => c.TrainingDataId).ToList();
//if (paramDto == null)
// throw new ArgumentNullException($"参数为空");
//if (trainingDataIds.Any(c => c < 0))
// throw new ArgumentNullException($"训练数据Id必传");
//var trainingDatas = await _studentRepository.FindAsync<Ai_TrainingData>(s =>
// s.SchoolCode.Equals(UserContext.Current.TenantId) &&
// trainingDataIds.Contains(s.Id)
//);
//// 判断是否所有训练详情都有训练数据
//if (!trainingDataIds.All(c => trainingDatas.Select(x => x.Id).Contains(c)))
// throw new ArgumentNullException($"存在未找到的训练数据Id");
//var students = await _studentRepository.FindAsync<S_Student>(s =>
// s.SchoolCode.Equals(UserContext.Current.TenantId) &&
// paramDto.Select(c => c.StudentNo).Contains(s.StudentNo)
//);
//// 判断是否都有学号
//if (!students.Select(c => c.StudentNo).All(c => students.Select(s => s.StudentNo).Contains(c)))
// throw new ArgumentNullException($"存在未找到的学号");
//var trainingDetailsData = paramDto.Select(c => new Ai_TrainingData
//{
// StudentNo = c.StudentNo,
// StudentName = students.First(d => d.StudentNo == c.StudentNo).StudentName,
// Sex = students.First(d => d.StudentNo == c.StudentNo).Sex,
// Value = c.Value,
// MotionDuration = c.MotionDuration,
// Consumption = c.Consumption,
// Strength = c.Strength,
// Score = c.Score,
// AdditionalScore = c.AdditionalScore,
// Rank = c.Rank,
// RankStr = c.RankStr,
// ErrorNumber = c.ErrorNumber,
// CorrectNumber = c.CorrectNumber,
// MaxValue = c.MaxValue,
// MinValue = c.MinValue
//}).ToList();
//_studentRepository.AddRange<Ai_TrainingData>(trainingDetailsData);
//await _studentRepository.SaveChangesAsync();
if (paramDto == null)
{
Console.WriteLine("参数为空,跳过...");
return;
}
var entity = _mapper.Map<Ai_TrainingData>(paramDto);
await _trainingDataRepository.AddAsync(entity);
await _trainingDataRepository.SaveChangesAsync();
}
/// <summary>
/// 体测成绩上传
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task SportsTestResultUpload(SportsTestResultUploadRequest paramDto)
{
if (paramDto == null)
{
Console.WriteLine("参数为空,跳过...");
return;
}
var entity = _mapper.Map<Ai_SportsTestData>(paramDto);
var student = await (from s in _studentRepository.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.StudentNo == paramDto.StudentNo)
join c in _studentRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
select new
{
s.ClassId,
s.ClassName,
s.Sex,
s.StudentName,
c.GradeId,
c.GradeName
}).FirstOrDefaultAsync();
if (student == null)
{
Console.WriteLine("未找到学生信息,跳过...");
return;
}
SemesterDto semesterDto = new SemesterDto();
entity.Year = semesterDto.Year;
entity.Semester = semesterDto.Semester;
entity.ScoreTime = paramDto.StartTime;
entity.GradeId = student.GradeId;
entity.ClassId = student.ClassId;
entity.GradeName = student.GradeName;
entity.ClassName = student.ClassName;
entity.StudentName = student.StudentName;
entity.Sex = student.Sex;
entity.IsDisplay = true;
var categoryEnum = Tool.GetEnumNameByValue(paramDto.CategoryValue);
var healthStandards = await _healthStandardsRepository.FindAsync(x => x.CategoryEnum == categoryEnum);
var standard = healthStandards.Where(x =>
x.CategoryEnum.Equals(categoryEnum) &&
x.GradeId == student.GradeId &&
x.Sex == student.Sex &&
paramDto.Value >= x.MinValue &&
paramDto.Value < x.MaxValue
).FirstOrDefault();
if (standard != null)
{
entity.Score = standard.Score;
entity.Rank = standard.Rank;
entity.RankEnum = Tool.GetEnumByRankName(standard.Rank);
entity.AdditionalScore = entity.Score == 100 ? 20 : 0;
}
await _sportsTestDataRepository.AddAsync(entity);
await _sportsTestDataRepository.SaveChangesAsync();
}
/// <summary>
/// Ai赛场成绩上传
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task ActivitiestResultUpload(Ai_ActivitiestResultUploadRequest paramDto)
{
if (paramDto == null)
{
Console.WriteLine("参数为空,跳过...");
return;
}
var entity = _mapper.Map<Ai_ActivitiestData>(paramDto);
SemesterDto semesterDto = new SemesterDto();
entity.Year = semesterDto.Year;
entity.Semester = semesterDto.Semester;
await _activitiestDataRepository.AddAsync(entity);
await _activitiestDataRepository.SaveChangesAsync();
}
/// <summary>
/// 考级测评成绩上传
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task LevelExamResultUpload(LevelExamDataUploadRequest paramDto)
{
if (paramDto == null)
{
Console.WriteLine("参数为空,跳过...");
return;
}
var entity = _mapper.Map<Ai_LevelExamData>(paramDto);
SemesterDto semesterDto = new SemesterDto();
entity.Year = semesterDto.Year;
entity.Semester = semesterDto.Semester;
var student = await (from s in _studentRepository.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.StudentNo == paramDto.StudentNo)
join c in _classRepository.DbContext.Set<S_Class>() on s.ClassId equals c.Id
select new
{
s.ClassId,
s.ClassName,
s.Sex,
s.StudentName,
c.GradeId,
c.GradeName
}).FirstOrDefaultAsync();
if (student == null)
{
Console.WriteLine("未找到学生信息,跳过...");
return;
}
entity.ScoreTime = paramDto.StartTime;
entity.StartTime = paramDto.StartTime;
entity.EndTime = paramDto.EndTime;
entity.GradeId = student.GradeId;
entity.ClassId = student.ClassId;
entity.GradeName = student.GradeName;
entity.ClassName = student.ClassName;
entity.StudentName = student.StudentName;
entity.Sex = student.Sex;
entity.IsDisplay = true;
await _levelExamDataRepository.AddAsync(entity);
await _levelExamDataRepository.SaveChangesAsync();
}
/// <summary>
/// 心率数据上传
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task HeartRateResultUpload(AddHeartRateResultUploadRequest paramDto)
{
if (paramDto == null || paramDto.Datas == null || paramDto.Datas.Count == 0)
{
Console.WriteLine("参数为空或数据为空,跳过处理。");
return;
}
var heartRateDatas = _mapper.Map<List<Ai_HeartRateData>>(paramDto.Datas);
var classIds = paramDto.Datas.Select(x => x.ClassId).Distinct().ToList();
var classList = await _classRepository.DbContext.Set<JS_Class>().Where(x => x.SchoolCode == paramDto.SchoolCode && classIds.Contains((int)x.Id)).ToListAsync();
var classDict = classList.ToDictionary(x => x.Id, x => x);
var timeNow = DateTime.Now;
heartRateDatas.ForEach(heartRateData =>
{
if (classDict.TryGetValue(heartRateData.ClassId, out var classInfo))
{
heartRateData.Code = paramDto.Code;
heartRateData.SchoolCode = paramDto.SchoolCode;
heartRateData.ClassRoomRecordId = paramDto.ClassRoomRecordId;
heartRateData.ClassroomStageId = paramDto.ClassroomStageId;
heartRateData.TeacherId = paramDto.TeacherId;
heartRateData.GradeId = (int)classInfo.GradeId;
heartRateData.GradeName = classInfo.GradeName;
heartRateData.ClassName = classInfo.ClassName;
heartRateData.ScoreTime = timeNow;
heartRateData.IsDisplay = true;
}
});
try
{
await _heartRateDataRepository.AddRangeAsync(heartRateDatas);
await _heartRateDataRepository.SaveChangesAsync();
}
catch (Exception e)
{
_logger.LogError(e, "心率数据保存失败");
throw;
}
// 异步上传心率数据,不阻塞主流程
_ = Task.Run(async () =>
{
try
{
var uploadList = heartRateDatas.Select(hr => new SportsDataPushDto
{
StudentNo = hr.StudentNo,
SportsItem = AppSetting.JingShiTiJiao.SportsItem,
SportsDate = hr.ScoreTime.ToString("yyyy-MM-dd"),
SportsService = AppSetting.JingShiTiJiao.Channel,
SportsScene = "HeartRate",
SportsDetail = JsonConvert.SerializeObject(hr)
}).ToList();
var res = await _jingShiTiJiaoService.PushSportsData(uploadList);
if (res)
{
_logger.LogInformation($"心率数据同步成功: {JsonConvert.SerializeObject(uploadList)}");
}
else
{
_logger.LogError($"心率数据同步失败: {JsonConvert.SerializeObject(uploadList)}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "心率数据上传异常");
}
});
}
/// <summary>
/// 速度跳绳成绩上传
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task FastJumpRopeResultUpload(AddFastJumpRopeResultUploadRequest paramDto)
{
if (paramDto == null || paramDto.Datas == null || paramDto.Datas.Count == 0)
{
Console.WriteLine("参数为空或数据为空,跳过处理。");
return;
}
var fastJumpRopeDatas = _mapper.Map<List<Ai_FastJumpRopeData>>(paramDto.Datas);
var classIds = paramDto.Datas.Select(x => x.ClassId).Distinct().ToList();
var classList = await _classRepository.DbContext.Set<JS_Class>().Where(x => x.SchoolCode == paramDto.SchoolCode && classIds.Contains((int)x.Id)).ToListAsync();
var classDict = classList.ToDictionary(x => x.Id, x => x);
fastJumpRopeDatas.ForEach(data =>
{
if (classDict.TryGetValue(data.ClassId, out var classInfo))
{
data.Code = paramDto.Code;
data.SchoolCode = paramDto.SchoolCode;
data.ClassRoomRecordId = paramDto.ClassRoomRecordId;
data.GroupId = paramDto.GroupId;
data.TeacherId = paramDto.TeacherId;
data.GradeId = (int)classInfo.GradeId;
data.GradeName = classInfo.GradeName;
data.ClassName = classInfo.ClassName;
data.ScoreTime = paramDto.ScoreTime;
data.ModeType = paramDto.ModeType;
data.ModelName = paramDto.ModeType.GetDescription();
data.UniqueId = paramDto.UniqueId;
}
});
await _fastJumpRopeDataRepository.AddRangeAsync(fastJumpRopeDatas);
await _fastJumpRopeDataRepository.SaveChangesAsync();
// 2️⃣ 异步上传到极智体教平台
_ = Task.Run(async () =>
{
try
{
var uploadList = fastJumpRopeDatas.Select(d => new SportsDataPushDto
{
StudentNo = d.StudentNo,
SportsItem = AppSetting.JingShiTiJiao.SportsItem,
SportsDate = d.ScoreTime.ToString("yyyy-MM-dd"),
SportsService = AppSetting.JingShiTiJiao.Channel,
SportsScene = "FastJumpRope",
SportsDetail = JsonConvert.SerializeObject(d)
}).ToList();
var res = await _jingShiTiJiaoService.PushSportsData(uploadList);
if (res)
{
_logger.LogInformation($"速度跳绳数据同步成功: {JsonConvert.SerializeObject(uploadList)}");
}
else
{
_logger.LogError($"速度跳绳数据同步失败: {JsonConvert.SerializeObject(uploadList)}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "速度跳绳数据上传异常");
}
});
}
/// <summary>
/// 速度跳绳测试列表
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<PageDataDto<Ai_FastJumpRopeDto>> FastJumpRopeTestList(Ai_FastJumpRopeRequest paramDto)
{
var res = new PageDataDto<Ai_FastJumpRopeDto>();
var groupedQuery = from a in _fastJumpRopeDataRepository.FindAsIQueryable(x =>
x.SchoolCode == paramDto.SchoolCode &&
(paramDto.ClassRoomRecordId == null || paramDto.ClassRoomRecordId <= 0 || x.ClassRoomRecordId == paramDto.ClassRoomRecordId))
group a by a.GroupId into g
select new
{
GroupId = g.Key,
g.FirstOrDefault().MotionDuration,
g.FirstOrDefault().ModeType,
g.FirstOrDefault().ModelName,
//MotionDuration = g.FirstOrDefault().MotionDuration,
ScoreTime = g.Max(x => x.ScoreTime),
TestItem = Ai_TrainingModuleEnum.FastJumpRope.GetDescription()
};
// 获取总记录数
res.Total = await groupedQuery.CountAsync();
// 分页查询
var list = await groupedQuery
.OrderByDescending(x => x.ScoreTime)
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.Select(x => new Ai_FastJumpRopeDto
{
GroupId = x.GroupId,
ModeType = x.ModeType,
//ModelName = x.ModeType.GetDescription(),
ModelName = x.ModelName,
MotionDuration = x.MotionDuration,
ScoreTime = x.ScoreTime,
TestItem = x.TestItem
})
.ToListAsync();
res.Datas = list;
return res;
}
/// <summary>
/// 速度跳绳成绩排行
/// </summary>
/// <param name="uniqueId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<PageDataDto<Ai_FastJumpRopeStudentList>> FastJumpRopeRanking(Ai_FastJumpRopeRankingRequest paramDto)
{
var res = new PageDataDto<Ai_FastJumpRopeStudentList>();
// 构建查询
//var query = _fastJumpRopeDataRepository.FindAsIQueryable(x =>
// x.ClassRoomRecordId == paramDto.ClassRoomRecordId &&
// x.SchoolCode == paramDto.SchoolCode &&
// (paramDto.ModeType != Ai_FastJumpRopeModeEnum.FollowMode || x.GroupId == paramDto.GroupId)
// );
var query = _fastJumpRopeDataRepository.FindAsIQueryable(x =>
x.ClassRoomRecordId == paramDto.ClassRoomRecordId &&
x.SchoolCode == paramDto.SchoolCode && x.GroupId == paramDto.GroupId);
// 异步获取数据列表
var rawData = await query.ToListAsync();
// 在内存中分组并获取每个分组中最大的值
var data = rawData
.GroupBy(x => x.StudentNo)
.Select(g => g.OrderByDescending(x => x.JumpValue).FirstOrDefault())
.OrderByDescending(x => x.JumpValue)
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.Select(x => new Ai_FastJumpRopeStudentList()
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
JumpValue = x.JumpValue,
ErrorNumber = x.ErrorNumber
})
.ToList();
res.Total = data.Count;
// 计算排名
var list = data
.Select((item, index) => new Ai_FastJumpRopeStudentList
{
StudentNo = item.StudentNo,
StudentName = item.StudentName,
JumpValue = item.JumpValue,
ErrorNumber = item.ErrorNumber,
Ranking = (paramDto.PageIndex - 1) * paramDto.PageSize + (index + 1)
})
.ToList();
res.Datas = list;
return res;
}
/// <summary>
/// 新增课堂记录
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<Ai_ClassRoomRecordDto> AddClassRoomRecord(Ai_ClassRoomRecordRequest paramDto)
{
if (paramDto == null)
{
Console.WriteLine("参数为空或数据为空,跳过处理。");
return new Ai_ClassRoomRecordDto();
}
var entity = _mapper.Map<Ai_ClassRoomRecord>(paramDto);
var ids = paramDto.ClassroomStudentRecord.Select(x => x.ClassId).ToList();
ids.Add(paramDto.ClassId);
var classInfos = await _classRepository.DbContext.Set<JS_Class>().Where(x => ids.Contains((int)x.Id)).ToListAsync();
var classInfo = classInfos.FirstOrDefault(x => x.Id == paramDto.ClassId);
if (classInfo == null)
{
Console.WriteLine("班级信息数据为空,跳过处理。");
return new Ai_ClassRoomRecordDto();
}
var teacher = await _classRepository.DbContext.Set<JS_Teacher>().Where(x => x.Id == paramDto.TeacherId).FirstOrDefaultAsync();
if (teacher == null)
{
Console.WriteLine("未找到老师信息");
return new Ai_ClassRoomRecordDto();
}
entity.TeacherNo = teacher.TeacherNo;
entity.ClassName = classInfo.ClassName;
entity.GradeId = (int)classInfo.GradeId;
entity.GradeName = classInfo.GradeName;
//entity.Name = $"{paramDto.TrainingModuleEnum.GetDescription()}-{paramDto.StartTime.ToUnixTimeMilliseconds()}";
entity.Name = paramDto.ClassroomName;
SemesterDto semesterDto = new SemesterDto();
entity.Year = semesterDto.Year;
entity.Semester = semesterDto.Semester;
foreach (var student in entity.ClassroomStudentRecord)
{
var StuClass = classInfos.First(x => x.Id == student.ClassId);
if (StuClass == null)
{
Console.WriteLine("班级信息数据为空,跳过处理。");
return new Ai_ClassRoomRecordDto();
}
student.ClassName = StuClass.ClassName;
student.GradeId = (int)StuClass.GradeId;
student.GradeName = StuClass.GradeName;
}
await _classRoomRecordRepository.AddAsync(entity);
await _classRoomRecordRepository.SaveChangesAsync();
var classroomSettingList = new List<Ai_ClassroomSetting>();
if (entity.Id > 0)
{
foreach (var item in paramDto.ClassroomSettingList)
{
if (item.Density == 0 || item.Duration == 0)
continue;
classroomSettingList.Add(new Ai_ClassroomSetting()
{
ClassRoomRecordId = entity.Id,
ClassroomStageId = item.ClassroomStageId,
Density = item.Density,
Duration = item.Duration
});
}
if (classroomSettingList.Count > 0)
{
await _classroomSettingRepository.AddRangeAsync(classroomSettingList);
await _classroomSettingRepository.SaveChangesAsync();
}
}
return new Ai_ClassRoomRecordDto()
{
ClassRoomRecordId = entity.Id,
ClassRoomRecordName = entity.Name
};
}
/// <summary>
/// 结束授课
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task EndTeaching(EndTeachingRequest paramDto)
{
if (paramDto == null)
{
Console.WriteLine("参数为空,跳过...");
return;
}
var entity = await _classRoomRecordRepository.FindAsyncFirst(x => x.Id == paramDto.ClassRoomRecordId && x.SchoolCode == paramDto.SchoolCode);
if (entity != null)
{
entity.EndTime = paramDto.EndTime;
_classRoomRecordRepository.Update(entity);
await _classRoomRecordRepository.SaveChangesAsync();
}
//if (paramDto.HeartRateResults != null && paramDto.HeartRateResults.Count > 0)
//{
// var entitys = _mapper.Map<List<Ai_HeartRateData>>(paramDto);
// await _heartRateDataRepository.AddRangeAsync(entitys);
// await _heartRateDataRepository.SaveChangesAsync();
//}
}
/// <summary>
/// 获取学生当前等级
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<StatusLevelExamDto> StatusLevelExam(StatusLevelExamRequest paramDto)
{
var maxRankModel = await _levelExamDataRepository.FindAsIQueryable(x => x.SpecialId == paramDto.SpecialId && x.StudentNo == paramDto.StudentNo && x.AssessmentResults == AssessmentResultsEnum.Pass).OrderByDescending(x => x.Rank).FirstOrDefaultAsync();
return new StatusLevelExamDto()
{
Rank = maxRankModel?.Rank ?? 0
};
}
/// <summary>
/// 验证学生是否在报名列表中
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<IsActivityInListDto> IsActivityInList(IsActivityInListRequest paramDto)
{
if (paramDto == null)
throw new ArgumentNullException($"参数为空");
var studentCount = await _studentRepository.DbContext.Set<G_EntryStudent>().Where(x =>
x.SchoolCode == paramDto.SchoolCode &&
x.ActivitiesId == paramDto.ActivitiesId &&
x.StudentNo == paramDto.StudentNo
).CountAsync();
var res = new IsActivityInListDto()
{
ActivitiesId = paramDto.ActivitiesId,
StudentNo = paramDto.StudentNo,
IsExist = studentCount > 0
};
return res;
}
#endregion
#region 2.0 接口
/// <summary>
/// Ai一体机扫描登录轮询
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
[HttpPost(nameof(ScanCodeLogin))]
[HttpPost(nameof(ScanCodeLogin))]
public async Task<Ai_TeacherFaceInfo> ScanCodeLogin(Ai_Request paramDto)
{
if (paramDto == null || string.IsNullOrWhiteSpace(paramDto.Code) || string.IsNullOrWhiteSpace(paramDto.SchoolCode))
throw new ArgumentException("参数不能为空!");
var schoolCode = paramDto.SchoolCode;
// 1️⃣ 查询扫码登录信息
var loginInfo = await _scanCodeLoginRepository
.FindAsIQueryable(x => x.Code == paramDto.Code && x.SchoolCode == schoolCode)
.FirstOrDefaultAsync();
if (loginInfo == null)
throw new Exception("未查询到登录信息!");
// 2️⃣ 获取京师老师信息
var teacher = await _jingShiTiJiaoService.GetClassListByTeacher(loginInfo.TeacherPhoneNo);
if (teacher == null || string.IsNullOrWhiteSpace(teacher.TeacherNo) || string.IsNullOrWhiteSpace(teacher.TeacherName))
throw new Exception("获取京师老师信息失败!");
// 3️⃣ 确保老师存在数据库
var dbTeacher = await _teacherRepository.DbContext.Set<JS_Teacher>()
.FirstOrDefaultAsync(t => t.TeacherNo == teacher.TeacherNo);
if (dbTeacher == null)
{
dbTeacher = new JS_Teacher
{
TeacherNo = teacher.TeacherNo,
TeacherName = teacher.TeacherName,
SchoolCode = paramDto.SchoolCode,
Code = paramDto.Code
};
await _teacherRepository.DbContext.Set<JS_Teacher>().AddAsync(dbTeacher);
await _teacherRepository.DbContext.SaveChangesAsync(); // 保存后 Id 可用
}
var gradeDtos = teacher.GradeList ?? new List<GradeDto>();
var allGradeNos = gradeDtos
.Where(g => !string.IsNullOrWhiteSpace(g.GradeNo))
.Select(g => g.GradeNo)
.ToList();
// 4️⃣ 查询数据库已有年级和班级
var existGrades = await _studentRepository.DbContext.Set<JS_Grade>()
.Where(x => x.SchoolCode == schoolCode && x.TeacherNo == teacher.TeacherNo && allGradeNos.Contains(x.GradeNo))
.Include(g => g.Classes)
.ToListAsync();
var existClasses = existGrades.SelectMany(g => g.Classes).ToList();
// 5️⃣ 先删除旧班级,再删除旧年级
if (existClasses.Any())
_studentRepository.DbContext.Set<JS_Class>().RemoveRange(existClasses);
if (existGrades.Any())
_studentRepository.DbContext.Set<JS_Grade>().RemoveRange(existGrades);
await _studentRepository.DbContext.SaveChangesAsync();
// 6️⃣ 构造新增年级和班级
var newGrades = gradeDtos
.Where(g => !string.IsNullOrWhiteSpace(g.GradeNo))
.Select(g => new JS_Grade
{
TeacherNo = teacher.TeacherNo,
TeacherName = teacher.TeacherName,
SchoolCode = schoolCode,
Code = paramDto.Code,
GradeNo = g.GradeNo,
GradeName = g.GradeName,
Classes = g.ClassList?
.Where(c => !string.IsNullOrWhiteSpace(c.ClassNo))
.Select(c => new JS_Class
{
TeacherNo = teacher.TeacherNo,
TeacherName = teacher.TeacherName,
SchoolCode = schoolCode,
Code = paramDto.Code,
GradeNo = g.GradeNo,
GradeName = g.GradeName,
ClassNo = c.ClassNo,
ClassName = c.ClassName
}).ToList() ?? new List<JS_Class>()
}).ToList();
if (newGrades.Any())
await _studentRepository.DbContext.Set<JS_Grade>().AddRangeAsync(newGrades);
await _studentRepository.DbContext.SaveChangesAsync();
// 7️⃣ 使用 EF 跟踪的实体直接返回,不用再查询数据库
var dbGrades = newGrades;
var res = new Ai_TeacherFaceInfo
{
Id = (int)dbTeacher.Id,
TeacherName = teacher.TeacherName,
Phone = teacher.TeacherNo,
GradeAndClassList = dbGrades
.SelectMany(g => g.Classes)
.Select(c => new Classes
{
Id = (int)c.Id,
Name = $"{c.GradeName}-{c.ClassName}"
})
.ToList()
};
// 8️⃣ 删除扫码记录
_scanCodeLoginRepository.Delete(loginInfo);
await _scanCodeLoginRepository.SaveChangesAsync();
return res;
}
/// <summary>
/// 登录
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<Ai_TeacherFaceInfo> Login(Ai_LoginRequest paramDto)
{
var userInfo = await _userRepository.FindFirstAsync(x => x.UserName == paramDto.UserName && x.UserPwd == paramDto.Password);
if (userInfo == null)
throw new Exception("用户名或密码错误");
var res = await _teacherRepository.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.TeacherStatus != TeacherStatus.Depart && x.TeacherPhoneNo == userInfo.UserName)
.Select(x =>
new Ai_TeacherFaceInfo()
{
Id = x.Id,
Age = x.Age,
//SchoolCode = x.SchoolCode,
Sex = x.Sex,
Phone = x.TeacherPhoneNo,
TeacherName = x.TeacherName,
Photo = x.TeacherPhoto,
}).FirstOrDefaultAsync();
if (res == null)
throw new Exception("未查询到登陆信息");
var grades = await (
from t in _studentRepository.DbContext.Set<S_ClassAssocTeacher>()
join c in _studentRepository.DbContext.Set<S_Class>() on t.ClassId equals c.Id into classGroup
from c in classGroup.DefaultIfEmpty()
where t.TeacherId == res.Id && c != null
select new Classes()
{
Id = c.Id,
Name = $"{c.GradeName}-{c.ClassName}",
}).ToListAsync();
res.GradeAndClassList = grades;
return res;
}
/// <summary>
/// 退出登录
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<bool> LogOut(LoginOutDto paramDto)
{
var teacher = await _scanCodeLoginRepository.FindAsIQueryable(x => x.Code == paramDto.Code && x.SchoolCode == paramDto.SchoolCode).FirstOrDefaultAsync();
if (teacher == null) return true;
_scanCodeLoginRepository.Delete(teacher);
return await _teacherRepository.SaveChangesAsync() > 0;
}
/// <summary>
/// 获取版本号
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<Ai_AppVersionDto> AiAppVersion(Ai_AppVersionRequest paramDto)
{
var res = await _aiAppVersionRepository.FindAsIQueryable(x => x.AppType == paramDto.AppType)
.Select(x => new Ai_AppVersionDto()
{
Id = x.Id,
AppType = x.AppType,
Remarks = x.Remarks,
Version = x.Version,
Url = x.Url,
CreateDate = x.CreateDate
}).OrderByDescending(x => x.CreateDate)
.FirstOrDefaultAsync();
return res ?? new Ai_AppVersionDto();
}
/// <summary>
/// 获取课堂记录列表
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<PageDataDto<Ai_ClassRoomRecordPageListDto>> ClassRoomRecordPageList(ClassRoomRecordPageListRequest paramDto)
{
var res = new PageDataDto<Ai_ClassRoomRecordPageListDto>();
var query = _classRoomRecordRepository.FindAsIQueryable(x => x.Code == paramDto.Code);
// 获取总记录数
res.Total = await query.CountAsync();
// 分页查询
var list = await query
.OrderByDescending(x => x.StartTime)
.Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
.Take(paramDto.PageSize)
.Select(x => new Ai_ClassRoomRecordPageListDto
{
ClassRoomRecordId = x.Id,
GradeAndClassName = $"{x.GradeName}-{x.ClassName}",
StartEndTime = x.EndTime == null || x.EndTime == DateTime.MinValue ? $"{x.StartTime:yyyy-MM-dd HH:mm}" : $"{x.StartTime:yyyy-MM-dd HH:mm} 至 {x.EndTime:yyyy-MM-dd HH:mm}",
TeacherName = x.TeacherName
}).ToListAsync();
res.Datas = list;
return res;
}
/// <summary>
/// 获取课堂阶段列表
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<List<ClassroomStageDto>> ClassroomStagePageList(Ai_Request paramDto)
{
var res = await _classroomStageRepository.FindAsIQueryable(x => true).Select(x => new ClassroomStageDto()
{
Id = x.Id,
Name = x.Name
}).ToListAsync();
return res;
}
/// <summary>
/// 添加课堂设置
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<bool> AddClassroomSetting(AddClassroomSettingRequest paramDto)
{
var classroomSettingList = new List<Ai_ClassroomSetting>();
foreach (var item in paramDto.ClassroomSettingList)
{
classroomSettingList.Add(new Ai_ClassroomSetting()
{
ClassRoomRecordId = paramDto.ClassRoomRecordId,
ClassroomStageId = item.ClassroomStageId,
Density = item.Density,
Duration = item.Duration
});
}
if (classroomSettingList.Count > 0)
{
await _classroomSettingRepository.AddRangeAsync(classroomSettingList);
return await _classroomSettingRepository.SaveChangesAsync() > 0;
}
return false;
}
/// <summary>
/// 更新课堂设置
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<bool> UpdateClassroomSetting(UpdateClassroomSettingRequest paramDto)
{
var classroomSetting = await _classroomSettingRepository.FindAsyncFirst(x => x.ClassRoomRecordId == paramDto.ClassRoomRecordId && x.ClassroomStageId == paramDto.ClassroomStageId);
if (classroomSetting == null)
return false;
classroomSetting.Density = paramDto.Density;
classroomSetting.Duration = paramDto.Duration;
_classroomSettingRepository.Update(classroomSetting);
return await _classroomSettingRepository.SaveChangesAsync() > 0;
}
/// <summary>
/// 获取课堂心率报告
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<Ai_HeartRateReportDto> HeartRateReport(HeartRateReportRequest paramDto)
{
var res = new Ai_HeartRateReportDto();
var query = from hrd in _teacherRepository.DbContext.Set<Ai_HeartRateData>()
where hrd.ClassRoomRecordId == paramDto.ClassRoomRecordId
&& (paramDto.ClassRoomStageId <= 0 || hrd.ClassroomStageId == paramDto.ClassRoomStageId)
select hrd;
var heartRateDataList = await query.ToListAsync();
//if (heartRateDataList.Count == 0)
// return res;
var classRoom = await _teacherRepository.DbContext.Set<Ai_ClassRoomRecord>().Include(x => x.ClassroomStudentRecord)
.Where(x => x.SchoolCode == paramDto.SchoolCode && x.Id == paramDto.ClassRoomRecordId)
.FirstOrDefaultAsync();
if (classRoom == null)
{
throw new Exception("未找到对应的班级记录");
}
var classroomStageList = await (from s in _classroomSettingRepository.DbContext.Set<Ai_ClassroomSetting>()
join t in _classroomStageRepository.DbContext.Set<Ai_ClassroomStage>()
on s.ClassroomStageId equals t.Id
where s.ClassRoomRecordId == paramDto.ClassRoomRecordId
select new ClassroomStageInfoDto
{
ClassroomStageId = s.ClassroomStageId,
ClassroomStageName = t.Name,
Duration = s.Duration,
Density = s.Density
}).ToListAsync();
res.ClassRoomRecordName = classRoom.Name;
res.ClassroomStageList = classroomStageList;
var classRoomStudent = await _teacherRepository.DbContext.Set<Ai_ClassroomStudentRecord>()
.Where(x => x.SchoolCode == paramDto.SchoolCode && x.ClassRoomRecordId == paramDto.ClassRoomRecordId)
.ToListAsync();
res.GradeAndClass = $"{classRoom.GradeName}-{classRoom.ClassName}";
res.TeacherName = classRoom.TeacherName;
res.StartingEndingTime = $"{(classRoom.StartTime.HasValue ? classRoom.StartTime.Value.ToString("yyyy-MM-dd HH:mm") : string.Empty)}{(classRoom.EndTime.HasValue ? " - " + classRoom.EndTime.Value.ToString("yyyy-MM-dd HH:mm") : string.Empty)}";
res.PeopleNumber = $"{heartRateDataList.GroupBy(x => x.StudentNo).Count()}/{classRoomStudent.Count}";
res.PeopleNumberBySex = $"{classRoomStudent.Count(x => x.Sex == SexType.Male)}/{classRoomStudent.Count(x => x.Sex == SexType.Female)}";
res.AvgHR = heartRateDataList.Count > 0
? $"{(int)(heartRateDataList.Sum(x => x.Value) / heartRateDataList.Count)} 次/分"
: "0 次/分";
res.Consumption = heartRateDataList.Count > 0 ? $"{Math.Abs(heartRateDataList.Sum(x => x.Consumption ?? 0) / heartRateDataList.Count)} 千卡" : "";
//res.Density = $"{(int)(heartRateDataList.Where(x => x.Strength > 50).Sum(x => x.Strength) / heartRateDataList.Count)} %";
res.Density = $"{(int)Tool.CalculatePercentage(heartRateDataList.Count(x => x.Strength > 50), heartRateDataList.Count)} %";
res.HighIntensity = $"{heartRateDataList.Where(x => x.Strength > 50).GroupBy(x => x.StudentNo).Count()} 人";
var studentTrainingRecordList = classRoom.ClassroomStudentRecord.ToList();
List<StudentTrainingRecordDto> studentList = new List<StudentTrainingRecordDto>();
foreach (var item in studentTrainingRecordList)
{
var studentTrainingData = heartRateDataList.Where(x => x.StudentNo == item.StudentNo).ToList();
if (studentTrainingData.Count > 0)
{
var student = new StudentTrainingRecordDto()
{
StudentNo = item.StudentNo,
Age = item.Age,
Sex = item.Sex
};
student.StudentName = studentTrainingData[0].StudentName;
student.AvgHR = (int)(studentTrainingData.Sum(x => x.Value) / studentTrainingData.Count);
student.Density = (int)Tool.CalculatePercentage(studentTrainingData.Count(x => x.Strength > 50), studentTrainingData.Count);
student.Strength = (int)studentTrainingData.Average(x => x.Strength);
student.Consumption = Math.Abs((int)studentTrainingData.Average(x => x.Consumption ?? 0));
studentList.Add(student);
}
}
res.HeartRateNumber = Tool.GetHeartRateNumber(heartRateDataList);
res.HeartRateTrend = Tool.GetHeartRateTrend(heartRateDataList);
res.StudentTrainingRecordList = studentList;
return res;
}
/// <summary>
/// 获取学生心率报告
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<GetStudentClassReportDetailsModel> StudentHeartRateReport(GetStudentClassReportDetailsDto paramDto)
{
var res = new GetStudentClassReportDetailsModel();
var query = from hrd in _teacherRepository.DbContext.Set<Ai_HeartRateData>()
where hrd.SchoolCode == paramDto.SchoolCode &&
hrd.ClassRoomRecordId == paramDto.ClassRoomRecordId &&
hrd.StudentNo == paramDto.StudentNo
select hrd;
var heartRateDataList = await query.Select(x => new { x.GradeName, x.StudentNo, x.StudentName, x.Sex, x.ClassName, x.Value, x.Strength, x.Consumption, x.ScoreTime }).ToListAsync();
if (heartRateDataList.Count == 0)
return res;
var student = heartRateDataList[0];
res.StudentNo = paramDto.StudentNo;
res.StudentName = student.StudentName;
res.Sex = student.Sex;
res.GradeAndClass = $"{student.GradeName}-{student.ClassName}";
res.AvgHR = (int)(heartRateDataList.Sum(x => x.Value) / heartRateDataList.Count);
res.Density = (int)Tool.CalculatePercentage(heartRateDataList.Count(x => x.Strength > 50), heartRateDataList.Count);
res.Strength = (int)heartRateDataList.Average(x => x.Strength);
res.Consumption = Math.Abs((int)heartRateDataList.Average(x => x.Consumption ?? 0));
//var baseTime = heartRateDataList.Min(x => x.ScoreTime);
var baseTime = await _teacherRepository.DbContext.Set<Ai_HeartRateData>().Where(x => x.SchoolCode == paramDto.SchoolCode && x.ClassRoomRecordId == paramDto.ClassRoomRecordId).MinAsync(x => x.ScoreTime);
var heartRateWithMinutes = heartRateDataList
.Select(data => new
{
Data = data,
MinuteBucket = (int)(data.ScoreTime - baseTime).TotalMinutes
})
.ToList();
var maxMinute = heartRateWithMinutes.Max(x => x.MinuteBucket);
for (int minute = 0; minute <= maxMinute; minute++)
{
var minuteData = heartRateWithMinutes
.Where(x => x.MinuteBucket == minute)
.Select(x => x.Data)
.ToList();
if (minuteData.Any())
{
res.HeartRateTrend.Add(new SportsProportionData()
{
Name = $"{minute + 1} 分钟",
Datas = new List<StudentSportsProportionData>
{
new StudentSportsProportionData {
Title = "心率",
Value = (int)minuteData.Average(x => x.Value)
}
}
});
}
}
return res;
}
/// <summary>
/// 跳绳报告
/// </summary>
/// <param name="paramDto"></param>
/// <returns></returns>
public async Task<List<Ai_JumpRopeReportDto>> JumpRopeReport(HeartRateReportRequest paramDto)
{
var rawData = await _fastJumpRopeDataRepository
.FindAsIQueryable(x => x.SchoolCode == paramDto.SchoolCode && x.ClassRoomRecordId == paramDto.ClassRoomRecordId)
.ToListAsync();
var result = rawData
.GroupBy(x => x.GroupId)
.Select(group =>
{
var rankedStudents = group
.OrderByDescending(x => x.JumpValue ?? 0)
.Select((x, index) => new Ai_StudentJumpRopeReportListDto
{
StudentNo = x.StudentNo,
StudentName = x.StudentName,
Ranking = x.Rank ?? 0,
JumpValue = x.JumpValue
}).ToList();
return new Ai_JumpRopeReportDto
{
ModelType = (int)group.First().ModeType,
ModelName = group.First().ModelName,
MotionDuration = group.First().MotionDuration ?? 0,
StudentCount = rankedStudents.Count,
StudentList = rankedStudents
};
})
.ToList();
return result;
}
#endregion
}
}