文件
shanghaipeiwan/server/tests/system-alerts-audit.spec.ts
xcbyy 81368f870f fix(order): 统一 expert_id 为大神档案 id,统计兼容历史接单;修复提现卡单告警误报
- claimOrder 写入 expert_id=companion.id、claimed_by=user.id;启动时幂等回填历史 expert_id=claimed_by 的数据

- getExpertStats / getExpertRevenueTrends 按 (expert_id=? OR claimed_by=?) 双口径统计,解决工作台与接单大厅口径不一致导致漏单

- withdrawals 新增 updated_at(ON UPDATE),提现卡单巡检改用最后更新时间,避免审核旧工单立即误报

- 115 套件/752 用例全绿;线上实测抢单写入 expert_id=9/claimed_by=2010,改后统计两口径一致
2026-10-22 10:31:21 +08:00

71 行
3.4 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 mysql = read('server/src/db/mysql.ts');
const repo = read('server/src/db/repository.ts');
const indexCode = read('server/src/index.ts');
const adminPage = read('admin/arco-design-pro-vite/src/views/safety/system-alerts/index.vue');
const safetyRoute = read('admin/arco-design-pro-vite/src/router/routes/modules/safety.ts');
const zhLocale = read('admin/arco-design-pro-vite/src/locale/zh-CN.ts');
const backupScript = read('server/scripts/backup-db.sh');
it('告警与审计表必须落库,并补充巡检所需索引', () => {
expect(mysql).toContain('CREATE TABLE IF NOT EXISTS system_alerts');
expect(mysql).toContain('CREATE TABLE IF NOT EXISTS admin_audit_logs');
expect(mysql).toContain("['withdrawals', 'idx_wd_status_created'");
expect(mysql).toContain("['payment_intents', 'idx_pi_status_created'");
});
it('巡检必须覆盖提现卡单、支付与订单不一致、待接单超时', () => {
expect(indexCode).toContain('runSystemAlertScan');
expect(indexCode).toContain('提现打款卡在「处理中」');
expect(indexCode).toContain('支付已成功但订单仍为待付款');
expect(indexCode).toContain('待接单订单超时未退款');
expect(indexCode).toContain('startSystemAlertScheduler');
});
it('敏感后台操作必须写审计日志', () => {
expect(repo).toContain('async createAdminAuditLog');
expect(indexCode).toContain('function logAdminAudit');
const actions = ['余额充值', '封禁用户', '删除用户', '通过大神入驻', '提现打款'];
for (const a of actions) expect(indexCode).toContain(a);
});
it('后台必须提供告警/审计查询与处理入口', () => {
expect(indexCode).toContain("app.get('/api/admin/system/alerts'");
expect(indexCode).toContain("app.get('/api/admin/audit-logs'");
expect(safetyRoute).toContain("path: 'system-alerts'");
expect(zhLocale).toContain('menu.safety.systemAlerts');
expect(adminPage).toContain('/api/admin/system/alerts');
expect(adminPage).toContain('/api/admin/audit-logs');
});
it('每日备份脚本必须校验压缩包并保留 14 天', () => {
expect(backupScript).toContain('mysqldump --single-transaction --quick --no-tablespaces');
expect(backupScript).toContain('gzip -t');
expect(backupScript).toContain("-mtime +14 -delete");
});
it('订阅消息必须识别演示账号的假 openid,避免 40003', () => {
expect(indexCode).toContain('function isPlausibleWechatOpenid');
expect(indexCode).toContain("if (/^wx_openid_/i.test(value)) return false;");
expect(indexCode).toContain('openidValid');
expect(indexCode).toContain('请改用真实微信登录用户测试');
});
it('订单 expert_id 必须统一为大神档案 id,统计口径兼容用户 id', () => {
expect(repo).toContain('const companionIdForOrder = comp ? Number(comp.id) || expertId : expertId;');
expect(repo).toContain('resolveExpertOrderScope');
expect(repo).toContain('(expert_id = ? OR claimed_by = ?)');
expect(mysql).toContain('SET o.expert_id = c.id');
});
});