- HANDOVER.md: 完整交接文档(架构/数据模型/API/业务流/已知坑/工作区状态/待办) - PRD-智能比价助手.md: 比价功能产品需求文档(供外部平台参考) - 数据维护脚本: 清理AI垃圾品类SQL、修复损坏标题SQL、线索重分类/重抓脚本
13 KiB
13 KiB
智能比价助手 - 产品需求文档 (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)
基于规则引擎生成自然语言建议:
- 概况:SKU数、供应商数
- 拆分 vs 集中:若
savingsVsSingle > 0→ 推荐拆分 + 具体金额/比例 - 优势供应商:
supplierSplitCountsTop 3 - 最大价差SKU:
splitPicks中 savings 最大者 - 风险提示: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() 中扩展:
- 新增
splitByCount[K]的 K 值 - 或新增约束维度(如:按品牌集中、按地区集中、按最小起订量过滤)
- 在
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 组合枚举性能
文档结束 — 如有疑问可参考源码实现细节。建议先跑通核心算法单元测试,再集成前后端。