# 智能比价助手 - 产品需求文档 (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 (供应商报价条目) ```prisma 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 (比价分析输出) ```typescript 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 采购建议报告 ```typescript 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 请求/响应示例 **上传报价** ```bash curl -X POST /api/comparison/upload \ -F "file=@quote.xlsx" \ -F "supplier=远东电缆" ``` ```json { "count": 45, "supplier": "远东电缆" } ``` **获取建议** ```bash curl -X POST /api/comparison/suggestion ``` ```json { "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`) ```typescript // 核心步骤: // 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 解析脚本接口 ```python # scripts/parse_products.py # 输入: 文件路径 # 输出: JSON 数组到 stdout # 格式: [{"title": "...", "spec": "...", "price": 100, ...}, ...] # 错误: 输出 {"error": "错误信息"} 到 stdout ``` --- ## 8. 部署与运维 ### 8.1 环境变量 ```env DATABASE_URL=file:./dev.db # SQLite文件路径 # 或 PostgreSQL: postgresql://user:pass@host:5432/db ``` ### 8.2 启动命令 ```bash # 开发 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 组合枚举性能 --- **文档结束** — 如有疑问可参考源码实现细节。建议先跑通核心算法单元测试,再集成前后端。