This commit is contained in:
zlwu committed 2026-06-10 22:59:34 +08:00
1 parent 725774ef0c
commit c3be9d1dce
176 files changed
+41925 -6342

No files matched your search

@@ -0,0 +1,393 @@
# 地磅系统移植 Implementation Plan
> **⚠️ 策略已修订(2026-06-07)**
> 旧版「CRM 式重写 `/api/web/admin/db/*`」代码已删除。
> **权威实施方案**:[`docs/db-migration/00-migration-plan.md`](../../db-migration/00-migration-plan.md)(**原样搬运 + 门户适配**,对齐 bx 思路)
> 端点清单:[`docs/db-migration/01-api-inventory.md`](../../db-migration/01-api-inventory.md)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将原地磅系统五模块(仪表盘、计划、出入库、地磅单、车牌)迁入 shanxia2.0,含 4 张 `db_*` 表、后端联动逻辑与 Fresh Mint 前端。
**Architecture:** 参照 bx 子域模式,新建 `app/api/db/` + `app/services/db/`;主数据方案 A(CRM 下拉写名称文本);地磅保存事务内更新计划并生成出入库单。
**Tech Stack:** Python 3.9 FastAPI · SQLAlchemy · Pydantic v2 · Vue 3 · Element Plus · MySQL 5.7
> **设计文档**: `docs/superpowers/specs/2026-06-07-dibang-migration-design.md`
> **OpenSpec**: `openspec/changes/dibang-migration/`
---
## File Map
### SQL — 新建
| 文件 | 职责 |
|------|------|
| `docs/sql/db_tables.sql` | 四张 `db_*` 表 DDL |
| `docs/sql/db_menu_permissions.sql` | 地磅菜单与按钮权限 |
| `scripts/migrate_dibang_data.py` | 历史数据 int→UUID 迁移 |
### Backend — 新建
| 文件 | 职责 |
|------|------|
| `backend/app/models/db_models.py` | 4 个 SQLAlchemy 模型 |
| `backend/app/services/db/base.py` | DbCrudService 基类 |
| `backend/app/services/db/vehicle.py` | 车牌业务 |
| `backend/app/services/db/plan.py` | 计划业务 + plan_no 生成 |
| `backend/app/services/db/inventory.py` | 出入库业务 |
| `backend/app/services/db/weighing.py` | 地磅单 + 联动事务 |
| `backend/app/services/db/dashboard.py` | 统计聚合 |
| `backend/app/schemas/db/*.py` | Pydantic schemas |
| `backend/app/api/db/__init__.py` | db_router 聚合 |
| `backend/app/api/db/vehicle.py` | 车牌路由 |
| `backend/app/api/db/plan.py` | 计划路由 |
| `backend/app/api/db/inventory.py` | 出入库路由 |
| `backend/app/api/db/weighing.py` | 地磅路由 |
| `backend/app/api/db/dashboard.py` | 仪表盘路由 |
| `backend/app/tasks/db_auto_plan.py` | 按月自动生成计划 |
### Backend — 修改
| 文件 | 变更 |
|------|------|
| `backend/app/api/__init__.py` | export db_router |
| `backend/app/main.py` | include_router(db_router) + import db_models |
### Frontend — 新建
| 文件 | 职责 |
|------|------|
| `web-admin/src/api/db/*.ts` | 5 个 API 模块 |
| `web-admin/src/views/db/vehicle/index.vue` | 车牌 P01 |
| `web-admin/src/views/db/plan/index.vue` | 计划 P02 |
| `web-admin/src/views/db/inventory/index.vue` | 出入库 P02 |
| `web-admin/src/views/db/weighing/index.vue` | 地磅 P02 |
| `web-admin/src/views/db/dashboard/index.vue` | 仪表盘 |
### Frontend — 修改
| 文件 | 变更 |
|------|------|
| `web-admin/src/router/modules/sqxp.ts`(或等价) | 地磅菜单路由 |
### 参考源码(只读)
| 路径 | 用途 |
|------|------|
| `simple-dibang-xiaoshou/backend/app/api/routes/weighing.py` | 联动逻辑 |
| `simple-dibang-xiaoshou/backend/app/api/routes/plans.py` | 计划号生成 |
| `simple-dibang-xiaoshou/art-design-pro/src/views/weighing/index.vue` | 字段与交互 |
| `backend/app/services/bx/base.py` | CRUD 基类模式 |
| `web-admin/src/views/system/order/index.vue` | 列表 UI 模板 |
---
## Task 1: 编写四表 DDL
**Files:**
- Create: `docs/sql/db_tables.sql`
- Modify: `docs/sql/shanxia2.0.sql`(追加四表定义)
- [x] **Step 1: 编写 `db_vehicle` 表**
```sql
CREATE TABLE `db_vehicle` (
`id` varchar(64) NOT NULL COMMENT '主键ID(UUID)',
`vehicle_no` varchar(255) NOT NULL COMMENT '车牌号',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0',
`order_num` int(11) DEFAULT NULL,
`remark` varchar(512) DEFAULT NULL,
`create_by` varchar(64) DEFAULT NULL,
`create_dept_id` varchar(64) NOT NULL DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_by` varchar(64) DEFAULT NULL,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`del_flag` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_vehicle_no` (`vehicle_no`),
KEY `idx_tenant` (`tenant_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='地磅-车牌';
```
- [x] **Step 2: 编写 `db_purchase_plan` 表**(业务字段参照 `dibang.sql` purchase_plans,关联 ID 改 VARCHAR(64),日期改 DATE/DATETIME)
- [x] **Step 3: 编写 `db_weighing_slip` 表**(保留 `is_voided`,关联 ID VARCHAR(64))
- [x] **Step 4: 编写 `db_inbound_outbound` 表**
- [ ] **Step 5: 在 MySQL 执行 DDL 验证**
Run: `mysql -u... shanxia2.0 < docs/sql/db_tables.sql`
Expected: `SHOW TABLES LIKE 'db_%';` 返回 4 行
- [x] **Step 6: 将 DDL 合并进 `docs/sql/shanxia2.0.sql`**
---
## Task 2: SQLAlchemy 模型
**Files:**
- Create: `backend/app/models/db_models.py`
- Modify: `backend/app/main.py`
- [ ] **Step 1: 创建 DbVehicle 模型**
```python
from app.core.database import Base
from sqlalchemy import Column, String, DateTime, Integer, SmallInteger
from datetime import datetime
class DbVehicle(Base):
__tablename__ = 'db_vehicle'
id = Column(String(64), primary_key=True)
vehicle_no = Column(String(255), nullable=False, index=True)
tenant_no = Column(String(64), nullable=False, default='shanxia2.0', index=True)
order_num = Column(Integer)
remark = Column(String(512))
create_by = Column(String(64))
create_dept_id = Column(String(64), nullable=False, default='')
create_time = Column(DateTime, nullable=False, default=datetime.now)
update_by = Column(String(64))
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
del_flag = Column(SmallInteger, nullable=False, default=0)
```
- [ ] **Step 2: 创建 DbPurchasePlan、DbWeighingSlip、DbInboundOutbound**(字段与 DDL 一一对应)
- [ ] **Step 3: 在 main.py 添加** `import app.models.db_models # noqa: F401`
---
## Task 3: DbCrudService 与 db_router 骨架
**Files:**
- Create: `backend/app/services/db/base.py`
- Create: `backend/app/api/db/__init__.py`
- Modify: `backend/app/api/__init__.py`, `backend/app/main.py`
- [ ] **Step 1: 复制并改名 BxCrudService → DbCrudService**(`backend/app/services/bx/base.py`)
- [ ] **Step 2: 创建 db_router 并注册 health 端点**
```python
from fastapi import APIRouter
db_router = APIRouter(tags=['地磅管理'])
@db_router.get('/db/health')
def db_health():
return {'code': 0, 'data': {'status': 'ok', 'module': 'db'}}
```
- [ ] **Step 3: main.py 注册** `app.include_router(db_router, prefix=settings.API_V1_PREFIX)`
- [ ] **Step 4: 启动后端验证** `./start-local.sh`,访问 `/docs` 可见 db health
---
## Task 4: 车牌 CRUD(后端 + 前端)
**Files:**
- Create: `backend/app/schemas/db/vehicle.py`
- Create: `backend/app/services/db/vehicle.py`
- Create: `backend/app/api/db/vehicle.py`
- Create: `web-admin/src/api/db/vehicle.ts`
- Create: `web-admin/src/views/db/vehicle/index.vue`
- [ ] **Step 1: 实现 `_row_to_dict` + list/create/update/soft_delete**(tenant_no 过滤,vehicle_no 唯一校验)
- [ ] **Step 2: 路由** `GET/POST/PUT/DELETE /api/.../db/vehicles`(路径与现有 admin 风格对齐)
- [ ] **Step 3: 前端 API 函数** `fetchVehicleList`, `fetchCreateVehicle`, ...
- [ ] **Step 4: 前端页面** 参照 `web-admin/src/views/system/unit/index.vue`:筛选、工具栏、表格、分页、弹窗
- [ ] **Step 5: 注册路由** `/#/db/vehicle`
- [ ] **Step 6: 手工验证** 新增/编辑/删除车牌
---
## Task 5: 计划管理(后端 + 前端)
**Files:**
- Create: `backend/app/schemas/db/plan.py`
- Create: `backend/app/services/db/plan.py`
- Create: `backend/app/api/db/plan.py`
- Create: `web-admin/src/api/db/plan.ts`
- Create: `web-admin/src/views/db/plan/index.vue`
- [ ] **Step 1: 移植 plan_no 生成逻辑**(参照 `simple-dibang-xiaoshou/backend/app/api/routes/plans.py` 中生成规则)
```python
def generate_plan_no() -> str:
from datetime import datetime
return f"SX-{datetime.now().strftime('%Y%m%d%H%M%S')}{'001'}" # 对齐原系统序号逻辑
```
- [ ] **Step 2: 实现计划 CRUD + 分页筛选**(business_type、execution_status、plan_no、日期范围)
- [ ] **Step 3: 表单支持 auto_generate_enabled / auto_generate_end_month**
- [ ] **Step 4: 前端列表** 参照 order 模板;执行状态用 `<el-tag>`
- [ ] **Step 5: 表单产品/客户下拉** 调用 CRM API,选中写入 `product`、`customer_name` 文本
- [ ] **Step 6: 验证** 创建计划 → 列表可见 → 编辑 → 软删除
---
## Task 6: 出入库管理(后端 + 前端)
**Files:**
- Create: `backend/app/schemas/db/inventory.py`
- Create: `backend/app/services/db/inventory.py`
- Create: `backend/app/api/db/inventory.py`
- Create: `web-admin/src/api/db/inventory.ts`
- Create: `web-admin/src/views/db/inventory/index.vue`
- [ ] **Step 1: 实现出入库 CRUD**(字段映射原 inbound_outbound)
- [ ] **Step 2: 列表筛选** date、partner_company、business_type、status、vehicle_no
- [ ] **Step 3: 前端页面** P02 模板 + 状态 Tag
- [ ] **Step 4: 验证** 独立 CRUD 可用(联动在 Task 7 验收)
---
## Task 7: 地磅单管理与联动(核心)
**Files:**
- Create: `backend/app/schemas/db/weighing.py`
- Create: `backend/app/services/db/weighing.py`
- Create: `backend/app/api/db/weighing.py`
- Create: `web-admin/src/api/db/weighing.ts`
- Create: `web-admin/src/views/db/weighing/index.vue`
- [ ] **Step 1: 阅读原 `weighing.py` 全部保存/作废逻辑**,列出函数清单
- [ ] **Step 2: 实现 `save_weighing_slip` 服务方法(单事务)**
```python
def save_weighing_slip(db, tenant_no, user_id, dept_id, payload):
# 1. 创建或更新 db_weighing_slip
# 2. 若有关联 plan:更新 executed_quantity, execution_status, last_weighing_slip_id
# 3. 创建或更新 db_inbound_outbound,回写 inbound_outbound_id
# 4. db.commit() — 任一步失败 rollback
```
- [ ] **Step 3: 实现 `void_weighing_slip`**:is_voided=1 + 回滚计划量
- [ ] **Step 4: API 路由** list / get / post / put / post:void
- [ ] **Step 5: 前端表单** 毛重/皮重/扣杂/净重/本次量;CRM 产品客户下拉;车牌下拉
- [ ] **Step 6: 全链路验证**
Run 手工流程:创建计划 → 保存地磅单 → 检查计划 executed_quantity 变化 → 检查出入库单生成
Expected: 三表数据一致,单位 kg/t 与原系统行为相同
---
## Task 8: 仪表盘
**Files:**
- Create: `backend/app/services/db/dashboard.py`
- Create: `backend/app/api/db/dashboard.py`
- Create: `web-admin/src/api/db/dashboard.ts`
- Create: `web-admin/src/views/db/dashboard/index.vue`
- [ ] **Step 1: 移植原 dashboard 统计 SQL/聚合**(参照 `simple-dibang-xiaoshou/backend/app/api/routes/dashboard.py`)
- [ ] **Step 2: 返回概览:计划数、地磅单数、出入库数、近期趋势**
- [ ] **Step 3: 前端 dashboard 页渲染卡片 + 图表(ECharts 与项目现有用法一致)**
---
## Task 9: 计划自动生成定时任务
**Files:**
- Create: `backend/app/tasks/db_auto_plan.py`
- Modify: `backend/app/tasks/__init__.py`
- [ ] **Step 1: 移植 `auto_create_monthly_plan.py` 核心查询与复制逻辑**
- [ ] **Step 2: 注册为 APScheduler 每月 1 日 01:30 任务(cron 可配置)**
- [ ] **Step 3: 提供手动触发入口或管理命令用于测试**
Run: 手动调用 task 函数
Expected: 模板计划生成新记录,`auto_generate_last_month` 更新
---
## Task 10: 菜单权限与路由
**Files:**
- Create: `docs/sql/db_menu_permissions.sql`
- Modify: `web-admin/src/router/modules/sqxp.ts`
- [ ] **Step 1: SQL 插入「地磅管理」一级菜单及 5 个子菜单**
- [ ] **Step 2: 按钮权限** db:plan:list/add/edit/remove 等
- [ ] **Step 3: 前端 hash 路由** `#/db/dashboard`, `#/db/plan`, ...
- [ ] **Step 4: 管理员角色绑定菜单**
---
## Task 11: 历史数据迁移
**Files:**
- Create: `scripts/migrate_dibang_data.py`
- [ ] **Step 1: 读取原库四表导出 CSV 或直连 dibang-test**
- [ ] **Step 2: 生成 UUID,写 mapping 表 `{table, old_id, new_id}`**
- [ ] **Step 3: 按依赖顺序导入** plan → inbound_outbound → weighing → vehicle
- [ ] **Step 4: 校验** 记录数一致;抽样检查关联 ID
Run: `python scripts/migrate_dibang_data.py --dry-run`
Expected: 打印将迁移行数,无 orphan 关联
---
## Task 12: 收尾验证
- [ ] **Step 1: 运行 field consistency 检查**(若 db 模块纳入脚本)
- [ ] **Step 2: `./scripts/update-graph.sh all`**
- [ ] **Step 3: 前端 lint** `pnpm lint`(仅 db 相关文件)
- [ ] **Step 4: 对照原系统 5 个核心场景写验收清单并逐项通过**
---
## Spec Coverage Checklist
| Spec 要求 | Task |
|-----------|------|
| 四表 DDL + 公共字段 | Task 1 |
| 历史迁移脚本 | Task 11 |
| 车牌 CRUD | Task 4 |
| 计划 CRUD + plan_no | Task 5 |
| 出入库 CRUD | Task 6 |
| 地磅联动 + 作废 | Task 7 |
| 仪表盘 API | Task 8 |
| auto_generate 定时任务 | Task 9 |
| 前端五页 + Fresh Mint | Task 4–8, 10 |
| CRM 下拉方案 A | Task 5, 7 |
| ecosystem 代码路径 | OpenSpec delta(已写入) |
---
**Plan complete.** 实施时可按 Task 1→12 顺序执行;Task 7 为风险最高项,建议单独评审原 `weighing.py` 后再编码。
**执行方式:**
1. **Subagent-Driven(推荐)** — 每 Task 派发子 agent,任务间 Review
2. **Inline Execution** — 本会话按 Task 批量实现,检查点暂停
如需开始实现,请说明采用哪种方式,或直接说「开始 Task 1」。
@@ -0,0 +1,20 @@
# 地磅三表 CRM 产品 ID + 规格 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
**Goal:** 计划/出入库/地磅单三表存储 `product_id` + `specification`,新建与编辑必选 CRM 产品。
**Architecture:** 方案 B(ID + 名称/规格快照);`resolve_crm_product` 统一校验并回填;级联复制 product 字段。
**Tech Stack:** FastAPI + SQLAlchemy + Vue 3 + Element Plus
---
## 任务清单
- [x] DDL:`docs/sql/20260609_db_product_id_specification.sql` + `db_tables.sql`
- [x] 后端:`product_helpers.py`、ORM、Schema、Mapper、三模块 API、自动计划
- [x] 前端:`DbCrmProductSelect` 三 v-model、三页面表单与 API 类型
- [ ] 执行增量 SQL 到开发库
- [ ] 历史数据回填 `product_id`(可选脚本)
- [ ] 全链路验收:计划 → 出入库 → 地磅
@@ -0,0 +1,222 @@
# 地磅系统移植设计文档
> **日期**: 2026-06-07
> **状态**: 已批准(方案 A)
> **源系统**: `simple-dibang-xiaoshou`(Python FastAPI + art-design-pro)
> **目标系统**: `shanxia2.0`(Python FastAPI + web-admin Fresh Mint)
---
## 1. 背景与目标
### 1.1 背景
CRM、报销(bx)业务已迁入 shanxia2.0 一体化门户。原地磅系统(`docs/sql/dibang.sql`)仍为独立部署,与门户用户体系、主数据割裂。目标是将地磅核心业务迁入本仓库,统一认证、租户与 UI 规范。
### 1.2 移植模块(用户确认)
| 模块 | 原前端路径 | 说明 |
|------|-----------|------|
| 仪表盘 | `views/dashboard/console` | 统计概览 |
| 计划管理 | `views/plans` | 含按月自动生成 |
| 出入库管理 | `views/inventory` | 与地磅单联动 |
| 地磅单管理 | `views/weighing` | 核心业务 |
| 车牌号 | `views/master-data` 车辆 Tab | 仅车辆 CRUD |
### 1.3 不移植
| 原模块 | 替代方案 |
|--------|---------|
| 基础资料-往来单位 | 下拉读 `crm_customer`,存客户名称文本 |
| 基础资料-货物 | 下拉读 `crm_product`,存产品名称文本 |
| 用户管理 / 独立登录 | 门户 `sys_user` + 菜单权限 |
| `cargos` / `companies` 表 | 不建表 |
---
## 2. 设计决策
| 决策点 | 结论 | 理由 |
|--------|------|------|
| 主数据关联 | **方案 A**:仅存名称文本,不做 FK | 与原系统一致;产品/客户名称存在历史不一致 |
| 表前缀 | `db_*` | 与 `bx_*`、`crm_*` 域隔离 |
| 主键 | `VARCHAR(64) UUID` | database-conventions |
| 软删除 | `del_flag`;地磅作废保留 `is_voided` | 作废是业务态,非物理删除 |
| 后端子域 | `app/api/db/`、`app/services/db/` | 参照 bx 子域模式 |
| ORM | SQLAlchemy 同步 Session | 与门户一致(非原地磅 SQLModel async) |
| 鉴权 | 门户 admin JWT + 菜单权限 | 不复用原地磅 auth |
| 租户 | 所有查询/写入带 `tenant_no` | api-conventions |
| 前端 UI | Fresh Mint + P02 复杂列表模板 | 参照 `/#/demo/order` |
| 历史数据 | 独立迁移脚本 int→UUID 映射 | 生产 dibang-test 库需可导入 |
---
## 3. 数据模型
### 3.1 新建表(4 张)
```
db_purchase_plan ← purchase_plans
db_weighing_slip ← weighing_slips
db_inbound_outbound ← inbound_outbound
db_vehicle ← vehicles
```
### 3.2 公共字段(每张业务表末尾)
```sql
`tenant_no` VARCHAR(64) NOT NULL DEFAULT 'shanxia2.0',
`order_num` INT DEFAULT NULL,
`remark` VARCHAR(512) DEFAULT NULL,
`create_by` VARCHAR(64) DEFAULT NULL,
`create_dept_id` VARCHAR(64) NOT NULL DEFAULT '',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_by` VARCHAR(64) DEFAULT NULL,
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`del_flag` SMALLINT NOT NULL DEFAULT 0
```
### 3.3 字段规范改造对照
| 原字段 | 新字段 | 说明 |
|--------|--------|------|
| `id` INT AUTO_INCREMENT | `id` VARCHAR(64) | UUID |
| `is_deleted` | `del_flag` | 0/1 |
| `remarks` | `remark` | 公共字段名 |
| `created_at` VARCHAR | `create_time` DATETIME | 规范时间类型 |
| `purchase_plan_id` INT | `purchase_plan_id` VARCHAR(64) | 关联 ID |
| `inbound_outbound_id` INT | `inbound_outbound_id` VARCHAR(64) | 关联 ID |
| `last_weighing_slip_id` INT | `last_weighing_slip_id` VARCHAR(64) | 关联 ID |
| `plan_date`/`start_date`/`end_date` VARCHAR | DATE 或 DATETIME | 按语义升级 |
| `executed_at` VARCHAR | DATETIME | 执行时间 |
| `is_voided` | 保留 | 地磅单作废标记 |
**业务字段名保持不变**:`plan_no`、`execution_status`、`batch_no`、`vehicle_no`、`cargo_name`、`partner_company` 等。
### 3.4 主数据映射(方案 A)
| 业务字段 | 表单控件 | 存储 |
|---------|---------|------|
| `product` / `cargo_name` | CRM 产品远程搜索下拉 | 选中项的 `product_name` 文本 |
| `customer_name` / `partner_company` / `supplier` / `consignee` | CRM 客户远程搜索下拉 | 选中项的 `customer_name` 文本 |
| `vehicle_no` | `db_vehicle` 下拉或手工输入 | 车牌文本 |
---
## 4. 业务链路(须原样保留)
```
保存地磅单
→ 计算净重/本次量
→ 更新 db_purchase_plan.executed_quantity、execution_status、last_weighing_slip_id
→ 创建或更新 db_inbound_outbound
→ 回写 purchase_plan_id、inbound_outbound_id
作废地磅单 (is_voided=1)
→ 回滚计划已执行量(若已计入)
→ 联动出入库单状态
计划 auto_generate_enabled=1
→ APScheduler 定时任务按月复制生成新计划(原 auto-create-plan 脚本逻辑)
```
**单位约定(勿改)**:计划/出入库 `planned_quantity`、`quantity` 为 **吨(t)**;地磅重量字段为 **kg**;`executed_quantity` 为 **kg**。
---
## 5. 后端架构
```
backend/app/
├── models/db_models.py # DbPurchasePlan, DbWeighingSlip, DbInboundOutbound, DbVehicle
├── schemas/db/ # CreateBody / UpdateBody / Response
├── services/db/
│ ├── base.py # DbCrudService(参照 BxCrudService)
│ ├── plan.py
│ ├── weighing.py # 含联动事务
│ ├── inventory.py
│ ├── vehicle.py
│ └── dashboard.py
├── api/db/
│ ├── __init__.py # db_router
│ ├── plan.py
│ ├── weighing.py
│ ├── inventory.py
│ ├── vehicle.py
│ └── dashboard.py
└── tasks/db_auto_plan.py # 按月自动生成计划
```
**路由注册**:`app/main.py` 增加 `db_router`,前缀 `/api/admin/db/`(或 `/api/web/admin/db/`,与现有 admin 路由一致)。
**CRM 只读依赖**:计划/地磅/出入库表单调用已有 CRM 产品、客户列表 API,不新建 cargos/companies 路由。
---
## 6. 前端架构
```
web-admin/src/
├── api/db/
│ ├── plan.ts
│ ├── weighing.ts
│ ├── inventory.ts
│ ├── vehicle.ts
│ └── dashboard.ts
└── views/db/
├── dashboard/index.vue # 工作台统计
├── plan/index.vue # P02
├── inventory/index.vue # P02
├── weighing/index.vue # P02 + 表单弹窗
└── vehicle/index.vue # P01
```
**菜单**:侧栏新增「地磅管理」一级菜单,子菜单:仪表盘、计划管理、出入库管理、地磅单管理、车牌管理。
**UI 规范**:`docs/web-admin-ui-spec-fresh-mint.md`;状态字段用 `<el-tag>`;按钮带图标;操作列 `fixed="right"`。
---
## 7. 数据迁移
1. 自 `dibang.sql` / 生产库导出四表数据
2. 脚本生成 UUID,维护 `legacy_id → new_id` 映射表
3. 重写 `purchase_plan_id`、`inbound_outbound_id`、`last_weighing_slip_id` 外键
4. 补 `tenant_no='shanxia2.0'` 及公共字段
5. CRM 客户/产品:仅保证下拉可选;历史文本名称不强制对齐 CRM 主数据
---
## 8. 分阶段交付
| 阶段 | 内容 |
|------|------|
| P1 SQL | 4 张 `db_*` DDL 写入 `docs/sql/` 并合并 `shanxia2.0.sql` |
| P2 后端 | vehicle → plan → inventory → weighing → dashboard → 定时任务 |
| P3 前端 | 同上顺序 + CRM 下拉 |
| P4 迁移 | 历史数据脚本 + 菜单权限 SQL |
| P5 验收 | 计划→地磅→出入库全链路 + 看板 |
---
## 9. 风险
| 风险 | 缓解 |
|------|------|
| 产品/客户名称与 CRM 不一致 | 方案 A 存文本;下拉仅辅助录入 |
| int→UUID 关联断裂 | 迁移脚本维护映射表并校验 |
| 联动逻辑回归 | 对照原 `weighing.py` 逐场景验收 |
| ecosystem-landscape 冲突 | OpenSpec 更新地磅归属为本仓库 |
---
## 10. 参考源码
| 用途 | 路径 |
|------|------|
| 原 DDL | `docs/sql/dibang.sql` |
| 原后端 | `simple-dibang-xiaoshou/backend/app/` |
| 原前端 | `simple-dibang-xiaoshou/art-design-pro/src/views/` |
| bx 移植参考 | `backend/app/api/bx/`、`backend/app/services/bx/` |
| CRM 产品/客户 API | `backend/app/api/crm.py` |
| UI 模板 | `web-admin/src/views/system/order/index.vue` |
@@ -0,0 +1,27 @@
# 地磅三表 CRM 产品 ID + 规格 — 设计文档
> **日期**: 2026-06-09
> **状态**: 已批准(方案 B + 新建必选 CRM 产品)
> **前置**: `docs/superpowers/specs/2026-06-07-dibang-migration-design.md` 方案 A 升级
## 目标
计划、出入库、地磅单三表均存储 `product_id`(`crm_product.id`)与 `specification`(规格快照,来源 `crm_product.model`),新建/编辑时必须从 CRM 选择产品,禁止手输纯文本。
## 字段
| 表 | 新增/补齐 | 保留快照 |
|---|---|---|
| `db_purchase_plan` | `product_id` | `product`、`specification` |
| `db_inbound_outbound` | `product_id`、`specification` | `product` |
| `db_weighing_slip` | `product_id`、`specification` | `cargo_name` |
不建 DB 外键;服务端按 `tenant_no` 校验 CRM 产品存在且 `status=on`,并用 CRM 数据回填名称与规格。
## 级联
计划 → 出入库 → 地磅:复制 `product_id`、`product`/`cargo_name`、`specification`。自动按月生成计划同步复制。
## 历史数据
已有行 `product_id` 可为 NULL;编辑保存时需重新选择 CRM 产品后方可提交。