文件
shanghaipeiwan/server/tests/admin-balance-adjust.spec.ts
xcbyy 14ce4b25f5 feat(admin): 用户列表新增「调余额」人工充值/扣减入口
- 新接口 POST /api/admin/users/:id/balance(requireAdmin):事务锁行、扣减不可为负、单次上限 100 万、写 finance_records 并推送站内通知

- 后台用户列表每行新增「调余额」按钮与弹窗(当前余额/方向/金额/备注)

- 新增 admin-balance-adjust 护栏;113 套件/737 用例全绿
2026-10-21 17:41:54 +08:00

40 行
1.7 KiB
TypeScript

// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第九十八批:后台人工调整余额。
* 背景:测试与运营需要给用户钱包充值/扣减,但后台没有入口,只能改库。
* 要求:必须管理员权限、事务锁行、禁止扣成负数、写财务流水。
*/
describe('第九十八批后台余额调整闸门', () => {
const root = path.resolve(__dirname, '../../');
const read = (rel: string) => fs.readFileSync(path.join(root, rel), 'utf-8');
const indexCode = read('server/src/index.ts');
const repo = read('server/src/db/repository.ts');
const adminPage = read('admin/arco-design-pro-vite/src/views/user/list/index.vue');
it('接口必须要求管理员权限', () => {
expect(indexCode).toContain("app.post('/api/admin/users/:id/balance', requireAdmin");
expect(indexCode).toContain('adjustUserBalance');
});
it('仓储层必须锁行、校验负数并写财务流水', () => {
const start = repo.indexOf('async adjustUserBalance(');
expect(start).toBeGreaterThan(-1);
const block = repo.slice(start, start + 3200);
expect(block).toContain('FOR UPDATE');
expect(block).toContain('余额不足');
expect(block).toContain('后台余额充值');
expect(block).toContain('后台余额扣减');
expect(block).toContain('INSERT INTO finance_records');
});
it('后台用户列表必须提供「调余额」入口与弹窗', () => {
expect(adminPage).toContain('openBalanceModal');
expect(adminPage).toContain('balanceModalVisible');
expect(adminPage).toContain('/api/admin/users/${balanceTarget.value.id}/balance');
expect(adminPage).toContain('调整用户钱包余额');
});
});