diff --git a/Learn.Archives.API/Controllers/AdminController.cs b/Learn.Archives.API/Controllers/AdminController.cs index f2a3db7..30528cf 100644 --- a/Learn.Archives.API/Controllers/AdminController.cs +++ b/Learn.Archives.API/Controllers/AdminController.cs @@ -150,7 +150,7 @@ namespace Learn.Archives.API.Controllers //处理数据 var accountArr = await baseService.AsQueryable() - .Select(s => s.Account).Distinct() + .Select(s => s.Account ).Distinct() .ToArrayAsync(); var accountH = accountArr.ToHashSet(); var roleDic = await roleDB.AsQueryable() @@ -180,6 +180,8 @@ namespace Learn.Archives.API.Controllers admin.RoleId = (long)roleDic[imp.Role]; admin.Password = imp.Password.Trim().GetMD5(); insertInfo.Add(admin); + //excel内预计新增的账号也计入重复 + accountH.Add(admin.Account); } if (errorExcelInfo.Count != 0) diff --git a/Learn.Archives.API/Controllers/ExamClassInfoController.cs b/Learn.Archives.API/Controllers/ExamClassInfoController.cs index 7de18c1..4c488aa 100644 --- a/Learn.Archives.API/Controllers/ExamClassInfoController.cs +++ b/Learn.Archives.API/Controllers/ExamClassInfoController.cs @@ -57,18 +57,8 @@ namespace Learn.Archives.API.Controllers { var exam = await examService.GetByIdAsync(dto.eId); - var fl = dto.File!=null? dto .File: accessor.HttpContext?.Request.Form.Files[0]; - if (exam == null || 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.Student)); - } + if (exam == null ) Oh.ModelError("为传入有效的数据"); + var dataList = await accessor.ParsingExcelAsync(); if (dataList == null || dataList.Count() == 0) Oh.ModelError("导入失败:无有效数据"); @@ -353,7 +343,7 @@ namespace Learn.Archives.API.Controllers c.GraduationYear == exam.GradeYear && !c.DeleteState) .ToArrayAsync(); - foreach (var classUserArr in userInfoArr.Where(s=>s.SchoolId == school.Id).GroupBy(s => s.ClassId)) + foreach (var classUserArr in schoolArr.GroupBy(s => s.ClassId)) { var classInfo = classArr.FirstOrDefault(s => s.Id == classUserArr.Key); if (classInfo is null) diff --git a/Learn.Archives.API/Controllers/StudentController.cs b/Learn.Archives.API/Controllers/StudentController.cs index 0823dd8..cf57187 100644 --- a/Learn.Archives.API/Controllers/StudentController.cs +++ b/Learn.Archives.API/Controllers/StudentController.cs @@ -31,13 +31,20 @@ namespace Learn.Archives.API.Controllers readonly Repository positionService; readonly UserCenterService _userCenterService; readonly LiveUserInfo userInfo; - public StudentController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContextAccessor, UserCenterService userCenterService, Repository positionService) : base(baseService) + readonly Repository schoolService; + readonly Repository classesService; + readonly Repository userService; + + public StudentController(Repository baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContextAccessor, UserCenterService userCenterService, Repository positionService, Repository schoolService, Repository classesService, Repository userService) : base(baseService) { this.baseService = baseService; this.userInfo = userInfo; _httpContextAccessor = httpContextAccessor; _userCenterService = userCenterService; this.positionService = positionService; + this.schoolService = schoolService; + this.classesService = classesService; + this.userService = userService; } [NonAction] @@ -66,7 +73,7 @@ namespace Learn.Archives.API.Controllers [HttpPost] public async Task PosititonIds(PositionIdsReq[] data) { - if (data == null || data.Count()==0) + if (data == null || data.Count() == 0) Oh.ModelError("无效数据"); var query = new Expressionable(); foreach (var pos in data) @@ -101,7 +108,7 @@ namespace Learn.Archives.API.Controllers /// /// [HttpPost] - public async Task EditInfo(Student e) + public async Task EditInfo(Student e) { if (e == null || e.UserCenterId == 0) Oh.ModelError("无效数据"); @@ -113,7 +120,7 @@ namespace Learn.Archives.API.Controllers { e.Id = has.Id; e.UserCenterId = has.UserCenterId; - await baseService.UpdateAsync(e); + await baseService.UpdateAsync(e); } return e.Id; } @@ -124,9 +131,9 @@ namespace Learn.Archives.API.Controllers [HttpPost] public new async Task> PageList(object data) { - var apiRes =await _userCenterService.CallAPI_GetPageUserList(_httpContextAccessor.HttpContext); - var res= new PageResult() { Data = new List() }; - if (apiRes == null|| apiRes.Data.Count == 0) return res; + var apiRes = await _userCenterService.CallAPI_GetPageUserList(_httpContextAccessor.HttpContext); + var res = new PageResult() { Data = new List() }; + if (apiRes == null || apiRes.Data.Count == 0) return res; res.Total = apiRes.Total; var uids = apiRes.Data.Select(s => s.Id).ToArray(); var exData = await baseService.AsQueryable() @@ -138,8 +145,8 @@ namespace Learn.Archives.API.Controllers res.Data.Add(item); if (!exDataDic.ContainsKey(item.Id)) continue; var ex = exDataDic[item.Id].First(); - item.Status = ex.Status??UserStatusEnum.未录入; - item.ExitTime = ex.ExitTime==null? string.Empty : ex.ExitTime?.ToString("yyyy-MM-dd"); + item.Status = ex.Status ?? UserStatusEnum.未录入; + item.ExitTime = ex.ExitTime == null ? string.Empty : ex.ExitTime?.ToString("yyyy-MM-dd"); item.JoinTime = ex.JoinTime == null ? string.Empty : ex.JoinTime?.ToString("yyyy-MM-dd"); item.AmountRelief = ex.AmountRelief; @@ -190,15 +197,15 @@ namespace Learn.Archives.API.Controllers var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); if (gStr.ToEnum() == null) //无效的传入年级 { - s.ErrorMsg = "无效的年级 例如[初一/初2028] [年级范围应当是当前有效的就读年级]"; + s.ErrorMsg = "无效的年级 例如[初一/初2028] [年 级范围应当是当前有效的就读年级]"; impError.Add(s); continue; } //基础信息校验 if (string.IsNullOrEmpty(s.UserType) || - string.IsNullOrEmpty(s.School)|| + string.IsNullOrEmpty(s.School) || (s.UserType != "年级主任" && string.IsNullOrEmpty(s.Class)) || - string.IsNullOrEmpty(s.Phone)|| + string.IsNullOrEmpty(s.Phone) || string.IsNullOrEmpty(s.RealName) ) { @@ -207,7 +214,7 @@ namespace Learn.Archives.API.Controllers continue; } //无效的任教学科 - if (!string.IsNullOrEmpty(s.Subject) && s.Subject.ToEnum()==null) + if (!string.IsNullOrEmpty(s.Subject) && s.Subject.ToEnum() == null) { s.ErrorMsg = "无效的任教学科"; impError.Add(s); @@ -275,13 +282,13 @@ namespace Learn.Archives.API.Controllers foreach (var s in dataList) { var ginfo = GradeHelper.GetStudentGradeBaseByGrade(s.Grade); - if(ginfo is null) + if (ginfo is null) { s.ErrorMsg = "未能识别的[初一/初2028] [年级范围应当是当前有效的就读年级]"; impError.Add(s); continue; } - var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); + var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); if (gStr.ToEnum() == null) //无效的传入年级 { s.ErrorMsg = "无效的年级 例如[初一/初2028] [年级范围应当是当前有效的就读年级]"; @@ -290,18 +297,18 @@ namespace Learn.Archives.API.Controllers } insertInfo.Add(new Student() { - AmountRelief = decimal.TryParse(s.AmountRelief,out decimal v)?v:0, + AmountRelief = decimal.TryParse(s.AmountRelief, out decimal v) ? v : 0, ExitTime = s.ExitTime, Remark = s.Remark, JoinTime = s.JoinTime, - Status = s.Status.ToEnum()??default, + Status = s.Status.ToEnum() ?? default, UserCenterId = s.Id, - ReliefApplication=s.ReliefApplication?.Contains("已申请")??false, + ReliefApplication = s.ReliefApplication?.Contains("已申请") ?? false, ReliefSubTime = s.ReliefSubTime, - ReliefType =s.ReliefType, - StudentType =s.StudentType.ToEnum(), + ReliefType = s.ReliefType, + StudentType = s.StudentType.ToEnum(), }); - userCenterImp.Add(new UserExcelExportData() + var addExcelUser = new UserExcelExportData() { UserType = "学生", Account = s.Id.ToString(), @@ -311,20 +318,23 @@ namespace Learn.Archives.API.Controllers ExamNo = s.Id.ToString(), Phone = s.Phone, RealName = s.RealName, - Stages = s.Grade.Contains("初")? StudentStagesEnum.初中.ToString() : StudentStagesEnum.高中.ToString() - }); + Stages = s.Grade.Contains("初") ? StudentStagesEnum.初中.ToString() : StudentStagesEnum.高中.ToString() + }; + userCenterImp.Add(addExcelUser); + if (!string.IsNullOrWhiteSpace(s.高考选科)) + addExcelUser.GKSubject = s.高考选科; } //如果有错误数据则返回excel if (impError.Count() != 0) return File(impError.ExportExcel(), "application/ms-excel", $"导入错误学生{DateTime.Now.ToString("MMddHHmm")}.xlsx"); //调用 用户中心 导入接口 - var importRes = await _userCenterService.CallAPI_ImportJsonData(_httpContextAccessor.HttpContext, userCenterImp); + var importRes = await _userCenterService.CallAPI_ImportJsonData(_httpContextAccessor.HttpContext, userCenterImp); - var hUAccount = importRes.InsertUsers.ToDictionary(s=>s.Account,s=>s.Id); + var hUAccount = importRes.InsertUsers.ToDictionary(s => s.Account, s => s.Id); //基于结果判断添加成功` insertInfo = insertInfo - .Where(s => hUAccount.ContainsKey(s.UserCenterId.ToString())).Select(s => + .Where(s => hUAccount.ContainsKey(s.UserCenterId.ToString())).Select(s => { s.UserCenterId = hUAccount[s.UserCenterId.ToString()]; return s; @@ -341,6 +351,152 @@ namespace Learn.Archives.API.Controllers } + /// + /// 导入excel 更新学生信息 + /// + /// + [HttpPost, ResultIgnore] + [HttpLogEnable] + public async Task ImportUpdateStudent(IFormFile? file) + { + var dataList = await _httpContextAccessor.ParsingExcelAsync(); + if (dataList == null || dataList.Length == 0) + Oh.ModelError("导入失败:无有效数据"); + + var updateArr = new List(); + var impError = new List(); + var userCenterUpdate = new List(); + + var db = schoolService.Context; + var schoolDic = await schoolService.AsQueryable().ToDictionaryAsync(s => s.Name, s => s.Id); + + foreach (var schoolGroup in dataList.GroupBy(s => s.School)) + { + if (!schoolDic.ContainsKey(schoolGroup.Key)) + Oh.ModelError($"未能识别的学校[{schoolGroup.Key}]"); + var schoolId = (long)schoolDic[schoolGroup.Key]; + var names = schoolGroup.Select(r => r.RealName) + .Where(n => !string.IsNullOrWhiteSpace(n)).Distinct().ToList(); + var userDic = new Dictionary(); + for (int i = 0; i < names.Count; i += 500) + { + var batch = names.Skip(i).Take(500).ToList(); + var part = await db.Queryable() + .LeftJoin((s, c) => c.SchoolId == s.Id) + .LeftJoin((s, c, p) => p.ClassId == c.Id) + .LeftJoin((s, c, p, pr) => pr.PositionId == p.Id) + .LeftJoin((s, c, p, pr, u) => u.Id == pr.UserId) + .Where((s, c, p, pr, u) => + s.Id == schoolId && + u.UserType == UserTypeEnum.学生.GetHashCode() && + batch.Contains(u.RealName) && + s.Enable && p.Enable && pr.Enable && + !p.DeleteState && !c.DeleteState && !u.DeleteState && !s.DeleteState) + .Select((s, c, p, pr, u) => new + { + Name = c.GradeLevel.ToString() + c.GraduationYear.ToString() + c.Name + u.RealName, + u.Id, + }).ToListAsync(); + foreach (var kv in part) + userDic[kv.Name] = (long)kv.Id; + } + + foreach (var row in schoolGroup) + { + var ginfo = GradeHelper.GetStudentGradeBaseByGrade(row.Grade); + var key = (ginfo?.GradeLevel.ToString() ?? string.Empty) + (ginfo?.GradeYear.ToString() ?? string.Empty) + row.Class + row.RealName; + if (!userDic.ContainsKey(key)) + { + row.ErrorMsg = "未能匹配到学校下对应的班级学生"; + impError.Add(row); + continue; + } + + var uid = (long)userDic[key]; + var u = new Student() + { + UserCenterId = uid, + AmountRelief = decimal.TryParse(row.AmountRelief, out var v) ? v : null, + ReliefApplication = row.ReliefApplication?.Contains("已申请") ?? false, + ReliefType = row.ReliefType, + ReliefSubTime = row.ReliefSubTime, + StudentType = row.StudentType.ToEnum(), + Status = row.Status.ToEnum(), + JoinTime = row.JoinTime, + ExitTime = row.ExitTime, + Remark = row.Remark, + }; + updateArr.Add(u); + + if (!string.IsNullOrWhiteSpace(row.高考选科)) + { + var gradeStd = ginfo is null ? row.Grade : GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); + var subjectArr = row.高考选科.Split(",") + .Select(s => s.ToEnum()) + .ToArray(); + userCenterUpdate.Add(new User + { + Id = uid, + Phone = row.Phone, + GLSubject = (int?)subjectArr[0], + GSubject1 = (int?)subjectArr[1], + GSubject2 = (int?)subjectArr[2], + }); + } + } + + } + + + if (impError.Count != 0) + return File(impError.ExportExcel(), "application/ms-excel", $"导入错误学生{DateTime.Now.ToString("MMddHHmm")}.xlsx"); + + if (updateArr.Count > 0) + { + updateArr = updateArr.Where(s => + new object[] { s.AmountRelief, + s.ReliefType, + s.ReliefSubTime, + s.StudentType, + s.Status, + s.JoinTime, + s.ExitTime, + s.Remark}.Any(x => !(x == default || x == null))).ToList(); + // 测试IgnoreColumns是否真实有效 + await baseService.AsUpdateable(updateArr) + .UpdateColumns(s => new + { + s.AmountRelief, + s.ReliefApplication, + s.ReliefType, + s.ReliefSubTime, + s.StudentType, + s.Status, + s.JoinTime, + s.ExitTime, + s.Remark + }) + //更新忽略null并且忽略默认值 (比如int默认值是0就不更新) + .IgnoreColumns(ignoreAllNullColumns: true, ignoreAllDefaultValue: true) + .WhereColumns(it => new { it.UserCenterId }) + .ExecuteCommandAsync(); + } + if (userCenterUpdate.Count > 0) + await userService.AsUpdateable(userCenterUpdate) + .UpdateColumns(s => new + { + s.Phone, + s.GLSubject, + s.GSubject1, + s.GSubject2, + }) + //更新忽略null并且忽略默认值 (比如int默认值是0就不更新) + .IgnoreColumns(ignoreAllNullColumns: true, ignoreAllDefaultValue: true) + .WhereColumns(it => new { it.Id }) + .ExecuteCommandAsync(); + Oh.Error("需要测试"); + return Ok(); + } @@ -365,8 +521,9 @@ namespace Learn.Archives.API.Controllers StudentType = "选填: 可选值:\r\n复读生\r\n艺术生\r\n春招生\r\n领导承诺批准全免\r\n资源班\r\n国际班\r\n合同制收费学校\r\n渠道商家属\r\n新开班但领导承诺第一学期不收费", Phone="选填", Remark="选填", + 高考选科 = "填写学科\r\n 例如 物理 \r\n多学科情况下 物理,化学,生物" } }; - return File(resultList.ExportExcel(), "application/ms-excel", + return File(resultList.ExportExcel(), "application/ms-excel", $"导入学生模板{DateTime.Now.ToString("MMddHHmm")}.xlsx"); } @@ -380,7 +537,7 @@ namespace Learn.Archives.API.Controllers [HttpGet, ResultIgnore, AllowAnonymous] public IActionResult DwImportTeacherTemplate() { - var resultList = new List() { + var resultList = new List() { new TeacherInfoImport() { Phone="必填", diff --git a/Learn.Archives.Core/Common/AppCommon.cs b/Learn.Archives.Core/Common/AppCommon.cs index 20e821a..5650b26 100644 --- a/Learn.Archives.Core/Common/AppCommon.cs +++ b/Learn.Archives.Core/Common/AppCommon.cs @@ -19,6 +19,8 @@ using System.Threading.Tasks; using UserCenter.Model.Interface; using MiniExcelLibs; using MiniExcelLibs.OpenXml; +using Microsoft.AspNetCore.Http; +using Learn.Archives.Core.Model.Dto; namespace Learn.Archives.Core.Common { @@ -146,7 +148,35 @@ namespace Learn.Archives.Core.Common }; return JsonSerializer.Serialize(o, jsonOptions); } + /// + /// 解析Excel为对象数组 + /// + /// + /// + /// + public static async Task ParsingExcelAsync(this IHttpContextAccessor context) where T: class, new() + { + if (context == null) Oh.ModelError("传入无效的请求上下文数据"); + var fl =context?.HttpContext?.Request.Form.Files[0]; + if (fl == null) Oh.ModelError("传入无效的Excel数据"); + if (!Path.GetExtension(fl.FileName).Equals(".xlsx", StringComparison.OrdinalIgnoreCase)) + Oh.ModelError("请选择导入文件为.xlsx的后缀名!"); + try + { + //分析excel + IEnumerable dataList; + using var stream = new MemoryStream(); + { + await fl.CopyToAsync(stream); + return stream.Query().ToArray(); + } + } + catch (Exception ex) + { + return Oh.Error("分析失败"+ex.Message); + } + } /// /// 获取应用有效程序集 diff --git a/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs b/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs index a9c3b7a..70fa2fe 100644 --- a/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs +++ b/Learn.Archives.Core/Model/Dto/UserCenterServiceDto.cs @@ -214,6 +214,12 @@ namespace Learn.Archives.Core.Model.Dto [ExcelColumn(Name = "备注",Width =50)] public string? Remark { get; set; } + /// + /// 高考选科 + /// + /// + [ExcelColumn(Name = "高考选科", Width = 20)] + public string? 高考选科 { get; set; } /// /// id /// @@ -311,6 +317,11 @@ namespace Learn.Archives.Core.Model.Dto /// [ExcelColumn(Name="学段")] public string? Stages { get; set; } + /// + /// 选修方向 + /// + [ExcelColumnName("选修方向")] + public string? GKSubject { get; set; } }