Files
bj_power/项目现状说明.md
T
SunYF 572fa975bc feat: 完成WMS系统权限体系与业务功能迭代
本提交完成了WMS系统的多维度优化升级:
1. 新增RBAC权限系统,支持角色/菜单/按钮三级权限管控
2. 重构库存盘点、物料管理、区域维护等模块的查询筛选与展示逻辑
3. 优化入库/出库/质检等业务流程,完善数据冗余与业务闭环
4. 移除旧装箱表,将装箱逻辑合并到出库主表
5. 新增前端权限判断工具、导出工具与端到端测试用例
6. 补充完善各类注释与数据库字段说明
2026-09-03 14:20:37 +08:00

262 lines
18 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.
# 北京电力 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.vuemode=zone | 区域维护 |
| `/material` | BaseData.vuemode=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` 再 buildWindows EPERM),无脚本化 |
| Git 状态 | **所有改动均未提交 git**(用户从未要求 commit |
---
## 十一、给重新规划者的建议输入
重新规划时建议优先处理(按依赖顺序):
1. **先定"列表规范"标准件**:统一筛选区(模糊/下拉/日期)、统一列(ID + 创建时间 `yyyy-MM-dd HH:mm:ss`)、统一分页 + 导出、创建时间默认近 3 个月。可抽为**通用列表组件/配置**,一次性解决物料档案、区域维护、库存查询等多处问题。
2. **权限数据化**:建角色表 + 菜单表 + 按钮权限表,取代硬编码 map;前端菜单与按钮权限改为按接口下发。
3. **库存查询重构**:明确"库存=按物料聚合查数量;SN/批次=子表按需下钻",同时解决性能与职责边界(问题八)。
4. **质量检验闭环**:补齐检验说明/不合格说明字段、检验记录查询与导出(问题七)。
5. **盘点能力**:支持选择盘点范围(全库/区域/物料),并新增物料分布图(问题六)。
6. **职责边界澄清**:半成品/成品 与 出库管理 的关系(问题五)。
**务必遵守**:第八节的 5 条强约束(统一主表、单一职责+分页、数据量意识、列表规范、先方案后改)。