Files
menchuang/doc/API.md
T

562 lines
45 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API 接口文档
> 本文档记录自研系统的接口规范,并对照目标站已验证接口。目标站基址:`https://zzc-serv.thinkerx.com/api/`(仅作参照)。
> 自研系统基址:`/api/`(FastAPI 挂载)。
> 认证方式:`Authorization: Bearer <JWT>`(HS256,24h);`X-Requested-With: XMLHttpRequest`;`Accept: application/json`。
## 0. 统一响应规范
```json
{ "code": 1, "message": "获取成功", "data": {}, "other_data": "", "other_data2": "" }
```
- `code=1` 成功,其他为失败;`message` 为用户可读文案。
- **`code=10086` 业务付费墙**:模块未购买/已到期,`message` 为"购买后解锁此功能,点击此处去购买";前端跳转商城。自研系统对应"套餐权限校验中间件"。
- 列表分页:`data: { current_page, data: [], total, per_page, last_page, next_page_url, ... }`。
- 业务错误:HTTP 200 + code≠1;认证失败:HTTP 401;参数缺失:HTTP 200 + code=0(message 提示)或 422。
## 1. 认证模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 手机验证码登录 | POST | `/api/wechat-login-with-phone` | `{phone_number, code, ...}` | `{token, user}`(原站登录入口,JWT iss 即此) |
| 账号密码登录 | POST | `/api/login` | `{phone_number, password}` | `{token, ...}` |
| 验证码登录 | POST | `/api/code-login` | `{phone_number, code}` | `{token, ...}` |
| 发送验证码 | POST | `/api/verification-code-new` | `{phone_number, scene}` | `{code:1}` |
| 图形验证码 | GET | `/api/captcha` | — | 图形/`{code:1,data}` |
| 注册并登录 | POST | `/api/register-and-login` | `{phone_number, password, code}` | `{token, ...}` |
| 忘记密码 | POST | `/api/forget-password` | `{phone_number, code, password}` | `{code:1}` |
| 微信登录 | POST | `/api/wechat-login` | 微信 OAuth code | `{token, ...}` |
## 2. 用户模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例(已验证 ✅ 节选) |
|---|---|---|---|---|
| 当前用户信息 | GET | `/api/current-user` | — | ✅ `{"id":3562856,"nickname":"...","phone_number":"...","company_id":1,"money":"0.00","try_expiration_time":"2026-09-23 00:00:00","son_account_type":1,"language":"zh-CN",...}` |
| 子账户列表 | GET | `/api/users?page=1` | `page` | ✅ Laravel 分页,`data.data[]` |
| 邀请用户列表 | GET | `/api/invited_user_list?page=1` | `page` | ✅ 分页 |
| 修改密码 | POST | `/api/current-user/password` | `{old,new}` | ⏳ |
| 绑定微信 | POST | `/api/bind-wechat` | `code` | ⏳ |
| 解绑微信 | POST | `/api/unbind-wechat` | — | ⏳ |
## 3. 样板库模块(已全量采集 ✅)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 窗型分类树 | GET | `/api/default_windoor_catelog` | — | ✅ 树:`[{id:1,name:"推拉门",children:[{id:3,name:"平开"},...]}]` |
| 搜索官方模板 | GET | `/api/search_default_windoor` | `name`(可选,按名称过滤) | ✅ 无分页全量返回:`data.data: [{id,name,pic_url,catelog_id,note,times}]`,共 254 条 |
| 模板绘制详情 | GET | `/api/default_windoor_info/{id}` | 路径 `id` | ✅ `{id,drawing_data(编辑器画布JSON),pic_url,script_bar,script_glass,low/high_width,low/high_height,...}` |
| 用户自存窗型列表 | GET | `/api/windoor/` | `page` | ⏳(需服务端排序参数,待抓包) |
| 用户窗型详情 | GET | `/api/windoor/{id}` | 路径 `id` | ⏳(同 drawing_data 结构) |
| 模板排序 | POST | `/api/windoor_template_sort` | `{ids:[...]}` | ⏳ |
| **保存自定义样板** | POST | `/api/windoor/templates` | `{name, drawing_data, catelog_id?, note?}` | ✅ v0.4 新增:编辑器"另存为我的样板"(is_default=0,企业隔离) |
`drawing_data` 结构参见《克隆实施方案.md》§2.2。
## 4. 客户模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 客户列表 | GET | `/api/customers_list` | — | ✅ `[{id,name,phone,address,note,sort,type,salesmen,discount,balance,level,join_date}]` |
| 客户列表(分页) | GET | `/api/customers` | `page,keywords` | ⏳ |
| 新增/编辑客户 | POST | `/api/customers` | `{name,phone,address,...}` | ⏳ |
| 客户跟进记录 | GET | `/api/customer_records/` | `customer_id` | ⏳ |
| 客户导入 | POST | `/api/customers_excel_in` | Excel 文件 | ⏳ |
## 5. 合同/订单模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 合同列表 | GET | `/api/contracts` | — | ✅ `{"contracts":{current_page,data:[{id,contract_no:"FG20260818009",customer,area,count,money,status:"意向单",cut_status,sign_status,contract_type,confirmed,discount,oids[],agency_money}]}}` |
| 意向单转合同 | POST | `/api/convert-contract/` | `{contract_id,...}` | ⏳ |
| **订单列表/搜索** | POST | `/api/orders/` | `{page:1, ...筛选条件}` | ✅ 已验证(付费墙):`{"code":"10086","message":"购买后解锁此功能,点击此处去购买","data":""}`;已购账号返回 Laravel 分页订单列表 |
| **创建订单(合同+图纸)** | POST | `/api/orders/` | ✅ 源码确认:`{contract_id, drawing_data(shapeManager序列化), pic_url(缩略图)}` | ⏳ |
| 订单详情 | GET | `/api/orders/{id}` | 路径 `id` | ✅ 路由确认(源码),返回 `data.order` |
| 订单排序 | PUT | `/api/orders/{id}` | `{sort:n}` | ✅ 路由确认(源码) |
| 删除订单 | DELETE | `/api/orders/{id}` | 路径 `id`(支持批量 Promise.allSettled) | ✅ 路由确认(源码) |
| 订单详情(按pid) | POST | `/api/order-by-pid` | `{pid}` | ✅ 已验证(付费墙 code=10086) |
| 订单进度 | POST | `/api/get_order_process` | `{order_id}` | ✅ 已验证 code=1 |
| 订单进度列表 | GET | `/api/get_order_process(_list)` | — | ✅ GET code=1 |
| 订单日志 | GET | `/api/order_up_logs` | `订单id`(缺失时提示"订单id 必须") | ✅ 参数契约确认 |
| 阳光房订单 | POST | `/api/sunny_house_order/` | `{page:1,...}` | ✅ 已验证(付费墙 code=10086) |
| 售后单 | POST | `/api/after_sales_orders/` | `{问题描述...}` | ✅ 已验证(校验"请填写问题描述") |
| 重复订单校验 | POST | `/api/is-repeat-order` | 查询条件 | ✅ 已验证 code=1 |
| 订单状态(管家婆) | POST | `/api/cj-orders-status` | 需参数(当前500) | ⏳ |
| 合同确认 | POST | `/api/up-contract-confirmed/` | `{contract_id}` | ⏳ |
| 电子签名上传 | POST | `/api/update-contract-sign-status` | `{contract_id, sign_picture}` | ⏳ |
| 收款记录 | GET | `/api/contract_receipt_list/` | `contract_id` | ✅ GET code=1 |
| 收款统计 | GET | `/api/contract_receipt_statistics` | — | ✅ GET code=1 |
| 收款统计(按年) | GET | `/api/contract_receipt_statistics_tb` | `年份`(缺失提示"请选择年份") | ✅ 参数契约确认 |
**订单写入流完整契约(自研对齐)**:`画图(shapeManager.serialize) → POST windoor_default 保存模板 或 POST orders/{contract_id, drawing_data, pic_url} 创建订单 → GET orders/{id} 详情回显 → PUT orders/{id} 排序 → DELETE orders/{id} 删除`
## 6. 产品/价格模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 产品分类树 | GET | `/api/product-catelog` | — | ✅ 树(is_reference、lang、children) |
| 全部产品 | GET | `/api/all-product` | — | ✅ `data:[]`(当前账户为空) |
| 产品详情 | GET | `/api/product/{id}` | 路径 `id` | ⏳ |
| 产品搜索 | GET | `/api/search-product` | `keywords` | ⏳ |
| 团队折扣 | GET | `/api/team-discount` | — | ⏳ |
| 报价单 | GET | `/api/quotations?page=1` | `page` | ✅ 分页(当前账户为空) |
## 7. 仓库/库存模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 材料库存列表 | GET | `/api/warehouse_list` | 分页参数 | ✅ `[{id,bom_code,name,material:"profile",cost,price,color,pricing_method,length,count,warning,unit,meter_weight,thick,agency_price}]` |
| 库存分类 | GET | `/api/warehouse_catelogs` | — | ⏳ |
| 收款账户 | GET | `/api/receipt_accounts` | — | ✅ `[{id,type,name,number,bank,pic_url,is_default,balance}]` |
| 材料入库 | POST | `/api/product_move_in/` | `{...}` | ⏳ |
| 材料出库 | POST | `/api/product_move_out/` | `{...}` | ⏳ |
| 配件配置 | GET | `/api/materials_config` | — | ⏳ |
## 8. 优化下料/生产模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 一维优化 | POST | `/api/optimize1d` | `{bars:[], stock:[]}` | ⏳ |
| 切割方案 | GET | `/api/cut_solution(_new)/{contract_id}` | 路径 | ⏳ |
| 合同型材清单 | GET | `/api/contract_bars(_with_stock)` | `contract_id` | ⏳ |
| 更新切割状态 | POST | `/api/up-cut-status` | `{...}` | ⏳ |
| 订单打印模板 | GET | `/api/order_format/` | — | ⏳ |
## 9. 渲染/3D 模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 渲染信息 | GET | `/api/rendering-info` | — | ⏳ |
| 3D 数据 | POST | `/api/three-d-data` | `{drawing_data}` | ⏳ |
| 按订单取绘图 | GET | `/api/drawing_data_by_oid/{oid}` | 路径 `oid` | ⏳ |
| 照片生成窗型(AI) | POST | `/api/sketch-to-window-info` | 图片 | ⏳ |
## 10. 平台模块
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 公告列表 | GET | `/api/announcements` | — | ✅ `[{id,name,desc,pic_url,content(富文本)}]` |
| 商城模块配置 | GET | `/api/company-module-info` | — | ✅ 25 个模块(名称/图标/描述/价格/试用天数) |
| 帮助文档菜单 | GET | `/api/get-showdoc-menu` | — | ✅ showdoc 目录结构 |
| 操作日志 | GET | `/api/operation_logs?page=1` | `page` | ✅ 分页 |
| 业务员列表 | GET | `/api/salesmen` | — | ✅(当前账户为空) |
| 反馈 | POST | `/api/feedback` | `{content}` | ⏳ |
> 标注说明:✅ = 已用只读请求实测并留存样本于 `data/api-samples/`;⏳ = 写入/低频接口未对生产系统探测,自研时按功能语义实现,或抓包后补全。
## 11. 整站全量采集结果(2026-09-20)
原始留档:`data/full-site/`(`get/` 279 个接口响应、`post/` 24 个 POST 响应、`get-manifest.json`、`post-manifest.json`、采集脚本 `sweep_get.py`/`sweep_post.py`)。
### 11.1 GET 扫描(279 个接口)
| 结果 | 数量 | 说明 |
|---|---|---|
| HTTP 200 | 90 | 其中 code=1 业务成功 **76 个** |
| HTTP 405 | 110 | POST/PUT/DELETE-only 接口(已列入 ⏳ 并按语义实现) |
| HTTP 404 | 65 | 需路径参数(如 `{id}` 后缀类) |
| HTTP 500 | 14 | 缺必填参数触发(服务端未做输入校验,自研须避免) |
参数契约(由失败提示反推):`order_up_logs`←订单id;`script/`←系列id;`pay-page`←类型;`contract_receipt_statistics_tb`←年份;`ywy-ykt-user`←手机号;`three-d-data`(POST)←order_id。
### 11.2 POST 安全探测(24 个,白名单制)
- 付费墙确认(code=10086):`orders/`、`sunny_house_order/`、`order-by-pid`、`offer-price(/)`、`product_by_warehouse`、`order_format_by_warehouse`、`ai_product`
- 成功(code=1):`get_order_process`、`is-repeat-order`、`xf_contract_workshop`
- 参数提示:`three-d-data`←必须 order_id;`after_sales_orders/`←问题描述
- 未探测(危险类,永不盲发):支付(wechat_pay*、withdraw)、短信(send-bind-code、verification-code-new)、账号绑定(bind-*、login)、删除/更新类(del_*、void_*、up_*、*_sort)、文件上传(upload*)
### 11.3 源码静态提取的写入契约
| 流程 | 方法+路径 | 请求体 |
|---|---|---|
| 创建订单 | POST `orders/` | `{contract_id, drawing_data, pic_url}` |
| 保存官方模板 | POST `windoor_default` | `{drawing_data: shapeManager.serialize(true), ...}` |
| 修改系列脚本 | PUT `script/{id}` | `{drawing_data: shapeManager.serialize(...)}` |
| 模板排序 | POST `windoor_template_sort` | `{[模板id]: 序号, ...}` |
| 删除订单 | DELETE `orders/{id}` | 路径参数,前端支持批量 |
| 订单排序 | PUT `orders/{id}` | `{sort}` |
### 11.4 权限/付费墙语义(自研须实现)
- 试账户可用模块:样板库、客户、合同、仓库、收款账户、公告、报表统计、帮助文档
- 付费墙模块(code=10086):订单全流程、报价(offer-price)、产品库、订单打印模板、AI 产品
- 到期语义:`pay-page-list` 返回 code=0 "您的账号已经到期,请联系销售人员!";`try_render` 返回 "体验版无法体验实景渲染"
- 角色权限:`contract-stat` 返回 code=210 "该账号没有该权限"
## 12. 自研系统接口规范(已实现 v0.1)
> 自研基址 `http://localhost:8000/api/`,envelope 与分页结构完全对齐 §0。认证:`Authorization: Bearer <JWT>`(HS256/24h)。
> 分层:`api/`(Controller) → `services/`(业务) → `repositories/`(数据) → `models/`(ORM)。
### 12.1 认证模块 `/api/auth`
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 登录 | POST | `/api/auth/login` | `{"phone_number":"13800000000","password":"123456"}` | `{"code":1,"data":{"token":"...","expires_hours":24,"user":{...}}}` |
| 注册 | POST | `/api/auth/register` | `{"phone_number","password"}` | `{"code":1,"data":{"token","user_id"}}` |
| 当前用户 | GET | `/api/auth/current-user` | Bearer token | `{"code":1,"data":{"id,nickname,phone_number,company_id,language,try_expiration_time}}` |
### 12.2 订单模块 `/api/orders`(本节核心,自研设计)
**订单状态机**:`0意向单 → 1正式单 → 2生产中 → 3已完工`;任意未完工状态可 `4已取消`(终态)。非法迁移返回 code=0。
**单号规则**:`DO + YYYYMMDD + 4位流水`(同企业同日递增),例如 `DO202609200001`。
**创建订单后自动初始化生产进度节点**:下料/组装/玻璃/打胶/五金/质检/完工(7 节点,与目标站 get_order_process 语义对齐)。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| **创建订单** | POST | `/api/orders` | `{"contract_id":1,"drawing_data":"{画布JSON}","pic_url":"/static/windoor-images/1.png","customer_id":1,"money":0,"count":0,"area":0,"note":"","status":0}` | `{"code":1,"message":"创建成功","data":{"id":1,"order_no":"DO202609200001","status":"意向单",...}}` |
| 订单搜索 | POST | `/api/orders/search` | `{"page":1,"page_size":15,"keyword":"","status":null,"contract_id":null,"start_date":"","end_date":"","order_by":"created_desc"}` | `{"code":1,"data":{"current_page":1,"data":[...],"total":0,"per_page":15}}` |
| 订单列表 | GET | `/api/orders?page=1&page_size=15` | query | 同上分页结构 |
| **订单详情** | GET | `/api/orders/{id}` | 路径 | `data` 含 `drawing_data`(画布 openFile 直接渲染)与 `processes[7]` |
| 更新订单 | PUT | `/api/orders/{id}` | 基础字段任意子集;`{"action":"change_status","target_status":1}` 执行状态迁移 | `{"code":1,"data":{...}}`;非法迁移 `{"code":0,"message":"不允许的状态迁移: 意向单→已完工"}` |
| 删除订单 | DELETE | `/api/orders/{id}` | 路径 | `{"code":1,"message":"删除成功"}` |
| **更新生产进度** | POST | `/api/orders/{id}/process` | `{"name":"下料","status":2}` (status: 0未开始/1进行中/2完成,自动记 finish_time) | `{"code":1,"data":{"id,"name","status","finish_time"}}` |
| 订单操作日志 | GET | `/api/orders/{id}/logs` | 路径 | `{"code":1,"data":[{"action":"create","detail":"创建订单 DO202609200001","created_at":...}]}` |
### 12.3 样板库模块 `/api/windoor`
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 分类树 | GET | `/api/windoor/catelogs` | Bearer | 树结构同目标站 `default_windoor_catelog` |
| 模板搜索 | GET | `/api/windoor/templates?name=&catelog_id=&page=1&page_size=50` | query | 列表含 `pic_url`(本地平面图 `/static/windoor-images/{id}.png`),不含大字段 |
| 模板详情 | GET | `/api/windoor/templates/{id}` | 路径 | `data.drawing_data` — 与采集的 254 套样本同构,画布可直接渲染 |
### 12.4 系统模块
| 接口名称 | 方式 | URL 路径 | 说明 |
|---|---|---|---|
| 健康检查 | GET | `/api/health` | `{"code":1,"data":{"status":"up"}}` |
| 接口文档 | GET | `/docs` | FastAPI 自动生成 OpenAPI |
| 平面图静态 | GET | `/static/windoor-images/{id}.png` | 254 张采集平面图(开发参照,上线前须替换为自绘素材) |
### 12.5 客户模块 `/api/customers`(v0.2 新增)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 新增客户 | POST | `/api/customers` | `{name(必填), phone, address, note, salesmen}` | |
| 客户列表 | GET | `/api/customers?page=1&page_size=15&keyword=` | query | 模糊匹配姓名/电话/地址 |
| 编辑客户 | PUT | `/api/customers/{id}` | 同新增 | |
| 删除客户 | DELETE | `/api/customers/{id}` | 路径 | |
### 12.6 合同模块 `/api/contracts`(v0.2 新增)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 创建合同 | POST | `/api/contracts` | `{customer_id, customer_name, phone, address, area, count, money, note}` | 合同号 `FG+YYYYMMDD+3位流水`,默认意向单 |
| 合同列表 | GET | `/api/contracts?page=1&keyword=&status=` | query | 响应 `data.contracts` 分页(对齐目标站结构) |
| 编辑合同 | PUT | `/api/contracts/{id}` | 同创建 | |
| **意向单转正式** | POST | `/api/contracts/convert` | `{contract_id}` | 仅意向单可转,重复转换 code=0 |
| 删除合同 | DELETE | `/api/contracts/{id}` | 路径 | |
**合同→订单联动**:创建订单传 `contract_id` 时,后端自动快照合同的客户名/电话/地址到订单(客户资料后续变更不影响订单)。
### 12.7 优化下料 `/api/optimize1d`(v0.2 新增,纯计算无状态)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 一维优化 | POST | `/api/optimize1d` | `{demands:[{length,qty,label}], stocks:[{length,qty,label}], kerf:5}` | FFD 首递减装箱;返回 `plans[]`(每根原料的切割组合+余料)、`unplaced[]`、`stats.utilization`;需求>库存时 code=0 |
### 12.8 报价算料 `/api/offer-price`(v0.3 新增,v2.42 增加系列选材)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 算料报价 | POST | `/api/offer-price` | `{drawing_data \| template_id \| order_id(三选一), prices?, series_id?}` | 从 drawing_data 几何提取:外框/中梃/扇框型材清单(工程近似)+ 玻璃面积(鞋带公式)+ 五金,套价格本计价。默认价: 型材40-50元/米、玻璃120元/㎡、五金180元/扇;`prices` 可覆盖 |
| **系列选材** | (series_id/materials 参数) | — | `series_id`(成本系列模板 id,见 12.27);`materials` 部位级材料覆盖 `{frame,mullion,sash,glass,hardware: {name,unit_price,weight_per_m}}` | 传入后响应附加 `series`(系列名/壁厚)与 `series_estimate`(按图纸实际尺寸的系列成本估算: total/per_sqm/分区成本/margin_ladder 毛利阶梯/overrides 回显)——**对应部位使用对应材料**: 商家可为外框/中梃/扇框/玻璃/五金分别选材(可跨系列混搭), 估算按所选材料单价实时重算 |
### 12.9 仓库库存 `/api/warehouse`(v0.3 新增)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 库存列表 | GET | `/api/warehouse/list?page=1&keyword=&material=` | query | material: profile/glass/hardware/addon |
| 新增材料 | POST | `/api/warehouse` | `{name(必填), material, spec, color, unit, price, count, warning, length}` | |
| 编辑材料 | PUT | `/api/warehouse/{id}` | 同新增 | |
| **出入库** | POST | `/api/warehouse/move` | `{item_id, direction: 1\|-1, count, note}` | 出库拒绝负库存,写流水 |
| 出入库流水 | GET | `/api/warehouse/moves?page=1` | query | 全部流水倒序 |
### 12.10 前端(v0.3,Vue3 + Element Plus,构建产物挂载于后端)
| 路由 | 页面 | 功能 |
|---|---|---|
| `/login` | 登录 | JWT 登录 |
| `/dashboard` | 工作台 | 订单/意向单/客户/合同统计 + 快捷入口 |
| `/orders` | 订单中心 | 搜索/新建(选合同+样板自动带图纸)/详情抽屉(**画布渲染 drawing_data**)+状态机操作+生产进度更新+算料报价+打印 |
| `/contracts` | 合同管理 | 列表/新建(选客户带出信息)/意向单转正式/一键下订单 |
| `/customers` | 客户管理 | CRUD + 搜索 |
| `/templates` | 样板库 | 254 套样板网格(平面图) + 分类筛选 + 画布预览 + 算料报价 + 用此样板下单 |
| `/warehouse` | 仓库库存 | 材料 CRUD + 出入库(流水) + 库存预警高亮 |
| `/optimize` | 优化下料 | 需求/原料编辑 + FFD 计算结果(利用率/切割组合表) |
**画布渲染器**(`components/WindoorCanvas.vue`,自研 Canvas2D):解析 drawing_data → Frame 玻璃区域多边形填充/描边 → 外框加粗 → 尺寸标注;支持滚轮缩放、拖拽平移、双击复位。
**扇样式推断引擎**(v0.5):子形状按 `有效bd → polyId映射区域(双扇对半分) → 帧bbox按扇数竖向均分` 三级解析,还原每套样板的真实分格与开向(od 角度三角标记、inward/outward 空心/实心、门标注)——修复此前"所有样板显示为默认矩形"的问题。
**画布编辑器**:+竖梃/+横梃 → 点击玻璃区域完成分割(两次交叉点法实时重算区域,mm.sp 同步追加分割线记录),支持撤销;**尺寸参数化编辑**(v0.5):W/H 输入框或拖拽外框右缘/下缘/右下角手柄 → 全部几何按比例自动缩放(型材料宽不变,最小 300mm),拖拽中静默、松手提交/回滚;订单详情可"保存图纸"(PUT orders/{id}),样板库可"另存为我的样板"(POST windoor/templates)。
**自动报价**(v0.5):画布变化(改尺寸/加分格)后 600ms 防抖自动调用 offer-price,页面实时显示当前尺寸总价。
**报价弹窗**(`components/QuoteDialog.vue`):明细表 + 总价,可从样板库/订单详情唤起,支持浏览器打印。
### 12.11 业务扩展模块(v0.7,`/api`)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 产品列表 | GET | `/api/products?page=&keyword=&material=` | query | 产品价格本(型材/玻璃/五金/配件) |
| 新增/编辑/删除产品 | POST/PUT/DELETE | `/api/products[/{id}]` | `{name, material, spec, unit, price, cost}` | |
| 保存报价单 | POST | `/api/quotation` | `{customer_id, items[], total, drawing_data, ...}` | 单号 `BJ+YYYYMMDD+3位流水` |
| 报价单列表/详情/更新/删除 | GET/PUT/DELETE | `/api/quotations[/{id}]` | 分页+keyword+status | status: 0草稿 1已发送 2已成交 3作废 |
| 提交售后单 | POST | `/api/after_sales` | `{title, description(必填), order_no?}` | 对齐原站"问题描述必填"校验 |
| 售后列表/状态流转 | GET/PUT | `/api/after_sales[/{id}]` | status: 0待处理 1处理中 2已完成 | |
| 创建量尺单 | POST | `/api/measures` | `{customer_name, phone, address, measure_date, surveyor, data[{name,width,height,qty}]}` | 对齐原站量尺管理 |
| 量尺列表/更新 | GET/PUT | `/api/measures[/{id}]` | status: 0待量尺 1已量尺 2已出图 | |
| 阳光房订单 | GET | `/api/sunny_house_orders` | 分页 | 订单按 `order_type=1` 过滤(Order 新增 order_type 字段) |
| 修改资料/密码 | PUT | `/api/auth/profile` `/api/auth/password` | — | 对齐原站个人中心 |
| 邀请信息 | GET | `/api/auth/invitation` | — | 邀请码 `HMC+6位用户ID` |
### 12.12 前端页面总览(v0.7,15 个页面,响应式布局)
| 路由 | 页面 | 说明 |
|---|---|---|
| `/login` | 登录/注册 | 双 Tab(注册对齐原站 register) |
| `/dashboard` | 工作台 | 统计 + 快捷入口 |
| `/orders` | 订单管理 | 搜索/创建/图纸编辑/状态机/进度/报价/打印 |
| `/sunny` | 阳光房 | 阳光房订单(order_type=1) |
| `/quotations` | 报价单 | 手动报价(选样板自动算料+手动调整)/转订单 |
| `/contracts` | 合同管理 | 意向单→正式单/一键下单 |
| `/customers` | 客户管理 | CRUD |
| `/measure` | 量尺管理 | 量尺单 + 洞口尺寸清单 |
| `/aftersale` | 售后管理 | 提交/流转 |
| `/templates` | 样板库 | 254 套样板/画布预览/编辑/报价/下单 |
| `/products` | 产品管理 | 价格本 CRUD |
| `/warehouse` | 仓库库存 | 出入库/预警 |
| `/optimize` | 优化下料 | FFD 计算 |
| `/market` | 商城 | 模块开通(购买流程占位,支付需外部资质) |
| `/guide` + `/personal` | 使用指南/个人中心 | 帮助文档/资料/密码/邀请返利 |
**响应式适配**:侧边栏 <900px 自动折叠为图标栏;<700px 主区边距收窄;表格列横向滚动。
### 12.14 平台扩展模块(v1.0,`/api`)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 渲染设置 + AI 风格 | GET/POST | `/api/rendering-info` | `{wall_color, scene, glass_tint, ai_style, ai_time}` | 存公司参数;风格: modern/chinese/european/nordic,时间: day/dusk/night |
| **合同下料方案** | POST | `/api/cut_solution` | `{contract_id, kerf}` | 汇总合同下订单图纸的算料需求 → FFD 优化 → 参数持久化;`GET /api/cut_solution/{id}` 读回 |
| 设备激活 | POST | `/api/device-activate` | `{sn, activate_code, name}` | 首绑自动生成激活码,防重复绑定 |
| 设备列表 | GET | `/api/devices` | — | |
| 生成订单分享 | POST | `/api/share-order/{id}` | — | HMAC 可逆 token |
| **公开订单摘要** | GET | `/api/shared-order/{token}` | 无需登录 | 分享页数据 |
| 帮助文章 | GET | `/api/articles?category=` | query | 种子 8 篇自撰指南 |
| 报表汇总 | GET | `/api/reports/summary?days=14` | days | 按日订单数/金额 + 8 项总量 + 状态分布 |
> v1.0 调整:按需求**移除短信验证码登录与支付收银接口**(verification-code-new / code-login / pay-orders 已下线);登录采用手机号+密码。
新增前端能力(v1.0):
- **AI 炫图**(`/ai_render`):自研程序化场景合成引擎——风格(现代简约/新中式/欧式/北欧)× 时间(日景/黄昏/夜景)× 场景(室内/室外/影棚),阶段化生成动画,四方案画廊,一键下载 PNG
- **效果图渲染**(`/rendering`)与 **3D 展示**(`/threed`,Three.js 挤出重建)
- **打印中心**(`/printer`)、**下料方案**(`/cut_solution`)、**设备管理**(`/devices`)、**订单分享**(`/share/order/:token` 公开页)、**报表统计**(`/chart`)
- **主题精致化**:品牌色设计变量、Element Plus 主题覆盖、卡片圆角阴影、登录页氛围渐变、响应式侧边栏
### 12.13 种子数据
```bash
uv sync # 安装依赖
uv run python -m huamenchuang.scripts.seed # 管理员 + 分类树 + 254 套样板入库
uv run uvicorn huamenchuang.main:app --reload # 启动,http://localhost:8000/docs
```
管理员账号:`13800000000 / 123456`(开发用,生产必须修改)。
默认 SQLite(`HMC_DATABASE_URL` 可切 MySQL),JWT 密钥 `HMC_JWT_SECRET` 生产必须设置。
### 12.15 价格脚本模块 `/api/scripts`(v2.20 新增,P0-报价真实化)
对齐目标站 `script`/`product-script`/`product-test` 语义:按系列为各计价项配置**定价公式**,报价时启用即按脚本计价。
公式引擎:安全表达式(递归下降解析,无 eval)。支持 `+ - * / ( )` 与函数 `min/max/round/abs/ceil/floor/sqrt`。
变量表:`w`/`h`(窗宽高mm)、`area`(㎡)、`perimeter`(m)、`glass_area`(㎡)、`sashes`(扇)、`meters_frame`/`meters_mullion`/`meters_sash`(各类型材米数)、`length`(作用项对应量:型材=米数、玻璃=面积、五金=扇数)。
公式语义:结果 = 该作用项**单价**(元/米、元/㎡、元/扇);`target=total` 时结果直接作为**总价**。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 脚本列表 | GET | `/api/scripts` | — | 公司全部脚本 |
| 新增脚本 | POST | `/api/scripts` | `{name(系列名), target, formula, note?, enabled?}` | target ∈ frame/mullion/sash/glass/hardware/total;公式后端白名单+语法校验,非法返回 code=0 |
| 编辑脚本 | PUT | `/api/scripts/{id}` | 同新增 | |
| 删除脚本 | DELETE | `/api/scripts/{id}` | 路径 | |
| **脚本试算** | POST | `/api/scripts/preview` | `{formula, target?, w?, h?, glass_area?, sashes?, meters_*?}` | 对齐目标站 product-test;返回 `{result, vars}` |
| 报价启用脚本 | POST | `/api/offer-price` | 增加 `use_scripts: true` 或 `scripts: {target: formula}` | 响应加 `scripted[]`(被脚本覆盖的项)、`script_total`(总价是否被脚本直取);行级 `scripted` 标记 |
种子示例脚本 5 条:断桥铝-55系列(frame/mullion/sash)、双层中空玻璃(glass)、标准五金(hardware)。
### 12.16 订单 BOM 与导出(v2.20 新增,P0-生产闭环)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| **订单 BOM 单** | GET | `/api/orders/{id}/bom` | 路径 | 对齐目标站 calcbomOrder;返回 `{bars:[{kind,note,length,qty}], glass_area_m2, sash_count, width_mm, height_mm}`(从 drawing_data 重放算料,不含价格) |
| **订单导出 Excel** | GET | `/api/orders/{id}/export` | 路径 | 对齐 orders/export;xlsx(订单信息+BOM 下料清单+玻璃/五金汇总),`Content-Disposition: attachment` |
### 12.17 打印模板 `/api/order-formats`(v2.20 新增,P0-打印模板体系)
对齐目标站 `order_format` 语义:模板=区块列表 `blocks[]`,前端按区块渲染 A4。
区块类型:`title`(text=标题文字)、`info`(fields=订单字段列表:order_no/customer_name/phone/address/count/area/money/created_at/note)、`image`(图纸缩略图)、`table`(kind=process 生产进度 / bom 下料清单)、`sign`(签字行)。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 模板列表 | GET | `/api/order-formats` | — | 按 sort 排序 |
| 新增模板 | POST | `/api/order-formats` | `{name, order_type, blocks[], is_default?}` | 区块类型校验;is_default=1 时清除其他默认 |
| 编辑模板 | PUT | `/api/order-formats/{id}` | 同新增 | |
| 删除模板 | DELETE | `/api/order-formats/{id}` | 路径 | |
种子模板 2 套:标准生产订单(默认)、生产订单+BOM 下料单。
### 12.18 合同下料方案升级(v2.20,P0-按库存优化)
`POST /api/cut_solution` 请求扩展:`{contract_id, kerf, use_stock?, stock_length?}`。
- `use_stock=true`:从仓库实际库存(material=profile 且 length>0 且 count>0)取原料优化;需求超出库存时输出 `shortage[]` 缺料清单(length/label/qty),不拦截计算
- `use_stock=false`:按 `stock_length`(默认 6000mm)虚拟原料,数量按需求自动估算
- 响应/保存均新增:`result`(完整优化方案)、`preparation[]`(切割准备/领料清单:原料×根数×切割件数×余料)、`shortage[]`(按库存模式的缺料清单)
- `GET /api/cut_solution/{contract_id}` 返回完整保存方案(含 result/preparation/shortage)
### 12.19 财务模块 `/api`(v2.20 新增,P0-收款财务闭环)
对齐目标站 `/manage/financial` + `contract_receipt*` 语义。
**收款方式**:1现金 2银行转账 3微信 4支付宝 5其他;**状态**:1正常 0已作废(作废不参与统计/已收)。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 收款账户列表 | GET | `/api/receipt-accounts` | — | |
| 新增/编辑/删除账户 | POST/PUT/DELETE | `/api/receipt-accounts[/{id}]` | `{type,name,number?,bank?,note?,is_default?}` | is_default=1 唯一默认 |
| **登记收款** | POST | `/api/contracts/{id}/receipts` | `{amount(>0), method, account_id?, receipt_date?, note?}` | 收款单号 `SK+YYYYMMDD+3位流水` |
| 作废收款 | POST | `/api/receipts/{id}/void` | 路径 | 对齐 void_contract_receipt |
| 收款流水分页 | GET | `/api/receipts` | `page,keyword,year,contract_id,status` | 联合同号/客户名 |
| **收款统计** | GET | `/api/receipts/statistics` | `year?`(默认当年) | `{total_received, contract_total, unreceived, monthly[12]}`(对齐 statistics/_tb) |
合同列表(`GET /api/contracts`)响应行新增:`received`(有效收款合计)、`unreceived`(合同额-已收)。
### 12.20 Excel 导入导出(v2.20 新增,P0-数据迁移)
依赖 `openpyxl` + `python-multipart`。导入文件自动探测表头行(兼容带标题行的导出格式);导出为带样式 xlsx。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 客户导入模板 | GET | `/api/customers/excel-template` | — | 列:姓名*/电话/地址/业务员/备注 |
| **客户批量导入** | POST | `/api/customers/excel-in` | multipart `file` | 对齐 customers_excel_in;缺姓名行跳过并逐行报错 |
| 客户导出 | GET | `/api/customers/excel-out` | — | 全量 |
| 合同导出 | GET | `/api/contracts/excel-out` | — | 对齐 contracts_excel,含已收金额 |
| **库存批量导入** | POST | `/api/warehouse/excel-in` | multipart `file` | 对齐 materials_config_excel:同名材料更新(数量累加),否则新增 |
| 库存导出 | GET | `/api/warehouse/excel-out` | — | 对齐 warehouse_excel_out |
| 统计报表导出 | GET | `/api/reports/excel-statistics` | — | 对齐 excel-statistics:订单汇总+收款统计双 Sheet |
### 12.21 装饰格条(v2.20 新增,P0-格条设计器)
对齐目标站 `/draw-decoration` + `decorationBarLibrary`(8 种装修风格)。**自研数据扩展**:`drawing_data.sm[i].decs[] = [{ri(重放区域索引), kind(风格), rows, cols, bw(条宽mm), color}]`,序列化向后兼容(254 套样板校验 0 回归)。
- **2D**:画布编辑态点选玻璃格 → HUD"格条"按钮 → 对话框(8 风格预设+行列/条宽/颜色)→ 应用/清除
- **3D**:玻璃前表面按行列挤出格条体(静态格与扇面板均支持)
- **报价**:`offer-price`/BOM 自动计入格条算料(横条=区域宽×rows、竖条=区域高×cols,默认 12 元/米,`prices.dec_bar` 可覆盖,kind=`dec_bar`)
风格预设:现代轻奢(1×1黑25)/意式简约(1×1灰18)/奶油风(2×2白22)/中式(2×2棕28)/欧式古典(3×3金20)/法式(2×2金16)/地中海(2×3蓝24)/田园密格(4×4白14)。
### 12.22 照片式 AI 炫图(v2.20 升级,P0-照片炫图)
> **v2.44 新增 `blueprint` 模式(工程大样图,整合自 autocad 项目草图标准化资产);v2.45 支持手绘草图**:`{mode:"blueprint", sketch_b64(手绘量尺登记表照片或 2D 图纸截图), template_id|drawing_data?(可选, 仅作窗型特征参考; 纯草图模式可不传)}` → AI 重绘为黑白 CAD 工程大样图(正投影立面/细黑线框/玻璃淡灰填充/尺寸线/中文工程标注/标题栏;一页多窗逐窗转换,保留分格/开启扇/手写尺寸并规范誊写,不编造尺寸),可作报价与合同附件。提示词预览 `/api/ai-render/prompt` 同样支持 `mode:"blueprint"`。
对齐目标站 `renderV2` 炫图 7 分类:**渲图 / 安装合成 / 门窗擦除 / 室内装修 / 别墅 / 去围栏 / 变毛坯**。
纯前端像素合成(`components/photoCompose.ts`,无需服务端 AI):
- 流程:上传场景照片 → 拖拽框选窗洞(原始照片坐标)→ 选样板窗型 → `renderWindowOnly`(分割树重放渲染透明背景窗型:玻璃渐变/中梃/扇框开向/装饰格条)→ 光照匹配(采样洞口周边亮度调整窗户亮度)→ 边缘贴合 → 场景后处理(安装投影/擦除填充/暖色滤镜/石材基座/背景虚化/去饱和毛坯)
- 一键生成 4 方案画廊 + PNG 下载;原"风格速览"模式保留为第二 Tab
- 无新增后端接口(复用 `/api/windoor/templates` 与 `/api/rendering-info`)
### 12.23 云端 AI 生图 `/api/ai-render`(v2.28 新增,gpt-image-2.5)
接入 OpenAI Images 兼容中转站(`geminiapi.ai`,模型 `gpt-image-2.5-1k`)。**Key 仅存后端**(`.env` 的 `HMC_AI_API_KEY`,已 gitignore;Docker 部署用环境变量注入)。
**提示词工程**(`services/ai_prompt.py`,核心资产):从 `drawing_data` 结构化提取窗型特征(组合单元数/每单元宽高 mm/玻璃格数/扇类型(平开、推拉、内开内倒、对开门)/开向内外/纱扇/圆弧复古/装饰格条)→ 生成专业英文提示词:主体先行(窗型几何量化描述)→ 材质风格(4 风格映射铝型材 finish)→ 摄影光照(3 时间)→ 负面约束("保持窗型分格不变,勿增删扇")→ 质量词。生成尺寸按窗型宽高比自动选择(1024x1024 / 1536x1024 / 1024x1536)。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 提示词预览 | POST | `/api/ai-render/prompt` | `{template_id\|drawing_data, style, time, scene}` | 返回 `{features(窗型特征), prompt(优化后完整提示词), size}` |
| **云端生成** | POST | `/api/ai-render/generate` | `{template_id\|drawing_data, mode: quick\|photo\|blueprint, style, time, scene, photo_b64?, sketch_b64?}` | quick→`images/generations`;photo→`images/edits`(用户照片为参考图,AI 将窗型合成进照片窗洞);**blueprint(v2.44 工程大样图,整合自 autocad 项目)→`images/edits` 以 2D 图纸截图为底图重绘黑白 CAD 大样图**(正投影/尺寸线/中文工程标注/标题栏,严格保留分格与开启扇,不编造尺寸,可作报价与合同附件);返回 `{image_b64, prompt, size, features}`;耗时 30~90s(前端 timeout 200s) |
- 未配置 Key 时接口返回 code=0,前端降级本地程序化渲染("本地 4 方案"按钮)
- 前端:AI 炫图页两个模式(照片合成/风格速览)主按钮均走云端生成,结果大图展示 + 提示词折叠面板可查看 + PNG 下载;照片模式参考图自动压缩 ≤1536px JPEG
### 12.24 P1 设计域扩展(v2.29)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| **AI 推荐窗型** | POST | `/api/ai-recommend` | `{width_mm, height_mm, room(living/bedroom/balcony/kitchen/bath/shop), needs[]: ventilation/light/sound/safe/view/door}` | 对齐 ai_recommend;规则引擎从样板库打分推荐 TOP5,含匹配分与可解释理由(宽高比/开扇数/玻璃面积比/门类/纱扇) |
| **创建分享** | POST | `/api/shares` | `{type: render\|combine\|quote\|sunny, title, payload}` | 对齐 share-render/group-detail-share/quote_link;返回 `{token, url:/share/s/{token}}` |
| 公开访问分享 | GET | `/api/shares/{token}` | 路径(无需登录) | 返回 `{type, title, payload, created_at}`;前端 `/share/s/:token` 公开页渲染(大图/报价明细/组合只读画布) |
| 阳光房设计列表 | GET | `/api/sunroom/designs` | — | |
| **保存阳光房设计** | POST | `/api/sunroom/designs` | `{name, roof: flat\|gable, width_mm, depth_mm, height_mm, bay_mm, glass, frame_color, order_id?}` | 对齐 sunnyhouse 平顶/人字顶;前端 SunroomCanvas 参数化 2D(正视+俯视),AI 效果图走 `/api/ai-render/generate`(scene=sunroom-flat/gable) |
| 删除阳光房设计 | DELETE | `/api/sunroom/designs/{id}` | 路径 | |
**逐格扇/玻璃指定**(自研扩展 `drawing_data.sm[i].assigns[] = [{ri, sash:{kind: fixed/casement/awning/sliding/kfc, openToward, openDirection}, subBar, glass}]`):画布选格 → "扇/玻璃"对话框指定开启类型/方向/铰链侧/副梃/玻璃配置;2D 画开向三角与副梃线;算料计入指定扇框料+五金(`sash_count` 含 assigns)与副梃长度(`subbar` 默认 25 元/米,价格本可覆盖)。量尺拍照标注:洞口行 `photo` 字段存标注合成图(前端 Canvas 拖线标注)。
### 12.25 P1 报价/产品域(v2.30)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 系列树 | GET | `/api/product-catelogs` | — | 型材系列树(对齐 product-catelog),产品价格本页左侧管理 |
| 新增/改名/删系列 | POST/PUT/DELETE | `/api/product-catelogs[/{id}]` | `{name, fid?}` | 系列下有产品时禁止删除 |
| 产品筛选 | GET | `/api/products` | 增加 `catelog_id` | 产品挂系列(catelog_id);material 新增 `subbar`(副梃) |
| **成本口径** | — | (自动) | — | 对齐 quote_by_cost:报价保存/更新时按产品名匹配价格本成本写入行 `_cost`;报价响应新增 `cost_total`(成本合计)与 `profit`(毛利);报价链接分享(quote_link)走 `/api/shares` type=quote 公开页 |
### 12.26 P1 生产/CRM/仓库域(v2.31~v2.32)
| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
|---|---|---|---|---|
| 车间员工 CRUD | GET/POST/PUT/DELETE | `/api/workshop/staff[/{id}]` | `{name, phone?, role(环节), status?}` | 车间管理页名册 |
| **派工** | POST | `/api/workshop/assign` | `{order_id, process_name, staff_id, note?}` | 进度节点指派员工,记录 staffHistory |
| 派工历史 | GET | `/api/workshop/assignments` | `page` | |
| 扫码跟踪 | GET | `/api/workshop/scan/{order_no}` | 路径(条码号=订单号) | 对齐 scan_tracking;返回订单+7 节点进度 |
| 跟单看板 | GET | `/api/workshop/board` | — | 在产订单按当前环节分列 |
| 双通道优化 | POST | `/api/optimize1d` | 增加 `method: fast\|fine` | fast=FFD 快速;fine=BFD+余料均衡(详细优化,对齐 optimize1d-ai) |
| **订单利润** | GET | `/api/reports/order-profit` | `days?` | 对齐 order-profit:金额 vs BOM 材料成本估算(价格本成本均价),行级毛利/毛利率 |
| **客户跟进** | GET/POST/DELETE | `/api/customers/{id}/follows[/{fid}]` | `{content, next_date?}` | 对齐 customer_records;客户页"跟进"对话框 |
| 业务员体系 | GET/POST/PUT/DELETE | `/api/salesmen[/{id}]` | `{name, phone?, commission_rate?}` | 名册 CRUD;客户表 salesmen 字段选择业务员 |
| 业务员业绩 | GET | `/api/salesmen/statistics` | — | 名下客户数/合同数/合同额/预估提成(salesmen_statistics) |
| **余料入库** | POST | `/api/warehouse/surplus-in` | `{contract_id, min_length?}` | 读合同下料方案,≥最小长度余料逐根入库(note=surplus) |
| 余料列表 | GET | `/api/warehouse/surplus` | `page` | |
| **领料出库** | POST | `/api/warehouse/pickout` | `{contract_id, note?}` | 对齐 pickout:按领料清单扣库存+流水;库存不足报缺口 |
| **采购单** | POST | `/api/purchases` | `{contract_id?(自动读缺料), items?, note?}` | 缺料清单自动生成(CG+日期+流水) |
| 采购列表/入库 | GET/POST | `/api/purchases[?status]` · `/{id}/receive` | — | 入库联动:明细写入库存+StockMove 流水 |
### 12.27 成本核算模块 `/api/costbook`(v2.36 新增,鲁铭成本核算 Excel 适配)
> 适配门窗厂成本核算工作簿(12 sheet 格式:铝成本/铝计算/木成本 + 价格库 8 表 + 其他)。
> 价格库 8 表(华健开模汇总/丝吉利娅五金/索宾柯/隔热条/胶条/赛遨辅材/螺丝/其他价格)全量导入产品价格本 `/api/products`(同名同材质更新价格,否则新建,material 按 sheet 语义映射);
> 「铝成本」页解析为**系列成本模板** `cost_series_templates`(data JSON 含 window/profile/addon/strip/seal/glass/extra/hardware/labor 八分区)。
> 种子:`uv run python -m huamenchuang.scripts.seed_costbook`(data/ 下三个鲁铭 xlsx,75/85/95 三系列已预置)。
| 接口名称 | 方式 | URL 路径 | 请求参数 | 响应示例 |
|---|---|---|---|---|
| 导入核算 Excel | POST | `/api/costbook/import-excel` | multipart `file`(.xlsx) | `{libraries:{created,updated}, optimize_stats:{bars,total_length,sashes,glasses,glass_area}}`,message 带统计 |
| 系列模板列表 | GET | `/api/costbook/series` | — | `[{id,name,series,wall_thick,source_file,enabled,window:{w,h,area,sash_area,...}}]` |
| 系列模板详情 | GET | `/api/costbook/series/{id}` | — | `{...brief, data:{window,profile[],addon[],strip[],seal[],glass[],extra[],hardware[],labor}}` |
| 新建系列模板 | POST | `/api/costbook/series` | `{name,series,wall_thick,data}` | `{id}` |
| 删除系列模板 | DELETE | `/api/costbook/series/{id}` | — | `{code:1}` |
| **成本估算** | POST | `/api/costbook/estimate` | `{series_id,width,height,count=1,sash_count?}` | 见下 |
| 部位材料清单 | GET | `/api/costbook/series-materials` | — | 聚合全部系列用料按部位分组(frame/mullion/sash/pressline/glass/hardware),供报价「按部位选材」下拉(可跨系列混搭) |
**估算响应**(口径对齐核算表:固定类料随固定面积、开启类随开启面积、once 类按樘数缩放;人工/其他成本优先用核算表每平实际口径):
```json
{
"series": 75, "wall_thick": 1.4, "name": "75框扇齐平",
"width": 1600, "height": 1600, "count": 1, "sash_count": 1,
"area": 2.56, "open_area": 0.78, "fixed_area": 1.78,
"profile": [{"code","name","qty(m)","weight","weight_per_m","unit_price","amount"}],
"profile_cost": 466.8, "addon_cost": 14.11, "strip_cost": 17.69, "seal_cost": 27,
"glass_cost": 179.2, "extra_cost": 0, "hardware_cost": 55,
"labor_cost": 243.2, "other_cost": 115.2,
"total": 1118.2, "per_sqm": 436.8,
"margin_ladder": {"10%": 1230.02, "20%": 1341.84, "...": "50%=1677.29", "70%": 1900.93}
}
```
校验基准:75 系列基准窗(1600×1600,开启 1 扇)估算 total=1118.20,与核算表"成本合计"完全一致(冒烟 COST-1~9)。