游标分页优化

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

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
@@ -62,14 +63,15 @@ public sealed class DegreeAwardsController(
[Authorize(Roles = Reviewers)]
public async Task<ActionResult> GetBatch(
Guid id,
[FromQuery] int page = 1,
[FromQuery] string? cursor = null,
[FromQuery] int pageSize = 20,
[FromQuery] string? keyword = null,
[FromQuery] DegreeAwardConclusion? conclusion = null,
CancellationToken token = default)
{
page = Math.Max(page, 1);
pageSize = Math.Clamp(pageSize, 10, 100);
if (!CursorToken.TryDecode<DegreeResultCursor>(cursor, out var after))
return BadRequest("学位授予结果游标无效。");
var batch = await db.DegreeAwardBatches.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == id, token);
if (batch is null) return NotFound();
@@ -104,9 +106,13 @@ public sealed class DegreeAwardsController(
source = source.Where(x => x.Conclusion == conclusion.Value);
var total = await source.CountAsync(token);
var items = await source.OrderBy(x => x.Student!.StudentNumber)
.Skip((page - 1) * pageSize)
.Take(pageSize)
if (after is not null)
{
source = source.Where(x => x.Student!.StudentNumber.CompareTo(after.StudentNumber) > 0 ||
x.Student.StudentNumber == after.StudentNumber && x.Id.CompareTo(after.Id) > 0);
}
var rows = await source.OrderBy(x => x.Student!.StudentNumber).ThenBy(x => x.Id)
.Take(pageSize + 1)
.Select(x => new
{
x.Id,
@@ -124,6 +130,9 @@ public sealed class DegreeAwardsController(
x.ReviewComment,
x.ReviewedAt
}).ToListAsync(token);
var hasMore = rows.Count > pageSize;
var items = rows.Take(pageSize).ToArray();
var last = items.LastOrDefault();
return Ok(new
{
@@ -144,12 +153,16 @@ public sealed class DegreeAwardsController(
{
Items = items,
Total = total,
Page = page,
HasMore = hasMore,
NextCursor = hasMore && last is not null
? CursorToken.Encode(new DegreeResultCursor(last.StudentNumber, last.Id)) : null,
PageSize = pageSize
}
});
}
private sealed record DegreeResultCursor(string StudentNumber, Guid Id);
[HttpPost("batches")]
[Authorize(Roles = Managers)]
public async Task<ActionResult> Create(