diff --git a/CONTEXT.md b/CONTEXT.md index 54c9dc8..d5d5fe7 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -167,6 +167,7 @@ Novalon 的 6 个核心产品,互为互补关系,常以组合形式出现在 | 方案页删除服务方式区域 | Solutions 列表页"服务方式"区域职责越界(属 Services 层),应删除;替换为方案→服务的轻量关联推荐链接 | ✅ 2026-06-07 确认 | | L3 信任层内容策略 | 初创阶段无真实案例/数据/资质,L3 暂不渲染;组件结构预留,数据就绪后启用。三档策略:有案例→正常展示;有意向→"正在服务中"标签;无数据→不显示 L3。**阶段 0(2026-08-20)已落地**:信任策略从「结果证据」转向「可验证的过程+能力+治理证据」——首页「共创计划」板块(共创进行中/产品内测中/成果授权公开三档如实状态 + 招募 CTA)、关于页资质区如实「建设中」空态 | ✅ 2026-06-07 确认 / 阶段 0 完成 2026-08-20 | | 首页共创计划板块 | 成立 <1 年无真实案例时,以主动板块替代被动标签:`page-copy` 字段 `earlyAccessTitle`/`earlyAccessCtaLabel`/`earlyAccessStatus` 驱动首页「首批客户共创计划」板块,如实呈现共创/内测/授权公开状态并引导转化(成为共创客户 → /contact)。零编造:不虚构客户/数据/资质 | ✅ 2026-08-20 确认 | +| 数字口径 basis 结构强制 | 「每个数字带口径」从个别页自觉升级为结构约束:走共享数字样式渲染的指标(`metrics`/`outcomes`/`dataProofs`)必须声明 `basis: target\|team-history\|verified`,渲染端按 basis 自动附角注,缺失/非法保守回落到最弱的 `target`(「目标口径,非既成结果」)。CMS 侧 `basis` 为 select 字段,真实出路由业务在 admin 录入;`metrics-basis.test.ts` 机械校验 seed 全量条目显式声明、禁止条目无据自称 `verified`,并拦截「源自真实客户案例/经过实战验证」式虚构佐证文案。**注意**:字段定义改动需重跑 seed 同步 `ContentModel.fields` 后 admin 才出现该输入项;渲染端已对未同步数据兜底为 `target` | ✅ 2026-09-20 确认(critique P0 收尾) | | 组件版本统一 | 独立产品迁移后删除 `detail/`(V1),将 `detail-v2/` 重命名为 `detail/`,组件文件名去掉版本后缀(如 `DetailHeroV3` → `DetailHero`) | ✅ 2026-06-07 确认(待 Phase 0 执行) | | 设计定位 | 纯咨询专业风(Accenture + Bain + Porsche 三位一体),水墨元素已移除 | ✅ 2026-07-10 更新(ADR-0006) | | 设计 DNA 框架 | 三维度模型:Design System + Design Style + Visual Effects,基于 Accenture+Bain+Porsche 整合 | ✅ 2026-06-28 确认 | diff --git a/DESIGN.md b/DESIGN.md index 9d5a80e..eace856 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -174,6 +174,7 @@ Novalon 的界面是一份可以逐条审计的经营宣言。它模仿顶级咨 **The Answer-First Rule.** 标题写结论断言,不写问句或口号铺垫;紧随其后的数字条是证据。 **The -0.04em Floor.** 追踪最紧 -0.04em(现用最紧 -0.03em);display 不超过 6rem。 **The 10px Floor.** 10–11px 大写 tracking 微标签(eyebrow、digest、case 分类签)是 Bain 账本风的有意下限,全站保留、不上坡;绝对像素字号不得 <10px,由 `src/lib/constants/font-floor.test.ts` 机械守卫禁止再引入(rem 因 18px 根字号歧义不纳入)。 +**The Declared-Basis Rule.** 凡以大数字样式呈现的指标必须声明口径 `basis: target | team-history | verified`(单一真源 `src/lib/constants/metrics-basis.ts`),渲染端据此自动附角注;缺失或非法一律按最弱的 `target`(「目标口径,非既成结果」)处理,宁可多标注也不裸奔。聚合时取一组数字里最弱的那个。禁止任何「数字源自真实客户案例 / 经过实战验证」式佐证文案——本站 2026-01 成立、零公开客户案例,该措辞由 `metrics-basis.test.ts` 全源码扫描拦截。 ## Layout diff --git a/PRODUCT.md b/PRODUCT.md index 0887c25..c69461d 100644 --- a/PRODUCT.md +++ b/PRODUCT.md @@ -34,3 +34,4 @@ - 字距下限 -0.04em,不得更紧 - 暗黑模式由 `html[data-theme='dark']` 覆盖 CSS 变量驱动,组件不得写死颜色字面量 - 楷体(`--font-brand`)为遗留例外,仅存量页面保留,新页面禁用 +- 大数字必须有口径:指标声明 `basis: target|team-history|verified`,未声明按「目标口径,非既成结果」自动标注;禁止宣称数字源自真实客户案例或实战验证(公司 2026-01 成立、零公开客户案例) diff --git a/README.md b/README.md index 8609972..d6abf81 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ - [x] **结构性文案全站 CMS 化(2026-08-19)**:新增 `page-copy` 内容模型承载全部营销页面章节标题/眉标/描述/CTA/空状态(首页 + 服务/方案/产品/案例/新闻列表页),「CMS 优先 + 硬编码兜底」不白屏;seed 写入 6 条,本地 db:seed 已生效 - [x] **信任证据补齐·阶段 0(成立 <1 年策略,2026-08-20)**:信任策略从「结果证据」转向「可验证的过程 + 能力 + 治理证据」——首页「首批客户共创计划」板块(共创进行中/产品内测中/成果授权公开三档如实状态 + 招募 CTA)+ 关于页资质「建设中」如实空态,零编造 - [x] **生产部署上线(2026-08-20)**:`deploy.sh deploy`(本地 dist → 远端容器重建)+ 生产库 seed(page-copy/methodology 数据)完成;生产目录 Drizzle 残留源码归档清理;站点全路由 200、生产库 Prisma 结构 -- [ ] **信任证据补齐·阶段 1/2(待内测/试点客户素材)**:如实披露「共创中/内测中/已交付」三档 + 经授权客户(脱敏);有交付成果后经授权公示可量化结果(零编造原则) +- [x] **数字口径结构强制(2026-09-20,critique P0 收尾)**:共享数字样式渲染的指标(`metrics`/`outcomes`/`dataProofs`)统一携带 `basis: target|team-history|verified`,渲染端自动附口径角注、未声明保守回落 `target`;CMS 新增 `basis` select 录入字段;删除产品详情页「每一项指标都源自真实客户案例」「经实际场景验证」等虚构佐证并补注角注;`metrics-basis.test.ts` 机械校验声明完整性并拦截虚构佐证禁词 +- [ ] **信任证据补齐·阶段 1/2(待内测/试点客户素材)**:如实披露「共创中/内测中/已交付」三档 + 经授权客户(脱敏);有交付成果后经授权公示可量化结果(零编造原则)——口径字段与 admin 录入口已就绪,待真实数据把 `basis` 从 `target` 升级为 `verified` ## 技术栈 diff --git a/prisma/seeds/products.ts b/prisma/seeds/products.ts index 0bd3e7d..0f89e23 100644 --- a/prisma/seeds/products.ts +++ b/prisma/seeds/products.ts @@ -14,17 +14,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'erp', scenario: '面向中大型制造与零售企业的核心运营系统', metrics: [ - { value: '10 万级', label: 'SKU 支撑' }, - { value: '200+', label: '标准报表模板' }, - { value: '99.5%', label: '数据准确率' }, + { value: '10 万级', label: 'SKU 支撑', basis: 'target' }, + { value: '200+', label: '标准报表模板', basis: 'target' }, + { value: '99.5%', label: '数据准确率', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['财务核算', '采购管理', '生产计划', '库存控制'], caseStudies: [], dataProofs: [ - { metric: '运营效率提升', value: '40%+', description: '流程自动化减少人工操作' }, - { metric: '数据准确性', value: '99.2%', description: '实时数据同步消除信息孤岛' }, - { metric: '部署周期', value: '3-6个月', description: '从需求调研到正式上线' }, + { metric: '运营效率提升', value: '40%+', description: '流程自动化减少人工操作', basis: 'target' }, + { metric: '数据准确性', value: '99.2%', description: '实时数据同步消除信息孤岛', basis: 'target' }, + { metric: '部署周期', value: '3-6个月', description: '从需求调研到正式上线', basis: 'target' }, ], certifications: [], overview: '睿新ERP管理系统是面向企业打造的一体化资源管理平台,打通财务、采购、销售、库存、生产等核心模块,让各部门数据实时流转、业务流程自动协同,帮助企业告别信息孤岛。', @@ -71,17 +71,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'crm', scenario: '面向销售驱动型企业的一体化客户运营管理平台', metrics: [ - { value: '360°', label: '客户画像' }, - { value: '3x', label: '转化率提升' }, - { value: '<2h', label: '线索响应' }, + { value: '360°', label: '客户画像', basis: 'target' }, + { value: '3x', label: '转化率提升', basis: 'target' }, + { value: '<2h', label: '线索响应', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['线索管理', '商机跟进', '合同审批', '售后工单'], caseStudies: [], dataProofs: [ - { metric: '销售转化率', value: '+113%', description: '从15%提升至32%' }, - { metric: '客户数据整合', value: '100%', description: '多渠道数据统一管理' }, - { metric: '响应速度', value: '<2小时', description: '线索到首次联系平均时间' }, + { metric: '销售转化率', value: '+113%', description: '从15%提升至32%', basis: 'target' }, + { metric: '客户数据整合', value: '100%', description: '多渠道数据统一管理', basis: 'target' }, + { metric: '响应速度', value: '<2小时', description: '线索到首次联系平均时间', basis: 'target' }, ], certifications: [], overview: '睿新客户关系管理系统是面向企业销售团队的全渠道客户管理平台,覆盖从线索到成交的全流程可视化管理,让销售团队清楚每一步该做什么、每个机会卡在哪里。系统支持多渠道客户数据整合,提供全面的客户画像和销售漏斗分析。', @@ -128,17 +128,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'cms', scenario: '面向多品牌、多站点企业的统一内容管理平台', metrics: [ - { value: '50+', label: '站点统管' }, - { value: '60%', label: '发布提效' }, - { value: '100%', label: '私有化部署' }, + { value: '50+', label: '站点统管', basis: 'target' }, + { value: '60%', label: '发布提效', basis: 'target' }, + { value: '100%', label: '私有化部署', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['多站点管理', '工作流审批', '版本控制', 'SEO 优化'], caseStudies: [], dataProofs: [ - { metric: '发布效率提升', value: '60%', description: '多站点统一管理' }, - { metric: '站点支持数', value: '50+', description: '单系统支持多站点' }, - { metric: '部署方式', value: '私有化', description: '数据自主可控' }, + { metric: '发布效率提升', value: '60%', description: '多站点统一管理', basis: 'target' }, + { metric: '站点支持数', value: '50+', description: '单系统支持多站点', basis: 'target' }, + { metric: '部署方式', value: '私有化', description: '数据自主可控', basis: 'target' }, ], certifications: [], overview: '睿新内容管理系统是企业级内容管理平台,提供所见即所得的编辑体验和多站点统一管理能力,让内容发布像写文档一样简单。系统提供强大的内容编辑功能和灵活的权限管理机制。', @@ -185,17 +185,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'bi', scenario: '面向业务人员的自助式数据洞察与决策支持平台', metrics: [ - { value: '70%+', label: '自助分析' }, - { value: '<1天', label: '决策响应' }, - { value: '85%', label: '报表减负' }, + { value: '70%+', label: '自助分析', basis: 'target' }, + { value: '<1天', label: '决策响应', basis: 'target' }, + { value: '85%', label: '报表减负', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['拖拽分析', '实时看板', '智能预警', '移动报表'], caseStudies: [], dataProofs: [ - { metric: '数据整合效率', value: '85%', description: '减少手工报表工作量' }, - { metric: '决策响应时间', value: '<1天', description: '从周级缩短至实时' }, - { metric: '自助分析占比', value: '70%+', description: '业务人员独立完成分析' }, + { metric: '数据整合效率', value: '85%', description: '减少手工报表工作量', basis: 'target' }, + { metric: '决策响应时间', value: '<1天', description: '从周级缩短至实时', basis: 'target' }, + { metric: '自助分析占比', value: '70%+', description: '业务人员独立完成分析', basis: 'target' }, ], certifications: [], overview: '睿新商业智能平台是数据可视化分析平台,提供拖拽式自助分析能力,让业务人员无需写SQL也能从数据中找到答案。平台支持自助式数据分析和实时数据监控。', @@ -242,17 +242,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'sds', scenario: '面向零售与制造企业的智能销量预测与库存优化系统', metrics: [ - { value: '73%', label: '缺货率降低' }, - { value: '38%', label: '周转提升' }, - { value: '<6个月', label: 'ROI 周期' }, + { value: '73%', label: '缺货率降低', basis: 'target' }, + { value: '38%', label: '周转提升', basis: 'target' }, + { value: '<6个月', label: 'ROI 周期', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['销量预测', '智能补货', '库存诊断', '供应商协同'], caseStudies: [], dataProofs: [ - { metric: '缺货率降低', value: '73%', description: '从12%降至3.2%' }, - { metric: '库存周转提升', value: '38%', description: '周转天数从45天降至28天' }, - { metric: 'ROI回报周期', value: '<6个月', description: '投入产出清晰可算' }, + { metric: '缺货率降低', value: '73%', description: '从12%降至3.2%', basis: 'target' }, + { metric: '库存周转提升', value: '38%', description: '周转天数从45天降至28天', basis: 'target' }, + { metric: 'ROI回报周期', value: '<6个月', description: '投入产出清晰可算', basis: 'target' }, ], certifications: [], overview: '睿新供应链决策支持系统是智能供应链管理平台,自动预测销量、智能补货建议,帮您把库存成本降下来、把缺货损失挽回来。系统采用智能算法,实现从经验驱动到数据驱动的供应链管理升级。', @@ -299,17 +299,17 @@ export const PRODUCTS: Product[] = [ heroThemeId: 'oa', scenario: '面向中大型组织的一站式流程审批与协同办公平台', metrics: [ - { value: '95%', label: '审批提效' }, - { value: '100%', label: '移动覆盖' }, - { value: '10万+', label: '日流程承载' }, + { value: '95%', label: '审批提效', basis: 'target' }, + { value: '100%', label: '移动覆盖', basis: 'target' }, + { value: '10万+', label: '日流程承载', basis: 'target' }, ], bundle: 'enterprise', capabilities: ['流程审批', '公文管理', '知识中心', '移动办公'], caseStudies: [], dataProofs: [ - { metric: '审批效率提升', value: '95%', description: '从4天缩短至4小时' }, - { metric: '移动办公覆盖率', value: '100%', description: 'PC+移动端全覆盖' }, - { metric: '流程电子化率', value: '100%', description: '告别纸质流转' }, + { metric: '审批效率提升', value: '95%', description: '从4天缩短至4小时', basis: 'target' }, + { metric: '移动办公覆盖率', value: '100%', description: 'PC+移动端全覆盖', basis: 'target' }, + { metric: '流程电子化率', value: '100%', description: '告别纸质流转', basis: 'target' }, ], certifications: [], overview: '睿新协同办公系统是综合型企业协同办公平台,融合传统办公自动化与现代协作理念。集成流程审批、公文管理、即时通讯、知识管理、会议日程等核心模块,支持PC端与移动端全场景覆盖,帮助企业实现办公数字化、流程规范化、协作高效化。', diff --git a/prisma/seeds/solutions.ts b/prisma/seeds/solutions.ts index 2ffa669..9c684cc 100644 --- a/prisma/seeds/solutions.ts +++ b/prisma/seeds/solutions.ts @@ -37,8 +37,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['生产数据孤岛,决策缺乏实时数据支撑', '库存管理粗放,资金占用成本高'], outcomes: [ - { value: '30%', label: '库存周转提升' }, - { value: '40%', label: '生产效率提升' }, + { value: '30%', label: '库存周转提升', basis: 'target' }, + { value: '40%', label: '生产效率提升', basis: 'target' }, ], recommendedProducts: ['erp', 'bi', 'sds'], }, @@ -77,8 +77,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['线上线下渠道割裂,会员体验碎片化', '供应链响应慢,库存积压与缺货并存'], outcomes: [ - { value: '25%', label: '营收增长' }, - { value: '99%', label: '全渠道库存准确率' }, + { value: '25%', label: '营收增长', basis: 'target' }, + { value: '99%', label: '全渠道库存准确率', basis: 'target' }, ], recommendedProducts: ['crm', 'bi', 'sds'], }, @@ -117,8 +117,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['招生线索管理分散,转化率难以提升', '教学资源管理混乱,优质内容难以沉淀'], outcomes: [ - { value: '45%', label: '线索转化率提升' }, - { value: '60%', label: '内容发布效率提升' }, + { value: '45%', label: '线索转化率提升', basis: 'target' }, + { value: '60%', label: '内容发布效率提升', basis: 'target' }, ], recommendedProducts: ['cms', 'oa', 'crm'], }, @@ -157,8 +157,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['临床数据分散,医生调阅效率低', '运营决策依赖人工统计,时效性差'], outcomes: [ - { value: '10x', label: '决策效率提升' }, - { value: '30%', label: '运营成本下降' }, + { value: '10x', label: '决策效率提升', basis: 'target' }, + { value: '30%', label: '运营成本下降', basis: 'target' }, ], recommendedProducts: ['erp', 'sds', 'bi'], }, @@ -197,8 +197,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['监管要求持续升级,合规管理成本高', '数据孤岛严重,客户画像难以落地'], outcomes: [ - { value: '70%', label: '财务效率提升' }, - { value: '0', label: '重大合规事故' }, + { value: '70%', label: '财务效率提升', basis: 'target' }, + { value: '0', label: '重大合规事故', basis: 'target' }, ], recommendedProducts: ['bi', 'sds', 'erp'], }, @@ -237,8 +237,8 @@ export const SOLUTIONS: Solution[] = [ }, painPoints: ['运输过程不透明,客户体验差', '调度依赖人工经验,车辆空驶率高'], outcomes: [ - { value: '20%', label: '空驶率降低' }, - { value: '95%+', label: '全程可视化率' }, + { value: '20%', label: '空驶率降低', basis: 'target' }, + { value: '95%+', label: '全程可视化率', basis: 'target' }, ], recommendedProducts: ['erp', 'bi', 'sds'], }, diff --git a/prisma/seeds/standalone-products.ts b/prisma/seeds/standalone-products.ts index 28dd40d..d763607 100644 --- a/prisma/seeds/standalone-products.ts +++ b/prisma/seeds/standalone-products.ts @@ -15,18 +15,18 @@ export const STANDALONE_PRODUCTS: StandaloneProduct[] = [ heroThemeId: 'novavis', scenario: '面向执法机关的离线智能数据分析与资金关系图谱平台', metrics: [ - { value: '100万+', label: '秒级处理' }, - { value: '<30秒', label: '分析耗时' }, - { value: '20层+', label: '资金追踪' }, + { value: '100万+', label: '秒级处理', basis: 'target' }, + { value: '<30秒', label: '分析耗时', basis: 'target' }, + { value: '20层+', label: '资金追踪', basis: 'target' }, ], bundle: 'standalone', capabilities: ['资金追踪', '关系图谱', 'AI 分析', '离线运行'], caseStudies: [], dataProofs: [ - { metric: '数据处理能力', value: '100万+', description: '百万级记录秒级处理' }, - { metric: '分析耗时', value: '<30秒', description: '百万级交易完成分析' }, - { metric: '内存占用降低', value: '70%', description: '相比传统方案显著优化' }, - { metric: '部署时间', value: '5分钟', description: '即插即用,快速启动' }, + { metric: '数据处理能力', value: '100万+', description: '百万级记录秒级处理', basis: 'target' }, + { metric: '分析耗时', value: '<30秒', description: '百万级交易完成分析', basis: 'target' }, + { metric: '内存占用降低', value: '70%', description: '相比传统方案显著优化', basis: 'target' }, + { metric: '部署时间', value: '5分钟', description: '即插即用,快速启动', basis: 'target' }, ], certifications: [], overview: '睿视 NovaVis 是我们即将推出的面向执法机关的新一代智能数据分析平台。以完全离线架构保障数据安全——所有计算均在本地完成,数据不出本地、不触网络。融合 AI 智能分析与专业关系网络图谱,提供资金追踪、团伙分析、时空碰撞等核心能力,让海量涉案数据秒级响应,为经济犯罪侦查、反腐败调查、涉黑涉恶打击等场景提供全链路数据分析支撑。', diff --git a/src/app/(marketing)/about/about-content-v4.tsx b/src/app/(marketing)/about/about-content-v4.tsx index 800e594..d99c952 100644 --- a/src/app/(marketing)/about/about-content-v4.tsx +++ b/src/app/(marketing)/about/about-content-v4.tsx @@ -3,6 +3,7 @@ import { motion } from 'framer-motion'; import { CTAButton } from '@/components/ui/cta-button'; import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; +import { weakestBasis, type MetricBasis } from '@/lib/constants/metrics-basis'; import { ContentUnavailableState } from '@/components/ui/content-unavailable'; import { Trophy, @@ -35,7 +36,7 @@ interface AboutData { promiseText?: string; coreValues?: { number: string; title: string; description: string }[]; milestones?: { year: string; title: string; description: string; highlight: string }[]; - keyMetrics?: { value: string; label: string; sub: string }[]; + keyMetrics?: { value: string; label: string; sub: string; basis?: MetricBasis }[]; certifications?: { name: string; description: string }[]; partners?: string[]; ctaTitle?: string; @@ -147,7 +148,7 @@ function MetricsStrip({ data }: { data: AboutData }) { ))} - + m.basis))} className="mt-8 text-center lg:text-left" /> ); diff --git a/src/app/(marketing)/home-content-v15.tsx b/src/app/(marketing)/home-content-v15.tsx index 45aa1b6..2d71a82 100644 --- a/src/app/(marketing)/home-content-v15.tsx +++ b/src/app/(marketing)/home-content-v15.tsx @@ -14,6 +14,7 @@ import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants'; import { canonicalCtaLabel, CTA_LABELS } from '@/lib/constants/cta-labels'; import { canonicalTrustSource } from '@/lib/constants/company'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; import { useSiteConfig } from '@/lib/site-config'; import { useReducedMotion } from '@/hooks/use-reduced-motion'; import { type NewsItem } from '@/lib/constants/news'; @@ -296,7 +297,7 @@ function HeroStatsBand({ stats }: { stats?: Record[] }) { ))} - + s.basis))} className="mt-6" /> ); @@ -558,7 +559,7 @@ function ServicesSection({ services, pageCopy }: { services?: Record ))} - + m.basis))} /> )}
diff --git a/src/app/(marketing)/products/page.tsx b/src/app/(marketing)/products/page.tsx index 4ce6d38..9d8fb75 100644 --- a/src/app/(marketing)/products/page.tsx +++ b/src/app/(marketing)/products/page.tsx @@ -6,7 +6,7 @@ import ProductsContentV3 from './products-content-v3'; export const metadata: Metadata = { title: `产品矩阵 - ${COMPANY_INFO.displayName}`, - description: `覆盖企业数字化全场景的产品矩阵,从数据智能到业务协同,每一款产品都经过实战验证。`, + description: `覆盖企业数字化核心场景的自研产品矩阵,从业务系统到数据智能,套装与专业产品线互为补充。`, }; export const revalidate = 3600; diff --git a/src/app/(marketing)/products/product-detail-content-v3.tsx b/src/app/(marketing)/products/product-detail-content-v3.tsx index a4f0d10..75ba655 100644 --- a/src/app/(marketing)/products/product-detail-content-v3.tsx +++ b/src/app/(marketing)/products/product-detail-content-v3.tsx @@ -325,7 +325,7 @@ export default function ProductDetailContentV3({ product }: ProductDetailContent {product.methodology && product.methodology.length > 0 && ( )} @@ -341,12 +341,13 @@ export default function ProductDetailContentV3({ product }: ProductDetailContent {product.dataProofs && product.dataProofs.length > 0 && ( ({ value: dp.value, label: dp.metric, description: dp.description, + basis: dp.basis, }))} /> )} diff --git a/src/app/(marketing)/products/products-content-v3.tsx b/src/app/(marketing)/products/products-content-v3.tsx index 75a2b89..b421a3f 100644 --- a/src/app/(marketing)/products/products-content-v3.tsx +++ b/src/app/(marketing)/products/products-content-v3.tsx @@ -10,6 +10,7 @@ import { BentoItem } from '@/components/sections/bento-grid'; import { ArrowRight, ArrowUpRight, Package, Cpu, TrendingUp, Users, Settings, FileText } from 'lucide-react'; import type { Product } from '@/lib/constants/products'; import { CTA_LABELS } from '@/lib/constants/cta-labels'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; import { cn } from '@/lib/utils'; const EASE_SPRING = { type: 'spring', stiffness: 300, damping: 30 } as const; @@ -146,7 +147,7 @@ function ProductCard({ product, size = 'small' }: { product: Product; size?: 'la ))}
{product.metrics && product.metrics.length > 0 && product.status !== '已发布' && ( - + m.basis))} className="-mt-2 mb-4" /> )}
@@ -319,7 +320,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) { 推荐产品组合

- 经过实战验证的产品组合,发挥 1+1 > 2 的协同效应 + 套装与专业产品线按场景互补,覆盖从核心业务系统到数据分析的关键链路

diff --git a/src/app/(marketing)/products/standalone/[id]/client.tsx b/src/app/(marketing)/products/standalone/[id]/client.tsx index 6432fa5..8bb2923 100644 --- a/src/app/(marketing)/products/standalone/[id]/client.tsx +++ b/src/app/(marketing)/products/standalone/[id]/client.tsx @@ -178,7 +178,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)

