Merge pull request #2456 from Evsdrg/fix/unbounded-queries-and-sort
fix(repo): 公告查询添加分页上限,优化分组按账户数排序的数据加载方式
This commit is contained in:
commit
902af920b7
@ -204,7 +204,8 @@ func (r *announcementRepository) ListActive(ctx context.Context, now time.Time)
|
||||
announcement.Or(announcement.StartsAtIsNil(), announcement.StartsAtLTE(now)),
|
||||
announcement.Or(announcement.EndsAtIsNil(), announcement.EndsAtGT(now)),
|
||||
).
|
||||
Order(dbent.Desc(announcement.FieldID))
|
||||
Order(dbent.Desc(announcement.FieldID)).
|
||||
Limit(200)
|
||||
|
||||
items, err := q.All(ctx)
|
||||
if err != nil {
|
||||
|
||||
@ -283,47 +283,90 @@ func (r *groupRepository) ListWithFilters(ctx context.Context, params pagination
|
||||
}
|
||||
|
||||
func (r *groupRepository) listWithAccountCountSort(ctx context.Context, q *dbent.GroupQuery, params pagination.PaginationParams, total int) ([]service.Group, *pagination.PaginationResult, error) {
|
||||
groups, err := q.
|
||||
// 第一步:只查 ID + sort_order(轻量,不做分页 — 需要全量排序 account_count)。
|
||||
rows, err := q.Clone().
|
||||
Select(group.FieldID, group.FieldSortOrder).
|
||||
Order(dbent.Asc(group.FieldSortOrder), dbent.Asc(group.FieldID)).
|
||||
All(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
groupIDs := make([]int64, 0, len(groups))
|
||||
outGroups := make([]service.Group, 0, len(groups))
|
||||
for i := range groups {
|
||||
g := groupEntityToService(groups[i])
|
||||
outGroups = append(outGroups, *g)
|
||||
groupIDs = append(groupIDs, g.ID)
|
||||
type sortEntry struct {
|
||||
id int64
|
||||
sortOrder int
|
||||
accountCount int64
|
||||
}
|
||||
entries := make([]sortEntry, 0, len(rows))
|
||||
groupIDs := make([]int64, len(rows))
|
||||
for i, r := range rows {
|
||||
groupIDs[i] = r.ID
|
||||
entries = append(entries, sortEntry{id: r.ID, sortOrder: r.SortOrder})
|
||||
}
|
||||
|
||||
// 第二步:批量加载 account counts(一次 SQL)。
|
||||
counts, err := r.loadAccountCounts(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i := range outGroups {
|
||||
c := counts[outGroups[i].ID]
|
||||
outGroups[i].AccountCount = c.Total
|
||||
outGroups[i].ActiveAccountCount = c.Active
|
||||
outGroups[i].RateLimitedAccountCount = c.RateLimited
|
||||
for i := range entries {
|
||||
c := counts[entries[i].id]
|
||||
if c.Total > 0 {
|
||||
entries[i].accountCount = c.Total
|
||||
}
|
||||
}
|
||||
|
||||
// 第三步:Go 侧排序(数据量 = Group 总数,通常 < 200,安全)。
|
||||
sortOrder := params.NormalizedSortOrder(pagination.SortOrderDesc)
|
||||
sort.SliceStable(outGroups, func(i, j int) bool {
|
||||
if outGroups[i].AccountCount == outGroups[j].AccountCount {
|
||||
if outGroups[i].SortOrder == outGroups[j].SortOrder {
|
||||
return outGroups[i].ID < outGroups[j].ID
|
||||
}
|
||||
return outGroups[i].SortOrder < outGroups[j].SortOrder
|
||||
tieCmp := func(a, b sortEntry) bool {
|
||||
if a.sortOrder == b.sortOrder {
|
||||
return a.id < b.id
|
||||
}
|
||||
return a.sortOrder < b.sortOrder
|
||||
}
|
||||
sort.SliceStable(entries, func(i, j int) bool {
|
||||
if entries[i].accountCount == entries[j].accountCount {
|
||||
return tieCmp(entries[i], entries[j])
|
||||
}
|
||||
if sortOrder == pagination.SortOrderAsc {
|
||||
return outGroups[i].AccountCount < outGroups[j].AccountCount
|
||||
return entries[i].accountCount < entries[j].accountCount
|
||||
}
|
||||
return outGroups[i].AccountCount > outGroups[j].AccountCount
|
||||
return entries[i].accountCount > entries[j].accountCount
|
||||
})
|
||||
|
||||
return paginateSlice(outGroups, params), paginationResultFromTotal(int64(total), params), nil
|
||||
// 第四步:分页,只加载当前页需要的完整 Group。
|
||||
page := paginateSlice(entries, params)
|
||||
if len(page) == 0 {
|
||||
return nil, paginationResultFromTotal(int64(total), params), nil
|
||||
}
|
||||
|
||||
pageIDs := make([]int64, len(page))
|
||||
pageIdx := make(map[int64]int, len(page))
|
||||
for i, e := range page {
|
||||
pageIDs[i] = e.id
|
||||
pageIdx[e.id] = i
|
||||
}
|
||||
|
||||
groups, err := r.client.Group.Query().
|
||||
Where(group.IDIn(pageIDs...)).
|
||||
All(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
outGroups := make([]service.Group, len(page))
|
||||
for i := range groups {
|
||||
g := groupEntityToService(groups[i])
|
||||
c := counts[g.ID]
|
||||
g.AccountCount = c.Total
|
||||
g.ActiveAccountCount = c.Active
|
||||
g.RateLimitedAccountCount = c.RateLimited
|
||||
if idx, ok := pageIdx[g.ID]; ok {
|
||||
outGroups[idx] = *g
|
||||
}
|
||||
}
|
||||
|
||||
return outGroups, paginationResultFromTotal(int64(total), params), nil
|
||||
}
|
||||
|
||||
func groupListOrder(params pagination.PaginationParams) []func(*entsql.Selector) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user