feat(admin): 用户列表新增「调余额」人工充值/扣减入口
- 新接口 POST /api/admin/users/:id/balance(requireAdmin):事务锁行、扣减不可为负、单次上限 100 万、写 finance_records 并推送站内通知 - 后台用户列表每行新增「调余额」按钮与弹窗(当前余额/方向/金额/备注) - 新增 admin-balance-adjust 护栏;113 套件/737 用例全绿
这个提交包含在:
@@ -0,0 +1,39 @@
|
||||
// 已读取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('调整用户钱包余额');
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户