diff --git a/admin/src/types/omni.ts b/admin/src/types/omni.ts new file mode 100644 index 0000000..7240d11 --- /dev/null +++ b/admin/src/types/omni.ts @@ -0,0 +1,254 @@ +/** + * OmniCard AI (CRMmp) - 全平台跨端核心数据契约与 TypeScript 类型体系 + * + * 遵循出海 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。 + * 供 后端服务 (Cool-Admin) 与 管理后台 (Arco Pro React) 共享镜像复用。 + * 严格类型安全,杜绝 any。 + */ + +// ========================================== +// 1. 名片与微官网契约 (Card Profile & Bento) +// ========================================== + +/** + * 名片生命周期状态 + */ +export type CardStatus = 'draft' | 'published' | 'archived'; + +/** + * CardProfile - 名片基础资料契约 + */ +export interface CardProfile { + /** 名片主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 关联用户账号 ID */ + userId: string | number; + /** 姓名 */ + name: string; + /** 职业头衔/职位 */ + title: string; + /** 所属企业/组织名称 */ + company: string; + /** 个人签名 / 品牌 Slogan */ + slogan?: string; + /** 头像 / 形象照 URL */ + avatarUrl?: string; + /** 国际电话号码 (E.164 规范) */ + phone?: string; + /** 电子邮箱 */ + email?: string; + /** WhatsApp 国际直达账号/号码 */ + whatsapp?: string; + /** LinkedIn 个人/企业主页链接 */ + linkedin?: string; + /** 所在地时区 (如 'UTC+8', 'America/New_York') */ + timezone?: string; + /** 姓名双语发音音频 URL (RFC 6350 语音扩展) */ + audioPronunciationUrl?: string; + /** 名片状态 */ + status: CardStatus; + /** 主题样式代码 (如 'corporate_navy', 'tech_dark', 'gold_luxe') */ + themeCode?: string; +} + +/** + * Bento Grid 网格块类型 + */ +export type BentoBlockType = 'hero' | 'case' | 'article' | 'social' | 'video' | 'form'; + +/** + * Bento Grid 列跨度 (1列 / 2列满宽) + */ +export type BentoColSpan = 1 | 2; + +/** + * Bento Grid 行跨度 (1行 / 2行加高) + */ +export type BentoRowSpan = 1 | 2; + +/** + * BentoBlock - Bento Grid 网格块契约 + */ +export interface BentoBlock { + /** 网格块主键 ID */ + id: string | number; + /** 所属名片 ID */ + cardId: string | number; + /** 组件类型 */ + type: BentoBlockType; + /** 栅格跨列 (1 | 2) */ + colSpan: BentoColSpan; + /** 栅格跨行 (1 | 2) */ + rowSpan: BentoRowSpan; + /** 排序序号 (从小到大排列) */ + sortOrder: number; + /** 块标题 */ + title?: string; + /** 块正文 / 描述 / 嵌入内容 */ + content?: string; + /** 多媒体资源 URL (图片/视频封面/音频) */ + mediaUrl?: string; + /** 目标跳转外链 URL */ + linkUrl?: string; + /** 扩展元数据 JSON (支持序列化字符串或键值对字典,禁止 any) */ + metaJson?: string | Record; +} + +// ========================================== +// 2. 访客雷达与轻量级出海 CRM 契约 (Radar & Lead) +// ========================================== + +/** + * 潜客线索全生命周期跟进状态 + */ +export type LeadStatus = 'new' | 'contacted' | 'hot' | 'won' | 'archived'; + +/** + * 线索触达来源渠道 + */ +export type LeadSource = 'nfc' | 'qr' | 'link' | 'ai_chat'; + +/** + * LeadEntity - 潜客线索关系模型契约 + */ +export interface LeadEntity { + /** 线索主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 来源名片 ID */ + cardId: string | number; + /** 访客唯一追踪指纹 ID */ + visitorId: string; + /** 客户姓名 */ + name: string; + /** 客户所属企业名称 */ + company?: string; + /** 联系电话 */ + phone?: string; + /** 电子邮箱 */ + email?: string; + /** 跟进状态 */ + status: LeadStatus; + /** 意向度评分 (0 - 100) */ + intentScore: number; + /** 触达来源途径 */ + source: LeadSource; + /** 跟进备忘录 / 需求备注 */ + notes?: string; +} + +/** + * 访客触碰/打开途径 + */ +export type TouchType = 'nfc' | 'qr' | 'link'; + +/** + * RadarTrack - 实时访客足迹契约 + */ +export interface RadarTrack { + /** 轨迹记录主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 访问的目标名片 ID */ + cardId: string | number; + /** 访客唯一追踪指纹 ID */ + visitorId: string; + /** 访客公网 IP 地址 */ + ip: string; + /** 访客所在国家/地区 */ + country: string; + /** 访客所在城市 */ + city: string; + /** 触碰方式途径 */ + touchType: TouchType; + /** 页面停留时长 (毫秒) */ + stayDurationMs: number; + /** 页面最大滚动深度百分比 (0 - 100) */ + scrollDepth: number; + /** 浏览过的 Bento 组件块标识列表 */ + viewedBlocks: string[]; + /** 访问时间戳 */ + createdAt: string | Date; +} + +// ========================================== +// 3. NFC 实体卡设备台账契约 (NFC Fleet) +// ========================================== + +/** + * NFC 卡片实体物理材质 + */ +export type NFCMaterial = 'wood' | 'metal' | 'pvc'; + +/** + * NFC 硬件设备生命周期状态 + */ +export type NFCStatus = 'active' | 'suspended' | 'unbound'; + +/** + * NFC 场景模式 + */ +export type NFCSceneMode = 'business' | 'social'; + +/** + * NFCRecord - NFC 实体卡设备台账契约 + */ +export interface NFCRecord { + /** 台账主键 ID */ + id: string | number; + /** 硬件唯一识别芯片 UID (如 '04:7A:B2:8F:1C:6D:80') */ + uid: string; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 当前绑定的名片 ID (未绑定时为 null 或空) */ + cardId?: string | number | null; + /** 硬件制造/下发批次号 */ + batchNo: string; + /** 物理材质 */ + material: NFCMaterial; + /** 设备状态 */ + status: NFCStatus; + /** 场景模式 (如白天商务/夜晚社交) */ + sceneMode: NFCSceneMode; + /** 最后一次被触碰扫描时间 */ + lastScannedAt?: string | Date | null; +} + +// ========================================== +// 4. 企业租户与品牌 VI 契约 (Tenant & BrandKit) +// ========================================== + +/** + * 租户订阅商业版本套餐 + */ +export type TenantPlanType = 'free' | 'pro' | 'enterprise'; + +/** + * 品牌 VI 规范套件 + */ +export interface BrandKit { + /** 官方企业 Logo 图标 URL */ + logoUrl: string; + /** 品牌标准主色调 (Hex 颜色代码,如 '#0B5CFF') */ + primaryColor: string; +} + +/** + * TenantProfile - 企业租户及品牌 VI 契约 + */ +export interface TenantProfile { + /** 租户主键 ID */ + id: string | number; + /** 企业/团队官方名称 */ + companyName: string; + /** 商业订阅版本 */ + planType: TenantPlanType; + /** 自定义独立主域名 (如 'me.brand.com') */ + customDomain?: string | null; + /** 统一品牌 VI 套件 */ + brandKit: BrandKit; +} diff --git a/backend/src/comm/types/omni.ts b/backend/src/comm/types/omni.ts new file mode 100644 index 0000000..7240d11 --- /dev/null +++ b/backend/src/comm/types/omni.ts @@ -0,0 +1,254 @@ +/** + * OmniCard AI (CRMmp) - 全平台跨端核心数据契约与 TypeScript 类型体系 + * + * 遵循出海 SaaS 规范、Bento Grid 便当盒美学与 RFC 6350 国际标准。 + * 供 后端服务 (Cool-Admin) 与 管理后台 (Arco Pro React) 共享镜像复用。 + * 严格类型安全,杜绝 any。 + */ + +// ========================================== +// 1. 名片与微官网契约 (Card Profile & Bento) +// ========================================== + +/** + * 名片生命周期状态 + */ +export type CardStatus = 'draft' | 'published' | 'archived'; + +/** + * CardProfile - 名片基础资料契约 + */ +export interface CardProfile { + /** 名片主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 关联用户账号 ID */ + userId: string | number; + /** 姓名 */ + name: string; + /** 职业头衔/职位 */ + title: string; + /** 所属企业/组织名称 */ + company: string; + /** 个人签名 / 品牌 Slogan */ + slogan?: string; + /** 头像 / 形象照 URL */ + avatarUrl?: string; + /** 国际电话号码 (E.164 规范) */ + phone?: string; + /** 电子邮箱 */ + email?: string; + /** WhatsApp 国际直达账号/号码 */ + whatsapp?: string; + /** LinkedIn 个人/企业主页链接 */ + linkedin?: string; + /** 所在地时区 (如 'UTC+8', 'America/New_York') */ + timezone?: string; + /** 姓名双语发音音频 URL (RFC 6350 语音扩展) */ + audioPronunciationUrl?: string; + /** 名片状态 */ + status: CardStatus; + /** 主题样式代码 (如 'corporate_navy', 'tech_dark', 'gold_luxe') */ + themeCode?: string; +} + +/** + * Bento Grid 网格块类型 + */ +export type BentoBlockType = 'hero' | 'case' | 'article' | 'social' | 'video' | 'form'; + +/** + * Bento Grid 列跨度 (1列 / 2列满宽) + */ +export type BentoColSpan = 1 | 2; + +/** + * Bento Grid 行跨度 (1行 / 2行加高) + */ +export type BentoRowSpan = 1 | 2; + +/** + * BentoBlock - Bento Grid 网格块契约 + */ +export interface BentoBlock { + /** 网格块主键 ID */ + id: string | number; + /** 所属名片 ID */ + cardId: string | number; + /** 组件类型 */ + type: BentoBlockType; + /** 栅格跨列 (1 | 2) */ + colSpan: BentoColSpan; + /** 栅格跨行 (1 | 2) */ + rowSpan: BentoRowSpan; + /** 排序序号 (从小到大排列) */ + sortOrder: number; + /** 块标题 */ + title?: string; + /** 块正文 / 描述 / 嵌入内容 */ + content?: string; + /** 多媒体资源 URL (图片/视频封面/音频) */ + mediaUrl?: string; + /** 目标跳转外链 URL */ + linkUrl?: string; + /** 扩展元数据 JSON (支持序列化字符串或键值对字典,禁止 any) */ + metaJson?: string | Record; +} + +// ========================================== +// 2. 访客雷达与轻量级出海 CRM 契约 (Radar & Lead) +// ========================================== + +/** + * 潜客线索全生命周期跟进状态 + */ +export type LeadStatus = 'new' | 'contacted' | 'hot' | 'won' | 'archived'; + +/** + * 线索触达来源渠道 + */ +export type LeadSource = 'nfc' | 'qr' | 'link' | 'ai_chat'; + +/** + * LeadEntity - 潜客线索关系模型契约 + */ +export interface LeadEntity { + /** 线索主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 来源名片 ID */ + cardId: string | number; + /** 访客唯一追踪指纹 ID */ + visitorId: string; + /** 客户姓名 */ + name: string; + /** 客户所属企业名称 */ + company?: string; + /** 联系电话 */ + phone?: string; + /** 电子邮箱 */ + email?: string; + /** 跟进状态 */ + status: LeadStatus; + /** 意向度评分 (0 - 100) */ + intentScore: number; + /** 触达来源途径 */ + source: LeadSource; + /** 跟进备忘录 / 需求备注 */ + notes?: string; +} + +/** + * 访客触碰/打开途径 + */ +export type TouchType = 'nfc' | 'qr' | 'link'; + +/** + * RadarTrack - 实时访客足迹契约 + */ +export interface RadarTrack { + /** 轨迹记录主键 ID */ + id: string | number; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 访问的目标名片 ID */ + cardId: string | number; + /** 访客唯一追踪指纹 ID */ + visitorId: string; + /** 访客公网 IP 地址 */ + ip: string; + /** 访客所在国家/地区 */ + country: string; + /** 访客所在城市 */ + city: string; + /** 触碰方式途径 */ + touchType: TouchType; + /** 页面停留时长 (毫秒) */ + stayDurationMs: number; + /** 页面最大滚动深度百分比 (0 - 100) */ + scrollDepth: number; + /** 浏览过的 Bento 组件块标识列表 */ + viewedBlocks: string[]; + /** 访问时间戳 */ + createdAt: string | Date; +} + +// ========================================== +// 3. NFC 实体卡设备台账契约 (NFC Fleet) +// ========================================== + +/** + * NFC 卡片实体物理材质 + */ +export type NFCMaterial = 'wood' | 'metal' | 'pvc'; + +/** + * NFC 硬件设备生命周期状态 + */ +export type NFCStatus = 'active' | 'suspended' | 'unbound'; + +/** + * NFC 场景模式 + */ +export type NFCSceneMode = 'business' | 'social'; + +/** + * NFCRecord - NFC 实体卡设备台账契约 + */ +export interface NFCRecord { + /** 台账主键 ID */ + id: string | number; + /** 硬件唯一识别芯片 UID (如 '04:7A:B2:8F:1C:6D:80') */ + uid: string; + /** 所属企业租户 ID */ + tenantId: string | number; + /** 当前绑定的名片 ID (未绑定时为 null 或空) */ + cardId?: string | number | null; + /** 硬件制造/下发批次号 */ + batchNo: string; + /** 物理材质 */ + material: NFCMaterial; + /** 设备状态 */ + status: NFCStatus; + /** 场景模式 (如白天商务/夜晚社交) */ + sceneMode: NFCSceneMode; + /** 最后一次被触碰扫描时间 */ + lastScannedAt?: string | Date | null; +} + +// ========================================== +// 4. 企业租户与品牌 VI 契约 (Tenant & BrandKit) +// ========================================== + +/** + * 租户订阅商业版本套餐 + */ +export type TenantPlanType = 'free' | 'pro' | 'enterprise'; + +/** + * 品牌 VI 规范套件 + */ +export interface BrandKit { + /** 官方企业 Logo 图标 URL */ + logoUrl: string; + /** 品牌标准主色调 (Hex 颜色代码,如 '#0B5CFF') */ + primaryColor: string; +} + +/** + * TenantProfile - 企业租户及品牌 VI 契约 + */ +export interface TenantProfile { + /** 租户主键 ID */ + id: string | number; + /** 企业/团队官方名称 */ + companyName: string; + /** 商业订阅版本 */ + planType: TenantPlanType; + /** 自定义独立主域名 (如 'me.brand.com') */ + customDomain?: string | null; + /** 统一品牌 VI 套件 */ + brandKit: BrandKit; +} diff --git a/backend/test/comm/omni.test.ts b/backend/test/comm/omni.test.ts new file mode 100644 index 0000000..c641e73 --- /dev/null +++ b/backend/test/comm/omni.test.ts @@ -0,0 +1,170 @@ +import { + CardProfile, + CardStatus, + BentoBlock, + BentoBlockType, + BentoColSpan, + BentoRowSpan, + LeadEntity, + LeadStatus, + LeadSource, + RadarTrack, + TouchType, + NFCRecord, + NFCMaterial, + NFCStatus, + NFCSceneMode, + TenantProfile, + TenantPlanType, + BrandKit, +} from '../../src/comm/types/omni'; + +describe('OmniCard AI 核心跨端数据契约与 TypeScript 类型体系测试', () => { + it('应正确支持 CardProfile 契约及其字段与状态枚举', () => { + const cardStatus: CardStatus = 'published'; + const card: CardProfile = { + id: 'card_1001', + tenantId: 'tenant_888', + userId: 'user_001', + name: 'Alex Rivera', + title: 'Global VP of Business Development', + company: 'OmniCard Global Ltd.', + slogan: 'Empowering global trade with AI', + avatarUrl: 'https://cdn.omnicard.ai/avatars/alex.jpg', + phone: '+1-555-892-0199', + email: 'alex@omnicard.ai', + whatsapp: '+15558920199', + linkedin: 'https://linkedin.com/in/alex-rivera-global', + timezone: 'UTC+8', + audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/alex.mp3', + status: cardStatus, + themeCode: 'corporate_navy', + }; + + expect(card.id).toBe('card_1001'); + expect(card.name).toBe('Alex Rivera'); + expect(card.status).toBe('published'); + expect(card.themeCode).toBe('corporate_navy'); + }); + + it('应正确支持 BentoBlock 契约与尺寸、网格类型约束', () => { + const blockType: BentoBlockType = 'hero'; + const colSpan: BentoColSpan = 2; + const rowSpan: BentoRowSpan = 1; + + const block: BentoBlock = { + id: 101, + cardId: 1001, + type: blockType, + colSpan, + rowSpan, + sortOrder: 0, + title: 'Hero Profile', + content: 'Leading AI-driven digital identity across MENA and APAC markets.', + mediaUrl: 'https://cdn.omnicard.ai/media/hero.png', + linkUrl: 'https://omnicard.ai', + metaJson: { + accentColor: '#0B5CFF', + badge: 'Top Speaker', + }, + }; + + expect(block.type).toBe('hero'); + expect(block.colSpan).toBe(2); + expect(block.rowSpan).toBe(1); + expect(typeof block.sortOrder).toBe('number'); + }); + + it('应正确支持 LeadEntity 潜客线索契约与意向评分、状态流转', () => { + const leadStatus: LeadStatus = 'hot'; + const leadSource: LeadSource = 'nfc'; + + const lead: LeadEntity = { + id: 'lead_5001', + tenantId: 'tenant_888', + cardId: 'card_1001', + visitorId: 'vis_ae_9921', + name: 'Tariq Mansour', + company: 'Dubai Energy Holding', + phone: '+971-50-1234567', + email: 'tariq@dubaienergy.ae', + status: leadStatus, + intentScore: 92, + source: leadSource, + notes: 'Met at GITEX 2026. Very interested in enterprise NFC fleet and multi-tenant branding.', + }; + + expect(lead.status).toBe('hot'); + expect(lead.intentScore).toBe(92); + expect(lead.source).toBe('nfc'); + expect(lead.company).toBe('Dubai Energy Holding'); + }); + + it('应正确支持 RadarTrack 实时访客足迹契约与时空数据追踪', () => { + const touch: TouchType = 'qr'; + const now = new Date(); + + const track: RadarTrack = { + id: 'radar_8001', + tenantId: 'tenant_888', + cardId: 'card_1001', + visitorId: 'vis_de_4432', + ip: '194.12.88.4', + country: 'Germany', + city: 'Frankfurt', + touchType: touch, + stayDurationMs: 185000, + scrollDepth: 88, + viewedBlocks: ['hero', 'case_energy', 'article_ai_2026'], + createdAt: now, + }; + + expect(track.touchType).toBe('qr'); + expect(track.country).toBe('Germany'); + expect(track.stayDurationMs).toBeGreaterThan(0); + expect(track.viewedBlocks).toHaveLength(3); + }); + + it('应正确支持 NFCRecord 实体卡设备台账契约与生命周期状态', () => { + const material: NFCMaterial = 'metal'; + const status: NFCStatus = 'active'; + const sceneMode: NFCSceneMode = 'business'; + + const nfcCard: NFCRecord = { + id: 'nfc_9001', + uid: '04:7A:B2:8F:1C:6D:80', + tenantId: 'tenant_888', + cardId: 'card_1001', + batchNo: 'BATCH-2026-Q1-METAL', + material, + status, + sceneMode, + lastScannedAt: '2026-09-20T14:30:00.000Z', + }; + + expect(nfcCard.material).toBe('metal'); + expect(nfcCard.status).toBe('active'); + expect(nfcCard.sceneMode).toBe('business'); + expect(nfcCard.uid).toBe('04:7A:B2:8F:1C:6D:80'); + }); + + it('应正确支持 TenantProfile 企业租户与 BrandKit 品牌 VI 契约', () => { + const planType: TenantPlanType = 'enterprise'; + const brandKit: BrandKit = { + logoUrl: 'https://cdn.omnicard.ai/brands/omnitech-logo.svg', + primaryColor: '#0B5CFF', + }; + + const tenant: TenantProfile = { + id: 'tenant_888', + companyName: 'OmniTech Global Inc.', + planType, + customDomain: 'vip.omnitech.com', + brandKit, + }; + + expect(tenant.planType).toBe('enterprise'); + expect(tenant.brandKit.primaryColor).toBe('#0B5CFF'); + expect(tenant.customDomain).toBe('vip.omnitech.com'); + }); +});