platform-assistant/PRD-智能比价助手.md
Sisyphus 5a49a37235 docs: 项目交接文档 + 比价功能PRD + 数据维护脚本
- HANDOVER.md: 完整交接文档(架构/数据模型/API/业务流/已知坑/工作区状态/待办)
- PRD-智能比价助手.md: 比价功能产品需求文档(供外部平台参考)
- 数据维护脚本: 清理AI垃圾品类SQL、修复损坏标题SQL、线索重分类/重抓脚本
2026-09-30 09:35:05 +08:00

13 KiB
Raw Blame History

智能比价助手 - 产品需求文档 (PRD)

版本: v1.0
日期: 2025-08
适用平台: 参考设计文档,供其他平台二次开发/移植使用
源项目: 平台助手 - 智能比价助手模块


1. 产品概述

1.1 产品定位

智能比价助手 是面向采购场景的多供应商报价自动对齐与智能决策工具。核心价值:将非结构化的供应商报价文件(Excel/CSV)转化为结构化可比价数据,通过算法自动生成最优采购方案(拆分/集中/限制供应商数),输出可执行的采购建议报告。

1.2 核心用户场景

场景 痛点 解决方案
采购员收到多家供应商报价单 格式不一、人工对齐耗时、易漏项 拖拽上传 → 自动解析 → 以商品名+规格自动对齐
不知道找哪家供应商最便宜 需人工逐行对比、易漏最优 自动计算每SKU最优/次优供应商、价差、降本率
纠结是集中采购还是拆分采购 缺量化依据、靠经验拍脑袋 三大策略对比:集中采购 / 无限制拆分 / 限制K家供应商
需向领导汇报采购方案 缺专业报告、数据支撑弱 一键生成含策略对比、SKU明细、供应商画像、降本测算的结构化报告

2. 系统架构

2.1 技术栈

层 技术选型
后端框架 NestJS 10 + TypeScript
数据库 SQLite (Prisma ORM) / 可替换为 PostgreSQL/MySQL
文件解析 Python 脚本 parse_products.py (pandas/openpyxl)
前端框架 Next.js 14 App Router + React 18 + Tailwind CSS 3
状态管理 React useState + useEffect (SPA 无路由模式)
文件上传 Multer + FormData 拖拽上传

2.2 模块依赖

ComparisonModule
├── ComparisonController  (REST API)
├── ComparisonService     (核心业务逻辑)
├── PrismaModule          (数据库)
└── (无外部服务依赖)

3. 数据模型

3.1 核心实体:Product (供应商报价条目)