技术参数

- 关键性能指标与技术指标,经实际场景验证。 + 关键性能指标与技术规格,为产品设计目标口径,尚无第三方实测报告。

diff --git a/src/app/(marketing)/services/services-content-v3.tsx b/src/app/(marketing)/services/services-content-v3.tsx index 5396e35..0ca1345 100644 --- a/src/app/(marketing)/services/services-content-v3.tsx +++ b/src/app/(marketing)/services/services-content-v3.tsx @@ -6,12 +6,13 @@ import { StaticLink } from '@/components/ui/static-link'; import { CTAButton } from '@/components/ui/cta-button'; import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; import { CTA_LABELS, canonicalCtaLabel } from '@/lib/constants/cta-labels'; -import type { Service } from '@/lib/constants/services'; +import type { Service, ServiceMetric } from '@/lib/constants/services'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; const EASE_OUT = [0.22, 1, 0.36, 1] as const; /** Fallback metrics keyed by service ID — used when CMS data does not provide metrics */ -const SERVICE_UI_META: Record = { +const SERVICE_UI_META: Record = { consulting: { metrics: [{ value: '10+', label: '人核心团队' }, { value: '6', label: '行业场景' }] }, software: { metrics: [{ value: '6', label: '款自研产品' }, { value: 'A+', label: '代码质量' }] }, data: { metrics: [{ value: '99.9%', label: '系统可用性' }, { value: '24/7', label: '运维支持' }] }, @@ -231,7 +232,9 @@ function ServicesGridSection({ services, pageCopy }: { services?: Service[]; pag ))} - {service.metrics.length > 0 && } + {service.metrics.length > 0 && ( + m.basis))} className="mb-6" /> + )}
了解详情 diff --git a/src/app/(marketing)/solutions/solutions-content-v3.tsx b/src/app/(marketing)/solutions/solutions-content-v3.tsx index e8c75c6..c85d289 100644 --- a/src/app/(marketing)/solutions/solutions-content-v3.tsx +++ b/src/app/(marketing)/solutions/solutions-content-v3.tsx @@ -6,6 +6,7 @@ import { StaticLink } from '@/components/ui/static-link'; import { CTAButton } from '@/components/ui/cta-button'; import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; import { CTA_LABELS, canonicalCtaLabel } from '@/lib/constants/cta-labels'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; import { type Solution } from '@/lib/constants/solutions'; import type { Product } from '@/lib/constants/products'; @@ -135,7 +136,7 @@ function SolutionCard({ solution, index, products, featured = false }: { solutio ))}
{solution.outcomes && solution.outcomes.length > 0 && ( - + o.basis))} className="-mt-2 mb-4" /> )}
diff --git a/src/components/content/sections.tsx b/src/components/content/sections.tsx index d7cde5c..8f07ab5 100644 --- a/src/components/content/sections.tsx +++ b/src/components/content/sections.tsx @@ -5,6 +5,8 @@ import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; import { EASE_OUT } from '@/components/ui/page-decoration'; import { CheckCircle2, ChevronDown } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; +import { weakestBasis, type MetricBasis } from '@/lib/constants/metrics-basis'; import { useState } from 'react'; export interface MethodologyLayer { @@ -318,6 +320,7 @@ export interface DataMetric { description?: string; trend?: string; trendUp?: boolean; + basis?: MetricBasis; } export interface DataProofSectionProps { @@ -328,6 +331,7 @@ export interface DataProofSectionProps { export function DataProofSection({ title, subtitle, metrics }: DataProofSectionProps) { if (!metrics || metrics.length === 0) return null; + const basis = weakestBasis(metrics.map((m) => m.basis)); return (
@@ -375,6 +379,7 @@ export function DataProofSection({ title, subtitle, metrics }: DataProofSectionP ))}
+ ); diff --git a/src/components/detail/detail-product-value.tsx b/src/components/detail/detail-product-value.tsx index 9bdfa10..f061215 100644 --- a/src/components/detail/detail-product-value.tsx +++ b/src/components/detail/detail-product-value.tsx @@ -15,6 +15,8 @@ import { type LucideIcon, } from 'lucide-react'; import type { Product } from '@/lib/constants/products'; +import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; interface ProductValueSectionProps { product: Product; @@ -250,11 +252,11 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
- 效果数据 + 设计目标

- 用真实效果证明价值 + 以可量化的目标约束交付

@@ -275,6 +277,10 @@ export function ProductValueSection({ product }: ProductValueSectionProps) { ); })} + proof.basis))} + className="mt-8" + /> )} diff --git a/src/components/detail/detail-trust-section.tsx b/src/components/detail/detail-trust-section.tsx index 9c48cdc..55e3706 100644 --- a/src/components/detail/detail-trust-section.tsx +++ b/src/components/detail/detail-trust-section.tsx @@ -5,6 +5,8 @@ import { useRef, useEffect, useState } from 'react'; import { CaseStudyCard } from './detail-case-study'; import { CertificationList } from './detail-certification'; import type { CaseStudy, DataProof, Certification } from '@/lib/constants/products'; +import { MetricsBasisNote } from '@/components/ui/metrics-basis-note'; +import { weakestBasis } from '@/lib/constants/metrics-basis'; interface DetailTrustSectionProps { caseStudies?: CaseStudy[]; @@ -84,13 +86,13 @@ export function DetailTrustSection({
- 用真实效果证明价值 + 目标口径

- 来自真实客户的使用数据, + 以设计目标约束交付,
- 让效果可衡量、可验证 + 在首批共创中转为可核验数据

@@ -114,6 +116,10 @@ export function DetailTrustSection({ ))}
+ proof.basis))} + className="mt-8" + /> )} diff --git a/src/components/detail/detail.test.tsx b/src/components/detail/detail.test.tsx index 4e771fa..fc0ffb7 100644 --- a/src/components/detail/detail.test.tsx +++ b/src/components/detail/detail.test.tsx @@ -435,14 +435,14 @@ describe('ProductValueSection', () => { it('should render data proofs when available', async () => { const { ProductValueSection } = await import('./detail-product-value'); render(); - expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument(); + expect(screen.getByText('以可量化的目标约束交付')).toBeInTheDocument(); }); it('should not render data proofs section when empty', async () => { const { ProductValueSection } = await import('./detail-product-value'); const productWithoutData = { ...mockProduct, dataProofs: [] }; render(); - expect(screen.queryByText('用真实效果证明价值')).not.toBeInTheDocument(); + expect(screen.queryByText('以可量化的目标约束交付')).not.toBeInTheDocument(); }); }); @@ -462,8 +462,10 @@ describe('DetailTrustSection', () => { dataProofs={mockProduct.dataProofs} /> ); - expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument(); + expect(screen.getByText('目标口径')).toBeInTheDocument(); expect(screen.getByText(mockProduct.dataProofs[0]!.metric)).toBeInTheDocument(); + // 未声明 basis 的 dataProofs 必须自动落最弱口径角注,不得裸奔 + expect(screen.getByTestId('metrics-basis-note')).toHaveTextContent('目标口径,非既成结果'); }); it('should render case studies', async () => { @@ -500,7 +502,7 @@ describe('DetailTrustSection', () => { accentColor="#C41E3A" /> ); - expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument(); + expect(screen.getByText('目标口径')).toBeInTheDocument(); expect(screen.getByText('他们已经实现了转型突破')).toBeInTheDocument(); expect(screen.getByText('资质认证')).toBeInTheDocument(); }); diff --git a/src/components/detail/early-access-notice.tsx b/src/components/detail/early-access-notice.tsx index 218c8fc..948ed3d 100644 --- a/src/components/detail/early-access-notice.tsx +++ b/src/components/detail/early-access-notice.tsx @@ -9,7 +9,7 @@ * - 内容源于 `EARLY_ACCESS` 常量(company.ts L119),单一真源。 * * 与 L3SignalsSlot 关系:EarlyAccessNotice 是 L3EmptyFallback 的次要兜底块, - * 在 signals(4 类信号卡)下方呈现——回答「为什么没有真实客户案例」这个问题。 + * 在 signals(4 类信号卡)下方呈现——回答「为什么还没有可公开案例」这个问题。 */ import Link from 'next/link'; diff --git a/src/components/ui/metrics-basis-note.tsx b/src/components/ui/metrics-basis-note.tsx index 14ef77c..6a5c79c 100644 --- a/src/components/ui/metrics-basis-note.tsx +++ b/src/components/ui/metrics-basis-note.tsx @@ -1,14 +1,21 @@ import { cn } from '@/lib/utils'; -import { METRICS_BASIS_NOTE } from '@/lib/constants/metrics-basis'; +import { METRIC_BASIS_NOTES, resolveMetricBasis, type MetricBasis } from '@/lib/constants/metrics-basis'; /** * 结果型数字(提升百分比 / 可用性 / 达标率等)下方的口径角注。 - * 全站统一文案,见 METRICS_BASIS_NOTE。供 solutions / services / home / about / products 复用。 + * 文案按数字声明的 `basis` 口径解析,未声明一律按最弱的 target 处理,见 METRIC_BASIS_NOTES。 + * 供 solutions / services / home / about / products 与详情页 DataProofSection 复用。 */ -export function MetricsBasisNote({ className }: { className?: string }) { +export function MetricsBasisNote({ + className, + basis = 'target', +}: { + className?: string; + basis?: MetricBasis | string | null; +}) { return (

- {METRICS_BASIS_NOTE} + {METRIC_BASIS_NOTES[resolveMetricBasis(basis)]}

); } diff --git a/src/lib/cms/content-types.ts b/src/lib/cms/content-types.ts index fb7d225..0b13e9d 100644 --- a/src/lib/cms/content-types.ts +++ b/src/lib/cms/content-types.ts @@ -1,5 +1,22 @@ import type { FieldDefinition } from './types'; +/** + * 数字口径声明字段——所有以大字数字样式呈现的指标(metrics / outcomes / dataProofs)必须携带。 + * 渲染端按此自动附口径角注;留空一律按最弱的 target 处理,见 lib/constants/metrics-basis.ts。 + * 只有具备可核验实测出处(报告、监控、已交付客户数据)时才应选 verified。 + */ +const metricBasisField: FieldDefinition = { + name: 'basis', + type: 'select', + label: '数据口径', + description: '留空按「目标口径」处理并自动标注;仅在有可核验实测出处时选「已验证」', + options: [ + { label: '目标口径(设计/承诺值,非既成结果)', value: 'target' }, + { label: '团队历史项目成果(非本公司交付数据)', value: 'team-history' }, + { label: '已验证(有可核验实测出处)', value: 'verified' }, + ], +}; + export interface ContentTypeConfig { model: { id: string; @@ -164,6 +181,7 @@ const serviceFields: FieldDefinition[] = [ fields: [ { name: 'value', type: 'text', label: '数值', required: true }, { name: 'label', type: 'text', label: '标签', required: true }, + { ...metricBasisField }, ], }, { @@ -186,6 +204,7 @@ const serviceFields: FieldDefinition[] = [ { name: 'metric', type: 'text', label: '指标名称', required: true }, { name: 'value', type: 'text', label: '数值', required: true }, { name: 'description', type: 'text', label: '描述', required: true }, + { ...metricBasisField }, ], }, { @@ -363,6 +382,7 @@ const productFields: FieldDefinition[] = [ fields: [ { name: 'value', type: 'text', label: '数值', required: true }, { name: 'label', type: 'text', label: '标签', required: true }, + { ...metricBasisField }, ], }, { @@ -385,6 +405,7 @@ const productFields: FieldDefinition[] = [ { name: 'metric', type: 'text', label: '指标名称', required: true }, { name: 'value', type: 'text', label: '数值', required: true }, { name: 'description', type: 'text', label: '描述', required: true }, + { ...metricBasisField }, ], }, { @@ -548,6 +569,7 @@ const standaloneProductFields: FieldDefinition[] = [ fields: [ { name: 'value', type: 'text', label: '数值', required: true }, { name: 'label', type: 'text', label: '标签', required: true }, + { ...metricBasisField }, ], }, { @@ -570,6 +592,7 @@ const standaloneProductFields: FieldDefinition[] = [ { name: 'metric', type: 'text', label: '指标名称', required: true }, { name: 'value', type: 'text', label: '数值', required: true }, { name: 'description', type: 'text', label: '描述', required: true }, + { ...metricBasisField }, ], }, { @@ -755,6 +778,7 @@ const solutionFields: FieldDefinition[] = [ fields: [ { name: 'value', type: 'text', label: '数值', required: true }, { name: 'label', type: 'text', label: '标签', required: true }, + { ...metricBasisField }, ], }, { @@ -1180,6 +1204,7 @@ const aboutPageFields: FieldDefinition[] = [ { name: 'value', type: 'text', label: '数值', required: true }, { name: 'label', type: 'text', label: '标签', required: true }, { name: 'sub', type: 'text', label: '副标题' }, + { ...metricBasisField }, ], }, { diff --git a/src/lib/constants/metrics-basis.test.ts b/src/lib/constants/metrics-basis.test.ts index a6f8d88..107ff62 100644 --- a/src/lib/constants/metrics-basis.test.ts +++ b/src/lib/constants/metrics-basis.test.ts @@ -1,12 +1,25 @@ /** - * 结果型数字口径机械防线(2026-09-19 critique P0): - * 「目标口径,非既成结果」必须来自 METRICS_BASIS_NOTE 单一真源; - * 且每个呈现结果型数字的营销页必须接入 MetricsBasisNote。 + * 数字口径机械防线(2026-09-19 critique P0 → 结构强制收尾): + * 1) 「目标口径,非既成结果」必须来自 METRICS_BASIS_NOTE 单一真源,不得散写字面量; + * 2) 每个呈现结果型数字的渲染源(含详情页共享数字组件)必须接入 MetricsBasisNote; + * 3) 走共享数字样式渲染的指标必须声明 basis(target|team-history|verified), + * 且口径解析对缺失/非法值保守回落到最弱的 target; + * 4) 禁止再引入「数字源自真实客户案例 / 经过实战验证」式虚构佐证(本站零公开客户案例)。 * 扫描范围:src/app + src/components 的 .ts/.tsx(排除测试文件与 _archive 归档)。 */ import { readdirSync, readFileSync, statSync } from 'fs'; import { join } from 'path'; -import { METRICS_BASIS_NOTE } from './metrics-basis'; +import { + METRICS_BASIS_NOTE, + METRIC_BASES, + METRIC_BASIS_NOTES, + FORBIDDEN_PROOF_PHRASES, + resolveMetricBasis, + weakestBasis, +} from './metrics-basis'; +import { PRODUCTS } from './products'; +import { SOLUTIONS } from './solutions'; +import { STANDALONE_PRODUCTS } from '../../../prisma/seeds/standalone-products'; const SOURCE_ROOTS = ['src/app', 'src/components']; @@ -16,6 +29,10 @@ const REQUIRED_ANNOTATED_SOURCES = [ 'src/app/(marketing)/solutions/solutions-content-v3.tsx', 'src/app/(marketing)/services/services-content-v3.tsx', 'src/app/(marketing)/home-content-v15.tsx', + // 详情页同样以大数字样式渲染 dataProofs,不得裸奔 + 'src/components/content/sections.tsx', + 'src/components/detail/detail-trust-section.tsx', + 'src/components/detail/detail-product-value.tsx', ]; function collectSourceFiles(dir: string, acc: string[] = []): string[] { @@ -32,11 +49,29 @@ function collectSourceFiles(dir: string, acc: string[] = []): string[] { return acc; } +/** 把三处 seed 里的全部数字条目摊平,用于口径声明完整性断言。 */ +function allSeedMetricBases(): Array { + const bases: Array = []; + for (const p of [...PRODUCTS, ...STANDALONE_PRODUCTS]) { + (p.metrics ?? []).forEach((m) => bases.push(m.basis)); + (p.dataProofs ?? []).forEach((d) => bases.push(d.basis)); + } + for (const s of SOLUTIONS) { + (s.outcomes ?? []).forEach((o) => bases.push(o.basis)); + (s.dataProofs ?? []).forEach((d) => bases.push(d.basis)); + } + return bases; +} + describe('结果型数字口径角注防线', () => { it('METRICS_BASIS_NOTE 固定为「目标口径,非既成结果」', () => { expect(METRICS_BASIS_NOTE).toBe('目标口径,非既成结果'); }); + it('METRICS_BASIS_NOTE 等于最弱口径 target 的文案', () => { + expect(METRICS_BASIS_NOTE).toBe(METRIC_BASIS_NOTES.target); + }); + it('生产源码不得硬编码口径角注字面量(必须引用单一真源)', () => { const violations: string[] = []; for (const root of SOURCE_ROOTS) { @@ -63,3 +98,86 @@ describe('结果型数字口径角注防线', () => { } }); }); + +describe('basis 口径解析', () => { + it('枚举固定为 target / team-history / verified', () => { + expect(METRIC_BASES).toEqual(['target', 'team-history', 'verified']); + }); + + it('三个口径均有文案,无空串', () => { + for (const basis of METRIC_BASES) { + expect(METRIC_BASIS_NOTES[basis].length).toBeGreaterThan(0); + } + }); + + it('合法口径原样解析(正控制,防匹配零的假绿)', () => { + expect(resolveMetricBasis('verified')).toBe('verified'); + expect(resolveMetricBasis('team-history')).toBe('team-history'); + expect(resolveMetricBasis('target')).toBe('target'); + }); + + it('缺失/非法/大小写错误一律保守回落到 target', () => { + expect(resolveMetricBasis(undefined)).toBe('target'); + expect(resolveMetricBasis(null)).toBe('target'); + expect(resolveMetricBasis('VERIFIED')).toBe('target'); + expect(resolveMetricBasis('已验证')).toBe('target'); + expect(resolveMetricBasis('verified ')).toBe('target'); + }); + + it('weakestBasis 取最弱口径:任一 target 存在即整体为 target', () => { + expect(weakestBasis(['verified', 'team-history'])).toBe('team-history'); + expect(weakestBasis(['verified', 'target'])).toBe('target'); + expect(weakestBasis(['verified', 'verified'])).toBe('verified'); + }); + + it('weakestBasis 空数组与含未声明项均落到 target', () => { + expect(weakestBasis([])).toBe('target'); + expect(weakestBasis(['verified', undefined])).toBe('target'); + }); +}); + +describe('seed 数字必须显式声明口径', () => { + it('存在可检出的数字条目(覆盖证明,防空跑)', () => { + expect(allSeedMetricBases().length).toBeGreaterThan(40); + }); + + it('每条 metrics / outcomes / dataProofs 均声明 basis', () => { + const missing = allSeedMetricBases().filter((b) => !b); + expect(missing).toEqual([]); + }); + + it('声明的 basis 全部合法', () => { + const invalid = allSeedMetricBases().filter( + (b) => !(METRIC_BASES as readonly string[]).includes(String(b)), + ); + expect(invalid).toEqual([]); + }); + + it('当前无任何条目自称 verified(零编造:尚无客户实测数据)', () => { + const claimed = allSeedMetricBases().filter((b) => b === 'verified'); + expect(claimed).toEqual([]); + }); +}); + +describe('虚构佐证禁词防线', () => { + it('正控制:禁词表非空且每条均可被检出', () => { + expect(FORBIDDEN_PROOF_PHRASES.length).toBeGreaterThan(0); + for (const phrase of FORBIDDEN_PROOF_PHRASES) { + expect(`示例文案${phrase}结尾`).toContain(phrase); + } + }); + + it('生产源码不含「真实客户案例 / 实战验证」式虚构佐证', () => { + const violations: string[] = []; + for (const root of SOURCE_ROOTS) { + for (const file of collectSourceFiles(root)) { + const lines = readFileSync(file, 'utf-8').split('\n'); + lines.forEach((line, i) => { + const hit = FORBIDDEN_PROOF_PHRASES.find((p) => line.includes(p)); + if (hit) violations.push(`${file}:${i + 1} ${hit}`); + }); + } + } + expect(violations).toEqual([]); + }); +}); diff --git a/src/lib/constants/metrics-basis.ts b/src/lib/constants/metrics-basis.ts index 53727c4..bd212d3 100644 --- a/src/lib/constants/metrics-basis.ts +++ b/src/lib/constants/metrics-basis.ts @@ -1,9 +1,59 @@ /** - * METRICS_BASIS_NOTE — 全站「结果型数字」口径角注单一真源。 + * 数字口径(metric basis)单一真源。 * - * 2026-09-19 critique P0:solutions / services / home / about 把「30% 库存周转提升」 - * 「99.9% 可用性」这类目标值/能力值以既成结果口吻呈现,缺出处即构成误导。 - * 与 CTA_LABELS 同属渲染入口的诚实性防线:数字保留,但强制附口径说明。 - * 根治方式是补充真实案例数据或 admin 修正内容,此处为最后一道防线。 + * 2026-09-19 critique P0:solutions / services / home / about / products 把「30% 库存周转提升」 + * 「99.9% 系统可用率」这类目标值/能力值以既成结果口吻呈现,缺出处即构成误导。 + * + * 本文件把「每个数字必须声明口径」从个别页的自觉升级为结构约束:数字携带 `basis`, + * 渲染端据此自动附角注;未声明或声明非法一律按最弱口径 `target` 处理(保守默认, + * 宁可多标注也不裸奔)。真实出处由业务在 admin 录入,代码零虚构。 */ -export const METRICS_BASIS_NOTE = '目标口径,非既成结果'; + +/** 由弱到强:聚合一组数字时取最弱者,保证角注不夸大。 */ +export const METRIC_BASES = ['target', 'team-history', 'verified'] as const; + +export type MetricBasis = (typeof METRIC_BASES)[number]; + +const BASIS_STRENGTH: Record = { + target: 0, + 'team-history': 1, + verified: 2, +}; + +/** 各口径对应的角注文案。 */ +export const METRIC_BASIS_NOTES: Record = { + target: '目标口径,非既成结果', + 'team-history': '团队历史项目成果,非本公司交付数据', + verified: '已有可核验的实测出处', +}; + +/** 全站默认口径角注(= 最弱口径 target 的文案)。 */ +export const METRICS_BASIS_NOTE = METRIC_BASIS_NOTES.target; + +/** 把数据里的任意值归一为合法口径;缺失/非法一律回落到最弱的 `target`。 */ +export function resolveMetricBasis(basis?: string | null): MetricBasis { + return (METRIC_BASES as readonly string[]).includes(String(basis)) + ? (basis as MetricBasis) + : 'target'; +} + +/** 一组数字的聚合口径:取最弱的一个。 */ +export function weakestBasis(bases: Array): MetricBasis { + if (bases.length === 0) return 'target'; + return bases.map(resolveMetricBasis).reduce( + (weakest, cur) => (BASIS_STRENGTH[cur] < BASIS_STRENGTH[weakest] ? cur : weakest), + resolveMetricBasis(bases[0]), + ); +} + +/** + * 虚构佐证禁词表:本站 2026-01 成立、零公开客户案例(见 early-access-notice), + * 因此任何「数字源自真实客户案例 / 经过实战验证」的措辞都是编造佐证。 + * 由 metrics-basis.test.ts 在 src/app + src/components 全源码扫描强制。 + */ +export const FORBIDDEN_PROOF_PHRASES = [ + '源自真实客户案例', + '真实客户案例', + '经过实战验证', + '实战验证', +] as const; diff --git a/src/lib/constants/products.ts b/src/lib/constants/products.ts index 13fd764..1659125 100644 --- a/src/lib/constants/products.ts +++ b/src/lib/constants/products.ts @@ -1,3 +1,5 @@ +import type { MetricBasis } from './metrics-basis'; + export interface CaseStudy { client: string; industry: string; @@ -10,6 +12,7 @@ export interface DataProof { metric: string; value: string; description: string; + basis?: MetricBasis; } export interface Certification { @@ -78,7 +81,7 @@ export interface Product { techStack?: TechStackCategory[]; faqs?: FAQItem[]; scenario?: string; - metrics?: { value: string; label: string }[]; + metrics?: { value: string; label: string; basis?: MetricBasis }[]; bundle: 'enterprise' | 'standalone'; capabilities?: string[]; } diff --git a/src/lib/constants/services.ts b/src/lib/constants/services.ts index 61d2363..2facc2c 100644 --- a/src/lib/constants/services.ts +++ b/src/lib/constants/services.ts @@ -37,6 +37,7 @@ export interface FAQItem { export interface ServiceMetric { value: string; label: string; + basis?: import('./metrics-basis').MetricBasis; } export interface Service { diff --git a/src/lib/constants/solutions.ts b/src/lib/constants/solutions.ts index 78cbd10..2b94288 100644 --- a/src/lib/constants/solutions.ts +++ b/src/lib/constants/solutions.ts @@ -24,7 +24,7 @@ export interface Solution { dataProofs?: import('./products').DataProof[]; certifications?: import('./products').Certification[]; painPoints?: string[]; - outcomes?: { value: string; label: string }[]; + outcomes?: { value: string; label: string; basis?: import('./metrics-basis').MetricBasis }[]; recommendedProducts?: string[]; }