feat(round-15): 跨境多币种发票税额/线索自动化阶段跃升/名片私密访问锁/芯片激光标刻排版 (P1/P2功能达成)

这个提交包含在:
wikiboss
2026-09-21 02:15:08 +08:00
父节点 45f5f74805
当前提交 e6d06f762c
共有 8 个文件被更改,包括 928 次插入 和 0 次删除

查看文件

@@ -0,0 +1,76 @@
import {
CardPrivacyPasswordService,
hashPrivacyPassword,
verifyPrivacyPassword,
generatePrivacyAccessToken,
verifyPrivacyAccessToken,
} from '../../../src/modules/card/service/privacy';
describe('Point 59 (P2): 移动微官网名片私密保护模式与访问口令鉴权', () => {
let service: CardPrivacyPasswordService;
const mockCardId = 601;
const rawPassword = 'Vip-Secret-Pass-2026';
beforeEach(() => {
service = new CardPrivacyPasswordService();
});
describe('1. 密码加盐哈希与校验 (hashPrivacyPassword & verifyPrivacyPassword)', () => {
it('应使用 SHA-256 加盐算法生成密码哈希,不存明文', () => {
const hashed = hashPrivacyPassword(rawPassword);
expect(hashed).toContain(':');
expect(hashed).not.toBe(rawPassword);
// 正确口令校验成功
expect(verifyPrivacyPassword(rawPassword, hashed)).toBe(true);
// 错误口令校验失败
expect(verifyPrivacyPassword('wrong-password', hashed)).toBe(false);
});
it('空密码或无效哈希时应安全返回 false', () => {
expect(verifyPrivacyPassword('', 'salt:hash')).toBe(false);
expect(verifyPrivacyPassword('pass', '')).toBe(false);
});
});
describe('2. 私密解锁授权凭据 (Privacy Access Token)', () => {
it('口令验证成功后生成包含时钟与签名的临时访问 Token', () => {
const token = generatePrivacyAccessToken(mockCardId, 'sys-secret-key', 3600);
expect(token).toBeDefined();
const verifyRes = verifyPrivacyAccessToken(mockCardId, token, 'sys-secret-key');
expect(verifyRes.valid).toBe(true);
expect(verifyRes.cardId).toBe(mockCardId);
});
it('Token 跨名片使用时应被严密拦截', () => {
const token = generatePrivacyAccessToken(mockCardId, 'sys-secret-key');
// 尝试用来解锁 602 名片
const verifyRes = verifyPrivacyAccessToken(602, token, 'sys-secret-key');
expect(verifyRes.valid).toBe(false);
expect(verifyRes.reason).toMatch(/不匹配|mismatch/i);
});
it('伪造或过期的 Token 应被坚决拒绝', () => {
const expiredToken = generatePrivacyAccessToken(mockCardId, 'sys-secret-key', -10); // 已过期
const res = verifyPrivacyAccessToken(mockCardId, expiredToken, 'sys-secret-key');
expect(res.valid).toBe(false);
expect(res.reason).toMatch(/过期|expired/i);
});
});
describe('3. CardPrivacyPasswordService 解锁与脱敏输出', () => {
it('未解锁前返回脱敏联系方式,验证口令后颁发解锁凭据', async () => {
const hashed = hashPrivacyPassword(rawPassword);
// 1. 验证错误口令
const failUnlock = await service.unlockCard(mockCardId, 'wrong', hashed);
expect(failUnlock.success).toBe(false);
// 2. 验证正确口令
const successUnlock = await service.unlockCard(mockCardId, rawPassword, hashed);
expect(successUnlock.success).toBe(true);
expect(successUnlock.accessToken).toBeDefined();
});
});
});

查看文件

