新增 赴校信息导入流程

This commit is contained in:
小肥羊 2025-08-20 18:42:51 +08:00
parent 74fe063253
commit 1278fed18d
9 changed files with 331 additions and 17 deletions

View File

@ -2,7 +2,7 @@
namespace Learn.Archives.API.Controllers.Dto namespace Learn.Archives.API.Controllers.Dto
{ {
public class ImportDto public class ImportExamDto
{ {
/// <summary> /// <summary>
/// 导入文件 /// 导入文件

View File

@ -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; }
/// <summary>
/// 赴校人员
/// <para>多个,分割</para>
/// </summary>
[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; }
}
}

View File

@ -2,6 +2,7 @@
using Learn.Archives.API.Expand; using Learn.Archives.API.Expand;
using Learn.Archives.Core.Common; using Learn.Archives.Core.Common;
using Learn.Archives.Core.Model; using Learn.Archives.Core.Model;
using Learn.Archives.Core.Model.Dto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs; using MiniExcelLibs;
@ -33,6 +34,7 @@ namespace Learn.Archives.API.Controllers
this.examService = examService; this.examService = examService;
this.schoolService = schoolService; this.schoolService = schoolService;
} }
[NonAction] [NonAction]
private Dictionary<SubjectEnum, decimal>? ImportExamInfoSubjectDic(ImportExamInfo info) private Dictionary<SubjectEnum, decimal>? ImportExamInfoSubjectDic(ImportExamInfo info)
{ {
@ -49,6 +51,7 @@ namespace Learn.Archives.API.Controllers
{ SubjectEnum., info.}, { SubjectEnum., info.},
}; };
} }
/// <summary> /// <summary>
/// 导入考试信息 /// 导入考试信息
/// </summary> /// </summary>
@ -56,7 +59,7 @@ namespace Learn.Archives.API.Controllers
/// <returns></returns> /// <returns></returns>
[HttpPost, ResultIgnore] [HttpPost, ResultIgnore]
[HttpLogEnable] [HttpLogEnable]
public async Task<IActionResult> Import(ImportDto dto) public async Task<IActionResult> Import(ImportExamDto dto)
{ {
var exam = await examService.GetByIdAsync(dto.eId); var exam = await examService.GetByIdAsync(dto.eId);
@ -164,6 +167,7 @@ namespace Learn.Archives.API.Controllers
PeopleCount = classUserArr.Count(), PeopleCount = classUserArr.Count(),
MinScore = int.MaxValue, MinScore = int.MaxValue,
EntryPersonId = userInfo.Id, EntryPersonId = userInfo.Id,
BaseSchoolScore = exam.BaseSchoolScore,
EntryPerson = userInfo.Name, EntryPerson = userInfo.Name,
}; };
insertClassInfo.Add(eCInfo); insertClassInfo.Add(eCInfo);
@ -287,5 +291,29 @@ namespace Learn.Archives.API.Controllers
} }
public override Task<dynamic> 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);
}
} }
} }

View File

