diff --git a/Learn.Archives.API/Controllers/AdminController.cs b/Learn.Archives.API/Controllers/AdminController.cs index 08a15a1..ae41ea9 100644 --- a/Learn.Archives.API/Controllers/AdminController.cs +++ b/Learn.Archives.API/Controllers/AdminController.cs @@ -3,10 +3,16 @@ using Learn.Archives.API.Controllers.Dto; using Learn.Archives.API.Expand; using Learn.Archives.Core.Common; using Learn.Archives.Core.Model; +using Learn.Archives.Core.Model.Dto; +using Learn.Archives.Core.Model.Enum; +using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using MiniExcelLibs; using System.Diagnostics; using System.Security.Claims; +using System.Text.RegularExpressions; +using UserCenter.Model.Common; namespace Learn.Archives.API.Controllers { @@ -15,11 +21,17 @@ namespace Learn.Archives.API.Controllers readonly Repository baseService; readonly Repository menuRelationDB; readonly Repository menuDB; - public AdminController(Repository baseService, Repository menuRelationDB, Repository menuDB) : base(baseService) + readonly Repository roleDB; + readonly LiveUserInfo userInfo; + readonly IHttpContextAccessor accessor; + public AdminController(Repository baseService, Repository menuRelationDB, Repository menuDB, IHttpContextAccessor accessor, Repository roleDB, LiveUserInfo userInfo = null) : base(baseService) { this.baseService = baseService; this.menuRelationDB = menuRelationDB; this.menuDB = menuDB; + this.accessor = accessor; + this.roleDB = roleDB; + this.userInfo = userInfo; } /// /// 管理员登录 @@ -44,7 +56,7 @@ namespace Learn.Archives.API.Controllers if (admin.Password != model.Password.GetMD5()) Oh.Error("登录失败,密码错误"); // 获取租户信息 - var buttonRole = admin.RoleId==1 + var buttonRole = admin.RoleId == 1 ? ["*:*:*"] : await menuRelationDB.AsQueryable() .LeftJoin((mr, m) => mr.MenuId == m.Id) @@ -73,12 +85,107 @@ namespace Learn.Archives.API.Controllers } - public override Task Edit([FromBody] Admin model) + public override async Task Edit([FromBody] Admin model) { //创建用户时 密码加密 if (model.Id == 0) model.Password = model.Password.GetMD5(); - return base.Edit(model); + if (string.IsNullOrEmpty(model.Account) || model.Account.Length < 2 || + string.IsNullOrEmpty(model.Phone) || model.Phone.Length < 11 || + string.IsNullOrEmpty(model.Name) || model.Phone.Length < 2) + { + Oh.ModelError("账号/手机号/名称 不合法"); + } + if (await baseService.IsAnyAsync(s => s.Account == model.Account && s.Id != model.Id)) + Oh.ModelError($"账号 {model.Account} 已被使用!"); + return await base.Edit(model); + } + + + /// + /// 下载导入模板 + /// + /// + [HttpGet, ResultIgnore, AllowAnonymous] + public IActionResult DwImportTemplate() + { + var resultList = new List() { new AdminImport() + { + Account = "登录账号[建议使用手机号]", + Name = "必填:用户名称", + Phone = "联系方式", + Role = "必填:与系统的角色名称匹配\r\n普通成员 管理员", + Password = "必填: 登录密码", + } }; + return File(resultList.ExportExcel(), "application/ms-excel", + $"导入管理员模板{DateTime.Now.ToString("MMddHHmm")}.xlsx"); + } + + /// + /// 导入考试信息 + /// + /// + [HttpPost, ResultIgnore] + [HttpLogEnable] + public async Task Import(IFormFile? file) + { + if(!userInfo.IsSa) + Oh.ModelError("只允许管理员使用本功能!"); + var fl = file != null ? file : accessor.HttpContext?.Request.Form.Files[0]; + if (fl == null) Oh.ModelError("传入无效的数据"); + if (!Path.GetExtension(fl.FileName).Equals(".xlsx", StringComparison.OrdinalIgnoreCase)) + Oh.ModelError("请选择导入文件为.xlsx的后缀名!"); + //分析excel + IEnumerable dataList; + using var stream = new MemoryStream(); + { + await fl.CopyToAsync(stream); + dataList = stream.Query() + .Where(s => !string.IsNullOrEmpty(s.Account)); + } + if (dataList == null || dataList.Count() == 0) + Oh.ModelError("导入失败:无有效数据"); + + //处理数据 + var accountArr = await baseService.AsQueryable() + .Select(s => s.Account).Distinct() + .ToArrayAsync(); + var accountH = accountArr.ToHashSet(); + var roleDic = await roleDB.AsQueryable() + .ToDictionaryAsync(s => s.Name, s => s.Id); + var errorExcelInfo = new List(); + var insertInfo = new List(); + foreach (var imp in dataList) + { + imp.Account = imp.Account.Trim(); + imp.Phone = imp.Phone.Trim(); + imp.Name = imp.Name.Trim(); + imp.Role = imp.Role.Trim(); + if (accountH.Contains(imp.Account)) + { + imp.Error = $"导入失败:账号已被使用!"; + errorExcelInfo.Add(imp); + continue; + } + else if (!roleDic.ContainsKey(imp.Role)) + { + imp.Error = $"导入失败:无效的 角色名称!"; + errorExcelInfo.Add(imp); + continue; + } + var admin = imp.Adapt(); + admin.Enable = true; + admin.RoleId = (long)roleDic[imp.Role]; + admin.Password = imp.Password.Trim().GetMD5(); + insertInfo.Add(admin); + } + + if (errorExcelInfo.Count != 0) + return File(errorExcelInfo.ExportExcel(), "application/ms-excel" + , $"错误管理员信息{DateTime.Now.ToString("MMddHHmm")}.xlsx"); + //写入数据库 + await baseService.InsertRangeAsync(insertInfo); + return Ok(); } } } diff --git a/Learn.Archives.API/Controllers/Dto/AdminDto.cs b/Learn.Archives.API/Controllers/Dto/AdminDto.cs new file mode 100644 index 0000000..d7b3f72 --- /dev/null +++ b/Learn.Archives.API/Controllers/Dto/AdminDto.cs @@ -0,0 +1,10 @@ +using UserCenter.Model; +using UserCenter.Model.Common; + +namespace Learn.Archives.API.Controllers.Dto +{ + public class ClassDto:Classes + { + public string Grade => GradeHelper.GetGrade(this.GradeLevel, GraduationYear); + } +} diff --git a/Learn.Archives.API/Controllers/Dto/ClassDto.cs b/Learn.Archives.API/Controllers/Dto/ClassDto.cs index d7b3f72..2ed5b38 100644 --- a/Learn.Archives.API/Controllers/Dto/ClassDto.cs +++ b/Learn.Archives.API/Controllers/Dto/ClassDto.cs @@ -1,10 +1,37 @@ -using UserCenter.Model; +using MiniExcelLibs.Attributes; +using UserCenter.Model; using UserCenter.Model.Common; namespace Learn.Archives.API.Controllers.Dto { - public class ClassDto:Classes + + + public class AdminImportError : AdminImport { - public string Grade => GradeHelper.GetGrade(this.GradeLevel, GraduationYear); + + [ExcelColumn(Name = "错误原因", Width = 25)] + public string Error { get; set; } + } + public class AdminImport + { + + [ExcelColumn(Name = "账号", Width = 30)] + public string Account { get; set; } + + [ExcelColumn(Name = "名称", Width = 15)] + public string Name { get; set; } + + [ExcelColumn(Name = "电话号码", Width = 30)] + public string Phone { get; set; } + + [ExcelColumn(Name = "角色", Width = 30)] + public string Role { get; set; } + /// + /// 密码 + /// + [ExcelColumn(Name = "密码", Width = 20)] + public string Password { get; set; } + + } } diff --git a/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs b/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs index a6b065e..a18a342 100644 --- a/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs +++ b/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs @@ -1,7 +1,15 @@ -using MiniExcelLibs.Attributes; +using Learn.Archives.Core.Model; +using MiniExcelLibs.Attributes; namespace Learn.Archives.API.Controllers.Dto { + public class ExamClassTagReq : ExamClassTag + { + /// + /// 学科类型 + /// + public string SubjectStr => SubjectId?.ToString() ?? "总分"; + } public class ImportExamDto { /// @@ -46,11 +54,6 @@ namespace Learn.Archives.API.Controllers.Dto [ExcelColumnName("云校班级号")] public string Class { get; set; } /// - /// - /// - [ExcelColumnName("班级类型")] - public string ClassType { get; set; } - /// /// 学生姓名 /// [ExcelColumnName("学生姓名")] diff --git a/Learn.Archives.API/Controllers/ExamClassInfoController.cs b/Learn.Archives.API/Controllers/ExamClassInfoController.cs index b74a5d7..d9d6c60 100644 --- a/Learn.Archives.API/Controllers/ExamClassInfoController.cs +++ b/Learn.Archives.API/Controllers/ExamClassInfoController.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; using System.Buffers.Text; +using System.Collections.Generic; using System.Diagnostics; using System.Security.Claims; using UserCenter.Model; @@ -25,10 +26,15 @@ namespace Learn.Archives.API.Controllers readonly Repository baseService; readonly Repository examService; readonly Repository schoolService; + readonly Repository tagService; + readonly Repository classTagService; readonly Repository examUserInfoService; readonly LiveUserInfo userInfo; readonly IHttpContextAccessor accessor; - public ExamClassInfoController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContext, Repository examUserInfoService, Repository examService, Repository schoolService) : base(baseService) + public ExamClassInfoController(Repository baseService, LiveUserInfo userInfo, + IHttpContextAccessor httpContext, Repository examUserInfoService, + Repository examService, Repository schoolService, + Repository tagService, Repository classTagService) : base(baseService) { this.baseService = baseService; this.userInfo = userInfo; @@ -36,23 +42,8 @@ namespace Learn.Archives.API.Controllers this.examUserInfoService = examUserInfoService; this.examService = examService; this.schoolService = schoolService; - } - - [NonAction] - private Dictionary? ImportExamInfoSubjectDic(ImportExamInfo info) - { - return new Dictionary() - { - { SubjectEnum.语文, info.语文}, - { SubjectEnum.数学, info.数学}, - { SubjectEnum.英语, info.英语}, - { SubjectEnum.物理, info.物理}, - { SubjectEnum.化学, info.化学}, - { SubjectEnum.生物, info.生物}, - { SubjectEnum.政治, info.政治}, - { SubjectEnum.历史, info.历史}, - { SubjectEnum.地理, info.地理}, - }; + this.tagService = tagService; + this.classTagService = classTagService; } /// @@ -118,14 +109,14 @@ namespace Learn.Archives.API.Controllers !p.DeleteState && !c.DeleteState && !u.DeleteState && !s.DeleteState) .Select((s, c, p, pr, u) => new { - Name = c.Name + c.Type.ToString() + u.RealName, + Name = c.Name + u.RealName, u.Id, }).ToDictionaryAsync(s => s.Name, s => s.Id); //处理学生成绩数据 var userList = dataList.Select(s => { var classInfo = classArr - .FirstOrDefault(x => x.Name == s.Class && x.Type == s.ClassType.ToEnum()); + .FirstOrDefault(x => x.Name == s.Class ); if (classInfo == null) { s.Error = "未能匹配班级"; @@ -134,7 +125,7 @@ namespace Learn.Archives.API.Controllers } var grade = GradeHelper.GetStudentGradeBaseByGrade(s.Grade); var sub = ImportExamInfoSubjectDic(s); - var name = s.Class + s.ClassType.ToEnum().GetHashCode() + s.Student; + var name = s.Class + s.Student; if (!userDic.ContainsKey(name)) { s.Error = "未能匹配到年级班级下对应的学生"; @@ -175,115 +166,11 @@ namespace Learn.Archives.API.Controllers //写入数据库 await examUserInfoService.AsInsertable(insertUserInfo).ExecuteCommandAsync(); - await CalculatingTestResults(exam,examUserInfoService,schoolService); + await CalculatingTestResults(exam,examUserInfoService,schoolService, tagService); return Ok(); } - /// - /// 计算考试成绩 - /// - /// - /// - /// - /// - [NonAction] - public static async Task CalculatingTestResults(Exam exam, Repository eUService, Repository sService) - { - var userInfoArr = await eUService.AsQueryable() - .Where(s => s.ExamId == exam.Id) - .ToArrayAsync(); - - var insertTotalClassInfo = new List(); - var db = sService.Context; - foreach (var schoolArr in userInfoArr.GroupBy(s => s.SchoolId)) - { - - var insertClassInfo = new List(); - var school = await sService.GetFirstAsync(s => s.Id == schoolArr.Key); - var classArr = await db.Queryable().Where(c => c.SchoolId == school.Id && - c.GradeLevel == exam.GradeLevel && - c.GraduationYear == exam.GradeYear && !c.DeleteState).ToArrayAsync(); - - foreach (var classUserArr in userInfoArr.GroupBy(s => s.ClassId)) - { - var classInfo = classArr.First(s => s.Id == classUserArr.Key); - var eCInfo = new ExamClassInfo() - { - ExamId = exam.Id, - ExamName = exam.Name, - SchoolId = classInfo.SchoolId, - SchoolName = school.Name, - ClassId = classInfo.Id, - ClassName = classInfo.Name, - GradeLevel = classInfo.GradeLevel, - GradeYear = classInfo.GraduationYear, - PeopleCount = classUserArr.Count(), - MinScore = int.MaxValue, - MaxScore = -99, - BaseSchoolScore = exam.BaseSchoolScore, - TestPaperType = exam.TestPaperType, - Type = exam.Type, - Average1 = exam.BaseSchoolScore, - }; - insertClassInfo.Add(eCInfo); - var avgTotal = 0m; - foreach (var eUserInfo in classUserArr) - { - var v = eUserInfo.AssignScore; - //上线人数 - if (v >= exam.ScoreLine) - eCInfo.OnLineCount++; - //最大小分 - if (v < eCInfo.MinScore) - eCInfo.MinScore = v; - if (v > eCInfo.MaxScore) - eCInfo.MaxScore = v; - avgTotal += v;//追加得分 - } - //总分平均分 - eCInfo.Average = avgTotal / eCInfo.PeopleCount; - //计算上线率 - eCInfo.OnLineRate = (decimal)eCInfo.OnLineCount / (decimal)eCInfo.PeopleCount; - } - - insertTotalClassInfo.AddRange(insertClassInfo); - } - - { //计算年级上线率排名 - var i = 0; - foreach (var item in insertTotalClassInfo.OrderByDescending(s => s.OnLineRate) - .GroupBy(s => s.OnLineRate)) - { - foreach (var u in item) - u.OnLineRanking = ++i; - } - - //计算年级平均分排名 - i = 0; - foreach (var item in insertTotalClassInfo.OrderByDescending(s => s.Average) - .GroupBy(s => s.Average)) - { - foreach (var u in item) - u.AverageRank = ++i; - } - } - //写入数据库 - var baseDB = eUService.Context; - baseDB.Ado.BeginTran(); - try - { - await baseDB.Deleteable().Where(s=>s.ExamId==exam.Id).ExecuteCommandAsync(); - await baseDB.Insertable(insertTotalClassInfo).ExecuteCommandAsync(); - baseDB.Ado.CommitTran(); - } - catch (Exception ex) - { - baseDB.Ado.RollbackTran(); - Oh.ModelError($"导入失败:写入数据时候出现了异常 [{ex.Message}] !"); - } - - } /// /// 下载导入成绩模板 @@ -296,7 +183,6 @@ namespace Learn.Archives.API.Controllers { School="例子学校[导入时候请删除]", Class="测试班级", - ClassType="普通班级", Grade="高2028", Student="学生姓名", 语文=80.5m, @@ -359,5 +245,217 @@ namespace Learn.Archives.API.Controllers } return base.PageList(model); } + + + + /// + /// 重新计算考试成绩排名 + /// + /// 考试id + /// + [HttpGet] + public async Task RecalculateExamRankings(long examId) + { + if (examId == 0) + Oh.ModelError("传入了异常参数"); + var exam = examService.GetById(examId); + if (exam is null) + Oh.ModelError("无效的考试"); + await CalculatingTestResults(exam, examUserInfoService, schoolService, tagService); + return Ok(); + + } + + + /// + /// 获取班级考试分段排名 + /// + /// 考试id + /// + [HttpGet] + public async Task> ClassRanking(long examId,long classId) + { + if (examId == 0) + Oh.ModelError("传入异常参数"); + if (! await examService.IsAnyAsync(s => s.Id == examId)) + Oh.ModelError("无效的考试"); + var arr = await classTagService.AsQueryable().Where(s => s.ExamId == examId && s.ClassId == classId) + .Select() + .ToArrayAsync(); + return arr.OrderBy(s => s.SubjectId); + } + + + + + + [NonAction] + private Dictionary? ImportExamInfoSubjectDic(ImportExamInfo info) + { + return new Dictionary() + { + { SubjectEnum.语文, info.语文}, + { SubjectEnum.数学, info.数学}, + { SubjectEnum.英语, info.英语}, + { SubjectEnum.物理, info.物理}, + { SubjectEnum.化学, info.化学}, + { SubjectEnum.生物, info.生物}, + { SubjectEnum.政治, info.政治}, + { SubjectEnum.历史, info.历史}, + { SubjectEnum.地理, info.地理}, + }; + } + [NonAction] + private static decimal? SubjectScore(ExamUserInfo info, SubjectEnum? s) + { + switch (s) + { + case null: + return info.AssignScore; + default: + if (info?.SubjectDic is null) + return -999; + return info.SubjectDic.ContainsKey(s.Value) ? info.SubjectDic?[s.Value] : -999; + } + } + + /// + /// 计算考试成绩 + /// + /// + /// + /// + /// + [NonAction] + public static async Task CalculatingTestResults(Exam exam, Repository eUService, + Repository sService, Repository tagService) + { + var userInfoArr = await eUService.AsQueryable() + .Where(s => s.ExamId == exam.Id) + .ToArrayAsync(); + + var insertTotalClassInfo = new List(); + var insertTotalClassTag = new List(); + var eTagArr = await tagService.AsQueryable().Where(s => s.ExamId == exam.Id) + .ToArrayAsync(); + var db = sService.Context; + foreach (var schoolArr in userInfoArr.GroupBy(s => s.SchoolId)) + { + + var school = await sService.GetFirstAsync(s => s.Id == schoolArr.Key); + var classArr = await db.Queryable().Where(c => c.SchoolId == school.Id && + c.GradeLevel == exam.GradeLevel && + c.GraduationYear == exam.GradeYear && !c.DeleteState).ToArrayAsync(); + + foreach (var classUserArr in userInfoArr.GroupBy(s => s.ClassId)) + { + var classInfo = classArr.First(s => s.Id == classUserArr.Key); + var eCInfo = new ExamClassInfo() + { + ExamId = exam.Id, + ExamName = exam.Name, + SchoolId = classInfo.SchoolId, + SchoolName = school.Name, + ClassId = classInfo.Id, + ClassName = classInfo.Name, + GradeLevel = classInfo.GradeLevel, + GradeYear = classInfo.GraduationYear, + PeopleCount = classUserArr.Count(), + MinScore = int.MaxValue, + MaxScore = -99, + BaseSchoolScore = exam.BaseSchoolScore, + TestPaperType = exam.TestPaperType, + Type = exam.Type, + Average1 = exam.BaseSchoolScore, + }; + insertTotalClassInfo.Add(eCInfo); + var avgTotal = 0m; + + //todo 班级分段分析 + var classTagDic = new Dictionary(); + foreach (var eUserInfo in classUserArr) + { + //上线人数 + foreach (var item in eTagArr) + { + var subV = SubjectScore(eUserInfo, item.SubjectId); + if (!classTagDic.TryGetValue(item.Id, out ExamClassTag? tag)) + { + tag = new ExamClassTag() + { + MaxScore = item.MaxScore, + MinScore = item.MinScore, + ClassId = classInfo.Id, + PeopleCount = eCInfo.PeopleCount, + ExamId = exam.Id, + ExamTagId = item.Id, + Name = item.TagName, + SubjectId = item.SubjectId + }; + insertTotalClassTag.Add(tag); + classTagDic.Add(item.Id, tag); + } + //总分分段 + if (subV >= item.MinScore && subV <= item.MaxScore) + { + tag.OnLineCount++; + } + + } + var v = eUserInfo.AssignScore; + //最大小分 + if (v < eCInfo.MinScore) + eCInfo.MinScore = v; + if (v > eCInfo.MaxScore) + eCInfo.MaxScore = v; + avgTotal += v;//追加得分 + } + //总分平均分 + eCInfo.Average = avgTotal / eCInfo.PeopleCount; + } + } + + { + + var i = 0; + //计算班级上线率排名 + foreach (var examTagArr in insertTotalClassTag.GroupBy(s => s.ExamTagId)) + { + i = 0; + foreach (var item in examTagArr.OrderByDescending(s => s.OnLineRate) + .GroupBy(s => s.OnLineRate)) + { + foreach (var u in item) + u.OnLineRanking = ++i; + } + } + + //计算年级平均分排名 + i = 0; + foreach (var item in insertTotalClassInfo.OrderByDescending(s => s.Average) + .GroupBy(s => s.Average)) + { + foreach (var u in item) + u.AverageRank = ++i; + } + } + //写入数据库 + var baseDB = eUService.Context; + baseDB.Ado.BeginTran(); + try + { + await baseDB.Deleteable().Where(s => s.ExamId == exam.Id).ExecuteCommandAsync(); + await baseDB.Deleteable().Where(s => s.ExamId == exam.Id).ExecuteCommandAsync(); + await baseDB.Insertable(insertTotalClassInfo).ExecuteCommandAsync(); + await baseDB.Insertable(insertTotalClassTag).ExecuteCommandAsync(); + baseDB.Ado.CommitTran(); + } + catch (Exception ex) + { + baseDB.Ado.RollbackTran(); + Oh.ModelError($"导入失败:写入数据时候出现了异常 [{ex.Message}] !"); + } + + } } } diff --git a/Learn.Archives.API/Controllers/ExamController.cs b/Learn.Archives.API/Controllers/ExamController.cs index 1b8d7d8..e28a7d7 100644 --- a/Learn.Archives.API/Controllers/ExamController.cs +++ b/Learn.Archives.API/Controllers/ExamController.cs @@ -20,18 +20,20 @@ namespace Learn.Archives.API.Controllers { readonly Repository baseService; readonly Repository schoolService; + readonly Repository tagService; readonly Repository examClassInfoService; readonly Repository examUserInfoService; readonly LiveUserInfo userInfo; - public ExamController(Repository baseService, LiveUserInfo userInfo, - Repository examClassInfoService, Repository examUserInfoService, - Repository schoolService) : base(baseService) + public ExamController(Repository baseService, LiveUserInfo userInfo, + Repository examClassInfoService, Repository examUserInfoService, + Repository schoolService, Repository tagService) : base(baseService) { this.baseService = baseService; this.userInfo = userInfo; this.examClassInfoService = examClassInfoService; this.examUserInfoService = examUserInfoService; this.schoolService = schoolService; + this.tagService = tagService; } public override Task PageList([FromBody] QueryRequestBase model) { @@ -69,7 +71,7 @@ namespace Learn.Archives.API.Controllers } var res =await base.Edit(model); if (res) - await ExamClassInfoController.CalculatingTestResults(model, examUserInfoService, schoolService); + await ExamClassInfoController.CalculatingTestResults(model, examUserInfoService, schoolService, tagService); return res ; } public override async Task Del([FromBody] params long[] ids) diff --git a/Learn.Archives.API/Controllers/ExamTagsController.cs b/Learn.Archives.API/Controllers/ExamTagsController.cs new file mode 100644 index 0000000..2d61097 --- /dev/null +++ b/Learn.Archives.API/Controllers/ExamTagsController.cs @@ -0,0 +1,28 @@ +using Learn.Archives.API.Controllers.Dto; +using Learn.Archives.API.Expand; +using Learn.Archives.Core.Common; +using Learn.Archives.Core.Model; +using Learn.Archives.Core.Model.Dto; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; +using System.Linq; +using System.Security.Claims; +using UserCenter.Model; +using UserCenter.Model.Common; +using UserCenter.Model.Enum; + +namespace Learn.Archives.API.Controllers +{ + /// + /// 年级控制器 + /// + public class ExamTagsController : BackController + { + readonly Repository baseService; + public ExamTagsController(Repository baseService) : base(baseService) + { + this.baseService = baseService; + } + } +} diff --git a/Learn.Archives.API/Controllers/ExamUserInfoController.cs b/Learn.Archives.API/Controllers/ExamUserInfoController.cs index 8b0754e..ca1d860 100644 --- a/Learn.Archives.API/Controllers/ExamUserInfoController.cs +++ b/Learn.Archives.API/Controllers/ExamUserInfoController.cs @@ -6,9 +6,11 @@ using Learn.Archives.Core.Model.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; +using System.Linq; using System.Security.Claims; using UserCenter.Model; using UserCenter.Model.Common; +using UserCenter.Model.Enum; namespace Learn.Archives.API.Controllers { @@ -24,7 +26,7 @@ namespace Learn.Archives.API.Controllers this.baseService = baseService; this.userInfo = userInfo; } - public override Task PageList([FromBody] QueryRequestBase model) + public override async Task PageList([FromBody] QueryRequestBase model) { var c = model.Conditions.FirstOrDefault(s => s.FieldName == "Grade"); if (c != null) @@ -46,7 +48,18 @@ namespace Learn.Archives.API.Controllers }); } } - return base.PageList(model); + var oldSer = model.OrderBy.ToEnum(); + if (oldSer != null) + { + model.OrderBy = "id"; + var res = (PageResult)await base.PageList(model); + if (model.OrderByType == SqlSugar.OrderByType.Asc) + res.Data = res.Data.OrderBy(s => s.SubjectDic?[oldSer.Value]).ToList(); + else + res.Data = res.Data.OrderByDescending(s => s.SubjectDic?[oldSer.Value]).ToList(); + return res; + } + return await base.PageList(model); } } } diff --git a/Learn.Archives.API/Controllers/StudentController.cs b/Learn.Archives.API/Controllers/StudentController.cs index c6a5b8c..cf10306 100644 --- a/Learn.Archives.API/Controllers/StudentController.cs +++ b/Learn.Archives.API/Controllers/StudentController.cs @@ -10,7 +10,9 @@ using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; +using SqlSugar; using System.Diagnostics; +using System.Linq.Expressions; using System.Security.Claims; using System.Text.RegularExpressions; using UserCenter.Model; @@ -20,20 +22,22 @@ using UserCenter.Model.Enum; namespace Learn.Archives.API.Controllers { /// - /// 年级控制器 + /// 学生接口 /// public class StudentController : BackController { private readonly IHttpContextAccessor _httpContextAccessor; readonly Repository baseService; + readonly Repository positionService; readonly UserCenterService _userCenterService; readonly LiveUserInfo userInfo; - public StudentController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContextAccessor, UserCenterService userCenterService) : base(baseService) + public StudentController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContextAccessor, UserCenterService userCenterService, Repository positionService) : base(baseService) { this.baseService = baseService; this.userInfo = userInfo; _httpContextAccessor = httpContextAccessor; _userCenterService = userCenterService; + this.positionService = positionService; } [NonAction] @@ -54,6 +58,42 @@ namespace Learn.Archives.API.Controllers Oh.ModelError("无效数据"); return await baseService.AsQueryable().FirstAsync(s => s.UserCenterId == uid); } + /// + /// 获取职位id + /// 调用流程 用户中心->档案系统 + /// + /// + [HttpPost] + public async Task PosititonIds(PositionIdsReq[] data) + { + if (data == null || data.Count()==0) + Oh.ModelError("无效数据"); + var query = new Expressionable(); + foreach (var pos in data) + { + query = query.Or(q => + q.SchoolId == pos.SchoolId && + q.PositionType == pos.PositionType && + q.GradeLevel == pos.GradeLevel && + q.GraduationYear == pos.GraduationYear && + q.PositionLevel == pos.PositionLevel + && + ( + // PositionLevel == 3:只匹配前三个字段 + (pos.PositionLevel == 3) || + // PositionLevel == 4:再加上 ClassId + (pos.PositionLevel == 4 && q.ClassId == pos.ClassId) || + + // PositionLevel == 5:再加上 ClassId + SubjectId + (pos.PositionLevel == 5 && q.ClassId == pos.ClassId && q.SubjectId == pos.SubjectId) + ) + ); + } + return await positionService.AsQueryable() + .Where(query.ToExpression()) + .Select(p => p.Id).ToArrayAsync(); + } + /// /// 修改用户信息 @@ -181,7 +221,6 @@ namespace Learn.Archives.API.Controllers School = s.School, Grade = gStr, Class = s.Class, - ClassType = s.ClassType??ClassTypeEnum.云校班.ToString(), Phone = s.Phone, RealName = s.RealName, Stages = s.Grade.Contains("初") ? StudentStagesEnum.初中.ToString() : StudentStagesEnum.高中.ToString() @@ -262,7 +301,6 @@ namespace Learn.Archives.API.Controllers School = s.School, Grade = gStr, Class = s.Class, - ClassType = s.ClassType??ClassTypeEnum.云校班.ToString(), ExamNo = s.Id.ToString(), Phone = s.Phone, RealName = s.RealName, @@ -309,11 +347,10 @@ namespace Learn.Archives.API.Controllers { var resultList = new List() { new StudentInfoImport() { - RealName = "导入规范[导入时请删除本列]", + RealName = "导入规范[导入时请删除本行]", School = "必填:与系统匹配", Grade = "必填:可选值\r\n[初一初二初三,高一高二高山]", Class = "必填:与系统匹配\r\n格式:10班[数字+班]", - ClassType = "必填:可选值\r\n[云校班 海豚智学班 蓝鲸智库班 中职班 其他 雅思班 点阵笔班级 移动校园班级 智学班 ...]", Status = "选填 可选值\r\n[就读,退出]", AmountRelief ="选填: 为0则视为 '未申请减免'", ReliefType ="选填: 建卡贫困户\r\n低保户\r\n教师子女 \r\n孤儿\r\n艺体生\r\n残疾学生\r\n领导特殊承诺减免\r\n领导同意的特殊贫困减免", @@ -340,12 +377,11 @@ namespace Learn.Archives.API.Controllers new TeacherInfoImport() { Phone="必填", - RealName = "导入规范[导入时请删除本列]", + RealName = "导入规范[导入时请删除本行]", UserType = "必填 可选值\r\n[年级主任,班主任,教师]", School = "必填:与系统匹配", Grade = "必填:可选值\r\n[初一初二初三,高一高二高山]", Class = "必填:与系统匹配\r\n格式:10班[数字+班]\r\n任教类型是年级主任时不填", - ClassType = "必填:可选值\r\n[云校班 海豚智学班 蓝鲸智库班 中职班 其他 雅思班 点阵笔班级 移动校园班级 智学班 ...]\r\n任教类型是年级主任时不填", Subject = "选填学科", ExamNo ="选填: 填写老师任职信息[不在授课架构中的职务]", }}; diff --git a/Learn.Archives.API/Expand/HttpFilter.cs b/Learn.Archives.API/Expand/HttpFilter.cs index 6353a10..415aede 100644 --- a/Learn.Archives.API/Expand/HttpFilter.cs +++ b/Learn.Archives.API/Expand/HttpFilter.cs @@ -21,6 +21,7 @@ using Learn.Archives.Core.Common; using Learn.Archives.Core.Model.Dto; using Learn.Archives.Core.Model; using SqlSugar.IOC; +using static System.Net.Mime.MediaTypeNames; namespace Learn.Archives.API.Expand { @@ -50,8 +51,32 @@ namespace Learn.Archives.API.Expand this.userInfo = userInfo; } + /// + /// 执行接口前文件做缓存处理 + /// + /// + /// + public void ExecutingFileCached(ActionExecutingContext context) + { + //特殊处理:ResultIgnore,不进行返回结果包装,原样输出 + var endpoint = context.HttpContext.GetEndpoint(); + // 直接返回原始结果,不封装 + if (endpoint?.Metadata.GetMetadata() == null) return; + if (context.HttpContext.Request.HasFormContentType && + context.HttpContext.Request.Form.Files != null && + context.HttpContext.Request.Form.Files.Count() > 0) + { + context.HttpContext.Items["FileCached"]= + context.HttpContext.Request.Form.Files.Select(s => + { + var stream = new MemoryStream(); + s.CopyTo(stream); + stream.Position = 0; + return (s, stream); + }).ToArray(); + } - + } /// /// 执行接口前400 处理 /// @@ -132,43 +157,43 @@ namespace Learn.Archives.API.Expand string request = null; var logId = Yitter.IdGenerator.YitIdHelper.NextId(); - if (!context.Request.Method - .Equals("GET", StringComparison.InvariantCultureIgnoreCase)) + if (!context.Request.Method.Equals("GET", StringComparison.InvariantCultureIgnoreCase)) { - context.Request.EnableBuffering(); //记录请求参数 if (context.Request.Body.CanSeek) { try { - if (context.Request.HasFormContentType && context.Request?.Form?.Files?.Count() > 0) + var fileArr = context.Items.ContainsKey("FileCached") ? context.Items["FileCached"] as (IFormFile file, MemoryStream stream)[] : null; + if (context.Request.HasFormContentType && fileArr != null) { // 设置保存目录(例如:项目根目录下的Uploads文件夹) string uploadsFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadLogs", logId.ToString()); // 创建目录(如果不存在) if (!Directory.Exists(uploadsFolder)) Directory.CreateDirectory(uploadsFolder); - foreach (var file in context.Request.Form.Files) + foreach (var fileInfo in fileArr) { // 生成安全文件名(防止路径遍历攻击) - string uniqueFileName = Guid.NewGuid().ToString().Substring(0, 5) + "_" + Path.GetFileName(file.FileName); - string filePath = Path.Combine(uploadsFolder, uniqueFileName); + string uniqueFileName = Guid.NewGuid().ToString().Substring(0, 5) + "_" + Path.GetFileName(fileInfo.file.FileName); // 保存文件 - using var stream = new FileStream(filePath, FileMode.Create); - await file.CopyToAsync(stream); + using var stream = new FileStream(Path.Combine(uploadsFolder, uniqueFileName), FileMode.Create, FileAccess.Write); + fileInfo.stream.Position = 0; + await fileInfo.stream.CopyToAsync(stream); + fileInfo.stream.Dispose(); } request = $"请求体包含{context.Request.Form.Files.Count()}个文件 目录 {uploadsFolder}"; } else { context.Request.Body.Position = 0; - using var sr = new StreamReader(context.Request.Body); + using var sr = new System.IO.StreamReader(context.Request.Body); request = await sr.ReadToEndAsync(); } } catch (Exception ex) { - request = "处理请求日志时发生了错误 \r\n" + ex.ToString(); + request = "处理请求入参时发生了错误 \r\n" + ex.ToString() + "\r\n 原有请求数据 " + request; } } } @@ -181,7 +206,7 @@ namespace Learn.Archives.API.Expand Request = request, IP = context.Connection?.RemoteIpAddress?.ToString(), ResponseCode = result?.Code ?? -1, - Response = result != null ? JsonSerializer.Serialize(result) : null, + Response = (result != null ? JsonSerializer.Serialize(result) : null) , Authorization = context.Request.Headers.ContainsKey("Authorization") ? context.Request.Headers["Authorization"].ToString() : string.Empty, @@ -193,18 +218,12 @@ namespace Learn.Archives.API.Expand } - - - - - - /// - /// 在Controller的Action执行前执行 - /// - /// - public override void OnActionExecuting(ActionExecutingContext context) + public override async void OnActionExecuting(ActionExecutingContext context) { + + Executing400(context); + ExecutingFileCached(context); base.OnActionExecuting(context); } @@ -214,6 +233,8 @@ namespace Learn.Archives.API.Expand /// public override async void OnActionExecuted(ActionExecutedContext context) { + + try { BaseReturn? res = ApiResultFormatting(context); diff --git a/Learn.Archives.API/Program.cs b/Learn.Archives.API/Program.cs index 0918320..0a3d6b4 100644 --- a/Learn.Archives.API/Program.cs +++ b/Learn.Archives.API/Program.cs @@ -17,17 +17,6 @@ builder.Services.AddLogging(loggingBuilder => loggingBuilder.SetMinimumLevel(LogLevel.Warning); // С־Ϊ Warning }); -builder.Services.AddControllers(options => -{ - // ȫģ͸ֵĬֵ ͳһظʽ - options.Filters.Add(); -}) -.AddJsonOptions(options => -{ - - options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//תʱʹUnicode - options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// ĬСշ null շ -}); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerExpand("ѧУϵͳ"); builder.Configuration.AddAppConfig(args); @@ -43,6 +32,17 @@ builder.Services.AddHttpContextAccessor(); +builder.Services.AddControllers(options => +{ + // ȫģ͸ֵĬֵ ͳһظʽ + options.Filters.Add(); +}) +.AddJsonOptions(options => +{ + + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//תʱʹUnicode + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// ĬСշ null շ +}); var app = builder.Build(); AppCommon.Services = app.Services; diff --git a/Learn.Archives.API/appsettings.Staging.json b/Learn.Archives.API/appsettings.Staging.json index 10408ea..6da4172 100644 --- a/Learn.Archives.API/appsettings.Staging.json +++ b/Learn.Archives.API/appsettings.Staging.json @@ -19,7 +19,7 @@ "OtherDBArr": [ { "ConfigId": 1001, //用户中心 - "ConnectionString": "AllowLoadLocalInfile=true;Server=58.17.132.2;User ID=marking;Password=qwe123!@#;Port=3306;Database=usercenter;CharSet=utf8mb4;Port=43306;pooling=true;SslMode=None;", + "ConnectionString": "AllowLoadLocalInfile=true;Server=58.17.132.2;User ID=marking;Password=qwe123!@#;Port=3306;Database=usercenter_v1;CharSet=utf8mb4;Port=43306;pooling=true;SslMode=None;", "SqlType": "MySql" } ] diff --git a/Learn.Archives.API/appsettings.json b/Learn.Archives.API/appsettings.json index ab25147..76c592f 100644 --- a/Learn.Archives.API/appsettings.json +++ b/Learn.Archives.API/appsettings.json @@ -14,7 +14,7 @@ "DB": { "ConnectionString": "AllowLoadLocalInfile=true;Server=58.17.132.2;User ID=marking;Password=qwe123!@#;Port=3306;Database=learn.archives;CharSet=utf8mb4;Port=43306;pooling=true;SslMode=None;", "SqlType": "MySql", - "UpdateTable": false + "UpdateTable": true }, "AuthKey": { "Secret": "9FAB7AC7-F1DB-4C56-B84F-044055A34AF2", @@ -29,7 +29,7 @@ { "ConfigId": 1001, //用户中心 //"ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=usercenter;CharSet=utf8mb4;pooling=true;SslMode=None;", - "ConnectionString": "AllowLoadLocalInfile=true;Server=58.17.132.2;User ID=marking;Password=qwe123!@#;Port=3306;Database=usercenter;CharSet=utf8mb4;Port=43306;pooling=true;SslMode=None;", + "ConnectionString": "AllowLoadLocalInfile=true;Server=58.17.132.2;User ID=marking;Password=qwe123!@#;Port=3306;Database=usercenter_v1;CharSet=utf8mb4;Port=43306;pooling=true;SslMode=None;", "SqlType": "MySql" } ] diff --git a/Learn.Archives.Core/Common/BasicAuthMiddleware.cs b/Learn.Archives.Core/Common/BasicAuthMiddleware.cs index 9414eec..9705a5f 100644 --- a/Learn.Archives.Core/Common/BasicAuthMiddleware.cs +++ b/Learn.Archives.Core/Common/BasicAuthMiddleware.cs @@ -24,6 +24,9 @@ namespace Learn.Archives.Core.Common public async Task InvokeAsync(HttpContext context) { + if (!context.Request.Body.CanSeek) + context.Request.EnableBuffering(); // 允许重新读取请求体 + if (context.Request.Path.StartsWithSegments("/swagger") && (context.Request.Path.Value?.Contains("swagger.json") ?? true)) { @@ -36,7 +39,6 @@ namespace Learn.Archives.Core.Common if (await IsAuthorized(usernamePassword[0], usernamePassword[1])) { - await _next(context); return; } diff --git a/Learn.Archives.Core/Learn.Archives.Core.csproj b/Learn.Archives.Core/Learn.Archives.Core.csproj index 7f3019b..d41fa79 100644 --- a/Learn.Archives.Core/Learn.Archives.Core.csproj +++ b/Learn.Archives.Core/Learn.Archives.Core.csproj @@ -28,6 +28,6 @@ - + diff --git a/Learn.Archives.Core/Model/Dto/ExamClassDto.cs b/Learn.Archives.Core/Model/Dto/ExamClassDto.cs new file mode 100644 index 0000000..ebe47a1 --- /dev/null +++ b/Learn.Archives.Core/Model/Dto/ExamClassDto.cs @@ -0,0 +1,13 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Model.Dto +{ + + + +} diff --git a/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs b/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs index 374f7fc..62306d0 100644 --- a/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs +++ b/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs @@ -51,7 +51,7 @@ namespace Learn.Archives.Core.Model.Dto /// public DateTime? ExecutionTime { get; set; } /// - /// 操作人 [后台自动赋值提交人名称] + /// 操作人 /// public string? Operator { get; set; } public string? ExecutionTimeStr => ExecutionTime?.ToString("yyyy-MM-dd"); @@ -86,6 +86,10 @@ namespace Learn.Archives.Core.Model.Dto /// 解决时间 /// public DateTime? EndTime { get; set; } + /// + /// 反馈问题的执行记录 + /// + public RecordDto[]? RecordArr { get; set; } public string? EndTimeStr=> EndTime?.ToString("yyyy-MM-dd"); diff --git a/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs b/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs index e1f0b17..19ca055 100644 --- a/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs +++ b/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs @@ -10,6 +10,38 @@ using UserCenter.Model; namespace Learn.Archives.Core.Model.Dto { + + + public class PositionIdsReq + { + /// + /// 学校编号 + /// + public long SchoolId { get; set; } + /// + /// 年级 + /// + public string GradeLevel { get; set; } + /// + /// 毕业届 + /// + public int GraduationYear { get; set; } + + /// + /// 班级编号 + /// + public long? ClassId { get; set; } + + /// + /// 职级 + /// + public int PositionLevel { get; set; } + public int? PositionType { get; set; } = 2; + /// + /// 科目 + /// + public int? SubjectId { get; set; } + } public class TeacherInfoImportError : TeacherInfoImport { /// @@ -51,11 +83,6 @@ namespace Learn.Archives.Core.Model.Dto /// [ExcelColumn(Name = "班级", Width = 15)] public string Class { get; set; } - /// - /// 班级类型 - /// - [ExcelColumn(Name = "班级类型", Width = 20)] - public string ClassType { get; set; } /// /// 科目 @@ -129,11 +156,6 @@ namespace Learn.Archives.Core.Model.Dto /// [ExcelColumn(Name="班级", Width = 15)] public string Class { get; set; } - /// - /// 班级 - /// - [ExcelColumn(Name="班级类型", Width = 20)] - public string ClassType { get; set; } /// /// 减免金额 @@ -263,11 +285,6 @@ namespace Learn.Archives.Core.Model.Dto [ExcelColumn(Name="班级")] public string Class { get; set; } /// - /// 班级 - /// - [ExcelColumn(Name="班级类型")] - public string ClassType { get; set; } - /// /// 科目 /// [ExcelColumn(Name="科目")] diff --git a/Learn.Archives.Core/Model/Exam.cs b/Learn.Archives.Core/Model/Exam.cs index 58f9fbb..9e521a2 100644 --- a/Learn.Archives.Core/Model/Exam.cs +++ b/Learn.Archives.Core/Model/Exam.cs @@ -38,7 +38,8 @@ namespace Learn.Archives.Core.Model /// public string? _grade; /// - /// 年级 + /// 年级 + /// AOP自动转换年级 /// [SugarColumn(IsIgnore = true)] public string Grade diff --git a/Learn.Archives.Core/Model/ExamClassInfo.cs b/Learn.Archives.Core/Model/ExamClassInfo.cs index 4223d4c..8bf0b2d 100644 --- a/Learn.Archives.Core/Model/ExamClassInfo.cs +++ b/Learn.Archives.Core/Model/ExamClassInfo.cs @@ -1,4 +1,5 @@ -using Learn.Archives.Core.Model.Enum; +using Learn.Archives.Core.Model.Dto; +using Learn.Archives.Core.Model.Enum; using Learn.Archives.Core.Model.Interface; using SqlSugar; using System.ComponentModel.DataAnnotations; @@ -71,18 +72,19 @@ namespace Learn.Archives.Core.Model /// public int GradeYear { get; set; } - /// - /// 上线率 考试排名 - /// - public decimal OnLineRanking { get; set; } - /// - /// 上线率 - /// - public decimal OnLineRate { get; set; } - /// - /// 上线人数 - /// - public int OnLineCount { get; set; } + ///// + ///// 上线率 考试排名 + ///// + //public decimal OnLineRanking { get; set; } + ///// + ///// 上线率 + ///// + //public decimal OnLineRate { get; set; } + ///// + ///// 上线人数 + ///// + //public int OnLineCount { get; set; } + /// /// 参加人数 /// diff --git a/Learn.Archives.Core/Model/ExamClassTag.cs b/Learn.Archives.Core/Model/ExamClassTag.cs new file mode 100644 index 0000000..15e490e --- /dev/null +++ b/Learn.Archives.Core/Model/ExamClassTag.cs @@ -0,0 +1,86 @@ +using Learn.Archives.Core.Model.Dto; +using Learn.Archives.Core.Model.Enum; +using Learn.Archives.Core.Model.Interface; +using SqlSugar; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Text.Json; +using UserCenter.Model; +using UserCenter.Model.Common; +using UserCenter.Model.Enum; +using UserCenter.Model.Interface; + +namespace Learn.Archives.Core.Model +{ + /// + /// 班级考试详情 + /// + [SugarTable("examclasstag")] + public class ExamClassTag : EntityBaseId, IDB + { + /// + /// 考试Id + /// + [SugarColumn(Length = 20)] + public long ExamId { get; set; } + + /// + /// 班级id + /// + public long ClassId { get; set; } + + /// + /// 考试标签id + /// + public long ExamTagId { get; set; } + /// + /// 标签名称 + /// + public string Name { get; set; } + + + /// + /// 分段所属学科 + /// 可空 空即为总分分段 + /// + [SugarColumn(IsNullable = true)] + public SubjectEnum? SubjectId { get; set; } + + /// + /// 最小分值 + /// + [SugarColumn(DecimalDigits = 2)] + public decimal MinScore { get; set; } + + /// + /// 最大分值 + /// + [SugarColumn(DecimalDigits = 2)] + public decimal MaxScore { get; set; } + + /// + /// 上线率 考试排名 + /// + public int OnLineRanking { get; set; } + /// + /// 上线率 + /// + [SugarColumn(IsIgnore = true)] + public decimal OnLineRate => (decimal)OnLineCount / (decimal)PeopleCount; + /// + /// 总人数 + /// + public int PeopleCount { get; set; } + /// + /// 上线人数 + /// + public int OnLineCount { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } = DateTime.Now; + + + } +} diff --git a/Learn.Archives.Core/Model/ExamTags.cs b/Learn.Archives.Core/Model/ExamTags.cs new file mode 100644 index 0000000..edb3cde --- /dev/null +++ b/Learn.Archives.Core/Model/ExamTags.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using System.Text; + +using UserCenter.Model; +using UserCenter.Model.Interface; +using SqlSugar; +using UserCenter.Model.Enum; +using Learn.Archives.Core.Model.Interface; + +namespace Learn.Archives.Core.Model +{ + /// + /// 考试成绩分段表 + /// + [SugarTable("examtags")] + public partial class ExamTags : EntityBaseId, IDB + { + public ExamTags() + { + + this.IsDefault = Convert.ToInt32("0"); + this.CreateTime = DateTime.Now; + + } + /// + /// + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public override long Id { get; set; } + + /// + /// 考试编号 + /// + public long ExamId { get; set; } + + + /// + /// 分段所属学科 + /// 可空 空即为总分分段 + /// + [SugarColumn(IsNullable = true)] + public SubjectEnum? SubjectId { get; set; } + + /// + /// 最小分值 + /// + [SugarColumn(DecimalDigits = 2)] + public decimal MinScore { get; set; } + + /// + /// 最大分值 + /// + [SugarColumn(DecimalDigits = 2)] + public decimal MaxScore { get; set; } + + /// + /// 标签名称 + /// + public string TagName { get; set; } + + /// + /// 是否为默认标签 0:否 1:是 + /// + public int IsDefault { get; set; } + + /// + /// 添加时间 + /// + public DateTime CreateTime { get; set; } + + } +} diff --git a/Learn.Archives.Core/Model/SchoolBusiness.cs b/Learn.Archives.Core/Model/SchoolBusiness.cs index a181514..2542666 100644 --- a/Learn.Archives.Core/Model/SchoolBusiness.cs +++ b/Learn.Archives.Core/Model/SchoolBusiness.cs @@ -89,6 +89,12 @@ namespace Learn.Archives.Core.Model [SugarColumn(IsNullable = true, Length = 1000)] public string? ClassMeeting { get; set; } + /// + /// 预计解决时间 + /// + [SugarColumn(IsNullable = true)] + public DateTime? EndTime { get; set; } + /// /// 反馈问题数量 ///