优化
This commit is contained in:
1 parent
e5afe60a11
commit
7994ed1ed3
8 files changed
+408
-13
No files matched your search
@@ -4,6 +4,7 @@ using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Infrastructure.Auth;
|
||||
using Jiaowu.Api.Infrastructure.Caching;
|
||||
using Jiaowu.Api.Infrastructure.Academic;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@@ -302,6 +303,30 @@ public sealed class PersonnelController(
|
||||
return Ok(new PagedResult<object>(items, total, page, pageSize));
|
||||
}
|
||||
|
||||
[HttpGet("students/current-courses")]
|
||||
public async Task<ActionResult> GetStudentCurrentCourses(
|
||||
[FromQuery] Guid[] studentIds,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var requestedIds = studentIds.Distinct().Take(100).ToArray();
|
||||
if (requestedIds.Length == 0) return Ok(Array.Empty<object>());
|
||||
|
||||
var students = await ApplyStudentScope(db.Students.AsNoTracking())
|
||||
.WhereIn(requestedIds, x => x.Id)
|
||||
.Select(x => new CurrentCourseStudent(x.Id, x.AdministrativeClassId))
|
||||
.ToListAsync(cancellationToken);
|
||||
var summaries = await StudentCurrentCourseQuery.GetAsync(
|
||||
db,
|
||||
students,
|
||||
cancellationToken);
|
||||
return Ok(students.Select(student => new
|
||||
{
|
||||
student.Id,
|
||||
CurrentCourses = summaries[student.Id].Courses,
|
||||
summaries[student.Id].TotalCredits
|
||||
}));
|
||||
}
|
||||
|
||||
[HttpPost("students")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> CreateStudent(
|
||||
|
||||
@@ -2,6 +2,7 @@ using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Infrastructure.Auth;
|
||||
using Jiaowu.Api.Infrastructure.Caching;
|
||||
using Jiaowu.Api.Infrastructure.Academic;
|
||||
using Jiaowu.Api.Infrastructure.Excel;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -49,6 +50,11 @@ public sealed class PersonnelExcelController(
|
||||
"紧急联系电话", "特殊标记", "特殊情况说明", "个人简介", "备注"
|
||||
];
|
||||
|
||||
private static readonly string[] StudentCurrentCourseHeaders =
|
||||
[
|
||||
"学号", "姓名", "学院", "专业", "行政班", "正在修读课程", "学分", "总学分"
|
||||
];
|
||||
|
||||
[HttpGet("{kind}/template")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public IActionResult DownloadTemplate(string kind)
|
||||
@@ -117,6 +123,60 @@ public sealed class PersonnelExcelController(
|
||||
$"{RegistryName(kind)}-{DateTime.Now:yyyyMMdd-HHmm}.xlsx");
|
||||
}
|
||||
|
||||
[HttpGet("students/current-courses/export")]
|
||||
[Authorize(Roles = ExportRoles)]
|
||||
public async Task<IActionResult> ExportStudentCurrentCourses(
|
||||
[FromQuery] PersonnelQuery query,
|
||||
[FromQuery] Guid[]? studentIds,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var source = ApplyStudentFilters(
|
||||
ApplyStudentScope(db.Students.AsNoTracking()),
|
||||
query);
|
||||
var selectedIds = studentIds?.Distinct().ToArray() ?? [];
|
||||
if (selectedIds.Length > 0)
|
||||
source = source.WhereIn(selectedIds, x => x.Id);
|
||||
|
||||
var students = await source.OrderBy(x => x.StudentNumber)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.StudentNumber,
|
||||
x.Name,
|
||||
x.AdministrativeClassId,
|
||||
ClassName = x.AdministrativeClass!.Name,
|
||||
MajorName = x.AdministrativeClass.Major!.Name,
|
||||
CollegeName = x.AdministrativeClass.Major.College!.Name
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
var summaries = await StudentCurrentCourseQuery.GetAsync(
|
||||
db,
|
||||
students.Select(x => new CurrentCourseStudent(x.Id, x.AdministrativeClassId))
|
||||
.ToList(),
|
||||
cancellationToken);
|
||||
var rows = students.Select(student =>
|
||||
{
|
||||
var summary = summaries[student.Id];
|
||||
return Row(
|
||||
student.StudentNumber,
|
||||
student.Name,
|
||||
student.CollegeName,
|
||||
student.MajorName,
|
||||
student.ClassName,
|
||||
string.Join(";", summary.Courses.Select(course =>
|
||||
$"{course.CourseCode} {course.CourseName}")),
|
||||
string.Join(";", summary.Courses.Select(course =>
|
||||
$"{course.Credits:0.##}")),
|
||||
summary.TotalCredits);
|
||||
}).ToList();
|
||||
var bytes = ExcelWorkbookHelper.Create(
|
||||
"学生正在修读课程",
|
||||
StudentCurrentCourseHeaders,
|
||||
rows);
|
||||
return File(bytes, ExcelWorkbookHelper.ContentType,
|
||||
$"学生正在修读课程-{DateTime.Now:yyyyMMdd-HHmm}.xlsx");
|
||||
}
|
||||
|
||||
[HttpPost("{kind}/import")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
[RequestSizeLimit(10 * 1024 * 1024)]
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Academic;
|
||||
|
||||
public sealed record CurrentCourseStudent(Guid Id, Guid AdministrativeClassId);
|
||||
|
||||
public sealed record CurrentCourseItem(
|
||||
Guid CourseId,
|
||||
string CourseCode,
|
||||
string CourseName,
|
||||
decimal Credits);
|
||||
|
||||
public sealed record StudentCurrentCourseSummary(
|
||||
IReadOnlyList<CurrentCourseItem> Courses,
|
||||
decimal TotalCredits);
|
||||
|
||||
public static class StudentCurrentCourseQuery
|
||||
{
|
||||
public static async Task<IReadOnlyDictionary<Guid, StudentCurrentCourseSummary>>
|
||||
GetAsync(
|
||||
AppDbContext db,
|
||||
IReadOnlyList<CurrentCourseStudent> students,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (students.Count == 0)
|
||||
return new Dictionary<Guid, StudentCurrentCourseSummary>();
|
||||
|
||||
var studentIds = students.Select(x => x.Id).Distinct().ToArray();
|
||||
var classIds = students.Select(x => x.AdministrativeClassId).Distinct().ToArray();
|
||||
var currentTasks = db.TeachingTasks.AsNoTracking()
|
||||
.Where(x => x.Status == TeachingTaskStatus.Published && x.AcademicTerm!.IsCurrent);
|
||||
var taskClasses = db.TeachingTaskClasses.AsNoTracking()
|
||||
.WhereIn(classIds, x => x.AdministrativeClassId);
|
||||
var enrollments = db.CourseEnrollments.AsNoTracking()
|
||||
.Where(x => x.Status == CourseEnrollmentStatus.Enrolled)
|
||||
.WhereIn(studentIds, x => x.StudentId);
|
||||
|
||||
var assigned = await (
|
||||
from link in taskClasses
|
||||
join task in currentTasks on link.TeachingTaskId equals task.Id
|
||||
join course in db.Courses.AsNoTracking() on task.CourseId equals course.Id
|
||||
select new CurrentCourseAssignment(
|
||||
null,
|
||||
link.AdministrativeClassId,
|
||||
course.Id,
|
||||
course.Code,
|
||||
course.Name,
|
||||
course.Credits))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var enrolled = await (
|
||||
from enrollment in enrollments
|
||||
join offering in db.CourseSelectionOfferings.AsNoTracking()
|
||||
on enrollment.CourseSelectionOfferingId equals offering.Id
|
||||
join task in currentTasks on offering.TeachingTaskId equals task.Id
|
||||
join course in db.Courses.AsNoTracking() on task.CourseId equals course.Id
|
||||
select new CurrentCourseAssignment(
|
||||
enrollment.StudentId,
|
||||
null,
|
||||
course.Id,
|
||||
course.Code,
|
||||
course.Name,
|
||||
course.Credits))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var assignedByClass = assigned.GroupBy(x => x.AdministrativeClassId!.Value)
|
||||
.ToDictionary(x => x.Key, x => x.ToList());
|
||||
var enrolledByStudent = enrolled.GroupBy(x => x.StudentId!.Value)
|
||||
.ToDictionary(x => x.Key, x => x.ToList());
|
||||
|
||||
return students.ToDictionary(
|
||||
student => student.Id,
|
||||
student =>
|
||||
{
|
||||
var courses = (assignedByClass.GetValueOrDefault(student.AdministrativeClassId) ?? [])
|
||||
.Concat(enrolledByStudent.GetValueOrDefault(student.Id) ?? [])
|
||||
.GroupBy(x => x.CourseId)
|
||||
.Select(x => x.First())
|
||||
.OrderBy(x => x.CourseCode)
|
||||
.Select(x => new CurrentCourseItem(
|
||||
x.CourseId,
|
||||
x.CourseCode,
|
||||
x.CourseName,
|
||||
x.Credits))
|
||||
.ToList();
|
||||
return new StudentCurrentCourseSummary(courses, courses.Sum(x => x.Credits));
|
||||
});
|
||||
}
|
||||
|
||||
private sealed record CurrentCourseAssignment(
|
||||
Guid? StudentId,
|
||||
Guid? AdministrativeClassId,
|
||||
Guid CourseId,
|
||||
string CourseCode,
|
||||
string CourseName,
|
||||
decimal Credits);
|
||||
}
|
||||
Loaded 3 of 8 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user