Просмотр исходного кода

feat: complete domestic voc framework

gangvy 1 месяц назад
Родитель
Сommit
0642615f9e

+ 56 - 0
FRAMEWORK-MIGRATION.md

@@ -0,0 +1,56 @@
+# 国内 VOC 框架迁移矩阵
+
+基线:`msq-voc-web@9326c20`
+目标:`Saas-voc` 国内电商通用模板
+
+本矩阵用于区分“页面文件已经复制”与“页面已经安全启用”。安全启用必须同时满足:使用国内商品语义、不请求旧跨境接口、不连接原线上数据库、没有证据时显示真实空态。
+
+## 已启用框架
+
+| 原框架业务组 | 国内模板入口 | 数据状态 | 迁移方式 |
+|---|---|---|---|
+| 核心看板 | `/domestic/overview` | 真实经营数据 | 复用 App Shell、指标卡、图表和表格 |
+| 商品列表与详情 | `/voc-insight/products`、`/voc-insight/detail` | 2,817 个商品,评论 0 条 | 复用商品列表、原详情布局和共享组件 |
+| 品类与市场 | `/domestic/categories`、`/domestic/category-voc` | 真实类目经营数据,VOC 空态 | 国内类目适配 |
+| 竞品关系 | `/domestic/competitors` | 40 条关系 | 国内商品映射适配 |
+| VOC 全局监测 | `/domestic/voc-overview`、`/domestic/voc-trend`、`/domestic/emotion`、`/domestic/store-monitoring`、`/domestic/risk-alerts` | 经营背景真实,评论结论空态 | 复用监测看板、页签、图表和状态组件 |
+| VOC 深度洞察 | `/voc-insight/voice`、`/voc-insight/pain-point`、`/voc-insight/feature`、`/voc-insight/scenario` | 评论 0 条 | 保留原页面结构,禁止生成虚假洞察 |
+| 退款深度分析 | `/return-analysis/overview`、`/return-analysis/attribution`、`/return-analysis/correlation` | 退款汇总真实,原因与关联空态 | 复用原三层分析结构 |
+| 新品开发驱动 | `/product-development/trend`、`/product-development/requirement`、`/product-development/competitor-benchmark`、`/product-development/product-definition`、`/product-development/lifecycle` | 趋势/竞品/定义真实,需求空态 | 复用原五环节导航与分析组件 |
+| 行动建议 | `/action-suggestion/kanban`、`/action-suggestion/experience`、`/action-suggestion/iteration`、`/action-suggestion/strategy` | 经营规则建议真实,体验建议空态 | 复用建议看板和优先级组件 |
+| 数据接入状态 | `/data-status` | 真实导入与质量状态 | 国内数据状态页 |
+
+当前共启用 29 个业务入口。
+
+## 国内等价承接
+
+以下原页面没有重复挂载,而由国内页面承接同一职责:
+
+| 原页面 | 国内承接页 | 原因 |
+|---|---|---|
+| `dashboard` | `/domestic/overview` | 使用德玛仕真实经营指标重建核心概览 |
+| `dashboard/category-voc` | `/domestic/category-voc` | 国内类目模型替代原站点/类目字段 |
+| `monitoring/store` | `/domestic/store-monitoring` | 单案例店铺经营与类目监测 |
+| `market-insight/overview` | `/domestic/overview`、`/domestic/categories` | 当前市场范围等于案例商品经营范围 |
+| `market-insight/category` | `/domestic/categories`、`/product-development/trend` | 当前仅有 7 天真实类目趋势,不伪造长线数据 |
+| `product-lifecycle-review/monthly` | `/product-development/lifecycle` | 当前数据只支持单商品短周期复盘 |
+
+## 刻意不启用
+
+以下页面属于跨境专属、外部内容或系统后台,直接启用会产生错误语义或旧请求,因此不属于国内 VOC 通用模板的安全迁移范围:
+
+| 原页面/能力 | 不启用原因 |
+|---|---|
+| Amazon 关键词、排名、新品榜单、Listing T+1 更新 | 依赖 Amazon、BSR、Sorftime 或站点专属字段 |
+| 独立站、社媒、达人和视频知识库 | 当前需求没有对应采集源或内容契约 |
+| 原退货原因、客户画像、责任归因 | 德玛仕文件只有退款汇总,没有订单级原因明细 |
+| 原 AI 自动报告和自动工单推送 | 当前没有评论证据、持久化工单和验收数据 |
+| Amazon 类目、店铺、用户、API 等系统管理页 | 属于原线上后台,不应连接或复制到独立国内案例 |
+| 原登录守卫 | 独立 Parse 应用尚未建立,当前案例模式不启用旧账号体系 |
+
+## 复用约束
+
+- 页面骨架优先复用 `PageHeader`、`SummaryMetricCard`、`BoardTabs`、`ContentCard`、`DataTableShell`、`ProductIdentity`、`AlertCard`、`PriorityAccordion`、`LoadingSpinner`、`ErrorState` 和 `EmptyState`。
+- `asin` 仅作为原组件内部兼容字段,所有可见标识统一为“商品ID”。
+- 评论、退款原因和关联样本为 0 时不生成情绪、痛点、需求、归因或体验结论。
+- 浏览器运行时只允许同源静态数据或新的国内服务入口,不允许访问旧 Parse、Amazon 或跨境域名。

+ 15 - 4
README.md

@@ -2,7 +2,7 @@
 
 基于现有 `msq-voc-web` 前端框架打包出的国内电商 VOC 通用模板,首个案例为德玛仕京东商品。项目保留原系统的 Angular App Shell、顶部搜索、侧边导航、共享组件、VOC 页面结构和 ECharts 能力,只替换最小闭环所需的数据入口与国内商品语义。
 
-> 当前状态(2026-07-23):原项目 App Shell、共享组件体系、经营分析、市场与竞品、VOC 全局监测、商品 VOC 详情和 VOC 深度洞察框架已完成迁入;已启用页面均使用国内商品语义和统一状态组件
+> 当前状态(2026-07-23):国内 VOC 前端框架已经收口。原项目 App Shell、共享组件体系、经营分析、市场与竞品、VOC 监测、退款分析、新品开发、生命周期复盘和行动建议均已安全迁入;已启用页面统一使用国内商品语义和真实数据状态
 
 ## 迁入基线
 
@@ -21,12 +21,18 @@
 - `/domestic/voc-overview`:VOC 核心指标、情绪健康度、问题与热词总览
 - `/domestic/category-voc`:类目 VOC 四象限、下钻维度与诊断摘要
 - `/domestic/risk-alerts`:商品风险、场景化投诉与异常激增预警框架
+- `/domestic/voc-trend`:VOC 数据就绪状态与经营背景趋势
+- `/domestic/emotion`:情绪极性分布框架;无评论时展示真实空态
+- `/domestic/store-monitoring`:店铺经营、类目结构和退款表现
 - `/voc-insight/products`:德玛仕商品列表、搜索、排序和商品选择
 - `/voc-insight/detail?productId=11266507445`:复用原商品详情框架的国内 VOC 详情、评论空状态、经营建议和趋势
 - `/voc-insight/voice`:用户声音分类;无评论时展示真实空状态
 - `/voc-insight/pain-point`:痛点深度洞察
 - `/voc-insight/feature`:功能满足度
 - `/voc-insight/scenario`:使用场景
+- `/return-analysis/overview|attribution|correlation`:退款概况、归因和特征关联三层框架
+- `/product-development/trend|requirement|competitor-benchmark|product-definition|lifecycle`:新品开发五环节
+- `/action-suggestion/kanban|experience|iteration|strategy`:行动建议四页
 - `/data-status`:导入结果、待接依赖和数据质量问题
 
 `/login` 保留为独立 Parse 应用接入后的入口,但当前路由未启用 `AuthGuard`。
@@ -42,7 +48,10 @@
 5. 在商品 VOC 详情、经营详情、竞品关系和深度洞察页面之间保持商品上下文。
 6. 对当前真实的 0 条评论展示空状态,不伪造情绪、痛点或分析结果。
 7. 查看 VOC 趋势总览、类目 VOC 和风险预警的完整页面结构与真实空状态。
-8. 查看导入统计和数据质量问题。
+8. 查看退款汇总、高退款商品和等待订单级原因的归因/关联空态。
+9. 查看趋势、需求、竞品、产品定义和生命周期五个新品开发环节。
+10. 查看基于真实经营规则的行动建议,并与等待评论证据的体验建议分开。
+11. 查看导入统计和数据质量问题。
 
 德玛仕数据摘要:
 
@@ -124,8 +133,8 @@ npm run import:demashi -- "E:\xwechat_files\wxid_ay08t6ugo2h922_df4f\msg\file\20
 - `npm run build`:通过
 - `npm test`:5 项 Jasmine 测试通过
 - `npm run verify:data`:通过
-- 桌面端:14 个启用入口的页面身份、主内容、导航、空状态和交互通过
-- 移动端:14 个启用入口无页面级横向溢出,导航抽屉展开/收起、标题和控件布局通过
+- 桌面端:29 个启用入口的页面身份、主内容、导航、空状态和交互通过
+- 移动端:29 个启用入口无页面级横向溢出,导航抽屉展开/收起、标题和控件布局通过
 - 浏览器控制台:启用入口无 Angular 运行时错误、警告或失败请求
 - 运行时请求:当前激活闭环只请求同源德玛仕 JSON,没有访问旧 Parse 或旧跨境域名
 - `npm audit --omit=dev`:17 项原框架依赖公告(1 critical、11 high、5 moderate),正式公网部署前必须升级处理
@@ -141,6 +150,8 @@ npm run import:demashi -- "E:\xwechat_files\wxid_ay08t6ugo2h922_df4f\msg\file\20
 
 完整任务顺序见 [TASKS.md](TASKS.md)。
 
+原框架页面的启用、等价承接和排除边界见 [FRAMEWORK-MIGRATION.md](FRAMEWORK-MIGRATION.md)。
+
 ## 明确不包含
 
 - 原 Amazon、Sorftime、SP-API 和跨境订单链路

+ 6 - 1
TASKS.md

@@ -15,6 +15,11 @@
 - [x] 迁入经营概览、商品经营详情、品类分析和竞品关系页面
 - [x] 迁入 VOC 趋势总览、类目 VOC 和风险预警页面
 - [x] 迁入商品 VOC 详情,复用原详情布局、样式和共享组件,并接通六个内容标签
+- [x] 迁入 VOC 数据趋势、情绪分布和店铺监测细分页
+- [x] 迁入退款概况、退款归因和退款特征关联三层框架
+- [x] 迁入趋势、需求、竞品对标、产品定义和生命周期复盘五个新品开发环节
+- [x] 迁入建议看板、体验优化、产品迭代和长期策略四个行动建议页面
+- [x] 建立原框架安全启用、国内等价承接和刻意排除矩阵
 - [x] 复用原 `PageHeader`、`SummaryMetricCard`、`ContentCard`、`DataTableShell`、`ProductIdentity`、`BoardTabs`、`NeuButton`、`LoadingSpinner` 和 `EmptyState`
 - [x] 将左侧导航整理为“经营分析”“市场与竞品”“VOC 全局监测”“VOC 深度洞察”和“数据接入状态”五组结构
 - [x] 将原 `FilterBar` 解耦为可关闭旧数据选项自动加载的外部受控模式
