diff --git a/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs b/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs index 9434ef5..8745436 100644 --- a/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs +++ b/Learn.Archives.API/Controllers/Dto/ExamClassInfoDto.cs @@ -2,7 +2,7 @@ namespace Learn.Archives.API.Controllers.Dto { - public class ImportDto + public class ImportExamDto { /// /// 导入文件 diff --git a/Learn.Archives.API/Controllers/Dto/SchoolBusinessDto.cs b/Learn.Archives.API/Controllers/Dto/SchoolBusinessDto.cs new file mode 100644 index 0000000..d23e1da --- /dev/null +++ b/Learn.Archives.API/Controllers/Dto/SchoolBusinessDto.cs @@ -0,0 +1,79 @@ +using MiniExcelLibs.Attributes; + +namespace Learn.Archives.API.Controllers.Dto +{ + public class SchoolBusinessDto + { + } + public class SchoolBusinessImportError : SchoolBusinessImport + { + + + [ExcelColumnName("错误信息")] + public string Error { get; set; } + } + public class SchoolBusinessImport + { + + [ExcelColumnName("学校")] + public string School { get; set; } + [ExcelColumnName("年级")] + public string Grade { get; set; } + /// + /// 赴校人员 + /// 多个,分割 + /// + [ExcelColumnName("赴校人员")] + public string Users { get; set; } + [ExcelColumnName("赴校时间")] + public DateTime StartTime { get; set; } + [ExcelColumnName("开展会谈")] + public bool IsDiscussion { get; set; } + + [ExcelColumnName("开展会谈情况")] + public string Discussion { get; set; } + + [ExcelColumnName("开展班会")] + public bool IsClassMeeting { get; set; } + [ExcelColumnName("开展班会情况")] + public string ClassMeeting { get; set; } + + + //----------------------------问题列表 + [ExcelColumnName("[问题]学校领导班子")] + public string Q学校领导班子 { get; set; } + [ExcelColumnName("[问题]双师课堂")] + public string Q双师课堂 { get; set; } + [ExcelColumnName("[问题]设备")] + public string Q设备 { get; set; } + [ExcelColumnName("[问题]学生")] + public string Q学生 { get; set; } + [ExcelColumnName("[问题]其他")] + public string Q其他 { get; set; } + + //----------------------------解决办法 + [ExcelColumnName("[解决]学校领导班子")] + public string P学校领导班子 { get; set; } + [ExcelColumnName("[解决]双师课堂")] + public string P双师课堂 { get; set; } + [ExcelColumnName("[解决]设备")] + public string P设备 { get; set; } + [ExcelColumnName("[解决]学生")] + public string P学生 { get; set; } + [ExcelColumnName("[解决]其他")] + public string P其他 { get; set; } + + + //----------------------------额外需求 + [ExcelColumnName("需求/方案")] + public string Solution { get; set; } + [ExcelColumnName("沟通时间/执行记录")] + public string Record { get; set; } + [ExcelColumnName("完结记录")] + public string EndRecord { get; set; } + [ExcelColumnName("备注")] + public string Remark { get; set; } + + + } +} diff --git a/Learn.Archives.API/Controllers/ExamClassInfoController.cs b/Learn.Archives.API/Controllers/ExamClassInfoController.cs index 3c5d3cf..30ae640 100644 --- a/Learn.Archives.API/Controllers/ExamClassInfoController.cs +++ b/Learn.Archives.API/Controllers/ExamClassInfoController.cs @@ -2,6 +2,7 @@ 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 MiniExcelLibs; @@ -33,6 +34,7 @@ namespace Learn.Archives.API.Controllers this.examService = examService; this.schoolService = schoolService; } + [NonAction] private Dictionary? ImportExamInfoSubjectDic(ImportExamInfo info) { @@ -49,6 +51,7 @@ namespace Learn.Archives.API.Controllers { SubjectEnum.地理, info.地理}, }; } + /// /// 导入考试信息 /// @@ -56,7 +59,7 @@ namespace Learn.Archives.API.Controllers /// [HttpPost, ResultIgnore] [HttpLogEnable] - public async Task Import(ImportDto dto) + public async Task Import(ImportExamDto dto) { var exam = await examService.GetByIdAsync(dto.eId); @@ -164,6 +167,7 @@ namespace Learn.Archives.API.Controllers PeopleCount = classUserArr.Count(), MinScore = int.MaxValue, EntryPersonId = userInfo.Id, + BaseSchoolScore = exam.BaseSchoolScore, EntryPerson = userInfo.Name, }; insertClassInfo.Add(eCInfo); @@ -287,5 +291,29 @@ namespace Learn.Archives.API.Controllers } + public override Task PageList([FromBody] QueryRequestBase model) + { + var c = model.Conditions.FirstOrDefault(s => s.FieldName == "Grade"); + if (c != null) + { + var gInfo = GradeHelper.GetStudentGradeBaseByGrade(c.FieldValue); + if (gInfo != null) + { + model.Conditions = model.Conditions.Where(s => s != c).ToList(); + model.Conditions.Add(new SqlSugar.ConditionalModel() + { + FieldName = "GradeLevel", + FieldValue = gInfo.GradeLevel, + }); + model.Conditions.Add(new SqlSugar.ConditionalModel() + { + FieldName = "GradeYear", + FieldValue = gInfo.GradeYear.ToString(), + CSharpTypeName="int" + }); + } + } + return base.PageList(model); + } } } diff --git a/Learn.Archives.API/Controllers/SchoolBusinessController.cs b/Learn.Archives.API/Controllers/SchoolBusinessController.cs index c4a0ff8..16dab34 100644 --- a/Learn.Archives.API/Controllers/SchoolBusinessController.cs +++ b/Learn.Archives.API/Controllers/SchoolBusinessController.cs @@ -3,25 +3,36 @@ 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 Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using MiniExcelLibs; using SqlSugar; +using Swashbuckle.AspNetCore.SwaggerGen; using System.Diagnostics; using System.Security.Claims; +using System.Text.RegularExpressions; using UserCenter.Model; using UserCenter.Model.Common; +using UserCenter.Model.Enum; namespace Learn.Archives.API.Controllers { public class SchoolBusinessController : BackController { readonly Repository baseService; - public SchoolBusinessController(Repository baseService) : base(baseService) + readonly Repository schoolService; + readonly Repository adminService; + readonly IHttpContextAccessor accessor; + public SchoolBusinessController(Repository baseService, Repository schoolService, IHttpContextAccessor accessor, Repository adminService) : base(baseService) { this.baseService = baseService; + this.schoolService = schoolService; + this.accessor = accessor; + this.adminService = adminService; } - public class QueryPageDto + public class QueryPageDto { /// /// 学校id @@ -32,13 +43,13 @@ namespace Learn.Archives.API.Controllers /// public string? Grade { get; set; } /// - /// 赴校人员名称 + /// 赴校人员ID /// - public string? UserName { get; set; } + public long? UserId { get; set; } /// /// 是否查询完结 /// - public bool SolutionEnd { get; set; } + public bool? SolutionEnd { get; set; } public DateTime? StartTime { get; set; } public DateTime? EndTime { get; set; } @@ -57,7 +68,7 @@ namespace Learn.Archives.API.Controllers { if (model.SolutionRecord != null && model.SolutionRecord.SolutionEnd == true) model.SolutionEnd = true; - if (!string.IsNullOrEmpty(model._grade)) + if (!string.IsNullOrEmpty(model._grade)) { var g = GradeHelper.GetStudentGradeBaseByGrade(model._grade); model.GradeLevel = g.GradeLevel; @@ -71,19 +82,183 @@ namespace Learn.Archives.API.Controllers /// /// [HttpPost] - public async Task> QueryPageList(QueryPageDto dto) + public async Task> QueryPageList(QueryPageDto dto) { + + var uid = dto.UserId.ToString(); + var gInfo = GradeHelper.GetStudentGradeBaseByGrade(dto.Grade); RefAsync total = 0; var resData = await baseService.AsQueryable() .WhereIF(dto.SchoolId != 0, s => s.SchoolId == dto.SchoolId) - .WhereIF(!string.IsNullOrEmpty(dto.Grade), s => s.Grade == dto.Grade) - .WhereIF(dto.SolutionEnd, s => s.SolutionEnd == dto.SolutionEnd) + .WhereIF(gInfo != null, s => s.GradeLevel == gInfo.GradeLevel && s.GradeYear == gInfo.GradeYear) + .WhereIF(dto.SolutionEnd != null, s => s.SolutionEnd == dto.SolutionEnd) .WhereIF(dto.StartTime != null, s => s.StartTime >= dto.StartTime) .WhereIF(dto.EndTime != null, s => s.StartTime <= dto.EndTime) - .WhereIF(!string.IsNullOrEmpty(dto.UserName), s => s.SchoolBusinessUser != null && SqlFunc.JsonLike(s.SchoolBusinessUser, dto.Grade)) - .OrderByDescending(s=>s.Id) + .WhereIF(uid != null, s => s.SchoolBusinessUser != null && SqlFunc.JsonLike(s.SchoolBusinessUser, uid)) + .OrderByDescending(s => s.Id) .ToPageListAsync(dto.PageIndex, dto.PageSize, total); return new PageResult() { Data = resData, Total = total }; } + + + + /// + /// 导入考试信息 + /// + /// + /// + [HttpPost, ResultIgnore] + [HttpLogEnable] + public async Task Import(IFormFile? file) + { + + 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.School)); + } + if (dataList == null || dataList.Count() == 0) + Oh.ModelError("导入失败:无有效数据"); + + //todo + //处理数据 + var errorExcelInfo = new List(); + var insertInfo = new List(); + var db = schoolService.Context; + foreach (var imp in dataList) + { + var school = await schoolService.GetFirstAsync(s => s.Name == imp.School); + if (school == null) + { + imp.Error = $"导入失败:学校 [{imp.School}] 未找到!"; + errorExcelInfo.Add(imp); + continue; + } + var gradeInfo = GradeHelper.GetStudentGradeBaseByGrade(imp.Grade); + if (gradeInfo == null) + { + imp.Error = $"导入失败:学校 [{imp.School}] 年级[{imp.Grade}]不符合规范!"; + errorExcelInfo.Add(imp); + continue; + } + var adminNameArr = imp.Users.Split(","); + if (adminNameArr == null) + { + imp.Error = $"赴校人员未能识别"; + errorExcelInfo.Add(imp); + continue; + } + var adminArr = await adminService.AsQueryable() + .Where(s => adminNameArr.Contains(s.Name)) + .Select(s => s.Id).ToArrayAsync(); + var qStr = new Dictionary() + { + {FeedbackQuestionTypeEnum.学校领导班子,(imp.Q学校领导班子,imp.P学校领导班子) }, + {FeedbackQuestionTypeEnum.双师课堂,(imp.Q双师课堂,imp.P双师课堂) }, + {FeedbackQuestionTypeEnum.设备,(imp.Q设备,imp.P设备) }, + {FeedbackQuestionTypeEnum.学生,(imp.Q学生,imp.P学生) }, + {FeedbackQuestionTypeEnum.其他,(imp.Q其他,imp.P其他) } + }; + var resultQuestion = new List(); + var regex = new Regex(@"(问题\d+):(.*?)(?=;|$)", RegexOptions.Singleline); + foreach (var kvp in qStr) + { + var questionType = kvp.Key; + var questions = kvp.Value.Questions; + var solutions = kvp.Value.Solutions; + // 解析问题 + var questionMatches = regex.Matches(questions); + var solutionMatches = regex.Matches(solutions); + // 创建字典以便快速查找解决方案 + var solutionDict = new Dictionary(); + foreach (Match match in solutionMatches) + if (match.Groups.Count == 3) + solutionDict[match.Groups[1].Value] = match.Groups[2].Value; + // 创建DTO对象 + foreach (Match match in questionMatches) + { + if (match.Groups.Count == 3) + { + var sort = match.Groups[1].Value; + var question = match.Groups[2].Value; + + solutionDict.TryGetValue(sort, out var solution); + + resultQuestion.Add(new FeedbackQuestionsDto + { + QuestionType = questionType, + Sort = sort, + Question = question, + Solution = solution, + EndTime = null // 根据需求设置解决时间 + }); + } + } + } + var feedbackQuestions = resultQuestion.ToArray(); + + //沟通时间/执行记录 + var regex1 = new Regex(@"(.*?):(.*?)(?=;|$)", RegexOptions.Singleline); + var record = new List(); + foreach (Match match in regex1.Matches(imp.Record)) + { + record.Add(new RecordDto() + { + ExecutionTime = match.Groups[1].Value.ExtractDateTime(), + ExecutionRecords = match.Groups[2].Value, + }); + } + + var business = new SchoolBusiness() + { + SchoolId = school.Id, + SchoolName = school.Name, + GradeLevel = gradeInfo.GradeLevel, + GradeYear = gradeInfo.GradeYear, + ClassMeeting = imp.ClassMeeting, + Discussion = imp.Discussion, + SchoolBusinessUser = adminArr, + Remark = imp.Remark, + StartTime = imp.StartTime, + FeedbackQuestions = feedbackQuestions, + SolutionRecord = new SolutionRecordDto() + { + EndRecord = imp.EndRecord, + EndRecordTime = imp.EndRecord.ExtractDateTime(), + Solution = imp.Solution, + Record = record, + } + }; + + + }; + if (errorExcelInfo.Count != 0) + return File(errorExcelInfo.ExportExcel(), "application/ms-excel"); + + //写入数据库 + + return Ok(); + } + + /// + /// 下载导入成绩模板 + /// + /// + [HttpGet, ResultIgnore, AllowAnonymous] + public IActionResult DwImportTemplate() + { + var resultList = new List() { new SchoolBusinessImport() { } }; + return File(resultList.ExportExcel(), "application/ms-excel"); + } + + + } } diff --git a/Learn.Archives.API/Program.cs b/Learn.Archives.API/Program.cs index 605742e..0a8498b 100644 --- a/Learn.Archives.API/Program.cs +++ b/Learn.Archives.API/Program.cs @@ -9,7 +9,6 @@ using Learn.Archives.API.Expand; using System.ComponentModel; var builder = WebApplication.CreateBuilder(args); - // Add services to the container. builder.Services.AddLogging(loggingBuilder => { diff --git a/Learn.Archives.Core/Common/AppCommon.cs b/Learn.Archives.Core/Common/AppCommon.cs index e572f79..20e821a 100644 --- a/Learn.Archives.Core/Common/AppCommon.cs +++ b/Learn.Archives.Core/Common/AppCommon.cs @@ -102,7 +102,35 @@ namespace Learn.Archives.Core.Common return Convert.ToHexString(hashBytes).ToUpper(); // 或者保留大写 } } + public static DateTime? ExtractDateTime(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return null; + // 定义日期模式的正则表达式 + string pattern = @"(\d{4})[年./\s-](\d{1,2})[月./\s-](\d{1,2})日?"; + var match = Regex.Match(input, pattern); + + if (match.Success && match.Groups.Count == 4) + { + if (int.TryParse(match.Groups[1].Value, out int year) && + int.TryParse(match.Groups[2].Value, out int month) && + int.TryParse(match.Groups[3].Value, out int day)) + { + try + { + return new DateTime(year, month, day); + } + catch (ArgumentOutOfRangeException) + { + // 处理无效日期(如2月30日) + return null; + } + } + } + + return null; + } /// /// 对象转化为JSON字符串 /// diff --git a/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs b/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs index cde4b35..764c0c6 100644 --- a/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs +++ b/Learn.Archives.Core/Model/Dto/SchoolBusinessDto.cs @@ -47,7 +47,7 @@ namespace Learn.Archives.Core.Model.Dto /// /// 执行时间 /// - public DateTime ExecutionTime { get; set; } + public DateTime? ExecutionTime { get; set; } } diff --git a/Learn.Archives.Core/Model/ExamClassInfo.cs b/Learn.Archives.Core/Model/ExamClassInfo.cs index 02a60ed..d687b31 100644 --- a/Learn.Archives.Core/Model/ExamClassInfo.cs +++ b/Learn.Archives.Core/Model/ExamClassInfo.cs @@ -101,6 +101,11 @@ namespace Learn.Archives.Core.Model /// public decimal AverageRank1 { get; set; } + /// + /// 资源校平均分 + /// + public decimal BaseSchoolScore { get; set; } + /// /// 录入人Id diff --git a/Learn.Archives.Core/Model/SchoolBusiness.cs b/Learn.Archives.Core/Model/SchoolBusiness.cs index bddd5b2..e4f3d05 100644 --- a/Learn.Archives.Core/Model/SchoolBusiness.cs +++ b/Learn.Archives.Core/Model/SchoolBusiness.cs @@ -48,7 +48,7 @@ namespace Learn.Archives.Core.Model /// 赴校人员 逗号分隔 /// [SugarColumn(IsNullable = true, IsJson = true)] - public string[]? SchoolBusinessUser { get; set; } + public long[]? SchoolBusinessUser { get; set; } /// /// 赴校时间 /// @@ -56,7 +56,7 @@ namespace Learn.Archives.Core.Model /// /// 赴校备注 /// - [SugarColumn(IsNullable = true)] + [SugarColumn(IsNullable = true, Length =1000)] public string? Remark { get; set; } ///