- [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 打包无错误
198 行
9.4 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|