model Product {
  id        String   @id @default(uuid())
  name      String   // 商品名称(去重键核心字段)
  spec      String   // 规格型号
  unit      String   // 单位(个/吨/米/卷...)
  price     Float    // 单价
  quantity  Int      // 数量/采购量
  category  String   // 品类(用于筛选/统计)
  brand     String?  // 品牌
  supplier  String   // 供应商名称(去重键辅助字段)
  minOrder  Int?     // 起订量
  imageUrl  String?  // 图片URL
  status    String   @default("draft") // draft/published
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

3.2 聚合视图:ComparisonItem (比价分析输出)

interface ComparisonItem {
  productName: string;      // 商品名(去重后)
  spec: string;             // 规格
  category: string;         // 品类
  quantity: number;         // 采购量(取该商品所有报价的最大值)
  suppliers: SupplierInfo[]; // 去重后的供应商报价列表
  bestPrice: number;        // 最低价
  bestSupplier: string;     // 最优供应商名
  hasSupplier: boolean;     // 是否有有效供应商报价
  savings: number;          // 平均价 - 最优价(单价维度)
  priceSpread: number;      // 最高价 - 最低价(价差)
}

interface SupplierInfo {
  id: string;
  name: string;
  price: number;
  unit: string;
  spec: string;
  brand?: string;
  minOrder?: number;
  imageUrl?: string;
}

3.3 采购建议报告

interface SuggestionReport {
  // 核心汇总
  totalItems: number;              // 参与比价的SKU数
  totalSplitCost: number;          // 无限制拆分总价
  savingsVsSingle: number;         // 拆分 vs 单一供应商节省金额
  savingsPctVsSingle: number;      // 综合降本比例
  bestSingleSupplier: { name, total }; // 单一供应商最优方案
  
  // 策略维度
  supplierRanking: { name, total, skuCount }[]; // 单一供应商总价排名
  splitPicks: SplitPick[];         // 无限制拆分时每SKU最优分配
  splitByCount: {                  // 限制K家供应商时的最优组合
    2: StrategyResult;
    3: StrategyResult;
    4: StrategyResult;
  };
  
  // 可执行建议
  recommendations: string[];       // 自然语言建议列表
}

4. 核心 API 设计

4.1 REST Endpoints

方法 路径 说明 请求/响应
GET /api/comparison 获取所有商品分组比价数据 返回 ComparisonItem[]
POST /api/comparison/upload 上传供应商报价文件 multipart/form-data: file + supplier
POST /api/comparison/suggestion 生成采购建议报告 返回 SuggestionReport

4.2 请求/响应示例

上传报价

curl -X POST /api/comparison/upload \
  -F "file=@quote.xlsx" \
  -F "supplier=远东电缆"
{ "count": 45, "supplier": "远东电缆" }

获取建议

curl -X POST /api/comparison/suggestion
{
  "totalItems": 12,
  "totalSplitCost": 125600.00,
  "savingsVsSingle": 18500.50,
  "savingsPctVsSingle": 12.8,
  "bestSingleSupplier": { "name": "远东电缆", "total": 144100.50 },
  "supplierRanking": [
    { "name": "远东电缆", "total": 144100.50, "skuCount": 12 },
    { "name": "宝胜电缆", "total": 152300.00, "skuCount": 10 }
  ],
  "splitPicks": [...],
  "splitByCount": { "2": {...}, "3": {...}, "4": {...} },
  "recommendations": [
    "本次比价共涉及 12 个SKU,2 家供应商参与报价。",
    "按SKU拆分下单比统一从「远东电缆」采购可再节省 ¥18500.50(降本 12.8%)。"
  ]
}

5. 核心算法逻辑

5.1 数据聚合与去重 (getComparisonData)

// 核心步骤:
// 1. 读取所有 Product 记录
// 2. 按 name.trim().toLowerCase() 分组(商品名去重)
// 3. 组内按 supplier 去重 → 同一供应商保留最低价
// 4. 计算每组统计指标:
//    - bestPrice / bestSupplier
//    - savings = avgPrice - bestPrice
//    - priceSpread = maxPrice - minPrice

去重策略:

  • 商品级:name 忽略大小写/首尾空格分组
  • 供应商级:组内按 supplier 去重,保留最低价
  • 数量取最大值:quantity = max(所有报价的 quantity)

5.2 无限制拆分策略 (getSuggestion → splitPicks)

每个 SKU 独立选择最低总价供应商:

对每个 SKU:
  1. 计算各供应商总价 = price * quantity
  2. 选总价最低者为最优
  3. 次优 = 总价第二低者
  4. 单品节省 = 次优总价 - 最优总价

输出:splitPicks[] + totalSplitCost + supplierSplitCounts(各供应商中标SKU数)

5.3 单一供应商策略 (supplierRanking)

对每个供应商:
  总价 = Σ(该供应商所有SKU的 price * quantity)
  SKU数 = 该供应商有报价的SKU数
按总价升序排序 → bestSingleSupplier = 第1名

5.4 限制 K 家供应商策略 (splitByCount[K])

组合枚举 + 贪心分配:

输入:所有供应商名单 allNames,目标数量 K (2/3/4)
对于 allNames 中所有 C(n, K) 种组合:
  对每个 SKU:
    从组合中选价格最低的供应商
    累加总价
  记录总价最低的组合

复杂度:C(n, K) × m (SKU数)。n≤10, K≤4 时可接受(C(10,4)=210)。

输出:

  • bestSuppliers: 实际使用的供应商列表(去除组合中未中标者)
  • totalCost: 最优总价
  • assignments[]: 每SKU的分配明细
  • supplierBreakdown: 各供应商中标SKU数/金额占比

5.3 采购建议生成 (recommendations)

基于规则引擎生成自然语言建议:

  1. 概况:SKU数、供应商数
  2. 拆分 vs 集中:若 savingsVsSingle > 0 → 推荐拆分 + 具体金额/比例
  3. 优势供应商:supplierSplitCounts Top 3
  4. 最大价差SKU:splitPicks 中 savings 最大者
  5. 风险提示:SKU>10 时提示交付协调风险

6. 前端交互流程

6.1 三步向导式交互

Step 1: 上传报价 (upload)
  ├── 输入供应商名称
  ├── 拖拽/点击上传 .xlsx/.xls/.csv
  ├── Python脚本解析 → 入库
  └── 显示已上传供应商列表 + 报价条数

Step 2: 比价分析 (compare)
  ├── 表格展示:商品名 | 规格 | 数量 | 各供应商单价 | 最优价 | 价差
  ├── 最优价绿色高亮,缺报显示 "-"
  ├── 支持按价格/名称/节省金额排序
  └── 无供应商对比数据单独展示

Step 3: 采购建议 (suggestion)
  ├── Hero汇总卡:拆分总价 / 节省金额 / 降本率
  ├── 策略对比卡:
  │   ├── A方案:单一供应商(含供应商总价排名表)
  │   └── B方案:拆分采购(含K家供应商选择器 K=2/3/4)
  │       ├── 动态条形图:供应商中标SKU占比
  │       └── 明细表:每SKU分配供应商/单价/数量/总价
  ├── SKU明细表:推荐供应商/单价/总价/次优总价/节省/降本率
  ├── 采购建议列表(自然语言)
  └── 导出 CSV 报告

6.2 关键交互细节

交互 实现细节
文件上传 拖拽区域 + 点击选择,支持 .xlsx/.xls/.csv,显示上传进度
供应商名称 必填校验,作为报价归属标识
K值选择器 2/3/4 三按钮,单选互斥,支持取消选中回退无限制方案
表格高亮 最优价格绿色背景+粗体,价差红色显示
空状态处理 无数据/无对比数据分别展示占位提示
导出CSV BOM头防乱码,含完整SKU明细列

7. 文件解析规范

7.1 支持格式

  • .xlsx / .xls (Excel)
  • .csv (逗号分隔)

7.2 必需列映射 (parse_products.py)

标准列名 别名/兼容 说明
title / name 商品名称、产品名称 必填,作为去重主键
spec 规格、型号 可选
unit 单位 默认 "个"
price 单价、价格 必填,数值型
quantity 数量、采购量 默认 1
category 品类、分类 默认 "未分类"
brand 品牌 可选
minOrder 起订量 可选

7.3 解析脚本接口

# scripts/parse_products.py
# 输入: 文件路径
# 输出: JSON 数组到 stdout
# 格式: [{"title": "...", "spec": "...", "price": 100, ...}, ...]
# 错误: 输出 {"error": "错误信息"} 到 stdout

8. 部署与运维

8.1 环境变量

DATABASE_URL=file:./dev.db          # SQLite文件路径
# 或 PostgreSQL: postgresql://user:pass@host:5432/db

8.2 启动命令

# 开发
npm run dev          # 同时启动前端(3001) + 后端(4000)
npm run dev:api      # 仅后端
npm run dev:web      # 仅前端

# 生产部署
npm run build
bash scripts/deploy.sh  # 一键构建+rsync+PM2重启

8.3 生产架构

Nginx (80/443)
├── /api/* → NestJS:4000
├── /uploads/* → 静态文件 /data/pingtai/uploads/
└── /* → Next.js:3001
PM2 管理: pingtai-api, pingtai-web

9. 扩展指南

9.1 新增策略维度

在 ComparisonService.getSuggestion() 中扩展:

  1. 新增 splitByCount[K] 的 K 值
  2. 或新增约束维度(如:按品牌集中、按地区集中、按最小起订量过滤)
  3. 在 recommendations 中增加对应建议文案

9.2 多租户隔离

当前单租户设计,扩展多租户需:

  • Product 增加 tenantId 字段 + 复合索引
  • API 增加租户上下文中间件
  • getComparisonData / getSuggestion 加 where: { tenantId }

9.3 报价有效期管理

  • Product 增加 validFrom / validTo 字段
  • 查询时过滤 validTo >= now()
  • 前端增加"报价有效期"筛选器

9.4 供应商画像扩展

  • 新增 Supplier 实体:资质、交付周期、历史履约率、质量评分
  • 建议算法引入权重:综合得分 = 价格权重*0.6 + 交付权重*0.2 + 质量权重*0.2

10. 附录:关键代码路径速查

功能 文件路径
比价数据聚合 apps/api/src/comparison/comparison.service.ts:12-69
采购建议核心算法 apps/api/src/comparison/comparison.service.ts:131-314
K家供应商组合优化 apps/api/src/comparison/comparison.service.ts:254-314
报价文件上传解析 apps/api/src/comparison/comparison.service.ts:71-129
Python解析脚本 scripts/parse_products.py
前端三步向导主页 apps/web/src/components/ComparisonPage.tsx
比价表格渲染 ComparisonPage.tsx:256-320
采购建议报告UI ComparisonPage.tsx:327-623
导出CSV逻辑 ComparisonPage.tsx:606-617

11. 迁移清单 (给目标平台)

  • 复刻 Product 数据模型到目标数据库
  • 移植 parse_products.py 适配目标平台的文件存储
  • 实现 ComparisonService 核心类(去重、聚合、三大策略算法)
  • 暴露 3 个 REST API 端点
  • 实现前端三步向导页面(上传/比价/建议)
  • 集成文件上传组件 + 进度反馈
  • 实现 CSV 导出功能
  • 补充单元测试:去重逻辑、K组合枚举、边界情况(单供应商、无报价SKU)
  • 压力测试:1000 SKU × 10 供应商 × K=4 组合枚举性能

文档结束 — 如有疑问可参考源码实现细节。建议先跑通核心算法单元测试,再集成前后端。