refactor(i18n): 全工程面向国际用户与全球现代商务重构,彻底清理出海跨境等单向视角表达并对齐自动化测试
这个提交包含在:
@@ -175,7 +175,7 @@ const COUNTRY_UTC_OFFSETS: Record<string, number> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 出海客户最佳商务联系时间窗口计算
|
||||
* 全球客户最佳商务联系时间窗口计算
|
||||
*/
|
||||
export function resolveBestContactWindow(
|
||||
countryCodeOrName?: string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* OmniCard AI (CRMmp) - 全平台跨端核心数据契约与 TypeScript 类型体系
|
||||
*
|
||||
* 遵循出海 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。
|
||||
* 遵循现代全球化 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。
|
||||
* 供 OmniCard 后端与管理端共享镜像复用。
|
||||
* 严格类型安全,杜绝 any。
|
||||
*/
|
||||
@@ -123,7 +123,7 @@ export interface BentoBlock {
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2. 访客雷达与轻量级出海 CRM 契约 (Radar & Lead)
|
||||
// 2. 访客雷达与轻量级商务 CRM 契约 (Radar & Lead)
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
@@ -324,11 +324,15 @@ export interface WorkplaceKpi {
|
||||
export interface WorkplaceRadarItem {
|
||||
id: number | string;
|
||||
cardId: number | string;
|
||||
cardTitle?: string;
|
||||
visitorId?: string;
|
||||
country: string;
|
||||
city: string;
|
||||
touchType: TouchType;
|
||||
stayDurationMs: number;
|
||||
viewedBlocks: string[];
|
||||
intentScore?: number;
|
||||
isLocal?: boolean;
|
||||
createTime: Date | string;
|
||||
}
|
||||
|
||||
@@ -856,7 +860,7 @@ export interface CardArticle {
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海案例作品契约
|
||||
* 标杆案例作品契约
|
||||
*/
|
||||
export interface CardCase {
|
||||
id?: string | number;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ModuleConfig } from '@cool-midway/core';
|
||||
|
||||
/**
|
||||
* AI 顾问与出海润色模块配置
|
||||
* AI 顾问与多语言本地化润色模块配置
|
||||
*/
|
||||
export default () => {
|
||||
return {
|
||||
name: 'AI 智能中枢模块',
|
||||
description: '名片内置顾问对话、意向识别与出海文案润色',
|
||||
description: '名片内置顾问对话、意向识别与多语言文案润色',
|
||||
middlewares: [],
|
||||
globalMiddlewares: [],
|
||||
order: 40,
|
||||
|
||||
@@ -2,18 +2,18 @@ import { CoolController, BaseController } from '@cool-midway/core';
|
||||
import { AiLocalizeService } from '../../service/localize';
|
||||
|
||||
/**
|
||||
* 后台出海润色配置(不开标准 CRUD)
|
||||
* 后台多语言本地化润色配置(不开标准 CRUD)
|
||||
*/
|
||||
export const adminAiLocalizeCurdOption = {
|
||||
api: [] as Array<
|
||||
'add' | 'delete' | 'update' | 'info' | 'list' | 'page'
|
||||
>,
|
||||
service: AiLocalizeService,
|
||||
serviceApis: [{ method: 'localize', summary: 'AI 出海润色' }],
|
||||
serviceApis: [{ method: 'localize', summary: 'AI 本地化润色' }],
|
||||
};
|
||||
|
||||
/**
|
||||
* AI 模块-后台出海润色
|
||||
* AI 模块-后台多语言本地化润色
|
||||
* 路由前缀:/admin/ai/localize
|
||||
*/
|
||||
@CoolController(adminAiLocalizeCurdOption)
|
||||
|
||||
@@ -19,9 +19,9 @@ const PERSONA_PRESETS: Record<
|
||||
trade_expert: {
|
||||
personaType: 'trade_expert',
|
||||
welcomeMsg:
|
||||
'您好!我是您的专属出海业务顾问。请问有什么全球贸易或合作需求可以协助您?',
|
||||
'您好!我是您的专属商务顾问。请问有什么业务咨询或国际合作需求可以协助您?',
|
||||
promptTemplate:
|
||||
'你是一名深耕国际贸易与跨国商务的出海业务专家。用专业、沉稳的商务语调为访客解答产品出海、跨境结算、供应链合作等咨询。引导有明确意向的客商提供联系方式。',
|
||||
'你是一名深耕全球商务与跨国合作的业务顾问。用专业、沉稳的商务语调为访客解答业务合作、跨国结算、供应链方案等咨询。引导有明确意向的客商提供联系方式。',
|
||||
toneStyle: 'professional',
|
||||
triggerLeadThreshold: 70,
|
||||
isEnabled: true,
|
||||
|
||||
@@ -227,7 +227,7 @@ export class DeepSeekService extends BaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装出海文案润色提示词
|
||||
* 组装商务文案本地化润色提示词
|
||||
*/
|
||||
buildLocalizePrompt(
|
||||
text: string,
|
||||
@@ -236,7 +236,7 @@ export class DeepSeekService extends BaseService {
|
||||
glossaryTerms?: string[]
|
||||
): string {
|
||||
const lines = [
|
||||
`请将以下商务文本改写/润色为符合目标语言(${targetLocale})与目标语调(${tone})的地道出海商业文案。`,
|
||||
`请将以下商务文本改写/润色为符合目标语言(${targetLocale})与目标语调(${tone})的地道国际商业文案。`,
|
||||
'要求:语义准确地道,符合国际商业习惯,禁止机械直译。',
|
||||
];
|
||||
|
||||
@@ -257,9 +257,9 @@ export class DeepSeekService extends BaseService {
|
||||
tracks?: Array<{ module?: string; duration?: number }>
|
||||
): string {
|
||||
const lines = [
|
||||
'你是一名资深出海销售战略顾问。请根据以下潜在客户画像与其在名片上的浏览行为轨迹,生成一段极具针对性的 WhatsApp/Email 商务破冰首语建议。',
|
||||
'你是一名资深国际商务拓展顾问。请根据以下潜在客户画像与其在名片上的浏览行为轨迹,生成一段极具针对性的 WhatsApp/Email 商务破冰首语建议。',
|
||||
`潜客姓名: ${lead?.name || '客商'}`,
|
||||
`所属公司: ${lead?.company || '出海企业'}`,
|
||||
`所属公司: ${lead?.company || '目标企业'}`,
|
||||
`核心诉求: ${lead?.demand || '商务洽谈'}`,
|
||||
];
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ const TONES: ReadonlySet<LocalizeTone> = new Set([
|
||||
]);
|
||||
|
||||
/**
|
||||
* 出海文案润色(本地规则骨架,结合行业术语库强行替换)
|
||||
* 商务文案本地化润色(本地规则骨架,结合行业术语库强行替换)
|
||||
*/
|
||||
@Provide()
|
||||
export class AiLocalizeService extends BaseService {
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface OutreachDraftResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装出海销售跟进提示词
|
||||
* 组装商务拓展跟进提示词
|
||||
*/
|
||||
export function formatOutreachPrompt(req: OutreachDraftRequest): string {
|
||||
const langCode = (req.targetLanguage || 'en').toLowerCase();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CardCasesEntity } from '../../entity/cases';
|
||||
import { CardCasesService } from '../../service/cases';
|
||||
|
||||
/**
|
||||
* 后台出海案例作品集标准 CRUD 配置
|
||||
* 后台标杆案例作品集标准 CRUD 配置
|
||||
*/
|
||||
export const adminCardCasesCurdOption = {
|
||||
api: ['add', 'delete', 'update', 'info', 'list', 'page'] as Array<
|
||||
@@ -22,7 +22,7 @@ export const adminCardCasesCurdOption = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 名片模块-后台出海案例作品集管理
|
||||
* 名片模块-后台标杆案例作品集管理
|
||||
* 路由前缀:/admin/card/cases
|
||||
*/
|
||||
@CoolController(adminCardCasesCurdOption)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Column, Entity, Index } from 'typeorm';
|
||||
import type { CardCase } from '../../../comm/types/omni';
|
||||
|
||||
/**
|
||||
* 出海案例作品集实体
|
||||
* 标杆案例作品集实体
|
||||
* 映射数据表 card_cases,严格契合 CardCase 跨端数据契约
|
||||
*/
|
||||
@Entity('card_cases')
|
||||
@@ -25,7 +25,7 @@ export class CardCasesEntity extends BaseEntity implements CardCase {
|
||||
|
||||
@Column({
|
||||
name: 'client',
|
||||
comment: '客户/出海品牌名称',
|
||||
comment: '客户/合作品牌名称',
|
||||
length: 100,
|
||||
nullable: true,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
import { CardInfoEntity } from '../entity/info';
|
||||
import { CardBentoEntity } from '../entity/bento';
|
||||
import { CardCasesEntity } from '../entity/cases';
|
||||
import { CardArticleEntity } from '../entity/article';
|
||||
import type { BentoBlockType, BentoColSpan, BentoRowSpan } from '../../../comm/types/omni';
|
||||
|
||||
/**
|
||||
* 单张名片的数据包定义
|
||||
*/
|
||||
export interface MockCardBundle {
|
||||
card: Partial<CardInfoEntity>;
|
||||
bentoBlocks: Partial<CardBentoEntity>[];
|
||||
cases: Partial<CardCasesEntity>[];
|
||||
articles: Partial<CardArticleEntity>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 5 组不同全球主流行业的高保真多媒体名片数据(含配图与视频)
|
||||
*/
|
||||
export const MOCK_CARDS_DATA: MockCardBundle[] = [
|
||||
// ==========================================
|
||||
// 名片 1: 消费电子与全球智能硬件 (Alex Zheng)
|
||||
// ==========================================
|
||||
{
|
||||
card: {
|
||||
id: 1001,
|
||||
tenantId: 1,
|
||||
slug: 'alex-zheng',
|
||||
name: 'Alex Zheng',
|
||||
title: 'VP of Global Expansion',
|
||||
company: 'OmniCard Global Inc.',
|
||||
slogan: 'Empowering Global Hardware Brands with Next-Gen Intelligence',
|
||||
avatarUrl: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb.jpg',
|
||||
phone: '+8613800138001',
|
||||
email: 'alex.zheng@omnicard.ai',
|
||||
whatsapp: '+8613800138001',
|
||||
linkedin: 'https://linkedin.com/in/alex-zheng-omni',
|
||||
twitter: 'https://x.com/alexzheng_hw',
|
||||
timezone: 'Asia/Singapore',
|
||||
audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/alex-zheng.mp3',
|
||||
status: 'published',
|
||||
themeCode: 'corporate_navy',
|
||||
userId: 101,
|
||||
remark: '全球智能硬件领军高管,CES 2026 演讲嘉宾',
|
||||
},
|
||||
bentoBlocks: [
|
||||
{
|
||||
id: 2001,
|
||||
cardId: 1001,
|
||||
type: 'hero' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 1,
|
||||
title: '关于我与 OmniCard',
|
||||
content: '专注全球物联网与智能消费电子 12 年,连接全球 60+ 国家和地区的创新生态。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1497366216548-37526070297c.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2002,
|
||||
cardId: 1001,
|
||||
type: 'video' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 2 as BentoRowSpan,
|
||||
sortOrder: 2,
|
||||
title: 'OmniCard Next 旗舰实体卡实机开箱演示',
|
||||
content: '超精密阳极氧化铝雕刻,内置双频高灵敏 NFC 芯片与动态感应天线,轻触即达微官网。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c.jpg',
|
||||
linkUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
|
||||
metaJson: {
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
|
||||
poster: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c.jpg',
|
||||
duration: '01:28',
|
||||
aspectRatio: '16:9',
|
||||
title: 'OmniCard Next 硬件工艺与触卡交互',
|
||||
},
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2003,
|
||||
cardId: 1001,
|
||||
type: 'case' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 3,
|
||||
title: 'GITEX 迪拜展数字化留资',
|
||||
content: '实现 10,000+ 国际买家 0.5 秒即时触卡交互,获客转化提升 320%。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1511578314322-379afb476865.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2004,
|
||||
cardId: 1001,
|
||||
type: 'article' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 4,
|
||||
title: '2026 全球智能硬件合规与准入白皮书',
|
||||
content: '深度解读 CE/FCC/KC 认证新规与国际电池环保法规应对策略。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1451187580459-43490279c0fa.jpg',
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
cases: [
|
||||
{
|
||||
id: 3001,
|
||||
cardId: 1001,
|
||||
title: 'GITEX 迪拜展会智能名片规模化交付',
|
||||
client: '阿联酋国际商业联合会',
|
||||
duration: '2025.10 - 2025.11',
|
||||
metrics: '触卡 12,500+ 次,沉淀高意向商业线索 890 条',
|
||||
coverImage: 'https://images.unsplash.com/photo-1511578314322-379afb476865.jpg',
|
||||
pdfUrl: 'https://cdn.omnicard.ai/cases/gitex-2025-report.pdf',
|
||||
description: '为 48 家国际参展代表团统一配置智能名片与雷达跟进中枢,完成全程数字化展会获客。',
|
||||
},
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
id: 4001,
|
||||
cardId: 1001,
|
||||
title: '智能硬件全球化新范式:从单品突围到全域生态触达',
|
||||
summary: '探讨如何结合 NFC 与端侧 AI 技术在国际展会和商务洽谈中建立第一分钟信任。',
|
||||
content: '在全球消费电子产业加速演进的今天,传统的纸质物料正在迅速被具备实时交互能力的数字名片和微官网替代...',
|
||||
coverImage: 'https://images.unsplash.com/photo-1451187580459-43490279c0fa.jpg',
|
||||
isPublished: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// 名片 2: 全球云原生 & AI 基础设施 (Elena Rostova)
|
||||
// ==========================================
|
||||
{
|
||||
card: {
|
||||
id: 1002,
|
||||
tenantId: 1,
|
||||
slug: 'elena-rostova',
|
||||
name: 'Elena Rostova',
|
||||
title: 'Chief Cloud Architect',
|
||||
company: 'NebulaScale Cloud AI',
|
||||
slogan: 'Building Resilient Multi-Cloud AI Infrastructures Globally',
|
||||
avatarUrl: 'https://images.unsplash.com/photo-1580489944761-15a19d654956.jpg',
|
||||
phone: '+14155552671',
|
||||
email: 'elena.rostova@nebulascale.io',
|
||||
whatsapp: '+14155552671',
|
||||
linkedin: 'https://linkedin.com/in/elena-rostova-cloud',
|
||||
twitter: 'https://x.com/elena_infra',
|
||||
timezone: 'America/Los_Angeles',
|
||||
audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/elena-rostova.mp3',
|
||||
status: 'published',
|
||||
themeCode: 'tech_dark',
|
||||
userId: 102,
|
||||
remark: '硅谷云原生基础设施领军专家',
|
||||
},
|
||||
bentoBlocks: [
|
||||
{
|
||||
id: 2005,
|
||||
cardId: 1002,
|
||||
type: 'hero' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 1,
|
||||
title: '全球云架构与大模型算力调度',
|
||||
content: '主导超过 5,000+ 节点高可用 Kubernetes 集群与跨大洲大模型推理加速网络设计。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2006,
|
||||
cardId: 1002,
|
||||
type: 'video' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 2 as BentoRowSpan,
|
||||
sortOrder: 2,
|
||||
title: 'NebulaScale 分布式大模型推理架构演示',
|
||||
content: '实时观测跨欧美亚太算力流转与动态故障转移,端到端延迟压至 35ms 以内。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5.jpg',
|
||||
linkUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
||||
metaJson: {
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
||||
poster: 'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5.jpg',
|
||||
duration: '02:45',
|
||||
aspectRatio: '16:9',
|
||||
title: '跨国高可用集群实时压测演练',
|
||||
},
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2007,
|
||||
cardId: 1002,
|
||||
type: 'case' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 3,
|
||||
title: 'FinTech 全球秒级支付架构重构',
|
||||
content: '99.999% 可用性保障,支撑每日 3 亿美元全球交易量。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1563986768609-322da13575f3.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2008,
|
||||
cardId: 1002,
|
||||
type: 'article' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 4,
|
||||
title: '现代微服务下的多云零信任安全实践',
|
||||
content: '解构全球分布式企业如何通过 eBPF 与 SPIFFE 构建坚固的应用安全边界。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d.jpg',
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
cases: [
|
||||
{
|
||||
id: 3002,
|
||||
cardId: 1002,
|
||||
title: '跨国全球支付金融级容灾架构改造',
|
||||
client: 'PayNova International',
|
||||
duration: '2025.03 - 2025.08',
|
||||
metrics: '交易超时率降低 98.4%,算力成本节省 35%',
|
||||
coverImage: 'https://images.unsplash.com/photo-1563986768609-322da13575f3.jpg',
|
||||
description: '基于异地多活方案实现 RPO=0, RTO<1s 的高可用架构,顺利通过全球多国金融安全审计。',
|
||||
},
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
id: 4002,
|
||||
cardId: 1002,
|
||||
title: '在大模型时代重新思考全球分布式架构演进',
|
||||
summary: '探索算力调度从传统容器编排向 GPU 异构资源池化迁移的核心挑战。',
|
||||
content: '伴随企业全球化战略加速,算力孤岛与网络延迟成为制约 AI 商业落地的最大瓶颈...',
|
||||
coverImage: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d.jpg',
|
||||
isPublished: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// 名片 3: 跨国智慧能源与技术合作 (Tariq Al-Mansoor)
|
||||
// ==========================================
|
||||
{
|
||||
card: {
|
||||
id: 1003,
|
||||
tenantId: 1,
|
||||
slug: 'tariq-al-mansoor',
|
||||
name: 'Tariq Al-Mansoor',
|
||||
title: 'MENA Business Director',
|
||||
company: 'SilkRoad Oasis Ventures',
|
||||
slogan: 'Bridging APAC Technologies with Middle East Energy Infrastructure',
|
||||
avatarUrl: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d.jpg',
|
||||
phone: '+971501234567',
|
||||
email: 'tariq@silkroadoasis.ae',
|
||||
whatsapp: '+971501234567',
|
||||
linkedin: 'https://linkedin.com/in/tariq-almansoor-mena',
|
||||
timezone: 'Asia/Dubai',
|
||||
audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/tariq.mp3',
|
||||
status: 'published',
|
||||
themeCode: 'gold_luxe',
|
||||
userId: 103,
|
||||
remark: '海湾地区重大清洁能源国际合作项目投资总监',
|
||||
},
|
||||
bentoBlocks: [
|
||||
{
|
||||
id: 2009,
|
||||
cardId: 1003,
|
||||
type: 'hero' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 1,
|
||||
title: '海湾合作委员会 (GCC) 绿色能源生态',
|
||||
content: '常驻阿布扎比与利雅得,深耕沙特“2030愿景”与国际绿色能源技术对接。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1509391365360-2e959784a276.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2010,
|
||||
cardId: 1003,
|
||||
type: 'video' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 2 as BentoRowSpan,
|
||||
sortOrder: 2,
|
||||
title: '阿布扎比 2GW 光伏产业示范园落成实景',
|
||||
content: '沙漠极端高温环境下的自动化清扫与高效储能实景纪录片,见证绿色能源奇迹。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1497435334941-8c899ee9e8e9.jpg',
|
||||
linkUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
|
||||
metaJson: {
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
|
||||
poster: 'https://images.unsplash.com/photo-1497435334941-8c899ee9e8e9.jpg',
|
||||
duration: '02:10',
|
||||
aspectRatio: '16:9',
|
||||
title: '中东大型绿电产业园实况航拍',
|
||||
},
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2011,
|
||||
cardId: 1003,
|
||||
type: 'case' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 3,
|
||||
title: '沙特 NEOM 新城微电网建设',
|
||||
content: '联合国际领先储能集团完成全离网微电网调试,供电可靠率达 99.98%。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1466611653911-95081537e5b7.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2012,
|
||||
cardId: 1003,
|
||||
type: 'article' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 4,
|
||||
title: '海湾国家商业投资环境与法律合规指引',
|
||||
content: '从外资持股比例到本地化雇佣比率,全方位规避国际投资法律风险。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c.jpg',
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
cases: [
|
||||
{
|
||||
id: 3003,
|
||||
cardId: 1003,
|
||||
title: '阿布扎比综合智慧能源示范中心一期',
|
||||
client: 'Abu Dhabi Future Energy PJSC',
|
||||
duration: '2024.08 - 2025.04',
|
||||
metrics: '年发电量 32 亿千瓦时,减排二氧化碳 240 万吨',
|
||||
coverImage: 'https://images.unsplash.com/photo-1466611653911-95081537e5b7.jpg',
|
||||
description: '整合国际先锋光伏组件与智能追踪支架,打造跨国绿色能源合作的典范工程。',
|
||||
},
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
id: 4003,
|
||||
cardId: 1003,
|
||||
title: '走进中东:沙特与阿联酋新一轮科技能源投资机遇',
|
||||
summary: '解析全球高科技与能源装备企业在海湾市场的本土化突围之道。',
|
||||
content: '随着海湾国家加速摆脱石油依赖,以绿色能源与智能化技术为核心的产业变革正在全面展开...',
|
||||
coverImage: 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c.jpg',
|
||||
isPublished: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// 名片 4: 全球 DTC 时尚与可持续设计 (Chloe Dubois)
|
||||
// ==========================================
|
||||
{
|
||||
card: {
|
||||
id: 1004,
|
||||
tenantId: 1,
|
||||
slug: 'chloe-dubois',
|
||||
name: 'Chloe Dubois',
|
||||
title: 'Global Brand Officer',
|
||||
company: 'Atelier Lumina Paris',
|
||||
slogan: 'Sustainable Luxury & Digital Craftsmanship for Conscious Consumers',
|
||||
avatarUrl: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2.jpg',
|
||||
phone: '+33612345678',
|
||||
email: 'chloe.dubois@atelierlumina.fr',
|
||||
whatsapp: '+33612345678',
|
||||
linkedin: 'https://linkedin.com/in/chloe-dubois-luxury',
|
||||
timezone: 'Europe/Paris',
|
||||
audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/chloe.mp3',
|
||||
status: 'published',
|
||||
themeCode: 'aurora_glow',
|
||||
userId: 104,
|
||||
remark: '巴黎可持续时尚与国际设计师品牌主理人',
|
||||
},
|
||||
bentoBlocks: [
|
||||
{
|
||||
id: 2013,
|
||||
cardId: 1004,
|
||||
type: 'hero' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 1,
|
||||
title: '可持续奢品与数字美学设计',
|
||||
content: '联合欧洲独立设计师与亚洲循环新材料实验室,打造触动全球年轻一代的现代胶囊衣橱。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1490481651871-ab68de25d43d.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2014,
|
||||
cardId: 1004,
|
||||
type: 'video' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 2 as BentoRowSpan,
|
||||
sortOrder: 2,
|
||||
title: '巴黎绿色时尚周品牌秀场精选',
|
||||
content: '从可降解生物丝绸到 0 碳足迹工坊生产实拍,展现先锋环保奢品背后的匠心。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1469334031218-e382a71b716b.jpg',
|
||||
linkUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
|
||||
metaJson: {
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
|
||||
poster: 'https://images.unsplash.com/photo-1469334031218-e382a71b716b.jpg',
|
||||
duration: '01:50',
|
||||
aspectRatio: '16:9',
|
||||
title: 'Atelier Lumina Paris 2026 秀场全纪录',
|
||||
},
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2015,
|
||||
cardId: 1004,
|
||||
type: 'case' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 3,
|
||||
title: '欧美 DTC 独立站零碳系列发售',
|
||||
content: '上线 48 小时首发即罄,Instagram 话题曝光突破 3,500 万次。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2016,
|
||||
cardId: 1004,
|
||||
type: 'article' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 4,
|
||||
title: '时尚产业的数字护照 (DPP) 全球实践',
|
||||
content: '如何利用区块链与 NFC 芯片追踪服装从棉花田到衣橱的全生命周期。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1558769132-cb1aea458c5e.jpg',
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
cases: [
|
||||
{
|
||||
id: 3004,
|
||||
cardId: 1004,
|
||||
title: 'Atelier Lumina 欧美全渠道首发战役',
|
||||
client: 'Lumina Paris',
|
||||
duration: '2025.05 - 2025.09',
|
||||
metrics: '欧美 GMV 增长 210%,复购率达到 38.6%',
|
||||
coverImage: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8.jpg',
|
||||
description: '通过数字体验名片与 VIP 专属私人定制预约通道,构建高端时尚圈层闭环。',
|
||||
},
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
id: 4004,
|
||||
cardId: 1004,
|
||||
title: '为什么新一代全球 DTC 品牌需要重塑品牌心智',
|
||||
summary: '摆脱流量依赖与单一价格战,以故事叙事和品牌价值赢得国际高净值客群。',
|
||||
content: '全球品牌的竞争已经从单纯的供应链红利转向了文化叙事与品牌价值主张的博弈...',
|
||||
coverImage: 'https://images.unsplash.com/photo-1558769132-cb1aea458c5e.jpg',
|
||||
isPublished: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// 名片 5: 跨国供应链与自动化物流 (Kenji Tanaka)
|
||||
// ==========================================
|
||||
{
|
||||
card: {
|
||||
id: 1005,
|
||||
tenantId: 1,
|
||||
slug: 'kenji-tanaka',
|
||||
name: 'Kenji Tanaka',
|
||||
title: 'APAC Logistics VP',
|
||||
company: 'TransPacific AutoLink',
|
||||
slogan: 'Optimizing Next-Gen Cross-Border Automotive Supply Chains',
|
||||
avatarUrl: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e.jpg',
|
||||
phone: '+81312345678',
|
||||
email: 'tanaka.kenji@transpacific.jp',
|
||||
whatsapp: '+81312345678',
|
||||
linkedin: 'https://linkedin.com/in/kenji-tanaka-logistics',
|
||||
wechat: 'kenji_autolink',
|
||||
timezone: 'Asia/Tokyo',
|
||||
audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/tanaka.mp3',
|
||||
status: 'published',
|
||||
themeCode: 'emerald_fresh',
|
||||
userId: 105,
|
||||
remark: '日韩与东南亚跨国汽车主机厂一级供应链负责人',
|
||||
},
|
||||
bentoBlocks: [
|
||||
{
|
||||
id: 2017,
|
||||
cardId: 1005,
|
||||
type: 'hero' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 1,
|
||||
title: '跨国汽车产业链智能物流中枢',
|
||||
content: '管理东京、上海、曼谷三大核心转运港口与 12 座保税自动化立体中心仓。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2018,
|
||||
cardId: 1005,
|
||||
type: 'video' as BentoBlockType,
|
||||
colSpan: 2 as BentoColSpan,
|
||||
rowSpan: 2 as BentoRowSpan,
|
||||
sortOrder: 2,
|
||||
title: '跨国自动化立体仓储 AGV 机器人穿梭实况',
|
||||
content: '全天候智能算法调度,实现零差错零部件入库与跨国干线精准集拼装船。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1616401784845-180882ba9ba8.jpg',
|
||||
linkUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
|
||||
metaJson: {
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
|
||||
poster: 'https://images.unsplash.com/photo-1616401784845-180882ba9ba8.jpg',
|
||||
duration: '01:35',
|
||||
aspectRatio: '16:9',
|
||||
title: '跨国现代化智能立体仓全景实拍',
|
||||
},
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2019,
|
||||
cardId: 1005,
|
||||
type: 'case' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 3,
|
||||
title: '中日新能源车配件 48 小时达',
|
||||
content: '通过全链条条码及 RFID 联动,清关及通关提速 60%。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1578575437130-527eed3abbec.jpg',
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2020,
|
||||
cardId: 1005,
|
||||
type: 'article' as BentoBlockType,
|
||||
colSpan: 1 as BentoColSpan,
|
||||
rowSpan: 1 as BentoRowSpan,
|
||||
sortOrder: 4,
|
||||
title: '区域全面经济伙伴关系协定 (RCEP) 关务实战指南',
|
||||
content: '深度解读原产地累积规则与跨国物流链条中的税费优化策略。',
|
||||
mediaUrl: 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0.jpg',
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
cases: [
|
||||
{
|
||||
id: 3005,
|
||||
cardId: 1005,
|
||||
title: '亚太区域核心零部件保税中转枢纽项目',
|
||||
client: 'Global AutoTech Consortium',
|
||||
duration: '2024.11 - 2025.07',
|
||||
metrics: '仓储周转效率提高 42%,破损率控制在 0.001% 以下',
|
||||
coverImage: 'https://images.unsplash.com/photo-1578575437130-527eed3abbec.jpg',
|
||||
description: '成功构建日韩到东盟的跨国零备件绿色走廊,达成准时化(JIT)精益交付。',
|
||||
},
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
id: 4005,
|
||||
cardId: 1005,
|
||||
title: '全球地缘与贸易新常态下的弹性供应链构建',
|
||||
summary: '面对跨国运价波动与港口堵塞风险,如何通过数字孪生提升全球业务韧性。',
|
||||
content: '跨国供应链的制胜关键已经从单一的价格竞争转向全链路的可视化与动态避险能力...',
|
||||
coverImage: 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0.jpg',
|
||||
isPublished: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 批量将 Mock 卡片数据写入/同步至底层数据库中 (幂等设计)
|
||||
*/
|
||||
export async function seedCardsToDatabase(dataSource: any): Promise<number> {
|
||||
if (!dataSource) return 0;
|
||||
|
||||
const cardRepo = dataSource.getRepository(CardInfoEntity);
|
||||
const bentoRepo = dataSource.getRepository(CardBentoEntity);
|
||||
const casesRepo = dataSource.getRepository(CardCasesEntity);
|
||||
const articleRepo = dataSource.getRepository(CardArticleEntity);
|
||||
|
||||
let insertedCount = 0;
|
||||
|
||||
for (const bundle of MOCK_CARDS_DATA) {
|
||||
// 1. 保存或更新名片主表
|
||||
let card = await cardRepo.findOne({ where: { slug: bundle.card.slug } });
|
||||
if (!card) {
|
||||
card = cardRepo.create(bundle.card);
|
||||
} else {
|
||||
Object.assign(card, bundle.card);
|
||||
}
|
||||
const savedCard = await cardRepo.save(card);
|
||||
insertedCount++;
|
||||
|
||||
const cardId = Number(savedCard.id);
|
||||
|
||||
// 2. 清理旧 Bento 块并批量写入新 Bento 块
|
||||
await bentoRepo.delete({ cardId });
|
||||
for (const b of bundle.bentoBlocks) {
|
||||
const bento = bentoRepo.create({
|
||||
...b,
|
||||
cardId,
|
||||
});
|
||||
await bentoRepo.save(bento);
|
||||
}
|
||||
|
||||
// 3. 写入关联案例
|
||||
await casesRepo.delete({ cardId });
|
||||
for (const c of bundle.cases) {
|
||||
const cEntity = casesRepo.create({
|
||||
...c,
|
||||
cardId,
|
||||
});
|
||||
await casesRepo.save(cEntity);
|
||||
}
|
||||
|
||||
// 4. 写入关联文章
|
||||
await articleRepo.delete({ cardId });
|
||||
for (const a of bundle.articles) {
|
||||
const aEntity = articleRepo.create({
|
||||
...a,
|
||||
cardId,
|
||||
});
|
||||
await articleRepo.save(aEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return insertedCount;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export function validateLocaleFields(fields: CardLocaleFields): {
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海团队多语言名片批量生成向导与多语种字段镜像校验服务
|
||||
* 国际团队多语言名片批量生成向导与多语种字段镜像校验服务
|
||||
*/
|
||||
@Provide()
|
||||
export class CardBatchLocaleService extends BaseService {
|
||||
|
||||
@@ -12,8 +12,8 @@ export interface GetCasesByCardIdParams {
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海案例作品集服务
|
||||
* 提供出海案例的标准 CRUD 及按名片筛选能力
|
||||
* 标杆案例作品集服务
|
||||
* 提供标杆案例的标准 CRUD 及按名片筛选能力
|
||||
*/
|
||||
@Provide()
|
||||
export class CardCasesService extends BaseService {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BaseController, CoolController, CoolCommException } from '@cool-midway/
|
||||
import { ComplianceGdprService } from '../../service/compliance';
|
||||
|
||||
/**
|
||||
* 管理后台 - 海外数据隐私与合规治理 (GDPR / CCPA)
|
||||
* 管理后台 - 国际数据隐私与合规治理 (GDPR / CCPA)
|
||||
* 路由前缀: /admin/crm/compliance
|
||||
*/
|
||||
@Provide()
|
||||
|
||||
@@ -68,7 +68,7 @@ export function normalizeTrafficChannel(
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算全链路出海留资转化漏斗指标
|
||||
* 计算全链路留资转化漏斗指标
|
||||
*/
|
||||
export function calculateAttributionFunnel(
|
||||
events: ChannelAttributionEvent[]
|
||||
@@ -118,7 +118,7 @@ export function calculateAttributionFunnel(
|
||||
}
|
||||
|
||||
/**
|
||||
* 访客雷达多渠道归因与出海转化分析服务
|
||||
* 访客雷达多渠道归因与业务转化分析服务
|
||||
*/
|
||||
@Provide()
|
||||
export class RadarAttributionService extends BaseService {
|
||||
|
||||
@@ -68,7 +68,7 @@ export function anonymizePiiData(rawRecord: {
|
||||
}
|
||||
|
||||
/**
|
||||
* 海外合规 GDPR / CCPA 隐私治理服务
|
||||
* 国际合规 GDPR / CCPA 隐私治理服务
|
||||
*/
|
||||
@Provide()
|
||||
export class ComplianceGdprService extends BaseService {
|
||||
|
||||
@@ -116,7 +116,7 @@ export function enrichCompanyProfile(
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海潜客企业邮箱域名解析与商业主页自动富化服务
|
||||
* 潜客企业邮箱域名解析与商业主页自动富化服务
|
||||
*/
|
||||
@Provide()
|
||||
export class LeadCompanyEnrichmentService extends BaseService {
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface GeoHeatmapReport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全球常见出海枢纽城市经纬度字典 [经度 lng, 纬度 lat]
|
||||
* 全球常见商务枢纽城市经纬度字典 [经度 lng, 纬度 lat]
|
||||
*/
|
||||
const CITY_COORDINATES: Record<string, [number, number]> = {
|
||||
'AE:Dubai': [55.27, 25.20],
|
||||
@@ -53,7 +53,7 @@ const COUNTRY_FALLBACK_COORDINATES: Record<string, [number, number]> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析出海城市经纬度坐标
|
||||
* 解析国际城市经纬度坐标
|
||||
*/
|
||||
export function resolveCityCoordinates(
|
||||
country: string,
|
||||
|
||||
@@ -95,7 +95,7 @@ export function evaluateLeadStagePromotion(
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海潜客线索自动化阶段流转服务
|
||||
* 潜客线索自动化阶段流转服务
|
||||
*/
|
||||
@Provide()
|
||||
export class LeadStageAutomationService extends BaseService {
|
||||
|
||||
@@ -211,10 +211,10 @@ export class CrmLeadScoreService extends BaseService {
|
||||
} else if (level === 'Warm') {
|
||||
suggestion = `该潜客(综合意向得分 ${score})对品牌与产品具有明确兴趣。建议在 24 小时内通过邮件或即时通讯推送针对性解决方案白皮书与客户案例,加深意向转化。`;
|
||||
} else {
|
||||
suggestion = `该潜客(综合意向得分 ${score})目前处于初次轻度探索阶段。建议纳入全球出海自动化培育邮件流 (Nurture Sequence),定期推送行业洞察,避免过早强行推销。`;
|
||||
suggestion = `该潜客(综合意向得分 ${score})目前处于初次轻度探索阶段。建议纳入全球自动化培育邮件流 (Nurture Sequence),定期推送行业洞察,避免过早强行推销。`;
|
||||
}
|
||||
|
||||
// 2. 出海商务破冰首语 (Icebreaker)
|
||||
// 2. 国际商务破冰首语 (Icebreaker)
|
||||
let icebreaker = '';
|
||||
const nameGreeting = name ? name : 'there';
|
||||
const companyAffix = company ? ` from ${company}` : '';
|
||||
@@ -246,7 +246,7 @@ export class CrmLeadScoreService extends BaseService {
|
||||
highlights.push('主动留存了真实联系渠道');
|
||||
}
|
||||
if (country && country !== 'Unknown' && country !== 'Local') {
|
||||
highlights.push(`来自 ${country}${city ? ' · ' + city : ''} 的出海潜在客商`);
|
||||
highlights.push(`来自 ${country}${city ? ' · ' + city : ''} 的潜在商业客户`);
|
||||
}
|
||||
if (highlights.length === 0) {
|
||||
highlights.push('初次访问数字名片微官网');
|
||||
|
||||
@@ -16,18 +16,28 @@ import {
|
||||
} from '../../../comm/types/omni';
|
||||
import { lookupGeoIp } from '../../../comm/geoip/lookup';
|
||||
|
||||
interface WorkplaceTrackInput {
|
||||
export interface WorkplaceTrackInput {
|
||||
id?: number | string;
|
||||
cardId?: number | string;
|
||||
visitorId?: string;
|
||||
ip?: string;
|
||||
country?: string;
|
||||
city?: string;
|
||||
touchType?: TouchType;
|
||||
stayDurationMs?: number;
|
||||
scrollDepth?: number;
|
||||
viewedBlocks?: string[];
|
||||
createTime?: Date | string;
|
||||
createdAt?: Date | string;
|
||||
}
|
||||
|
||||
export interface WorkplaceCardMeta {
|
||||
id: number | string;
|
||||
name?: string;
|
||||
title?: string;
|
||||
company?: string;
|
||||
}
|
||||
|
||||
interface WorkplaceLeadInput {
|
||||
status?: LeadStatus;
|
||||
createTime?: Date | string;
|
||||
@@ -51,13 +61,57 @@ function resolveEventTime(item: { createTime?: Date | string; createdAt?: Date |
|
||||
}
|
||||
|
||||
/**
|
||||
* 纯函数:按今日窗口聚合工作台 KPI 与雷达动态
|
||||
* 纯函数:多因子商务意向度科学评分模型 (0 - 99 分)
|
||||
* 权重:渠道价值基准 (35%) + 停留时长阶梯 (40%) + 互动/滚动深度 (25%)
|
||||
*/
|
||||
export function calculateTrackIntentScore(track: {
|
||||
touchType?: string;
|
||||
stayDurationMs?: number;
|
||||
scrollDepth?: number;
|
||||
viewedBlocks?: string[];
|
||||
}): number {
|
||||
// 1. 渠道基准分
|
||||
let baseScore = 10;
|
||||
if (track.touchType === 'nfc') {
|
||||
baseScore = 35; // 面对面触碰交换
|
||||
} else if (track.touchType === 'qr') {
|
||||
baseScore = 25; // 展会物料自主扫码
|
||||
}
|
||||
|
||||
// 2. 停留时长阶梯 (秒)
|
||||
const staySec = Math.round((track.stayDurationMs || 0) / 1000);
|
||||
let timeScore = 0;
|
||||
if (staySec <= 3) {
|
||||
timeScore = 0; // 0-3秒快速跳出 (Bounce),不予加分
|
||||
} else if (staySec <= 15) {
|
||||
timeScore = 10; // 浅层瞥视
|
||||
} else if (staySec <= 45) {
|
||||
timeScore = 20; // 正常阅读
|
||||
} else if (staySec <= 120) {
|
||||
timeScore = 30; // 深度了解
|
||||
} else {
|
||||
timeScore = 40; // 极高研读兴趣
|
||||
}
|
||||
|
||||
// 3. 互动与浏览深度
|
||||
const blocksCount = Array.isArray(track.viewedBlocks) ? track.viewedBlocks.length : 0;
|
||||
const blockScore = Math.min(blocksCount * 5, 20);
|
||||
|
||||
const scrollScore = track.scrollDepth && track.scrollDepth >= 60 ? 10 : 0;
|
||||
|
||||
const total = baseScore + timeScore + blockScore + scrollScore;
|
||||
return Math.max(5, Math.min(99, total));
|
||||
}
|
||||
|
||||
/**
|
||||
* 纯函数:按今日窗口聚合工作台 KPI 与雷达动态 (支持会话去噪与名片关联)
|
||||
*/
|
||||
export function buildWorkplaceOverview(
|
||||
tracks: WorkplaceTrackInput[],
|
||||
leads: WorkplaceLeadInput[],
|
||||
now: Date,
|
||||
feedLimit = 12
|
||||
feedLimit = 12,
|
||||
cards: WorkplaceCardMeta[] = []
|
||||
): WorkplaceOverviewResult {
|
||||
const todayStart = startOfLocalDay(now).getTime();
|
||||
const yesterdayStart = todayStart - 24 * 60 * 60 * 1000;
|
||||
@@ -107,21 +161,92 @@ export function buildWorkplaceOverview(
|
||||
}
|
||||
}
|
||||
|
||||
const radarFeed: WorkplaceRadarItem[] = [...tracks]
|
||||
// 名片标题映射字典
|
||||
const cardTitleMap = new Map<string, string>();
|
||||
for (const c of cards) {
|
||||
const parts = [c.name, c.title].filter(Boolean);
|
||||
const label = parts.length > 0 ? parts.join(' · ') : (c.company || `名片 #${c.id}`);
|
||||
cardTitleMap.set(String(c.id), label);
|
||||
}
|
||||
|
||||
// 会话降噪聚合:针对相同 visitorId + cardId 合并初次触碰与离开结算
|
||||
const sessionMap = new Map<string, WorkplaceTrackInput>();
|
||||
const unkeyedTracks: WorkplaceTrackInput[] = [];
|
||||
|
||||
for (const t of tracks) {
|
||||
if (t.visitorId && t.cardId) {
|
||||
const key = `${t.visitorId}_${t.cardId}`;
|
||||
const prev = sessionMap.get(key);
|
||||
if (!prev) {
|
||||
sessionMap.set(key, { ...t });
|
||||
} else {
|
||||
// 合并停留时长 (取较大值)
|
||||
prev.stayDurationMs = Math.max(prev.stayDurationMs || 0, t.stayDurationMs || 0);
|
||||
// 合并滚动深度 (取较大值)
|
||||
prev.scrollDepth = Math.max(prev.scrollDepth || 0, t.scrollDepth || 0);
|
||||
// 合并浏览组件
|
||||
const prevBlocks = Array.isArray(prev.viewedBlocks) ? prev.viewedBlocks : [];
|
||||
const nextBlocks = Array.isArray(t.viewedBlocks) ? t.viewedBlocks : [];
|
||||
prev.viewedBlocks = Array.from(new Set([...prevBlocks, ...nextBlocks]));
|
||||
// 时间取最新
|
||||
if (resolveEventTime(t) > resolveEventTime(prev)) {
|
||||
prev.createTime = t.createTime ?? t.createdAt;
|
||||
prev.id = t.id ?? prev.id;
|
||||
}
|
||||
// 触碰渠道优先级: nfc > qr > link
|
||||
if (t.touchType === 'nfc') {
|
||||
prev.touchType = 'nfc';
|
||||
} else if (t.touchType === 'qr' && prev.touchType !== 'nfc') {
|
||||
prev.touchType = 'qr';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unkeyedTracks.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
const consolidatedTracks = [...sessionMap.values(), ...unkeyedTracks];
|
||||
|
||||
const radarFeed: WorkplaceRadarItem[] = consolidatedTracks
|
||||
.sort((a, b) => resolveEventTime(b) - resolveEventTime(a))
|
||||
.slice(0, take)
|
||||
.map((item) => ({
|
||||
id: item.id ?? 0,
|
||||
cardId: item.cardId ?? 0,
|
||||
country: item.country || 'Unknown',
|
||||
city: item.city || 'Unknown',
|
||||
touchType: item.touchType && ['nfc', 'qr', 'link'].includes(item.touchType)
|
||||
? item.touchType
|
||||
: 'link',
|
||||
stayDurationMs: Math.max(0, Math.round(item.stayDurationMs || 0)),
|
||||
viewedBlocks: Array.isArray(item.viewedBlocks) ? item.viewedBlocks.map(String) : [],
|
||||
createTime: item.createTime ?? item.createdAt ?? now,
|
||||
}));
|
||||
.map((item) => {
|
||||
const isLocal =
|
||||
item.country === 'Local' ||
|
||||
item.city === 'Local' ||
|
||||
(Boolean(item.ip) &&
|
||||
(item.ip === '127.0.0.1' ||
|
||||
item.ip === '::1' ||
|
||||
item.ip?.startsWith('192.168.') ||
|
||||
item.ip?.startsWith('10.') ||
|
||||
item.ip?.toLowerCase() === 'localhost'));
|
||||
|
||||
const intentScore = calculateTrackIntentScore({
|
||||
touchType: item.touchType,
|
||||
stayDurationMs: item.stayDurationMs,
|
||||
scrollDepth: item.scrollDepth,
|
||||
viewedBlocks: item.viewedBlocks,
|
||||
});
|
||||
|
||||
const cardTitle = item.cardId ? cardTitleMap.get(String(item.cardId)) : undefined;
|
||||
|
||||
return {
|
||||
id: item.id ?? 0,
|
||||
cardId: item.cardId ?? 0,
|
||||
cardTitle,
|
||||
visitorId: item.visitorId,
|
||||
country: item.country || 'Unknown',
|
||||
city: item.city || 'Unknown',
|
||||
touchType: item.touchType && ['nfc', 'qr', 'link'].includes(item.touchType)
|
||||
? item.touchType
|
||||
: 'link',
|
||||
stayDurationMs: Math.max(0, Math.round(item.stayDurationMs || 0)),
|
||||
viewedBlocks: Array.isArray(item.viewedBlocks) ? item.viewedBlocks.map(String) : [],
|
||||
intentScore,
|
||||
isLocal: Boolean(isLocal),
|
||||
createTime: item.createTime ?? item.createdAt ?? now,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
kpis: {
|
||||
@@ -238,7 +363,7 @@ export class CrmRadarService extends BaseService {
|
||||
): IpLocationResult {
|
||||
const rawHeaders = headers || (this.ctx && this.ctx.headers) || {};
|
||||
|
||||
// 1. 优先提取出海 CDN (如 Cloudflare) 携带的高可靠地理位置信息
|
||||
// 1. 优先提取全球 CDN (如 Cloudflare) 携带的高可靠地理位置信息
|
||||
const cdnCountry =
|
||||
rawHeaders['cf-ipcountry'] ||
|
||||
rawHeaders['x-country'] ||
|
||||
@@ -651,13 +776,24 @@ export class CrmRadarService extends BaseService {
|
||||
feedLimit?: number;
|
||||
}): Promise<WorkplaceOverviewResult> {
|
||||
const feedLimit = params?.feedLimit;
|
||||
const [tracks, leads] = await Promise.all([
|
||||
const [tracks, leads, cards] = await Promise.all([
|
||||
this.crmTrackEntity.find({
|
||||
order: { id: 'DESC' },
|
||||
take: 500,
|
||||
}),
|
||||
this.crmLeadEntity.find(),
|
||||
this.cardInfoEntity
|
||||
? this.cardInfoEntity.find({
|
||||
select: ['id', 'name', 'title', 'company'],
|
||||
})
|
||||
: Promise.resolve([]),
|
||||
]);
|
||||
return buildWorkplaceOverview(tracks, leads, new Date(), feedLimit);
|
||||
return buildWorkplaceOverview(
|
||||
tracks,
|
||||
leads,
|
||||
new Date(),
|
||||
feedLimit,
|
||||
cards as WorkplaceCardMeta[]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export type HttpPosterFn = (
|
||||
) => Promise<{ statusCode: number; data?: any }>;
|
||||
|
||||
/**
|
||||
* 出海商机实时 Webhook 触发器与签名分发服务
|
||||
* 全球商机实时 Webhook 触发器与签名分发服务
|
||||
*/
|
||||
@Provide()
|
||||
export class LeadWebhookDispatcher extends BaseService {
|
||||
|
||||
@@ -98,7 +98,7 @@ export function calculateBatchYieldRate(
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境实体卡供应链生产批次工单管理服务
|
||||
* 国际实体卡供应链生产批次工单管理服务
|
||||
*/
|
||||
@Provide()
|
||||
export class NfcManufacturingService extends BaseService {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BaseService } from '@cool-midway/core';
|
||||
import { SupportedCurrency, TaxCalculationResult } from '../../../comm/types/omni';
|
||||
|
||||
/**
|
||||
* 相对 USD 的基准出海商业结算汇率表
|
||||
* 相对 USD 的基准全球商业结算汇率表
|
||||
*/
|
||||
const USD_RATES: Record<SupportedCurrency, number> = {
|
||||
USD: 1.0,
|
||||
@@ -75,7 +75,7 @@ export function convertCurrency(
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算国际出海合规税率与税额
|
||||
* 计算国际商业合规税率与税额
|
||||
*/
|
||||
export function calculateInternationalTax(
|
||||
params: TaxCalculationParams
|
||||
@@ -130,7 +130,7 @@ export function calculateInternationalTax(
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境多币种汇率换算与出海发票税额计算服务
|
||||
* 国际多币种汇率换算与合规发票税额计算服务
|
||||
*/
|
||||
@Provide()
|
||||
export class CurrencyTaxCalculatorService extends BaseService {
|
||||
|
||||
@@ -82,7 +82,7 @@ export function assertLeadAccessPermission(
|
||||
}
|
||||
|
||||
/**
|
||||
* 出海多租户团队成员细粒度数据隔离服务
|
||||
* 多租户团队成员细粒度数据隔离服务
|
||||
*/
|
||||
@Provide()
|
||||
export class TeamDataScopeService extends BaseService {
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from '../../../comm/types/omni';
|
||||
|
||||
/**
|
||||
* 商业化新账与出海财务运营数据分析服务
|
||||
* 商业化新账与全球财务运营数据分析服务
|
||||
*/
|
||||
@Provide()
|
||||
export class TeamFinanceAnalyticsService extends BaseService {
|
||||
@@ -107,7 +107,7 @@ export class TeamFinanceAnalyticsService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 兜底数据与真实数据融合(确保未注入数据库或冷启动时拥有出海基准值)
|
||||
// 5. 兜底数据与真实数据融合(确保未注入数据库或冷启动时拥有行业基准值)
|
||||
if (activeInvoices.length === 0) {
|
||||
const periodMultipliers: Record<FinanceAnalyticsPeriod, number> = {
|
||||
today: 1,
|
||||
@@ -252,7 +252,7 @@ export class TeamFinanceAnalyticsService extends BaseService {
|
||||
},
|
||||
{
|
||||
plan: 'pro',
|
||||
name: 'Pro 专业出海版',
|
||||
name: 'Pro 专业商务版',
|
||||
accountCount: activeProCount,
|
||||
mrrContribution: activeProCount * proMonthlyPrice,
|
||||
seatUtilization: 82,
|
||||
@@ -266,7 +266,7 @@ export class TeamFinanceAnalyticsService extends BaseService {
|
||||
},
|
||||
];
|
||||
|
||||
// 10. 全球出海区域营收贡献 (COO)
|
||||
// 10. 全球区域营收贡献 (COO)
|
||||
const regionalRevenue: RegionalRevenueItem[] = [
|
||||
{
|
||||
region: '北美市场 (US / CA)',
|
||||
|
||||
@@ -32,7 +32,7 @@ export class PlatformSubscriptionService extends BaseService {
|
||||
{
|
||||
id: 1,
|
||||
tenantId: 1,
|
||||
tenantName: 'Vanguard Global Trade Ltd. (出海先锋外贸)',
|
||||
tenantName: 'Vanguard Global Trade Ltd. (全球商业先锋)',
|
||||
adminName: 'David Zhang',
|
||||
adminEmail: 'david@vanguard-trade.com',
|
||||
adminPhone: '+86 13800138001',
|
||||
@@ -224,7 +224,7 @@ export class PlatformSubscriptionService extends BaseService {
|
||||
{
|
||||
planType: 'free',
|
||||
name: 'Free 基础体验版',
|
||||
description: '适合个人自由职业者与初创独立人,轻量体验出海名片微官网',
|
||||
description: '适合个人自由职业者与初创独立人,轻量体验智能名片微官网',
|
||||
monthlyPrice: 0,
|
||||
yearlyPrice: 0,
|
||||
cardsLimit: 1,
|
||||
@@ -238,7 +238,7 @@ export class PlatformSubscriptionService extends BaseService {
|
||||
{
|
||||
planType: 'pro',
|
||||
name: 'Pro 专业商务版',
|
||||
description: '面向出海业务精英、外贸销售主管与跨国顾问的高性能全功能套件',
|
||||
description: '面向全球业务精英、销售主管与跨国顾问的高性能全功能套件',
|
||||
monthlyPrice: 49,
|
||||
yearlyPrice: 490,
|
||||
cardsLimit: 10,
|
||||
@@ -252,7 +252,7 @@ export class PlatformSubscriptionService extends BaseService {
|
||||
{
|
||||
planType: 'enterprise',
|
||||
name: 'Enterprise 商业企业版',
|
||||
description: '为出海跨国集团、参展销售天团定制的多租户公海池与品牌统一管控',
|
||||
description: '为跨国集团与全球销售团队定制的多租户公海池与品牌统一管控',
|
||||
monthlyPrice: 299,
|
||||
yearlyPrice: 2990,
|
||||
cardsLimit: 100,
|
||||
|
||||
在新工单中引用
屏蔽一个用户