perf(C6): 去重总数据列表顺序翻页改 keyset(前后端契约一起改)

背景:列表页 `ORDER BY id DESC LIMIT offset,size`,带筛选且选择性低时每页都要
对命中集做一次 filesort;深翻页 offset 也白扫索引。

改法(保持跳页/回退/改每页的原有行为):
- 后端:page 接口新增可选 `last_id` 游标 —— 传了就 `id < lastId` + `LIMIT size`(无 offset),
  响应新增 `nextLastId`(本页最后一行 id);不传仍是原 OFFSET 分页
- 管理前端:只有"下一页"用游标(上一页响应带回),跳页/改每页/筛选清空游标走 OFFSET
- 测试:新增 2 个后端契约测试(keyset 无 offset + nextLastId;无游标保持 LIMIT 30,15)

验证:mvn test 2897 全绿;admin-frontend-vue vue-tsc 通过 + 1619 测试全绿;
已部署 JAR 2b9ab774c60c3f5d802c9a2cee549008(双节点 health=200)与 admin-vue-20260914-112055。
This commit is contained in:
2026-09-14 11:21:40 +08:00
parent 1360a44e01
commit 9166656673
8 changed files with 105 additions and 4 deletions
@@ -43,6 +43,10 @@ const lockedGroupId = computed<number | null>(() => {
return groups.value.length === 1 ? groups.value[0].id : null
})
const jumpPage = ref('')
/** 顺序翻页游标:上一页响应给的 nextLastId;仅在"下一页"时使用,跳页/筛选时清空走 OFFSET。 */
const pageCursor = ref<number | null>(null)
/** 本次请求实际使用的游标(load 时决定) */
let pendingCursor: number | null = null
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
@@ -85,10 +89,15 @@ function stopExportWait(): void {
async function load(): Promise<void> {
loading.value = true
try {
const result = await fetchDedupeTotalList(toDedupeListParams(filter, page.value, pageSize.value))
const result = await fetchDedupeTotalList(
toDedupeListParams(filter, page.value, pageSize.value, pendingCursor),
)
rows.value = result.items
total.value = result.total
if (result.page >= 1) page.value = result.page
// 本页响应回传的游标留作"下一页"用;空页则清空(没有更多)
pageCursor.value = result.nextLastId ?? null
pendingCursor = null
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '去重数据加载失败')
} finally {
@@ -98,11 +107,15 @@ async function load(): Promise<void> {
function apply(): void {
page.value = 1
pageCursor.value = null
pendingCursor = null
void load()
}
function changePage(next: number): void {
if (next < 1 || next > totalPages.value) return
// 只有"顺序下一页"用 keyset 游标(避免深分页 offset);跳页/回退走 OFFSET,行为不变
pendingCursor = next === page.value + 1 ? pageCursor.value : null
page.value = next
void load()
}
@@ -119,6 +132,8 @@ function goJump(): void {
function changeSize(size: number) {
pageSize.value = size
page.value = 1
pageCursor.value = null
pendingCursor = null
void load()
}