From 17882ca1cb0652b97b2fe854b5a5a8980126529b Mon Sep 17 00:00:00 2001 From: wikiboss Date: Mon, 21 Sep 2026 15:24:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(i18n):=20=E5=85=A8=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E9=9D=A2=E5=90=91=E5=9B=BD=E9=99=85=E7=94=A8=E6=88=B7=E4=B8=8E?= =?UTF-8?q?=E5=85=A8=E7=90=83=E7=8E=B0=E4=BB=A3=E5=95=86=E5=8A=A1=E9=87=8D?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E5=BD=BB=E5=BA=95=E6=B8=85=E7=90=86=E5=87=BA?= =?UTF-8?q?=E6=B5=B7=E8=B7=A8=E5=A2=83=E7=AD=89=E5=8D=95=E5=90=91=E8=A7=86?= =?UTF-8?q?=E8=A7=92=E8=A1=A8=E8=BE=BE=E5=B9=B6=E5=AF=B9=E9=BD=90=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8C=96=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 159 +- README.md | 8 +- admin/src/locale/index.ts | 2 +- admin/src/pages/ai/concierge/index.tsx | 4 +- admin/src/pages/ai/concierge/locale/index.ts | 6 +- .../src/pages/ai/translation/locale/index.ts | 4 +- admin/src/pages/card/articles/index.tsx | 4 +- admin/src/pages/card/articles/locale/index.ts | 2 +- admin/src/pages/card/cases/index.tsx | 2 +- admin/src/pages/card/cases/locale/index.ts | 14 +- admin/src/pages/dashboard/financial/index.tsx | 2 +- .../pages/dashboard/financial/locale/index.ts | 6 +- .../pages/dashboard/radar-map/locale/index.ts | 2 +- admin/src/pages/dashboard/workplace/index.tsx | 121 +- .../pages/dashboard/workplace/locale/index.ts | 30 +- .../workplace/style/index.module.less | 2 +- admin/src/pages/login/locale/index.ts | 6 +- .../src/pages/platform/subscribers/index.tsx | 2 +- .../pages/platform/subscriptions/index.tsx | 4 +- .../pages/settings/audit-logs/locale/index.ts | 2 +- admin/src/types/omni.ts | 10 +- admin/tests/ai-concierge.test.ts | 2 +- admin/tests/card-cases.test.ts | 2 +- admin/tests/dashboard-workplace.test.ts | 37 +- admin/tests/routes-menu.test.ts | 2 +- admin/tests/tokens-and-locale.test.ts | 14 +- .../tests/workplace-layout-alignment.test.ts | 2 +- backend/database/apply-seed.js | 40 + backend/database/seed_cards.sql | 287 ++++ backend/package.json | 1 + backend/src/comm/i18n/geo.ts | 2 +- backend/src/comm/types/omni.ts | 10 +- backend/src/modules/ai/config.ts | 4 +- .../modules/ai/controller/admin/localize.ts | 6 +- backend/src/modules/ai/service/concierge.ts | 4 +- backend/src/modules/ai/service/deepseek.ts | 8 +- backend/src/modules/ai/service/localize.ts | 2 +- backend/src/modules/ai/service/outreach.ts | 2 +- .../modules/card/controller/admin/cases.ts | 4 +- backend/src/modules/card/entity/cases.ts | 4 +- backend/src/modules/card/mock/seed-cards.ts | 619 ++++++++ .../src/modules/card/service/batch-locale.ts | 2 +- backend/src/modules/card/service/cases.ts | 4 +- .../crm/controller/admin/compliance.ts | 2 +- .../src/modules/crm/service/attribution.ts | 4 +- backend/src/modules/crm/service/compliance.ts | 2 +- backend/src/modules/crm/service/enrichment.ts | 2 +- .../src/modules/crm/service/geo-heatmap.ts | 4 +- .../modules/crm/service/lead-automation.ts | 2 +- backend/src/modules/crm/service/lead-score.ts | 6 +- backend/src/modules/crm/service/radar.ts | 174 +- .../modules/crm/service/webhook-dispatcher.ts | 2 +- .../src/modules/nfc/service/manufacturing.ts | 2 +- .../src/modules/team/service/currency-tax.ts | 6 +- .../src/modules/team/service/data-scope.ts | 2 +- .../modules/team/service/finance-analytics.ts | 8 +- .../team/service/platform-subscription.ts | 8 +- backend/test/comm/calendar-invite.test.ts | 2 +- backend/test/comm/contact-window.test.ts | 4 +- backend/test/e2e/omnicard-30-points.test.ts | 36 +- backend/test/modules/ai/deepseek.test.ts | 12 +- backend/test/modules/ai/localize.test.ts | 2 +- backend/test/modules/ai/outreach.test.ts | 2 +- backend/test/modules/ai/sandbox.test.ts | 6 +- .../test/modules/ai/sentiment-intent.test.ts | 2 +- backend/test/modules/card/admin-bento.test.ts | 6 +- backend/test/modules/card/article.test.ts | 8 +- .../test/modules/card/batch-locale.test.ts | 10 +- backend/test/modules/card/bento.test.ts | 12 +- backend/test/modules/card/calendar.test.ts | 18 +- backend/test/modules/card/cases.test.ts | 20 +- backend/test/modules/card/portal.test.ts | 6 +- .../test/modules/card/poster-layout.test.ts | 2 +- backend/test/modules/card/seed-cards.test.ts | 121 ++ backend/test/modules/crm/attribution.test.ts | 4 +- backend/test/modules/crm/bot-filter.test.ts | 2 +- .../modules/crm/btp-20-push-webhook.test.ts | 2 +- backend/test/modules/crm/compliance.test.ts | 2 +- backend/test/modules/crm/enrichment.test.ts | 2 +- backend/test/modules/crm/geo-heatmap.test.ts | 2 +- .../test/modules/crm/lead-automation.test.ts | 2 +- .../modules/crm/tenant-lead-track.test.ts | 4 +- .../modules/crm/webhook-dispatcher.test.ts | 2 +- backend/test/modules/crm/workplace.test.ts | 94 ++ .../test/modules/nfc/manufacturing.test.ts | 2 +- .../test/modules/team/currency-tax.test.ts | 6 +- backend/test/modules/team/data-scope.test.ts | 2 +- .../modules/team/finance-analytics.test.ts | 2 +- backend/test/modules/team/handover.test.ts | 2 +- backend/test/modules/team/live-rank.test.ts | 4 +- backend/test/modules/team/members.test.ts | 10 +- backend/test/mpuniapp/article-detail.test.ts | 126 ++ backend/test/mpuniapp/case-detail.test.ts | 76 + backend/test/mpuniapp/deeplink-router.test.ts | 2 +- backend/test/mpuniapp/i18n-rtl.test.ts | 4 +- backend/test/mpuniapp/portal-cache.test.ts | 2 +- backend/test/mpuniapp/tabbar-config.test.ts | 2 +- docs/PRODUCT_REQUIREMENTS_DOCUMENT.md | 36 +- docs/mpuniapp-ui-refactor-plan.md | 775 +++++++++ docs/test-evidence/round-2/test-btp-08.ps1 | 44 +- docs/test-evidence/round-4/test-btp-15.ps1 | 8 +- docs/test-evidence/round-4/test-btp-16.ps1 | 4 +- .../round-5/browser-init-btp19.js | 4 +- docs/test-evidence/round-5/browser-init.js | 4 +- docs/test-evidence/round-5/inject-panel.js | 2 +- docs/test-evidence/round-5/test-btp-20.ps1 | 2 +- docs/test-evidence/round-6/browser-init.js | 2 +- docs/test-evidence/round-6/test-btp-24.ps1 | 2 +- mpuniapp/App.uvue | 4 +- mpuniapp/common/deeplink-router.uts | 2 +- mpuniapp/common/tokens.uts | 51 +- mpuniapp/common/vcard-export.uts | 2 +- mpuniapp/pages/common/login.uvue | 903 +++++++++-- mpuniapp/pages/common/useLogin.uts | 12 +- mpuniapp/pages/owner/card/cases.uvue | 2 +- mpuniapp/pages/owner/card/qrcode.uvue | 891 ++++++++++- mpuniapp/pages/owner/card/useCardCases.uts | 2 +- mpuniapp/pages/owner/hall/index.uvue | 667 ++++++-- mpuniapp/pages/owner/nfc/mode.uvue | 1124 +++++++++++-- mpuniapp/pages/owner/nfc/scan.uvue | 978 +++++++++++- mpuniapp/pages/owner/nfc/security.uvue | 1413 ++++++++++++++--- mpuniapp/pages/owner/nfc/useSecurity.uts | 76 +- mpuniapp/pages/owner/profile/useProfile.uts | 2 +- mpuniapp/pages/owner/radar.uvue | 833 +++++++++- mpuniapp/pages/owner/team/useOverview.uts | 30 +- mpuniapp/pages/owner/useLeads.uts | 104 +- mpuniapp/pages/owner/useRadar.uts | 258 ++- mpuniapp/pages/portal/ai-chat.uvue | 492 ++++-- mpuniapp/pages/portal/article-detail.uvue | 767 ++++++++- mpuniapp/pages/portal/case-detail.uvue | 760 +++++++-- mpuniapp/pages/portal/exchange.uvue | 1161 +++++++++++--- mpuniapp/pages/portal/useArticleDetail.uts | 362 ++++- mpuniapp/pages/portal/useCaseDetail.uts | 144 +- mpuniapp/pages/portal/useExchange.uts | 355 +++++ mpuniapp/tests/card-cases.test.ts | 4 +- 135 files changed, 12928 insertions(+), 1653 deletions(-) create mode 100644 backend/database/apply-seed.js create mode 100644 backend/database/seed_cards.sql create mode 100644 backend/src/modules/card/mock/seed-cards.ts create mode 100644 backend/test/modules/card/seed-cards.test.ts create mode 100644 docs/mpuniapp-ui-refactor-plan.md diff --git a/AGENTS.md b/AGENTS.md index d51502f..f6da2f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,10 +8,10 @@ - **项目名称**:OmniCard AI (全球智能数字名片 & 移动微官网 SaaS) - **远程仓库**:`https://gitcode.gynhc.com/wikiboss/OmniCard.git` -- **业务定位**:面向全球商务出海的下一代 **AI 驱动型智能名片 + 移动微官网 + 访客雷达 CRM + NFC 实体卡管理** 一体化平台。 +- **业务定位**:面向全球现代商务、跨国团队与专业顾问的新一代 **AI 驱动型智能名片 + 移动微官网 + 访客雷达 CRM + NFC 实体卡管理** 一体化平台。 - **业务核心模块**: 1. 微官网搭建与展示系统(Bento Grid 便当盒美学、响应式与 RTL 多语言适配) - 2. 访客雷达与轻量级出海 CRM(国家定位、停留轨迹、意向度评分、线索跟进) + 2. 访客雷达与轻量级商务 CRM(国家定位、停留轨迹、意向度评分、线索跟进) 3. AI 智能中枢与数字人顾问(全天候接待客商、智能留资、多语言地道润色) 4. NFC 实体卡硬件云端生命周期管理(场景切换、远程挂失) 5. 企业团队管理与多租户商业化订阅体系 @@ -83,7 +83,7 @@ CRMmp/ - **类型安全**:全面采用 TypeScript,严禁滥用 `any`。严格定义 Props 接口与服务端 API 响应契约。 - **状态与样式**: - 遵循现代 React 最佳实践(Hooks、函数组件、避免多余状态冗余)。 - - 优先复用 Arco 官方样式变量与工具类,保持设计一致性与出海高端商务调性。 + - 优先复用 Arco 官方样式变量与工具类,保持设计一致性与国际高端商务调性。 ### 3.3 用户端:uni-app x (App + H5) 规范 (目录: mpuniapp/) - **语法与语言**: @@ -178,100 +178,97 @@ CRMmp/ 更新版,已把**风格约束**一起并入 AI Coding 约束。可直接放入 `.cursorrules` / `CLAUDE.md`。 ```md -# NexaCard AI — AI Coding 约束 +# OmniCard AI — AI Coding & UI/UX 核心设计约束 -## 风格定位 -- 瑞士国际主义 + 现代 SaaS + 编辑感 -- 内容优先,留白即品牌,AI 隐形,CRM 无感,NFC 轻量 -- 全球商务、中性、可信、克制 -- 禁止炫技、拟物、霓虹、玻璃拟态、重装饰 -- 一屏只允许 1 个主 CTA +## 1. 风格定位 (Aesthetic & Style) +- **Bento Grid(便当盒美学)模组化布局 + 现代消费级全球化 SaaS(Consumerized B2B SaaS)** +- **场景优先(Context-First)**:移动端名片出示场景采用**沉浸式全屏大二维码展台** + **底部大圆角浮层抽屉(Bottom Sheet)**,物理 NFC 实体卡与数字名片具象化联动 +- **极简科技纯净感**:中性冷白底色为主,辅以科技天蓝与局部极光弥散微光晕(Ambient Aurora / Mesh Gradient) +- **全球现代商务**:可信、现代、通透、高呼吸感与留白,杜绝冰冷传统表单后台感 +- **一屏仅允许 1 个核心主 CTA**(悬浮胶囊式或居中凸起发送快捷按钮) -## 技术栈 - -## 设计 Token(必须引用,禁止硬编码) +## 2. 设计 Token (Design Tokens - 必须引用,禁止硬编码) 颜色 Light / Dark: -- primary: #0B5CFF / #4D8DFF -- primary-soft: #EAF1FF / #102A4D -- ai: #7C5CFF / #9B8CFF -- bg: #FFFFFF / #0B0D10 -- surface: #F7F8FA / #12151A -- border: #E3E7ED / #262B33 -- text: #101828 / #F2F4F7 -- text-2: #667085 / #98A2B3 +- primary: #0088FF / #3894FF(科技天蓝/钴蓝) +- primary-gradient: linear-gradient(135deg, #00A3FF 0%, #0B63E5 100%) +- primary-soft: #EBF5FF / #0D2847 +- aurora: linear-gradient(135deg, #00D2FF 0%, #0088FF 50%, #6366F1 100%)(极光弥散) +- bg: #F5F8FC / #0B0D10(冷白天幕底色) +- surface: #FFFFFF / #13171F(纯白卡片容器) +- border: #EBF1F7 / #232936(极浅分割微线) +- text: #101828 / #F2F4F7(主内容高对比) +- text-2: #64748B / #94A3B8(次级说明文字) - success: #12B76A / #32D583 - warning: #F79009 / #FDB022 - error: #F04438 / #F97066 -间距:4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 -圆角:按钮 12px,卡片 16px,模态 20px -阴影:0 1px 2px rgba(16,24,40,0.04) -字体:Inter, Noto Sans SC, Noto Sans Arabic, Noto Sans JP, Noto Sans KR -字号:12 / 14 / 16 / 20 / 24 / 32 / 40 +间距:4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 / 64 +平滑大圆角 (Squircle): +- 按钮与胶囊标签: 14px / 9999px (Pill) +- Bento 卡片与内容容器: 20px / 24px +- 移动端底部抽屉与模态面板: 顶角 24px / 28px +- 二维码展台卡片: 20px +漫反射环境软阴影 (Ambient Soft Shadow): +- shadow-card: 0 8px 30px rgba(0, 0, 0, 0.04), 0 2px 8px rgba(11, 99, 229, 0.03) +- shadow-cta: 0 8px 20px rgba(0, 136, 255, 0.22) +- shadow-float: 0 12px 36px rgba(16, 24, 40, 0.08) +字体:Inter, Noto Sans SC, Noto Sans Arabic, Noto Sans JP, Noto Sans KR, SF Pro Display +字号:12 / 14 / 16 / 18 / 20 / 24 / 32 / 40 行高:正文 1.6,标题 1.25,阿语 1.7 -最大宽度:内容 1200px,文章 720px +最大宽度:内容 1200px,文章 720px,移动端自适应 480px 行宽:45–75 字符 -## 配色规则 -- 中性色占 80–90%,品牌色占 10–20% -- 品牌主色只用单一钴蓝 -- 语义色只用于状态,不做装饰 -- 禁止红、绿、黄作为品牌主色 -- AI 渐变只允许小面积,面积 <5% -- NFC 用主色 20% 透明脉冲,一次 400ms,不循环 +## 3. 配色与光影规则 +- **比例原则**:纯净冷白与中性色占 85%–90%,品牌科技蓝占 10%–15% +- **受控弥散光晕**:极光渐变(Aurora)仅限局部高价值区域(沉浸式展示头部、品牌卡、NFC 实体卡演示位、雷达高光),面积 < 8%,禁止全屏杂乱渐变 +- **环境软阴影**:以超大半径、极淡漫反射阴影营造卡片微悬浮感,替代厚重硬边框 +- **语义色只用于状态**:成功绿、警告橙、错误红仅用于雷达意向度、系统状态与验证提示,不做大面积装饰 +- **NFC 反馈**:触碰感应触发主色 20% 透明单次微脉冲动效,时长 400ms,不循环 -## 组件规则 -- 按钮高 40 / 44 / 48,触控区 ≥44×44 -- 输入框高 44,标签在上,错误在下 -- 卡片 1px 边框,圆角 16,无重阴影 -- 卡片最多两层嵌套,优先用分隔线 -- 图标线性 1.5px,20 / 24px -- 焦点环 2px primary,offset 2,必须可见 -- 加载优先骨架屏 -- 空状态:一句话 + 一个主 CTA -- 悬停仅 2–4% 明暗变化,不放大 +## 4. 核心组件与交互规范 +- **Bento Grid 模组规范**:各卡片高度内聚自洽(趋势折线图、访客雷达通知条、个人 Profile、NFC 硬件状态、隐私时效控制),网格弹性响应 +- **移动端沉浸展台模式 (Showcase Mode)**: + - 顶部占屏约 45% 的科技蓝/极光底色,大号高清晰二维码居中直出,配 `Scan to share card` 微光引导 + - 底部大圆角抽屉 (Bottom Sheet) 承载:一键复制直链栏(直链 + `COPY` 胶囊)、绿色可持续发展理念文案、实体白卡拟物微轮播 + - 底部悬浮 TabBar:Contacts、Share(主 CTA 胶囊凸起微阴影)、Privacy +- **访客雷达实时动态通知条**: + - 采用轻微圆角悬浮条(如 "Alex saved your contact card Just Now"),左侧品牌 Logo/头像,白底轻投影 +- **隐私时效控制控件**: + - 结构化时效行(Temporary Visibility / Extended Visibility)搭配平滑展开行与单选/滑块 +- **按钮与触控区**:高度 40 / 44 / 48,触控热区 ≥ 44×44,悬停仅 2–4% 明暗微变化,不放大 +- **图标体系**:统一 1.5px 极细线性图标,规格 20 / 24px -## 动效 -- 微交互 150ms,抽屉 250ms,页面 400ms +## 5. 动效规范 +- 微交互 150ms,抽屉滑出/收起 250ms,页面转场 350ms - 缓动:cubic-bezier(0.2, 0.8, 0.2, 1) -- 支持 prefers-reduced-motion -- AI 用微光,不用打字机作为唯一反馈 -- NFC 用一次脉冲,不循环 +- 极光微光晕呼吸缓慢流动,支持 prefers-reduced-motion +- AI 采用微光晕浮现,不以打字机作为单一阻塞反馈 -## 代码规范 -- 严格 TypeScript,禁止 any -- 组件 PascalCase,函数式 -- 样式只用 Tailwind,禁止内联 style -- 所有文案走 i18n key,禁止硬编码 -- 颜色 / 间距 / 字体必须用 token,禁止魔法值 -- 支持暗色模式,用 CSS 变量 -- 支持 RTL,用 logical properties(ms / me / ps / pe) -- 语义化标签 + aria + 键盘可达 -- 动效必须支持 reduced-motion +## 6. 代码与工程规范 +- 严格 TypeScript / uts,严禁使用 any +- 组件 PascalCase,函数式,统一使用设计 Token,严禁内联魔法值 +- 所有文案必须走 i18n key,禁止硬编码文字 +- 支持暗色模式,采用 CSS/UTS 主题变量 +- 支持 RTL,一律采用逻辑属性(ms / me / ps / pe) +- 语义化标签 + 键盘可达 + 无障碍 aria 规范 -## 国际化 -- next-intl,默认 en -- 独立 URL:/en /es /fr -- hreflang + sitemap -- 日期 / 货币 / 时区本地化 -- 按钮和标签预留 30% 文本扩展 -- 社交适配:WhatsApp、LinkedIn、Telegram、Line、KakaoTalk、微信 -- 支付:Stripe、PayPal -- GDPR、CCPA、Cookie 同意、数据导出 / 删除 -- WCAG 2.2 AA,正文对比度 ≥ 4.5:1 -- 不强制登录看名片,不自动播放声音 +## 7. 国际化与全球合规标准 +- 国际多语言优先(默认 en,支持多语言独立路由或上下文切换) +- 国际主流社交一键触达:WhatsApp、LinkedIn、Telegram、Line、KakaoTalk、WeChat +- 支付与协议:Stripe、PayPal,名片遵循 vCard (RFC 6350) +- 合规:GDPR、CCPA、Cookie 同意、隐私时效自毁 +- WCAG 2.2 AA 标准,文本对比度 ≥ 4.5:1 -## 禁止 -- 纯黑 #000 -- 超过 3 种字体 -- 大面积渐变 / 重阴影 / 霓虹 -- 硬编码文案或颜色 -- 固定宽度按钮 -- 强制登录看名片 -- 自动播放声音 -- 一屏多个主按钮 -- 装饰性插画 / 拟物图标 / 文化特定符号 -- 禁止使用emoji +## 8. 禁止事项 (Strictly Prohibited) +- 严禁使用纯黑 #000 +- 严禁生硬、灰暗、传统冷冰冰的表单式名片页(禁止将二维码埋入杂乱表单角中) +- 严禁大面积土味高饱和霓虹渐变、重阴影与过度玻璃拟态 +- 严禁超过 3 种字体 +- 严禁硬编码文案、固定宽度按钮 +- 严禁强制登录才可浏览名片,严禁自动播放声音 +- 严禁一屏出现多个主按钮 +- 严禁装饰性插画、拟物大图标与特定文化敏感符号 +- 严禁在正式生产界面中使用非业务硬编码 emoji ``` ### 翻译要求 diff --git a/README.md b/README.md index 07cc228..376df42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OmniCard AI (全球智能数字名片 & 移动微官网 SaaS) -> 面向全球商务出海的下一代 **AI 驱动型智能名片 + 移动微官网 + 访客雷达 CRM + NFC 实体卡管理** 一体化平台。 +> 面向全球现代商务、跨国团队与专业顾问的下一代 **AI 驱动型智能名片 + 移动微官网 + 访客雷达 CRM + NFC 实体卡管理** 一体化平台。 --- @@ -15,7 +15,7 @@ - 🎨 **[体验设计大师规范 (UI/UX Design Spec)](./docs/UI_UX_DESIGN_SPEC.md)** - “Touch & Spark” 零阻力触达设计哲学 - Bento Grid (便当盒网格) 响应式美学 - - 全球化出海色彩系统与字体规范(包含阿语 RTL 镜像布局) + - 全球化设计色彩系统与字体规范(包含阿语 RTL 镜像布局) - 关键页面组件字典(访客端主页、AI 顾问浮层、NFC 交互触感、CRM 销售看板) - 性能与首屏秒开体验预算 (FCP < 500ms) @@ -24,8 +24,8 @@ ## 🌟 核心价值亮点 1. **零摩擦触达**:访客无需注册、无需下载 App,NFC 触碰或扫码即可瞬间打开,一键导入手机通讯录 (`.vcf`)。 -2. **多语言与智能出海**:支持英、西、阿 (RTL)、中、德、日等国际主流语种,AI 不仅支持翻译更提供地道商务润色。 -3. **AI 24/7 数字化身**:名片内置基于企业/个人知识库的 AI 问答代表,全天候接待跨时区海外客商并智能留资。 +2. **多语言与智能本地化**:支持英、西、阿 (RTL)、中、德、日等国际主流语种,AI 不仅支持翻译更提供地道商务润色。 +3. **AI 24/7 数字化身**:名片内置基于企业/个人知识库的 AI 问答代表,全天候接待跨时区全球客户并智能留资。 4. **访客雷达与轻量 CRM**:实时掌握客户访问动态(国家定位、停留时长、阅读案例偏好),智能生成意向度评分与跟进话术。 5. **NFC 动态云端硬件管理**:实体卡一次购入,云端自由切换业务场景(商务展会模式 / 社交模式 / 纯通讯录模式),支持一键远程挂失。 diff --git a/admin/src/locale/index.ts b/admin/src/locale/index.ts index 6f20940..929af55 100644 --- a/admin/src/locale/index.ts +++ b/admin/src/locale/index.ts @@ -78,7 +78,7 @@ const i18n = { 'menu.card': '数字名片', 'menu.card.list': '名片资产库', 'menu.card.builder': 'Bento 搭建器', - 'menu.card.cases': '出海案例作品集', + 'menu.card.cases': '案例作品集', 'menu.card.articles': '文章与动态', 'menu.crm': '访客雷达', 'menu.crm.leads': '潜客线索', diff --git a/admin/src/pages/ai/concierge/index.tsx b/admin/src/pages/ai/concierge/index.tsx index 88bfca7..d25d2fb 100644 --- a/admin/src/pages/ai/concierge/index.tsx +++ b/admin/src/pages/ai/concierge/index.tsx @@ -66,9 +66,9 @@ interface PersonaPresetDetail { const PRESET_DATA: Record = { trade_expert: { welcomeMsg: - '您好!我是您的专属出海业务顾问。请问有什么全球贸易或合作需求可以协助您?', + '您好!我是您的专属商务顾问。请问有什么业务咨询或国际合作需求可以协助您?', promptTemplate: - '你是一名深耕国际贸易与跨国商务的出海业务专家。用专业、沉稳的商务语调为访客解答产品出海、跨境结算、供应链合作等咨询。引导有明确意向的客商提供联系方式。', + '你是一名深耕全球商务与跨国合作的业务顾问。用专业、沉稳的商务语调为访客解答业务合作、跨国结算、供应链方案等咨询。引导有明确意向的客商提供联系方式。', toneStyle: 'professional', triggerLeadThreshold: 70, }, diff --git a/admin/src/pages/ai/concierge/locale/index.ts b/admin/src/pages/ai/concierge/locale/index.ts index 915144f..2a883de 100644 --- a/admin/src/pages/ai/concierge/locale/index.ts +++ b/admin/src/pages/ai/concierge/locale/index.ts @@ -77,9 +77,9 @@ const i18n = { 'aiConcierge.form.cardSelect': '关联名片', 'aiConcierge.form.cardSelect.placeholder': '选择或输入名片 ID', 'aiConcierge.form.persona': '预设人设', - 'aiConcierge.persona.trade_expert': '外贸专家', + 'aiConcierge.persona.trade_expert': '国际贸易顾问', 'aiConcierge.persona.trade_expert.desc': - '专精跨境贸易、供应链对接与外贸商务咨询,语调稳重专业。', + '专精全球贸易、供应链对接与国际商务咨询,语调稳重专业。', 'aiConcierge.persona.business_rep': '商务代表', 'aiConcierge.persona.business_rep.desc': '主动介绍企业竞争优势与落地案例,敏锐捕捉客商商机。', @@ -115,7 +115,7 @@ const i18n = { 'aiConcierge.request.saveFail': '保存配置失败', 'aiDeepSeek.card.title': 'DeepSeek 官方大模型底座配置', 'aiDeepSeek.card.desc': - '配置 DeepSeek API Key,支持 deepseek-chat 与 deepseek-reasoner 双模型接入,赋能 24/7 AI 顾问对话与出海多语言润色。', + '配置 DeepSeek API Key,支持 deepseek-chat 与 deepseek-reasoner 双模型接入,赋能 24/7 AI 顾问对话与多语言智能润色。', 'aiDeepSeek.form.apiKey': 'API Key 密钥', 'aiDeepSeek.form.apiKey.placeholder': '输入 sk- 开头的官方密钥 (留空则保留当前已配置密钥)', diff --git a/admin/src/pages/ai/translation/locale/index.ts b/admin/src/pages/ai/translation/locale/index.ts index cdf7de3..db474ec 100644 --- a/admin/src/pages/ai/translation/locale/index.ts +++ b/admin/src/pages/ai/translation/locale/index.ts @@ -53,9 +53,9 @@ const i18n = { }, 'zh-CN': { 'menu.ai.translation': '润色与术语库', - 'aiTranslation.playground.title': '出海文案实时润色试炼器', + 'aiTranslation.playground.title': '商务文案实时润色试炼器', 'aiTranslation.playground.text': '待润色文本', - 'aiTranslation.playground.text.placeholder': '输入待润色的出海商务文案,系统将自动应用语气模板并强制对齐行业术语...', + 'aiTranslation.playground.text.placeholder': '输入待润色的商务文案,系统将自动应用语气模板并强制对齐行业术语...', 'aiTranslation.playground.targetLocale': '目标语言', 'aiTranslation.playground.targetLocale.placeholder': '请选择目标语言', 'aiTranslation.playground.tone': '语气基调', diff --git a/admin/src/pages/card/articles/index.tsx b/admin/src/pages/card/articles/index.tsx index fa2b925..8d672d6 100644 --- a/admin/src/pages/card/articles/index.tsx +++ b/admin/src/pages/card/articles/index.tsx @@ -520,7 +520,7 @@ export default function CardArticlesPage() { field="title" rules={[{ required: true }]} > - + - {/* 模拟出海社交平台卡片 (LinkedIn / WhatsApp / X Card) */} + {/* 模拟国际社交平台卡片 (LinkedIn / WhatsApp / X Card) */}
- {/* COO 模块 B: 全球出海目标市场营收分布 */} + {/* COO 模块 B: 全球目标市场营收分布 */} +): string { + if (!createTime) return t['workplace.radar.justNow'] || 'Just now'; + const diffMs = Date.now() - new Date(createTime).getTime(); + if (Number.isNaN(diffMs) || diffMs < 60000) { + return t['workplace.radar.justNow'] || 'Just now'; + } + const diffMins = Math.floor(diffMs / 60000); + if (diffMins < 60) { + return (t['workplace.radar.minsAgo'] || '{mins}m ago').replace( + '{mins}', + String(diffMins) + ); + } + const diffHours = Math.floor(diffMins / 60); + if (diffHours < 24) { + return (t['workplace.radar.hoursAgo'] || '{hours}h ago').replace( + '{hours}', + String(diffHours) + ); + } + const date = new Date(createTime); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${month}-${day} ${hours}:${minutes}`; +} + +function formatLocation( + item: WorkplaceRadarItem, + t: Record +): string { + if (item.isLocal || item.country === 'Local' || item.city === 'Local') { + return t['workplace.radar.localEnv'] || 'Local / Intranet'; + } + const parts = [item.city, item.country].filter( + (p) => Boolean(p) && p !== 'Unknown' + ); + if (parts.length === 0) { + return item.country || '—'; + } + return parts.join(', '); +} + +function formatStayText( + stayDurationMs: number, + t: Record +): string { + const sec = Math.max(0, Math.round(stayDurationMs / 1000)); + if (sec <= 3) { + return t['workplace.radar.bounce'] || 'Quick bounce'; + } + return (t['workplace.radar.stay'] || '{seconds}s').replace( + '{seconds}', + String(sec) + ); } function computeIntentScore(item: WorkplaceRadarItem): number { + if (typeof item.intentScore === 'number' && item.intentScore > 0) { + return item.intentScore; + } const staySec = Math.round(item.stayDurationMs / 1000); const blocksCount = item.viewedBlocks?.length || 0; - let score = 40; - if (staySec > 30) score += 20; - if (staySec > 90) score += 20; - score += Math.min(blocksCount * 10, 20); + let score = 10; + if (item.touchType === 'nfc') score = 35; + else if (item.touchType === 'qr') score = 25; + if (staySec > 3) score += Math.min(40, Math.round(staySec / 3)); + score += Math.min(blocksCount * 5, 20); return Math.min(score, 99); } +function getIntentTagColor(score: number): string { + if (score >= 75) return 'green'; + if (score >= 45) return 'arcoblue'; + return 'gray'; +} + function escapeCsvCell(value: string): string { if (value.includes(',') || value.includes('"') || value.includes('\n')) { return `"${value.replace(/"/g, '""')}"`; @@ -424,7 +491,7 @@ function Workplace(): React.ReactElement { - {/* 出海转化漏斗 */} + {/* 业务转化漏斗 */}
- {locationLabel || '—'} + + {timeLabel} + + {cardLabel ? ( + + {cardLabel} + + ) : null} + {locationLabel} {getChannelLabel(touchType)} = 70 ? 'green' : 'gray'} + color={getIntentTagColor(intentScore)} size="small" > - {t['workplace.radar.intentScore'].replace('{score}', String(intentScore))} + {t['workplace.radar.intentScore'].replace( + '{score}', + String(intentScore) + )} - {t['workplace.radar.stay'].replace( - '{seconds}', - formatStaySeconds(item.stayDurationMs) - )} + {formatStayText(item.stayDurationMs, t)}
{blocks ? ( @@ -568,7 +651,7 @@ function Workplace(): React.ReactElement {
- {/* 右侧出海运营快捷中枢 (7/24 栅格,完美填补用户红框空白) */} + {/* 右侧全球运营快捷中枢 (7/24 栅格,完美填补用户红框空白) */}
{/* 模块 1:触达渠道健康度 */} @@ -773,7 +856,7 @@ function Workplace(): React.ReactElement {
- {/* 模块 4:出海待办预警 */} + {/* 模块 4:业务待办预警 */} @@ -144,7 +144,7 @@ export default function SubscriptionsPage() { - SaaS 订阅套餐规格与出海权益矩阵 + SaaS 订阅套餐规格与全球商务权益矩阵 平台超管可调控
} diff --git a/admin/src/pages/settings/audit-logs/locale/index.ts b/admin/src/pages/settings/audit-logs/locale/index.ts index a1962b3..49b0ba8 100644 --- a/admin/src/pages/settings/audit-logs/locale/index.ts +++ b/admin/src/pages/settings/audit-logs/locale/index.ts @@ -38,7 +38,7 @@ const i18n = { 'zh-CN': { 'menu.settings.auditLogs': '安全审计', 'auditLogs.title': '系统安全审计日志', - 'auditLogs.subtitle': '追踪管理员与系统关键操作行为链路,满足安全与出海合规要求。', + 'auditLogs.subtitle': '追踪管理员与系统关键操作行为链路,满足安全与全球合规要求。', 'auditLogs.search.keywordPlaceholder': '搜索操作人、行为名称或 IP 地址', 'auditLogs.filter.module': '业务模块', 'auditLogs.filter.module.all': '全部模块', diff --git a/admin/src/types/omni.ts b/admin/src/types/omni.ts index 0c2de87..823fbf8 100644 --- a/admin/src/types/omni.ts +++ b/admin/src/types/omni.ts @@ -1,7 +1,7 @@ /** * OmniCard AI (CRMmp) - 全平台跨端核心数据契约与 TypeScript 类型体系 * - * 遵循出海 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。 + * 遵循现代全球化 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。 * 供 OmniCard 后端与管理端共享镜像复用。 * 严格类型安全,杜绝 any。 */ @@ -117,7 +117,7 @@ export interface BentoBlock { } // ========================================== -// 2. 访客雷达与轻量级出海 CRM 契约 (Radar & Lead) +// 2. 访客雷达与轻量级商务 CRM 契约 (Radar & Lead) // ========================================== /** @@ -221,11 +221,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; } @@ -655,7 +659,7 @@ export interface CardArticle { } /** - * 出海案例作品契约 + * 标杆案例作品契约 */ export interface CardCase { id?: string | number; diff --git a/admin/tests/ai-concierge.test.ts b/admin/tests/ai-concierge.test.ts index 3a633be..d135407 100644 --- a/admin/tests/ai-concierge.test.ts +++ b/admin/tests/ai-concierge.test.ts @@ -49,7 +49,7 @@ test('2. ai/concierge 必须遵循设计 Token,禁止 emoji 与硬编码颜色 assert.ok(!emojiRegex.test(content), '页面中严禁使用 emoji 图标或字符'); // 人设切换支持 - assert.ok(content.includes('trade_expert'), '必须支持外贸专家预设 trade_expert'); + assert.ok(content.includes('trade_expert'), '必须支持国际贸易顾问预设 trade_expert'); assert.ok(content.includes('business_rep'), '必须支持商务代表预设 business_rep'); assert.ok(content.includes('tech_advisor'), '必须支持技术顾问预设 tech_advisor'); diff --git a/admin/tests/card-cases.test.ts b/admin/tests/card-cases.test.ts index 45ca193..4618b46 100644 --- a/admin/tests/card-cases.test.ts +++ b/admin/tests/card-cases.test.ts @@ -34,7 +34,7 @@ test('1. routes.ts 已注册 card/cases,对应 pages/card/cases', () => { test('2. locale 必须包含 menu.card.cases 中英文案', () => { assert.equal(defaultI18n['en-US']['menu.card.cases'], 'Portfolio'); - assert.equal(defaultI18n['zh-CN']['menu.card.cases'], '出海案例作品集'); + assert.equal(defaultI18n['zh-CN']['menu.card.cases'], '案例作品集'); }); test('3. cases 页面必须请求 CRUD 接口并遵守 Cool-Admin 协议', () => { diff --git a/admin/tests/dashboard-workplace.test.ts b/admin/tests/dashboard-workplace.test.ts index 4c7ac70..790dde3 100644 --- a/admin/tests/dashboard-workplace.test.ts +++ b/admin/tests/dashboard-workplace.test.ts @@ -82,7 +82,7 @@ test('5. 工作台 i18n 必须覆盖 KPI、雷达动态与快捷入口,且含 assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji'); }); -test('6. 专业出海运营官视角:包含全球雷达监控、渠道漏斗与效率工具,全屏仅 1 个主 CTA', () => { +test('6. 专业运营官视角:包含全球雷达监控、渠道漏斗与效率工具,全屏仅 1 个主 CTA', () => { const content = fs.readFileSync(pagePath, 'utf-8'); const localeContent = fs.readFileSync(localePath, 'utf-8'); @@ -118,7 +118,7 @@ test('6. 专业出海运营官视角:包含全球雷达监控、渠道漏斗 assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji'); }); -test('7. 出海运营快捷中枢:覆盖8大核心业务快捷入口与运营预警,全屏仅1个主CTA', () => { +test('7. 全球运营快捷中枢:覆盖8大核心业务快捷入口与运营预警,全屏仅1个主CTA', () => { const content = fs.readFileSync(pagePath, 'utf-8'); const localeContent = fs.readFileSync(localePath, 'utf-8'); @@ -152,4 +152,37 @@ test('7. 出海运营快捷中枢:覆盖8大核心业务快捷入口与运营 assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji'); }); +test('8. 实时雷达数据专家重构:相对时间、本地环境优雅降级、名片归属与科学意向分分级', () => { + const content = fs.readFileSync(pagePath, 'utf-8'); + const localeContent = fs.readFileSync(localePath, 'utf-8'); + + // 1. 相对时间与本地化支持 + const radarKeys = [ + 'workplace.radar.localEnv', + 'workplace.radar.justNow', + 'workplace.radar.minsAgo', + 'workplace.radar.hoursAgo', + 'workplace.radar.bounce', + 'workplace.radar.cardFallback', + ]; + for (const key of radarKeys) { + assert.ok( + localeContent.includes(key), + `语言包必须包含雷达数据语义化词条: ${key}` + ); + } + + // 2. 页面中实现相对时间格式化与本地环境优雅降级 + assert.ok(content.includes('formatRelativeTime'), '必须包含相对时间计算逻辑'); + assert.ok(content.includes('formatLocation'), '必须包含本地环境优雅降级逻辑'); + assert.ok(content.includes('formatStayText'), '必须包含跳出/停留文案格式化'); + assert.ok(content.includes('getIntentTagColor'), '必须包含科学意向分语义色彩分级'); + + // 3. 规范约束 + assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji'); + assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji'); + assert.ok(!/\bany\b/.test(content), '严禁使用 any'); +}); + + diff --git a/admin/tests/routes-menu.test.ts b/admin/tests/routes-menu.test.ts index ab140bf..f334ee4 100644 --- a/admin/tests/routes-menu.test.ts +++ b/admin/tests/routes-menu.test.ts @@ -27,7 +27,7 @@ const REMOVED_DEMO_MODULES = [ 'user', ]; -test('1. 核心出海业务菜单必须保留在侧边栏正常展示', () => { +test('1. 核心业务菜单必须保留在侧边栏正常展示', () => { const content = fs.readFileSync(routesPath, 'utf-8'); for (const key of CORE_BIZ_MODULES) { assert.ok( diff --git a/admin/tests/tokens-and-locale.test.ts b/admin/tests/tokens-and-locale.test.ts index 59aa00b..b50a850 100644 --- a/admin/tests/tokens-and-locale.test.ts +++ b/admin/tests/tokens-and-locale.test.ts @@ -126,14 +126,12 @@ test('5. mpuniapp/common/tokens.uts - uni-app x 跨端 Token 校验', () => { assert.ok(content.includes('export const utsRadius: UtsRadius =')); assert.ok(content.includes('export const utsTypography: UtsTypography =')); - // 校验关键值 - assert.ok(content.includes("'#0B5CFF'")); - assert.ok(content.includes("'#4D8DFF'")); - assert.ok(content.includes("'#7C5CFF'")); - assert.ok(content.includes("'#9B8CFF'")); - assert.ok(content.includes("button: '12px'")); - assert.ok(content.includes("card: '16px'")); - assert.ok(content.includes("modal: '20px'")); + // 校验关键值 (严格遵守 AGENTS.md 权威 Design Token) + assert.ok(content.includes("'#0088FF'")); + assert.ok(content.includes("'#3894FF'")); + assert.ok(content.includes("button: '14px'")); + assert.ok(content.includes("card: '20px'")); + assert.ok(content.includes("modal: '24px'")); assert.ok(content.includes("space16: '16px'")); }); diff --git a/admin/tests/workplace-layout-alignment.test.ts b/admin/tests/workplace-layout-alignment.test.ts index d092b8c..61b7036 100644 --- a/admin/tests/workplace-layout-alignment.test.ts +++ b/admin/tests/workplace-layout-alignment.test.ts @@ -69,7 +69,7 @@ test('3. 快捷导航卡片副标题必须具备单行文本防截断样式', () ); }); -test('4. 零 any,零 emoji,符合出海国际化商务规范', () => { +test('4. 零 any,零 emoji,符合国际化现代商务规范', () => { const tsx = read('src/pages/dashboard/workplace/index.tsx'); const styles = read('src/pages/dashboard/workplace/style/index.module.less'); diff --git a/backend/database/apply-seed.js b/backend/database/apply-seed.js new file mode 100644 index 0000000..47bc755 --- /dev/null +++ b/backend/database/apply-seed.js @@ -0,0 +1,40 @@ +const fs = require('fs'); +const path = require('path'); +const mysql = require('mysql2/promise'); + +async function run() { + const sqlPath = path.join(__dirname, 'seed_cards.sql'); + const sqlContent = fs.readFileSync(sqlPath, 'utf8'); + + const connection = await mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '123456', + database: 'crmmpdata', + multipleStatements: true, + charset: 'utf8mb4', + }); + + try { + console.log('[Seed] 正在导入多媒体名片数据 (UTF-8)...'); + await connection.query(sqlContent); + console.log('[Seed] 5 张高保真多媒体名片数据导入成功!'); + + // 打印验证统计 + const [cards] = await connection.query('SELECT id, slug, name, company FROM card_info WHERE id >= 1001'); + console.log(`[Seed] 成功验证 ${cards.length} 张名片:`); + cards.forEach(c => console.log(` - [${c.id}] ${c.name} (${c.company}) -> /${c.slug}`)); + + const [videos] = await connection.query("SELECT id, card_id, title FROM card_bento WHERE type = 'video'"); + console.log(`[Seed] 成功验证 ${videos.length} 个视频 Bento 块:`); + videos.forEach(v => console.log(` - [Bento #${v.id}] (名片 #${v.card_id}): ${v.title}`)); + } catch (err) { + console.error('[Seed Error]', err); + process.exit(1); + } finally { + await connection.end(); + } +} + +run(); diff --git a/backend/database/seed_cards.sql b/backend/database/seed_cards.sql new file mode 100644 index 0000000..7fb91d7 --- /dev/null +++ b/backend/database/seed_cards.sql @@ -0,0 +1,287 @@ +-- ==================================================================== +-- OmniCard AI (CRMmp) - 全球多媒体智能名片种子数据 (配图与配视频) +-- 包含 5 组覆盖全球主流行业的真实高保真名片、Bento Grid 图文与视频组件、案例及文章 +-- 适用数据库: crmmpdata +-- ==================================================================== + +-- 1. 清理已有 Mock 名片数据(避免重复插入报错,支持幂等执行) +DELETE FROM `card_article` WHERE `card_id` IN (1001, 1002, 1003, 1004, 1005); +DELETE FROM `card_cases` WHERE `card_id` IN (1001, 1002, 1003, 1004, 1005); +DELETE FROM `card_bento` WHERE `card_id` IN (1001, 1002, 1003, 1004, 1005); +DELETE FROM `card_info` WHERE `id` IN (1001, 1002, 1003, 1004, 1005); + +-- -------------------------------------------------------------------- +-- 2. 插入名片主表数据 (card_info) +-- -------------------------------------------------------------------- +INSERT INTO `card_info` ( + `id`, `tenant_id`, `slug`, `name`, `title`, `company`, `slogan`, + `avatar_url`, `phone`, `email`, `whatsapp`, `linkedin`, `twitter`, `wechat`, + `timezone`, `audio_pronunciation_url`, `status`, `theme_code`, `user_id`, `remark`, + `create_time`, `update_time` +) VALUES +-- 名片 1: 消费电子与全球智能硬件 (Alex Zheng) +( + 1001, 1, 'alex-zheng', 'Alex Zheng', 'VP of Global Expansion', 'OmniCard Global Inc.', + 'Empowering Global Hardware Brands with Next-Gen Intelligence', + 'https://images.unsplash.com/photo-1534528741775-53994a69daeb.jpg', + '+8613800138001', 'alex.zheng@omnicard.ai', '+8613800138001', + 'https://linkedin.com/in/alex-zheng-omni', 'https://x.com/alexzheng_hw', NULL, + 'Asia/Singapore', 'https://cdn.omnicard.ai/audio/alex-zheng.mp3', + 'published', 'corporate_navy', 101, '全球智能硬件领军高管,CES 2026 演讲嘉宾', + NOW(), NOW() +), +-- 名片 2: 全球云原生 & AI 基础设施 (Elena Rostova) +( + 1002, 1, 'elena-rostova', 'Elena Rostova', 'Chief Cloud Architect', 'NebulaScale Cloud AI', + 'Building Resilient Multi-Cloud AI Infrastructures Globally', + 'https://images.unsplash.com/photo-1580489944761-15a19d654956.jpg', + '+14155552671', 'elena.rostova@nebulascale.io', '+14155552671', + 'https://linkedin.com/in/elena-rostova-cloud', 'https://x.com/elena_infra', NULL, + 'America/Los_Angeles', 'https://cdn.omnicard.ai/audio/elena-rostova.mp3', + 'published', 'tech_dark', 102, '硅谷云原生基础设施领军专家', + NOW(), NOW() +), +-- 名片 3: 跨国智慧能源与技术合作 (Tariq Al-Mansoor) +( + 1003, 1, 'tariq-al-mansoor', 'Tariq Al-Mansoor', 'MENA Business Director', 'SilkRoad Oasis Ventures', + 'Bridging APAC Technologies with Middle East Energy Infrastructure', + 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d.jpg', + '+971501234567', 'tariq@silkroadoasis.ae', '+971501234567', + 'https://linkedin.com/in/tariq-almansoor-mena', NULL, NULL, + 'Asia/Dubai', 'https://cdn.omnicard.ai/audio/tariq.mp3', + 'published', 'gold_luxe', 103, '海湾地区重大清洁能源国际合作项目投资总监', + NOW(), NOW() +), +-- 名片 4: 全球 DTC 时尚与可持续设计 (Chloe Dubois) +( + 1004, 1, 'chloe-dubois', 'Chloe Dubois', 'Global Brand Officer', 'Atelier Lumina Paris', + 'Sustainable Luxury & Digital Craftsmanship for Conscious Consumers', + 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2.jpg', + '+33612345678', 'chloe.dubois@atelierlumina.fr', '+33612345678', + 'https://linkedin.com/in/chloe-dubois-luxury', NULL, NULL, + 'Europe/Paris', 'https://cdn.omnicard.ai/audio/chloe.mp3', + 'published', 'aurora_glow', 104, '巴黎可持续时尚与国际设计师品牌主理人', + NOW(), NOW() +), +-- 名片 5: 跨国供应链与自动化物流 (Kenji Tanaka) +( + 1005, 1, 'kenji-tanaka', 'Kenji Tanaka', 'APAC Logistics VP', 'TransPacific AutoLink', + 'Optimizing Next-Gen Cross-Border Automotive Supply Chains', + 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e.jpg', + '+81312345678', 'tanaka.kenji@transpacific.jp', '+81312345678', + 'https://linkedin.com/in/kenji-tanaka-logistics', NULL, 'kenji_autolink', + 'Asia/Tokyo', 'https://cdn.omnicard.ai/audio/tanaka.mp3', + 'published', 'emerald_fresh', 105, '日韩与东南亚跨国汽车主机厂一级供应链负责人', + NOW(), NOW() +); + +-- -------------------------------------------------------------------- +-- 3. 插入 Bento Grid 网格块组件 (card_bento) 含配图与视频 +-- -------------------------------------------------------------------- +INSERT INTO `card_bento` ( + `id`, `card_id`, `type`, `col_span`, `row_span`, `sort_order`, + `title`, `content`, `media_url`, `link_url`, `meta_json`, `status`, + `create_time`, `update_time` +) VALUES +-- 卡片 1 (Alex Zheng) Bento 块 +( + 2001, 1001, 'hero', 2, 1, 1, + '关于我与 OmniCard', '专注全球物联网与智能消费电子 12 年,连接全球 60+ 国家和地区的创新生态。', + 'https://images.unsplash.com/photo-1497366216548-37526070297c.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2002, 1001, 'video', 2, 2, 2, + 'OmniCard Next 旗舰实体卡实机开箱演示', '超精密阳极氧化铝雕刻,内置双频高灵敏 NFC 芯片与动态感应天线,轻触即达微官网。', + 'https://images.unsplash.com/photo-1519389950473-47ba0277781c.jpg', + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4', + '{"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 硬件工艺与触卡交互"}', + 1, NOW(), NOW() +), +( + 2003, 1001, 'case', 1, 1, 3, + 'GITEX 迪拜展数字化留资', '实现 10,000+ 国际买家 0.5 秒即时触卡交互,获客转化提升 320%。', + 'https://images.unsplash.com/photo-1511578314322-379afb476865.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2004, 1001, 'article', 1, 1, 4, + '2026 全球智能硬件合规与准入白皮书', '深度解读 CE/FCC/KC 认证新规与国际电池环保法规应对策略。', + 'https://images.unsplash.com/photo-1451187580459-43490279c0fa.jpg', NULL, NULL, 1, NOW(), NOW() +), + +-- 卡片 2 (Elena Rostova) Bento 块 +( + 2005, 1002, 'hero', 2, 1, 1, + '全球云架构与大模型算力调度', '主导超过 5,000+ 节点高可用 Kubernetes 集群与跨大洲大模型推理加速网络设计。', + 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2006, 1002, 'video', 2, 2, 2, + 'NebulaScale 分布式大模型推理架构演示', '实时观测跨欧美亚太算力流转与动态故障转移,端到端延迟压至 35ms 以内。', + 'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5.jpg', + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', + '{"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":"跨国高可用集群实时压测演练"}', + 1, NOW(), NOW() +), +( + 2007, 1002, 'case', 1, 1, 3, + 'FinTech 全球秒级支付架构重构', '99.999% 可用性保障,支撑每日 3 亿美元全球交易量。', + 'https://images.unsplash.com/photo-1563986768609-322da13575f3.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2008, 1002, 'article', 1, 1, 4, + '现代微服务下的多云零信任安全实践', '解构全球分布式企业如何通过 eBPF 与 SPIFFE 构建坚固的应用安全边界。', + 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d.jpg', NULL, NULL, 1, NOW(), NOW() +), + +-- 卡片 3 (Tariq Al-Mansoor) Bento 块 +( + 2009, 1003, 'hero', 2, 1, 1, + '海湾合作委员会 (GCC) 绿色能源生态', '常驻阿布扎比与利雅得,深耕沙特“2030愿景”与国际绿色能源技术对接。', + 'https://images.unsplash.com/photo-1509391365360-2e959784a276.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2010, 1003, 'video', 2, 2, 2, + '阿布扎比 2GW 光伏产业示范园落成实景', '沙漠极端高温环境下的自动化清扫与高效储能实景纪录片,见证绿色能源奇迹。', + 'https://images.unsplash.com/photo-1497435334941-8c899ee9e8e9.jpg', + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4', + '{"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":"中东大型绿电产业园实况航拍"}', + 1, NOW(), NOW() +), +( + 2011, 1003, 'case', 1, 1, 3, + '沙特 NEOM 新城微电网建设', '联合国际领先储能集团完成全离网微电网调试,供电可靠率达 99.98%。', + 'https://images.unsplash.com/photo-1466611653911-95081537e5b7.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2012, 1003, 'article', 1, 1, 4, + '海湾国家商业投资环境与法律合规指引', '从外资持股比例到本地化雇佣比率,全方位规避国际投资法律风险。', + 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c.jpg', NULL, NULL, 1, NOW(), NOW() +), + +-- 卡片 4 (Chloe Dubois) Bento 块 +( + 2013, 1004, 'hero', 2, 1, 1, + '可持续奢品与数字美学设计', '联合欧洲独立设计师与亚洲循环新材料实验室,打造触动全球年轻一代的现代胶囊衣橱。', + 'https://images.unsplash.com/photo-1490481651871-ab68de25d43d.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2014, 1004, 'video', 2, 2, 2, + '巴黎绿色时尚周品牌秀场精选', '从可降解生物丝绸到 0 碳足迹工坊生产实拍,展现先锋环保奢品背后的匠心。', + 'https://images.unsplash.com/photo-1469334031218-e382a71b716b.jpg', + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4', + '{"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 秀场全纪录"}', + 1, NOW(), NOW() +), +( + 2015, 1004, 'case', 1, 1, 3, + '欧美 DTC 独立站零碳系列发售', '上线 48 小时首发即罄,Instagram 话题曝光突破 3,500 万次。', + 'https://images.unsplash.com/photo-1441986300917-64674bd600d8.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2016, 1004, 'article', 1, 1, 4, + '时尚产业的数字护照 (DPP) 全球实践', '如何利用区块链与 NFC 芯片追踪服装从棉花田到衣橱的全生命周期。', + 'https://images.unsplash.com/photo-1558769132-cb1aea458c5e.jpg', NULL, NULL, 1, NOW(), NOW() +), + +-- 卡片 5 (Kenji Tanaka) Bento 块 +( + 2017, 1005, 'hero', 2, 1, 1, + '跨国汽车产业链智能物流中枢', '管理东京、上海、曼谷三大核心转运港口与 12 座保税自动化立体中心仓。', + 'https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2018, 1005, 'video', 2, 2, 2, + '跨国自动化立体仓储 AGV 机器人穿梭实况', '全天候智能算法调度,实现零差错零部件入库与跨国干线精准集拼装船。', + 'https://images.unsplash.com/photo-1616401784845-180882ba9ba8.jpg', + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4', + '{"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":"跨国现代化智能立体仓全景实拍"}', + 1, NOW(), NOW() +), +( + 2019, 1005, 'case', 1, 1, 3, + '中日新能源车配件 48 小时达', '通过全链条条码及 RFID 联动,清关及通关提速 60%。', + 'https://images.unsplash.com/photo-1578575437130-527eed3abbec.jpg', NULL, NULL, 1, NOW(), NOW() +), +( + 2020, 1005, 'article', 1, 1, 4, + '区域全面经济伙伴关系协定 (RCEP) 关务实战指南', '深度解读原产地累积规则与跨国物流链条中的税费优化策略。', + 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0.jpg', NULL, NULL, 1, NOW(), NOW() +); + +-- -------------------------------------------------------------------- +-- 4. 插入标杆案例表数据 (card_cases) +-- -------------------------------------------------------------------- +INSERT INTO `card_cases` ( + `id`, `card_id`, `title`, `client`, `duration`, `metrics`, + `cover_image`, `pdf_url`, `description`, `sort_order`, + `create_time`, `update_time` +) VALUES +( + 3001, 1001, 'GITEX 迪拜展会智能名片规模化交付', '阿联酋国际商业联合会', '2025.10 - 2025.11', + '触卡 12,500+ 次,沉淀高意向商业线索 890 条', + 'https://images.unsplash.com/photo-1511578314322-379afb476865.jpg', + 'https://cdn.omnicard.ai/cases/gitex-2025-report.pdf', + '为 48 家国际参展代表团统一配置智能名片与雷达跟进中枢,完成全程数字化展会获客。', 1, NOW(), NOW() +), +( + 3002, 1002, '跨国全球支付金融级容灾架构改造', 'PayNova International', '2025.03 - 2025.08', + '交易超时率降低 98.4%,算力成本节省 35%', + 'https://images.unsplash.com/photo-1563986768609-322da13575f3.jpg', NULL, + '基于异地多活方案实现 RPO=0, RTO<1s 的高可用架构,顺利通过全球多国金融安全审计。', 1, NOW(), NOW() +), +( + 3003, 1003, '阿布扎比综合智慧能源示范中心一期', 'Abu Dhabi Future Energy PJSC', '2024.08 - 2025.04', + '年发电量 32 亿千瓦时,减排二氧化碳 240 万吨', + 'https://images.unsplash.com/photo-1466611653911-95081537e5b7.jpg', NULL, + '整合国际先锋光伏组件与智能追踪支架,打造跨国绿色能源合作的典范工程。', 1, NOW(), NOW() +), +( + 3004, 1004, 'Atelier Lumina 欧美全渠道首发战役', 'Lumina Paris', '2025.05 - 2025.09', + '欧美 GMV 增长 210%,复购率达到 38.6%', + 'https://images.unsplash.com/photo-1441986300917-64674bd600d8.jpg', NULL, + '通过数字体验名片与 VIP 专属私人定制预约通道,构建高端时尚圈层闭环。', 1, NOW(), NOW() +), +( + 3005, 1005, '亚太区域核心零部件保税中转枢纽项目', 'Global AutoTech Consortium', '2024.11 - 2025.07', + '仓储周转效率提高 42%,破损率控制在 0.001% 以下', + 'https://images.unsplash.com/photo-1578575437130-527eed3abbec.jpg', NULL, + '成功构建日韩到东盟的跨国零备件绿色走廊,达成准时化(JIT)精益交付。', 1, NOW(), NOW() +); + +-- -------------------------------------------------------------------- +-- 5. 插入深度文章与洞察表数据 (card_article) +-- -------------------------------------------------------------------- +INSERT INTO `card_article` ( + `id`, `card_id`, `title`, `summary`, `content`, `cover_image`, + `is_published`, `create_time`, `update_time` +) VALUES +( + 4001, 1001, '智能硬件全球化新范式:从单品突围到全域生态触达', + '探讨如何结合 NFC 与端侧 AI 技术在国际展会和商务洽谈中建立第一分钟信任。', + '在全球消费电子产业加速演进的今天,传统的纸质物料正在迅速被具备实时交互能力的数字名片和微官网替代。在 CES、IFA、GITEX 等大型展会现场,商务人员递出的不再是一张单薄的卡纸,而是一个聚合了产品全景演示、合规资质、多语言视频与 AI 顾问的微型展厅...', + 'https://images.unsplash.com/photo-1451187580459-43490279c0fa.jpg', 1, NOW(), NOW() +), +( + 4002, 1002, '在大模型时代重新思考全球分布式架构演进', + '探索算力调度从传统容器编排向 GPU 异构资源池化迁移的核心挑战。', + '伴随企业全球化战略加速,算力孤岛与网络延迟成为制约 AI 商业落地的最大瓶颈。本文结合我们在跨大洲大模型推理加速网络设计中的实践,系统阐述如何利用 eBPF 网络加速、智能边缘缓存与拓扑感知调度,将端到端跨洲请求延迟压缩至极限...', + 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d.jpg', 1, NOW(), NOW() +), +( + 4003, 1003, '走进中东:沙特与阿联酋新一轮科技能源投资机遇', + '解析全球高科技与能源装备企业在海湾市场的本土化突围之道。', + '随着海湾国家加速推进“2030愿景”,以绿色能源、海水淡化与智慧城市为核心的万亿级基础设施建设全面铺开。国际企业深耕中东正经历从单纯“设备供应”向“资本+技术+本土化运营”深度模式的迭代升级...', + 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c.jpg', 1, NOW(), NOW() +), +( + 4004, 1004, '为什么新一代全球 DTC 品牌需要重塑品牌心智', + '摆脱流量依赖与单一价格战,以故事叙事和品牌价值赢得国际高净值客群。', + '全球品牌的竞争已经从单纯的供应链红利转向了文化叙事与品牌价值主张的博弈。在国际主流市场,年轻消费者越来越看重品牌的环保倡议、透明供应链与数字化体验。通过 NFC 数字衣标与沉浸式便当盒微官网,品牌得以与消费者建立长期信任关系...', + 'https://images.unsplash.com/photo-1558769132-cb1aea458c5e.jpg', 1, NOW(), NOW() +), +( + 4005, 1005, '全球地缘与贸易新常态下的弹性供应链构建', + '面对跨国运价波动与港口堵塞风险,如何通过数字孪生提升全球业务韧性。', + '跨国供应链的制胜关键已经从单一的价格竞争转向全链路的可视化与动态避险能力。通过引入 AGV 自动化立体仓储系统、跨国关务自动化预申报与 RFID 实时追踪技术,亚太区域的零部件周转效率获得了质的飞跃...', + 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0.jpg', 1, NOW(), NOW() +); diff --git a/backend/package.json b/backend/package.json index 2836f42..a0080ce 100644 --- a/backend/package.json +++ b/backend/package.json @@ -61,6 +61,7 @@ "lint": "mwts check", "lint:fix": "mwts fix", "ci": "npm run cov", + "seed:cards": "node database/apply-seed.js", "build": "cool entity && bundle && mwtsc --cleanOutDir", "build:obfuscate": "cool entity && bundle && mwtsc --cleanOutDir && cool obfuscate", "pkg": "rimraf build && mkdir build && npm run build && pkg . -d > build/pkg.log", diff --git a/backend/src/comm/i18n/geo.ts b/backend/src/comm/i18n/geo.ts index 9a78053..f69e763 100644 --- a/backend/src/comm/i18n/geo.ts +++ b/backend/src/comm/i18n/geo.ts @@ -175,7 +175,7 @@ const COUNTRY_UTC_OFFSETS: Record = { }; /** - * 出海客户最佳商务联系时间窗口计算 + * 全球客户最佳商务联系时间窗口计算 */ export function resolveBestContactWindow( countryCodeOrName?: string, diff --git a/backend/src/comm/types/omni.ts b/backend/src/comm/types/omni.ts index b56c001..41a55b1 100644 --- a/backend/src/comm/types/omni.ts +++ b/backend/src/comm/types/omni.ts @@ -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; diff --git a/backend/src/modules/ai/config.ts b/backend/src/modules/ai/config.ts index d295a7c..a048b6e 100644 --- a/backend/src/modules/ai/config.ts +++ b/backend/src/modules/ai/config.ts @@ -1,12 +1,12 @@ import { ModuleConfig } from '@cool-midway/core'; /** - * AI 顾问与出海润色模块配置 + * AI 顾问与多语言本地化润色模块配置 */ export default () => { return { name: 'AI 智能中枢模块', - description: '名片内置顾问对话、意向识别与出海文案润色', + description: '名片内置顾问对话、意向识别与多语言文案润色', middlewares: [], globalMiddlewares: [], order: 40, diff --git a/backend/src/modules/ai/controller/admin/localize.ts b/backend/src/modules/ai/controller/admin/localize.ts index 6de33bd..77f36f3 100644 --- a/backend/src/modules/ai/controller/admin/localize.ts +++ b/backend/src/modules/ai/controller/admin/localize.ts @@ -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) diff --git a/backend/src/modules/ai/service/concierge.ts b/backend/src/modules/ai/service/concierge.ts index 9b533b5..4cc031c 100644 --- a/backend/src/modules/ai/service/concierge.ts +++ b/backend/src/modules/ai/service/concierge.ts @@ -19,9 +19,9 @@ const PERSONA_PRESETS: Record< trade_expert: { personaType: 'trade_expert', welcomeMsg: - '您好!我是您的专属出海业务顾问。请问有什么全球贸易或合作需求可以协助您?', + '您好!我是您的专属商务顾问。请问有什么业务咨询或国际合作需求可以协助您?', promptTemplate: - '你是一名深耕国际贸易与跨国商务的出海业务专家。用专业、沉稳的商务语调为访客解答产品出海、跨境结算、供应链合作等咨询。引导有明确意向的客商提供联系方式。', + '你是一名深耕全球商务与跨国合作的业务顾问。用专业、沉稳的商务语调为访客解答业务合作、跨国结算、供应链方案等咨询。引导有明确意向的客商提供联系方式。', toneStyle: 'professional', triggerLeadThreshold: 70, isEnabled: true, diff --git a/backend/src/modules/ai/service/deepseek.ts b/backend/src/modules/ai/service/deepseek.ts index 76e646a..b492371 100644 --- a/backend/src/modules/ai/service/deepseek.ts +++ b/backend/src/modules/ai/service/deepseek.ts @@ -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 || '商务洽谈'}`, ]; diff --git a/backend/src/modules/ai/service/localize.ts b/backend/src/modules/ai/service/localize.ts index 6180241..e4e95a5 100644 --- a/backend/src/modules/ai/service/localize.ts +++ b/backend/src/modules/ai/service/localize.ts @@ -17,7 +17,7 @@ const TONES: ReadonlySet = new Set([ ]); /** - * 出海文案润色(本地规则骨架,结合行业术语库强行替换) + * 商务文案本地化润色(本地规则骨架,结合行业术语库强行替换) */ @Provide() export class AiLocalizeService extends BaseService { diff --git a/backend/src/modules/ai/service/outreach.ts b/backend/src/modules/ai/service/outreach.ts index eab7abf..ba291fe 100644 --- a/backend/src/modules/ai/service/outreach.ts +++ b/backend/src/modules/ai/service/outreach.ts @@ -22,7 +22,7 @@ export interface OutreachDraftResponse { } /** - * 组装出海销售跟进提示词 + * 组装商务拓展跟进提示词 */ export function formatOutreachPrompt(req: OutreachDraftRequest): string { const langCode = (req.targetLanguage || 'en').toLowerCase(); diff --git a/backend/src/modules/card/controller/admin/cases.ts b/backend/src/modules/card/controller/admin/cases.ts index 79da80b..eb98f30 100644 --- a/backend/src/modules/card/controller/admin/cases.ts +++ b/backend/src/modules/card/controller/admin/cases.ts @@ -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) diff --git a/backend/src/modules/card/entity/cases.ts b/backend/src/modules/card/entity/cases.ts index fa75b69..aa85425 100644 --- a/backend/src/modules/card/entity/cases.ts +++ b/backend/src/modules/card/entity/cases.ts @@ -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, }) diff --git a/backend/src/modules/card/mock/seed-cards.ts b/backend/src/modules/card/mock/seed-cards.ts new file mode 100644 index 0000000..003dc73 --- /dev/null +++ b/backend/src/modules/card/mock/seed-cards.ts @@ -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; + bentoBlocks: Partial[]; + cases: Partial[]; + articles: Partial[]; +} + +/** + * 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 { + 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; +} diff --git a/backend/src/modules/card/service/batch-locale.ts b/backend/src/modules/card/service/batch-locale.ts index 8698bf2..fce4daa 100644 --- a/backend/src/modules/card/service/batch-locale.ts +++ b/backend/src/modules/card/service/batch-locale.ts @@ -83,7 +83,7 @@ export function validateLocaleFields(fields: CardLocaleFields): { } /** - * 出海团队多语言名片批量生成向导与多语种字段镜像校验服务 + * 国际团队多语言名片批量生成向导与多语种字段镜像校验服务 */ @Provide() export class CardBatchLocaleService extends BaseService { diff --git a/backend/src/modules/card/service/cases.ts b/backend/src/modules/card/service/cases.ts index d8d4ad1..042e6d5 100644 --- a/backend/src/modules/card/service/cases.ts +++ b/backend/src/modules/card/service/cases.ts @@ -12,8 +12,8 @@ export interface GetCasesByCardIdParams { } /** - * 出海案例作品集服务 - * 提供出海案例的标准 CRUD 及按名片筛选能力 + * 标杆案例作品集服务 + * 提供标杆案例的标准 CRUD 及按名片筛选能力 */ @Provide() export class CardCasesService extends BaseService { diff --git a/backend/src/modules/crm/controller/admin/compliance.ts b/backend/src/modules/crm/controller/admin/compliance.ts index c7a2c22..b99cad6 100644 --- a/backend/src/modules/crm/controller/admin/compliance.ts +++ b/backend/src/modules/crm/controller/admin/compliance.ts @@ -3,7 +3,7 @@ import { BaseController, CoolController, CoolCommException } from '@cool-midway/ import { ComplianceGdprService } from '../../service/compliance'; /** - * 管理后台 - 海外数据隐私与合规治理 (GDPR / CCPA) + * 管理后台 - 国际数据隐私与合规治理 (GDPR / CCPA) * 路由前缀: /admin/crm/compliance */ @Provide() diff --git a/backend/src/modules/crm/service/attribution.ts b/backend/src/modules/crm/service/attribution.ts index e350197..7746983 100644 --- a/backend/src/modules/crm/service/attribution.ts +++ b/backend/src/modules/crm/service/attribution.ts @@ -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 { diff --git a/backend/src/modules/crm/service/compliance.ts b/backend/src/modules/crm/service/compliance.ts index 774e87c..ae3f097 100644 --- a/backend/src/modules/crm/service/compliance.ts +++ b/backend/src/modules/crm/service/compliance.ts @@ -68,7 +68,7 @@ export function anonymizePiiData(rawRecord: { } /** - * 海外合规 GDPR / CCPA 隐私治理服务 + * 国际合规 GDPR / CCPA 隐私治理服务 */ @Provide() export class ComplianceGdprService extends BaseService { diff --git a/backend/src/modules/crm/service/enrichment.ts b/backend/src/modules/crm/service/enrichment.ts index d53d598..ce4c4a0 100644 --- a/backend/src/modules/crm/service/enrichment.ts +++ b/backend/src/modules/crm/service/enrichment.ts @@ -116,7 +116,7 @@ export function enrichCompanyProfile( } /** - * 出海潜客企业邮箱域名解析与商业主页自动富化服务 + * 潜客企业邮箱域名解析与商业主页自动富化服务 */ @Provide() export class LeadCompanyEnrichmentService extends BaseService { diff --git a/backend/src/modules/crm/service/geo-heatmap.ts b/backend/src/modules/crm/service/geo-heatmap.ts index 9bef737..f9bdd24 100644 --- a/backend/src/modules/crm/service/geo-heatmap.ts +++ b/backend/src/modules/crm/service/geo-heatmap.ts @@ -23,7 +23,7 @@ export interface GeoHeatmapReport { } /** - * 全球常见出海枢纽城市经纬度字典 [经度 lng, 纬度 lat] + * 全球常见商务枢纽城市经纬度字典 [经度 lng, 纬度 lat] */ const CITY_COORDINATES: Record = { 'AE:Dubai': [55.27, 25.20], @@ -53,7 +53,7 @@ const COUNTRY_FALLBACK_COORDINATES: Record = { }; /** - * 解析出海城市经纬度坐标 + * 解析国际城市经纬度坐标 */ export function resolveCityCoordinates( country: string, diff --git a/backend/src/modules/crm/service/lead-automation.ts b/backend/src/modules/crm/service/lead-automation.ts index 071b602..fbfc04b 100644 --- a/backend/src/modules/crm/service/lead-automation.ts +++ b/backend/src/modules/crm/service/lead-automation.ts @@ -95,7 +95,7 @@ export function evaluateLeadStagePromotion( } /** - * 出海潜客线索自动化阶段流转服务 + * 潜客线索自动化阶段流转服务 */ @Provide() export class LeadStageAutomationService extends BaseService { diff --git a/backend/src/modules/crm/service/lead-score.ts b/backend/src/modules/crm/service/lead-score.ts index 4816a69..0219713 100644 --- a/backend/src/modules/crm/service/lead-score.ts +++ b/backend/src/modules/crm/service/lead-score.ts @@ -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('初次访问数字名片微官网'); diff --git a/backend/src/modules/crm/service/radar.ts b/backend/src/modules/crm/service/radar.ts index f7a6025..c02faf2 100644 --- a/backend/src/modules/crm/service/radar.ts +++ b/backend/src/modules/crm/service/radar.ts @@ -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(); + 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(); + 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 { 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[] + ); } } diff --git a/backend/src/modules/crm/service/webhook-dispatcher.ts b/backend/src/modules/crm/service/webhook-dispatcher.ts index 59ba9b2..d603994 100644 --- a/backend/src/modules/crm/service/webhook-dispatcher.ts +++ b/backend/src/modules/crm/service/webhook-dispatcher.ts @@ -83,7 +83,7 @@ export type HttpPosterFn = ( ) => Promise<{ statusCode: number; data?: any }>; /** - * 出海商机实时 Webhook 触发器与签名分发服务 + * 全球商机实时 Webhook 触发器与签名分发服务 */ @Provide() export class LeadWebhookDispatcher extends BaseService { diff --git a/backend/src/modules/nfc/service/manufacturing.ts b/backend/src/modules/nfc/service/manufacturing.ts index 025b3c1..28bce02 100644 --- a/backend/src/modules/nfc/service/manufacturing.ts +++ b/backend/src/modules/nfc/service/manufacturing.ts @@ -98,7 +98,7 @@ export function calculateBatchYieldRate( } /** - * 跨境实体卡供应链生产批次工单管理服务 + * 国际实体卡供应链生产批次工单管理服务 */ @Provide() export class NfcManufacturingService extends BaseService { diff --git a/backend/src/modules/team/service/currency-tax.ts b/backend/src/modules/team/service/currency-tax.ts index 8e30c65..087d735 100644 --- a/backend/src/modules/team/service/currency-tax.ts +++ b/backend/src/modules/team/service/currency-tax.ts @@ -3,7 +3,7 @@ import { BaseService } from '@cool-midway/core'; import { SupportedCurrency, TaxCalculationResult } from '../../../comm/types/omni'; /** - * 相对 USD 的基准出海商业结算汇率表 + * 相对 USD 的基准全球商业结算汇率表 */ const USD_RATES: Record = { 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 { diff --git a/backend/src/modules/team/service/data-scope.ts b/backend/src/modules/team/service/data-scope.ts index 2021ee0..07753a5 100644 --- a/backend/src/modules/team/service/data-scope.ts +++ b/backend/src/modules/team/service/data-scope.ts @@ -82,7 +82,7 @@ export function assertLeadAccessPermission( } /** - * 出海多租户团队成员细粒度数据隔离服务 + * 多租户团队成员细粒度数据隔离服务 */ @Provide() export class TeamDataScopeService extends BaseService { diff --git a/backend/src/modules/team/service/finance-analytics.ts b/backend/src/modules/team/service/finance-analytics.ts index dea5cfd..3c2f159 100644 --- a/backend/src/modules/team/service/finance-analytics.ts +++ b/backend/src/modules/team/service/finance-analytics.ts @@ -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 = { 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)', diff --git a/backend/src/modules/team/service/platform-subscription.ts b/backend/src/modules/team/service/platform-subscription.ts index 3eb08aa..b731c85 100644 --- a/backend/src/modules/team/service/platform-subscription.ts +++ b/backend/src/modules/team/service/platform-subscription.ts @@ -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, diff --git a/backend/test/comm/calendar-invite.test.ts b/backend/test/comm/calendar-invite.test.ts index ebd665e..0cd5bcf 100644 --- a/backend/test/comm/calendar-invite.test.ts +++ b/backend/test/comm/calendar-invite.test.ts @@ -6,7 +6,7 @@ import { MeetingEventOptions, } from '../../src/modules/card/service/calendar'; -describe('Point 42: 出海潜客跨国会议 Google Calendar / Outlook 一键预约同步', () => { +describe('Point 42: 跨国会议与潜客预约 Google Calendar / Outlook 一键同步', () => { let calendarService: CardCalendarService; const mockMeeting: MeetingEventOptions = { diff --git a/backend/test/comm/contact-window.test.ts b/backend/test/comm/contact-window.test.ts index 41f5b9a..827ccc0 100644 --- a/backend/test/comm/contact-window.test.ts +++ b/backend/test/comm/contact-window.test.ts @@ -3,7 +3,7 @@ import { ContactWindowInfo, } from '../../src/comm/i18n/geo'; -describe('Point 39: 出海潜客多时区识别与最佳联系时间窗口推荐 (Best Contact Time Window)', () => { +describe('Point 39: 潜客多时区识别与最佳联系时间窗口推荐 (Best Contact Time Window)', () => { describe('1. 国际多国家/时区时间计算', () => { it('应准确计算中东阿联酋 (UTC+4) 当地时间与联系窗口', () => { // 假设 UTC 时间为 06:00 (北京时间 14:00,阿联酋当地时间为 10:00) @@ -20,7 +20,7 @@ describe('Point 39: 出海潜客多时区识别与最佳联系时间窗口推荐 expect(windowInfo.suggestionText).toContain('当地工作时间'); }); - it('当海外客商处于深夜休息时段 (如欧洲当地 23:00) 时,建议勿扰', () => { + it('当国际客商处于深夜休息时段 (如欧洲当地 23:00) 时,建议勿扰', () => { // 假设 UTC 时间为 22:00 (欧洲 CET UTC+1 当地为 23:00) const baseUtcDate = new Date(Date.UTC(2026, 8, 21, 22, 0, 0)); diff --git a/backend/test/e2e/omnicard-30-points.test.ts b/backend/test/e2e/omnicard-30-points.test.ts index 7c9009a..7e7b354 100644 --- a/backend/test/e2e/omnicard-30-points.test.ts +++ b/backend/test/e2e/omnicard-30-points.test.ts @@ -406,7 +406,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/alex-pronunciation.mp3', status: 'published', themeCode: 'corporate_navy', - remark: '内部高管名片,核心出海带队', + remark: '内部高管名片,国际业务核心', internalConfig: '{"rateLimit": 1000, "debug": false}', }); @@ -425,7 +425,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { colSpan: 2 as BentoColSpan, rowSpan: 1 as BentoRowSpan, title: '个人简介与身份卡', - content: '专注于中东与欧洲出海数字商务拓客', + content: '专注于中东与欧洲数字商务合作', status: 1, }, { @@ -440,7 +440,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { type: 'case' as BentoBlockType, colSpan: 2 as BentoColSpan, rowSpan: 2 as BentoRowSpan, - title: '新能源出海案例', + title: '新能源国际合作案例', content: '协助头部光伏企业落地阿布扎比', status: 1, }, @@ -448,8 +448,8 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { type: 'article' as BentoBlockType, colSpan: 1 as BentoColSpan, rowSpan: 1 as BentoRowSpan, - title: '2026出海白皮书', - content: '深度解读跨境智能名片获客漏斗', + title: '2026全球数字名片白皮书', + content: '深度解读全球智能名片获客漏斗', status: 1, }, ]; @@ -488,14 +488,14 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { it('P04: 博客文章发布与 SEO OpenGraph 社交卡片自动提炼', async () => { const rawHtmlContent = `
-

出海获客新引擎:2026 跨境数字名片实战洞察

-

在全球数字化出海与国际展会场景中,传统的纸质名片不仅浪费印刷成本,且存在高达 88% 的即时丢弃率。通过引入具备 NFC 极速碰触、多语言自适应以及 24 小时 AI 自动留资的智能微官网,出海销售代表能够在 30 秒内建立品牌信任,并让每一位海外客商的浏览轨迹实时映射在销售雷达上,实现端到端的精准线索转化。

+

全球获客新引擎:2026 数字名片实战洞察

+

在全球数字化与国际商务展会场景中,传统的纸质名片不仅浪费印刷成本,且存在高达 88% 的即时丢弃率。通过引入具备 NFC 极速碰触、多语言自适应以及 24 小时 AI 自动留资的智能微官网,销售代表能够在 30 秒内建立品牌信任,并让每一位国际客商的浏览轨迹实时映射在销售雷达上,实现端到端的精准线索转化。

`; const articleEntity = await articleRepo.save({ cardId: sharedCardId, - title: '出海获客新引擎:2026 跨境数字名片实战洞察', + title: '全球获客新引擎:2026 数字名片实战洞察', content: rawHtmlContent, coverImage: 'https://cdn.omnicard.ai/articles/og-banner.png', isPublished: true, @@ -506,7 +506,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { expect(ogMeta.title).toBe(articleEntity.title); expect(ogMeta.image).toBe('https://cdn.omnicard.ai/articles/og-banner.png'); - expect(ogMeta.description).toContain('在全球数字化出海与国际展会场景中'); + expect(ogMeta.description).toContain('在全球数字化与国际商务展会场景中'); expect(ogMeta.description.length).toBeLessThanOrEqual(160); expect(ogMeta.description).not.toContain('

'); }); @@ -746,7 +746,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { it('P13: 名片 AI 顾问人设、基调与留资触发阈值配置', async () => { const config = await aiConciergeService.saveByCardId(sharedCardId, { personaType: 'trade_expert', - welcomeMsg: '您好!我是 Alex 的 AI 出海顾问,请问有什么可以协助您的?', + welcomeMsg: '您好!我是 Alex 的 AI 商务顾问,请问有什么可以协助您的?', triggerLeadThreshold: 75, promptTemplate: 'You are an export trade consultant specialized in clean energy solutions.', toneStyle: 'professional', @@ -758,14 +758,14 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { expect(config.triggerLeadThreshold).toBe(75); const queried = await aiConciergeService.getByCardId(sharedCardId); - expect(queried?.welcomeMsg).toContain('Alex 的 AI 出海顾问'); + expect(queried?.welcomeMsg).toContain('Alex 的 AI 商务顾问'); }); it('P14: 知识库 RAG 滑动窗口文本分块与重叠切片算法', () => { const rawDocText = ` - OmniCard AI 致力于为全球商务出海团队提供次世代数字名片与微官网解决方案。 + OmniCard AI 致力于为全球商务团队提供次世代数字名片与微官网解决方案。 支持中东、欧洲及北美地区的主流商务支付与 CRM 系统无缝互联。 - 我们针对新能源、智能硬件及跨境电商客户提供定制化的展会获客方案, + 我们针对新能源、智能硬件及全球电商客户提供定制化的展会获客方案, 配合实体 NFC 智能卡片,可实现秒级留资并自动将高意向线索同步至公海池。 `.trim(); @@ -782,7 +782,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { const testChunks = [ { index: 0, - content: '公司主营产品包含新能源储能电站与光伏逆变器海外解决方案。', + content: '公司主营产品包含新能源储能电站与光伏逆变器全球解决方案。', length: 30, }, { @@ -808,7 +808,7 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { expect(results[0].score).toBeGreaterThan(0.5); }); - it('P16: 外贸行业术语库 (Glossary) 导入与出海多语言润色替换', async () => { + it('P16: 行业专有术语库 (Glossary) 导入与国际多语言润色替换', async () => { const importRes = await aiGlossaryService.batchImport([ { sourceTerm: 'digital business card', @@ -1103,15 +1103,15 @@ describe('OmniCard AI 30-Point End-to-End Pipeline Integration Suite', () => { // ========================================================================= describe('主干流 6: 多租户与商业化 (Points 27-30)', () => { it('P27: 组织架构部门树多层级递归与成员名片分配 (TeamMembersService)', async () => { - // 1. 初始化部门树结构:总部 -> 海外销售中心 -> 中东拓展组 + // 1. 初始化部门树结构:总部 -> 国际销售中心 -> 中东拓展组 const headDept = await deptRepo.save({ - name: '全球出海总部', + name: '全球总部', parentId: null as any, orderNum: 1, }); const salesDept = await deptRepo.save({ - name: '海外拓展事业群', + name: '全球拓展事业群', parentId: headDept.id, orderNum: 2, }); diff --git a/backend/test/modules/ai/deepseek.test.ts b/backend/test/modules/ai/deepseek.test.ts index e019385..ea9275c 100644 --- a/backend/test/modules/ai/deepseek.test.ts +++ b/backend/test/modules/ai/deepseek.test.ts @@ -54,13 +54,13 @@ describe('T31 DeepSeek 大模型集成与适配服务 (deepseek.ts)', () => { it('2.1 buildSystemPrompt 组装名片主、人设与知识库切片', () => { const card = { name: '张总', - company: '华强出海科技', - title: '海外商务总监', + company: '华强全球科技', + title: '国际商务总监', }; const concierge = { personaType: 'trade_expert' as const, toneStyle: 'professional' as const, - promptTemplate: '你是一名资深出海贸易专家,精通中东与东南亚市场。', + promptTemplate: '你是一名资深国际贸易专家,精通中东与东南亚市场。', }; const chunks = [ { index: 1, content: '支持信用证结算与 FOB 迪拜港条款。' }, @@ -70,9 +70,9 @@ describe('T31 DeepSeek 大模型集成与适配服务 (deepseek.ts)', () => { const systemPrompt = service.buildSystemPrompt(card, concierge, chunks); expect(systemPrompt).toContain('张总'); - expect(systemPrompt).toContain('华强出海科技'); - expect(systemPrompt).toContain('海外商务总监'); - expect(systemPrompt).toContain('你是一名资深出海贸易专家'); + expect(systemPrompt).toContain('华强全球科技'); + expect(systemPrompt).toContain('国际商务总监'); + expect(systemPrompt).toContain('你是一名资深国际贸易专家'); expect(systemPrompt).toContain('支持信用证结算与 FOB 迪拜港条款。'); expect(systemPrompt).toContain('最小起订量 (MOQ) 为 500 台。'); }); diff --git a/backend/test/modules/ai/localize.test.ts b/backend/test/modules/ai/localize.test.ts index aef98da..21993e2 100644 --- a/backend/test/modules/ai/localize.test.ts +++ b/backend/test/modules/ai/localize.test.ts @@ -52,7 +52,7 @@ function localizeReq( }; } -describe('T39: AI 出海润色 (modules/ai localize)', () => { +describe('T39: AI 多语言本地化润色 (modules/ai localize)', () => { describe('1. AiLocalizeService.localize 入参校验', () => { let service: AiLocalizeService; diff --git a/backend/test/modules/ai/outreach.test.ts b/backend/test/modules/ai/outreach.test.ts index db7ee53..85abc7d 100644 --- a/backend/test/modules/ai/outreach.test.ts +++ b/backend/test/modules/ai/outreach.test.ts @@ -11,7 +11,7 @@ describe('Point 47: AI 销售潜客智能跟进邮件/消息地道草拟引擎', service = new AiOutreachDraftService(); }); - describe('1. formatOutreachPrompt (出海商务邮件地道提示词组装)', () => { + describe('1. formatOutreachPrompt (全球商务邮件地道提示词组装)', () => { it('英语场景应包含地道商务开场、兴趣针对性提及与日程预约呼吁', () => { const req: OutreachDraftRequest = { leadName: 'Arthur Pendelton', diff --git a/backend/test/modules/ai/sandbox.test.ts b/backend/test/modules/ai/sandbox.test.ts index fe19824..ef84a7c 100644 --- a/backend/test/modules/ai/sandbox.test.ts +++ b/backend/test/modules/ai/sandbox.test.ts @@ -103,7 +103,7 @@ describe('Point 07: AI 知识库分块切片与召回沙盒 (modules/ai knowledg }, { index: 1, - content: '雷达系统支持出海 CRM 访客轨迹追踪、停留时长与意向度自动评分。', + content: '雷达系统支持智能 CRM 访客轨迹追踪、停留时长与意向度自动评分。', length: 33, }, { @@ -150,10 +150,10 @@ describe('Point 07: AI 知识库分块切片与召回沙盒 (modules/ai knowledg it('应兼容接收纯字符串数组 chunks', () => { const stringChunks = [ - '出海多语言支持英文、中文、阿拉伯语', + '国际多语言支持英文、中文、阿拉伯语', '国内网络环境配置说明', ]; - const results = service.simulateRetrieval('阿拉伯语 出海', stringChunks, 2); + const results = service.simulateRetrieval('阿拉伯语 国际多语言', stringChunks, 2); expect(results.length).toBe(1); expect(results[0].content).toContain('阿拉伯语'); expect(results[0].score).toBeGreaterThan(0); diff --git a/backend/test/modules/ai/sentiment-intent.test.ts b/backend/test/modules/ai/sentiment-intent.test.ts index ac64b5d..c11df3c 100644 --- a/backend/test/modules/ai/sentiment-intent.test.ts +++ b/backend/test/modules/ai/sentiment-intent.test.ts @@ -12,7 +12,7 @@ describe('Point 61 (P1): AI 对话客商意图与情绪极性分析引擎', () = analyzer = new AiSentimentIntentAnalyzer(); }); - describe('1. analyzeConversationIntent (出海商务意图识别)', () => { + describe('1. analyzeConversationIntent (商务意图识别)', () => { it('包含价格、报价、起订量或购买关键词应识别为 PURCHASE_INTENT', () => { const msgs = [ 'Hello, what is the MOQ for 500 NFC metal cards?', diff --git a/backend/test/modules/card/admin-bento.test.ts b/backend/test/modules/card/admin-bento.test.ts index 9800a84..f339b3a 100644 --- a/backend/test/modules/card/admin-bento.test.ts +++ b/backend/test/modules/card/admin-bento.test.ts @@ -190,7 +190,7 @@ function seedBlocks(): Array> { rowSpan: 1, sortOrder: 1, title: '个人简介与身份卡', - content: '全栈出海专家', + content: '全球商务专家', status: 1, }, { @@ -211,7 +211,7 @@ function seedBlocks(): Array> { colSpan: 2, rowSpan: 2, sortOrder: 2, - title: '新能源出海案例', + title: '新能源国际合作案例', content: '中东客户成功交付', status: 0, }, @@ -334,7 +334,7 @@ describe('后台 Bento 画布 CRUD (Admin Card Bento CoolController)', () => { type: 'video' as BentoBlockType, colSpan: 2 as BentoColSpan, rowSpan: 2 as BentoRowSpan, - title: '企业出海宣传片', + title: '企业宣传片', mediaUrl: 'https://cdn.omnicard.ai/video/intro.mp4', }, ]; diff --git a/backend/test/modules/card/article.test.ts b/backend/test/modules/card/article.test.ts index 7ff2a2a..674b536 100644 --- a/backend/test/modules/card/article.test.ts +++ b/backend/test/modules/card/article.test.ts @@ -74,7 +74,7 @@ describe('后台博客文章与动态发布中心 (Card Article CoolController & it('完整文章入参时,应正确映射 title、summary 与 coverImage', () => { const article = { cardId: 101, - title: '出海战略新视界:2026 跨境数字名片白皮书', + title: '全球商务新视界:2026 数字名片白皮书', summary: '本文深度剖析全球化商务场景下 NFC 与微官网结合的数字化获客新路径。', content: '

正文内容...

', coverImage: 'https://cdn.omnicard.ai/articles/cover-1.jpg', @@ -91,18 +91,18 @@ describe('后台博客文章与动态发布中心 (Card Article CoolController & const longHtml = `

一级标题

-

在全球数字化浪潮席卷而来的今天,智能名片已经不仅是身份凭证,更是出海企业的第一道数字门面与线索转化漏斗。通过多语言自适应与 NFC 瞬时触达,销售代表可以在海外展会现场以极低摩擦完成商机留资与精准雷达跟进,大幅提升商机转化效率与客户体验。

+

在全球数字化浪潮席卷而来的今天,智能名片已经不仅是身份凭证,更是现代企业的第一道数字门面与线索转化漏斗。通过多语言自适应与 NFC 瞬时触达,销售代表可以在国际展会现场以极低摩擦完成商机留资与精准雷达跟进,大幅提升商机转化效率与客户体验。

`; const article = { cardId: 101, - title: '出海营销指南', + title: '全球营销指南', content: longHtml, isPublished: true, }; const og = service.generateOgMeta(article); - expect(og.title).toBe('出海营销指南'); + expect(og.title).toBe('全球营销指南'); expect(og.description.length).toBeLessThanOrEqual(160); expect(og.description).not.toContain('

'); expect(og.description).not.toContain(''); diff --git a/backend/test/modules/card/batch-locale.test.ts b/backend/test/modules/card/batch-locale.test.ts index ffef9c0..96593d8 100644 --- a/backend/test/modules/card/batch-locale.test.ts +++ b/backend/test/modules/card/batch-locale.test.ts @@ -5,7 +5,7 @@ import { } from '../../../src/modules/card/service/batch-locale'; import { CardLocaleEntity } from '../../../src/modules/card/entity/locale'; -describe('Point 43: 出海团队多语言名片批量生成向导与多语种字段镜像校验', () => { +describe('Point 43: 跨国团队多语言名片批量生成向导与多语种字段镜像校验', () => { let service: CardBatchLocaleService; let mockRepository: any; let savedRecords: CardLocaleEntity[]; @@ -80,8 +80,8 @@ describe('Point 43: 出海团队多语言名片批量生成向导与多语种字 cardId: 101, baseFields: { name: '张三', - title: '出海销售总监', - company: '深圳数字智能出海科技', + title: '国际业务总监', + company: '数字智能科技', slogan: '智能连接全球', }, }, @@ -90,7 +90,7 @@ describe('Point 43: 出海团队多语言名片批量生成向导与多语种字 baseFields: { name: '李四', title: '中东区域代表', - company: '深圳数字智能出海科技', + company: '数字智能科技', slogan: '专注海湾六国', }, }, @@ -164,7 +164,7 @@ describe('Point 43: 出海团队多语言名片批量生成向导与多语种字 }); }); - describe('3. inspectTeamLocaleCoverage (出海团队语言覆盖率与就绪度诊断)', () => { + describe('3. inspectTeamLocaleCoverage (跨国团队语言覆盖率与就绪度诊断)', () => { it('应精准统计团队名片各语言的覆盖率与缺失名单', async () => { savedRecords.push( { id: 1, cardId: 101, locale: 'en', name: 'Alex' } as CardLocaleEntity, diff --git a/backend/test/modules/card/bento.test.ts b/backend/test/modules/card/bento.test.ts index 9407990..b699802 100644 --- a/backend/test/modules/card/bento.test.ts +++ b/backend/test/modules/card/bento.test.ts @@ -153,7 +153,7 @@ describe('后端微官网模块 (modules/card) - Bento Grid 便当盒组件树 rowSpan: 1, sortOrder: 1, title: '个人简介与身份卡', - content: '全栈出海专家', + content: '全球商务专家', status: 1, }, { @@ -174,7 +174,7 @@ describe('后端微官网模块 (modules/card) - Bento Grid 便当盒组件树 colSpan: 2, rowSpan: 2, sortOrder: 2, - title: '新能源出海案例', + title: '新能源国际合作案例', content: '中东客户成功交付', status: 0, // 禁用 }, @@ -275,7 +275,7 @@ describe('后端微官网模块 (modules/card) - Bento Grid 便当盒组件树 type: 'video' as BentoBlockType, colSpan: 2 as BentoColSpan, rowSpan: 2 as BentoRowSpan, - title: '企业出海宣传片', + title: '企业宣传片', mediaUrl: 'https://cdn.omnicard.ai/video/intro.mp4', }, ]; @@ -336,7 +336,7 @@ describe('后端微官网模块 (modules/card) - Bento Grid 便当盒组件树 type: 'form' as BentoBlockType, colSpan: 2 as BentoColSpan, rowSpan: 1 as BentoRowSpan, - title: '外贸留资表单', + title: '商务留资表单', metaJson: JSON.stringify({ fields: ['name', 'email', 'country', 'requirement'], buttonText: 'Get Quote', @@ -383,12 +383,12 @@ describe('后端微官网模块 (modules/card) - Bento Grid 便当盒组件树 const created = await service.saveBlock({ cardId: 100, type: 'article', - title: '出海白皮书', + title: '全球商务白皮书', colSpan: 1, rowSpan: 1, }); expect(created.id).toBeDefined(); - expect(created.title).toBe('出海白皮书'); + expect(created.title).toBe('全球商务白皮书'); }); it('deleteBlock 应能成功删除单个块', async () => { diff --git a/backend/test/modules/card/calendar.test.ts b/backend/test/modules/card/calendar.test.ts index 29bf68f..b92d646 100644 --- a/backend/test/modules/card/calendar.test.ts +++ b/backend/test/modules/card/calendar.test.ts @@ -86,7 +86,7 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 const mockMeeting: MeetingEventOptions = { uid: 'omnicard-meet-20260920-001@omnicard.ai', summary: 'OmniCard AI 商务拓展战略会谈 (Alex & Partner)', - description: '讨论海外微官网落地方案, NFC 实体卡发行与访客雷达 CRM 对接。\n请准时参会。', + description: '讨论国际微官网落地方案, NFC 实体卡发行与访客雷达 CRM 对接。\n请准时参会。', location: 'Zoom 会议室 (ID: 888-999-000), 新加坡滨海湾金融中心', startTime: new Date(Date.UTC(2026, 8, 20, 14, 0, 0)), endTime: new Date(Date.UTC(2026, 8, 20, 15, 0, 0)), @@ -122,7 +122,7 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 expect(ics).toContain('DTSTART:20260920T140000Z\r\n'); expect(ics).toContain('DTEND:20260920T150000Z\r\n'); expect(ics).toContain('SUMMARY:OmniCard AI 商务拓展战略会谈 (Alex & Partner)\r\n'); - expect(ics).toContain('DESCRIPTION:讨论海外微官网落地方案\\, NFC 实体卡发行与访客雷达 CRM 对接。\\n请准时参会。\r\n'); + expect(ics).toContain('DESCRIPTION:讨论国际微官网落地方案\\, NFC 实体卡发行与访客雷达 CRM 对接。\\n请准时参会。\r\n'); expect(ics).toContain('LOCATION:Zoom 会议室 (ID: 888-999-000)\\, 新加坡滨海湾金融中心\r\n'); expect(ics).toContain('ORGANIZER;CN=Alex Zheng:mailto:alex@omnicard.ai\r\n'); expect(ics).toContain('URL:https://omnicard.ai/p/alex-zheng\r\n'); @@ -193,10 +193,10 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 portalController.ctx = mockCtx; const result = await portalController.meetingIcs({ - summary: '全球合作伙伴出海研讨会', + summary: '全球合作伙伴研讨会', startTime: '2026-09-22T09:00:00Z', endTime: '2026-09-22T10:00:00Z', - description: '探讨 2026 年品牌海外本土化与数字名片生态布局', + description: '探讨 2026 年品牌全球本土化与数字名片生态布局', location: '线上 Google Meet', organizerName: 'Alex Zheng', organizerEmail: 'alex@omnicard.ai', @@ -211,7 +211,7 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 const icsOutput = (responseBody as string) || (result as string); expect(typeof icsOutput).toBe('string'); expect(icsOutput).toContain('BEGIN:VCALENDAR'); - expect(icsOutput).toContain('SUMMARY:全球合作伙伴出海研讨会'); + expect(icsOutput).toContain('SUMMARY:全球合作伙伴研讨会'); expect(icsOutput).toContain('DTSTART:20260922T090000Z'); expect(icsOutput).toContain('DTEND:20260922T100000Z'); expect(icsOutput).toContain('ORGANIZER;CN=Alex Zheng:mailto:alex@omnicard.ai'); @@ -266,8 +266,8 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 it('应正确从 ICS 字符串反向提取会议核心属性', () => { const meeting: MeetingEventOptions = { uid: 'roundtrip-test-001@omnicard.ai', - summary: '出海战略合作洽谈', - description: '讨论微官网与海外本地化支付通道', + summary: '全球战略合作洽谈', + description: '讨论微官网与全球本地化支付通道', location: '线上 Google Meet', startTime: '2026-09-25T08:00:00Z', endTime: '2026-09-25T09:00:00Z', @@ -282,8 +282,8 @@ describe('Point 26: 国际商务日程预约排期与 ICS 日历订阅 (RFC 5545 const parsed = parseIcsEvent(ics); expect(parsed.uid).toBe('roundtrip-test-001@omnicard.ai'); - expect(parsed.summary).toBe('出海战略合作洽谈'); - expect(parsed.description).toBe('讨论微官网与海外本地化支付通道'); + expect(parsed.summary).toBe('全球战略合作洽谈'); + expect(parsed.description).toBe('讨论微官网与全球本地化支付通道'); expect(parsed.location).toBe('线上 Google Meet'); expect(parsed.status).toBe('CONFIRMED'); expect(typeof parsed.organizer).toBe('object'); diff --git a/backend/test/modules/card/cases.test.ts b/backend/test/modules/card/cases.test.ts index d24a98a..c85264a 100644 --- a/backend/test/modules/card/cases.test.ts +++ b/backend/test/modules/card/cases.test.ts @@ -8,7 +8,7 @@ import { adminCardCasesCurdOption, } from '../../../src/modules/card/controller/admin/cases'; -describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { +describe('后台案例作品集 (Admin Card Cases CRUD)', () => { const entityFile = path.join( __dirname, '../../../src/modules/card/entity/cases.ts' @@ -59,10 +59,10 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { }); describe('2. 实体字段契约 (CardCasesEntity)', () => { - it('实体实例应支持出海案例必需契约字段', () => { + it('实体实例应支持案例必需契约字段', () => { const item = new CardCasesEntity(); item.cardId = 1001; - item.title = '智能电动滑板车欧美 DTC 出海'; + item.title = '智能电动滑板车欧美 DTC 全球拓展'; item.client = 'VoltRide Global'; item.duration = '2024.01 - 2024.06'; item.metrics = '+320% ROI, 45,000+ 留资量'; @@ -72,7 +72,7 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { item.sortOrder = 1; expect(item.cardId).toBe(1001); - expect(item.title).toBe('智能电动滑板车欧美 DTC 出海'); + expect(item.title).toBe('智能电动滑板车欧美 DTC 全球拓展'); expect(item.client).toBe('VoltRide Global'); expect(item.duration).toBe('2024.01 - 2024.06'); expect(item.metrics).toBe('+320% ROI, 45,000+ 留资量'); @@ -92,7 +92,7 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { Object.assign(new CardCasesEntity(), { id: 1, cardId: 101, - title: '跨境智能硬件众筹出海', + title: '全球智能硬件众筹发布', client: 'TechNova', duration: '3个月', metrics: '+280% 众筹目标达成', @@ -103,7 +103,7 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { Object.assign(new CardCasesEntity(), { id: 2, cardId: 101, - title: '东南亚 TikTok 跨境直播获客', + title: '东南亚 TikTok 国际直播获客', client: 'FashionGo', duration: '6个月', metrics: '千万级曝光, +150% GMV', @@ -163,9 +163,9 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { it('getCasesByCardId 应根据名片 cardId 筛选并按 sortOrder 升序排列', async () => { const cases = await service.getCasesByCardId(101); expect(cases).toHaveLength(2); - expect(cases[0].title).toBe('东南亚 TikTok 跨境直播获客'); + expect(cases[0].title).toBe('东南亚 TikTok 国际直播获客'); expect(cases[0].sortOrder).toBe(1); - expect(cases[1].title).toBe('跨境智能硬件众筹出海'); + expect(cases[1].title).toBe('全球智能硬件众筹发布'); expect(cases[1].sortOrder).toBe(2); }); @@ -177,7 +177,7 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { it('创建案例并能查询出最新案例', async () => { const created = await service.cardCasesEntity.save({ cardId: 101, - title: '拉美电商本土化品牌出海', + title: '拉美电商本土化品牌打造', client: 'LatamDirect', duration: '4个月', metrics: '+90% 复购率', @@ -187,7 +187,7 @@ describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => { }); expect(created.id).toBeDefined(); - expect(created.title).toBe('拉美电商本土化品牌出海'); + expect(created.title).toBe('拉美电商本土化品牌打造'); const found = await service.cardCasesEntity.findOneBy({ id: created.id }); expect(found).not.toBeNull(); diff --git a/backend/test/modules/card/portal.test.ts b/backend/test/modules/card/portal.test.ts index fc8f772..8bfc2ea 100644 --- a/backend/test/modules/card/portal.test.ts +++ b/backend/test/modules/card/portal.test.ts @@ -29,7 +29,7 @@ describe('Task 08: 后端公开短链路由与秒级微官网详情 API (Card Po themeCode: 'corporate_navy', // 敏感内部数据 userId: 9999, - remark: '重点出海客户核心高管,年费 VIP 专属', + remark: '重点合作客户核心高管,年费 VIP 专属', internalConfig: '{"rateLimit": 1000, "debug": true, "internalTenantSecret": "sk_live_xyz"}', createTime: new Date(), updateTime: new Date(), @@ -71,7 +71,7 @@ describe('Task 08: 后端公开短链路由与秒级微官网详情 API (Card Po colSpan: 1, rowSpan: 1, sortOrder: 3, - title: '出海 SaaS 落地指南', + title: '全球 SaaS 落地指南', content: '深度解读全球数字名片与微官网搭建...', status: 1, // 有效 internalNotes: '文章需由法务团队二次审核', // 敏感 @@ -84,7 +84,7 @@ describe('Task 08: 后端公开短链路由与秒级微官网详情 API (Card Po rowSpan: 1, sortOrder: 1, title: '关于我', - content: '10 年全球出海商务拓展经验', + content: '10 年全球商务拓展经验', status: 1, // 有效 internalNotes: '置顶 Hero 块', } as CardBentoEntity, diff --git a/backend/test/modules/card/poster-layout.test.ts b/backend/test/modules/card/poster-layout.test.ts index 20a9fa1..a5a4b48 100644 --- a/backend/test/modules/card/poster-layout.test.ts +++ b/backend/test/modules/card/poster-layout.test.ts @@ -6,7 +6,7 @@ import { PosterElementBox, } from '../../../src/modules/card/service/poster-layout'; -describe('Point 54: 出海展会多语言电子邀请海报轻量级排版引擎', () => { +describe('Point 54: 商务展会多语言电子邀请海报轻量级排版引擎', () => { let engine: CardPosterLayoutEngine; beforeEach(() => { diff --git a/backend/test/modules/card/seed-cards.test.ts b/backend/test/modules/card/seed-cards.test.ts new file mode 100644 index 0000000..cd1e871 --- /dev/null +++ b/backend/test/modules/card/seed-cards.test.ts @@ -0,0 +1,121 @@ +import { MOCK_CARDS_DATA, MockCardBundle } from '../../../src/modules/card/mock/seed-cards'; +import { CardPortalService } from '../../../src/modules/card/service/portal'; +import { CardInfoEntity } from '../../../src/modules/card/entity/info'; +import { CardBentoEntity } from '../../../src/modules/card/entity/bento'; + +describe('多张多媒体名片数据 (配图与配视频) 规格与集成测试', () => { + it('1. 应包含至少 5 组不同国际行业的高保真名片数据包', () => { + expect(Array.isArray(MOCK_CARDS_DATA)).toBe(true); + expect(MOCK_CARDS_DATA.length).toBeGreaterThanOrEqual(5); + + const slugs = MOCK_CARDS_DATA.map(item => item.card.slug); + const uniqueSlugs = new Set(slugs); + expect(uniqueSlugs.size).toBe(MOCK_CARDS_DATA.length); // Slug 必须唯一 + }); + + it('2. 每张名片的基础信息必须完整且配有高分辨率头像与国际化联系方式', () => { + MOCK_CARDS_DATA.forEach((bundle: MockCardBundle) => { + const { card } = bundle; + expect(card.id).toBeDefined(); + expect(card.name).toBeTruthy(); + expect(card.title).toBeTruthy(); + expect(card.company).toBeTruthy(); + expect(card.slogan).toBeTruthy(); + expect(card.status).toBe('published'); + + // 配图验证:头像必须是合法图片 URL (http/https) + expect(card.avatarUrl).toMatch(/^https?:\/\/.+\.(png|jpe?g|webp|svg)(\?.*)?$/i); + + // 国际化联系方式 + expect(card.phone).toMatch(/^\+[1-9]\d{1,14}$/); // E.164 规范 + expect(card.email).toMatch(/^[^\s@]+@[^\s@]+\.[^\s@]+$/); + expect(card.timezone).toBeTruthy(); + }); + }); + + it('3. 每张名片必须包含有效的视频 Bento 块,且配有可播放视频流与专属海报封面', () => { + MOCK_CARDS_DATA.forEach((bundle: MockCardBundle) => { + const { bentoBlocks } = bundle; + expect(Array.isArray(bentoBlocks)).toBe(true); + expect(bentoBlocks.length).toBeGreaterThan(0); + + // 必须包含至少一个 video 类型的 Bento 块 + const videoBlocks = bentoBlocks.filter(b => b.type === 'video'); + expect(videoBlocks.length).toBeGreaterThanOrEqual(1); + + videoBlocks.forEach(videoBlock => { + expect(videoBlock.title).toBeTruthy(); + // mediaUrl 作为视频封面海报,必须是合法图片 URL + expect(videoBlock.mediaUrl).toMatch(/^https?:\/\/.+\.(png|jpe?g|webp)(\?.*)?$/i); + // linkUrl 或 metaJson.videoUrl 必须是可播放的视频源 (mp4/m3u8/webm) + let videoUrl = videoBlock.linkUrl; + if (typeof videoBlock.metaJson === 'object' && videoBlock.metaJson !== null) { + videoUrl = (videoBlock.metaJson as any).videoUrl || videoUrl; + } else if (typeof videoBlock.metaJson === 'string') { + const parsed = JSON.parse(videoBlock.metaJson); + videoUrl = parsed.videoUrl || videoUrl; + } + expect(videoUrl).toMatch(/^https?:\/\/.+\.(mp4|webm|m3u8)(\?.*)?$/i); + }); + }); + }); + + it('4. 每张名片必须包含图文案例与文章,且均配置了真实多媒体封面配图', () => { + MOCK_CARDS_DATA.forEach((bundle: MockCardBundle) => { + // 检查案例 + expect(Array.isArray(bundle.cases)).toBe(true); + expect(bundle.cases.length).toBeGreaterThanOrEqual(1); + bundle.cases.forEach(c => { + expect(c.title).toBeTruthy(); + expect(c.client).toBeTruthy(); + expect(c.coverImage).toMatch(/^https?:\/\/.+\.(png|jpe?g|webp)(\?.*)?$/i); + }); + + // 检查文章 + expect(Array.isArray(bundle.articles)).toBe(true); + expect(bundle.articles.length).toBeGreaterThanOrEqual(1); + bundle.articles.forEach(a => { + expect(a.title).toBeTruthy(); + expect(a.coverImage).toMatch(/^https?:\/\/.+\.(png|jpe?g|webp)(\?.*)?$/i); + }); + }); + }); + + it('5. 微官网服务 (CardPortalService) 能够无缝聚合各名片详情,正确渲染视频与图文块', async () => { + const cardInfoRepo: any = { + findOne: jest.fn().mockImplementation(async options => { + const slug = options?.where?.slug; + const id = options?.where?.id; + const found = MOCK_CARDS_DATA.find( + b => (slug && b.card.slug === slug) || (id && b.card.id === Number(id)) + ); + return found ? ({ ...found.card } as CardInfoEntity) : null; + }), + }; + + const cardBentoRepo: any = { + find: jest.fn().mockImplementation(async options => { + const cardId = options?.where?.cardId; + const found = MOCK_CARDS_DATA.find(b => b.card.id === Number(cardId)); + return found ? found.bentoBlocks.map(blk => ({ ...blk } as CardBentoEntity)) : []; + }), + }; + + const portalService = new CardPortalService(); + portalService.cardInfoEntity = cardInfoRepo; + portalService.cardBentoEntity = cardBentoRepo; + CardPortalService.clearAllCache(); + + for (const bundle of MOCK_CARDS_DATA) { + const detail = await portalService.getPortalDetail(bundle.card.slug); + expect(detail).toBeDefined(); + expect(detail.name).toBe(bundle.card.name); + expect(detail.avatarUrl).toBe(bundle.card.avatarUrl); + + // 验证视频块是否存在于公开微官网聚合结果中 + const videoBlock = detail.bentoBlocks.find(b => b.type === 'video'); + expect(videoBlock).toBeDefined(); + expect(videoBlock?.mediaUrl).toBeTruthy(); + } + }); +}); diff --git a/backend/test/modules/crm/attribution.test.ts b/backend/test/modules/crm/attribution.test.ts index 2ab1303..fa0225f 100644 --- a/backend/test/modules/crm/attribution.test.ts +++ b/backend/test/modules/crm/attribution.test.ts @@ -5,7 +5,7 @@ import { ChannelAttributionEvent, } from '../../../src/modules/crm/service/attribution'; -describe('Point 46: 访客雷达多渠道来源归因与出海转化漏斗分析', () => { +describe('Point 46: 访客雷达多渠道来源归因与转化漏斗分析', () => { let service: RadarAttributionService; beforeEach(() => { @@ -18,7 +18,7 @@ describe('Point 46: 访客雷达多渠道来源归因与出海转化漏斗分析 expect(normalizeTrafficChannel('booth', 'qrcode')).toBe('booth_qr'); }); - it('应精准识别 WhatsApp 与 LinkedIn 社交出海渠道', () => { + it('应精准识别 WhatsApp 与 LinkedIn 国际社交渠道', () => { expect(normalizeTrafficChannel('whatsapp', 'im')).toBe('whatsapp_chat'); expect(normalizeTrafficChannel('linkedin', 'social')).toBe('linkedin_share'); expect(normalizeTrafficChannel('email', 'sig')).toBe('email_signature'); diff --git a/backend/test/modules/crm/bot-filter.test.ts b/backend/test/modules/crm/bot-filter.test.ts index 10ee066..5727971 100644 --- a/backend/test/modules/crm/bot-filter.test.ts +++ b/backend/test/modules/crm/bot-filter.test.ts @@ -5,7 +5,7 @@ import { TrafficFilterResult, } from '../../../src/modules/crm/service/bot-filter'; -describe('Point 63 (P2): 访客雷达异常流量与出海社交抓取机器爬虫清洗过滤', () => { +describe('Point 63 (P2): 访客雷达异常流量与社交抓取机器爬虫清洗过滤', () => { let service: RadarBotFilterService; beforeEach(() => { diff --git a/backend/test/modules/crm/btp-20-push-webhook.test.ts b/backend/test/modules/crm/btp-20-push-webhook.test.ts index 2891b5f..9359a7c 100644 --- a/backend/test/modules/crm/btp-20-push-webhook.test.ts +++ b/backend/test/modules/crm/btp-20-push-webhook.test.ts @@ -5,7 +5,7 @@ import { AdminCrmLeadController } from '../../../src/modules/crm/controller/admi import { CrmLeadEntity } from '../../../src/modules/crm/entity/lead'; import { RadarTrack } from '../../../src/comm/types/omni'; -describe('【BTP-20】线索流转自动化与出海 Webhook / 实时推送集成测试', () => { +describe('【BTP-20】线索流转自动化与外部 Webhook / 实时推送集成测试', () => { let syncService: CrmSyncService; let pushService: CrmRadarPushService; let mockLead: CrmLeadEntity; diff --git a/backend/test/modules/crm/compliance.test.ts b/backend/test/modules/crm/compliance.test.ts index b16e56b..c5e7d84 100644 --- a/backend/test/modules/crm/compliance.test.ts +++ b/backend/test/modules/crm/compliance.test.ts @@ -5,7 +5,7 @@ import { GdprUserDataBundle, } from '../../../src/modules/crm/service/compliance'; -describe('Point 53: 海外合规 GDPR / CCPA 访客数据匿名化脱敏与被遗忘权导出', () => { +describe('Point 53: 国际合规 GDPR / CCPA 访客数据匿名化脱敏与被遗忘权导出', () => { let service: ComplianceGdprService; beforeEach(() => { diff --git a/backend/test/modules/crm/enrichment.test.ts b/backend/test/modules/crm/enrichment.test.ts index f565e30..8b4db4f 100644 --- a/backend/test/modules/crm/enrichment.test.ts +++ b/backend/test/modules/crm/enrichment.test.ts @@ -6,7 +6,7 @@ import { CompanyProfileResult, } from '../../../src/modules/crm/service/enrichment'; -describe('Point 49: 出海潜客企业邮箱域名解析与商业主页自动富化', () => { +describe('Point 49: 潜客企业邮箱域名解析与商业主页自动富化', () => { let service: LeadCompanyEnrichmentService; beforeEach(() => { diff --git a/backend/test/modules/crm/geo-heatmap.test.ts b/backend/test/modules/crm/geo-heatmap.test.ts index fc202dd..90cfcb3 100644 --- a/backend/test/modules/crm/geo-heatmap.test.ts +++ b/backend/test/modules/crm/geo-heatmap.test.ts @@ -13,7 +13,7 @@ describe('Point 55: 全球访客雷达国家城市坐标聚合与热力散点引 service = new RadarGeoHeatmapService(); }); - describe('1. resolveCityCoordinates (出海核心商务枢纽城市经纬度解析)', () => { + describe('1. resolveCityCoordinates (全球核心商务枢纽城市经纬度解析)', () => { it('应精准解析迪拜、利雅得、新加坡与伦敦经纬度', () => { const dubai = resolveCityCoordinates('AE', 'Dubai'); expect(dubai).toEqual([55.27, 25.20]); diff --git a/backend/test/modules/crm/lead-automation.test.ts b/backend/test/modules/crm/lead-automation.test.ts index 7ee6139..ced42bc 100644 --- a/backend/test/modules/crm/lead-automation.test.ts +++ b/backend/test/modules/crm/lead-automation.test.ts @@ -5,7 +5,7 @@ import { LeadBehaviorTrigger, } from '../../../src/modules/crm/service/lead-automation'; -describe('Point 58 (P1): 出海潜客线索行为自动化阶段推进引擎', () => { +describe('Point 58 (P1): 潜客线索行为自动化阶段推进引擎', () => { let service: LeadStageAutomationService; beforeEach(() => { diff --git a/backend/test/modules/crm/tenant-lead-track.test.ts b/backend/test/modules/crm/tenant-lead-track.test.ts index d3f45e5..b20c78c 100644 --- a/backend/test/modules/crm/tenant-lead-track.test.ts +++ b/backend/test/modules/crm/tenant-lead-track.test.ts @@ -54,7 +54,7 @@ describe('多租户安全闭环: 公开留资与雷达足迹自动绑定名片 it('公开留资未显式传入 tenantId 时,必须根据 cardId 自动追溯绑定归属名片的 tenantId 和 userId', async () => { const lead = await leadService.submitLead({ cardId: 101, - name: '海外采购商', + name: '国际采购商', phone: '+971-50-9876543', company: 'Dubai Trading LLC', }); @@ -68,7 +68,7 @@ describe('多租户安全闭环: 公开留资与雷达足迹自动绑定名片 const lead = await leadService.submitLead({ cardId: 101, tenantId: 999, - name: '海外采购商', + name: '国际采购商', phone: '+971-50-9876543', } as any); diff --git a/backend/test/modules/crm/webhook-dispatcher.test.ts b/backend/test/modules/crm/webhook-dispatcher.test.ts index d36c0b1..d75a487 100644 --- a/backend/test/modules/crm/webhook-dispatcher.test.ts +++ b/backend/test/modules/crm/webhook-dispatcher.test.ts @@ -5,7 +5,7 @@ import { LeadWebhookPayload, } from '../../../src/modules/crm/service/webhook-dispatcher'; -describe('Point 51: 出海商机实时 Webhook 触发器与 HMAC-SHA256 签名分发', () => { +describe('Point 51: 商机实时 Webhook 触发器与 HMAC-SHA256 签名分发', () => { let dispatcher: LeadWebhookDispatcher; const mockSecret = 'whsec_8f9a2b4c6e1d7g5h3j8k9l0m'; const mockPayload: LeadWebhookPayload = { diff --git a/backend/test/modules/crm/workplace.test.ts b/backend/test/modules/crm/workplace.test.ts index 0d46781..05aaffe 100644 --- a/backend/test/modules/crm/workplace.test.ts +++ b/backend/test/modules/crm/workplace.test.ts @@ -1,6 +1,7 @@ import { AdminCrmRadarController } from '../../../src/modules/crm/controller/admin/radar'; import { buildWorkplaceOverview, + calculateTrackIntentScore, CrmRadarService, } from '../../../src/modules/crm/service/radar'; import { CrmTrackEntity } from '../../../src/modules/crm/entity/track'; @@ -152,4 +153,97 @@ describe('商务概览工作台聚合 (getWorkplaceOverview)', () => { expect(res.data.kpis.viewCount).toBe(2); expect(controller.crmRadarService.getWorkplaceOverview).toHaveBeenCalled(); }); + + describe('数据专家重构:科学意向度与会话降噪增强', () => { + it('科学意向分算法 (calculateTrackIntentScore) 必须符合多因子阶梯模型', () => { + // 1. 直链 0 秒跳出,无任何浏览与滚动:低意向分 (10分) + const bounceScore = calculateTrackIntentScore({ + touchType: 'link', + stayDurationMs: 0, + scrollDepth: 0, + viewedBlocks: [], + }); + expect(bounceScore).toBe(10); + + // 2. NFC 面对面触碰初始分:天然高权重 (35分) + const nfcInitialScore = calculateTrackIntentScore({ + touchType: 'nfc', + stayDurationMs: 0, + }); + expect(nfcInitialScore).toBe(35); + + // 3. QR 扫码 + 30秒研读 + 2个模块 + 深度滚动:高意向买家 (25 + 20 + 10 + 10 = 65分) + const engagedScore = calculateTrackIntentScore({ + touchType: 'qr', + stayDurationMs: 30000, + viewedBlocks: ['hero', 'products'], + scrollDepth: 75, + }); + expect(engagedScore).toBe(65); + + // 4. NFC 触碰 + 150秒深读 + 4个模块 + 深度滚动:极高意向买家 (封顶 99分) + const hotScore = calculateTrackIntentScore({ + touchType: 'nfc', + stayDurationMs: 150000, + viewedBlocks: ['hero', 'products', 'cases', 'contact'], + scrollDepth: 95, + }); + expect(hotScore).toBe(99); + }); + + it('buildWorkplaceOverview 应实现会话去噪与合并,并填充名片名称和本地标记', () => { + const cardList = [ + { id: 1, name: 'Alex Chen', title: '全球商务总监' }, + ]; + + // 同一访客 vis_101 先产生了 0 秒 PV,又更新了 45 秒有效停留 + const t1 = track({ + id: 10, + cardId: 1, + touchType: 'link', + country: 'Local', + city: 'Local', + stayDurationMs: 0, + createTime: new Date('2026-09-21T10:00:00'), + }); + t1.visitorId = 'vis_101'; + + const t2 = track({ + id: 11, + cardId: 1, + touchType: 'link', + country: 'Local', + city: 'Local', + stayDurationMs: 45000, + viewedBlocks: ['products'], + createTime: new Date('2026-09-21T10:01:00'), + }); + t2.visitorId = 'vis_101'; + + const result = buildWorkplaceOverview( + [t1, t2], + [], + now, + 10, + cardList + ); + + // 验证 1:去噪合并后同一会话只保留 1 条记录 + expect(result.radarFeed).toHaveLength(1); + const feedItem = result.radarFeed[0]; + + // 验证 2:停留时长采用合并后的最高值 45 秒 + expect(feedItem.stayDurationMs).toBe(45000); + expect(feedItem.viewedBlocks).toContain('products'); + + // 验证 3:名片名称正确注入 + expect(feedItem.cardTitle).toBe('Alex Chen · 全球商务总监'); + + // 验证 4:识别为本地环境 + expect(feedItem.isLocal).toBe(true); + + // 验证 5:意向分由后端自动算出并大于 0 秒跳出分 + expect(feedItem.intentScore).toBeGreaterThan(10); + }); + }); }); diff --git a/backend/test/modules/nfc/manufacturing.test.ts b/backend/test/modules/nfc/manufacturing.test.ts index f037f8e..4587dfe 100644 --- a/backend/test/modules/nfc/manufacturing.test.ts +++ b/backend/test/modules/nfc/manufacturing.test.ts @@ -6,7 +6,7 @@ import { canTransitionBatchStatus, } from '../../../src/modules/nfc/service/manufacturing'; -describe('Point 45: 跨境实体卡供应链生产批次工单生命周期与良率检验', () => { +describe('Point 45: 国际实体卡供应链生产批次工单生命周期与良率检验', () => { let service: NfcManufacturingService; beforeEach(() => { diff --git a/backend/test/modules/team/currency-tax.test.ts b/backend/test/modules/team/currency-tax.test.ts index 1f418aa..2607783 100644 --- a/backend/test/modules/team/currency-tax.test.ts +++ b/backend/test/modules/team/currency-tax.test.ts @@ -5,7 +5,7 @@ import { } from '../../../src/modules/team/service/currency-tax'; import { TaxCalculationResult } from '../../../src/comm/types/omni'; -describe('Point 57 (P1): 跨境多币种汇率换算与出海发票税制计算', () => { +describe('Point 57 (P1): 跨国多币种汇率换算与国际发票税制计算', () => { let service: CurrencyTaxCalculatorService; beforeEach(() => { @@ -35,7 +35,7 @@ describe('Point 57 (P1): 跨境多币种汇率换算与出海发票税制计算' }); }); - describe('2. calculateInternationalTax (出海合规发票税制计算)', () => { + describe('2. calculateInternationalTax (国际合规发票税制计算)', () => { it('阿联酋企业或个人消费应按 5% VAT 计税', () => { const res = calculateInternationalTax({ amount: 1000, @@ -50,7 +50,7 @@ describe('Point 57 (P1): 跨境多币种汇率换算与出海发票税制计算' expect(res.taxName).toBe('UAE VAT'); }); - it('欧盟企业间跨境 B2B (Reverse Charge) 应适用反向征税 0% VAT', () => { + it('欧盟企业间跨国 B2B (Reverse Charge) 应适用反向征税 0% VAT', () => { const res = calculateInternationalTax({ amount: 2000, currency: 'EUR', diff --git a/backend/test/modules/team/data-scope.test.ts b/backend/test/modules/team/data-scope.test.ts index 5f76b8d..36aa4b4 100644 --- a/backend/test/modules/team/data-scope.test.ts +++ b/backend/test/modules/team/data-scope.test.ts @@ -5,7 +5,7 @@ import { assertLeadAccessPermission, } from '../../../src/modules/team/service/data-scope'; -describe('Point 52: 出海多租户团队成员细粒度数据隔离与销售管辖范围保护', () => { +describe('Point 52: 多租户团队成员细粒度数据隔离与销售管辖范围保护', () => { let service: TeamDataScopeService; beforeEach(() => { diff --git a/backend/test/modules/team/finance-analytics.test.ts b/backend/test/modules/team/finance-analytics.test.ts index 558a1a6..7cfa51d 100644 --- a/backend/test/modules/team/finance-analytics.test.ts +++ b/backend/test/modules/team/finance-analytics.test.ts @@ -44,7 +44,7 @@ describe('【财务与运营新账统计】TeamFinanceAnalyticsService 单元测 expect(Array.isArray(result.planDistribution)).toBe(true); expect(result.planDistribution.some((item) => item.plan === 'pro')).toBe(true); - // 出海区域分布 + // 国际区域分布 expect(Array.isArray(result.regionalRevenue)).toBe(true); // 流水明细 diff --git a/backend/test/modules/team/handover.test.ts b/backend/test/modules/team/handover.test.ts index 8b86667..ccda00a 100644 --- a/backend/test/modules/team/handover.test.ts +++ b/backend/test/modules/team/handover.test.ts @@ -103,7 +103,7 @@ describe('Point 19: 离职资产交接工单流程与历史线索迁移 单元 lead1.id = 1; lead1.userId = 1001; lead1.cardId = 101; - lead1.name = '重点出海客户 A'; + lead1.name = '重点合作客户 A'; lead1.isPool = false; lead1.status = 'contacted'; leads.push(lead1); diff --git a/backend/test/modules/team/live-rank.test.ts b/backend/test/modules/team/live-rank.test.ts index 2cef817..424cc80 100644 --- a/backend/test/modules/team/live-rank.test.ts +++ b/backend/test/modules/team/live-rank.test.ts @@ -91,7 +91,7 @@ describe('Point 28: 展会现场团队获客实时排行榜大屏广播 (Live Ra u1.id = 101; u1.name = 'Alex Rivera'; u1.tenantId = 1; - u1.departmentName = '出海一队'; + u1.departmentName = '国际业务一队'; const c1 = new CardInfoEntity(); c1.id = 1; @@ -103,7 +103,7 @@ describe('Point 28: 展会现场团队获客实时排行榜大屏广播 (Live Ra u2.id = 102; u2.name = 'Sarah Wang'; u2.tenantId = 1; - u2.departmentName = '出海二队'; + u2.departmentName = '国际业务二队'; const c2 = new CardInfoEntity(); c2.id = 2; diff --git a/backend/test/modules/team/members.test.ts b/backend/test/modules/team/members.test.ts index 8e7286f..5b42b9d 100644 --- a/backend/test/modules/team/members.test.ts +++ b/backend/test/modules/team/members.test.ts @@ -47,7 +47,7 @@ describe('Point 17: 后台组织架构、部门树与成员名片分配 (Team Me users = []; cards = []; - // 预置部门数据:总部 (1) -> 研发中心 (2) -> 前端组 (3); 总部 (1) -> 销售出海中心 (4) + // 预置部门数据:总部 (1) -> 研发中心 (2) -> 前端组 (3); 总部 (1) -> 国际业务拓展部 (4) const d1 = new BaseSysDepartmentEntity(); d1.id = 1; d1.name = '全球商务总部'; @@ -56,7 +56,7 @@ describe('Point 17: 后台组织架构、部门树与成员名片分配 (Team Me const d2 = new BaseSysDepartmentEntity(); d2.id = 2; - d2.name = '海外研发中心'; + d2.name = '国际研发中心'; d2.parentId = 1; d2.orderNum = 10; @@ -68,7 +68,7 @@ describe('Point 17: 后台组织架构、部门树与成员名片分配 (Team Me const d4 = new BaseSysDepartmentEntity(); d4.id = 4; - d4.name = '出海业务拓展部'; + d4.name = '国际业务拓展部'; d4.parentId = 1; d4.orderNum = 20; @@ -201,7 +201,7 @@ describe('Point 17: 后台组织架构、部门树与成员名片分配 (Team Me expect(root.title).toBe('全球商务总部'); expect(root.children?.length).toBe(2); - // children 排序应为 海外研发中心 (orderNum: 10) 和 出海业务拓展部 (orderNum: 20) + // children 排序应为 国际研发中心 (orderNum: 10) 和 国际业务拓展部 (orderNum: 20) expect(root.children?.[0].id).toBe(2); expect(root.children?.[0].children?.length).toBe(1); // 前端组 (3) expect(root.children?.[0].children?.[0].id).toBe(3); @@ -211,7 +211,7 @@ describe('Point 17: 后台组织架构、部门树与成员名片分配 (Team Me // 人数统计验证 expect(root.memberCount).toBe(1); // u1 在总部 expect(root.children?.[0].children?.[0].memberCount).toBe(1); // u2 在前端组 - expect(root.children?.[1].memberCount).toBe(1); // u3 在出海业务部 + expect(root.children?.[1].memberCount).toBe(1); // u3 在国际业务部 }); it('2.2 addDepartment 校验部门名称必填并成功入库', async () => { diff --git a/backend/test/mpuniapp/article-detail.test.ts b/backend/test/mpuniapp/article-detail.test.ts index e12dbc9..1dc0d46 100644 --- a/backend/test/mpuniapp/article-detail.test.ts +++ b/backend/test/mpuniapp/article-detail.test.ts @@ -34,6 +34,15 @@ type ArticleDetailState = { description: string; image: string; }; + authorName?: string; + authorTitle?: string; + authorCompany?: string; + authorAvatar?: string; + readTimeMinutes?: number; + summaryText?: string; + scrollDepth?: number; + enterTime?: number; + visitorId?: string; }; type ArticleShareFields = { @@ -65,6 +74,15 @@ type ArticleDetailModule = { getArticleShareFields: (state: ArticleDetailState, pageUrl: string) => ArticleShareFields; loadArticleDetail: (state: ArticleDetailState) => Promise; copyArticleShareLink: (state: ArticleDetailState, pageUrl?: string) => boolean; + resolveArticleDetailText: (key: string, locale?: string) => string; + updateArticleScrollDepth: ( + state: ArticleDetailState, + scrollTop: number, + scrollHeight: number, + windowHeight: number + ) => number; + reportArticleStay: (state: ArticleDetailState, scrollDepth?: number) => Promise; + navToAuthorPortal: (state: ArticleDetailState) => void; }; type UniToastOption = { @@ -453,4 +471,112 @@ describe('OmniCard AI 用户端文章详情业务逻辑测试 (useArticleDetail. expect(toastCalls.length).toBeGreaterThan(0); }); }); + + describe('4. 专栏作者微展台与核心摘要导读', () => { + it('4.1 加载文章时能一并解析名片作者信息与核心摘要', async () => { + const { + createArticleDetailState, + applyArticleDetailOptions, + loadArticleDetail, + } = loadUseArticleDetail(); + + const state = createArticleDetailState(); + applyArticleDetailOptions(state, { cardId: 'card-101', blockId: '3' }); + await loadArticleDetail(state); + + expect(state.authorName).toBe('Alex Vance'); + expect(state.authorTitle).toBe('Managing Director & Global Partner'); + expect(state.authorCompany).toBe('Nexus Tech Global Ltd'); + expect(state.readTimeMinutes).toBeGreaterThanOrEqual(1); + expect(state.summaryText?.length).toBeGreaterThan(0); + }); + }); + + describe('5. 阅读滑屏深度计算与雷达停留时长上报', () => { + it('5.1 updateArticleScrollDepth 正确计算并单调递增滑屏百分比', () => { + const { createArticleDetailState, updateArticleScrollDepth } = loadUseArticleDetail(); + const state = createArticleDetailState(); + + expect(state.scrollDepth).toBe(0); + // 顶部 + updateArticleScrollDepth(state, 0, 2000, 600); + expect(state.scrollDepth).toBe(0); + + // 中间 (scrollTop = 700 / maxScroll = 1400 => 50%) + updateArticleScrollDepth(state, 700, 2000, 600); + expect(state.scrollDepth).toBe(50); + + // 向上回滚时保持最大深度 + updateArticleScrollDepth(state, 200, 2000, 600); + expect(state.scrollDepth).toBe(50); + + // 触底 (100%) + updateArticleScrollDepth(state, 1400, 2000, 600); + expect(state.scrollDepth).toBe(100); + }); + + it('5.2 reportArticleStay 将停留时长与阅读深度正确上报至雷达', async () => { + const { + createArticleDetailState, + applyArticleDetailOptions, + loadArticleDetail, + reportArticleStay, + } = loadUseArticleDetail(); + + const state = createArticleDetailState(); + applyArticleDetailOptions(state, { cardId: 'card-101', blockId: '3' }); + await loadArticleDetail(state); + + mockRequestResponse = { + statusCode: 200, + data: { + code: 1000, + message: 'success', + data: { + id: 'track-1', + cardId: 'card-101', + visitorId: 'vis-test', + touchType: 'link', + stayDurationMs: 1200, + scrollDepth: 75, + viewedBlocks: ['3'], + }, + }, + }; + + state.enterTime = Date.now() - 1500; + state.scrollDepth = 75; + + await reportArticleStay(state, 75); + + expect(lastRequestOptions?.url).toContain('/open/crm/radar/track'); + const body = lastRequestOptions?.data as Record; + expect(body.cardId).toBe('card-101'); + expect(body.touchType).toBe('link'); + expect(body.scrollDepth).toBe(75); + expect(body.viewedBlocks).toEqual(['3']); + }); + }); + + describe('6. 多语言本地化文案体系', () => { + it('6.1 覆盖 zh-CN, en-US, ar-SA, es-ES 等语种的核心文案', () => { + const { resolveArticleDetailText } = loadUseArticleDetail(); + + expect(resolveArticleDetailText('kicker', 'zh-CN')).toBe('深度洞见'); + expect(resolveArticleDetailText('summary', 'zh-CN')).toBe('核心导读'); + expect(resolveArticleDetailText('author', 'zh-CN')).toBe('专栏作者'); + expect(resolveArticleDetailText('viewPortal', 'zh-CN')).toBe('查看微官网'); + + expect(resolveArticleDetailText('kicker', 'en-US')).toBe('Thought Leadership'); + expect(resolveArticleDetailText('summary', 'en-US')).toBe('Key Takeaways'); + expect(resolveArticleDetailText('author', 'en-US')).toBe('Author'); + expect(resolveArticleDetailText('viewPortal', 'en-US')).toBe('View Profile'); + + expect(resolveArticleDetailText('kicker', 'ar-SA')).toBe('رؤى متعمقة'); + expect(resolveArticleDetailText('summary', 'ar-SA')).toBe('أبرز النقاط'); + + expect(resolveArticleDetailText('kicker', 'es-ES')).toBe('Perspectivas'); + expect(resolveArticleDetailText('summary', 'es-ES')).toBe('Puntos clave'); + }); + }); }); diff --git a/backend/test/mpuniapp/case-detail.test.ts b/backend/test/mpuniapp/case-detail.test.ts index 3dedf1e..1bcb47e 100644 --- a/backend/test/mpuniapp/case-detail.test.ts +++ b/backend/test/mpuniapp/case-detail.test.ts @@ -47,6 +47,12 @@ type MockBentoBlock = { metrics?: CaseKpi[]; pdfUrl?: string; youtubeUrl?: string; + client?: string; + industry?: string; + region?: string; + challenge?: string; + solution?: string; + [key: string]: unknown; }; }; @@ -89,6 +95,8 @@ type UseCaseDetailModule = { buildExchangePath: (cardId: string) => string; openCaseExchange: (cardId: string) => void; goCaseBack: () => void; + copyCaseExternalLink: (url: string) => boolean; + resolveCaseDetailText: (key: string) => string; }; function loadUseCaseDetail(): UseCaseDetailModule { @@ -371,4 +379,72 @@ describe('OmniCard AI 用户端案例详情业务逻辑测试 (useCaseDetail.uts expect(navigateBackCount).toBe(1); }); }); + + describe('5. Bento Grid 模块化结构化数据与国际化解析', () => { + it('5.1 支持从 metaJson 解析 client, industry, region, challenge, solution', () => { + const { createCaseDetailState, applyCaseDetailOptions, applyPortalCase } = + loadUseCaseDetail(); + const card = buildMockPortal(); + card.bentoBlocks[1].metaJson = { + client: 'Global Logistics Corp', + industry: 'Supply Chain IoT', + region: 'Middle East & North Africa', + challenge: 'High latency in cross-border tracking and data silos.', + solution: 'Deployed dual-frequency NFC sensors and cloud-native mesh.', + kpis: [{ label: 'On-time Delivery', value: '99.8%' }], + }; + + const state = createCaseDetailState(); + applyCaseDetailOptions(state, { cardId: 'card-101', blockId: '2' }); + applyPortalCase(state, card); + + expect((state as any).client).toBe('Global Logistics Corp'); + expect((state as any).industry).toBe('Supply Chain IoT'); + expect((state as any).region).toBe('Middle East & North Africa'); + expect((state as any).challenge).toBe( + 'High latency in cross-border tracking and data silos.' + ); + expect((state as any).solution).toBe( + 'Deployed dual-frequency NFC sensors and cloud-native mesh.' + ); + expect(state.kpis).toEqual([{ label: 'On-time Delivery', value: '99.8%' }]); + }); + + it('5.2 resolveCaseDetailText 正确解析新增的 Bento 模组词条', () => { + const { resolveCaseDetailText } = loadUseCaseDetail(); + + expect(resolveCaseDetailText('badge')).toBeDefined(); + expect(resolveCaseDetailText('overview')).toBeDefined(); + expect(resolveCaseDetailText('challenge')).toBeDefined(); + expect(resolveCaseDetailText('solution')).toBeDefined(); + expect(resolveCaseDetailText('kpis')).toBeDefined(); + expect(resolveCaseDetailText('resources')).toBeDefined(); + expect(resolveCaseDetailText('pdfAction')).toBeDefined(); + expect(resolveCaseDetailText('videoAction')).toBeDefined(); + }); + + it('5.3 copyCaseExternalLink 触发 uni.setClipboardData 与 Toast 提示', () => { + let clipboardText = ''; + let toastCalled = false; + (global as { uni?: Record }).uni = { + setClipboardData: (opts: { data: string; success?: () => void }) => { + clipboardText = opts.data; + opts.success?.(); + }, + showToast: () => { + toastCalled = true; + }, + }; + + const { copyCaseExternalLink } = loadUseCaseDetail(); + const res = copyCaseExternalLink('https://cdn.omnicard.ai/doc.pdf'); + + expect(res).toBe(true); + expect(clipboardText).toBe('https://cdn.omnicard.ai/doc.pdf'); + expect(toastCalled).toBe(true); + + const emptyRes = copyCaseExternalLink(''); + expect(emptyRes).toBe(false); + }); + }); }); diff --git a/backend/test/mpuniapp/deeplink-router.test.ts b/backend/test/mpuniapp/deeplink-router.test.ts index 3d1c00e..b5766fb 100644 --- a/backend/test/mpuniapp/deeplink-router.test.ts +++ b/backend/test/mpuniapp/deeplink-router.test.ts @@ -3,7 +3,7 @@ * 严格遵循 TDD 测试驱动规范 */ -describe('Point 56: 移动端 App / H5 出海名片 Universal Link 深度链接路由分发解析器', () => { +describe('Point 56: 移动端 App / H5 智能数字名片 Universal Link 深度链接路由分发解析器', () => { let router: any; beforeEach(() => { diff --git a/backend/test/mpuniapp/i18n-rtl.test.ts b/backend/test/mpuniapp/i18n-rtl.test.ts index 1ea091c..cc33878 100644 --- a/backend/test/mpuniapp/i18n-rtl.test.ts +++ b/backend/test/mpuniapp/i18n-rtl.test.ts @@ -1,6 +1,6 @@ /** * OmniCard AI (CRMmp) - 访客端多语言动态切换与 RTL 镜像契约测试 (TDD) - * 严格按照 AGENTS.md 与出海国际化 WCAG 标准规范 + * 严格按照 AGENTS.md 与国际化 WCAG 标准规范 */ import * as fs from 'fs'; @@ -173,7 +173,7 @@ describe('Point 04: 访客端多语言动态切换与 RTL 镜像无感适配', ( }); describe('4. 默认 GeoIP 语言回退 (resolveGeoLocale)', () => { - it('根据出海访客的国家码准确映射主流语言或回退 en-US', () => { + it('根据访客的国家码准确映射主流语言或回退 en-US', () => { const { resolveGeoLocale } = require(LOCALE_UTS) as { resolveGeoLocale: (countryCode?: string, fallback?: string) => string; }; diff --git a/backend/test/mpuniapp/portal-cache.test.ts b/backend/test/mpuniapp/portal-cache.test.ts index c6944b4..eb0be35 100644 --- a/backend/test/mpuniapp/portal-cache.test.ts +++ b/backend/test/mpuniapp/portal-cache.test.ts @@ -1,6 +1,6 @@ /** * OmniCard AI (CRMmp) - 移动微官网离线 LocalStorage 极速预加载与骨架屏单测套件 (TDD) - * 严格遵循 AGENTS.md 规范与出海高性能架构契约 + * 严格遵循 AGENTS.md 规范与高性能架构契约 */ export interface MockBentoBlock { diff --git a/backend/test/mpuniapp/tabbar-config.test.ts b/backend/test/mpuniapp/tabbar-config.test.ts index 5c16151..c6cebeb 100644 --- a/backend/test/mpuniapp/tabbar-config.test.ts +++ b/backend/test/mpuniapp/tabbar-config.test.ts @@ -19,7 +19,7 @@ describe('uni-app 框架自带原生 tabBar 规范测试 (TDD Red -> Green)', () // 关键规范:tabBar.list[0].pagePath 必须与 pages[0].path 一致,原生 tabBar 才能在首屏正常加载 expect(config.tabBar.list[0].pagePath).toBe(config.pages[0].path); - expect(config.tabBar.list[0].text).toBe('官网'); + expect(config.tabBar.list[0].text).toBe('名片'); // 校验颜色 Token expect(config.tabBar.color.toLowerCase()).toBe('#667085'); diff --git a/docs/PRODUCT_REQUIREMENTS_DOCUMENT.md b/docs/PRODUCT_REQUIREMENTS_DOCUMENT.md index 37ef5f3..f985df2 100644 --- a/docs/PRODUCT_REQUIREMENTS_DOCUMENT.md +++ b/docs/PRODUCT_REQUIREMENTS_DOCUMENT.md @@ -6,17 +6,17 @@ ### 一、 产品战略定位与商业愿景 (Product Vision & Strategy) #### 1.1 产品定位 -**OmniCard AI** 是一款面向**全球出海商务、跨国企业团队、自由职业者及国际会展场景**的新一代 **AI 驱动型智能数字名片 + 移动微官网 (Bento Micro-Site) + 潜客雷达 CRM + 实体 NFC 智能卡管家** 一体化 SaaS 平台。 +**OmniCard AI** 是一款面向**全球现代商务、跨国企业团队、自由职业者及国际会展场景**的新一代 **AI 驱动型智能数字名片 + 移动微官网 (Bento Micro-Site) + 潜客雷达 CRM + 实体 NFC 智能卡管家** 一体化 SaaS 平台。 -它不仅是一张数字名片,更是出海企业与个人的**微型官网**与**24小时自动获客线索引擎**: +它不仅是一张数字名片,更是全球企业与个人的**微型官网**与**24小时自动获客线索引擎**: - **用户端与微官网(User Client: App & H5)**:秒级首屏响应、无缝多语言自适应(含 RTL 排版)、Bento Grid 现代卡片美学、名片内置 24/7 AI 顾问对话、NFC 实体卡秒级碰触与离线 vCard 导入。 -- **管理后台前端(Admin Web)**:可视化名片搭建、AI 批量出海润色、NFC 硬件全生命周期下发管控、访客轨迹透视与 CRM 商机漏斗、多租户企业品牌管控。 +- **管理后台前端(Admin Web)**:可视化名片搭建、AI 批量本地化润色、NFC 硬件全生命周期下发管控、访客轨迹透视与 CRM 商机漏斗、多租户企业品牌管控。 - **后端中枢服务(Backend API)**:基于 Cool-Admin (Node.js) 模块化架构,提供高性能企业级数据中枢、安全 RBAC 鉴权、微秒级雷达日志追踪及官方插件集成。 #### 1.2 核心用户画像 (Target Personas) -1. **外贸/跨境出海销售与参展团队**:常年参与海外展会(CES、广交会、迪拜 GITEX 等),需要跨语种交流,通过手机或 NFC 实体卡一碰即达,零摩擦留存客户线索。 +1. **全球商务销售与国际参展团队**:常年参与国际知名展会(CES、IFA、迪拜 GITEX 等),需要跨语种交流,通过手机或 NFC 实体卡一碰即达,零摩擦留存客户线索。 2. **跨国商务顾问/自由专家 (Consultants & Creators)**:需要集个人品牌展示、过往案例 (Portfolio)、干货博客文章 (Insights) 与预约排期于一体的移动端便当盒微官网。 -3. **出海科技企业 / 品牌出海团队**:需要统一管理数十到数百名商务人员的名片物料,统一品牌 VI 形象,并集中追踪团队名片带来的全球线索转化与公海池沉淀。 +3. **全球化科技企业 / 国际品牌团队**:需要统一管理数十到数百名商务人员的名片物料,统一品牌 VI 形象,并集中追踪团队名片带来的全球线索转化与公海池沉淀。 --- @@ -44,7 +44,7 @@ flowchart TD subgraph 后端服务_Cool-Admin [Backend API: Node.js Midway.js] CardModule[名片与微官网模块 modules/card] CRMModule[访客雷达与商机模块 modules/crm] - AIModule[AI 对话与出海润色模块 modules/ai] + AIModule[AI 对话与本地化润色模块 modules/ai] NFCModule[NFC 设备生命周期模块 modules/nfc] TeamModule[多租户与团队管理 modules/team] PluginEco[官方生态: OSS存储 / Redis缓存 / Task调度] @@ -121,7 +121,7 @@ flowchart TD #### 3.4 模块四:轻量级客户关系 CRM 与访客雷达 (Lead CRM & Radar) 1. **实时访客雷达 (Visitor Radar)**: - - 海外客户在展会或拜访后点开名片,销售 App 收到即时推送(Push Notification): + - 国际客商在展会或拜访后点开名片,销售 App 收到即时推送(Push Notification): *“来自阿联酋迪拜的访客正在浏览您的名片,已停留 3 分钟,正在重点查看‘新能源储能项目案例’!”* 2. **无摩擦线索沉淀 (Frictionless Lead Exchange)**: - “互换名片”弹窗:访客快速输入姓名、公司、邮箱/WhatsApp,或拍照上传纸质名片(OCR 识别入库)。 @@ -174,14 +174,14 @@ admin-web/ (Arco Pro 架构) | 模块名称 | 页面路由路径 | 页面中文名称 | 核心功能点与交互规范 | 推荐复用 Arco 官方组件 | | :--- | :--- | :--- | :--- | :--- | | **工作台** | `/dashboard/workplace` | 商务概览工作台 | 核心 KPI 概览(触碰量、浏览量、新线索、成交转化);今日实时雷达动态流;快捷入口(新建名片、发卡、线索导出)。 | `Grid`, `Statistic`, `Card`, `Timeline`, `Button` | -| **数据大屏** | `/dashboard/radar-map` | 全球访客雷达大屏 | 全球出海访客分布地理热力图(国家/地区占比);各名片停留时长与访问峰值走势图;出海客户语言/设备终端分析。 | `Card`, `DatePicker.RangePicker`, `Spin`, `Empty` | +| **数据大屏** | `/dashboard/radar-map` | 全球访客雷达大屏 | 全球访客分布地理热力图(国家/地区占比);各名片停留时长与访问峰值走势图;国际客户语言/设备终端分析。 | `Card`, `DatePicker.RangePicker`, `Spin`, `Empty` | | **名片管理** | `/card/list` | 数字名片资产库 | 名片表格与卡片双视图切换;发布/草稿状态;一键复制短链;动态防伪二维码下载(PNG/SVG/PDF打印格式);批量导出 vCard。 | `Table`, `Radio.Group`, `Badge`, `Popconfirm`, `Dropdown` | | **微官网搭建** | `/card/builder/:id` | Bento 可视化搭建器 | **左侧组件库**:Hero身份、社媒矩阵、案例卡、文章卡、视频音频、留资表单;
**中间画布**:手机端视口实时响应模拟(支持浅/深色、RTL阿语实时镜像);
**右侧属性配置**:模块属性、多语言文案切换、Bento 跨度调整 (1x1, 1x2, 2x1, 2x2)。 | `Layout`, `Tabs`, `Form`, `Slider`, `Upload`, `Switch`, `ColorPicker` | -| **案例展厅** | `/card/cases` | 案例作品集管理 | 出海成功案例列表;多媒体素材管理(图片瀑布流、YouTube/Vimeo 链接);方案 PDF 白皮书上传;成果 KPI 标签打标。 | `Table`, `Modal`, `Form.Item`, `Upload`, `Tag` | +| **案例展厅** | `/card/cases` | 案例作品集管理 | 标杆成功案例列表;多媒体素材管理(图片瀑布流、YouTube/Vimeo 链接);方案 PDF 白皮书上传;成果 KPI 标签打标。 | `Table`, `Modal`, `Form.Item`, `Upload`, `Tag` | | **文章动态** | `/card/articles` | 博客与动态发布 | 富文本文章发布;移动端排版预览;SEO 与 OpenGraph 社交预览卡(WhatsApp/LinkedIn)图片与描述自动生成。 | `Table`, `Input`, `Drawer`, `Alert` | -| **AI 智能客服** | `/ai/concierge` | 名片 AI 顾问调优 | 数字人/顾问人设设定(外贸专家、商务代表等);欢迎语与引导词设置;高频 FAQ 手动配置;自动留资触发策略设置。 | `Form`, `Input.TextArea`, `Slider`, `Radio.Group`, `Card` | +| **AI 智能客服** | `/ai/concierge` | 名片 AI 顾问调优 | 数字人/顾问人设设定(国际贸易顾问、商务代表等);欢迎语与引导词设置;高频 FAQ 手动配置;自动留资触发策略设置。 | `Form`, `Input.TextArea`, `Slider`, `Radio.Group`, `Card` | | **知识库 RAG** | `/ai/knowledge` | 知识库与向量切片 | 知识库文件批量上传(PDF/Docx/TXT/MD);URL 网站智能抓取;向量化进度条与分块切片预览;召回命中率调试沙盒。 | `Upload.Dragger`, `Progress`, `Table`, `Drawer`, `Input.Search` | -| **出海润色** | `/ai/translation` | 多语言 AI 润色中心 | 批量多语言内容润色与地道化翻译;外贸专有名词与行业术语库 (Glossary) 维护;口吻风格切换(稳重/直接/故事感)。 | `Table`, `Select`, `Input`, `Button`, `Space` | +| **智能润色** | `/ai/translation` | 多语言 AI 润色中心 | 批量多语言内容润色与地道化翻译;跨国专有名词与行业术语库 (Glossary) 维护;口吻风格切换(稳重/直接/故事感)。 | `Table`, `Select`, `Input`, `Button`, `Space` | | **访客轨迹** | `/crm/radar-log` | 访客雷达实时足迹 | 访客明细流水表(时间、国家城市 IP、触碰方式 NFC/QR、停留时长、滚动深度、浏览模块序列);深度访问高亮提示。 | `Table`, `Tag`, `Badge`, `Tooltip`, `Pagination` | | **线索管理** | `/crm/leads` | 潜客线索关系库 | 客户线索全量列表;意向度等级打标 (`Hot` / `Warm` / `Cold`);跟进人分配;批量导出 Excel;一键推送到外部 CRM。 | `Table`, `Select`, `Tag`, `Modal`, `Drawer`, `Message` | | **商机看板** | `/crm/pipeline` | 销售商机流转看板 | 泳道式阶段看板(新留资 ➔ 首次接触 ➔ 方案对接 ➔ 赢单/输单);卡片拖拽流转;商机总金额与转化率统计。 | `Grid`, `Card`, `Avatar`, `Tag`, `Typography.Text` | @@ -214,7 +214,7 @@ user-client/ (uni-app x 目录架构) │ │ │ ├── radar/ # 移动访客雷达 │ │ │ ├── leads/ # 移动 CRM 与商机跟进 │ │ │ ├── nfc/ # 手机 NFC 贴卡激活与场景管理 -│ │ │ └── profile/ # 个人设置与出海展会战报 +│ │ │ └── profile/ # 个人设置与展会活动战报 │ │ └── common/ # 公共认证、404 与协议页面 ``` @@ -223,20 +223,20 @@ user-client/ (uni-app x 目录架构) | 端分类 | 页面路由路径 | 页面中文名称 | 核心功能点与交互规范 | 推荐复用原生/uni-ui组件 | | :--- | :--- | :--- | :--- | :--- | | **访客端** | `pages/portal/index` | **Bento 移动微官网主页** | **首屏秒开 (<500ms)**;自适应国际化与阿语 RTL;
1. **Hero 身份区**:头像、姓名、头衔、公司、时区、名字语音发音条;
2. **快捷动作条**:一键呼叫 WhatsApp / LinkedIn / 电话;
3. **常驻悬浮 CTA**:一键保存到手机通讯录 (vCard 3.0/4.0);
4. **Bento 瀑布流**:精选案例展厅、深度文章、服务定价卡;
5. **右下角悬浮**:24/7 AI 对话顾问唤起气泡。 | ``, ``, ``, ``, `uni-popup`, `uni-badge` | -| **访客端** | `pages/portal/case-detail` | 案例作品集详情页 | 出海案例深度展示;多图画廊轮播;方案成效指标数据卡 (KPI Cards);嵌入式 PDF 白皮书在线预览;一键咨询按钮。 | ``, ``, ``, `