using Aliyun.OSS; using Dm; 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 SqlSugar; using System.Diagnostics; using System.Linq.Expressions; 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 StudentController : BackController { private readonly IHttpContextAccessor _httpContextAccessor; readonly Repository baseService; readonly Repository positionService; readonly UserCenterService _userCenterService; readonly LiveUserInfo userInfo; 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] public override Task PageList([FromBody] QueryRequestBase model) { return base.PageList(model); } /// /// 用户信息 /// /// [HttpGet] public async Task Info(long uid) { if (uid == 0) 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(); } /// /// 修改用户信息 /// 调用流程 用户中心->档案系统 /// /// [HttpPost] public async Task EditInfo(Student e) { if (e == null || e.UserCenterId == 0) Oh.ModelError("无效数据"); var has = await baseService.AsQueryable().FirstAsync(s => s.UserCenterId == e.UserCenterId); e.Status = e.Status ?? UserStatusEnum.未录入; if (has == null) await baseService.InsertAsync(e); else { e.Id = has.Id; e.UserCenterId = has.UserCenterId; await baseService.UpdateAsync(e); } return e.Id; } /// /// 获取学生列表 /// /// [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; res.Total = apiRes.Total; var uids = apiRes.Data.Select(s => s.Id).ToArray(); var exData = await baseService.AsQueryable() .Where(s => uids.Contains(s.UserCenterId)) .ToArrayAsync(); var exDataDic = exData.GroupBy(s => s.UserCenterId).ToDictionary(s => s.Key); foreach (var item in apiRes.Data.Select(s => s.Adapt())) { 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.JoinTime = ex.JoinTime == null ? string.Empty : ex.JoinTime?.ToString("yyyy-MM-dd"); item.AmountRelief = ex.AmountRelief; item.ReliefApplication = ex.ReliefApplication; item.StudentType = ex.StudentType.ToString(); item.Remark = ex.Remark; item.ReliefType = ex?.ReliefType?.ToString(); item.ReliefSubTime = ex.ReliefSubTime?.ToString("yyyy-MM-dd") ?? string.Empty; } return res; } /// /// 导入老师信息 /// /// [HttpPost, ResultIgnore] [HttpLogEnable] public async Task ImportTeacher(IFormFile? file) { var fl = file != null ? file : _httpContextAccessor.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("导入失败:无有效数据"); var insertInfo = new List(); var impError = new List(); var userCenterImp = new List(); foreach (var s in dataList) { var ginfo = GradeHelper.GetStudentGradeBaseByGrade(s.Grade); var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); if (gStr.ToEnum() == null) //无效的传入年级 { s.ErrorMsg = "无效的年级 例如[初一/初2028] [年 级范围应当是当前有效的就读年级]"; impError.Add(s); continue; } //基础信息校验 if (string.IsNullOrEmpty(s.UserType) || string.IsNullOrEmpty(s.School) || (s.UserType != "年级主任" && string.IsNullOrEmpty(s.Class)) || string.IsNullOrEmpty(s.Phone) || string.IsNullOrEmpty(s.RealName) ) { s.ErrorMsg = "基础信息未能检查通过/请检查 导入行中 是否缺少了[任职信息类型/学校/班级/年级/名称//手机号]"; impError.Add(s); continue; } //无效的任教学科 if (!string.IsNullOrEmpty(s.Subject) && s.Subject.ToEnum() == null) { s.ErrorMsg = "无效的任教学科"; impError.Add(s); continue; } userCenterImp.Add(new UserExcelExportData() { UserType = s.UserType, Account = s.Phone.ToString(), Subject = s.Subject, ExamNo = s.ExamNo, School = s.School, Grade = gStr, Class = s.Class, Phone = s.Phone, RealName = s.RealName, Stages = s.Grade.Contains("初") ? StudentStagesEnum.初中.ToString() : StudentStagesEnum.高中.ToString() }); } //如果有错误数据则返回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 errorExcelInfo = importRes.ErrorExcelExport.Select(s => s.Adapt()); //如果有错误数据则返回excel if (errorExcelInfo.Count() != 0) return File(errorExcelInfo.ExportExcel(), "application/ms-excel", $"导入错误的老师{DateTime.Now.ToString("MMddHHmm")}.xlsx"); return Ok(); } /// /// 导入学生信息 /// /// [HttpPost, ResultIgnore] [HttpLogEnable] public async Task Import(IFormFile? file) { var dataList = await _httpContextAccessor.ParsingExcelAsync(); dataList = dataList.Where(s => !string.IsNullOrEmpty(s.School)).ToArray(); if (dataList == null || dataList.Count() == 0) Oh.ModelError("导入失败:无有效数据"); var insertInfo = new List(); var impError = new List(); var userCenterImp = new List(); foreach (var s in dataList) { var ginfo = GradeHelper.GetStudentGradeBaseByGrade(s.Grade); if (ginfo is null) { s.ErrorMsg = "未能识别的[初一/初2028] [年级范围应当是当前有效的就读年级]"; impError.Add(s); continue; } var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear); if (gStr.ToEnum() == null) //无效的传入年级 { s.ErrorMsg = "无效的年级 例如[初一/初2028] [年级范围应当是当前有效的就读年级]"; impError.Add(s); continue; } insertInfo.Add(new Student() { AmountRelief = decimal.TryParse(s.AmountRelief, out decimal v) ? v : 0, ExitTime = s.ExitTime, Remark = s.Remark, JoinTime = s.JoinTime, Status = s.Status.ToEnum() ?? default, UserCenterId = s.Id, ReliefApplication = s.ReliefApplication?.Contains("已申请") ?? false, ReliefSubTime = s.ReliefSubTime, ReliefType = s.ReliefType, StudentType = s.StudentType.ToEnum(), }); var addExcelUser = new UserExcelExportData() { UserType = "学生", Account = s.Id.ToString(), School = s.School, Grade = gStr, Class = s.Class, ExamNo = s.Id.ToString(), Phone = s.Phone, RealName = s.RealName, 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 hUAccount = importRes.InsertUsers.ToDictionary(s => s.Account, s => s.Id); //基于结果判断添加成功` insertInfo = insertInfo .Where(s => hUAccount.ContainsKey(s.UserCenterId.ToString())).Select(s => { s.UserCenterId = hUAccount[s.UserCenterId.ToString()]; return s; }).ToList(); //部分成功的数据写入数据库 await baseService.InsertRangeAsync(insertInfo); //处理数据 var errorExcelInfo = importRes.ErrorExcelExport.Select(s => s.Adapt()); //如果有错误数据则返回excel if (errorExcelInfo.Count() != 0) return File(errorExcelInfo.ExportExcel(), "application/ms-excel", $"导入错误学生{DateTime.Now.ToString("MMddHHmm")}.xlsx"); return Ok(); } /// /// 导入excel 更新学生信息 /// /// [HttpPost, ResultIgnore] [HttpLogEnable] public async Task ImportUpdateStudent(IFormFile? file) { var dataList = await _httpContextAccessor.ParsingExcelAsync(); dataList = dataList.Where(s => !string.IsNullOrEmpty(s.School)).ToArray(); 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(); var upUser = new User { Id = uid, Phone = row.Phone, }; if (subjectArr.Length > 0) upUser.GLSubject = (int?)subjectArr[0]; if (subjectArr.Length > 1) upUser.GSubject1 = (int?)subjectArr[1]; if (subjectArr.Length > 2) upUser.GSubject2 = (int?)subjectArr[2]; userCenterUpdate.Add(upUser); } } } 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(); //查询差异 var addInfoUserArr = updateArr.Select(s => s.UserCenterId).ToArray(); var hasUIdArr = await baseService.AsQueryable() .Where(s => addInfoUserArr.Contains(s.UserCenterId)) .Select(s => s.UserCenterId).ToArrayAsync(); var hasUIdHash = hasUIdArr.ToHashSet(); var updateData = new List(10); var InsertData = new List(10); foreach (var item in updateArr) { if (hasUIdHash.Contains(item.UserCenterId)) updateData.Add(item); else InsertData.Add(item); } //开启事务 baseService.Context.Ado .UseTran(() => { baseService.InsertRange(InsertData); baseService.AsUpdateable(updateData) .UpdateColumns(s => new { s.AmountRelief, s.ReliefApplication, s.ReliefType, s.ReliefSubTime, s.StudentType, s.Status, s.JoinTime, s.ExitTime, s.Remark }) //更新忽略null并且忽略默认值 (比如int默认值是0就不更新) //如果修改为0会出现修改不了的问题 .IgnoreColumns(ignoreAllNullColumns: true, ignoreAllDefaultValue: true) .WhereColumns(it => new { it.UserCenterId }) .ExecuteCommand(); return true; }, ex => { throw ex; }); } //写入选科方向 userCenterUpdate = userCenterUpdate.Where(s => new object[] { s.Phone, s.GLSubject, s.GSubject1, s.GSubject2,} .Any(x => !(x == default || x == null))).ToList(); 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(); } return Ok(); } /// /// 下载导入模板 /// /// [HttpGet, ResultIgnore, AllowAnonymous] public IActionResult DwImportTemplate() { var resultList = new List() { new StudentInfoImport() { RealName = "导入规范[导入时请删除本行]", School = "必填:与系统匹配", Grade = "必填:\r\n[高2027/高一/初一]", Class = "必填:与系统匹配\r\n格式:10班[数字+班]", Status = "选填 可选值\r\n[就读,退出]", AmountRelief ="选填: 为0则视为 '未申请减免'", ReliefType ="选填: 建卡贫困户\r\n低保户\r\n教师子女 \r\n孤儿\r\n艺体生\r\n残疾学生\r\n领导特殊承诺减免\r\n领导同意的特殊贫困减免", ReliefApplication ="选填: [已申请, 未申请]", 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", $"导入学生模板{DateTime.Now.ToString("MMddHHmm")}.xlsx"); } /// /// 下载导入老师模板 /// /// [HttpGet, ResultIgnore, AllowAnonymous] public IActionResult DwImportTeacherTemplate() { var resultList = new List() { new TeacherInfoImport() { Phone="必填", RealName = "导入规范[导入时请删除本行]", UserType = "必填 可选值\r\n[年级主任,班主任,教师]", School = "必填:与系统匹配", Grade = "必填:可选值\r\n[高2027/高一/初一]", Class = "必填:与系统匹配\r\n格式:10班[数字+班]\r\n任教类型是年级主任时不填", Subject = "选填学科", ExamNo ="选填: 填写老师任职信息[不在授课架构中的职务]", }}; return File(resultList.ExportExcel(), "application/ms-excel", $"导入老师模板_{DateTime.Now.ToString("MMddHHmm")}.xlsx"); } } }