Files

114 lines
9.2 KiB
Markdown
Raw Permalink Normal View History

2026-09-15 18:02:25 +08:00
# geMoldInsight 前后端契约(API_CONTRACT)
> 文档定位:**前后端契约的唯一归属**——端点总览、统一约定、OpenAPI 类型生成流程。
> 端点定义、路径、请求/响应 schema 的**最终真相源是根目录 `openapi.json`**(由 FastAPI 自动生成);本文维护人可读的总览与变更规则。当前状态见 [STATUS.md](STATUS.md),架构见 [ARCHITECTURE.md](ARCHITECTURE.md),接口类技术债见 [TECH_DEBT.md](TECH_DEBT.md) D1。
---
## 1. 总览
三种部署形态暴露的 API 面(入口见 [src/entrypoints/](../src/entrypoints/)):
| 形态 | API 面 |
|---|---|
| unified(推荐) | `/api/*`(moldinsight + inventory)+ `/api/auth/*` + 顶层 `/health` |
| moldinsight-only | `/api/*`(moldinsight)+ `/api/auth/*` + `/health` |
| inventory-only | `/api/*`(inventory)+ `/api/auth/*` + `/health` |
- moldinsight 路由在 [moldinsight/api/\_\_init\_\_.py](../src/moldinsight/api/__init__.py) 按 `ROUTE_MODULES` 清单经 `_safe_include` 聚合(装载失败登记 [route_registry.py](../src/moldinsight/api/route_registry.py),`/api/health` 呈现 `degraded` 并列出失败模块;`DEBUG=true` 下失败直接抛错;`debug_router` 仅 `DEBUG=true` 注册)。
2026-09-15 18:02:25 +08:00
- inventory 路由在 [inventory/api/\_\_init\_\_.py](../src/inventory/api/__init__.py) 按域静态聚合。
- 认证路由来自 [shared/services/auth_routes.py](../src/shared/services/auth_routes.py),由 [app_factory](../src/shared/app_factory.py) 挂载,三种形态共用。
## 2. 统一约定
- **鉴权**:JWT Bearer(`Authorization: Bearer <token>`)。登录:`POST /api/auth/login`(表单)/ `POST /api/auth/login/json`(JSON);受保护路由通过 FastAPI 依赖 `get_current_active_user` 注入当前用户([shared/services/auth_service.py](../src/shared/services/auth_service.py))。`SECRET_KEY` 跨进程必须一致。
- **响应形态**:现状**无统一信封包装**——各端点直接返回业务 JSON;schema 以 `openapi.json` 的 components 为准。新增接口不建议另起信封风格,保持与所在模块一致。
- **错误**:FastAPI 标准 `HTTPException` 状态码语义;业务校验优先 Pydantic 请求模型自动 422。
- **业务路由前缀**:全部业务端点在 `/api` 下;顶层仅 `/health`(探活)、`/html/{filename}`(可视化报告代理)、`/login`、`/users`(历史遗留入口,前端主链路用 `/api/auth/*`)。
2026-09-15 18:02:25 +08:00
## 3. 端点总览(按域分组)
> 下表为导航用速览;路径参数、请求/响应字段以 `openapi.json` 为准。
### 3.1 认证与用户(shared)
| 域 | 端点 | 文件 |
|---|---|---|
| 登录 | `/api/auth/login`、`/api/auth/login/json`、`/api/auth/logout` | auth_routes.py |
| 当前用户 | `/api/auth/me` | auth_routes.py |
| 用户管理 | `/api/auth/users`、`/api/auth/users/{user_id}`、`/api/auth/users/{user_id}/reset-password`(管理员;JSON body `{ new_password }`,最短 6 位) | auth_routes.py |
2026-09-15 18:02:25 +08:00
| 角色权限 | `/api/auth/roles`、`/api/auth/roles/{role_id}`、`/api/auth/roles/{role_id}/permissions`、`/api/auth/permissions`、`/api/auth/permissions/{permission_id}` | auth_routes.py |
### 3.2 moldinsight(模具分析)
| 域 | 端点 | 文件 |
|---|---|---|
| 上传 | `/api/upload` | upload_router.py |
2026-09-16 17:55:04 +08:00
| 批量分析 | `/api/batch-upload`、`/api/batch/{batch_id}`(聚合状态以 PG 为准;响应含 `current_step`;他人批次 403、不存在 404) | batch_router.py |
| 任务状态 | `/api/status/{task_id}`(需登录;仅任务所有者可访问,他人/无主任务 403,不存在 404) | task_router.py |
2026-09-15 18:02:25 +08:00
| 历史结果 | `/api/history`、`/api/history/{filename}` | history_router.py |
| HTML 报告 | `/html/{filename}`(根路径,非 `/api` 前缀;D11 代理:RustFS 报告键 `html/reports/{filename}` 直取 → 遗留 JSON 包装对象 → 本地 `html_output/` 存量兜底 → 404。已知约束:不做认证——iframe 无法携带 Authorization 头,沿用 StaticFiles 时代既定姿态) | html_report_router.py |
| CAM | `/api/cam/plan`(Pydantic 请求模型;未提供的偏好回落任务持久化偏好再回落默认) | cam_router.py |
| 设计 | `/api/optimize-layout`、`/api/design-cooling`、`/api/design-gating`、`/api/design-mold-system`、`/api/detect-undercuts` | design_router.py |
| 成本估算 | `/api/cost-estimate` | cost_router.py |
| 加工 | `/api/design-cam`、`/api/check-collision`、`/api/optimize-toolpath`、`/api/design-electrodes`、`/api/simulate-machining` | machining_router.py |
| 导出 | `/api/export-mold`、`/api/export-download/{filepath}`、`/api/export-recommendations` | export_router.py |
2026-09-15 18:02:25 +08:00
| 铝价(模拟数据) | `/api/aluminum-price/current`、`/api/aluminum-price/history` | aluminum_price_routes.py |
| 老师傅经验反馈(D17) | `/api/tasks/{task_id}/experience-feedback`(写入:需登录 + 任务归属 + `feedback_experience_hint` 权限;body 含 `scheme_id` / `feedback_status ∈ {adopted, adjust, rejected}` / 可选 `feedback_reason` / `adjust_suggestion` / 上下文快照;写完调用 `TaskQueryService.invalidate_task_view`);`/api/tasks/{task_id}/experience-hints`(读取:需登录 + 任务归属;返回同 stp_file_id + material_family + is_foam 锚定的历史 hints 聚合,按 scheme_axis 分组,含 adopted/rejected/adjust 计数 + 加权 confidence + sample_count + 回显 fingerprint) | experience_feedback_router.py |
| 健康检查 | `/api/health`(有路由装载失败时 `status: degraded` 并列出失败清单;`pythonocc` 为真实探测) | health_router.py |
2026-09-15 18:02:25 +08:00
| 调试(仅 DEBUG) | `/api/debug/tasks` | debug_router.py |
### 3.3 inventory(进销存)
| 域 | 端点 | 文件 |
|---|---|---|
| 成品 | `/api/products`、`/api/products/{product_id}`、`/api/products/{product_id}/materials`、`/api/products/from-task/{task_id}` | product_routes.py |
| 物料价格/供应商 | `/api/materials/*` | material_routes.py |
| 供应商 | `/api/suppliers`、`/api/suppliers/{supplier_id}` | supplier_routes.py |
| 客户 | `/api/customers`、`/api/customers/{customer_id}` | customer_routes.py |
| 仓库 | `/api/warehouses` | warehouse_routes.py |
| 库存 | `/api/inventory`、`/api/inventory/{inventory_id}` | inventory_routes.py |
| 库存流水 | `/api/stock-movements` | stock_movement_routes.py |
| 采购订单 | `/api/purchase-orders`、`/api/purchase-orders/{order_id}`、`.../receive`、`.../status` | purchase_order_routes.py |
| 采购建议 | `/api/purchase-demands/calculate` | purchase_demand_routes.py |
| 销售订单 | `/api/sales-orders`、`/api/sales-orders/{order_id}`、`.../status`、`.../issue-materials`、`.../consume-materials`、`.../production-plan` | sales_order_routes.py |
| 财务 | `/api/finance/summary`、`/api/finance/receivables|payables|receipts|payments|transactions`、`/api/finance/partner-statement/{partner_type}` 等 | finance_routes.py |
| 看板 | `/api/dashboard` | dashboard_routes.py |
**moldinsight → inventory 桥接**:`/api/products/from-task/{task_id}` 由分析任务一键创建成品(对应 `STPFile.product_id -> Product.id` 单库桥接,见 [ARCHITECTURE.md](ARCHITECTURE.md) §5.1)。
## 4. OpenAPI 与前端类型生成(接口变更三件套)
接口变更后**必须依次完成**:
1. **改代码**:路由 + Pydantic 请求/响应模型(优先模型,少写 `request.json()` 解析)。
2. **重导出 `openapi.json`**(在可 import 项目的环境执行,如 conda `gemold`):
```bash
python -c "import sys; sys.path.insert(0, 'src'); from entrypoints.unified import app; import json; print(json.dumps(app.openapi(), ensure_ascii=False, indent=2))" > openapi.json
```
(moldinsight-only 契约视角可把 `entrypoints.unified` 换成 `entrypoints.moldinsight`;对外主契约以 unified 为准。)
3. **重新生成前端类型**:
```bash
cd frontend && npm run gen:api # openapi-typescript:../openapi.json -> src/types/api.ts
```
- `frontend/src/types/api.ts` 是**生成物,禁止手改**;前端代码类型引用它。
- 三步缺一即前后端契约漂移(硬约束,见 [AGENTS.md](../AGENTS.md) §2)。
- 当前 `openapi.json` 于 2026-09-17 随批次 3 重导出(76 paths),前端 `src/types/api.ts` 同步再生。
2026-09-15 18:02:25 +08:00
## 5. 契约变更规则
- 新增接口先定模块归属(moldinsight / inventory / shared auth),再写路由;返回结构、路径、鉴权发生变化时,同步更新本文相应表格。
- 路由文件过大按职责拆分(参照批次 3 的 design / cost / machining / export 拆分先例;新增路由须登记 [moldinsight/api/\_\_init\_\_.py](../src/moldinsight/api/__init__.py) 的 `ROUTE_MODULES`)。
2026-09-15 18:02:25 +08:00
- 破坏性变更(删字段 / 改语义)需在 [STATUS.md](STATUS.md) 日志条目中记录,并确认前端同仓同步修改。
## 6. 前端消费约定
- 前端为独立工程 [frontend/](../frontend/)(Vue 3 + Vite + TS + Pinia + TDesign),按域组织在 `src/modules/`(moldinsight / inventory / users / login / home)。
- API 类型唯一来源 `src/types/api.ts`(生成物);组件不手写与后端重复的响应类型。
- 跨域:开发期走 Vite;部署期为同域反代(见 [DEPLOYMENT.md](DEPLOYMENT.md)),`CORS_ORIGINS` 仅服务于非同域场景。