@@ -0,0 +1,85 @@
import {
LeadStageAutomationService,
evaluateLeadStagePromotion,
LeadStage,
LeadBehaviorTrigger,
} from '../../../src/modules/crm/service/lead-automation';
describe('Point 58 (P1): 出海潜客线索行为自动化阶段推进引擎', () => {
let service: LeadStageAutomationService;
beforeEach(() => {
service = new LeadStageAutomationService();
});
describe('1. evaluateLeadStagePromotion (行为触发与阶段自动跃升)', () => {
it('新访客停留时长超过 120 秒且有 AI 咨询互动时,应自动从 new 推进为 contacted', () => {
const next = evaluateLeadStagePromotion('new', {
action: 'AI_CHAT_INTERACT',
staySeconds: 150,
radarScore: 65,
});
expect(next.promoted).toBe(true);
expect(next.targetStage).toBe('contacted');
expect(next.reason).toContain('AI');
});
it('访客提交留资或雷达意向分 >= 80 时,应自动推进为 qualified', () => {
const next = evaluateLeadStagePromotion('contacted', {
action: 'FORM_SUBMITTED',
radarScore: 85,
});
expect(next.promoted).toBe(true);
expect(next.targetStage).toBe('qualified');
});
it('成功预约跨国会议时,应直接跃升至 proposal 阶段', () => {
const next = evaluateLeadStagePromotion('qualified', {
action: 'MEETING_SCHEDULED',
});
expect(next.promoted).toBe(true);
expect(next.targetStage).toBe('proposal');
});
it('Stripe 订阅付款成功时,自动推进为 closed_won 成交', () => {
const next = evaluateLeadStagePromotion('proposal', {
action: 'PAYMENT_SUCCESS',
});
expect(next.promoted).toBe(true);
expect(next.targetStage).toBe('closed_won');
});
it('已有高阶段 (如 proposal) 不应因低频次事件发生逆向降级', () => {
const next = evaluateLeadStagePromotion('proposal', {
action: 'PAGE_VIEW',
staySeconds: 10,
});
expect(next.promoted).toBe(false);
expect(next.targetStage).toBe('proposal');
});
});
describe('2. LeadStageAutomationService 状态流转执行与日志审计', () => {
it('应自动执行线索推进并返回流转记录', async () => {
const mockLead = {
id: 701,
stage: 'new' as LeadStage,
name: 'Tariq',
};
const result = await service.processBehaviorEvent(mockLead, {
action: 'FORM_SUBMITTED',
radarScore: 90,
});
expect(result.previousStage).toBe('new');
expect(result.currentStage).toBe('qualified');
expect(result.historyLog.length).toBeGreaterThan(0);
});
});
});

查看文件

@@ -0,0 +1,77 @@
import {
NfcEngravingSpecService,
formatUidForLaserEngrave,
calculateIso7064CheckDigit,
generateLaserEngravingVectorSpec,
LaserEngravingOptions,
} from '../../../src/modules/nfc/service/engraving';
describe('Point 60 (P2): 实体卡芯片激光雕刻排版规范与校验码生成', () => {
let service: NfcEngravingSpecService;
const mockUid = '04a1b2c3d4e5f6';
beforeEach(() => {
service = new NfcEngravingSpecService();
});
describe('1. formatUidForLaserEngrave & calculateIso7064CheckDigit', () => {
it('应将原始 14 位 HEX UID 规范格式化为大写带冒号分隔', () => {
const formatted = formatUidForLaserEngrave(mockUid);
expect(formatted).toBe('04:A1:B2:C3:D4:E5:F6');
});
it('应基于 UID 计算 ISO 7064 模数校验位,供产线扫码枪二次防伪复核', () => {
const checkDigit = calculateIso7064CheckDigit(mockUid);
expect(typeof checkDigit).toBe('string');
expect(checkDigit).toHaveLength(2);
expect(/^[0-9]{2}$/.test(checkDigit)).toBe(true);
});
it('非法 UID (长度不等于 14 字符或非 HEX) 抛出明确校验异常', () => {
expect(() => formatUidForLaserEngrave('INVALID')).toThrow();
expect(() => calculateIso7064CheckDigit('12345')).toThrow();
});
});
describe('2. generateLaserEngravingVectorSpec (激光雕刻排版与矢量参数生成)', () => {
const options: LaserEngravingOptions = {
uid: mockUid,
holderName: 'Alex Zheng',
company: 'OmniCard Global Ltd.',
cardMaterial: 'MATTE_BLACK_METAL',
};
it('金属卡应配置适宜的激光功率 (Power=35%) 与标刻速率', () => {
const spec = generateLaserEngravingVectorSpec(options);
expect(spec.laserSettings.powerPercent).toBe(35);
expect(spec.laserSettings.speedMmPerSec).toBe(1200);
expect(spec.laserSettings.frequencyKhz).toBe(30);
// 文本坐标盒
expect(spec.elements.find(e => e.type === 'UID_TEXT')?.text).toBe('04:A1:B2:C3:D4:E5:F6');
expect(spec.elements.find(e => e.type === 'HOLDER_NAME')?.text).toBe('Alex Zheng');
});
it('支持包含防伪校验序列号与背部居中排布', () => {
const spec = generateLaserEngravingVectorSpec(options);
const serialElem = spec.elements.find(e => e.type === 'SECURITY_SERIAL');
expect(serialElem).toBeDefined();
expect(serialElem?.text.startsWith('SN-')).toBe(true);
});
});
describe('3. NfcEngravingSpecService 批量产线文件生成', () => {
it('应能为成批卡片一键生成激光机执行作业清单', async () => {
const result = await service.generateBatchEngravingJob([
{ uid: '04a1b2c3d4e5f6', holderName: 'Arthur', cardMaterial: 'MATTE_BLACK_METAL' },
{ uid: '04b1c2d3e4f5a6', holderName: 'Tariq', cardMaterial: 'GOLD_METAL' },
]);
expect(result.jobId).toBeDefined();
expect(result.cardCount).toBe(2);
expect(result.specs.length).toBe(2);
});
});
});