@@ -24,7 +29,7 @@
 - [x] 增加数据接入状态页
 - [x] 将 Parse 和电商中转默认地址改为同源路径
 - [x] 移除旧跨境线上域名和旧 Parse 二次登录逻辑
-- [x] 完成 14 个启用入口的桌面与移动端响应式验收
+- [x] 完成 29 个启用入口的桌面与移动端响应式验收
 - [x] 修复类目 VOC 首次加载的 Angular 变更检测错误
 - [x] 完成构建、单测和数据完整性校验
 

+ 108 - 0
src/app/app.routes.ts

@@ -45,6 +45,24 @@ export const routes: Routes = [
         title: '风险预警',
         loadComponent: () => import('../modules/monitoring/pages/risk-alert/risk-alert.component').then((m) => m.RiskAlertComponent),
       },
+      {
+        path: 'voc-trend',
+        title: 'VOC 数据趋势',
+        data: { section: 'trend' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-monitoring-workbench.component').then((m) => m.DomesticMonitoringWorkbenchComponent),
+      },
+      {
+        path: 'emotion',
+        title: '情绪极性分布',
+        data: { section: 'emotion' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-monitoring-workbench.component').then((m) => m.DomesticMonitoringWorkbenchComponent),
+      },
+      {
+        path: 'store-monitoring',
+        title: '店铺监测融合',
+        data: { section: 'store' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-monitoring-workbench.component').then((m) => m.DomesticMonitoringWorkbenchComponent),
+      },
     ],
   },
   {
@@ -89,5 +107,95 @@ export const routes: Routes = [
     title: '数据接入状态',
     loadComponent: () => import('../modules/domestic-voc/data-status.component').then((m) => m.DomesticDataStatusComponent),
   },
+  {
+    path: 'return-analysis',
+    children: [
+      { path: '', redirectTo: 'overview', pathMatch: 'full' },
+      {
+        path: 'overview',
+        title: '退款整体概况',
+        data: { section: 'overview' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-return-analysis.component').then((m) => m.DomesticReturnAnalysisComponent),
+      },
+      {
+        path: 'attribution',
+        title: '退款归因深度下钻',
+        data: { section: 'attribution' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-return-analysis.component').then((m) => m.DomesticReturnAnalysisComponent),
+      },
+      {
+        path: 'correlation',
+        title: '退款特征关联',
+        data: { section: 'correlation' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-return-analysis.component').then((m) => m.DomesticReturnAnalysisComponent),
+      },
+    ],
+  },
+  {
+    path: 'product-development',
+    children: [
+      { path: '', redirectTo: 'trend', pathMatch: 'full' },
+      {
+        path: 'trend',
+        title: '趋势洞察',
+        data: { section: 'trend' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-product-development.component').then((m) => m.DomesticProductDevelopmentComponent),
+      },
+      {
+        path: 'requirement',
+        title: '需求挖掘',
+        data: { section: 'requirement' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-product-development.component').then((m) => m.DomesticProductDevelopmentComponent),
+      },
+      {
+        path: 'competitor-benchmark',
+        title: '竞品对标',
+        data: { section: 'benchmark' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-product-development.component').then((m) => m.DomesticProductDevelopmentComponent),
+      },
+      {
+        path: 'product-definition',
+        title: '产品定义',
+        data: { section: 'definition' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-product-development.component').then((m) => m.DomesticProductDevelopmentComponent),
+      },
+      {
+        path: 'lifecycle',
+        title: '产品生命周期复盘',
+        data: { section: 'lifecycle' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-product-development.component').then((m) => m.DomesticProductDevelopmentComponent),
+      },
+    ],
+  },
+  {
+    path: 'action-suggestion',
+    children: [
+      { path: '', redirectTo: 'kanban', pathMatch: 'full' },
+      {
+        path: 'kanban',
+        title: '行动建议看板',
+        data: { section: 'kanban' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-action-workbench.component').then((m) => m.DomesticActionWorkbenchComponent),
+      },
+      {
+        path: 'experience',
+        title: '体验优化提案',
+        data: { section: 'experience' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-action-workbench.component').then((m) => m.DomesticActionWorkbenchComponent),
+      },
+      {
+        path: 'iteration',
+        title: '产品迭代方向',
+        data: { section: 'iteration' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-action-workbench.component').then((m) => m.DomesticActionWorkbenchComponent),
+      },
+      {
+        path: 'strategy',
+        title: '长期策略调整',
+        data: { section: 'strategy' },
+        loadComponent: () => import('../modules/domestic-voc/framework/domestic-action-workbench.component').then((m) => m.DomesticActionWorkbenchComponent),
+      },
+    ],
+  },
   { path: '**', redirectTo: '/domestic/overview' },
 ];

+ 230 - 0
src/app/core/services/domestic-analytics-adapter.service.ts

@@ -0,0 +1,230 @@
+import { Injectable } from '@angular/core';
+import {
+  DomesticDataset,
+  DomesticDailyMetric,
+  DomesticMappingGroup,
+  DomesticProduct,
+} from '../models/domestic.models';
+
+export interface DomesticAggregateSummary {
+  gmv: number;
+  soldUnits: number;
+  transactionOrders: number;
+  transactionCustomers: number;
+  impressions: number;
+  clicks: number;
+  visitors: number;
+  cartUnits: number;
+  refundAmount: number;
+  refundUnits: number;
+  refundOrders: number;
+  conversionRate: number;
+  clickThroughRate: number;
+  refundRate: number;
+}
+
+export interface DomesticCategoryAggregate {
+  category: string;
+  productCount: number;
+  relationCount: number;
+  gmv: number;
+  soldUnits: number;
+  transactionOrders: number;
+  visitors: number;
+  refundAmount: number;
+  conversionRate: number;
+  refundRate: number;
+}
+
+export interface DomesticOperatingAction {
+  id: string;
+  title: string;
+  description: string;
+  priority: 'P0' | 'P1' | 'P2';
+  type: '退款' | '转化' | '竞品' | '数据';
+  metric: string;
+  productId?: string;
+  productTitle?: string;
+  category?: string;
+  evidence: string;
+}
+
+@Injectable({ providedIn: 'root' })
+export class DomesticAnalyticsAdapterService {
+  aggregate(dataset: DomesticDataset): DomesticAggregateSummary {
+    const totals = dataset.dailyTotals.reduce(
+      (acc, row) => ({
+        gmv: acc.gmv + row.gmv,
+        soldUnits: acc.soldUnits + row.soldUnits,
+        transactionOrders: acc.transactionOrders + row.transactionOrders,
+        transactionCustomers: acc.transactionCustomers + row.transactionCustomers,
+        impressions: acc.impressions + row.impressions,
+        clicks: acc.clicks + row.clicks,
+        visitors: acc.visitors + row.visitors,
+        cartUnits: acc.cartUnits + row.cartUnits,
+        refundAmount: acc.refundAmount + row.refundAmount,
+        refundUnits: acc.refundUnits + row.refundUnits,
+        refundOrders: acc.refundOrders + row.refundOrders,
+      }),
+      {
+        gmv: 0,
+        soldUnits: 0,
+        transactionOrders: 0,
+        transactionCustomers: 0,
+        impressions: 0,
+        clicks: 0,
+        visitors: 0,
+        cartUnits: 0,
+        refundAmount: 0,
+        refundUnits: 0,
+        refundOrders: 0,
+      },
+    );
+
+    return {
+      ...totals,
+      conversionRate: totals.visitors ? totals.transactionCustomers / totals.visitors : 0,
+      clickThroughRate: totals.impressions ? totals.clicks / totals.impressions : 0,
+      refundRate: totals.gmv ? totals.refundAmount / totals.gmv : 0,
+    };
+  }
+
+  categories(dataset: DomesticDataset, limit = 12): DomesticCategoryAggregate[] {
+    const groups = new Map<string, DomesticCategoryAggregate>();
+    dataset.products
+      .filter((product) => product.role === 'own')
+      .forEach((product) => {
+        const category = product.category2 || product.category1 || '未分类';
+        const current = groups.get(category) ?? {
+          category,
+          productCount: 0,
+          relationCount: 0,
+          gmv: 0,
+          soldUnits: 0,
+          transactionOrders: 0,
+          visitors: 0,
+          refundAmount: 0,
+          conversionRate: 0,
+          refundRate: 0,
+        };
+        current.productCount += 1;
+        current.relationCount += product.relationCount;
+        current.gmv += product.summary.gmv;
+        current.soldUnits += product.summary.soldUnits;
+        current.transactionOrders += product.summary.transactionOrders;
+        current.visitors += product.summary.visitors;
+        current.refundAmount += product.summary.refundAmount;
+        groups.set(category, current);
+      });
+
+    return [...groups.values()]
+      .map((item) => ({
+        ...item,
+        conversionRate: item.visitors ? item.transactionOrders / item.visitors : 0,
+        refundRate: item.gmv ? item.refundAmount / item.gmv : 0,
+      }))
+      .sort((a, b) => b.gmv - a.gmv)
+      .slice(0, limit);
+  }
+
+  topProducts(dataset: DomesticDataset, limit = 10): DomesticProduct[] {
+    return [...dataset.products]
+      .filter((product) => product.role === 'own')
+      .sort((a, b) => b.summary.gmv - a.summary.gmv)
+      .slice(0, limit);
+  }
+
+  refundRiskProducts(dataset: DomesticDataset, limit = 10): DomesticProduct[] {
+    return [...dataset.products]
+      .filter((product) => product.role === 'own' && product.summary.gmv > 0 && product.summary.refundAmount > 0)
+      .sort((a, b) => b.summary.refundToGmvRate - a.summary.refundToGmvRate)
+      .slice(0, limit);
+  }
+
+  selectedProduct(dataset: DomesticDataset, productId?: string): DomesticProduct | null {
+    if (productId) {
+      const selected = dataset.products.find((product) => product.productId === productId);
+      if (selected) return selected;
+    }
+    return this.topProducts(dataset, 1)[0] ?? dataset.products[0] ?? null;
+  }
+
+  benchmarkGroups(dataset: DomesticDataset, limit = 20): DomesticMappingGroup[] {
+    return [...dataset.mappingGroups]
+      .sort((a, b) => b.competitors.length - a.competitors.length)
+      .slice(0, limit);
+  }
+
+  daily(dataset: DomesticDataset): DomesticDailyMetric[] {
+    return [...dataset.dailyTotals].sort((a, b) => a.date.localeCompare(b.date));
+  }
+
+  actions(dataset: DomesticDataset, limit = 12): DomesticOperatingAction[] {
+    const actions: DomesticOperatingAction[] = [];
+
+    this.refundRiskProducts(dataset, 5)
+      .filter((product) => product.summary.refundToGmvRate >= 0.1)
+      .forEach((product) => {
+        const rate = product.summary.refundToGmvRate;
+        actions.push({
+          id: `refund:${product.productId}`,
+          title: `核查「${product.model || product.productId}」退款异常`,
+          description: '退款金额占比较高。当前没有退货原因明细,应先核对售后单、型号说明和履约记录,再形成归因结论。',
+          priority: rate >= 0.2 ? 'P0' : 'P1',
+          type: '退款',
+          metric: `${(rate * 100).toFixed(1)}%`,
+          productId: product.productId,
+          productTitle: product.title,
+          category: product.category2 || product.category1,
+          evidence: '商品经营汇总 · 退款金额 / 成交金额',
+        });
+      });
+
+    this.topProducts(dataset, 20)
+      .filter((product) => product.summary.visitors >= 100 && product.summary.conversionRate < 0.025)
+      .slice(0, 3)
+      .forEach((product) => {
+        actions.push({
+          id: `conversion:${product.productId}`,
+          title: `复核「${product.model || product.productId}」成交路径`,
+          description: '商品已有稳定访问但访客转化偏低,建议先检查商品信息完整度、规格表达和加购到成交环节。',
+          priority: 'P1',
+          type: '转化',
+          metric: `${(product.summary.conversionRate * 100).toFixed(1)}%`,
+          productId: product.productId,
+          productTitle: product.title,
+          category: product.category2 || product.category1,
+          evidence: '商品经营汇总 · 成交客户 / 访客',
+        });
+      });
+
+    if (dataset.quality.mappingsWithoutCompetitor.length) {
+      actions.push({
+        id: 'competitor:mapping-gap',
+        title: '补齐无竞品的商品映射',
+        description: '部分自营商品已经进入映射表,但没有有效竞品关系,无法形成完整的横向对标。',
+        priority: 'P2',
+        type: '竞品',
+        metric: `${dataset.quality.mappingsWithoutCompetitor.length} 个商品`,
+        evidence: '竞品映射质量检查',
+      });
+    }
+
+    if (dataset.summary.reviewCount === 0) {
+      actions.push({
+        id: 'data:reviews',
+        title: '接入评论明细后再生成 VOC 行动',
+        description: '当前源文件没有评论正文,情绪、痛点、体验提案和产品迭代结论均保持空态。',
+        priority: 'P0',
+        type: '数据',
+        metric: '0 条评论',
+        evidence: '数据集评论样本计数',
+      });
+    }
+
+    const priorityOrder: Record<DomesticOperatingAction['priority'], number> = { P0: 0, P1: 1, P2: 2 };
+    return actions
+      .sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])
+      .slice(0, limit);
+  }
+}

+ 1 - 1
src/modules/domestic-voc/data-status.component.ts

@@ -49,7 +49,7 @@ import { SummaryMetricCardComponent } from '../shared/components/summary-metric-
           </app-content-card>
           <app-content-card title="待接通依赖" subtitle="不会访问原跨境线上数据库" tone="amber">
             <ol class="dependency-list">
-              <li><span class="status-pill success">已完成</span> 原 VOC 前端框架迁入</li>
+              <li><span class="status-pill success">已完成</span> 国内 VOC 前端框架迁入(29 个业务入口)</li>
               <li><span class="status-pill success">已完成</span> 商品 ID 兼容层与本地案例数据</li>
               <li><span class="status-pill">待处理</span> 独立 PostgreSQL / Parse 应用</li>
               <li><span class="status-pill">待处理</span> 评论采集服务</li>

+ 94 - 0
src/modules/domestic-voc/framework/domestic-action-workbench.component.html

@@ -0,0 +1,94 @@
+<section class="framework-page">
+  @if (loading) {
+    <div class="framework-state"><app-loading-spinner size="lg" text="正在加载行动建议框架..."></app-loading-spinner></div>
+  } @else if (error) {
+    <div class="framework-state"><app-error-state [description]="error"></app-error-state></div>
+  } @else if (dataset) {
+    <app-page-header
+      [title]="title"
+      eyebrow="Action Suggestion"
+      [description]="description"
+      icon="✓"
+      [badges]="[{ label: '经营建议已接通', tone: 'green' }, { label: dataset.summary.reviewCount ? 'VOC 建议已接通' : 'VOC 建议待接入', tone: dataset.summary.reviewCount ? 'green' : 'amber' }]"
+      [meta]="[{ label: '案例', value: dataset.caseName }, { label: '建议项', value: actions.length }, { label: '证据评论', value: dataset.summary.reviewCount }]">
+    </app-page-header>
+
+    <app-board-tabs class="framework-tabs" [tabs]="tabs" [activeKey]="section" density="compact"></app-board-tabs>
+
+    @if (section === 'kanban') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="建议总数" [value]="actions.length" icon="▦" tone="blue" description="基于真实数据规则生成"></app-summary-metric-card>
+        <app-summary-metric-card label="P0 事项" [value]="p0Count" icon="!" tone="red" description="优先处理"></app-summary-metric-card>
+        <app-summary-metric-card label="P1 事项" [value]="p1Count" icon="△" tone="amber" description="重点跟进"></app-summary-metric-card>
+        <app-summary-metric-card label="评论证据" [value]="dataset.summary.reviewCount" icon="▤" tone="default" description="VOC 建议样本"></app-summary-metric-card>
+      </div>
+
+      <app-alert-card title="建议来源已分层" description="退款、转化和竞品事项来自经营或映射数据;评论为 0 时不生成体验痛点、用户需求或情绪建议。" [primaryMetric]="actions.length + ' 项'" icon="i" tone="blue"></app-alert-card>
+
+      <app-priority-accordion
+        [sections]="accordionSections"
+        [(expandedIds)]="expandedIds"
+        variant="outlined">
+        <ng-template #sectionBody let-section>
+          @if (actionById(section.id); as action) {
+            <div class="info-list">
+              <div class="info-row"><div class="info-copy"><strong>建议说明</strong><span>{{ action.description }}</span></div><span class="info-value">{{ action.priority }}</span></div>
+              <div class="info-row"><div class="info-copy"><strong>证据来源</strong><span>{{ action.evidence }}</span></div><span class="info-value">{{ action.metric }}</span></div>
+              @if (action.productId) {
+                <div class="info-row"><div class="info-copy"><strong>关联商品</strong><span>{{ action.productTitle }}</span></div><span class="info-value">{{ action.productId }}</span></div>
+              }
+            </div>
+          }
+        </ng-template>
+      </app-priority-accordion>
+    }
+
+    @if (section === 'experience') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="体验提案" value="0" icon="◇" tone="default" description="等待用户原声"></app-summary-metric-card>
+        <app-summary-metric-card label="痛点证据" value="0" icon="!" tone="amber" description="当前评论样本"></app-summary-metric-card>
+        <app-summary-metric-card label="使用场景" value="0" icon="⌖" tone="blue" description="等待场景识别"></app-summary-metric-card>
+        <app-summary-metric-card label="满意度主题" value="0" icon="◉" tone="purple" description="等待主题分析"></app-summary-metric-card>
+      </div>
+      <app-content-card title="体验优化提案" subtitle="从评论痛点、售后原声和场景证据形成" variant="dashed" tone="amber">
+        <app-empty-state variant="low-sample" title="暂无可验证的体验提案" description="当前不根据销量、转化或退款汇总猜测用户体验问题。"></app-empty-state>
+      </app-content-card>
+    }
+
+    @if (section === 'iteration') {
+      <app-alert-card title="产品迭代方向仅包含经营复核项" description="以下事项可用于排查,但在补充评论和退款原因前,不能直接认定为产品设计缺陷。" [primaryMetric]="operatingActions.length + ' 项'" icon="△" tone="amber"></app-alert-card>
+      <div class="stack">
+        @for (action of operatingActions; track action.id) {
+          <app-alert-card [title]="action.title" [description]="action.description" [primaryMetric]="action.metric" [icon]="action.type === '退款' ? '↩' : '◎'" [tone]="toneForAction(action)"></app-alert-card>
+        } @empty {
+          <app-content-card title="产品迭代方向" variant="dashed"><app-empty-state title="暂无经营风险迭代项" description="当前阈值下没有需要优先复核的商品。"></app-empty-state></app-content-card>
+        }
+      </div>
+    }
+
+    @if (section === 'strategy') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="二级类目" [value]="dataset.summary.category2Count" icon="▦" tone="blue" description="经营覆盖"></app-summary-metric-card>
+        <app-summary-metric-card label="三级类目" [value]="dataset.summary.category3Count" icon="≡" tone="purple" description="细分覆盖"></app-summary-metric-card>
+        <app-summary-metric-card label="竞品关系" [value]="dataset.summary.relations" icon="↔" tone="green" description="横向对标基础"></app-summary-metric-card>
+        <app-summary-metric-card label="评论样本" [value]="dataset.summary.reviewCount" icon="▤" tone="default" description="长期 VOC 证据"></app-summary-metric-card>
+      </div>
+      <div class="two-column equal-column">
+        <app-content-card title="类目经营结构" subtitle="当前周期成交规模与退款表现" tone="blue">
+          <div class="rank-list">
+            @for (item of categories; track item.category) {
+              <div class="rank-row"><div class="rank-copy"><strong>{{ item.category }}</strong><span>{{ item.productCount }} 个商品 · 退款金额占比 {{ percent(item.refundRate) }}</span></div><span class="rank-value">{{ currency(item.gmv) }}</span></div>
+            }
+          </div>
+        </app-content-card>
+        <app-content-card title="策略证据边界" subtitle="长期策略需要更长周期和用户证据" tone="amber">
+          <div class="info-list">
+            <div class="info-row"><div class="info-copy"><strong>当前经营周期</strong><span>{{ dataset.source.dateRange.start }} 至 {{ dataset.source.dateRange.end }}</span></div><span class="info-value">短周期</span></div>
+            <div class="info-row"><div class="info-copy"><strong>竞品关系</strong><span>国内商品映射</span></div><span class="info-value">{{ dataset.summary.relations }} 条</span></div>
+            <div class="info-row"><div class="info-copy"><strong>用户声音</strong><span>评论正文与长期主题</span></div><span class="info-value">待接入</span></div>
+          </div>
+        </app-content-card>
+      </div>
+    }
+  }
+</section>

+ 158 - 0
src/modules/domestic-voc/framework/domestic-action-workbench.component.ts

@@ -0,0 +1,158 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Subject, combineLatest, takeUntil } from 'rxjs';
+import { DomesticDataset } from '../../../app/core/models/domestic.models';
+import {
+  DomesticAnalyticsAdapterService,
+  DomesticCategoryAggregate,
+  DomesticOperatingAction,
+} from '../../../app/core/services/domestic-analytics-adapter.service';
+import { DomesticDatasetService } from '../../../app/core/services/domestic-dataset.service';
+import { AlertCardComponent } from '../../shared/components/alert-card/alert-card.component';
+import { BoardTabItem, BoardTabsComponent } from '../../shared/components/board-tabs/board-tabs.component';
+import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
+import { EmptyStateComponent } from '../../shared/components/empty-state/empty-state.component';
+import { ErrorStateComponent } from '../../shared/components/error-state/error-state.component';
+import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
+import { PageHeaderComponent } from '../../shared/components/page-header/page-header.component';
+import {
+  PriorityAccordionComponent,
+  PriorityAccordionSection,
+} from '../../shared/components/priority-accordion/priority-accordion.component';
+import { SummaryMetricCardComponent } from '../../shared/components/summary-metric-card/summary-metric-card.component';
+
+type ActionSection = 'kanban' | 'experience' | 'iteration' | 'strategy';
+
+@Component({
+  selector: 'app-domestic-action-workbench',
+  standalone: true,
+  imports: [
+    CommonModule,
+    AlertCardComponent,
+    BoardTabsComponent,
+    ContentCardComponent,
+    EmptyStateComponent,
+    ErrorStateComponent,
+    LoadingSpinnerComponent,
+    PageHeaderComponent,
+    PriorityAccordionComponent,
+    SummaryMetricCardComponent,
+  ],
+  templateUrl: './domestic-action-workbench.component.html',
+  styleUrls: ['./domestic-framework.shared.scss'],
+})
+export class DomesticActionWorkbenchComponent implements OnInit, OnDestroy {
+  private readonly destroy$ = new Subject<void>();
+
+  readonly tabs: BoardTabItem[] = [
+    { key: 'kanban', label: '建议看板', icon: '▦', route: ['/action-suggestion/kanban'] },
+    { key: 'experience', label: '体验优化', icon: '◉', route: ['/action-suggestion/experience'] },
+    { key: 'iteration', label: '产品迭代', icon: '△', route: ['/action-suggestion/iteration'] },
+    { key: 'strategy', label: '长期策略', icon: '↗', route: ['/action-suggestion/strategy'] },
+  ];
+
+  section: ActionSection = 'kanban';
+  loading = true;
+  error = '';
+  dataset: DomesticDataset | null = null;
+  actions: DomesticOperatingAction[] = [];
+  categories: DomesticCategoryAggregate[] = [];
+  accordionSections: PriorityAccordionSection[] = [];
+  expandedIds: string[] = [];
+
+  constructor(
+    private readonly route: ActivatedRoute,
+    private readonly datasetService: DomesticDatasetService,
+    private readonly analytics: DomesticAnalyticsAdapterService,
+  ) {}
+
+  ngOnInit(): void {
+    combineLatest([this.route.data, this.datasetService.dataset$])
+      .pipe(takeUntil(this.destroy$))
+      .subscribe({
+        next: ([routeData, dataset]) => {
+          this.section = (routeData['section'] || 'kanban') as ActionSection;
+          this.dataset = dataset;
+          this.actions = this.analytics.actions(dataset, 12);
+          this.categories = this.analytics.categories(dataset, 8);
+          this.accordionSections = this.actions.map((action) => ({
+            id: action.id,
+            title: action.title,
+            subtitle: `${action.type} · ${action.evidence}`,
+            badge: `${action.priority} · ${action.metric}`,
+            icon: action.type === '退款' ? '↩' : action.type === '转化' ? '◎' : action.type === '竞品' ? '↔' : '▤',
+            tone: this.toneForPriority(action.priority),
+          }));
+          this.expandedIds = this.accordionSections.slice(0, 1).map((item) => item.id);
+          this.loading = false;
+          this.error = '';
+        },
+        error: () => {
+          this.error = '行动建议框架数据加载失败,请检查国内数据文件。';
+          this.loading = false;
+        },
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+  }
+
+  get title(): string {
+    const labels: Record<ActionSection, string> = {
+      kanban: '行动建议看板',
+      experience: '体验优化提案',
+      iteration: '产品迭代方向',
+      strategy: '长期策略调整',
+    };
+    return labels[this.section];
+  }
+
+  get description(): string {
+    const labels: Record<ActionSection, string> = {
+      kanban: '汇总有真实指标依据的退款、转化、竞品和数据准备事项。',
+      experience: '保留原体验提案框架,等待评论和售后原声后形成可验证的用户体验建议。',
+      iteration: '从当前经营风险中提取产品复核方向,并与尚缺的 VOC 证据严格区分。',
+      strategy: '结合类目经营结构和竞品覆盖,形成当前周期的策略观察。',
+    };
+    return labels[this.section];
+  }
+
+  get p0Count(): number {
+    return this.actions.filter((action) => action.priority === 'P0').length;
+  }
+
+  get p1Count(): number {
+    return this.actions.filter((action) => action.priority === 'P1').length;
+  }
+
+  get operatingActions(): DomesticOperatingAction[] {
+    return this.actions.filter((action) => action.type === '退款' || action.type === '转化');
+  }
+
+  actionById(id: string): DomesticOperatingAction | undefined {
+    return this.actions.find((action) => action.id === id);
+  }
+
+  toneForAction(action: DomesticOperatingAction): 'blue' | 'green' | 'amber' | 'red' | 'neutral' {
+    if (action.priority === 'P0') return 'red';
+    if (action.priority === 'P1') return 'amber';
+    return 'blue';
+  }
+
+  currency(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  percent(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(value || 0);
+  }
+
+  private toneForPriority(priority: DomesticOperatingAction['priority']): 'red' | 'amber' | 'blue' {
+    if (priority === 'P0') return 'red';
+    if (priority === 'P1') return 'amber';
+    return 'blue';
+  }
+}

+ 204 - 0
src/modules/domestic-voc/framework/domestic-framework.shared.scss

@@ -0,0 +1,204 @@
+:host {
+  display: block;
+  min-width: 0;
+}
+
+.framework-page {
+  min-height: 100vh;
+  padding: 24px;
+  background: #f8f9fa;
+  box-sizing: border-box;
+  overflow-x: hidden;
+}
+
+.framework-tabs {
+  margin: 0 0 18px;
+}
+
+.metric-grid {
+  display: grid;
+  grid-template-columns: repeat(4, minmax(0, 1fr));
+  gap: 14px;
+  margin-bottom: 18px;
+}
+
+.two-column {
+  display: grid;
+  grid-template-columns: minmax(0, 1.6fr) minmax(280px, 1fr);
+  gap: 16px;
+  align-items: start;
+  margin-bottom: 18px;
+}
+
+.equal-column {
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.stack {
+  display: grid;
+  gap: 14px;
+}
+
+.chart {
+  width: 100%;
+  height: 340px;
+}
+
+.compact-chart {
+  height: 300px;
+}
+
+.data-table {
+  width: 100%;
+  border-collapse: collapse;
+  table-layout: fixed;
+}
+
+.data-table th,
+.data-table td {
+  padding: 11px 12px;
+  border-bottom: 1px solid #eef2f7;
+  color: #475569;
+  font-size: 12px;
+  line-height: 1.45;
+  text-align: left;
+  vertical-align: top;
+  overflow-wrap: anywhere;
+}
+
+.data-table th {
+  color: #64748b;
+  font-weight: 700;
+  background: #f8fafc;
+}
+
+.data-table td strong {
+  color: #1f2937;
+}
+
+.product-cell {
+  width: 34%;
+}
+
+.rank-list,
+.info-list {
+  display: grid;
+  gap: 10px;
+}
+
+.rank-row,
+.info-row {
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) auto;
+  gap: 12px;
+  align-items: center;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #eef2f7;
+}
+
+.rank-row:last-child,
+.info-row:last-child {
+  padding-bottom: 0;
+  border-bottom: 0;
+}
+
+.rank-copy,
+.info-copy {
+  min-width: 0;
+}
+
+.rank-copy strong,
+.info-copy strong {
+  display: block;
+  color: #1f2937;
+  font-size: 13px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.rank-copy span,
+.info-copy span {
+  display: block;
+  margin-top: 3px;
+  color: #64748b;
+  font-size: 11px;
+}
+
+.rank-value,
+.info-value {
+  color: #2563eb;
+  font-size: 13px;
+  font-weight: 800;
+  white-space: nowrap;
+}
+
+.status-grid {
+  display: grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 12px;
+}
+
+.status-item {
+  min-width: 0;
+  padding: 14px;
+  border: 1px solid #e8eef6;
+  border-radius: 8px;
+  background: #fff;
+}
+
+.status-item span {
+  display: block;
+  color: #64748b;
+  font-size: 11px;
+}
+
+.status-item strong {
+  display: block;
+  margin-top: 5px;
+  color: #1f2937;
+  font-size: 15px;
+}
+
+.context-strip {
+  margin-bottom: 18px;
+}
+
+.framework-state {
+  min-height: 480px;
+  display: grid;
+  place-items: center;
+}
+
+@media (max-width: 1180px) {
+  .metric-grid {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+}
+
+@media (max-width: 900px) {
+  .two-column,
+  .equal-column {
+    grid-template-columns: 1fr;
+  }
+}
+
+@media (max-width: 720px) {
+  .framework-page {
+    padding: 16px;
+  }
+
+  .metric-grid,
+  .status-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .chart,
+  .compact-chart {
+    height: 290px;
+  }
+
+  .product-cell {
+    width: 48%;
+  }
+}

+ 92 - 0
src/modules/domestic-voc/framework/domestic-monitoring-workbench.component.html

@@ -0,0 +1,92 @@
+<section class="framework-page">
+  @if (loading) {
+    <div class="framework-state"><app-loading-spinner size="lg" text="正在加载监测框架..."></app-loading-spinner></div>
+  } @else if (error) {
+    <div class="framework-state"><app-error-state [description]="error"></app-error-state></div>
+  } @else if (dataset && summary) {
+    <app-page-header
+      [title]="title"
+      eyebrow="Domestic Monitoring"
+      [description]="description"
+      icon="◉"
+      [badges]="[{ label: dataset.platform, tone: 'blue' }, { label: dataset.summary.reviewCount ? '评论已接入' : '评论待接入', tone: dataset.summary.reviewCount ? 'green' : 'amber' }]"
+      [meta]="[{ label: '案例', value: dataset.caseName }, { label: '数据周期', value: dataset.source.dateRange.start + ' 至 ' + dataset.source.dateRange.end }]"
+      [sampleSize]="dataset.summary.reviewCount"
+      [confidence]="dataset.summary.reviewCount ? 'medium' : 'low'">
+    </app-page-header>
+
+    <app-board-tabs class="framework-tabs" [tabs]="tabs" [activeKey]="section" density="compact"></app-board-tabs>
+
+    @if (section === 'trend') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="评论样本" [value]="number(dataset.summary.reviewCount)" icon="▤" [tone]="dataset.summary.reviewCount ? 'green' : 'default'" description="VOC 趋势统计口径"></app-summary-metric-card>
+        <app-summary-metric-card label="数据天数" [value]="number(dataset.dailyTotals.length)" icon="▦" tone="blue" description="当前经营指标周期"></app-summary-metric-card>
+        <app-summary-metric-card label="经营商品" [value]="number(dataset.summary.metricProducts)" icon="□" tone="purple" description="可关联评论的商品池"></app-summary-metric-card>
+        <app-summary-metric-card label="评论覆盖率" [value]="dataset.summary.reviewCount ? '已接入' : '0.0%'" icon="◎" tone="amber" description="当前文件无评论正文"></app-summary-metric-card>
+      </div>
+
+      <app-alert-card
+        title="VOC 趋势等待评论数据"
+        description="下方经营趋势用于核对业务背景,不参与情绪或话题结论;评论明细接入后,原趋势框架可在同一页面增加声量序列。"
+        primaryMetric="0 条评论"
+        icon="!"
+        tone="amber">
+      </app-alert-card>
+
+      <app-content-card title="经营背景趋势" subtitle="成交金额与退款金额,为后续 VOC 异常提供业务对照" tone="blue">
+        <div echarts class="chart" [options]="trendOptions"></div>
+      </app-content-card>
+    }
+
+    @if (section === 'emotion') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="正向评论" value="-" icon="↑" tone="green" description="等待真实评分"></app-summary-metric-card>
+        <app-summary-metric-card label="中性评论" value="-" icon="—" tone="amber" description="等待真实评分"></app-summary-metric-card>
+        <app-summary-metric-card label="负向评论" value="-" icon="↓" tone="red" description="等待真实评分"></app-summary-metric-card>
+        <app-summary-metric-card label="评论样本" [value]="dataset.summary.reviewCount" icon="▤" tone="default" description="当前可分析样本"></app-summary-metric-card>
+      </div>
+
+      <div class="two-column equal-column">
+        <app-content-card title="情绪分布" subtitle="按真实评论评分和文本分析生成" variant="dashed" tone="amber">
+          <app-empty-state variant="low-sample" title="暂无情绪样本" description="当前德玛仕源文件不包含评论正文,因此不展示正向、负向或中性占比。"></app-empty-state>
+        </app-content-card>
+        <app-content-card title="关键词与情绪主题" subtitle="保留原系统的话题和关键词分析入口" variant="dashed" tone="purple">
+          <app-empty-state title="暂无关键词证据" description="评论接入后才能提取情绪主题、典型短语和代表性原声。"></app-empty-state>
+        </app-content-card>
+      </div>
+    }
+
+    @if (section === 'store') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="成交金额" [value]="currency(summary.gmv)" icon="¥" tone="blue" [description]="number(summary.transactionOrders) + ' 笔成交订单'"></app-summary-metric-card>
+        <app-summary-metric-card label="经营商品" [value]="number(dataset.summary.metricProducts)" icon="□" tone="green" [description]="number(dataset.summary.category2Count) + ' 个二级类目'"></app-summary-metric-card>
+        <app-summary-metric-card label="访客转化率" [value]="percent(summary.conversionRate)" icon="◎" tone="purple" [description]="number(summary.visitors) + ' 位访客'"></app-summary-metric-card>
+        <app-summary-metric-card label="退款金额占比" [value]="percent(summary.refundRate)" icon="↩" [tone]="summary.refundRate >= 0.12 ? 'red' : 'amber'" [description]="currency(summary.refundAmount) + ' 退款金额'"></app-summary-metric-card>
+      </div>
+
+      <div class="two-column">
+        <app-content-card title="类目成交结构" subtitle="按二级类目汇总成交金额" tone="blue">
+          <div echarts class="chart compact-chart" [options]="storeOptions"></div>
+        </app-content-card>
+        <app-content-card title="店铺数据就绪度" subtitle="当前德玛仕案例的可用业务范围" tone="green">
+          <div class="info-list">
+            <div class="info-row"><div class="info-copy"><strong>商品经营指标</strong><span>成交、流量、转化、退款</span></div><span class="info-value">已接通</span></div>
+            <div class="info-row"><div class="info-copy"><strong>竞品关系</strong><span>自营商品与竞品映射</span></div><span class="info-value">{{ dataset.summary.relations }} 条</span></div>
+            <div class="info-row"><div class="info-copy"><strong>评论明细</strong><span>情绪、主题与痛点来源</span></div><span class="info-value">{{ dataset.summary.reviewCount }} 条</span></div>
+          </div>
+        </app-content-card>
+      </div>
+
+      <app-data-table-shell title="类目经营监测" subtitle="店铺内各二级类目的经营与退款表现" [count]="categories.length" [scrollable]="true">
+        <table class="data-table">
+          <thead><tr><th>类目</th><th>商品数</th><th>成交金额</th><th>成交件数</th><th>转化率</th><th>退款金额占比</th></tr></thead>
+          <tbody>
+            @for (item of categories; track item.category) {
+              <tr><td><strong>{{ item.category }}</strong></td><td>{{ number(item.productCount) }}</td><td>{{ currency(item.gmv) }}</td><td>{{ number(item.soldUnits) }}</td><td>{{ percent(item.conversionRate) }}</td><td>{{ percent(item.refundRate) }}</td></tr>
+            }
+          </tbody>
+        </table>
+      </app-data-table-shell>
+    }
+  }
+</section>

+ 146 - 0
src/modules/domestic-voc/framework/domestic-monitoring-workbench.component.ts

@@ -0,0 +1,146 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import type { EChartsOption } from 'echarts';
+import { NgxEchartsModule } from 'ngx-echarts';
+import { Subject, combineLatest, takeUntil } from 'rxjs';
+import { DomesticDataset } from '../../../app/core/models/domestic.models';
+import {
+  DomesticAggregateSummary,
+  DomesticAnalyticsAdapterService,
+  DomesticCategoryAggregate,
+} from '../../../app/core/services/domestic-analytics-adapter.service';
+import { DomesticDatasetService } from '../../../app/core/services/domestic-dataset.service';
+import { AlertCardComponent } from '../../shared/components/alert-card/alert-card.component';
+import { BoardTabItem, BoardTabsComponent } from '../../shared/components/board-tabs/board-tabs.component';
+import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
+import { DataTableShellComponent } from '../../shared/components/data-table-shell/data-table-shell.component';
+import { EmptyStateComponent } from '../../shared/components/empty-state/empty-state.component';
+import { ErrorStateComponent } from '../../shared/components/error-state/error-state.component';
+import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
+import { PageHeaderComponent } from '../../shared/components/page-header/page-header.component';
+import { SummaryMetricCardComponent } from '../../shared/components/summary-metric-card/summary-metric-card.component';
+
+type MonitoringSection = 'trend' | 'emotion' | 'store';
+
+@Component({
+  selector: 'app-domestic-monitoring-workbench',
+  standalone: true,
+  imports: [
+    CommonModule,
+    NgxEchartsModule,
+    AlertCardComponent,
+    BoardTabsComponent,
+    ContentCardComponent,
+    DataTableShellComponent,
+    EmptyStateComponent,
+    ErrorStateComponent,
+    LoadingSpinnerComponent,
+    PageHeaderComponent,
+    SummaryMetricCardComponent,
+  ],
+  templateUrl: './domestic-monitoring-workbench.component.html',
+  styleUrls: ['./domestic-framework.shared.scss'],
+})
+export class DomesticMonitoringWorkbenchComponent implements OnInit, OnDestroy {
+  private readonly destroy$ = new Subject<void>();
+
+  readonly tabs: BoardTabItem[] = [
+    { key: 'trend', label: 'VOC 数据趋势', icon: '↗', route: ['/domestic/voc-trend'] },
+    { key: 'emotion', label: '情绪分布', icon: '◉', route: ['/domestic/emotion'] },
+    { key: 'store', label: '店铺监测', icon: '▦', route: ['/domestic/store-monitoring'] },
+  ];
+
+  section: MonitoringSection = 'trend';
+  loading = true;
+  error = '';
+  dataset: DomesticDataset | null = null;
+  summary: DomesticAggregateSummary | null = null;
+  categories: DomesticCategoryAggregate[] = [];
+  trendOptions: EChartsOption = {};
+  storeOptions: EChartsOption = {};
+
+  constructor(
+    private readonly route: ActivatedRoute,
+    private readonly datasetService: DomesticDatasetService,
+    private readonly analytics: DomesticAnalyticsAdapterService,
+  ) {}
+
+  ngOnInit(): void {
+    combineLatest([this.route.data, this.datasetService.dataset$])
+      .pipe(takeUntil(this.destroy$))
+      .subscribe({
+        next: ([routeData, dataset]) => {
+          this.section = (routeData['section'] || 'trend') as MonitoringSection;
+          this.dataset = dataset;
+          this.summary = this.analytics.aggregate(dataset);
+          this.categories = this.analytics.categories(dataset, 10);
+          this.trendOptions = this.buildTrendOptions(dataset);
+          this.storeOptions = this.buildStoreOptions(this.categories);
+          this.loading = false;
+          this.error = '';
+        },
+        error: () => {
+          this.error = '监测数据加载失败,请检查国内数据文件。';
+          this.loading = false;
+        },
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+  }
+
+  get title(): string {
+    if (this.section === 'emotion') return '情绪极性分布';
+    if (this.section === 'store') return '店铺监测融合';
+    return 'VOC 数据趋势';
+  }
+
+  get description(): string {
+    if (this.section === 'emotion') return '保留原情绪分析框架,并严格依据真实评论样本决定是否生成情绪结论。';
+    if (this.section === 'store') return '汇总店铺经营健康度、商品规模、类目结构与退款表现。';
+    return '同时呈现评论数据就绪状态和可用的经营趋势,不将经营指标伪装成用户声音。';
+  }
+
+  currency(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  number(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  percent(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(value || 0);
+  }
+
+  private buildTrendOptions(dataset: DomesticDataset): EChartsOption {
+    const daily = this.analytics.daily(dataset);
+    return {
+      color: ['#2563eb', '#dc2626'],
+      tooltip: { trigger: 'axis' },
+      legend: { data: ['成交金额', '退款金额'], top: 0, right: 0 },
+      grid: { left: 18, right: 18, top: 48, bottom: 16, containLabel: true },
+      xAxis: { type: 'category', boundaryGap: false, data: daily.map((item) => item.date.slice(5)), axisLabel: { color: '#64748b' } },
+      yAxis: { type: 'value', axisLabel: { color: '#64748b' }, splitLine: { lineStyle: { color: '#eef2f7' } } },
+      series: [
+        { name: '成交金额', type: 'line', smooth: true, data: daily.map((item) => item.gmv), areaStyle: { color: 'rgba(37,99,235,.08)' } },
+        { name: '退款金额', type: 'line', smooth: true, data: daily.map((item) => item.refundAmount) },
+      ],
+    };
+  }
+
+  private buildStoreOptions(categories: DomesticCategoryAggregate[]): EChartsOption {
+    const rows = [...categories].slice(0, 8).reverse();
+    return {
+      color: ['#2563eb'],
+      tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
+      grid: { left: 12, right: 24, top: 12, bottom: 12, containLabel: true },
+      xAxis: { type: 'value', axisLabel: { color: '#64748b' }, splitLine: { lineStyle: { color: '#eef2f7' } } },
+      yAxis: { type: 'category', data: rows.map((item) => item.category), axisLabel: { color: '#475569', width: 110, overflow: 'truncate' } },
+      series: [{ type: 'bar', data: rows.map((item) => item.gmv), barMaxWidth: 18, itemStyle: { borderRadius: [0, 4, 4, 0] } }],
+    };
+  }
+}

+ 116 - 0
src/modules/domestic-voc/framework/domestic-product-development.component.html

@@ -0,0 +1,116 @@
+<section class="framework-page">
+  @if (loading) {
+    <div class="framework-state"><app-loading-spinner size="lg" text="正在加载新品开发框架..."></app-loading-spinner></div>
+  } @else if (error) {
+    <div class="framework-state"><app-error-state [description]="error"></app-error-state></div>
+  } @else if (dataset && summary) {
+    <app-page-header
+      [title]="title"
+      eyebrow="Product Development"
+      [description]="description"
+      icon="△"
+      [badges]="[{ label: '国内通用框架', tone: 'blue' }, { label: dataset.summary.reviewCount ? 'VOC 已接入' : 'VOC 待接入', tone: dataset.summary.reviewCount ? 'green' : 'amber' }]"
+      [meta]="[{ label: '案例', value: dataset.caseName }, { label: '商品', value: dataset.summary.metricProducts }, { label: '竞品关系', value: dataset.summary.relations }]">
+    </app-page-header>
+
+    <app-board-tabs class="framework-tabs" [tabs]="tabs" [activeKey]="section" density="compact"></app-board-tabs>
+
+    @if (section === 'definition' || section === 'lifecycle') {
+      <div class="context-strip"><app-voc-product-picker></app-voc-product-picker></div>
+    }
+
+    @if (section === 'trend') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="成交金额" [value]="currency(summary.gmv)" icon="¥" tone="blue" [description]="dataset.dailyTotals.length + ' 天经营周期'"></app-summary-metric-card>
+        <app-summary-metric-card label="经营商品" [value]="number(dataset.summary.metricProducts)" icon="□" tone="green" [description]="dataset.summary.category2Count + ' 个二级类目'"></app-summary-metric-card>
+        <app-summary-metric-card label="成交件数" [value]="number(summary.soldUnits)" icon="▣" tone="purple" [description]="number(summary.transactionOrders) + ' 笔订单'"></app-summary-metric-card>
+        <app-summary-metric-card label="竞品关系" [value]="number(dataset.summary.relations)" icon="↔" tone="amber" [description]="dataset.summary.uniqueCompetitorProducts + ' 个唯一竞品'"></app-summary-metric-card>
+      </div>
+
+      <div class="two-column">
+        <app-content-card title="类目经营信号" subtitle="成交金额与退款金额,仅代表当前数据周期" tone="blue">
+          <div echarts class="chart" [options]="categoryOptions"></div>
+        </app-content-card>
+        <app-content-card title="类目机会观察" subtitle="按当前成交金额排序" tone="green">
+          <div class="rank-list">
+            @for (item of categories.slice(0, 6); track item.category) {
+              <div class="rank-row"><div class="rank-copy"><strong>{{ item.category }}</strong><span>{{ item.productCount }} 个商品 · 转化 {{ percent(item.conversionRate) }}</span></div><span class="rank-value">{{ currency(item.gmv) }}</span></div>
+            }
+          </div>
+        </app-content-card>
+      </div>
+
+      <app-data-table-shell title="成交商品表现" subtitle="用于识别当前周期的重点商品,不代表新品生命周期" [count]="topProducts.length">
+        <table class="data-table"><thead><tr><th class="product-cell">商品</th><th>类目</th><th>成交金额</th><th>成交件数</th><th>转化率</th></tr></thead><tbody>
+          @for (product of topProducts; track product.productId) {
+            <tr><td class="product-cell"><app-product-identity [title]="product.title" [asin]="product.productId" [productSku]="product.model" [showImage]="false" size="xs" [detailRoute]="['/voc-insight/detail']" [detailQueryParams]="{ productId: product.productId }"></app-product-identity></td><td>{{ product.category2 || product.category1 }}</td><td>{{ currency(product.summary.gmv) }}</td><td>{{ number(product.summary.soldUnits) }}</td><td>{{ percent(product.summary.conversionRate) }}</td></tr>
+          }
+        </tbody></table>
+      </app-data-table-shell>
+    }
+
+    @if (section === 'requirement') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="评论样本" [value]="dataset.summary.reviewCount" icon="▤" tone="default" description="需求原声来源"></app-summary-metric-card>
+        <app-summary-metric-card label="需求主题" value="-" icon="◉" tone="purple" description="等待评论主题"></app-summary-metric-card>
+        <app-summary-metric-card label="未满足需求" value="-" icon="!" tone="amber" description="等待痛点证据"></app-summary-metric-card>
+        <app-summary-metric-card label="使用场景" value="-" icon="⌖" tone="blue" description="等待场景原声"></app-summary-metric-card>
+      </div>
+      <app-alert-card title="需求挖掘框架已迁入,当前没有用户原声" description="经营指标可以反映销售和转化,但不能替代用户需求、场景或痛点证据。" primaryMetric="0 条评论" icon="!" tone="amber"></app-alert-card>
+      <div class="two-column equal-column">
+        <app-content-card title="显性需求" subtitle="用户明确提出的功能、规格和体验要求" variant="dashed" tone="blue"><app-empty-state variant="low-sample" title="暂无显性需求样本" description="接入评论正文后展示需求主题、频次和代表性原声。"></app-empty-state></app-content-card>
+        <app-content-card title="隐性需求与机会" subtitle="从痛点、场景和期望差中提炼" variant="dashed" tone="purple"><app-empty-state title="暂无隐性需求证据" description="当前不根据经营数据猜测用户需求。"></app-empty-state></app-content-card>
+      </div>
+    }
+
+    @if (section === 'benchmark') {
+      <div class="metric-grid">
+        <app-summary-metric-card label="已映射本品" [value]="number(dataset.mappingGroups.length)" icon="□" tone="blue" description="存在竞品配置的自营商品"></app-summary-metric-card>
+        <app-summary-metric-card label="竞品关系" [value]="number(dataset.summary.relations)" icon="↔" tone="green" description="有效商品关系"></app-summary-metric-card>
+        <app-summary-metric-card label="唯一竞品" [value]="number(dataset.summary.uniqueCompetitorProducts)" icon="◇" tone="purple" description="去重竞品商品"></app-summary-metric-card>
+        <app-summary-metric-card label="缺失竞品" [value]="number(dataset.quality.mappingsWithoutCompetitor.length)" icon="!" [tone]="dataset.quality.mappingsWithoutCompetitor.length ? 'amber' : 'green'" description="映射质量检查"></app-summary-metric-card>
+      </div>
+      <app-data-table-shell title="竞品对标清单" subtitle="保留原竞品对标框架,当前展示已核验的商品关系" [count]="benchmarkGroups.length">
+        <table class="data-table"><thead><tr><th>本品型号</th><th>本品商品ID</th><th>对标类目</th><th>竞品品牌</th><th>竞品数</th></tr></thead><tbody>
+          @for (group of benchmarkGroups; track group.ownProductId) {
+            <tr><td><strong>{{ group.model || '-' }}</strong></td><td>{{ group.ownProductId }}</td><td>{{ group.category }}</td><td>{{ relationBrands(group) }}</td><td>{{ group.competitors.length }}</td></tr>
+          }
+        </tbody></table>
+      </app-data-table-shell>
+    }
+
+    @if (section === 'definition' && selectedProduct) {
+      <div class="metric-grid">
+        <app-summary-metric-card label="平均成交单价" [value]="currency(selectedProduct.summary.averageUnitPrice)" icon="¥" tone="blue" description="当前周期"></app-summary-metric-card>
+        <app-summary-metric-card label="成交件数" [value]="number(selectedProduct.summary.soldUnits)" icon="▣" tone="green" description="真实经营数据"></app-summary-metric-card>
+        <app-summary-metric-card label="访客转化率" [value]="percent(selectedProduct.summary.conversionRate)" icon="◎" tone="purple" description="成交客户 / 访客"></app-summary-metric-card>
+        <app-summary-metric-card label="退款金额占比" [value]="percent(selectedProduct.summary.refundToGmvRate)" icon="↩" [tone]="selectedProduct.summary.refundToGmvRate >= 0.12 ? 'red' : 'amber'" description="退款金额 / 成交金额"></app-summary-metric-card>
+      </div>
+      <div class="two-column equal-column">
+        <app-content-card title="产品定义底稿" subtitle="来自标准商品身份与类目数据" tone="blue">
+          <app-product-identity [title]="selectedProduct.title" [asin]="selectedProduct.productId" [productSku]="selectedProduct.model" [subtitle]="selectedProduct.category3 || selectedProduct.category2" [showImage]="false" size="md"></app-product-identity>
+          <div class="info-list">
+            <div class="info-row"><div class="info-copy"><strong>品牌</strong><span>产品归属</span></div><span class="info-value">{{ selectedProduct.brand }}</span></div>
+            <div class="info-row"><div class="info-copy"><strong>二级类目</strong><span>产品线</span></div><span class="info-value">{{ selectedProduct.category2 || '-' }}</span></div>
+            <div class="info-row"><div class="info-copy"><strong>三级类目</strong><span>细分类目</span></div><span class="info-value">{{ selectedProduct.category3 || '-' }}</span></div>
+            <div class="info-row"><div class="info-copy"><strong>竞品关系</strong><span>当前对标覆盖</span></div><span class="info-value">{{ selectedProduct.relationCount }} 条</span></div>
+          </div>
+        </app-content-card>
+        <app-content-card title="VOC 定义输入" subtitle="需要评论证据才能补充的定义字段" variant="dashed" tone="amber">
+          <app-empty-state variant="low-sample" title="暂无用户需求输入" description="卖点优先级、功能满意度、场景和痛点需在评论接入后补齐。"></app-empty-state>
+        </app-content-card>
+      </div>
+    }
+
+    @if (section === 'lifecycle' && selectedProduct) {
+      <div class="metric-grid">
+        <app-summary-metric-card label="周期成交金额" [value]="currency(selectedProduct.summary.gmv)" icon="¥" tone="blue" [description]="selectedProduct.trend.length + ' 个日度点'"></app-summary-metric-card>
+        <app-summary-metric-card label="周期成交件数" [value]="number(selectedProduct.summary.soldUnits)" icon="▣" tone="green" description="当前数据周期"></app-summary-metric-card>
+        <app-summary-metric-card label="退款金额" [value]="currency(selectedProduct.summary.refundAmount)" icon="↩" tone="amber" [description]="percent(selectedProduct.summary.refundToGmvRate) + ' 成交金额占比'"></app-summary-metric-card>
+        <app-summary-metric-card label="评论样本" [value]="dataset.summary.reviewCount" icon="▤" tone="default" description="生命周期 VOC 证据"></app-summary-metric-card>
+      </div>
+      <app-alert-card title="当前仅支持短周期经营复盘" description="数据覆盖 2026-07-14 至 2026-07-20,不能据此判断完整上市、增长、成熟或衰退阶段。" [primaryMetric]="selectedProduct.model || selectedProduct.productId" icon="i" tone="blue"></app-alert-card>
+      <app-content-card title="单商品周期趋势" subtitle="成交金额与成交件数" tone="blue"><div echarts class="chart" [options]="lifecycleOptions"></div></app-content-card>
+    }
+  }
+</section>

+ 185 - 0
src/modules/domestic-voc/framework/domestic-product-development.component.ts

@@ -0,0 +1,185 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import type { EChartsOption } from 'echarts';
+import { NgxEchartsModule } from 'ngx-echarts';
+import { Subject, combineLatest, takeUntil } from 'rxjs';
+import {
+  DomesticDataset,
+  DomesticMappingGroup,
+  DomesticProduct,
+} from '../../../app/core/models/domestic.models';
+import {
+  DomesticAggregateSummary,
+  DomesticAnalyticsAdapterService,
+  DomesticCategoryAggregate,
+} from '../../../app/core/services/domestic-analytics-adapter.service';
+import { DomesticDatasetService } from '../../../app/core/services/domestic-dataset.service';
+import { AlertCardComponent } from '../../shared/components/alert-card/alert-card.component';
+import { BoardTabItem, BoardTabsComponent } from '../../shared/components/board-tabs/board-tabs.component';
+import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
+import { DataTableShellComponent } from '../../shared/components/data-table-shell/data-table-shell.component';
+import { EmptyStateComponent } from '../../shared/components/empty-state/empty-state.component';
+import { ErrorStateComponent } from '../../shared/components/error-state/error-state.component';
+import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
+import { PageHeaderComponent } from '../../shared/components/page-header/page-header.component';
+import { ProductIdentityComponent } from '../../shared/components/product-identity/product-identity.component';
+import { SummaryMetricCardComponent } from '../../shared/components/summary-metric-card/summary-metric-card.component';
+import { VocInsightStateService } from '../../shared/services/voc-insight-state.service';
+import { VocProductPickerComponent } from '../../voc-insight/shared/components/voc-product-picker/voc-product-picker.component';
+
+type ProductDevelopmentSection = 'trend' | 'requirement' | 'benchmark' | 'definition' | 'lifecycle';
+
+@Component({
+  selector: 'app-domestic-product-development',
+  standalone: true,
+  imports: [
+    CommonModule,
+    NgxEchartsModule,
+    AlertCardComponent,
+    BoardTabsComponent,
+    ContentCardComponent,
+    DataTableShellComponent,
+    EmptyStateComponent,
+    ErrorStateComponent,
+    LoadingSpinnerComponent,
+    PageHeaderComponent,
+    ProductIdentityComponent,
+    SummaryMetricCardComponent,
+    VocProductPickerComponent,
+  ],
+  templateUrl: './domestic-product-development.component.html',
+  styleUrls: ['./domestic-framework.shared.scss'],
+})
+export class DomesticProductDevelopmentComponent implements OnInit, OnDestroy {
+  private readonly destroy$ = new Subject<void>();
+
+  readonly tabs: BoardTabItem[] = [
+    { key: 'trend', label: '趋势洞察', icon: '↗', route: ['/product-development/trend'] },
+    { key: 'requirement', label: '需求挖掘', icon: '◉', route: ['/product-development/requirement'] },
+    { key: 'benchmark', label: '竞品对标', icon: '↔', route: ['/product-development/competitor-benchmark'] },
+    { key: 'definition', label: '产品定义', icon: '▣', route: ['/product-development/product-definition'] },
+    { key: 'lifecycle', label: '生命周期复盘', icon: '⌁', route: ['/product-development/lifecycle'] },
+  ];
+
+  section: ProductDevelopmentSection = 'trend';
+  loading = true;
+  error = '';
+  dataset: DomesticDataset | null = null;
+  summary: DomesticAggregateSummary | null = null;
+  categories: DomesticCategoryAggregate[] = [];
+  topProducts: DomesticProduct[] = [];
+  benchmarkGroups: DomesticMappingGroup[] = [];
+  selectedProduct: DomesticProduct | null = null;
+  categoryOptions: EChartsOption = {};
+  lifecycleOptions: EChartsOption = {};
+
+  constructor(
+    private readonly route: ActivatedRoute,
+    private readonly datasetService: DomesticDatasetService,
+    private readonly analytics: DomesticAnalyticsAdapterService,
+    private readonly stateService: VocInsightStateService,
+  ) {}
+
+  ngOnInit(): void {
+    combineLatest([this.route.data, this.datasetService.dataset$, this.stateService.product$])
+      .pipe(takeUntil(this.destroy$))
+      .subscribe({
+        next: ([routeData, dataset, context]) => {
+          this.section = (routeData['section'] || 'trend') as ProductDevelopmentSection;
+          this.dataset = dataset;
+          this.summary = this.analytics.aggregate(dataset);
+          this.categories = this.analytics.categories(dataset, 10);
+          this.topProducts = this.analytics.topProducts(dataset, 10);
+          this.benchmarkGroups = this.analytics.benchmarkGroups(dataset, 20);
+          this.selectedProduct = this.analytics.selectedProduct(dataset, context?.asin);
+          this.categoryOptions = this.buildCategoryOptions(this.categories);
+          this.lifecycleOptions = this.buildLifecycleOptions(this.selectedProduct);
+          this.loading = false;
+          this.error = '';
+        },
+        error: () => {
+          this.error = '新品开发框架数据加载失败,请检查国内数据文件。';
+          this.loading = false;
+        },
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+  }
+
+  get title(): string {
+    const labels: Record<ProductDevelopmentSection, string> = {
+      trend: '趋势洞察',
+      requirement: '需求挖掘',
+      benchmark: '竞品对标',
+      definition: '产品定义',
+      lifecycle: '产品生命周期复盘',
+    };
+    return labels[this.section];
+  }
+
+  get description(): string {
+    const labels: Record<ProductDevelopmentSection, string> = {
+      trend: '从真实成交、类目和商品表现中识别业务趋势,不将短周期经营波动包装成市场长期结论。',
+      requirement: '保留原用户需求挖掘框架,等待评论原声后提炼需求、场景和未满足机会。',
+      benchmark: '基于德玛仕自营商品与竞品映射,建立国内商品对标入口。',
+      definition: '将已选择商品的标准身份、经营表现和类目归属汇总为产品定义底稿。',
+      lifecycle: '按当前数据周期复盘单商品的成交、销量、转化和退款变化。',
+    };
+    return labels[this.section];
+  }
+
+  relationBrands(group: DomesticMappingGroup): string {
+    return [...new Set(group.competitors.map((item) => item.competitorBrand).filter(Boolean))].join('、') || '-';
+  }
+
+  currency(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  number(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  percent(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(value || 0);
+  }
+
+  private buildCategoryOptions(categories: DomesticCategoryAggregate[]): EChartsOption {
+    const rows = categories.slice(0, 8).reverse();
+    return {
+      color: ['#2563eb', '#059669'],
+      tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
+      legend: { data: ['成交金额', '退款金额'], top: 0, right: 0 },
+      grid: { left: 12, right: 18, top: 44, bottom: 12, containLabel: true },
+      xAxis: { type: 'value', axisLabel: { color: '#64748b' }, splitLine: { lineStyle: { color: '#eef2f7' } } },
+      yAxis: { type: 'category', data: rows.map((item) => item.category), axisLabel: { color: '#475569', width: 110, overflow: 'truncate' } },
+      series: [
+        { name: '成交金额', type: 'bar', data: rows.map((item) => item.gmv), barMaxWidth: 16 },
+        { name: '退款金额', type: 'bar', data: rows.map((item) => item.refundAmount), barMaxWidth: 16 },
+      ],
+    };
+  }
+
+  private buildLifecycleOptions(product: DomesticProduct | null): EChartsOption {
+    const trend = product?.trend ?? [];
+    return {
+      color: ['#2563eb', '#059669'],
+      tooltip: { trigger: 'axis' },
+      legend: { data: ['成交金额', '成交件数'], top: 0, right: 0 },
+      grid: { left: 18, right: 18, top: 48, bottom: 16, containLabel: true },
+      xAxis: { type: 'category', boundaryGap: false, data: trend.map((item) => item.date.slice(5)), axisLabel: { color: '#64748b' } },
+      yAxis: [
+        { type: 'value', name: '金额(元)', axisLabel: { color: '#64748b' }, splitLine: { lineStyle: { color: '#eef2f7' } } },
+        { type: 'value', name: '件数', axisLabel: { color: '#64748b' }, splitLine: { show: false } },
+      ],
+      series: [
+        { name: '成交金额', type: 'line', smooth: true, data: trend.map((item) => item.gmv), areaStyle: { color: 'rgba(37,99,235,.08)' } },
+        { name: '成交件数', type: 'line', smooth: true, yAxisIndex: 1, data: trend.map((item) => item.soldUnits) },
+      ],
+    };
+  }
+}

+ 73 - 0
src/modules/domestic-voc/framework/domestic-return-analysis.component.html

@@ -0,0 +1,73 @@
+<section class="framework-page">
+  @if (loading) {
+    <div class="framework-state"><app-loading-spinner size="lg" text="正在加载退款分析框架..."></app-loading-spinner></div>
+  } @else if (error) {
+    <div class="framework-state"><app-error-state [description]="error"></app-error-state></div>
+  } @else if (dataset && summary) {
+    <app-page-header
+      [title]="title"
+      eyebrow="Return Analysis"
+      [description]="description"
+      icon="↩"
+      [badges]="[{ label: '退款汇总已接入', tone: 'green' }, { label: '原因明细待接入', tone: 'amber' }]"
+      [meta]="[{ label: '案例', value: dataset.caseName }, { label: '数据周期', value: dataset.source.dateRange.start + ' 至 ' + dataset.source.dateRange.end }]">
+    </app-page-header>
+
+    <app-board-tabs class="framework-tabs" [tabs]="tabs" [activeKey]="section" density="compact"></app-board-tabs>
+
+    <div class="metric-grid">
+      <app-summary-metric-card label="退款金额" [value]="currency(summary.refundAmount)" icon="¥" tone="red" [description]="percent(summary.refundRate) + ' 成交金额占比'"></app-summary-metric-card>
+      <app-summary-metric-card label="退款件数" [value]="number(summary.refundUnits)" icon="□" tone="amber" [description]="number(summary.soldUnits) + ' 件成交商品'"></app-summary-metric-card>
+      <app-summary-metric-card label="退款订单" [value]="number(summary.refundOrders)" icon="▤" tone="purple" [description]="number(summary.transactionOrders) + ' 笔成交订单'"></app-summary-metric-card>
+      <app-summary-metric-card label="高退款商品池" [value]="number(riskProducts.length)" icon="!" tone="red" description="按退款金额占比排序"></app-summary-metric-card>
+    </div>
+
+    @if (section === 'overview') {
+      <div class="two-column">
+        <app-content-card title="退款趋势" subtitle="每日退款金额与退款订单数" tone="red">
+          <div echarts class="chart" [options]="trendOptions"></div>
+        </app-content-card>
+        <app-content-card title="分析口径" subtitle="当前已接通与待接入范围" tone="amber">
+          <div class="info-list">
+            <div class="info-row"><div class="info-copy"><strong>退款金额/件数/订单</strong><span>来自日度经营数据</span></div><span class="info-value">已接通</span></div>
+            <div class="info-row"><div class="info-copy"><strong>商品退款风险</strong><span>按商品汇总退款金额占比</span></div><span class="info-value">已接通</span></div>
+            <div class="info-row"><div class="info-copy"><strong>退款原因与责任归属</strong><span>需要订单级售后明细</span></div><span class="info-value">待接入</span></div>
+          </div>
+        </app-content-card>
+      </div>
+    }
+
+    @if (section === 'attribution') {
+      <app-alert-card title="当前只能定位高退款商品,不能判定退款原因" description="源文件没有订单级原因、责任方或客户反馈字段,因此归因树和原因占比保持空态。" primaryMetric="原因样本 0" icon="!" tone="amber"></app-alert-card>
+      <div class="two-column equal-column">
+        <app-content-card title="退款原因树" subtitle="等待订单级退款原因" variant="dashed" tone="amber">
+          <app-empty-state variant="low-sample" title="暂无退款原因明细" description="接入售后原因后可按产品、规格、类目和责任方逐层下钻。"></app-empty-state>
+        </app-content-card>
+        <app-content-card title="代表性用户反馈" subtitle="等待评论或售后描述" variant="dashed" tone="purple">
+          <app-empty-state title="暂无退款原声" description="当前数据无法展示退款用户的文字证据。"></app-empty-state>
+        </app-content-card>
+      </div>
+    }
+
+    @if (section === 'correlation') {
+      <app-alert-card title="关联分析需要订单级共同维度" description="必须同时具备退款原因、商品规格、下单时间、客户和履约字段,当前仅有汇总指标。" primaryMetric="关联样本 0" icon="↔" tone="blue"></app-alert-card>
+      <app-content-card title="退款特征关联矩阵" subtitle="框架已保留,数据不足时不计算相关性" variant="dashed" tone="blue">
+        <app-empty-state title="暂无可计算的关联样本" description="后续可在此展示规格、价格区间、类目、客户画像与退款原因的关联强度。"></app-empty-state>
+      </app-content-card>
+    }
+
+    <app-data-table-shell title="高退款商品监测" subtitle="仅基于真实商品退款汇总,不代表原因归因" [count]="riskProducts.length" [scrollable]="true">
+      <table class="data-table">
+        <thead><tr><th class="product-cell">商品</th><th>类目</th><th>成交金额</th><th>退款金额</th><th>退款金额占比</th></tr></thead>
+        <tbody>
+          @for (product of riskProducts; track product.productId) {
+            <tr>
+              <td class="product-cell"><app-product-identity [title]="product.title" [asin]="product.productId" [productSku]="product.model" [showImage]="false" size="xs" [detailRoute]="['/voc-insight/detail']" [detailQueryParams]="{ productId: product.productId }"></app-product-identity></td>
+              <td>{{ product.category2 || product.category1 }}</td><td>{{ currency(product.summary.gmv) }}</td><td>{{ currency(product.summary.refundAmount) }}</td><td><strong>{{ percent(product.summary.refundToGmvRate) }}</strong></td>
+            </tr>
+          }
+        </tbody>
+      </table>
+    </app-data-table-shell>
+  }
+</section>

+ 136 - 0
src/modules/domestic-voc/framework/domestic-return-analysis.component.ts

@@ -0,0 +1,136 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import type { EChartsOption } from 'echarts';
+import { NgxEchartsModule } from 'ngx-echarts';
+import { Subject, combineLatest, takeUntil } from 'rxjs';
+import { DomesticDataset, DomesticProduct } from '../../../app/core/models/domestic.models';
+import {
+  DomesticAggregateSummary,
+  DomesticAnalyticsAdapterService,
+} from '../../../app/core/services/domestic-analytics-adapter.service';
+import { DomesticDatasetService } from '../../../app/core/services/domestic-dataset.service';
+import { AlertCardComponent } from '../../shared/components/alert-card/alert-card.component';
+import { BoardTabItem, BoardTabsComponent } from '../../shared/components/board-tabs/board-tabs.component';
+import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
+import { DataTableShellComponent } from '../../shared/components/data-table-shell/data-table-shell.component';
+import { EmptyStateComponent } from '../../shared/components/empty-state/empty-state.component';
+import { ErrorStateComponent } from '../../shared/components/error-state/error-state.component';
+import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
+import { PageHeaderComponent } from '../../shared/components/page-header/page-header.component';
+import { ProductIdentityComponent } from '../../shared/components/product-identity/product-identity.component';
+import { SummaryMetricCardComponent } from '../../shared/components/summary-metric-card/summary-metric-card.component';
+
+type ReturnSection = 'overview' | 'attribution' | 'correlation';
+
+@Component({
+  selector: 'app-domestic-return-analysis',
+  standalone: true,
+  imports: [
+    CommonModule,
+    NgxEchartsModule,
+    AlertCardComponent,
+    BoardTabsComponent,
+    ContentCardComponent,
+    DataTableShellComponent,
+    EmptyStateComponent,
+    ErrorStateComponent,
+    LoadingSpinnerComponent,
+    PageHeaderComponent,
+    ProductIdentityComponent,
+    SummaryMetricCardComponent,
+  ],
+  templateUrl: './domestic-return-analysis.component.html',
+  styleUrls: ['./domestic-framework.shared.scss'],
+})
+export class DomesticReturnAnalysisComponent implements OnInit, OnDestroy {
+  private readonly destroy$ = new Subject<void>();
+
+  readonly tabs: BoardTabItem[] = [
+    { key: 'overview', label: '退款整体概况', icon: '▦', route: ['/return-analysis/overview'] },
+    { key: 'attribution', label: '退款归因下钻', icon: '↘', route: ['/return-analysis/attribution'] },
+    { key: 'correlation', label: '退款特征关联', icon: '↔', route: ['/return-analysis/correlation'] },
+  ];
+
+  section: ReturnSection = 'overview';
+  loading = true;
+  error = '';
+  dataset: DomesticDataset | null = null;
+  summary: DomesticAggregateSummary | null = null;
+  riskProducts: DomesticProduct[] = [];
+  trendOptions: EChartsOption = {};
+
+  constructor(
+    private readonly route: ActivatedRoute,
+    private readonly datasetService: DomesticDatasetService,
+    private readonly analytics: DomesticAnalyticsAdapterService,
+  ) {}
+
+  ngOnInit(): void {
+    combineLatest([this.route.data, this.datasetService.dataset$])
+      .pipe(takeUntil(this.destroy$))
+      .subscribe({
+        next: ([routeData, dataset]) => {
+          this.section = (routeData['section'] || 'overview') as ReturnSection;
+          this.dataset = dataset;
+          this.summary = this.analytics.aggregate(dataset);
+          this.riskProducts = this.analytics.refundRiskProducts(dataset, 12);
+          this.trendOptions = this.buildTrendOptions(dataset);
+          this.loading = false;
+          this.error = '';
+        },
+        error: () => {
+          this.error = '退款分析数据加载失败,请检查国内数据文件。';
+          this.loading = false;
+        },
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+  }
+
+  get title(): string {
+    if (this.section === 'attribution') return '退款归因深度下钻';
+    if (this.section === 'correlation') return '退款特征关联';
+    return '退款整体概况';
+  }
+
+  get description(): string {
+    if (this.section === 'attribution') return '区分已接通的商品退款指标与尚未接入的订单级退款原因,避免用汇总数据替代归因证据。';
+    if (this.section === 'correlation') return '保留原关联分析框架,等待订单、原因、规格和客户维度形成可验证的关联样本。';
+    return '汇总真实退款金额、件数、订单和商品风险,并保留后续归因分析入口。';
+  }
+
+  currency(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  number(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 0 }).format(value || 0);
+  }
+
+  percent(value: number): string {
+    return new Intl.NumberFormat('zh-CN', { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(value || 0);
+  }
+
+  private buildTrendOptions(dataset: DomesticDataset): EChartsOption {
+    const daily = this.analytics.daily(dataset);
+    return {
+      color: ['#dc2626', '#d97706'],
+      tooltip: { trigger: 'axis' },
+      legend: { data: ['退款金额', '退款订单'], top: 0, right: 0 },
+      grid: { left: 18, right: 18, top: 48, bottom: 16, containLabel: true },
+      xAxis: { type: 'category', data: daily.map((item) => item.date.slice(5)), axisLabel: { color: '#64748b' } },
+      yAxis: [
+        { type: 'value', name: '金额(元)', axisLabel: { color: '#64748b' }, splitLine: { lineStyle: { color: '#eef2f7' } } },
+        { type: 'value', name: '订单', axisLabel: { color: '#64748b' }, splitLine: { show: false } },
+      ],
+      series: [
+        { name: '退款金额', type: 'bar', data: daily.map((item) => item.refundAmount), barMaxWidth: 24, itemStyle: { borderRadius: [4, 4, 0, 0] } },
+        { name: '退款订单', type: 'line', smooth: true, yAxisIndex: 1, data: daily.map((item) => item.refundOrders) },
+      ],
+    };
+  }
+}

+ 42 - 0
src/modules/shared/components/navigation/navigation.component.ts

@@ -104,6 +104,9 @@ export class NavigationComponent {
       expanded: true,
       children: [
         { path: '/domestic/voc-overview', displayName: 'VOC 趋势总览', icon: 'trend', requiresAuth: false },
+        { path: '/domestic/voc-trend', displayName: 'VOC 数据趋势', icon: 'trend', requiresAuth: false },
+        { path: '/domestic/emotion', displayName: '情绪极性分布', icon: 'emotion', requiresAuth: false },
+        { path: '/domestic/store-monitoring', displayName: '店铺监测融合', icon: 'store', requiresAuth: false },
         { path: '/domestic/risk-alerts', displayName: '风险预警', icon: 'alert', requiresAuth: false },
       ],
     },
@@ -120,6 +123,45 @@ export class NavigationComponent {
         { path: '/voc-insight/scenario', displayName: '使用场景', icon: 'scenario', requiresAuth: false },
       ],
     },
+    {
+      path: '/return-analysis',
+      displayName: '退款深度分析',
+      icon: 'return',
+      requiresAuth: false,
+      expanded: false,
+      children: [
+        { path: '/return-analysis/overview', displayName: '退款整体概况', icon: 'overview', requiresAuth: false },
+        { path: '/return-analysis/attribution', displayName: '退款归因下钻', icon: 'trend', requiresAuth: false },
+        { path: '/return-analysis/correlation', displayName: '退款特征关联', icon: 'category', requiresAuth: false },
+      ],
+    },
+    {
+      path: '/product-development',
+      displayName: '新品开发驱动',
+      icon: 'rocket',
+      requiresAuth: false,
+      expanded: false,
+      children: [
+        { path: '/product-development/trend', displayName: '趋势洞察', icon: 'trend', requiresAuth: false },
+        { path: '/product-development/requirement', displayName: '需求挖掘', icon: 'overview', requiresAuth: false },
+        { path: '/product-development/competitor-benchmark', displayName: '竞品对标', icon: 'competitor', requiresAuth: false },
+        { path: '/product-development/product-definition', displayName: '产品定义', icon: 'product', requiresAuth: false },
+        { path: '/product-development/lifecycle', displayName: '生命周期复盘', icon: 'lifecycle', requiresAuth: false },
+      ],
+    },
+    {
+      path: '/action-suggestion',
+      displayName: '行动建议',
+      icon: 'bulb',
+      requiresAuth: false,
+      expanded: false,
+      children: [
+        { path: '/action-suggestion/kanban', displayName: '建议看板', icon: 'overview', requiresAuth: false },
+        { path: '/action-suggestion/experience', displayName: '体验优化提案', icon: 'emotion', requiresAuth: false },
+        { path: '/action-suggestion/iteration', displayName: '产品迭代方向', icon: 'product', requiresAuth: false },
+        { path: '/action-suggestion/strategy', displayName: '长期策略调整', icon: 'trend', requiresAuth: false },
+      ],
+    },
     {
       path: '/data-status',
       displayName: '数据接入状态',