通知中心游标分页
This commit is contained in:
1 parent
172577f668
commit
f5db2f6e77
4 files changed
+160
-64
No files matched your search
@@ -213,7 +213,7 @@ public sealed class NotificationsController(
|
||||
Guid? administrativeClassId,
|
||||
Guid? teachingTaskId,
|
||||
string? keyword,
|
||||
int page = 1,
|
||||
string? cursor = null,
|
||||
int pageSize = 30,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -241,17 +241,22 @@ public sealed class NotificationsController(
|
||||
if (filter.Role is not null && !SystemRoles.All.Contains(filter.Role))
|
||||
return ValidationProblem("所选身份类型无效。");
|
||||
|
||||
page = Math.Max(1, page);
|
||||
pageSize = Math.Clamp(pageSize, 10, 100);
|
||||
if (!CursorToken.TryDecode<RecipientCursor>(cursor, out var after))
|
||||
return BadRequest("收件人游标无效。");
|
||||
var source = ApplyRecipientFilter(
|
||||
ScopedRecipientQuery(scope, resolvedCollegeId),
|
||||
filter);
|
||||
var total = await source.CountAsync(cancellationToken);
|
||||
if (after is not null)
|
||||
{
|
||||
source = source.Where(x => x.DisplayName.CompareTo(after.DisplayName) > 0 ||
|
||||
x.DisplayName == after.DisplayName && x.Id.CompareTo(after.Id) > 0);
|
||||
}
|
||||
var users = await source
|
||||
.OrderBy(x => x.DisplayName)
|
||||
.ThenBy(x => x.UserName)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ThenBy(x => x.Id)
|
||||
.Take(pageSize + 1)
|
||||
.Select(user => new
|
||||
{
|
||||
user.Id,
|
||||
@@ -276,6 +281,8 @@ public sealed class NotificationsController(
|
||||
.FirstOrDefault()
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
var hasMore = users.Count > pageSize;
|
||||
users = users.Take(pageSize).ToList();
|
||||
var userIds = users.Select(x => x.Id).ToArray();
|
||||
var roleRows = await db.UserRoles.AsNoTracking()
|
||||
.Where(x => userIds.Contains(x.UserId))
|
||||
@@ -295,6 +302,7 @@ public sealed class NotificationsController(
|
||||
group => group.Key,
|
||||
group => group.Select(x => x.RoleName).Distinct().ToArray());
|
||||
|
||||
var last = users.LastOrDefault();
|
||||
return Ok(new
|
||||
{
|
||||
Items = users.Select(user => new
|
||||
@@ -308,11 +316,16 @@ public sealed class NotificationsController(
|
||||
Roles = rolesByUser.GetValueOrDefault(user.Id, [])
|
||||
}),
|
||||
Total = total,
|
||||
Page = page,
|
||||
HasMore = hasMore,
|
||||
NextCursor = hasMore && last is not null
|
||||
? CursorToken.Encode(new RecipientCursor(last.DisplayName, last.Id))
|
||||
: null,
|
||||
PageSize = pageSize
|
||||
});
|
||||
}
|
||||
|
||||
private sealed record RecipientCursor(string DisplayName, Guid Id);
|
||||
|
||||
[HttpPost("send")]
|
||||
[Authorize(Roles = Senders)]
|
||||
public async Task<ActionResult> Send(
|
||||
@@ -485,20 +498,24 @@ public sealed class NotificationsController(
|
||||
[HttpGet("sent")]
|
||||
[Authorize(Roles = Senders)]
|
||||
public async Task<ActionResult> GetSent(
|
||||
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.MessageDispatches.AsNoTracking()
|
||||
.Where(x => x.SenderUserId == userId);
|
||||
var total = await source.CountAsync(cancellationToken);
|
||||
var items = await source
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
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)
|
||||
.Take(pageSize + 1)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
@@ -511,7 +528,18 @@ public sealed class NotificationsController(
|
||||
x.CreatedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
return Ok(new { Items = items, Total = total });
|
||||
var hasMore = rows.Count > pageSize;
|
||||
var items = rows.Take(pageSize).ToArray();
|
||||
var last = items.LastOrDefault();
|
||||
return Ok(new
|
||||
{
|
||||
Items = items,
|
||||
HasMore = hasMore,
|
||||
NextCursor = hasMore && last is not null
|
||||
? CursorToken.Encode(new NotificationCursor(last.CreatedAt, last.Id))
|
||||
: null,
|
||||
PageSize = pageSize
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/read")]
|
||||
|
||||
@@ -138,15 +138,17 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
||||
|
||||
[HttpGet("records")]
|
||||
[Authorize(Roles = Managers + "," + SystemRoles.Counselor)]
|
||||
public async Task<ActionResult<PagedResult<WarningRecordListItem>>> GetRecords(
|
||||
public async Task<ActionResult<CursorPagedResult<WarningRecordListItem>>> GetRecords(
|
||||
Guid? academicTermId,
|
||||
WarningType? type,
|
||||
int page = 1,
|
||||
string? cursor = null,
|
||||
int pageSize = 20,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (page < 1 || pageSize is < 1 or > 100)
|
||||
return ValidationProblem("页码必须大于 0,且每页条数应在 1 至 100 之间。");
|
||||
if (pageSize is < 1 or > 100)
|
||||
return ValidationProblem("每批条数应在 1 至 100 之间。");
|
||||
if (!CursorToken.TryDecode<WarningCursor>(cursor, out var after))
|
||||
return BadRequest("预警记录游标无效。");
|
||||
|
||||
var q = db.WarningRecords.AsNoTracking().AsQueryable();
|
||||
if (scope.Current.Scope == DataScope.College || scope.Current.IsInRole(SystemRoles.Counselor))
|
||||
@@ -160,12 +162,15 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
||||
if (academicTermId.HasValue) q = q.Where(x => x.AcademicTermId == academicTermId);
|
||||
if (type.HasValue) q = q.Where(x => x.Type == type);
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
if (after is not null)
|
||||
{
|
||||
q = q.Where(x => x.CreatedAt < after.CreatedAt ||
|
||||
x.CreatedAt == after.CreatedAt && x.Id.CompareTo(after.Id) < 0);
|
||||
}
|
||||
var rows = await q
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.ThenByDescending(x => x.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Take(pageSize + 1)
|
||||
.Select(x => new WarningRecordListItem(
|
||||
x.Id,
|
||||
x.StudentId,
|
||||
@@ -180,9 +185,20 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
||||
x.AcknowledgeComment,
|
||||
x.CreatedAt))
|
||||
.ToListAsync(ct);
|
||||
return Ok(new PagedResult<WarningRecordListItem>(items, total, page, pageSize));
|
||||
var hasMore = rows.Count > pageSize;
|
||||
var items = rows.Take(pageSize).ToArray();
|
||||
var last = items.LastOrDefault();
|
||||
return Ok(new CursorPagedResult<WarningRecordListItem>(
|
||||
items,
|
||||
hasMore && last is not null
|
||||
? CursorToken.Encode(new WarningCursor(last.CreatedAt, last.Id))
|
||||
: null,
|
||||
hasMore,
|
||||
pageSize));
|
||||
}
|
||||
|
||||
private sealed record WarningCursor(DateTime CreatedAt, Guid Id);
|
||||
|
||||
// ═══════════ Student ═══════════
|
||||
[HttpGet("my-warnings")]
|
||||
[Authorize(Roles = SystemRoles.Student)]
|
||||
|
||||
Reference in new issue
Block a user