查看文件

@@ -0,0 +1,99 @@
import {
CurrencyTaxCalculatorService,
convertCurrency,
calculateInternationalTax,
TaxCalculationResult,
} from '../../../src/modules/team/service/currency-tax';
describe('Point 57 (P1): 跨境多币种汇率换算与出海发票税制计算', () => {
let service: CurrencyTaxCalculatorService;
beforeEach(() => {
service = new CurrencyTaxCalculatorService();
});
describe('1. convertCurrency (多币种汇率精准换算)', () => {
it('美元 USD 换算为阿联酋迪拉姆 AED (固定汇率 ~3.6725)', () => {
// 100 USD -> 367.25 AED
const aed = convertCurrency(100, 'USD', 'AED');
expect(aed).toBe(367.25);
});
it('欧元 EUR 换算为美元 USD', () => {
// 100 EUR -> 108 USD (基于 1.08 基准汇率)
const usd = convertCurrency(100, 'EUR', 'USD');
expect(usd).toBe(108);
});
it('同币种换算应保持原数值', () => {
expect(convertCurrency(500, 'USD', 'USD')).toBe(500);
expect(convertCurrency(800, 'AED', 'AED')).toBe(800);
});
it('不支持的币种应抛出明确异常保护', () => {
expect(() => convertCurrency(100, 'USD', 'XYZ' as any)).toThrow();
});
});
describe('2. calculateInternationalTax (出海合规发票税制计算)', () => {
it('阿联酋企业或个人消费应按 5% VAT 计税', () => {
const res = calculateInternationalTax({
amount: 1000,
currency: 'AED',
country: 'AE',
isB2b: true,
});
expect(res.taxRate).toBe(0.05);
expect(res.taxAmount).toBe(50);
expect(res.totalAmount).toBe(1050);
expect(res.taxName).toBe('UAE VAT');
});
it('欧盟企业间跨境 B2B (Reverse Charge) 应适用反向征税 0% VAT', () => {
const res = calculateInternationalTax({
amount: 2000,
currency: 'EUR',
country: 'DE', // 德国
isB2b: true,
vatNumber: 'DE123456789',
});
expect(res.taxRate).toBe(0);
expect(res.taxAmount).toBe(0);
expect(res.totalAmount).toBe(2000);
expect(res.isReverseCharge).toBe(true);
expect(res.taxName).toContain('Reverse Charge');
});
it('零税区或未开税国家 (如免税州/部分离岸) 税额为 0', () => {
const res = calculateInternationalTax({
amount: 500,
currency: 'USD',
country: 'US',
isB2b: true,
});
expect(res.taxRate).toBe(0);
expect(res.taxAmount).toBe(0);
expect(res.totalAmount).toBe(500);
});
});
describe('3. CurrencyTaxCalculatorService 综合结算账单生成', () => {
it('应生成包含格式化币种符号与详细税务明细的结算单', async () => {
const invoice = await service.generateTaxInvoiceSummary({
baseAmountUsd: 199, // 199 美元标准版年费
targetCurrency: 'AED',
billingCountry: 'AE',
isB2b: true,
});
expect(invoice.currency).toBe('AED');
expect(invoice.currencySymbol).toBe('AED ');
expect(invoice.subtotal).toBeCloseTo(730.83, 1);
expect(invoice.taxAmount).toBeGreaterThan(0);
expect(invoice.totalAmount).toBeGreaterThan(invoice.subtotal);
});
});
});