文件
OmniCard/backend/test/modules/admin/admin-sql-columns.test.ts
wikiboss 6c3d5d323a refactor(arch): 落地平台架构演进第一二阶段整改,消除OOM与越权风险并支持高并发缓冲与AI多租户BYOK
- [P0安全底座] 多租户数据闭环:公开留资与雷达足迹自动逆向追溯绑定名片 tenantId 与归属销售 userId
- [P0性能稳定] 消除内存全量加载:公海池分批带条件扫描回收,雷达国家分布改用 SQL 原生 GROUP BY 聚合查询彻底消除 OOM 隐患
- [P0商业变现] 商业化配额门禁切面:在 Cool-Admin modifyBefore 生命周期织入 assertQuotaAvailable 校验,阻断超额名片创建
- [P0环境解耦] 客户端解耦本地硬编码:request.uts 支持 api_base_url 本地覆盖,生产数据库名修正为 crmmpdata
- [P1高并发削峰] 雷达足迹批量缓冲引擎:引入 CrmRadarBufferService 环形缓冲队列与阈值/定时批量 Bulk Insert 落盘
- [P2中枢租户化] AI 智能中枢多租户 BYOK 与 Token 成本风控:支持企业自带 Key 隔离、Token 消耗账本、用量配额门禁与名片人设个性化联动
- [跨端规范] 对齐 Uni-app x 移动微官网原生首屏与底部原生 tabBar 规范
- [测试驱动] 全工程 116 个测试套件,1,175 个单元与集成测试用例全部 100% 绿色通过,管理后台 Vite 打包无错误
2026-09-21 11:14:10 +08:00

198 行
9.4 KiB
TypeScript

