游标分页初步

This commit is contained in:
biss committed 2026-08-23 09:11:48 +08:00
1 parent 14af0910e7
commit bdb2e54521
5 files changed
+99 -20

No files matched your search

@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Net;
using System.Text.RegularExpressions;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
@@ -34,12 +35,13 @@ public sealed class NotificationsController(
bool? unreadOnly,
NotificationCategory? category,
string? keyword,
int page = 1,
string? cursor = null,
int pageSize = 20,
CancellationToken cancellationToken = default)
{
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 1, 100);
if (!CursorToken.TryDecode<NotificationCursor>(cursor, out var after))
return BadRequest("通知游标无效。");
var userId = currentUserDataScope.Current.UserId;
var source = db.Notifications.AsNoTracking()
.Where(x => x.UserId == userId);
@@ -55,12 +57,16 @@ public sealed class NotificationsController(
x.Content.Contains(value));
}
var total = await source.CountAsync(cancellationToken);
var items = await source
if (after is not null)
{
source = source.Where(x => x.CreatedAt < after.CreatedAt ||
x.CreatedAt == after.CreatedAt && x.Id.CompareTo(after.Id) < 0);
}
var rows = await source
.OrderByDescending(x => x.CreatedAt)
.ThenByDescending(x => x.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Take(pageSize + 1)
.Select(x => new
{
x.Id,
@@ -79,6 +85,9 @@ public sealed class NotificationsController(
x.CreatedAt
})
.ToListAsync(cancellationToken);
var hasMore = rows.Count > pageSize;
var items = rows.Take(pageSize).ToArray();
var last = items.LastOrDefault();
var unreadCount = await db.Notifications
.CountAsync(x => x.UserId == userId && !x.IsRead, cancellationToken);
@@ -91,12 +100,18 @@ public sealed class NotificationsController(
return Ok(new
{
Items = items,
Total = total,
HasMore = hasMore,
NextCursor = hasMore && last is not null
? CursorToken.Encode(new NotificationCursor(last.CreatedAt, last.Id))
: null,
PageSize = pageSize,
UnreadCount = unreadCount,
UnreadByCategory = unreadByCategory
});
}
private sealed record NotificationCursor(DateTime CreatedAt, Guid Id);
[HttpGet("unread-count")]
public async Task<ActionResult> GetUnreadCount(CancellationToken cancellationToken)
{