Learn.Archives/Learn.Archives.API/Controllers/StudentController.cs

557 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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 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
{
/// <summary>
/// 学生接口
/// </summary>
public class StudentController : BackController<Student>
{
private readonly IHttpContextAccessor _httpContextAccessor;
readonly Repository<Student> baseService;
readonly Repository<Position> positionService;
readonly UserCenterService _userCenterService;
readonly LiveUserInfo userInfo;
readonly Repository<School> schoolService;
readonly Repository<Classes> classesService;
readonly Repository<User> userService;
public StudentController(Repository<Student> baseService, LiveUserInfo userInfo, IHttpContextAccessor httpContextAccessor, UserCenterService userCenterService, Repository<Position> positionService, Repository<School> schoolService, Repository<Classes> classesService, Repository<User> 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<dynamic> PageList([FromBody] QueryRequestBase model)
{
return base.PageList(model);
}
/// <summary>
/// 用户信息
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<Student> Info(long uid)
{
if (uid == 0)
Oh.ModelError("无效数据");
return await baseService.AsQueryable().FirstAsync(s => s.UserCenterId == uid);
}
/// <summary>
/// 获取职位id
/// <para> 调用流程 用户中心->档案系统</para>
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<long[]> PosititonIds(PositionIdsReq[] data)
{
if (data == null || data.Count() == 0)
Oh.ModelError("无效数据");
var query = new Expressionable<Position>();
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();
}
/// <summary>
/// 修改用户信息
/// <para> 调用流程 用户中心->档案系统</para>
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<long> 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;
}
/// <summary>
/// 获取学生列表
/// </summary>
/// <returns></returns>
[HttpPost]
public new async Task<PageResult<StudentInfoRes>> PageList(object data)
{
var apiRes = await _userCenterService.CallAPI_GetPageUserList(_httpContextAccessor.HttpContext);
var res = new PageResult<StudentInfoRes>() { Data = new List<StudentInfoRes>() };
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<StudentInfoRes>()))
{
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;
}
/// <summary>
/// 导入老师信息
/// </summary>
/// <returns></returns>
[HttpPost, ResultIgnore]
[HttpLogEnable]
public async Task<IActionResult> 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<TeacherInfoImportError> dataList;
using var stream = new MemoryStream();
{
await fl.CopyToAsync(stream);
dataList = stream.Query<TeacherInfoImportError>()
.Where(s => !string.IsNullOrEmpty(s.School));
}
if (dataList == null || dataList.Count() == 0)
Oh.ModelError("导入失败:无有效数据");
var insertInfo = new List<Student>();
var impError = new List<TeacherInfoImportError>();
var userCenterImp = new List<UserExcelExportData>();
foreach (var s in dataList)
{
var ginfo = GradeHelper.GetStudentGradeBaseByGrade(s.Grade);
var gStr = GradeHelper.GetGrade(ginfo.GradeLevel, ginfo.GradeYear);
if (gStr.ToEnum<GradeEnum>() == 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<SubjectEnum>() == 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<TeacherInfoImportError>());
//如果有错误数据则返回excel
if (errorExcelInfo.Count() != 0)
return File(errorExcelInfo.ExportExcel(), "application/ms-excel", $"导入错误的老师{DateTime.Now.ToString("MMddHHmm")}.xlsx");
return Ok();
}
/// <summary>
/// 导入学生信息
/// </summary>
/// <returns></returns>
[HttpPost, ResultIgnore]
[HttpLogEnable]
public async Task<IActionResult> Import(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<StudentInfoImportError> dataList;
using var stream = new MemoryStream();
{
await fl.CopyToAsync(stream);
dataList = stream.Query<StudentInfoImportError>()
.Where(s => !string.IsNullOrEmpty(s.School));
}
if (dataList == null || dataList.Count() == 0)
Oh.ModelError("导入失败:无有效数据");
var insertInfo = new List<Student>();
var impError = new List<StudentInfoImportError>();
var userCenterImp = new List<UserExcelExportData>();
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<GradeEnum>() == 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<UserStatusEnum>() ?? default,
UserCenterId = s.Id,
ReliefApplication = s.ReliefApplication?.Contains("已申请") ?? false,
ReliefSubTime = s.ReliefSubTime,
ReliefType = s.ReliefType,
StudentType = s.StudentType.ToEnum<StudentTypeEnum>(),
});
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<StudentInfoImportError>());
//如果有错误数据则返回excel
if (errorExcelInfo.Count() != 0)
return File(errorExcelInfo.ExportExcel(), "application/ms-excel", $"导入错误学生{DateTime.Now.ToString("MMddHHmm")}.xlsx");
return Ok();
}
/// <summary>
/// 导入excel 更新学生信息
/// </summary>
/// <returns></returns>
[HttpPost, ResultIgnore]
[HttpLogEnable]
public async Task<IActionResult> ImportUpdateStudent(IFormFile? file)
{
var dataList = await _httpContextAccessor.ParsingExcelAsync<StudentInfoImportError>();
dataList = dataList.Where(s => !string.IsNullOrEmpty(s.School)).ToArray();
if (dataList == null || dataList.Length == 0)
Oh.ModelError("导入失败:无有效数据");
var updateArr = new List<Student>();
var impError = new List<StudentInfoImportError>();
var userCenterUpdate = new List<User>();
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<string, long>();
for (int i = 0; i < names.Count; i += 500)
{
var batch = names.Skip(i).Take(500).ToList();
var part = await db.Queryable<School>()
.LeftJoin<Classes>((s, c) => c.SchoolId == s.Id)
.LeftJoin<Position>((s, c, p) => p.ClassId == c.Id)
.LeftJoin<PositionRelation>((s, c, p, pr) => pr.PositionId == p.Id)
.LeftJoin<User>((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<StudentTypeEnum>(),
Status = row.Status.ToEnum<UserStatusEnum>(),
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<SubjectEnum>())
.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();
return Ok();
}
/// <summary>
/// 下载导入模板
/// </summary>
/// <returns></returns>
[HttpGet, ResultIgnore, AllowAnonymous]
public IActionResult DwImportTemplate()
{
var resultList = new List<StudentInfoImport>() { 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");
}
/// <summary>
/// 下载导入老师模板
/// </summary>
/// <returns></returns>
[HttpGet, ResultIgnore, AllowAnonymous]
public IActionResult DwImportTeacherTemplate()
{
var resultList = new List<TeacherInfoImport>() {
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");
}
}
}