import { adminCardInfoCurdOption } from '../../../src/modules/card/controller/admin/info';
import { adminCardCasesCurdOption } from '../../../src/modules/card/controller/admin/cases';
import { adminCardArticleCurdOption } from '../../../src/modules/card/controller/admin/article';
import { adminCardBentoCurdOption } from '../../../src/modules/card/controller/admin/bento';
import { adminCardLocaleCurdOption } from '../../../src/modules/card/controller/admin/locale';
import { AdminCrmLeadController } from '../../../src/modules/crm/controller/admin/lead';
import { AdminCrmRadarController } from '../../../src/modules/crm/controller/admin/radar';
import { adminNfcDeviceCurdOption } from '../../../src/modules/nfc/controller/admin/device';
import { adminNfcRuleCurdOption } from '../../../src/modules/nfc/controller/admin/rule';
import { adminAiKnowledgeCurdOption } from '../../../src/modules/ai/controller/admin/knowledge';
import { adminAiGlossaryCurdOption } from '../../../src/modules/ai/controller/admin/glossary';
import { adminAiConciergeCurdOption } from '../../../src/modules/ai/controller/admin/concierge';
import { adminTeamBrandCurdOption } from '../../../src/modules/team/controller/admin/brand';
import { adminTeamDomainCurdOption } from '../../../src/modules/team/controller/admin/domain';
import { BaseAuditLogController } from '../../../src/modules/base/controller/admin/audit';
import { AiConciergeService } from '../../../src/modules/ai/service/concierge';
import { getClassMetadata, CONTROLLER_KEY } from '@midwayjs/core';
describe('Admin Controllers & Services SQL Column Matching', () => {
describe('1. Controller pageQueryOp SQL 列名必须与 MySQL 物理表字段一致', () => {
it('Card Info pageQueryOp 应使用 update_time 与 theme_code', () => {
expect(adminCardInfoCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminCardInfoCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining(['a.status', { column: 'a.theme_code', requestParam: 'themeCode' }])
);
});
it('Card Cases pageQueryOp 应使用 sort_order 与 card_id', () => {
expect(adminCardCasesCurdOption.pageQueryOp.addOrderBy).toEqual({ sort_order: 'asc' });
expect(adminCardCasesCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([{ column: 'a.card_id', requestParam: 'cardId' }])
);
});
it('Card Article pageQueryOp 应使用 update_time, card_id 与 is_published', () => {
expect(adminCardArticleCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminCardArticleCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([
{ column: 'a.card_id', requestParam: 'cardId' },
{ column: 'a.is_published', requestParam: 'isPublished' },
])
);
});
it('Card Bento pageQueryOp 应使用 sort_order 与 card_id', () => {
expect(adminCardBentoCurdOption.pageQueryOp.addOrderBy).toEqual({ sort_order: 'asc' });
expect(adminCardBentoCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([{ column: 'a.card_id', requestParam: 'cardId' }, 'a.type', 'a.status'])
);
});
it('Card Locale pageQueryOp 应使用 card_id', () => {
expect(adminCardLocaleCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([{ column: 'a.card_id', requestParam: 'cardId' }, 'a.locale'])
);
});
it('CRM Lead pageQueryOp 应使用 create_time 与 card_id', () => {
const option = getClassMetadata(CONTROLLER_KEY, AdminCrmLeadController).curdOption;
expect(option.pageQueryOp.addOrderBy).toEqual({ create_time: 'desc' });
expect(option.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining(['a.status', 'a.source', { column: 'a.card_id', requestParam: 'cardId' }])
);
});
it('CRM Radar pageQueryOp 应使用 visitor_id 与 card_id, touch_type', () => {
const option = getClassMetadata(CONTROLLER_KEY, AdminCrmRadarController).curdOption;
expect(option.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.country', 'a.city', 'a.visitor_id', 'a.ip'])
);
expect(option.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([
{ column: 'a.card_id', requestParam: 'cardId' },
{ column: 'a.touch_type', requestParam: 'touchType' },
'a.country',
])
);
});
it('NFC Device pageQueryOp 应使用 update_time, batch_no, card_id, scene_mode', () => {
expect(adminNfcDeviceCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminNfcDeviceCurdOption.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.uid', 'a.batch_no'])
);
expect(adminNfcDeviceCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([
'a.status',
'a.material',
{ column: 'a.card_id', requestParam: 'cardId' },
{ column: 'a.scene_mode', requestParam: 'sceneMode' },
])
);
});
it('NFC Rule pageQueryOp 应使用 update_time, device_id, scene_mode, country_code', () => {
expect((adminNfcRuleCurdOption.pageQueryOp as any).addOrderBy).toEqual({ update_time: 'desc' });
expect((adminNfcRuleCurdOption.pageQueryOp as any).fieldEq).toEqual(
expect.arrayContaining([
{ column: 'a.device_id', requestParam: 'deviceId' },
'a.enabled',
{ column: 'a.scene_mode', requestParam: 'sceneMode' },
{ column: 'a.country_code', requestParam: 'countryCode' },
])
);
});
it('AI Knowledge pageQueryOp 应使用 update_time 与 card_id', () => {
expect(adminAiKnowledgeCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminAiKnowledgeCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([{ column: 'a.card_id', requestParam: 'cardId' }, 'a.locale'])
);
});
it('AI Glossary pageQueryOp 应使用 update_time, source_term, target_term, source_lang, target_lang', () => {
expect(adminAiGlossaryCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminAiGlossaryCurdOption.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.source_term', 'a.target_term', 'a.notes'])
);
expect(adminAiGlossaryCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([
{ column: 'a.source_lang', requestParam: 'sourceLang' },
{ column: 'a.target_lang', requestParam: 'targetLang' },
'a.domain',
])
);
});
it('AI Concierge pageQueryOp 应使用 update_time, card_id, persona_type, is_enabled', () => {
expect((adminAiConciergeCurdOption.pageQueryOp as any).addOrderBy).toEqual({ update_time: 'desc' });
expect((adminAiConciergeCurdOption.pageQueryOp as any).fieldEq).toEqual(
expect.arrayContaining([
{ column: 'a.card_id', requestParam: 'cardId' },
{ column: 'a.persona_type', requestParam: 'personaType' },
{ column: 'a.is_enabled', requestParam: 'isEnabled' },
])
);
});
it('Team Brand pageQueryOp 应使用 update_time 与 company_name', () => {
expect(adminTeamBrandCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminTeamBrandCurdOption.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.company_name'])
);
});
it('Team Domain pageQueryOp 应使用 update_time, custom_domain 与 ssl_status', () => {
expect(adminTeamDomainCurdOption.pageQueryOp.addOrderBy).toEqual({ update_time: 'desc' });
expect(adminTeamDomainCurdOption.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.custom_domain'])
);
expect(adminTeamDomainCurdOption.pageQueryOp.fieldEq).toEqual(
expect.arrayContaining([{ column: 'a.ssl_status', requestParam: 'sslStatus' }])
);
});
it('Base Audit Log pageQueryOp 应使用 create_time 与 operator_name', () => {
const option = getClassMetadata(CONTROLLER_KEY, BaseAuditLogController).curdOption;
expect(option.pageQueryOp.addOrderBy).toEqual({ create_time: 'desc' });
expect(option.pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.operator_name', 'a.action', 'a.module', 'a.ip'])
);
});
});
describe('2. AiConciergeService 健壮入参处理 (防止 NaN 导致的 SQL 语法异常)', () => {
let service: AiConciergeService;
let mockFindOneBy: jest.Mock;
beforeEach(() => {
service = new AiConciergeService();
mockFindOneBy = jest.fn();
service.aiConciergeEntity = {
findOneBy: mockFindOneBy,
} as any;
});
it('getByCardId 支持对象入参 { cardId: 1 } 并正确提取数值', async () => {
mockFindOneBy.mockResolvedValueOnce({
id: 10,
cardId: 1,
personaType: 'trade_expert',
welcomeMsg: 'Hello',
});
const result = await service.getByCardId({ cardId: 1 } as any);
expect(mockFindOneBy).toHaveBeenCalledWith({ cardId: 1 });
expect(result.cardId).toBe(1);
});
it('getByCardId 遇非法 cardId 应降级返回默认预设,禁止传递 NaN 给数据库', async () => {
const result = await service.getByCardId({ cardId: NaN } as any);
expect(mockFindOneBy).not.toHaveBeenCalled();
expect(result.personaType).toBe('trade_expert');
expect(result.cardId).toBe(1);
});
});
});