@ -3,22 +3,33 @@ using Learn.Archives.API.Expand;
using Learn.Archives.Core.Common; using Learn.Archives.Core.Common;
using Learn.Archives.Core.Model; using Learn.Archives.Core.Model;
using Learn.Archives.Core.Model.Dto; using Learn.Archives.Core.Model.Dto;
using Learn.Archives.Core.Model.Enum;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using SqlSugar; using SqlSugar;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Diagnostics; using System.Diagnostics;
using System.Security.Claims; using System.Security.Claims;
using System.Text.RegularExpressions;
using UserCenter.Model; using UserCenter.Model;
using UserCenter.Model.Common; using UserCenter.Model.Common;
using UserCenter.Model.Enum;
namespace Learn.Archives.API.Controllers namespace Learn.Archives.API.Controllers
{ {
public class SchoolBusinessController : BackController<SchoolBusiness> public class SchoolBusinessController : BackController<SchoolBusiness>
{ {
readonly Repository<SchoolBusiness> baseService; readonly Repository<SchoolBusiness> baseService;
public SchoolBusinessController(Repository<SchoolBusiness> baseService) : base(baseService) readonly Repository<School> schoolService;
readonly Repository<Core.Model.Admin> adminService;
readonly IHttpContextAccessor accessor;
public SchoolBusinessController(Repository<SchoolBusiness> baseService, Repository<School> schoolService, IHttpContextAccessor accessor, Repository<Core.Model.Admin> adminService) : base(baseService)
{ {
this.baseService = baseService; this.baseService = baseService;
this.schoolService = schoolService;
this.accessor = accessor;
this.adminService = adminService;
} }
public class QueryPageDto public class QueryPageDto
@ -32,13 +43,13 @@ namespace Learn.Archives.API.Controllers
/// </summary> /// </summary>
public string? Grade { get; set; } public string? Grade { get; set; }
/// <summary> /// <summary>
/// 赴校人员名称 /// 赴校人员ID
/// </summary> /// </summary>
public string? UserName { get; set; } public long? UserId { get; set; }
/// <summary> /// <summary>
/// 是否查询完结 /// 是否查询完结
/// </summary> /// </summary>
public bool SolutionEnd { get; set; } public bool? SolutionEnd { get; set; }
public DateTime? StartTime { get; set; } public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; } public DateTime? EndTime { get; set; }
@ -73,17 +84,181 @@ namespace Learn.Archives.API.Controllers
[HttpPost] [HttpPost]
public async Task<PageResult<SchoolBusiness>> QueryPageList(QueryPageDto dto) public async Task<PageResult<SchoolBusiness>> QueryPageList(QueryPageDto dto)
{ {
var uid = dto.UserId.ToString();
var gInfo = GradeHelper.GetStudentGradeBaseByGrade(dto.Grade);
RefAsync<int> total = 0; RefAsync<int> total = 0;
var resData = await baseService.AsQueryable() var resData = await baseService.AsQueryable()
.WhereIF(dto.SchoolId != 0, s => s.SchoolId == dto.SchoolId) .WhereIF(dto.SchoolId != 0, s => s.SchoolId == dto.SchoolId)
.WhereIF(!string.IsNullOrEmpty(dto.Grade), s => s.Grade == dto.Grade) .WhereIF(gInfo != null, s => s.GradeLevel == gInfo.GradeLevel && s.GradeYear == gInfo.GradeYear)
.WhereIF(dto.SolutionEnd, s => s.SolutionEnd == dto.SolutionEnd) .WhereIF(dto.SolutionEnd != null, s => s.SolutionEnd == dto.SolutionEnd)
.WhereIF(dto.StartTime != null, s => s.StartTime >= dto.StartTime) .WhereIF(dto.StartTime != null, s => s.StartTime >= dto.StartTime)
.WhereIF(dto.EndTime != null, s => s.StartTime <= dto.EndTime) .WhereIF(dto.EndTime != null, s => s.StartTime <= dto.EndTime)
.WhereIF(!string.IsNullOrEmpty(dto.UserName), s => s.SchoolBusinessUser != null && SqlFunc.JsonLike(s.SchoolBusinessUser, dto.Grade)) .WhereIF(uid != null, s => s.SchoolBusinessUser != null && SqlFunc.JsonLike(s.SchoolBusinessUser, uid))
.OrderByDescending(s=>s.Id) .OrderByDescending(s => s.Id)
.ToPageListAsync(dto.PageIndex, dto.PageSize, total); .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
return new PageResult<SchoolBusiness>() { Data = resData, Total = total }; return new PageResult<SchoolBusiness>() { Data = resData, Total = total };
} }
/// <summary>
/// 导入考试信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost, ResultIgnore]
[HttpLogEnable]
public async Task<IActionResult> 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<SchoolBusinessImportError> dataList;
using var stream = new MemoryStream();
{
await fl.CopyToAsync(stream);
dataList = stream.Query<SchoolBusinessImportError>()
.Where(s => !string.IsNullOrEmpty(s.School));
}
if (dataList == null || dataList.Count() == 0)
Oh.ModelError("导入失败:无有效数据");
//todo
//处理数据
var errorExcelInfo = new List<SchoolBusinessImportError>();
var insertInfo = new List<SchoolBusiness>();
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, (string Questions, string Solutions)>()
{
{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<FeedbackQuestionsDto>();
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<string, string>();
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<RecordDto>();
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();
}
/// <summary>
/// 下载导入成绩模板
/// </summary>
/// <returns></returns>
[HttpGet, ResultIgnore, AllowAnonymous]
public IActionResult DwImportTemplate()
{
var resultList = new List<SchoolBusinessImport>() { new SchoolBusinessImport() { } };
return File(resultList.ExportExcel(), "application/ms-excel");
}
} }
} }

View File

@ -9,7 +9,6 @@ using Learn.Archives.API.Expand;
using System.ComponentModel; using System.ComponentModel;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddLogging(loggingBuilder => builder.Services.AddLogging(loggingBuilder =>
{ {

View File

@ -102,7 +102,35 @@ namespace Learn.Archives.Core.Common
return Convert.ToHexString(hashBytes).ToUpper(); // 或者保留大写 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;
}
/// <summary> /// <summary>
/// 对象转化为JSON字符串 /// 对象转化为JSON字符串
/// </summary> /// </summary>

View File

@ -47,7 +47,7 @@ namespace Learn.Archives.Core.Model.Dto
/// <summary> /// <summary>
/// 执行时间 /// 执行时间
/// </summary> /// </summary>
public DateTime ExecutionTime { get; set; } public DateTime? ExecutionTime { get; set; }
} }

View File

@ -101,6 +101,11 @@ namespace Learn.Archives.Core.Model
/// </summary> /// </summary>
public decimal AverageRank1 { get; set; } public decimal AverageRank1 { get; set; }
/// <summary>
/// 资源校平均分
/// </summary>
public decimal BaseSchoolScore { get; set; }
/// <summary> /// <summary>
/// 录入人Id /// 录入人Id

View File

@ -48,7 +48,7 @@ namespace Learn.Archives.Core.Model
/// 赴校人员 逗号分隔 /// 赴校人员 逗号分隔
/// </summary> /// </summary>
[SugarColumn(IsNullable = true, IsJson = true)] [SugarColumn(IsNullable = true, IsJson = true)]
public string[]? SchoolBusinessUser { get; set; } public long[]? SchoolBusinessUser { get; set; }
/// <summary> /// <summary>
/// 赴校时间 /// 赴校时间
/// </summary> /// </summary>
@ -56,7 +56,7 @@ namespace Learn.Archives.Core.Model
/// <summary> /// <summary>
/// 赴校备注 /// 赴校备注
/// </summary> /// </summary>
[SugarColumn(IsNullable = true)] [SugarColumn(IsNullable = true, Length =1000)]
public string? Remark { get; set; } public string? Remark { get; set; }
/// <summary> /// <summary>