教学点名:教学班、考勤表均改为服务端分页;支持课程/任务关键词、学期、考勤表名称/备注、状态、签到方式筛选。

成绩管理:确认原本已是服务端分页,且已有学期、状态、课程关键词与学生关键词筛选,无需重复改造。
新增服务端分页与筛选测试。
This commit is contained in:
2026-08-11 16:20:54 +08:00 Unverified
parent f85a900d82
commit b08b4cc4df
4 changed files with 245 additions and 17 deletions
@@ -3,6 +3,7 @@ using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using ClosedXML.Excel;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
@@ -33,14 +34,32 @@ public sealed class AttendanceController(
[Authorize(Roles = AttendanceRoles)]
public async Task<ActionResult> GetMyTasks(
Guid? academicTermId,
CancellationToken cancellationToken)
string? keyword = null,
int page = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
{
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 10, 50);
var tasks = AccessibleTasks().AsNoTracking()
.Where(x => x.Status == TeachingTaskStatus.Published);
if (academicTermId.HasValue)
tasks = tasks.Where(x => x.AcademicTermId == academicTermId);
if (!string.IsNullOrWhiteSpace(keyword))
{
keyword = keyword.Trim();
tasks = tasks.Where(x =>
x.TaskNumber.Contains(keyword) ||
x.Name.Contains(keyword) ||
x.Course!.Code.Contains(keyword) ||
x.Course.Name.Contains(keyword));
}
var total = await tasks.CountAsync(cancellationToken);
var result = await tasks
.OrderBy(x => x.Course!.Code)
.ThenBy(x => x.TaskNumber)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new
{
x.Id,
@@ -63,22 +82,44 @@ public sealed class AttendanceController(
sheet.TeachingTaskId == x.Id)
})
.ToListAsync(cancellationToken);
return Ok(result);
return Ok(new PagedResult<object>(result, total, page, pageSize));
}
[HttpGet("sheets")]
[Authorize(Roles = AttendanceRoles)]
public async Task<ActionResult> GetSheets(
Guid teachingTaskId,
CancellationToken cancellationToken)
string? keyword = null,
AttendanceSheetStatus? status = null,
AttendanceCheckInMethod? checkInMethod = null,
int page = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
{
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 10, 50);
var task = await AccessibleTasks().AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == teachingTaskId, cancellationToken);
if (task is null) return NotFound();
var sheets = await db.AttendanceSheets.AsNoTracking()
.Where(x => x.TeachingTaskId == teachingTaskId)
var source = db.AttendanceSheets.AsNoTracking()
.Where(x => x.TeachingTaskId == teachingTaskId);
if (!string.IsNullOrWhiteSpace(keyword))
{
keyword = keyword.Trim();
source = source.Where(x => x.Name.Contains(keyword) || x.Notes!.Contains(keyword));
}
if (status.HasValue)
source = source.Where(x => x.Status == status.Value);
if (checkInMethod.HasValue)
source = source.Where(x => x.CheckInMethod == checkInMethod.Value);
var total = await source.CountAsync(cancellationToken);
var sheets = await source
.OrderByDescending(x => x.AttendanceDate)
.ThenByDescending(x => x.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new
{
x.Id,
@@ -99,7 +140,7 @@ public sealed class AttendanceController(
TotalCount = x.Records.Count
})
.ToListAsync(cancellationToken);
return Ok(sheets);
return Ok(new PagedResult<object>(sheets, total, page, pageSize));
}
[HttpPost("sheets")]
@@ -90,7 +90,7 @@ public sealed class ClickHouseAnalyticsProjectionWorker(
(x.AttendanceSheetId.CompareTo(cursorSheetId) > 0 ||
x.AttendanceSheetId == cursorSheetId && x.StudentId.CompareTo(cursorStudentId) > 0))
.OrderBy(x => x.AttendanceSheet!.AttendanceDate).ThenBy(x => x.AttendanceSheetId).ThenBy(x => x.StudentId).Take(options.BatchSize)
.Select(x => new { x.AttendanceSheetId, x.StudentId, AttendanceDate = x.AttendanceSheet!.AttendanceDate, TeachingTaskId = x.AttendanceSheet.TeachingTaskId, AcademicTermId = x.AttendanceSheet.TeachingTask!.AcademicTermId, CollegeId = x.AttendanceSheet.TeachingTask.Course!.CollegeId, Status = (byte)x.Status, x.CheckInAt, CheckedInMethod = x.CheckedInMethod == null ? null : (byte?)x.CheckedInMethod, AppealStatus = (byte)x.AppealStatus, ProjectedAt = projectedAt })
.Select(x => new { x.AttendanceSheetId, x.StudentId, AttendanceDate = x.AttendanceSheet!.AttendanceDate, TeachingTaskId = x.AttendanceSheet.TeachingTaskId, AcademicTermId = x.AttendanceSheet.TeachingTask!.AcademicTermId, CollegeId = x.AttendanceSheet.TeachingTask.Course!.CollegeId, x.Status, x.CheckInAt, x.CheckedInMethod, x.AppealStatus, ProjectedAt = projectedAt })
.ToListAsync(cancellationToken);
if (rows.Count == 0) return count;
await client.InsertAsync("attendanceRecords", rows, cancellationToken);