From b96b5d934ba73de4f9f3d7d706ca36d3dd878da1 Mon Sep 17 00:00:00 2001 From: zhx Date: Fri, 10 Jul 2026 10:47:12 +0800 Subject: [PATCH] fix: load all manager grant resources --- common/api.js | 72 ++++++++++- gonghui/manager-center/index.html | 4 +- gonghui/manager-center/script.js | 17 ++- package.json | 1 + ...test_manager_center_resource_pagination.js | 116 ++++++++++++++++++ 5 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 scripts/test_manager_center_resource_pagination.js diff --git a/common/api.js b/common/api.js index a601201..41b9504 100644 --- a/common/api.js +++ b/common/api.js @@ -1509,15 +1509,85 @@ var default_api = 'https://api.global-interaction.com/'; method: 'GET', }); }, - listResources: function (resourceType, pageSize) { + listResources: function (resourceType, pageSize, page) { return request('/api/v1/manager-center/resource-grants/resources', { method: 'GET', query: { resource_type: resourceType, + page: page || 1, page_size: pageSize || 20, }, }); }, + listAllResources: function (resourceType, pageSize) { + var requestedSize = Math.floor(Number(pageSize)); + var size = + Number.isFinite(requestedSize) && requestedSize > 0 + ? Math.min(requestedSize, 20) + : 20; + + // Gateway 单页最多返回 20 条;经理中心必须根据 total 拉完剩余分页,否则较早创建但已开启赠送的资源会永久被截断。 + return managerCenterAPI + .listResources(resourceType, size, 1) + .then(function (firstPage) { + var firstItems = (firstPage && firstPage.items) || []; + var total = Number(firstPage && firstPage.total); + if (!Number.isFinite(total) || total < firstItems.length) { + total = firstItems.length; + } + var pageCount = Math.max(1, Math.ceil(total / size)); + if (pageCount === 1) return firstPage || { items: [] }; + + var remainingRequests = []; + for (var page = 2; page <= pageCount; page += 1) { + remainingRequests.push( + managerCenterAPI.listResources( + resourceType, + size, + page + ) + ); + } + return Promise.all(remainingRequests).then( + function (remainingPages) { + var resources = []; + var seenResourceIDs = Object.create(null); + [firstPage] + .concat(remainingPages) + .forEach(function (pageData) { + ( + (pageData && pageData.items) || + [] + ).forEach(function (resource) { + var resourceID = + resource && + (resource.resource_id || + resource.resourceId); + // 分页期间后台可能调整排序;按资源 ID 去重可避免同一素材在两个相邻页重复展示。 + if ( + resourceID && + seenResourceIDs[String(resourceID)] + ) { + return; + } + if (resourceID) { + seenResourceIDs[ + String(resourceID) + ] = true; + } + resources.push(resource); + }); + }); + return Object.assign({}, firstPage || {}, { + items: resources, + page: 1, + page_size: size, + total: Math.max(total, resources.length), + }); + } + ); + }); + }, grantResource: function (payload) { return request('/api/v1/manager-center/resource-grants', { method: 'POST', diff --git a/gonghui/manager-center/index.html b/gonghui/manager-center/index.html index 79ca033..c928a46 100644 --- a/gonghui/manager-center/index.html +++ b/gonghui/manager-center/index.html @@ -714,10 +714,10 @@ - + - + diff --git a/gonghui/manager-center/script.js b/gonghui/manager-center/script.js index b70642c..a9d2a41 100644 --- a/gonghui/manager-center/script.js +++ b/gonghui/manager-center/script.js @@ -50,6 +50,7 @@ overview: null, permissions: {}, resourcesByType: {}, + resourceRequestsByType: {}, countries: [], }; var blockState = { @@ -1165,13 +1166,23 @@ renderGrantOptions(); return; } - api.listResources(resourceType, 20) + if (pageState.resourceRequestsByType[resourceType]) return; + + // 同一资源类型只保留一个全量分页请求;全部页面成功后才缓存,任一页失败时允许下次打开弹窗重新获取。 + pageState.resourceRequestsByType[resourceType] = api + .listAllResources(resourceType, 20) .then(function (data) { pageState.resourcesByType[resourceType] = (data && data.items) || []; - renderGrantOptions(); + delete pageState.resourceRequestsByType[resourceType]; + if (grantState.resourceType === resourceType) { + renderGrantOptions(); + } }) - .catch(showError); + .catch(function (error) { + delete pageState.resourceRequestsByType[resourceType]; + showError(error); + }); } function searchGrantUser() { diff --git a/package.json b/package.json index 54f11dd..113ad15 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dev:h5": "python3 scripts/h5_dev_server.py --host 127.0.0.1 --port 8788 --directory .", "dev:manager-center": "python3 scripts/h5_dev_server.py --host 127.0.0.1 --port 8788 --directory .", "aslan:webview": "node scripts/aslan_flutter_webview.js", + "test:manager-center": "node scripts/test_manager_center_resource_pagination.js", "format": "prettier --write \"**/*.{js,html,css,json,md}\"", "format:check": "prettier --check \"**/*.{js,html,css,json,md}\"" }, diff --git a/scripts/test_manager_center_resource_pagination.js b/scripts/test_manager_center_resource_pagination.js new file mode 100644 index 0000000..82dfe85 --- /dev/null +++ b/scripts/test_manager_center_resource_pagination.js @@ -0,0 +1,116 @@ +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const vm = require('node:vm'); + +const requestedPages = []; +const responsesByPage = { + 1: { + items: Array.from({ length: 20 }, (_, index) => ({ + resource_id: String(1000 - index), + name: `Resource ${index + 1}`, + })), + page: 1, + page_size: 20, + total: 26, + }, + 2: { + items: [ + { resource_id: '202', name: 'Rose' }, + { resource_id: '197', name: 'Stellar Aura' }, + { resource_id: '192', name: 'Bugatti' }, + { resource_id: '191', name: 'Cupid' }, + { resource_id: '175', name: 'SuperAdmin' }, + { resource_id: '165', name: 'Admin' }, + ], + page: 2, + page_size: 20, + total: 26, + }, +}; + +function response(payload) { + return { + headers: { get: () => '' }, + ok: true, + status: 200, + statusText: 'OK', + text: () => + Promise.resolve( + JSON.stringify({ code: 'OK', message: 'ok', data: payload }) + ), + }; +} + +async function main() { + const storage = new Map(); + const window = { + document: { documentElement: { setAttribute: () => {} } }, + fetch: (rawURL) => { + const url = new URL(rawURL); + assert.equal( + url.pathname, + '/api/v1/manager-center/resource-grants/resources' + ); + assert.equal(url.searchParams.get('resource_type'), 'avatar_frame'); + assert.equal(url.searchParams.get('page_size'), '20'); + const page = Number(url.searchParams.get('page')); + requestedPages.push(page); + return Promise.resolve(response(responsesByPage[page])); + }, + localStorage: { + getItem: (key) => storage.get(key) || null, + removeItem: (key) => storage.delete(key), + setItem: (key, value) => storage.set(key, String(value)), + }, + location: { + hash: '', + hostname: 'h5.global-interaction.com', + href: 'https://h5.global-interaction.com/gonghui/manager-center/', + origin: 'https://h5.global-interaction.com', + pathname: '/gonghui/manager-center/', + search: '', + }, + }; + const context = vm.createContext({ + FormData, + JSON, + Math, + Number, + Object, + Promise, + String, + URL, + URLSearchParams, + Uint32Array, + window, + }); + const apiSource = fs.readFileSync( + path.join(__dirname, '..', 'common', 'api.js'), + 'utf8' + ); + vm.runInContext(apiSource, context, { filename: 'common/api.js' }); + + const result = await window.HyAppAPI.managerCenter.listAllResources( + 'avatar_frame', + 20 + ); + assert.deepEqual(requestedPages, [1, 2]); + assert.equal(result.total, 26); + assert.equal(result.items.length, 26); + assert.ok( + result.items.some( + (item) => item.resource_id === '175' && item.name === 'SuperAdmin' + ) + ); + assert.ok( + result.items.some( + (item) => item.resource_id === '165' && item.name === 'Admin' + ) + ); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +});