perf: 清理无界缓存、合并收益走势查询、补齐组合索引并优化前端定时器/图片加载

- 移除只写不读的 verifyCodeCache(无界内存增长)

- 大神收益走势由 20 次逐日/逐周/逐月查询改为 3 次 GROUP BY 聚合

- 派单池地理编码改为并行,避免串行等待外部接口

- 补齐 orders/activities/participants/user_notifications/finance/withdrawals/posts 组合索引;修复 withdrawals 使用不存在 created_at 的失效索引

- 数据库连接池支持 MYSQL_CONNECTION_LIMIT/MYSQL_QUEUE_LIMIT

- 首页/弹窗定时器卸载清理,活动列表参与者按活动 id 查询,大厅组局按页取数,图片 lazy-load

- 121 套件/792 用例全绿;已部署并验证新索引与 9ms 走势查询
这个提交包含在:
xcbyy
2026-10-23 15:31:37 +08:00
父节点 0d574b6420
当前提交 94cf6cdc69
共有 10 个文件被更改,包括 187 次插入 和 100 次删除

查看文件

@@ -486,11 +486,10 @@ describe('ClientFeatures (TDD 测试套件: 用户端功能闭环)', () => {
expect(wsContent).toContain('completeOrderApi');
});
it('3.1 活动 4 位核销码必须建立 act:verify 缓存索引、执行 CAS 原子防重核销,并在核销后触发发起人费用清算与付费活动二级分润', () => {
it('3.1 活动 4 位核销码必须执行 CAS 原子防重核销,并在核销后触发发起人费用清算与付费活动二级分润', () => {
const repoPath = path.join(serverSrcDir, 'db', 'repository.ts');
const content = fs.readFileSync(repoPath, 'utf-8');
expect(content).toContain('act:verify:');
expect(content).toContain('WHERE id = ? AND checkin_status = 0');
expect(content).toContain('settleActivityCheckinAndReferral');
expect(content).toContain('活动核销费用清算');

查看文件

@@ -0,0 +1,63 @@
// 已读取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 repo = read('server/src/db/repository.ts');
const mysql = read('server/src/db/mysql.ts');
const indexCode = read('server/src/index.ts');
it('不得保留只写不读的无界 verifyCodeCache', () => {
expect(repo).not.toContain('verifyCodeCache');
});
it('大神收益走势必须使用 GROUP BY 聚合,而不是按天/周/月逐条查询', () => {
const start = repo.indexOf('async getExpertRevenueTrends(');
const block = repo.slice(start, start + 6000);
expect(block).toContain('GROUP BY d');
expect(block).toContain('GROUP BY bucket');
expect(block).toContain('GROUP BY ym');
});
it('活动列表参与者必须按当前页活动 id 查询', () => {
expect(repo).toContain('WHERE p.activity_id IN (?)');
});
it('派单池地理编码必须并行,避免串行等待外部接口', () => {
expect(indexCode).toContain('Promise.allSettled(missing.map');
});
it('提现索引必须使用真实存在的 apply_time,而不是不存在的 created_at', () => {
expect(mysql).toContain("['withdrawals', 'idx_wd_status_apply', 'status, apply_time']");
expect(mysql).toContain("['withdrawals', 'idx_wd_companion_apply', 'companion_id, apply_time']");
expect(mysql).not.toContain("['withdrawals', 'idx_wd_status_created', 'status, created_at']");
});
it('关键列表查询必须有组合索引', () => {
for (const v of [
"['orders', 'idx_orders_buyer_created', 'buyer_id, created_at']",
"['orders', 'idx_orders_claimed_created', 'claimed_by, created_at']",
"['activities', 'idx_act_pay_created', 'pay_type, created_at']",
"['participants', 'idx_activity_created', 'activity_id, created_at']",
"['user_notifications', 'idx_notif_user_created', 'user_id, created_at, id']",
"['posts', 'idx_posts_top_created', 'is_top, created_at']",
]) {
expect(mysql).toContain(v);
}
});
it('数据库连接池必须可通过环境变量调优,并有排队上限', () => {
expect(mysql).toContain('MYSQL_CONNECTION_LIMIT');
expect(mysql).toContain('MYSQL_QUEUE_LIMIT');
});
});

查看文件

@@ -20,7 +20,7 @@ describe('第一百批系统告警与后台审计', () => {
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("['withdrawals', 'idx_wd_status_apply'");
expect(mysql).toContain("['payment_intents', 'idx_pi_status_created'");
});