本提交完成了WMS系统的多维度优化升级: 1. 新增RBAC权限系统,支持角色/菜单/按钮三级权限管控 2. 重构库存盘点、物料管理、区域维护等模块的查询筛选与展示逻辑 3. 优化入库/出库/质检等业务流程,完善数据冗余与业务闭环 4. 移除旧装箱表,将装箱逻辑合并到出库主表 5. 新增前端权限判断工具、导出工具与端到端测试用例 6. 补充完善各类注释与数据库字段说明
262 lines
18 KiB
Markdown
262 lines
18 KiB
Markdown
# 北京电力 WMS 项目 · 现状说明(供重新规划)
|
||
|
||
> 生成时间:2026-09-03
|
||
> 用途:作为「重新规划」的输入材料,供其他 AI 直接阅读并制定方案
|
||
> 核实方式:全部内容来自实际代码读取(非记忆/推测),关键结论已标注代码位置
|
||
|
||
---
|
||
|
||
## 一、系统全景
|
||
|
||
根仓库 `D:\hardman\bj_power` 下共有 **5 个子系统**:
|
||
|
||
| 子系统 | 端口 | 技术 | 职责 | 备注 |
|
||
|---|---|---|---|---|
|
||
| `bj_power_wms` | **8890** | Go + go-zero + ent | **WMS 后端**(全部业务逻辑 + PostgreSQL) | 核心系统 |
|
||
| `bj_power_wms_client` | **8891** | Go(仅 config/proxy) | **库房客户端**:静态托管 + `/api` 反向代理 | **无业务逻辑**,转发到 8890 |
|
||
| `bj_power_mes` | — | Go + ent + frontend | MES 系统(工单、BOM、台账来源) | 独立服务 |
|
||
| `bj_power_workstation` | — | Go + frontend + SQLite | 工位终端 | 独立 DB |
|
||
| `bj_power_dashboard` | — | Vue3 + TS + Vite | 大屏展示(独立构建产物 dist) | 免登录 |
|
||
|
||
**关键架构事实(已核实)**:
|
||
- `bj_power_wms_client`(8891)**不是**纯前端壳,它是一个 Go 服务,但 `internal/` 下**只有 `config` 和 `proxy` 两个包**,无 handler、无 ent。
|
||
- 其配置 `etc/bj_power_wms_client.yaml` 明确:`WmsAddr: http://127.0.0.1:8890`,请求转发时注入 `X-API-TOKEN: Hardman_2026`。
|
||
- 因此:**前端页面(8891)看到的 `/api/*` 实际全部由 8890 的 WMS 后端处理**。
|
||
- 前端源码在 `bj_power_wms_client/frontend/`,构建产物输出到 `bj_power_wms_client/web/static`。
|
||
- `bj_power_wms`(8890)**不含 `go:embed`**,不内嵌前端,前端由 8891 单独提供。
|
||
|
||
> 结论:用户口中的「wms 和 wms 客户端」= 8890(业务后端)+ 8891(客户端壳)。**所有功能/权限改造实际上都落在 8890。**
|
||
|
||
---
|
||
|
||
## 二、技术栈
|
||
|
||
**后端(bj_power_wms)**
|
||
- Go + `go-zero`(rest 路由)+ `ent` ORM
|
||
- PostgreSQL(`bj_power_wms`,账号 `postgres/postgres`)
|
||
- JWT 鉴权,Header:`Authorization: Bearer {token}`
|
||
- 统一响应包裹 `{code, message, data}`,**业务数据一律在 `data` 层**
|
||
- ent 代码生成:`go run -tags entgenerate ./tools`(build tag `entgenerate`,入口 `tools/generate.go`)
|
||
|
||
**前端(bj_power_wms_client/frontend)**
|
||
- Vue3(`<script setup>`)+ Element Plus + Vite
|
||
- Axios 封装 `src/utils/request.js`:`baseURL: '/api'`,响应拦截器统一拆 `data`、统一错误提示、401 跳登录
|
||
- 路由:`src/router/index.js`(history 模式)
|
||
- 布局/菜单:`src/layouts/MainLayout.vue`
|
||
- 页面:`src/pages/`(14 个 .vue)
|
||
- 页内帮助:每个页面挂 `<PageHelp>`,文案集中在 `src/help.js`
|
||
|
||
**运行约定**
|
||
- 服务端口 8890(后端)/ 8891(客户端);登录 `admin / 123456`
|
||
- 前端重建套路(避 Windows EPERM):
|
||
`mv web/static web/static_bak_<时间戳>` → `mkdir web/static` → `npm run build` → `rm -rf web/static_bak_*`
|
||
- **注意**:8890 与 8891 不能同时被两个实例占用;换调试方式前需先结束旧进程。
|
||
|
||
---
|
||
|
||
## 三、数据模型(ent schema,共 13 张业务表)
|
||
|
||
`bj_power_wms/schema/` 下 14 个文件(`mixin.go` 仅提供 `nowUnix()`,非共享 mixin)。
|
||
**所有表的 `created_at` 均为 `Int64` unix 秒时间戳**(不是 `time.Time`),多数表另有 `updated_at`。
|
||
|
||
### 入库域
|
||
- **inbound_order(入库单·主表)**:`inbound_no`(唯一) `inbound_type`(purchase/semi/finished/return) `material_code` `material_name` `manage_mode`(1批次/2SN) `batch_no` `zone_code` `quantity` `operator` `remark` `created_at`
|
||
- **inbound_detail(入库明细·子表)**:`inbound_no` `sn_code` `batch_no` `material_code` `quantity` `created_at`
|
||
- 结构件:一批一条(batch_no);精密件:**每个 SN 一条明细**(可能上万条)
|
||
|
||
### 出库域
|
||
- **outbound_order(出库单·统一主表)**:`outbound_no` `outbound_type`(**workorder 备料 / general 通用**) `order_no` `material_code` `material_name` `manage_mode` `batch_no` `zone_code` `quantity` `operator` `reviewer` `target_dock` `remark` **`box_no` `box_sn_list` `contract_no`(装箱信息)** `created_at`
|
||
- **outbound_detail(出库明细·子表)**:`outbound_no` `sn_code` `batch_no` `material_code` `quantity` `order_no` `created_at`
|
||
|
||
### 库存域
|
||
- **inventory(统一库存表)**:`manage_mode` `material_code` `material_name` `batch_no` `sn_code` `quantity` `locked_qty` `quality_status` `zone_code` `status` `production_date` `supplier` `inbound_no` `remark` `created_at` `updated_at`
|
||
- 结构件:按批次一条记录;精密件:**每个 SN 一条记录**
|
||
- **inventory_lock(库存锁定)**:`target_type` `target_id` `material_code` `locked_qty` `order_no` `status` `expired_at` `remark` `created_at` `updated_at`
|
||
|
||
### 基础数据
|
||
- **material(物料档案)**:`code`(唯一) `name` `short_name` `spec` `unit` `description` `manage_mode` `is_batch_managed` `is_serial_managed` `template_code` `created_at` `updated_at`
|
||
- **zone(区域)**:`zone_code` `zone_name` `description` `status` `created_at` `updated_at`
|
||
- **user(用户)**:`username` `password`(bcrypt) `real_name` `role` `dept` `phone` `is_active` `last_login_at` `created_at` `updated_at`
|
||
|
||
### 业务扩展
|
||
- **inspection_record(检验记录)**:`target_type` `target_id` `material_code` `inspection_type` `status` `result_value` `inspector` `remark` `created_at` `updated_at`
|
||
- **order_material_ledger(工单物料台账)**:`order_no` `material_code` `material_name` `total_qty` `out_qty` `status` `remark` `created_at` `updated_at`
|
||
- **semi_finished(半成品/成品)**:`sn` `material_code` `completed_process` `quantity` `zone_code` `status` `order_no` `created_at` `updated_at`
|
||
- **stocktake(盘点,`stocktake.go` 内含「盘点单 + 盘点明细」两个 schema)**:盘点单 `stocktake_no` `status` `operator` `total_targets` `created_at` `finished_at`;盘点明细 `target_type` `target_id` `material_code` `zone_code` `book_qty` `scanned_qty` `diff_qty` `counted` `updated_at`
|
||
|
||
---
|
||
|
||
## 四、后端 API 全清单(来自 `internal/handler/routes.go`)
|
||
|
||
| 模块 | 接口 |
|
||
|---|---|
|
||
| 健康/登录 | `GET /api/health`、`POST /api/auth/login` |
|
||
| 用户 | `POST /user/change-password`、`GET /user/info`、`GET /user/list`、`POST /user/create`、`POST /user/update`、`POST /user/delete` |
|
||
| 物料 | `POST /material/create`、`POST /material/update`、`GET /material/query`、`GET /material/list`、`GET /material/detail` |
|
||
| 入库 | `POST /inbound/create`、`GET /inbound/query`、`GET /inbound/details`(**明细分页,新增**)、`GET /inbound/export`、`POST /inbound/batch-excel` |
|
||
| 出库 | `POST /outbound/create`(备料)、`POST /outbound/general`(通用)、`GET /outbound/query`、`GET /outbound/export` |
|
||
| 库存 | `GET /stock/query`、`GET /stock/zone-summary`、`POST /stock/lock`、`POST /stock/unlock`、`POST /stock/deduct`、`POST /stock/check` |
|
||
| 检验 | `POST /inspection/create`、`POST /inspection/batch-flip`、`GET /inspection/query` |
|
||
| 区域 | `POST /zone/create`、`POST /zone/update`、`GET /zone/list`、`GET /zone/picker` |
|
||
| 盘点 | `POST /stocktake/start`、`POST /stocktake/record`、`POST /stocktake/finish`、`GET /stocktake/query` |
|
||
| 半成品 | `POST /semi/inbound`、`POST /semi/outbound`、`GET /semi/query` |
|
||
| 台账/工单 | `GET /api/ledger/query`、`GET /api/orders` |
|
||
| 大屏 | `GET /api/display/overview`(**免登录**) |
|
||
| 内部 API | `/api/internal/*`:`ledger/sync`、`stock/{check,lock,unlock,deduct,query}`、`semi/{inbound,outbound}`(用 `X-API-TOKEN` 保护,供 MES/工位调用) |
|
||
|
||
**接口风格现状**:均为「按业务模块自由命名」,**没有统一的 RESTful 资源风格、没有统一分页/排序/过滤参数规范、没有 OpenAPI 文档**。
|
||
|
||
---
|
||
|
||
## 五、前端页面 / 路由 / 菜单
|
||
|
||
**页面(`src/pages/`,14 个)**,路由在 `router/index.js`:
|
||
|
||
| 路由 | 页面 | 说明 |
|
||
|---|---|---|
|
||
| `/login` | Login.vue | 登录 |
|
||
| `/display` | Display.vue | 免登录大屏(全屏无侧栏) |
|
||
| `/` | Dashboard.vue | 工作台 |
|
||
| `/inbound` | Inbound.vue | 入库管理(**列表页**:筛选+分页+导出+「批量入库」按钮) |
|
||
| `/inbound-create` | InboundCreate.vue | 批量入库(**新 tab 打开,不进侧栏**) |
|
||
| `/outbound` | Outbound.vue | 出库管理(tab:工单备料出库 MES / 通用出库) |
|
||
| `/inventory` | Inventory.vue | 库存查询 |
|
||
| `/inspection` | Inspection.vue | 质量检验 |
|
||
| `/stocktake` | Stocktake.vue | 库存盘点 |
|
||
| `/semi` | Semi.vue | 半成品/成品(**3 个 tab**:入库 / 查询 / 出库) |
|
||
| `/ledger` | Ledger.vue | 备料台账 |
|
||
| `/zone` | BaseData.vue(mode=zone) | 区域维护 |
|
||
| `/material` | BaseData.vue(mode=material) | 物料档案 |
|
||
| `/change-password` | ChangePassword.vue | 修改密码 |
|
||
| `/users` | UserManage.vue | 账号管理 |
|
||
|
||
**侧栏菜单**:硬编码在 `MainLayout.vue` 的 `defaultMenus` 数组(11 项),每项带 `code`(如 `inbound:view`、`user:manage`),按后端返回的 `permissionCodes` 过滤显示。
|
||
另:每个页面挂 `<PageHelp>` 组件,帮助文案统一在 `src/help.js`。
|
||
|
||
---
|
||
|
||
## 六、核心业务流(现状)
|
||
|
||
1. **入库**:`InboundCreate.vue` 录单 → `POST /inbound/create`(事务:入库单 + inventory + 入库明细 原子提交)。结构件按批次;精密件按 SN 逐件(SN 全局查重)。支持 Excel 批量导入(`POST /inbound/batch-excel`,**事务化全成功或全失败**,支持 batch/SN 双模板)。
|
||
2. **出库**:统一主表 `outbound_order`,用 `outbound_type` 区分:
|
||
- `workorder`(工单备料出库):受 `order_material_ledger` 台账约束,累计出库 ≤ BOM 需求;MES 不可达时 `/api/orders` 优雅降级返回空列表。
|
||
- `general`(通用出库):退料/样品/报废/发货等不挂工单场景。
|
||
- 装箱信息(`box_no`/`box_sn_list`/`contract_no`)直接落在出库主表,未单独建表。
|
||
3. **库存查询**:直接查 `inventory` 表。**结构件按批次一条记录、精密件每个 SN 一条记录**。
|
||
4. **质量检验**:`/api/inspection/*`,记录落在 `inspection_record`。
|
||
5. **盘点**:`stocktake` 发起 → 记录 → 完成(含「差异写回库存」)。
|
||
6. **闭环跳转**:库存页点「入库单号」→ 跳 `/inbound?inboundNo=xxx` 自动预填查询。
|
||
|
||
---
|
||
|
||
## 七、权限模型现状(对应问题一)
|
||
|
||
**结论:权限是硬编码的,没有数据化。**
|
||
|
||
已核实的事实:
|
||
- `internal/handler/rbac.go` 第 20-35 行:角色→权限码映射写死在 Go 代码里的 `rolePermissions` map,仅 **admin / operator / inspector** 三个角色。
|
||
- `roleOrDefault()` 把未知角色统一回退为 `operator`。
|
||
- `user` 表只有字符串字段 `role`,**没有角色表、没有菜单表、没有按钮权限表**。
|
||
- `GET /user/info` 返回 `permissionCodes`(由 role 经 map 映射得出)。
|
||
- 后端权限校验只有一个 `requireAdmin()`,硬编码判断 `role == "admin"`(仅用于账号管理类接口)。
|
||
- 前端菜单硬编码在 `MainLayout.vue` 的 `defaultMenus`(11 项,各带 `code`),按 `permissionCodes` 做**显示/隐藏过滤**。
|
||
- **不存在**:角色维护页面、角色增删改接口、菜单配置管理、**按钮级权限**。
|
||
|
||
> 遗留问题:`rolePermissions` 中仍保留已废弃的 `"package:view"`(装箱功能已合并进出库主表),属脏数据。
|
||
|
||
---
|
||
|
||
## 八、已确立的设计约定(用户强约束,重新规划时务必遵守)
|
||
|
||
1. **统一主表原则**:同类业务必须用「一张主表 + 类型字段区分」,禁止为新增功能另建独立表。
|
||
- 正例:出库用 `outbound_type`(workorder/general) 区分;装箱字段直接落主表。
|
||
- 反例:曾因入库拆多表导致无法全量导出,被明确批评。
|
||
- 新增能力优先「主表加字段」+ 配套全量导出。
|
||
|
||
2. **单一职责 + 分页(用户称"互联网思维")**:每个页面只做一件事。
|
||
- 录入/创建页**不得内嵌查询列表**;查询/列表必须是独立页面(带查询条件、分页、导出)。
|
||
- 已落地范例:入库管理 `Inbound.vue`(列表页)+ 批量入库 `InboundCreate.vue`(新 tab 打开的录入页,不进侧栏)。
|
||
|
||
3. **数据量意识**:任何列表/明细/导出必须先估算数据量,禁止无脑全量加载。
|
||
- 列表接口每行只带 count,**禁止**在列表里对每行加载全部子明细(精密件单可上万 SN)。
|
||
- 明细必须懒加载 + 分页(如 `GET /inbound/details`,默认 50/页)。
|
||
|
||
4. **列表规范(用户最新要求,来自《问题记录.md》)**:
|
||
- 搜索条件要**合适的方式**:该模糊的模糊、该下拉的下拉;**去掉笼统的"关键字"搜索**。
|
||
- 所有列表增加**创建时间**字段,展示格式 `yyyy-MM-dd HH:mm:ss`;搜索条件增加**创建时间筛选,默认近 3 个月**。
|
||
- 所有列表增加**主键 ID**。
|
||
|
||
5. **协作流程**:非琐碎改动必须先给详细方案并给出推荐选项,用户确认后再动手。
|
||
|
||
---
|
||
|
||
## 九、当前待办问题(《问题记录.md》8 项 + 代码核实结果)
|
||
|
||
> 以下每条均已对照实际代码核实,便于规划时直接定位。
|
||
|
||
### 一、WMS 与 WMS 客户端
|
||
1. **缺少角色维护** → 核实:权限硬编码在 `rbac.go` 的 map,无角色表、无维护接口、无页面。
|
||
2. **缺少角色菜单按钮权限** → 核实:菜单硬编码在 `MainLayout.vue`,仅做显示过滤;**无按钮级权限**。
|
||
|
||
### 二、物料档案(`/material` → `BaseData.vue` mode=material)
|
||
1. **搜索条件不足** → 核实:目前**只有一个"关键字"输入框**(`BaseData.vue` 第 181/238 行),未按物料编码/名称/规格/类型拆分。
|
||
2. **列表缺创建时间** → 核实:`material` 表有 `created_at`(**Int64 unix 秒**),但前端列表未展示、无时间筛选。
|
||
3. **列表缺主键 ID** → `material` 表主键 id 存在,前端未展示。
|
||
|
||
### 三、区域维护(`/zone` → `BaseData.vue` mode=zone)
|
||
同上三点:`zone` 表有 `created_at`/`updated_at`,前端同样只有"关键字"搜索、无创建时间、无 ID 列。
|
||
|
||
### 四、备料台账(`/ledger`)
|
||
1. **需要填充假数据**以便查看展示效果(表:`order_material_ledger`)。
|
||
|
||
### 五、半成品/成品(`/semi` → `Semi.vue`)
|
||
1. **内嵌 tab 页叫什么** → 核实:`Semi.vue` 现有 **3 个 tab**:「半成品/成品入库」「半成品/成品查询」「半成品出库」(Element Plus 组件为 `el-tabs`,通称"标签页/选项卡")。
|
||
2. **与菜单「出库管理」的区别与关系** → 需重新规划明确职责边界。
|
||
|
||
### 六、库存盘点(`/stocktake`)
|
||
1. **发起盘点是否锁全部库存?能否选择盘点全库 / 某个物料?** → 需设计(当前 `stocktake` 表结构支持按 `target_type`/`material_code`/`zone_code` 记录明细,但前端流程未暴露选择能力)。
|
||
2. **缺少物料分布图**:无法查看"哪个物料、在哪个货架、每个多少、一共多少"。
|
||
|
||
### 七、质量检验(`/inspection`)
|
||
用户评价"设计胡闹、无法闭环"。核实:
|
||
- `Inspection.vue` 中**导出相关代码数量为 0**(确实不能导出)。
|
||
- 现有字段仅:目标类型、已选清单、检验员、检验结论。
|
||
- **缺失**:检测说明、不合格说明、检验记录闭环、导出能力。
|
||
- 表 `inspection_record` 现有字段:`target_type` `target_id` `material_code` `inspection_type` `status` `result_value` `inspector` `remark` `created_at` `updated_at`。
|
||
|
||
### 八、库存查询(`/inventory`)
|
||
用户评价"设计乱七八糟"。核实到两处关键:
|
||
1. **SN 被直接暴露在主列表**:`Inventory.vue` 第 73 行 `return row.manageMode === 2 ? row.snCode : row.batchNo`,即 `inventory` 表**每条 SN 一行**直接展示。用户主张:库存查询应**按物料聚合只显示数量**,SN 属子表明细,应点击查看。
|
||
2. **职责边界未定义**:「同批次」与「库存查询」如何分工?用户理解——**库存查询 = 查物料数量的地方**;**出入库管理 = 查自己批次的地方**。需重新规划确认。
|
||
|
||
---
|
||
|
||
## 十、已知技术债与风险
|
||
|
||
| 项 | 说明 |
|
||
|---|---|
|
||
| 权限硬编码 | 角色/菜单/权限码全在代码里,加角色或改权限需改代码重新部署 |
|
||
| 废弃权限码残留 | `rolePermissions` 中仍有 `package:view` |
|
||
| `created_at` 类型 | 全库为 **Int64 unix 秒**,前端 `fmtTime` 需 `*1000`;统一 `yyyy-MM-dd HH:mm:ss` 展示需统一处理 |
|
||
| 前端无 TypeScript | 全 JS,无类型约束 |
|
||
| 无统一列表组件 | 各页面各自实现筛选/表格/分页/导出,风格与能力不一致(这正是"列表乱"的根因) |
|
||
| 无统一 API 层 | 页面内直接 `request.get('/xxx')`,路径散落 |
|
||
| 无接口文档 | 无 OpenAPI;接口命名无统一 RESTful 规范;无统一错误码 |
|
||
| 库存查询性能 | 精密件每 SN 一条 inventory 记录,量大时列表与渲染压力大 |
|
||
| 前端构建 | 需手动 `mv` 备份 `web/static` 再 build(Windows EPERM),无脚本化 |
|
||
| Git 状态 | **所有改动均未提交 git**(用户从未要求 commit) |
|
||
|
||
---
|
||
|
||
## 十一、给重新规划者的建议输入
|
||
|
||
重新规划时建议优先处理(按依赖顺序):
|
||
1. **先定"列表规范"标准件**:统一筛选区(模糊/下拉/日期)、统一列(ID + 创建时间 `yyyy-MM-dd HH:mm:ss`)、统一分页 + 导出、创建时间默认近 3 个月。可抽为**通用列表组件/配置**,一次性解决物料档案、区域维护、库存查询等多处问题。
|
||
2. **权限数据化**:建角色表 + 菜单表 + 按钮权限表,取代硬编码 map;前端菜单与按钮权限改为按接口下发。
|
||
3. **库存查询重构**:明确"库存=按物料聚合查数量;SN/批次=子表按需下钻",同时解决性能与职责边界(问题八)。
|
||
4. **质量检验闭环**:补齐检验说明/不合格说明字段、检验记录查询与导出(问题七)。
|
||
5. **盘点能力**:支持选择盘点范围(全库/区域/物料),并新增物料分布图(问题六)。
|
||
6. **职责边界澄清**:半成品/成品 与 出库管理 的关系(问题五)。
|
||
|
||
**务必遵守**:第八节的 5 条强约束(统一主表、单一职责+分页、数据量意识、列表规范、先方案后改)。
|