fix: load all manager grant resources

This commit is contained in:
zhx 2026-07-10 10:47:12 +08:00
parent 0a1221933e
commit b96b5d934b
5 changed files with 204 additions and 6 deletions

View File

@ -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',

View File

@ -714,10 +714,10 @@
<script src="../../common/toast.js"></script>
<script src="../../common/loading.js?v=20260709-lalu-loading"></script>
<script src="../../common/skeleton.js?v=20260709-skeleton-mascot"></script>
<script src="../../common/api.js?v=20260624-manager-badge-country"></script>
<script src="../../common/api.js?v=20260710-manager-resource-pages"></script>
<script src="../../common/params.js?v=20260608-manager-real"></script>
<script src="../../common/jsbridge.js"></script>
<script src="../../common/i18n.js?v=20260608"></script>
<script src="./script.js?v=20260709-page-skeleton"></script>
<script src="./script.js?v=20260710-manager-resource-pages"></script>
</body>
</html>

View File

@ -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() {

View File

@ -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}\""
},

View File

@ -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;
});