游标分页优化

This commit is contained in:
biss committed 2026-08-23 10:03:32 +08:00
1 parent f5db2f6e77
commit 0a0713e65b
17 files changed
+448 -156

No files matched your search

@@ -30,8 +30,8 @@ public sealed class TeachingTasksController(
SystemRoles.AcademicAdmin;
[HttpGet]
public async Task<ActionResult<PagedResult<object>>> Get(
int page = 1,
public async Task<ActionResult<CursorPagedResult<object>>> Get(
string? cursor = null,
int pageSize = 20,
string? keyword = null,
Guid? academicTermId = null,
@@ -41,8 +41,9 @@ public sealed class TeachingTasksController(
string? teacherKeyword = null,
string? classKeyword = null)
{
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 10, 100);
if (!CursorToken.TryDecode<TaskCursor>(cursor, out var after))
return BadRequest("教学任务游标无效。");
var source = ScopedTasks().AsNoTracking();
if (academicTermId.HasValue)
source = source.Where(x => x.AcademicTermId == academicTermId);
@@ -75,11 +76,18 @@ public sealed class TeachingTasksController(
}
var total = await source.CountAsync(cancellationToken);
var items = await source
if (after is not null)
{
source = source.Where(x => x.AcademicTerm!.StartDate < after.TermStartDate ||
x.AcademicTerm.StartDate == after.TermStartDate &&
(x.TaskNumber.CompareTo(after.TaskNumber) > 0 ||
x.TaskNumber == after.TaskNumber && x.Id.CompareTo(after.Id) > 0));
}
var rows = await source
.OrderByDescending(x => x.AcademicTerm!.StartDate)
.ThenBy(x => x.TaskNumber)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ThenBy(x => x.Id)
.Take(pageSize + 1)
.Select(x => new
{
x.Id,
@@ -87,6 +95,7 @@ public sealed class TeachingTasksController(
x.Name,
x.AcademicTermId,
TermName = x.AcademicTerm!.Name,
TermStartDate = x.AcademicTerm.StartDate,
x.CourseId,
CourseCode = x.Course!.Code,
CourseName = x.Course.Name,
@@ -114,9 +123,17 @@ public sealed class TeachingTasksController(
x.UpdatedAt
})
.ToListAsync(cancellationToken);
return Ok(new PagedResult<object>(items, total, page, pageSize));
var hasMore = rows.Count > pageSize;
var items = rows.Take(pageSize).ToArray();
var last = items.LastOrDefault();
return Ok(new CursorPagedResult<object>(items.Cast<object>().ToArray(),
hasMore && last is not null
? CursorToken.Encode(new TaskCursor(last.TermStartDate, last.TaskNumber, last.Id)) : null,
hasMore, pageSize, total));
}
private sealed record TaskCursor(DateOnly TermStartDate, string TaskNumber, Guid Id);
[HttpGet("options")]
public async Task<ActionResult> GetOptions(
Guid academicTermId,