diff --git a/mcp-for-crm/MCP工具说明文档.md b/mcp-for-crm/MCP工具说明文档.md new file mode 100644 index 0000000..c233085 --- /dev/null +++ b/mcp-for-crm/MCP工具说明文档.md @@ -0,0 +1,555 @@ +# MCP for CRM — 工具说明文档 + +> 服务对象:汽车零部件(小型阀体外壳)智能报价智能体 +> 服务定位:CRM 侧客户/询价/报价/商机数据服务(业务写入 + 只读回读 + 受限清理) +> 版本:v1.1(2026-08-09) + +--- + +## 1 服务概览 + +| 项 | 值 | +|----|----| +| 服务名 | `mcp-for-crm-auto` | +| 框架 | MCPServer(mcp 2.0.0 内置高级框架),`@app.tool()` 装饰器注册 | +| 传输协议 | Streamable HTTP(JSON-RPC 2.0) | +| 远程端点 | `https://crm-data.dongsk.top/mcp` | +| 本地端点 | `http://0.0.0.0:8002/mcp`(`cd src && python3 server.py`) | +| 数据库 | PostgreSQL 15+,库 `smart_quotation_auto`(仅服务层内部经 `db.py` 连接池访问) | +| 工具数 | **10 个** = 6 个业务工具 + 4 个验证/管理工具 | + +### 工具一览 + +| # | 工具 | 功能 | 类型 | 报价环节 | +|---|------|------|------|----------| +| 1 | `get_customer_info` | 查询客户信用/折扣/账期 | 只读 | Step 2 客户查询 | +| 2 | `create_inquiry` | 创建询价单 | 写入 | Step 7 存档 | +| 3 | `save_quotation` | 保存报价单(自动版本+1) | 写入(事务) | Step 7 存档 | +| 4 | `get_quotation_history` | 历史报价/版本链查询 | 只读 | Step 7 前置检查 | +| 5 | `update_opportunity` | 创建/更新商机 | 写入 | Step 7 存档 | +| 6 | `get_inquiry_info` | 询价单整行回读 | 只读 | 存档后验证 | +| 7 | `get_opportunity_info` | 商机回读 | 只读 | 存档后验证 | +| 8 | `get_record_counts` | 三表行数统计 | 只读 | 数据卫生检查 | +| 9 | `purge_test_records` | 受限测试数据清理 | 写入(带防护) | 回归测试清理 | +| 10 | `save_quotation_mock` | 报价写入 CRM(Mock 模拟) | 写入(独立 mock 表) | 模拟报价写入 CRM | + +### 单号与状态约定 + +| 实体 | 单号规则 | 状态机 | +|------|----------|--------| +| 询价单 | `INQ-2026-NNNN`(自增) | `pending` → `quoted`(首次报价自动推进) | +| 报价单 | `QUO-2026-NNNN`(自增) | `draft` → `superseded`(被新版替代);`won` / `lost` 为终态,受保护 | +| 商机 | `OPP-2026-NNNN`(自增) | `lead` → `quoted` → `won` / `lost` | + +--- + +## 2 调用约定 + +### 2.1 客户端注册(mcpServers) + +```json +{ + "mcpServers": { + "crm": { "url": "https://crm-data.dongsk.top/mcp" } + } +} +``` + +### 2.2 兜底命令行客户端(纯标准库) + +```bash +python3 scripts/mcp_http_client.py crm <工具名> '' +# 示例 +python3 scripts/mcp_http_client.py crm get_customer_info '{"customer_code":"OEM-2024-003"}' +``` + +### 2.3 返回通用约定 + +- 所有工具返回 **JSON 字符串**(`ensure_ascii=False`,中文不转义)。 +- 查询类:`{"total": N, "<集合名>": [...]}`;写入/操作类:`{"success": true|false, ...}`。 +- 失败统一形态:`{"success": false, "message": "<原因>"}`。 +- 金额字段(decimal 列)在 JSON 中为字符串,如 `"180000.00"`;日期 `YYYY-MM-DD`。 +- 日期入参格式必须为 `YYYY-MM-DD`,否则返回格式错误。 + +--- + +## 3 业务工具详解(6 个) + +### 3.1 get_customer_info — 查询客户信息 + +**功能**:按客户编码精确查询,或按名称、OEM 层级模糊搜索。返回信用等级、折扣率、账期、历史订单统计,用于报价前的客户校验与商务条款确定。 + +**入参**(全部可选;全部不传 = 返回全表): + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `customer_code` | string | 否 | — | 客户编码,精确匹配,如 `OEM-2024-003` | +| `name` | string | 否 | — | 客户名称,`%模糊%` 匹配 | +| `oem_tier` | string | 否 | — | 层级精确匹配:`OEM` / `Tier1` / `Tier2` | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `total` | int | 命中条数 | +| `customers[].customer_code` | string | 客户编码 | +| `customers[].name` | string | 客户名称 | +| `customers[].oem_tier` | string | OEM / Tier1 / Tier2 | +| `customers[].credit_level` | string | 信用等级 A/B/C/D 或「无评级」 | +| `customers[].discount_rate` | decimal | 折扣率(字符串,如 `"0.040"` = 4%) | +| `customers[].payment_days` | int | 账期天数(月结 N 天) | +| `customers[].contact_person` / `phone` / `email` | string | 联系人信息 | +| `customers[].industry` / `region` | string | 行业 / 区域 | +| `customers[].total_orders` | int | 历史订单数 | +| `customers[].total_amount` | decimal | 历史订单总额 | +| `customers[].notes` | string | 备注 | + +**返回示例**: + +```json +{ + "total": 1, + "customers": [ + { + "customer_code": "OEM-2024-003", + "name": "某新能源主机厂", + "oem_tier": "OEM", + "credit_level": "A", + "discount_rate": "0.040", + "payment_days": 60, + "contact_person": "刘工", + "phone": "0551-2222-0005", + "email": "liu@oem-nev.example.com", + "industry": "汽车", + "region": "合肥", + "total_orders": 22, + "total_amount": "9800000.00", + "notes": "新能源三电系统配套,增长快", + "created_at": "2026-08-07 17:31:56.244396" + } + ] +} +``` + +--- + +### 3.2 create_inquiry — 创建询价记录 + +**功能**:客户提交询价后建档。校验客户存在性,自动生成 `INQ-2026-NNNN` 单号,初始状态 `pending`。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `customer_code` | string | **是** | — | 客户编码(不存在则失败) | +| `drawing_number` | string | 否 | null | 图纸号 | +| `part_number` | string | 否 | null | 零件号 | +| `annual_volume` | int | 否 | null | 年用量(件/年) | +| `target_price` | float | 否 | null | 客户目标价 ¥ | +| `notes` | string | 否 | null | 备注 | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `inquiry_id` | string | 生成的询价单号 | +| `customer_name` | string | 客户名称 | +| `status` | string | 固定 `pending` | +| `message` | string | 提示信息 | + +**失败返回**:`{"success": false, "message": "未找到客户 XXX"}` + +**返回示例**: + +```json +{ + "success": true, + "inquiry_id": "INQ-2026-0028", + "customer_name": "某新能源主机厂", + "status": "pending", + "message": "询价记录 INQ-2026-0028 已创建" +} +``` + +**业务规则**:同一询价单可多次创建(每次生成新单号);「同一询价单多次报价」通过 3.3 `save_quotation` 的版本机制实现,不重复建档。 + +--- + +### 3.3 save_quotation — 保存报价单(多版本核心) + +**功能**:报价存档核心工具。同一询价单**不限报价次数**,版本号自动 MAX+1;旧版非终态自动置 `superseded`;首报自动推询价单 `pending → quoted`;`won` 终态保护(仍存档但带 warning)。事务内完成:作废旧版 → 推询价状态 → 写报价主表 → 写成本明细表。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `inquiry_id` | string | **是** | — | 询价单号(不存在则失败并提示先建档) | +| `customer_code` | string | **是** | — | 客户编码(不存在则失败) | +| `mold_cost` | float | **是** | — | 模具费 ¥(一次性) | +| `unit_price` | float | **是** | — | 单价 ¥ | +| `annual_volume` | int | **是** | — | 年用量 | +| `material_cost` | float | **是** | — | 材料成本 ¥/件 | +| `casting_cost` | float | **是** | — | 压铸成本 ¥/件 | +| `machining_cost` | float | **是** | — | 机加工成本 ¥/件 | +| `post_process_cost` | float | **是** | — | 后处理成本 ¥/件 | +| `overhead_rate` | float | 否 | 0.12 | 管理费率 | +| `profit_rate` | float | 否 | 0.15 | 利润率 | +| `tax_rate` | float | 否 | 0.13 | 税率 | +| `payment_terms` | string | 否 | `月结30天` | 付款条款 | +| `delivery_terms` | string | 否 | `含税含运` | 交付条款 | +| `valid_days` | int | 否 | 30 | 报价有效天数(自当日起) | +| `remarks` | string | 否 | null | 备注 | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `quotation_id` | string | 生成的报价单号 | +| `status` | string | 固定 `draft`(草稿,待人工审核后发出) | +| `version` | int | 本次版本号(自动递增) | +| `previous_quotation_id` | string\|null | 上一版报价单号(首报为 null) | +| `price_change_pct` | float\|null | 较上版价差百分比(首报为 null) | +| `summary.mold_cost` / `unit_price` / `annual_volume` | float/int | 回显 | +| `summary.total_annual` | float | 年总额 = 单价×年用量 | +| `summary.tax_rate` | string | 税率百分比字符串,如 `"13%"` | +| `summary.tax_amount` | float | 税额 | +| `summary.total_amount` | float | 含税总额 | +| `summary.currency` | string | 固定 `CNY` | +| `valid_until` | string | 有效期至 `YYYY-MM-DD` | +| `warning` | string(可选) | 存在中标旧版时的提示 | +| `message` | string | 提示信息 | + +**返回示例**(第 2 版议价): + +```json +{ + "success": true, + "quotation_id": "QUO-2026-0029", + "status": "draft", + "version": 2, + "previous_quotation_id": "QUO-2026-0028", + "price_change_pct": -3.0, + "summary": { + "mold_cost": 180000, + "unit_price": 42.7, + "annual_volume": 80000, + "total_annual": 3416000.0, + "tax_rate": "13%", + "tax_amount": 444080.0, + "total_amount": 3860080.0, + "currency": "CNY" + }, + "valid_until": "2026-09-08", + "message": "报价单 QUO-2026-0029(第 2 版)已保存为草稿,待人工审核后发出" +} +``` + +**业务规则**: +- 版本链查询用 3.4;`won`/`lost` 终态永不被 superseded 覆盖。 +- 并发安全由数据库 `(inquiry_id, version)` 唯一约束兜底。 +- 草稿态报价单在 HTML 上带「草稿·待人工审核」水印,人工审核后才可发出。 + +--- + +### 3.4 get_quotation_history — 查询历史报价 + +**功能**:历史报价与版本链查询,用于再报价前置检查(有没有报过、上一版多少钱)与价格一致性校验。同一询价单按版本号降序返回,并逐行标记 `is_latest`。 + +**入参**(全部可选): + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `customer_code` | string | 否 | — | 按客户筛选 | +| `inquiry_id` | string | 否 | — | 按询价单筛选 | +| `start_date` | string | 否 | — | 报价日期起,`YYYY-MM-DD` | +| `end_date` | string | 否 | — | 报价日期止,`YYYY-MM-DD` | +| `status` | string | 否 | — | 状态筛选:draft/superseded/won/lost | +| `latest_only` | bool | 否 | false | true 时每个询价单仅返回最新一版 | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `total` | int | 命中条数 | +| `quotations[]` | array | 报价行(quotation 全字段 + `customer_name`) | +| `quotations[].version` | int | 版本号 | +| `quotations[].status` | string | 状态 | +| `quotations[].is_latest` | bool | 是否为该询价单最新版本(全表口径,不受筛选影响) | +| `quotations[].cost_detail` | object | 成本明细(四项成本 + 费率 + total_cost + unit_price) | + +**返回示例**(INQ-2026-0019 三版议价链,latest_only=true): + +```json +{ + "total": 1, + "quotations": [ + { + "quotation_id": "QUO-2026-0018", + "inquiry_id": "INQ-2026-0019", + "customer_code": "OEM-2024-002", + "quotation_date": "2026-08-07", + "valid_until": "2026-09-06", + "version": 3, + "status": "draft", + "mold_cost": "180000.00", + "unit_price": "41.50", + "annual_volume": 80000, + "customer_name": "某商用车主机厂", + "is_latest": true, + "cost_detail": { "...": "四项成本与费率" } + } + ] +} +``` + +**注意**:日期参数格式错误(非 `YYYY-MM-DD`)会抛出解析异常,调用方应保证格式。 + +--- + +### 3.5 update_opportunity — 创建/更新商机 + +**功能**:双模式。传 `opportunity_id` = 更新已有商机的阶段/金额/概率等字段(仅更新非 null 字段);不传 `opportunity_id` = 按 `customer_code + title` 新建商机(默认 stage=lead、probability=20、expected_amount=0,source=智能报价自动生成)。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `opportunity_id` | string | 否 | — | 商机号(传入则走更新模式) | +| `customer_code` | string | 新建时必填 | — | 客户编码 | +| `title` | string | 新建时必填 | — | 商机标题 | +| `stage` | string | 否 | 新建时 `lead` | 阶段:lead/quoted/won/lost | +| `expected_amount` | float | 否 | 新建时 0 | 预期金额 ¥ | +| `probability` | int | 否 | 新建时 20 | 成交概率 % | +| `oem_program` | string | 否 | — | OEM 项目/平台 | +| `expected_close_date` | string | 否 | — | 预期关单日期 `YYYY-MM-DD` | +| `notes` | string | 否 | — | 备注(保留参数) | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `opportunity_id` | string | 商机号(更新时为传入值,新建时为生成值) | +| `message` | string | 「已更新」或「已创建」 | + +**失败返回**: +- `{"success": false, "message": "expected_close_date 格式错误:...,应为 YYYY-MM-DD"}` +- `{"success": false, "message": "未找到商机 OPP-2026-XXXX"}` +- `{"success": false, "message": "新建商机需要提供 customer_code 和 title"}` +- `{"success": false, "message": "未找到客户 XXX"}` + +**返回示例**(新建): + +```json +{ + "success": true, + "opportunity_id": "OPP-2026-0019", + "message": "商机 OPP-2026-0019 已创建" +} +``` + +--- + +### 3.6 save_quotation_mock — 报价写入 CRM(Mock 模拟) + +**用途**:模拟「报价写入 CRM」动作。写入独立 mock 表 `quotation_mock`,不校验客户/询价单/商机,与其他工具无任何关联,适合演示与联调场景单独调用。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| `part_name` | string | ✅ | — | 零件名称 | +| `unit_price` | number | ✅ | — | 报价单价 | +| `customer_name` | string | — | null | 客户名称(自由文本,不做存在性校验) | +| `part_number` | string | — | null | 零件号 | +| `material_grade` | string | — | null | 材质牌号 | +| `mold_cost` | number | — | 0.0 | 模具费 | +| `annual_volume` | integer | — | null | 年用量(提供时自动计算 `total_annual = unit_price × annual_volume`) | +| `currency` | string | — | CNY | 币种 | +| `remarks` | string | — | null | 备注 | + +**返回示例**: + +```json +{ + "success": true, + "quotation_id": "QUO-MOCK-0001", + "status": "draft", + "summary": { + "part_name": "变速箱阀体外壳", "unit_price": 41.66, "mold_cost": 180000.0, + "annual_volume": 300000, "total_annual": 12498000.0, "currency": "CNY" + }, + "message": "报价单 QUO-MOCK-0001 已写入 CRM mock 表(quotation_mock)" +} +``` + +**关键说明**: +- 单号 `QUO-MOCK-####` 自增,独立于正式 `QUO-2026-####` 序列。 +- `quotation_mock` 表无外键,写入不影响正式三表(inquiry/quotation/opportunity),`get_record_counts` 也不统计该表。 +- 无版本管理、无状态流转:每次调用新增一行,状态固定 `draft`。 + +## 4 验证/管理工具详解(4 个) + +> 用途:**MCP 读写分离交叉验证**——写工具的返回值与只读回读工具交叉核对;测试数据清理经受限工具完成。 +> 原则:验证/清理环节禁止直连数据库;`db.py` 仅为服务层内部连接池。 + +### 4.1 get_inquiry_info — 询价单回读(只读) + +**功能**:按询价单号回读询价单整行,用于存档后验证(状态、图号、零件号、年用量、目标价、创建时间)。 + +**入参**: + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `inquiry_id` | string | **是** | 询价单号 | + +**返回**:命中 `{"success": true, "inquiry": {<整行字段>}}`;未命中 `{"success": false, "message": "未找到询价单 XXX"}`。 + +**示例**: + +```json +{ + "success": true, + "inquiry": { + "inquiry_id": "INQ-2026-0025", + "customer_code": "T1-2025-002", + "inquiry_date": "2026-08-08", + "drawing_number": "DWG-VHB-MN-Rev1", + "part_number": "P-VHB-009", + "annual_volume": 5000, + "target_price": "30.00", + "status": "quoted", + "notes": "TC-05生产报价:微型泵阀体壳 ADC12 96.3cc 160T一模两腔...", + "created_at": "2026-08-08 13:34:11.945841" + } +} +``` + +--- + +### 4.2 get_opportunity_info — 商机回读(只读) + +**功能**:按商机号或客户编码回读商机,用于存档后验证(stage/probability/expected_amount/更新时间)。两参数二选一,`opportunity_id` 优先。 + +**入参**: + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `opportunity_id` | string | 二选一 | 商机号(精确单条) | +| `customer_code` | string | 二选一 | 客户编码(该客户全部商机,按创建时间倒序) | + +**返回**:`{"total": N, "opportunities": [<整行字段>]}`;两参数都未提供时 `{"success": false, "message": "需提供 opportunity_id 或 customer_code"}`。 + +**示例**: + +```json +{ + "total": 1, + "opportunities": [ + { + "opportunity_id": "OPP-2026-0018", + "customer_code": "T1-2025-002", + "title": "微型泵阀体壳 ADC12(P-VHB-009)首单", + "stage": "quoted", + "expected_amount": "40300.00", + "probability": 50, + "oem_program": "大陆制动系统配套", + "expected_close_date": "2026-09-30", + "source": "智能报价自动生成", + "created_by": "AI智能报价", + "updated_at": "2026-08-08" + } + ] +} +``` + +--- + +### 4.3 get_record_counts — 三表行数统计(只读) + +**功能**:返回 inquiry/quotation/opportunity 三表行数,用于数据卫生检查(如回归前后行数一致性核对)。 + +**入参**:无。 + +**返回示例**: + +```json +{ + "success": true, + "counts": { "inquiry": 24, "quotation": 27, "opportunity": 18 } +} +``` + +--- + +### 4.4 purge_test_records — 受限测试数据清理(管理) + +**功能**:清理 TC-03 回归测试写入的数据。**带硬性防护**:仅当报价单 `remarks`、询价单 `notes` 含「TC-03回归测试」,或商机 `title` 以「TC-03」开头时才允许删除;其余一律进入 `rejected` 列表,防止误删演示/生产数据。级联删除子表(quotation_cost_detail / inquiry_item)。 + +**入参**(至少传一组;均为 ID 数组): + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `quotation_ids` | array[string] | 否 | 待清理报价单号列表 | +| `inquiry_ids` | array[string] | 否 | 待清理询价单号列表 | +| `opportunity_ids` | array[string] | 否 | 待清理商机号列表 | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 固定 true(单条拒绝不影响整体) | +| `deleted.quotations` / `deleted.inquiries` / `deleted.opportunities` | int | 实际删除条数 | +| `deleted.rejected` | array[string] | 被防护拒绝的 ID 列表(非测试标记或不存在) | + +**示例一:正常清理(回归测试自动调用)** + +```json +{ + "success": true, + "deleted": { "quotations": 3, "inquiries": 3, "opportunities": 1, "rejected": [] } +} +``` + +**示例二:防护生效(对生产数据调用)** + +```json +{ + "success": true, + "deleted": { + "quotations": 0, "inquiries": 0, "opportunities": 0, + "rejected": ["QUO-2026-0027", "INQ-2026-0025", "OPP-2026-0018"] + } +} +``` + +--- + +## 5 附录 + +### 5.1 依赖数据表 + +| 表 | 用途 | +|----|------| +| `customer` | 客户主数据(信用/折扣/账期/历史统计) | +| `inquiry` | 询价单(pending→quoted) | +| `quotation` | 报价单主表(版本链/状态机/金额) | +| `quotation_cost_detail` | 报价成本明细(四项成本+费率) | +| `opportunity` | 商机(阶段/金额/概率/关单日期) | + +### 5.2 标准存档链路(智能体 Step 7) + +``` +get_quotation_history(inquiry_id) ← 前置:查有无历史版本 + ├─ 无 → create_inquiry(...) ← 建询价单 + └─ 有 → 复用原 inquiry_id +save_quotation(...) ← 存报价(版本自动+1,事务) +update_opportunity(...) ← 商机同步最新版年总额(stage=quoted) +get_inquiry_info / get_opportunity_info ← 可选:存档后交叉验证 +``` + +### 5.3 与 ERP 服务的协作 + +核价计算(材质对照/压铸参数/机加工工时/汇总核价)由 `mcp-for-erp` 承担,见《MCP for ERP — 工具说明文档》;CRM 服务不做核价计算,只做客户与商务数据管理。 diff --git a/mcp-for-crm/README.md b/mcp-for-crm/README.md index cf04187..baa28ce 100644 --- a/mcp-for-crm/README.md +++ b/mcp-for-crm/README.md @@ -2,7 +2,7 @@ ## 概述 -为汽车零部件(小型阀体外壳)智能报价智能体提供 CRM 数据访问能力,通过 MCP 协议暴露 5 个工具。 +为汽车零部件(小型阀体外壳)智能报价智能体提供 CRM 数据访问能力,通过 MCP 协议暴露 10 个工具(6 个业务 + 4 个验证/管理)。 ## 技术栈 @@ -20,6 +20,7 @@ | `save_quotation` | 保存报价单(含模具费、单价、成本明细、版本管理) | | `get_quotation_history` | 查询历史报价(按客户/零件号/日期) | | `update_opportunity` | 商机状态管理(lead→quoted→won/lost) | +| `save_quotation_mock` | 报价写入 CRM(Mock 模拟,独立 mock 表,与其他工具无关联) | ## 快速开始 diff --git a/mcp-for-crm/sql/init.sql b/mcp-for-crm/sql/init.sql index a9965e2..3ad19a1 100644 --- a/mcp-for-crm/sql/init.sql +++ b/mcp-for-crm/sql/init.sql @@ -249,3 +249,23 @@ INSERT INTO opportunity (opportunity_id, customer_code, title, stage, expected_a ('OPP-2026-0001', 'OEM-2024-001', '2026年新车型阀体壳定点', 'proposal', 27750000, 60, '某日系新车型CVT配套', '2026-10-31', '客户主动询价', 'AI智能报价'), ('OPP-2026-0002', 'T1-2025-001', '转向器阀体壳年度采购', 'lead', 8250000, 30, 'EPS转向系统配套', '2026-12-31', '展会获客', 'AI智能报价') ON CONFLICT (opportunity_id) DO NOTHING; + +-- ============================================================ +-- 7. 报价写入 CRM Mock 表(独立模拟场景,无外键、与其他工具无关) +-- ============================================================ +CREATE TABLE IF NOT EXISTS quotation_mock ( + quotation_id VARCHAR(50) PRIMARY KEY, + customer_name VARCHAR(200), + part_name VARCHAR(200), + part_number VARCHAR(50), + material_grade VARCHAR(50), + mold_cost DECIMAL(12,2) DEFAULT 0, + unit_price DECIMAL(10,2), + annual_volume INTEGER, + total_annual DECIMAL(14,2), + currency VARCHAR(10) DEFAULT 'CNY', + quotation_date DATE NOT NULL DEFAULT CURRENT_DATE, + status VARCHAR(20) DEFAULT 'draft', + remarks TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/mcp-for-crm/src/server.py b/mcp-for-crm/src/server.py index 0800ca5..96d0ef2 100644 --- a/mcp-for-crm/src/server.py +++ b/mcp-for-crm/src/server.py @@ -482,6 +482,53 @@ async def purge_test_records( return json.dumps({"success": True, "deleted": deleted}, ensure_ascii=False, indent=2) +# ============================================================ +# 工具 10: save_quotation_mock — 报价写入 CRM(Mock 模拟) +# ============================================================ + +@app.tool() +async def save_quotation_mock( + part_name: str, + unit_price: float, + customer_name: str = None, + part_number: str = None, + material_grade: str = None, + mold_cost: float = 0.0, + annual_volume: int = None, + currency: str = "CNY", + remarks: str = None +) -> str: + """报价写入 CRM(模拟场景)。写入独立 mock 表 quotation_mock:不校验客户/询价单/商机,与其他工具无关联,仅模拟报价写入 CRM 动作。""" + pool = await get_pool() + + max_seq = await pool.fetchval( + "SELECT COALESCE(MAX(CAST(SUBSTRING(quotation_id FROM '(\\d+)$') AS INTEGER)), 0) FROM quotation_mock WHERE quotation_id LIKE 'QUO-MOCK-%'" + ) + quotation_id = f"QUO-MOCK-{str(max_seq + 1).zfill(4)}" + + total_annual = round(unit_price * annual_volume, 2) if annual_volume else None + + await pool.execute( + """INSERT INTO quotation_mock (quotation_id, customer_name, part_name, part_number, material_grade, + mold_cost, unit_price, annual_volume, total_annual, currency, status, remarks) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'draft', $11)""", + quotation_id, customer_name, part_name, part_number, material_grade, + mold_cost, unit_price, annual_volume, total_annual, currency, remarks + ) + + return json.dumps({ + "success": True, + "quotation_id": quotation_id, + "status": "draft", + "summary": { + "part_name": part_name, "unit_price": unit_price, "mold_cost": mold_cost, + "annual_volume": annual_volume, "total_annual": total_annual, "currency": currency + }, + "message": f"报价单 {quotation_id} 已写入 CRM mock 表(quotation_mock)" + }, ensure_ascii=False, indent=2) + + + # ============================================================ # 启动 HTTP Server # ============================================================ diff --git a/mcp-for-crm/数据表与种子数据说明.md b/mcp-for-crm/数据表与种子数据说明.md new file mode 100644 index 0000000..584f03a --- /dev/null +++ b/mcp-for-crm/数据表与种子数据说明.md @@ -0,0 +1,278 @@ +# MCP for CRM — 数据表结构与种子数据说明文档 + +> 数据库:PostgreSQL 15+,库 `smart_quotation_auto`(与 ERP 服务共库不同表) +> 建表/种子脚本:`sql/init.sql`(v3,幂等可重跑);执行入口:`cd src && python3 seed.py` +> 版本:v1.0(2026-08-09) + +--- + +## 1 概览 + +| # | 表名 | 用途 | 主键 | 种子行数 | +|---|------|------|------|----------| +| 1 | `customer` | 客户主数据(信用/折扣/账期) | customer_code | 20 | +| 2 | `inquiry` | 询价单 | inquiry_id | 9 | +| 3 | `inquiry_item` | 询价明细行(多零件) | id(SERIAL) | 9 | +| 4 | `quotation` | 报价单(多版本链) | quotation_id | 7 | +| 5 | `quotation_cost_detail` | 报价成本明细 | id(SERIAL) | 7 | +| 6 | `opportunity` | 商机 | opportunity_id | 8 | +| 7 | `quotation_mock` | 报价写入 CRM 模拟(独立 mock 表) | quotation_id | 0(运行时写入) | + +**表关系**: + +``` +customer ──┬── inquiry ──── inquiry_item (客户 1─N 询价;询价 1─N 明细行) + ├── quotation ── quotation_cost_detail(客户 1─N 报价;报价 1─1 成本明细) + │ └── inquiry_id 关联询价(版本链:同询价 N 版报价) + └── opportunity (客户 1─N 商机) +``` + +**外键**:inquiry/inquiry_item/quotation/opportunity 均外键指向 `customer.customer_code`;inquiry_item → inquiry;quotation → inquiry(可空);quotation_cost_detail → quotation。`quotation_mock` 无任何外键,为独立模拟表。 + +--- + +## 2 表结构详解 + +### 2.1 customer — 客户 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `customer_code` | VARCHAR(50) | PRIMARY KEY | 客户编码,如 `OEM-2024-003` | +| `name` | VARCHAR(200) | NOT NULL | 客户名称 | +| `oem_tier` | VARCHAR(20) | NOT NULL | OEM / Tier1 / Tier2 | +| `credit_level` | VARCHAR(5) | DEFAULT 'B' | 信用等级:A/B/C/D/无评级 | +| `discount_rate` | DECIMAL(4,3) | DEFAULT 0 | 折扣率(0.040 = 4%) | +| `payment_days` | INTEGER | DEFAULT 30 | 账期天数(0 = 货到付款/预付) | +| `contact_person` / `phone` / `email` | VARCHAR | — | 联系人信息 | +| `industry` / `region` | VARCHAR(50) | — | 行业 / 区域 | +| `total_orders` | INTEGER | DEFAULT 0 | 历史订单数 | +| `total_amount` | DECIMAL(14,2) | DEFAULT 0 | 历史订单总额 | +| `notes` | TEXT | — | 备注(风险提示等) | +| `created_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 建档时间 | + +### 2.2 inquiry — 询价单 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `inquiry_id` | VARCHAR(50) | PRIMARY KEY | 询价单号 `INQ-YYYY-NNNN` | +| `customer_code` | VARCHAR(50) | NOT NULL,FK | 客户编码 | +| `inquiry_date` | DATE | NOT NULL DEFAULT CURRENT_DATE | 询价日期 | +| `drawing_number` | VARCHAR(100) | — | 图纸号 | +| `part_number` | VARCHAR(100) | — | 零件号 | +| `annual_volume` | INTEGER | — | 年用量(件/年) | +| `target_price` | DECIMAL(10,2) | — | 客户目标价 ¥ | +| `status` | VARCHAR(20) | DEFAULT 'pending' | pending / quoted(首报自动推进);种子含历史态 completed/processing/lost | +| `notes` | TEXT | — | 备注 | +| `created_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 | + +### 2.3 inquiry_item — 询价明细行 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `inquiry_id` | VARCHAR(50) | NOT NULL,FK | 所属询价单 | +| `line_no` | INTEGER | NOT NULL | 行号;UNIQUE(inquiry_id, line_no) | +| `part_name` | VARCHAR(200) | — | 零件名称 | +| `part_number` | VARCHAR(100) | — | 零件号 | +| `material_grade` | VARCHAR(50) | — | 客户材质牌号 | +| `volume_cc` | DECIMAL(10,2) | — | 体积 cm³ | +| `annual_qty` | INTEGER | — | 该行年用量 | +| `unit` | VARCHAR(10) | DEFAULT '件' | 单位 | +| `remarks` | TEXT | — | 备注 | + +### 2.4 quotation — 报价单(多版本核心表) + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `quotation_id` | VARCHAR(50) | PRIMARY KEY | 报价单号 `QUO-YYYY-NNNN` | +| `inquiry_id` | VARCHAR(50) | FK(可空) | 所属询价单;索引 idx_quotation_inquiry | +| `customer_code` | VARCHAR(50) | NOT NULL,FK | 客户编码 | +| `quotation_date` | DATE | NOT NULL DEFAULT CURRENT_DATE | 报价日期 | +| `valid_until` | DATE | — | 有效期至 | +| `version` | INTEGER | DEFAULT 1 | 版本号;**UNIQUE(inquiry_id, version)**(并发防重) | +| `status` | VARCHAR(20) | DEFAULT 'draft' | draft/sent/approved/superseded/won/lost | +| `mold_cost` | DECIMAL(12,2) | DEFAULT 0 | 模具费(一次性) | +| `unit_price` | DECIMAL(10,2) | — | 单价 ¥ | +| `annual_volume` | INTEGER | — | 年用量 | +| `total_annual` | DECIMAL(14,2) | — | 年总额 = 单价×年用量 | +| `subtotal` | DECIMAL(14,2) | — | 小计 | +| `tax_rate` | DECIMAL(4,3) | DEFAULT 0.130 | 税率 | +| `tax_amount` | DECIMAL(14,2) | — | 税额 | +| `total_amount` | DECIMAL(14,2) | — | 含税总额 | +| `currency` | VARCHAR(10) | DEFAULT 'CNY' | 币种 | +| `payment_terms` | VARCHAR(200) | DEFAULT '月结30天' | 付款条款 | +| `delivery_terms` | VARCHAR(200) | DEFAULT '含税含运' | 交付条款 | +| `created_by` | VARCHAR(50) | DEFAULT 'AI智能报价' | 创建者 | +| `approved_by` | VARCHAR(50) | — | 审核人(人工审核后填写) | +| `remarks` | TEXT | — | 备注(测试数据防护标记写入处) | +| `created_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 | + +**版本链规则**:save_quotation 事务内 `version = MAX(version)+1`(不限次数);旧版非终态批量置 `superseded`;`won`/`lost` 终态保留不覆盖。 + +### 2.5 quotation_cost_detail — 报价成本明细 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `quotation_id` | VARCHAR(50) | NOT NULL,FK,UNIQUE(uq_quotation_cost) | 报价单号(1:1) | +| `material_cost` | DECIMAL(10,4) | — | 材料成本 ¥/件 | +| `casting_cost` | DECIMAL(10,4) | — | 压铸成本 ¥/件 | +| `machining_cost` | DECIMAL(10,4) | — | 机加工成本 ¥/件 | +| `post_process_cost` | DECIMAL(10,4) | — | 后处理成本 ¥/件 | +| `overhead_rate` | DECIMAL(4,3) | — | 管理费率 | +| `profit_rate` | DECIMAL(4,3) | — | 利润率 | +| `total_cost` | DECIMAL(10,4) | — | 四项成本小计 | +| `unit_price` | DECIMAL(10,4) | — | 单价(冗余留痕,与主表对账) | + +### 2.6 opportunity — 商机 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `opportunity_id` | VARCHAR(50) | PRIMARY KEY | 商机号 `OPP-YYYY-NNNN` | +| `customer_code` | VARCHAR(50) | NOT NULL,FK | 客户编码 | +| `title` | VARCHAR(200) | NOT NULL | 商机标题(测试数据防护标记写入处) | +| `stage` | VARCHAR(20) | DEFAULT 'lead' | lead/qualified/proposal/negotiation/quoted/won/lost | +| `expected_amount` | DECIMAL(14,2) | DEFAULT 0 | 预期金额 ¥ | +| `probability` | INTEGER | DEFAULT 20 | 成交概率 % | +| `oem_program` | VARCHAR(200) | — | OEM 项目/平台 | +| `expected_close_date` | DATE | — | 预期关单日期 | +| `source` | VARCHAR(100) | — | 来源(智能报价自动生成/客户主动询价/展会获客等) | +| `created_by` | VARCHAR(50) | — | 创建者 | +| `created_at` / `updated_at` | DATE | DEFAULT CURRENT_DATE | 创建/更新日期 | + +--- + +### 2.7 quotation_mock — 报价写入 CRM 模拟表(独立,无外键) + +| 字段 | 类型 | 说明 | +|------|------|------| +| quotation_id | VARCHAR(50) PK | 模拟报价单号,`QUO-MOCK-####` 自增 | +| customer_name | VARCHAR(200) | 客户名称(自由文本,不校验) | +| part_name | VARCHAR(200) | 零件名称 | +| part_number | VARCHAR(50) | 零件号 | +| material_grade | VARCHAR(50) | 材质牌号 | +| mold_cost | DECIMAL(12,2) | 模具费,默认 0 | +| unit_price | DECIMAL(10,2) | 报价单价 | +| annual_volume | INTEGER | 年用量 | +| total_annual | DECIMAL(14,2) | 年总额(unit_price × annual_volume) | +| currency | VARCHAR(10) | 币种,默认 CNY | +| quotation_date | DATE | 报价日期,默认当天 | +| status | VARCHAR(20) | 状态,固定 `draft` | +| remarks | TEXT | 备注 | +| created_at | TIMESTAMP | 创建时间 | + +> 由工具 `save_quotation_mock` 单独写入,与正式报价链路(inquiry/quotation/opportunity)完全隔离,用于模拟「报价写入 CRM」场景。 + +## 3 种子数据详解 + +### 3.1 customer — 20 家客户 + +覆盖设计: + +- **层级**:OEM×7 / Tier1×7 / Tier2×6 +- **信用梯度**:A×8 / B×6 / C×2 / D×1 / 无评级×3 +- **特殊样本**:零历史新客户×3(T2-2025-002、T2-2026-001、T2-2026-002,用于信用评审场景);长账期 90 天(OEM-2025-001);货到付款 C/D 级(T2-2025-001/003/004) + +| 编码 | 名称 | 层级 | 信用 | 折扣率 | 账期 | 区域 | 特征 | +|------|------|------|------|--------|------|------|------| +| OEM-2024-001 | 某日系主机厂 | OEM | A | 5.0% | 60 | 上海 | 长期合作,铝合金阀体壳 | +| OEM-2024-002 | 某德系主机厂 | OEM | A | 3.0% | 45 | 北京 | 质量要求高,偏好铸铁件 | +| OEM-2024-003 | 某新能源主机厂 | OEM | A | 4.0% | 60 | 合肥 | 三电配套,增长快 | +| OEM-2024-004 | 某美系主机厂 | OEM | A | 3.0% | 45 | 武汉 | 混动变速箱项目 | +| OEM-2025-001 | 某商用车主机厂 | OEM | B | 2.0% | 90 | 长春 | 长账期,注意现金流 | +| OEM-2025-002 | 某乘商两用主机厂 | OEM | B | 2.5% | 60 | 柳州 | 乘商双线采购 | +| OEM-2026-001 | 某智能电动新势力 | OEM | A | 5.0% | 45 | 上海 | 高折扣,战略培育 | +| T1-2025-001 | 某博世供应商 | Tier1 | B | 2.0% | 30 | 江苏苏州 | 变速箱配套 | +| T1-2025-002 | 某大陆供应商 | Tier1 | B | 0 | 30 | 广东广州 | 制动配套,新开发 | +| T1-2025-003 | 某采埃孚供应商 | Tier1 | A | 2.0% | 45 | 上海 | ZF 悬架配套 | +| T1-2025-004 | 某电装系供应商 | Tier1 | A | 2.5% | 45 | 广州 | 日系热管理 | +| T1-2025-005 | 某日立安斯泰莫供应商 | Tier1 | A | 2.0% | 45 | 大连 | 日系制动/转向 | +| T1-2026-001 | 某制动系统供应商 | Tier1 | B | 1.5% | 30 | 重庆 | 国产线控制动 | +| T1-2026-002 | 某液压系统供应商 | Tier1 | B | 1.0% | 60 | 长沙 | 液压阀体,账期长 | +| T2-2025-001 | 某售后市场贸易商 | Tier2 | C | 0 | 0 | 杭州 | 货到付款,注意风险 | +| T2-2025-002 | 某智能底盘初创公司 | Tier2 | 无评级 | 0 | 30 | 苏州 | 零订单新客户 | +| T2-2025-003 | 某非道路机械贸易商 | Tier2 | D | 0 | 0 | 济宁 | 仅接受预付款 | +| T2-2025-004 | 某改装件连锁商 | Tier2 | C | 0 | 0 | 成都 | 改装连锁,货到付款 | +| T2-2026-001 | 某机器人关节初创公司 | Tier2 | 无评级 | 0 | 30 | 深圳 | 人形机器人关节部件 | +| T2-2026-002 | 某低空经济无人机公司 | Tier2 | 无评级 | 0 | 30 | 深圳 | eVTOL 液压部件 | + +### 3.2 inquiry + inquiry_item — 9 单询价(含明细行) + +覆盖设计:2024~2025 历史闭环(completed/lost)+ 2026 在途(pending/processing);单零件行,多零件场景由运行时写入。 + +| 询价单 | 客户 | 日期 | 零件 | 年用量 | 目标价 | 状态 | +|--------|------|------|------|--------|--------|------| +| INQ-2024-9005 | OEM-2024-001 | 2024-10-20 | 发动机油路阀体壳(EN AC-4600,555.56cc) | 200,000 | 55.00 | completed | +| INQ-2025-9001 | OEM-2024-001 | 2025-09-25 | 变速箱阀体外壳(ADC12,444.44cc) | 300,000 | 96.00 | completed | +| INQ-2025-9002 | OEM-2024-002 | 2025-08-10 | 制动阀体壳(HT250,388.89cc) | 150,000 | 155.00 | completed | +| INQ-2025-9003 | T1-2025-001 | 2025-05-28 | 转向器阀体壳(A356-T6,296.30cc) | 150,000 | 25.00 | lost | +| INQ-2025-9006 | OEM-2024-002 | 2025-11-05 | 变速箱阀体外壳(ADC12,小批量) | 100,000 | 100.00 | completed | +| INQ-2026-0001 | OEM-2024-001 | 2026-08-01 | 变速箱阀体外壳(ADC12,444.44cc) | 300,000 | 95.00 | processing | +| INQ-2026-0002 | T1-2025-001 | 2026-08-03 | 转向器阀体壳(A356-T6,296.30cc) | 150,000 | 55.00 | pending | +| INQ-2026-0006 | OEM-2024-004 | 2026-08-05 | 涡轮增压控制阀体壳(ADC12,320cc) | 120,000 | 48.00 | pending | +| INQ-2026-0007 | OEM-2025-001 | 2026-08-06 | 重卡制动阀体壳(HT300,850cc,1000T) | 60,000 | 260.00 | processing | + +### 3.3 quotation + quotation_cost_detail — 7 张报价 + +覆盖设计:状态谱系 approved/won/lost/sent + 一条**两版议价链**(INQ-2025-9001:v1→v2,一致性校验基准)+ 跨客户价差样本。 + +| 报价单 | 询价单 | 客户 | 版本 | 状态 | 模具费 | 单价 | 年用量 | 含税总额 | 备注 | +|--------|--------|------|------|------|--------|------|--------|----------|------| +| QUO-2024-9005 | INQ-2024-9005 | OEM-2024-001 | 1 | approved | 180,000 | 52.30 | 200,000 | 11,819,800 | 2024 基线价 | +| QUO-2025-9001 | INQ-2025-9001 | OEM-2024-001 | 1 | approved | 0 | 42.50 | 300,000 | 14,407,500 | 模具费已摊销 | +| QUO-2025-9002 | INQ-2025-9001 | OEM-2024-001 | 2 | approved | 0 | 41.90 | 300,000 | 14,204,100 | 第二轮降价 | +| QUO-2025-9003 | INQ-2025-9002 | OEM-2024-002 | 1 | won | 250,000 | 46.80 | 150,000 | 7,932,600 | 已中标(终态保护样本) | +| QUO-2025-9004 | INQ-2025-9003 | T1-2025-001 | 1 | lost | 120,000 | 28.40 | 150,000 | 4,813,800 | 报价偏高丢单(利润率 28% 样本) | +| QUO-2025-9006 | INQ-2025-9006 | OEM-2024-002 | 1 | sent | 0 | 43.20 | 100,000 | 4,881,600 | 跨客户价差校验 | +| QUO-2026-0001 | INQ-2026-0001 | OEM-2024-001 | 1 | sent | 180,000 | 92.50 | 300,000 | 31,357,500 | 老客户优惠,含模具费 | + +成本明细与报价 1:1(四项成本 + 12% 管理费 + 15% 利润,仅 QUO-2025-9004 为 28% 利润)。 + +### 3.4 opportunity — 8 条商机 + +覆盖设计:阶段全谱系 lead/qualified/proposal/negotiation/won/lost;金额 450 万~2775 万;AI 与销售团队双来源。 + +| 商机号 | 客户 | 阶段 | 金额¥ | 概率 | 关单日期 | OEM 项目 | +|--------|------|------|-------|------|----------|----------| +| OPP-2025-0005 | OEM-2024-002 | won | 8,200,000 | 100% | 2025-09-30 | 德系新制动平台 | +| OPP-2025-0006 | T1-2025-001 | lost | 4,500,000 | 0% | 2025-07-31 | EPS 转向系统 | +| OPP-2026-0001 | OEM-2024-001 | proposal | 27,750,000 | 60% | 2026-10-31 | 日系新车型 CVT | +| OPP-2026-0002 | T1-2025-001 | lead | 8,250,000 | 30% | 2026-12-31 | EPS 转向配套 | +| OPP-2026-0003 | T1-2025-003 | lead | 5,000,000 | 20% | 2026-12-31 | 主动悬架配套 | +| OPP-2026-0004 | OEM-2024-003 | negotiation | 9,000,000 | 60% | 2026-11-30 | 纯电平台热管理+制动 | +| OPP-2026-0005 | OEM-2024-004 | qualified | 12,000,000 | 40% | 2027-03-31 | 下一代混动变速箱 | +| OPP-2026-0006 | OEM-2025-001 | lead | 6,000,000 | 15% | 2026-12-31 | 重卡制动升级 | + +--- + +## 4 运行时数据(种子之外,真实运行留痕) + +以下数据由智能体真实运行产生,**不属于 init.sql**,作为演示/生产留痕保留: + +- **版本链演示**:INQ-2026-0019 三版议价(v1→v2→v3,旧版 superseded,带 price_change_pct)。 +- **生产报价留痕**:INQ-2026-0020~0027、QUO-2026-0019~0027、OPP-2026-0012~0018(含 TC-01/02/05 生产运行,如 QUO-2026-0027 单价 ¥8.06)。 +- **数据库现状**(2026-08-09):customer 20 / inquiry 24 / quotation 27 / opportunity 18;inquiry_item 与成本明细随行数联动。 +- **回归测试数据**:TC-03 写入带「TC-03回归测试」标记,跑完经 `purge_test_records` 自清理,不累积。 + +--- + +## 5 幂等性与重跑机制 + +`init.sql` 整体可重复执行,无副作用(NF-3): + +1. **建表**:`CREATE TABLE IF NOT EXISTS`。 +2. **历史去重**:inquiry_item 按 (inquiry_id, line_no)、quotation_cost_detail 按 quotation_id 删重(保留最小 id),随后幂等补唯一约束。 +3. **版本号规范化**:对存量 quotation 按 (inquiry_id, created_at, quotation_id) 重写 version 为 1..n(幂等,已规范的行不变)。 +4. **并发防重**:`UNIQUE(inquiry_id, version)` + 索引 `idx_quotation_inquiry`。 +5. **种子写入**:全部 `ON CONFLICT (...) DO NOTHING`,重跑不产生重复客户/询价/报价/商机。 + +--- + +## 6 数据维护约定 + +| 场景 | 操作 | 说明 | +|------|------|------| +| 新客户建档 | 直接 SQL 或后续扩展工具 | customer_code 规则:OEM/T1/T2-年份-序号 | +| 报价审核 | UPDATE quotation SET status/approved_by | 当前无 MCP 写工具,人工线下完成 | +| 测试数据清理 | 仅经 MCP `purge_test_records` | 禁止直连 DELETE;非 TC-03 标记数据受防护 | +| 生产数据 | 不得清理 | 演示/运行留痕作为验收基线 | diff --git a/mcp-for-erp/MCP工具说明文档.md b/mcp-for-erp/MCP工具说明文档.md new file mode 100644 index 0000000..cd245ad --- /dev/null +++ b/mcp-for-erp/MCP工具说明文档.md @@ -0,0 +1,383 @@ +# MCP for ERP — 工具说明文档 + +> 服务对象:汽车零部件(小型阀体外壳)智能报价智能体 +> 服务定位:ERP 侧核价基础数据与成本计算引擎(只读 + 计算,不写库) +> 版本:v1.1(2026-08-09) + +--- + +## 1 服务概览 + +| 项 | 值 | +|----|----| +| 服务名 | `mcp-for-erp-auto` | +| 框架 | MCPServer(mcp 2.0.0 内置高级框架),`@app.tool()` 装饰器注册 | +| 传输协议 | Streamable HTTP(JSON-RPC 2.0) | +| 远程端点 | `https://erp-data.dongsk.top/mcp` | +| 本地端点 | `http://0.0.0.0:8001/mcp`(`cd src && python3 server.py`) | +| 数据库 | PostgreSQL 15+,库 `smart_quotation_auto`(仅服务层内部经 `db.py` 连接池访问) | +| 工具数 | **5 个**(全部只读/计算) | + +### 工具一览 + +| # | 工具 | 功能 | 报价环节 | +|---|------|------|----------| +| 1 | `query_material_master` | 查询物料主数据(体积/密度/吨位/后处理) | 基础数据 | +| 2 | `match_material_grade` | 客户牌号 → 内部等效牌号对照 | 材质确认 | +| 3 | `get_die_casting_params` | 压铸参数:铝水重量、节拍、铸造成本 | 压铸成本 | +| 4 | `get_machining_estimate` | 机加工工时估算(固化工装节拍) | 机加工成本 | +| 5 | `calculate_part_cost` | 汇总核价,输出最终单价 | 最终报价 | + +--- + +## 2 调用约定 + +### 2.1 客户端注册(mcpServers) + +```json +{ + "mcpServers": { + "erp": { "url": "https://erp-data.dongsk.top/mcp" } + } +} +``` + +### 2.2 JSON-RPC 调用流程 + +1. `initialize`(握手,获取 `Mcp-Session-Id`) +2. `notifications/initialized`(通知,无响应体) +3. `tools/list`(可选,列出工具) +4. `tools/call`(调用工具,`params: {name, arguments}`) + +### 2.3 兜底命令行客户端(纯标准库) + +```bash +python3 scripts/mcp_http_client.py erp <工具名> '' +# 示例 +python3 scripts/mcp_http_client.py erp calculate_part_cost '{"material_code":"VHB-AT-001","annual_qty":300000}' +``` + +### 2.4 返回通用约定 + +- 所有工具返回 **JSON 字符串**(`ensure_ascii=False`,中文不转义)。 +- 查询类工具返回 `{"total": N, "<集合名>": [...]}`;操作/计算类工具返回 `{"success": true|false, ...}`。 +- 失败统一形态:`{"success": false, "message": "<原因>"}`(查询类失败以 `total: 0` 空集表达)。 +- 金额/重量:浮点数,成本保留 2 位小数,重量保留 3 位小数;日期 `YYYY-MM-DD`,月份 `YYYY-MM`。 + +--- + +## 3 工具详解 + +### 3.1 query_material_master — 查询物料主数据 + +**功能**:按物料编码精确查询,或按零件类别、材质牌号、压铸吨位组合过滤。返回物料档案(含体积、密度、净重、吨位、表面处理),是核价的起点数据。 + +**入参**(全部可选;全部不传 = 返回全表): + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `material_code` | string | 否 | — | 物料编码,精确匹配,如 `VHB-AT-001` | +| `part_category` | string | 否 | — | 零件类别,精确匹配,如 `阀体外壳` | +| `material_grade` | string | 否 | — | 客户材质牌号,精确匹配,如 `ADC12` | +| `die_casting_ton` | int | 否 | — | 压铸机吨位,精确匹配,如 `400` | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `total` | int | 命中条数 | +| `materials[]` | array | 物料行列表 | +| `materials[].material_code` | string | 物料编码(内部主键) | +| `materials[].part_name` | string | 零件名称 | +| `materials[].part_category` | string | 零件类别 | +| `materials[].material_grade` | string | 客户材质牌号 | +| `materials[].density` | decimal | 密度 g/cm³(JSON 中为字符串) | +| `materials[].volume_cc` | decimal | 零件体积 cm³(算铝水重量的依据) | +| `materials[].net_weight_kg` | decimal | 净重 kg(算后处理成本的依据) | +| `materials[].die_casting_ton` | int | 压铸机吨位 | +| `materials[].surface_treatment` | string | 表面处理(阳极氧化/钝化/无等) | +| `materials[].unit` | string | 计量单位 | +| `materials[].created_at` | string | 建档时间 | + +**返回示例**: + +```json +{ + "total": 1, + "materials": [ + { + "material_code": "VHB-AT-001", + "part_name": "变速箱阀体外壳", + "part_category": "阀体外壳", + "material_grade": "ADC12", + "density": "2.700", + "volume_cc": "444.44", + "net_weight_kg": "1.200", + "die_casting_ton": 400, + "surface_treatment": "阳极氧化", + "unit": "件", + "created_at": "2026-08-07 12:18:51.484993" + } + ] +} +``` + +--- + +### 3.2 match_material_grade — 材质牌号对照 + +**功能**:把客户指定牌号(GB/ASTM/JIS/EN/ISO 任一体系或内部码)对照为企业内部等效牌号,返回密度与铝锭参考价。支持模糊匹配与热处理后缀自动剥离(如 `A356-T6` → `A356`,最多剥 2 段)。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `customer_grade` | string | **是** | — | 客户牌号,大小写不敏感,模糊匹配六大码系 | +| `standard` | string | 否 | — | 标准体系提示(当前实现未参与过滤,保留参数) | + +**返回字段**(命中时): + +| 字段 | 类型 | 说明 | +|------|------|------| +| `matched` | bool | 是否命中 | +| `customer_grade` | string | 原客户牌号 | +| `internal_code` | string | 内部等效牌号,如 `AL-ADC12` | +| `density` | float | 密度 g/cm³ | +| `price_per_kg` | float | 铝锭参考价 ¥/kg(兜底价,核价优先取月度价) | +| `all_mappings[]` | array | 全部命中行(含 gb/astm/jis/en/iso/internal 六码、grade_group 材质族) | + +**未命中返回**: + +```json +{ "matched": false, "message": "未找到材质 AD12" } +``` + +**返回示例**(`ADC12`): + +```json +{ + "matched": true, + "customer_grade": "ADC12", + "internal_code": "AL-ADC12", + "density": 2.7, + "price_per_kg": 18.5, + "all_mappings": [ + { + "grade_group": "铝合金", + "gb_code": "ZAlSi10Cu(ADC12)", + "astm_code": "A380", + "jis_code": "ADC12", + "en_code": "EN AC-4600", + "iso_code": "AlSi9Cu3", + "internal_code": "AL-ADC12", + "density": "2.700", + "price_per_kg": "18.50" + } + ] +} +``` + +**业务规则**:未命中时 Skill 层应回退默认铝价并产生 warning(TC-06 场景),不应中断报价。 + +--- + +### 3.3 get_die_casting_params — 获取压铸参数 + +**功能**:按物料编码取压铸参数。核心输出:铝水重量(体积×密度×1.05 损耗)、压铸机吨位、模次节拍、单件压铸成本。体现 MVP 原则「**靠体积算铝水重量、压铸机吨位固定工时**」。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `material_code` | string | **是** | — | 物料编码 | +| `volume_cc` | float | 否 | 物料档案体积 | 覆盖体积(图纸实测体积优先于档案默认值) | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `material_code` | string | 物料编码 | +| `volume_cc` | float | 采用体积 cm³ | +| `density` | float | 密度 g/cm³ | +| `aluminum_weight_kg` | float | 铝水重量 = 体积×密度/1000×1.05,3 位小数 | +| `die_casting_ton` | int | 压铸机吨位 | +| `cycle_time_sec` | float | 模次节拍(秒/模,按吨位固化) | +| `machine_rate` | float | 机台费率 ¥/小时 | +| `mold_cavities` | int | 模穴数 | +| `casting_cost_per_part` | float | 单件压铸成本 = (节拍/3600)×费率/模穴数,2 位小数 | + +**失败返回**:`{"success": false, "message": "未找到物料 XXX"}` 或 `{"success": false, "message": "未找到 400T 压铸机参数"}` + +**返回示例**(VHB-AT-001,444.44cc,400T): + +```json +{ + "success": true, + "material_code": "VHB-AT-001", + "volume_cc": 444.44, + "density": 2.7, + "aluminum_weight_kg": 1.26, + "die_casting_ton": 400, + "cycle_time_sec": 30.0, + "machine_rate": 260.0, + "mold_cavities": 1, + "casting_cost_per_part": 2.17 +} +``` + +--- + +### 3.4 get_machining_estimate — 机加工工时估算 + +**功能**:按孔清单(钻孔/攻丝/铰孔)与铣面数量估算机加工总工时与刀具成本。所有规格节拍来自固化表 `machining_cycle`(工装节拍),逐项累加。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `material_code` | string | **是** | — | 物料编码(回显用) | +| `drill_holes` | array[object] | 否 | [] | 钻孔清单,元素 `{"spec": "Φ6", "count": 2}` | +| `tap_holes` | array[object] | 否 | [] | 攻丝清单,元素 `{"spec": "M6×1.0", "count": 2}` | +| `ream_holes` | array[object] | 否 | [] | 铰孔清单,元素 `{"spec": "Φ8H7", "count": 1}` | +| `mill_faces` | int | 否 | 0 | 铣面数量 | + +孔元素字段:`spec`(规格字符串,缺省 Φ8 / M8×1.25 / Φ8H7)、`count`(数量,缺省 1)。 + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `material_code` | string | 物料编码 | +| `total_time_sec` | float | 总工时(秒),2 位小数 | +| `total_time_hours` | float | 总工时(小时),4 位小数 | +| `total_tool_cost` | float | 刀具成本合计 ¥ | +| `details[]` | array | 逐项明细:`operation`(drill/tap/ream/mill)、`spec`、`count`、`total_time_sec` | + +**返回示例**(钻 Φ6×2 + 攻 M6×1.0×2 + 铣面×1): + +```json +{ + "success": true, + "material_code": "VHB-AT-001", + "total_time_sec": 26.0, + "total_time_hours": 0.0072, + "total_tool_cost": 1.5, + "details": [ + { "operation": "drill", "spec": "Φ6", "count": 2, "total_time_sec": 6.0 }, + { "operation": "tap", "spec": "M6×1.0", "count": 2, "total_time_sec": 8.0 }, + { "operation": "mill", "spec": "face", "count": 1, "total_time_sec": 12.0 } + ] +} +``` + +**注意**:固化表中不存在的规格**静默跳过**(不计工时也不报错);毛坯件(无孔无面)返回总工时 0,Skill 层应产生 warning(TC-07 场景)。 + +--- + +### 3.5 calculate_part_cost — 汇总核价(最终单价) + +**功能**:一站式核价。材料成本(铝水重量×当月铝价)+ 压铸成本 + 机加工成本 + 后处理成本 → 小计 × (1+管理费率) × (1+利润率) = 最终单价。铝价优先取 `material_price_history` 最新月度价并留痕月份;机加工费率按材质族(铝合金 60 / 灰铁 75 / 球铁 80 ¥/h)取 `machining_rate`。 + +**入参**: + +| 参数 | 类型 | 必填 | 默认 | 说明 | +|------|------|------|------|------| +| `material_code` | string | **是** | — | 物料编码 | +| `annual_qty` | int | **是** | — | 年用量(件/年) | +| `drill_holes` | array[object] | 否 | [] | 同 3.4 | +| `tap_holes` | array[object] | 否 | [] | 同 3.4 | +| `ream_holes` | array[object] | 否 | [] | 同 3.4 | +| `mill_faces` | int | 否 | 0 | 铣面数量 | +| `overhead_rate` | float | 否 | 0.12 | 管理费率 | +| `profit_rate` | float | 否 | 0.15 | 利润率 | + +**返回字段**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `success` | bool | 成功标记 | +| `material_code` / `part_name` | string | 物料编码 / 零件名称 | +| `cost_breakdown.material_cost` | float | 材料成本 = 铝水重量×铝价 | +| `cost_breakdown.aluminum_weight_kg` | float | 铝水重量(含 1.05 损耗) | +| `cost_breakdown.aluminum_price_per_kg` | float | 采用铝价 ¥/kg | +| `cost_breakdown.price_basis_month` | string | 铝价月份 `YYYY-MM`(留痕;无月度价时为 null) | +| `cost_breakdown.internal_code` | string | 内部牌号 | +| `cost_breakdown.grade_group` | string | 材质族(决定机加工费率) | +| `cost_breakdown.casting_cost` | float | 单件压铸成本 | +| `cost_breakdown.die_casting_ton` | int | 压铸机吨位 | +| `cost_breakdown.machining_cost` | float | 机加工成本 = 工时×费率 | +| `cost_breakdown.machining_hours` | float | 机加工总工时(小时) | +| `cost_breakdown.machining_rate_per_hour` | float | 机加工费率 ¥/h | +| `cost_breakdown.post_process_cost` | float | 后处理成本 = 净重×工艺费率 | +| `cost_breakdown.surface_treatment` | string | 表面处理工艺 | +| `total_cost` | float | 四项成本小计 | +| `overhead_rate` / `overhead` | float | 管理费率 / 管理费额 | +| `profit_rate` | float | 利润率 | +| `unit_price` | float | **最终单价** = (total_cost+overhead)×(1+profit_rate) | +| `currency` | string | 固定 `CNY` | + +**返回示例**(VHB-AT-001,30 万件/年,钻 Φ6×2/攻 M6×1.0×2/铰 Φ8H7×1/铣面×1): + +```json +{ + "success": true, + "material_code": "VHB-AT-001", + "part_name": "变速箱阀体外壳", + "cost_breakdown": { + "material_cost": 23.31, + "aluminum_weight_kg": 1.26, + "aluminum_price_per_kg": 18.5, + "price_basis_month": "2026-08", + "internal_code": "AL-ADC12", + "grade_group": "铝合金", + "casting_cost": 2.17, + "die_casting_ton": 400, + "machining_cost": 0.53, + "machining_hours": 0.0089, + "machining_rate_per_hour": 60.0, + "post_process_cost": 6.0, + "surface_treatment": "阳极氧化" + }, + "total_cost": 32.01, + "overhead_rate": 0.12, + "overhead": 3.84, + "profit_rate": 0.15, + "unit_price": 41.23, + "currency": "CNY" +} +``` + +**失败返回**:`{"success": false, "message": "未找到物料 XXX"}` + +--- + +## 4 附录 + +### 4.1 核心公式 + +``` +铝水重量(kg) = 体积(cm³) × 密度(g/cm³) / 1000 × 1.05(损耗系数) +材料成本 = 铝水重量 × 铝锭单价(当月月度价,缺省 18.5) +压铸成本 = (节拍秒 / 3600) × 机台费率 / 模穴数 ← 吨位固化节拍 +机加工成本 = Σ(工序固化节拍×数量) / 3600 × 材质族费率 +后处理成本 = 净重(kg) × 表面处理费率 +单价 = (四项成本小计 × (1+管理费率)) × (1+利润率) +``` + +### 4.2 依赖数据表 + +| 表 | 用途 | +|----|------| +| `material_master` | 物料主数据(体积/密度/吨位/后处理) | +| `material_grade_mapping` | 五标准体系牌号对照 + 材质族 + 参考铝价 | +| `material_price_history` | 月度铝价留痕(核价取最新月) | +| `die_casting_params` | 吨位 → 节拍/费率/模穴数 | +| `machining_cycle` | 工序规格固化节拍与刀具费率 | +| `machining_rate` | 材质族机加工费率(60/75/80 ¥/h) | +| `post_process_rate` | 表面处理费率(¥/kg) | + +### 4.3 与 CRM 服务的协作 + +ERP 服务只读不写。报价存档链路(create_inquiry → save_quotation → update_opportunity)由 `mcp-for-crm` 承担,见《MCP for CRM — 工具说明文档》。 diff --git a/mcp-for-erp/数据表与种子数据说明.md b/mcp-for-erp/数据表与种子数据说明.md new file mode 100644 index 0000000..c0fd211 --- /dev/null +++ b/mcp-for-erp/数据表与种子数据说明.md @@ -0,0 +1,239 @@ +# MCP for ERP — 数据表结构与种子数据说明文档 + +> 数据库:PostgreSQL 15+,库 `smart_quotation_auto`(与 CRM 服务共库不同表) +> 建表/种子脚本:`sql/init.sql`(v3,幂等可重跑);执行入口:`cd src && python3 seed.py` +> 版本:v1.0(2026-08-09) + +--- + +## 1 概览 + +| # | 表名 | 用途 | 主键 | 种子行数 | +|---|------|------|------|----------| +| 1 | `material_master` | 物料主数据(体积/密度/吨位/后处理) | material_code | 16 | +| 2 | `material_grade_mapping` | 五标准体系牌号对照 | id(SERIAL) | 13 | +| 3 | `die_casting_params` | 压铸参数(吨位→固化节拍/费率/模穴) | tonnage | 9 | +| 4 | `machining_cycle` | 机加工固化工装节拍 | id(SERIAL) | 26 | +| 5 | `post_process_rate` | 后处理费率(¥/kg) | id(SERIAL) | 10 | +| 6 | `material_price_history` | 月度材料价格留痕 | id(SERIAL) | 260(13 牌号×20 月) | +| 7 | `machining_rate` | 材质族机加工费率 | grade_group | 4 | + +**表关系**:`material_master.material_grade` →(经 match_material_grade 模糊对照)→ `material_grade_mapping`(internal_code/grade_group)→ `material_price_history`(按 internal_code 取最新月价)、`machining_rate`(按 grade_group 取费率);`material_master.die_casting_ton` → `die_casting_params.tonnage`;机加工工时查 `machining_cycle`(operation_type+spec);后处理查 `post_process_rate`(process_type = material_master.surface_treatment)。 + +--- + +## 2 表结构详解 + +### 2.1 material_master — 物料主数据 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `material_code` | VARCHAR(50) | PRIMARY KEY | 物料编码,如 `VHB-AT-001` | +| `part_name` | VARCHAR(200) | NOT NULL | 零件名称 | +| `part_category` | VARCHAR(50) | NOT NULL | 零件类别(种子全为「阀体外壳」) | +| `material_grade` | VARCHAR(50) | NOT NULL | 客户材质牌号(对照表输入) | +| `density` | DECIMAL(6,3) | NOT NULL DEFAULT 2.700 | 密度 g/cm³ | +| `volume_cc` | DECIMAL(10,2) | NOT NULL | 零件体积 cm³(**算铝水重量的依据**) | +| `net_weight_kg` | DECIMAL(8,3) | NOT NULL | 净重 kg(**算后处理成本的依据**) | +| `die_casting_ton` | INTEGER | NOT NULL | 压铸机吨位(关联 die_casting_params) | +| `surface_treatment` | VARCHAR(50) | — | 表面处理(关联 post_process_rate) | +| `unit` | VARCHAR(10) | DEFAULT '件' | 计量单位 | +| `created_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 建档时间 | + +### 2.2 material_grade_mapping — 材质牌号对照表 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `grade_group` | VARCHAR(50) | NOT NULL | 材质族:铝合金/铸铝/铸铁/球墨铸铁(决定机加工费率) | +| `gb_code` | VARCHAR(80) | — | GB 牌号 | +| `astm_code` | VARCHAR(80) | — | ASTM 牌号 | +| `jis_code` | VARCHAR(80) | — | JIS 牌号 | +| `en_code` | VARCHAR(80) | — | EN 牌号 | +| `iso_code` | VARCHAR(80) | — | ISO 牌号 | +| `internal_code` | VARCHAR(50) | NOT NULL,UNIQUE(uq_grade_mapping) | 内部等效牌号 | +| `density` | DECIMAL(6,3) | NOT NULL | 密度 g/cm³ | +| `price_per_kg` | DECIMAL(10,2) | NOT NULL | 铝锭/材料参考价 ¥/kg(兜底价) | + +> 对照匹配对六列码值做大小写不敏感 LIKE 模糊匹配;带热处理后缀(如 A356-T6)自动剥后缀重试。 + +### 2.3 die_casting_params — 压铸参数表 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `tonnage` | INTEGER | PRIMARY KEY | 压铸机吨位 | +| `shot_weight_kg` | DECIMAL(8,3) | NOT NULL | 射出重量 kg(参考值) | +| `cycle_time_sec` | DECIMAL(8,2) | NOT NULL | 模次节拍 秒/模(**吨位固化**) | +| `machine_rate` | DECIMAL(10,2) | NOT NULL | 机台费率 ¥/小时 | +| `mold_cavities` | INTEGER | DEFAULT 1 | 模穴数(压铸成本按穴分摊) | + +### 2.4 machining_cycle — 机加工节拍表(固化工装节拍) + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `operation_type` | VARCHAR(30) | NOT NULL | drill 钻孔 / tap 攻丝 / ream 铰孔 / mill 铣面 | +| `spec` | VARCHAR(50) | NOT NULL | 规格:Φ6、M6×1.0、Φ8H7、face;UNIQUE(operation_type, spec) | +| `cycle_time_sec` | DECIMAL(8,2) | NOT NULL | 单件固化节拍(秒) | +| `tool_rate` | DECIMAL(8,4) | DEFAULT 0 | 单件刀具成本 ¥ | +| `notes` | VARCHAR(200) | — | 备注(通孔/盲孔等) | + +### 2.5 post_process_rate — 后处理费率表 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `process_type` | VARCHAR(50) | NOT NULL,UNIQUE(uq_post_process) | 工艺名(与物料 surface_treatment 对应) | +| `unit` | VARCHAR(10) | DEFAULT 'kg' | 计价单位 | +| `rate` | DECIMAL(10,2) | NOT NULL | 费率 ¥/kg | +| `currency` | VARCHAR(10) | DEFAULT 'CNY' | 币种 | + +### 2.6 material_price_history — 材料价格历史表 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `id` | SERIAL | PRIMARY KEY | 自增主键 | +| `internal_code` | VARCHAR(50) | NOT NULL | 内部牌号;UNIQUE(internal_code, price_date) | +| `price_date` | DATE | NOT NULL | 价格月份(每月 1 日) | +| `price_per_kg` | DECIMAL(10,2) | NOT NULL | 当月参考价 ¥/kg | +| `source` | VARCHAR(100) | DEFAULT '市场参考价' | 数据来源留痕 | + +> 核价取法:`ORDER BY price_date DESC LIMIT 1`(最新月),并在返回中带 `price_basis_month` 留痕。 + +### 2.7 machining_rate — 机加工费率表 + +| 列 | 类型 | 约束/默认 | 说明 | +|----|------|-----------|------| +| `grade_group` | VARCHAR(50) | PRIMARY KEY | 材质族 | +| `rate_per_hour` | DECIMAL(10,2) | NOT NULL | 费率 ¥/小时 | +| `notes` | VARCHAR(200) | — | 备注 | + +--- + +## 3 种子数据详解 + +### 3.1 material_master — 16 种阀体外壳 + +覆盖设计:铝合金 12 / 灰铸铁 2(HT250、HT300)/ 球墨铸铁 1(QT500-7)/ 铸铝 1(ZL101);吨位 160~1000T 全谱系;体积 96.3~1037 cm³。 + +| 物料编码 | 零件名称 | 牌号 | 密度 | 体积cm³ | 净重kg | 吨位 | 后处理 | +|----------|----------|------|------|---------|--------|------|--------| +| VHB-AT-001 | 变速箱阀体外壳 | ADC12 | 2.700 | 444.44 | 1.200 | 400 | 阳极氧化 | +| VHB-BK-002 | 制动阀体壳 | HT250 | 7.200 | 388.89 | 2.800 | 630 | 电泳涂装 | +| VHB-SP-003 | 转向器阀体壳 | A356-T6 | 2.650 | 296.30 | 0.785 | 280 | 钝化 | +| VHB-EG-004 | 发动机油路阀体壳 | EN AC-4600 | 2.700 | 555.56 | 1.500 | 400 | 阳极氧化 | +| VHB-CL-005 | 离合器控制阀体壳 | ADC10 | 2.700 | 480.00 | 1.300 | 400 | 阳极氧化 | +| VHB-TR-006 | 变速箱液压控制阀体 | AlSi9Cu3 | 2.700 | 620.00 | 1.670 | 630 | 电泳涂装 | +| VHB-EB-007 | 电子制动阀体壳 | AlSi10MnMg | 2.650 | 350.00 | 0.930 | 400 | 钝化 | +| VHB-QT-008 | 悬架阻尼阀体壳 | QT500-7 | 7.100 | 520.00 | 3.690 | 630 | 喷涂 | +| VHB-MN-009 | 微型泵阀体壳 | ADC12 | 2.700 | 96.30 | 0.260 | 160 | 钝化 | +| VHB-LG-010 | 大型主阀体壳 | ADC12 | 2.700 | 1037.00 | 2.800 | 800 | 电泳涂装 | +| VHB-TC-011 | 涡轮增压控制阀体壳 | ADC12 | 2.700 | 320.00 | 0.860 | 350 | 钝化 | +| VHB-EG-012 | EGR阀体壳 | ADC10 | 2.700 | 265.00 | 0.720 | 280 | 钝化 | +| VHB-WP-013 | 水泵阀体壳 | AlSi12 | 2.650 | 380.00 | 1.010 | 400 | 阳极氧化 | +| VHB-TM-014 | 热管理阀体壳(新能源) | AlSi10MnMg | 2.650 | 505.00 | 1.340 | 500 | 钝化 | +| VHB-OP-015 | 机油泵阀体壳 | ZL101 | 2.650 | 210.00 | 0.560 | 200 | 钝化 | +| VHB-HD-016 | 重卡制动阀体壳 | HT300 | 7.250 | 850.00 | 6.160 | 1000 | 达克罗 | + +### 3.2 material_grade_mapping — 13 条牌号对照 + +覆盖设计:铝合金 7 / 铸铝 1 / 铸铁 3 / 球墨铸铁 2;每条六码齐全(GB/ASTM/JIS/EN/ISO/内部码)。 + +| 材质族 | GB | ASTM | JIS | EN | ISO | 内部码 | 密度 | 参考价¥/kg | +|--------|----|------|-----|----|----|--------|------|-----------| +| 铝合金 | ZAlSi10Cu(ADC12) | A380 | ADC12 | EN AC-4600 | AlSi9Cu3 | AL-ADC12 | 2.700 | 18.50 | +| 铝合金 | ZAlSi7Mg(A356) | A356 | AC4C-T6 | EN AC-4210 | AlSi7Mg | AL-A356 | 2.650 | 20.00 | +| 铝合金 | ZAlSi9Cu(AlSi9Cu) | B443 | AC4B | EN AC-4600 | AlSi9Cu | AL-4600 | 2.700 | 18.00 | +| 铸铝 | ZL101 | 356.0 | AC4C | EN AC-4200 | AlSi7 | AL-ZL101 | 2.650 | 19.00 | +| 铸铁 | HT250 | G2500 | FC250 | EN-GJL-250 | GJL-250 | IR-HT250 | 7.200 | 6.50 | +| 球墨铸铁 | QT500-7 | D5007 | FCD500 | EN-GJS-500 | GJS-500 | IR-QT500 | 7.100 | 7.20 | +| 铝合金 | ZAlSi8Cu(ADC10) | A384 | ADC10 | EN AC-4650 | AlSi8Cu | AL-ADC10 | 2.700 | 18.20 | +| 铝合金 | ZAlSi10MnMg | — | — | AlSi10MnMg | AlSi10MnMg | AL-S10MM | 2.650 | 22.00 | +| 铝合金 | ZAlSi12 | 413.0 | AC3A | EN AC-4420 | AlSi12 | AL-S12 | 2.650 | 17.80 | +| 铸铁 | HT200 | G3000 | FC200 | EN-GJL-200 | GJL-200 | IR-HT200 | 7.200 | 6.00 | +| 铸铁 | HT300 | G3500 | FC300 | EN-GJL-300 | GJL-300 | IR-HT300 | 7.250 | 6.80 | +| 球墨铸铁 | QT450-10 | D4510 | FCD450 | EN-GJS-450 | GJS-450 | IR-QT450 | 7.100 | 7.00 | +| 球墨铸铁 | QT600-3 | D6003 | FCD600 | EN-GJS-600 | GJS-600 | IR-QT600 | 7.100 | 7.50 | + +> 脚本含修正语句:`IR-QT%` 组别归位「球墨铸铁」(历史数据曾误归「铸铁」)。 + +### 3.3 die_casting_params — 9 档吨位 + +| 吨位 | 射出重量kg | 节拍秒/模 | 机台费率¥/h | 模穴数 | +|------|-----------|-----------|-------------|--------| +| 160 | 0.50 | 15.00 | 120.00 | **2**(一模两腔) | +| 200 | 0.80 | 18.00 | 140.00 | **2** | +| 280 | 1.20 | 22.00 | 180.00 | 1 | +| 350 | 1.80 | 26.00 | 220.00 | 1 | +| 400 | 2.50 | 30.00 | 260.00 | 1 | +| 500 | 3.20 | 36.00 | 320.00 | 1 | +| 630 | 4.00 | 42.00 | 380.00 | 1 | +| 800 | 6.50 | 55.00 | 520.00 | 1 | +| 1000 | 8.50 | 65.00 | 650.00 | 1 | + +> **故意不含 1250T**:用于回退测试(未命中吨位场景)。 + +### 3.4 machining_cycle — 26 条固化节拍 + +| 工序 | 规格谱系 | 条数 | 节拍范围(秒) | 刀具费范围(¥) | +|------|----------|------|--------------|----------------| +| drill 钻孔 | Φ3 / Φ4 / Φ5 / Φ6 / Φ8 / Φ10 / Φ12 / Φ14 / Φ16 / Φ20 / Φ24 | 11 | 2.00~12.00 | 0.10~0.60 | +| tap 攻丝 | M4×0.7 / M5×0.8 / M6×1.0 / M8×1.25 / M10×1.5 / M12×1.75 / M14×2.0 / M16×2.0 | 8 | 3.00~9.00 | 0.15~0.45 | +| ream 铰孔 | Φ6H7 / Φ8H7 / Φ10H7 / Φ12H7 / Φ16H7 / Φ20H7 | 6 | 5.00~12.00 | 0.45~1.10 | +| mill 铣面 | face(单面) | 1 | 12.00 | 0.80 | + +> **故意不含 Φ25 钻孔 / M18 攻丝**:用于回退测试(未知规格静默跳过场景)。 + +### 3.5 post_process_rate — 10 种后处理 + +| 工艺 | 费率¥/kg | 工艺 | 费率¥/kg | +|------|----------|------|----------| +| 喷砂 | 1.50 | 粉末喷涂 | 3.00 | +| 钝化 | 2.00 | 磷化 | 1.80 | +| 阳极氧化 | 5.00 | 达克罗 | 4.00 | +| 电泳涂装 | 3.50 | 硬质阳极氧化 | 8.00 | +| 喷涂 | 2.50 | 化学镀镍 | 9.00 | + +> **故意不含「镀铬」**:用于回退测试(未命中后处理场景,成本计 0)。 + +### 3.6 material_price_history — 260 行月度价格(2025-01 ~ 2026-08) + +生成规则(脚本内 CTE 批量生成,非手工录入): + +- 13 个内部牌号 × 20 个月 = 260 行,`source='市场参考价'`。 +- 每月价格 = 对照表参考价 × 月份系数;铝系用 `al_factor`(0.92~1.02 波动),铁系(铸铁/球墨铸铁)用 `ir_factor`(0.95~1.01 波动)。 +- **最新月(2026-08)系数 = 1.00**,即与对照表 `price_per_kg` 完全一致——保证核价结果与静态对照表口径可对账(如 AL-ADC12 当月价 18.50)。 + +### 3.7 machining_rate — 4 条材质族费率 + +| 材质族 | 费率¥/h | 备注 | +|--------|---------|------| +| 铝合金 | 60.00 | 常规机加工费率 | +| 铸铝 | 60.00 | 同铝合金 | +| 铸铁 | 75.00 | 刀具损耗高,费率上浮 | +| 球墨铸铁 | 80.00 | 强度高,费率最高 | + +--- + +## 4 幂等性与重跑机制 + +`init.sql` 整体可重复执行,无副作用(NF-3): + +1. **建表**:`CREATE TABLE IF NOT EXISTS`。 +2. **历史去重**:对 SERIAL 表先按业务键删除重复行(保留最小 id)。 +3. **补唯一约束**:`DO $$ ... ADD CONSTRAINT ... EXCEPTION WHEN duplicate_table` 幂等加约束(uq_machining_cycle / uq_grade_mapping / uq_post_process)。 +4. **种子写入**:全部 `ON CONFLICT (...) DO NOTHING`。 +5. **月度价格**:`UNIQUE(internal_code, price_date)` + `ON CONFLICT DO NOTHING`,重跑不翻倍。 + +--- + +## 5 数据维护约定 + +| 场景 | 操作 | 约束 | +|------|------|------| +| 月度铝价更新 | `material_price_history` 追加新月行 | 核价自动取最新月并留痕 `price_basis_month` | +| 新增机加工规格 | `machining_cycle` 增行 | 不改旧行(历史报价可复算) | +| 新增吨位 | `die_casting_params` 增行 | 同上 | +| 新增物料 | `material_master` 增行 | material_grade 须能命中对照表,否则走回退 | + +> ERP 侧全部为只读基础数据:无 MCP 写工具,数据变更仅通过 SQL/脚本进行。