持久化用户历史消费泥点投影
后台用户详情展示历史花费并保留手动对账 消费落账原子更新投影并提供存量初始化 为手动对账增加独立管理员操作权限 更新SpacetimeDB绑定、文档和测试
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
getAdminFeatureGateConfig,
|
||||
getAdminUserDetail,
|
||||
listAdminRechargeOrders,
|
||||
reconcileAdminUserConsumption,
|
||||
resolveAdminRechargeRefundManualReview,
|
||||
updateAdminAccount,
|
||||
uploadAdminEditorShowcaseCampaignImage,
|
||||
@@ -16,7 +17,7 @@ afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () => {
|
||||
test('后台账号创建和更新同时携带 Tab 与独立操作权限', async () => {
|
||||
const fetchMock = vi.fn().mockImplementation(() =>
|
||||
Promise.resolve(
|
||||
new Response(JSON.stringify({ account: { accountId: 'member-1' } }), {
|
||||
@@ -31,11 +32,13 @@ test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () =>
|
||||
displayName: '运营',
|
||||
password: 'secret123',
|
||||
tabPermissions: ['dashboard', 'tracking'],
|
||||
actionPermissions: ['profile-wallet-consumption-reconcile'],
|
||||
enabled: true,
|
||||
});
|
||||
await updateAdminAccount('owner-token', 'member/1', {
|
||||
displayName: '运营二组',
|
||||
tabPermissions: ['tracking'],
|
||||
actionPermissions: [],
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
@@ -44,6 +47,14 @@ test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () =>
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
headers: expect.objectContaining({ Authorization: 'Bearer owner-token' }),
|
||||
body: JSON.stringify({
|
||||
username: 'operator',
|
||||
displayName: '运营',
|
||||
password: 'secret123',
|
||||
tabPermissions: ['dashboard', 'tracking'],
|
||||
actionPermissions: ['profile-wallet-consumption-reconcile'],
|
||||
enabled: true,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(fetchMock.mock.calls[1]?.[0]).toBe('/admin/api/accounts/member%2F1');
|
||||
@@ -53,6 +64,7 @@ test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () =>
|
||||
body: JSON.stringify({
|
||||
displayName: '运营二组',
|
||||
tabPermissions: ['tracking'],
|
||||
actionPermissions: [],
|
||||
enabled: false,
|
||||
}),
|
||||
}),
|
||||
@@ -238,6 +250,33 @@ test('用户详情只发送实际提供的用户定位字段', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('历史花费手动对账使用独立管理员写接口', async () => {
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
userId: 'user-1',
|
||||
historicalConsumedPoints: 1300,
|
||||
changed: true,
|
||||
}),
|
||||
{ status: 200 },
|
||||
),
|
||||
);
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await reconcileAdminUserConsumption('token-1', { userId: 'user-1' });
|
||||
|
||||
expect(String(fetchMock.mock.calls[0]?.[0])).toBe(
|
||||
'/admin/api/profile/users/reconcile-consumption',
|
||||
);
|
||||
expect(fetchMock.mock.calls[0]?.[1]).toEqual(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
headers: expect.objectContaining({ Authorization: 'Bearer token-1' }),
|
||||
body: JSON.stringify({ userId: 'user-1' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('退款执行使用独立 execute 管理员路由', async () => {
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify({ outRefundNo: 'refund-1' }), {
|
||||
|
||||
Reference in New Issue
Block a user