盘点单 CRUD 后端记录
日期:2026-05-18
这次后端做了什么
这次盘点单后端已经把增删改查主接口补齐并接入到公共路由里,前端现在调用的是一套真实接口,不是本地假数据。
当前后端已经覆盖:
- 新建盘点单
- 编辑盘点单
- 获取盘点单列表
- 获取盘点单详情
- 删除盘点单
后端项目位置
后端项目路径:
/home/gl/桌面/rfid/smart-doc-vault-rfid
这次盘点单 CRUD 相关核心文件:
router/system/common.goapi/system/inventory_plan.goservice/system/inventory_plan.gomodel/system/request/inventory_plan.gomodel/system/response/inventory_plan.goinitialize/gorm.go
1. 接口路由
注册位置:
router/system/common.go
当前盘点单接口统一挂在:
POST /inventoryPlan/createPOST /inventoryPlan/updatePOST /inventoryPlan/getListPOST /inventoryPlan/getDetailPOST /inventoryPlan/delete
这意味着前端所有盘点单 CRUD 请求现在都是走同一套路由前缀。
2. API 层职责
文件:
api/system/inventory_plan.go
这个文件主要负责三件事:
- 绑定请求参数
- 做基础校验
- 调 service 返回统一响应
Create
这个接口用于新建盘点单。
前端发送:
nameexecutorIdmoderulesarchiveIdsremark
后端会:
- 校验必填字段
- 从登录态里取当前用户 ID 作为创建人
- 调
inventoryPlanService.Create - 成功后返回盘点单基础信息
Update
这个接口用于编辑盘点单。
前端发送:
idnameexecutorIdmoderulesarchiveIdsremark
后端会:
- 校验参数
- 调
inventoryPlanService.Update
GetList
这个接口用于盘点单列表查询。
前端可传:
pagepageSizesearchmodestatusmyOnly
如果 myOnly = true,后端会从登录态里取当前用户 ID,用来筛选“只看我的盘点单”。
GetDetail
这个接口用于获取盘点单详情。
前端发送:
id
后端返回:
- 盘点单基础信息
- 执行人信息
- 规则配置
- 档案 ID 列表
- 档案明细列表
Delete
这个接口用于删除盘点单。
前端发送:
id
后端校验通过后,调用 service 删除主表和子表数据。
3. 请求结构
文件:
model/system/request/inventory_plan.go
当前请求模型包括:
CreateInventoryPlanReqUpdateInventoryPlanReqInventoryPlanListReqDeleteInventoryPlanReqInventoryPlanIDReq
规则对象字段:
manualMarkmanualLocationallowAddfilterBorrowedfilterDestroyed
这里的设计很清楚:
- 新增和编辑的字段结构基本一致
- 编辑只比新增多一个
id
所以前端共用一个新增/编辑页是合理的,后端接口模型也是按这个思路配合的。
4. 响应结构
文件:
model/system/response/inventory_plan.go
当前响应模型包括:
InventoryPlanCreateRespInventoryPlanRulesRespInventoryPlanArchiveItemRespInventoryPlanDetailRespInventoryPlanListItemRespInventoryPlanStatusStatRespInventoryPlanListResp
创建 / 编辑成功返回
会返回:
idcodenamestatusarchiveCount
详情返回
会返回:
idcodenameexecutorIdexecutorNamemodestatusremarkarchiveCountrulesarchiveIdsitems
这也是前端编辑页能直接回填档案范围和规则的基础。
列表返回
会返回:
listtotalstats
其中 stats 用于前端顶部 tab 计数展示。
5. Service 层核心业务
文件:
service/system/inventory_plan.go
真正的业务规则基本都在这里。
Create 的业务逻辑
Create 会做这些事情:
- 检查是否选择了档案范围
- 校验盘点模式是否合法,只允许:
scopeprecise
- 校验执行人是否存在
- 对
archiveIds去重 - 查询档案并预加载位置信息、层架、库区
- 检查档案是否真的都存在
- 生成盘点单编号
- 保存盘点单主表
- 为每个档案生成一条盘点单明细快照
这里的关键点是:
- 盘点单不是只保存档案 ID
- 还会把档案名称、编号、位置、状态、RFID 等信息做快照落到明细表
这样后续即使档案本体信息变了,盘点单里的当时数据也还能保留。
Update 的业务逻辑
Update 的核心规则是:
- 只有
pending状态允许编辑
后端具体做法:
- 校验盘点单 ID
- 检查盘点单是否存在
- 检查盘点单状态是否为
pending - 校验执行人
- 重新校验档案范围
- 重新生成明细快照
- 更新主表
- 删除旧的明细
- 插入新的明细
所以编辑不是“局部改一点字段”,而是按最新选中的档案范围,把整张盘点单和它的子项重新整理一遍。
GetDetail 的业务逻辑
GetDetail 会:
- 查盘点单主表
- 预加载执行人
- 预加载明细项
- 把明细项组装成前端要的
items - 同时返回
archiveIds
这个接口对前端很重要,因为:
- 编辑页要靠它回填
- 详情弹窗也要靠它展示当前盘点单包含的档案数据
GetList 的业务逻辑
GetList 做了两部分事情:
- 先根据搜索条件统计各状态数量
- 再查当前列表数据
它支持的过滤条件有:
- 搜索名称或编号
- 按模式筛选
- 按状态筛选
myOnly筛选执行人
当前列表项里:
creator默认给"系统",如果查到创建人则回真实用户名assignee来自执行人date用创建时间格式化成YYYY-MM-DD
当前 completedItems / surplus / loss 这些字段还没有在这里做真实计算,先返回默认值 0。
Delete 的业务逻辑
删除规则现在是:
- 执行中的盘点单不能删
也就是:
- 如果状态是
executing,直接返回错误:执行中的盘点单请先取消,变为已关闭后再删除
通过校验后,后端会在事务里:
- 先删
InventoryPlanItem - 再删
InventoryPlan
6. 数据快照设计
这次盘点单后端有一个很重要的设计点:
- 明细表保存的是“盘点当下的档案快照”
当前快照字段包括:
- 档案 ID
- 档案名称
- 档案编号
- 位置 ID
- 位置名称
- 状态
- RFID
- 层架信息
- 库区信息
这个设计的意义是:
- 详情页不需要每次都回查档案实时表再拼
- 盘点单保存的是业务发生当时的上下文
- 更适合后续做盘点追溯
7. 盘点详情字段补充
盘点详情接口现在有两个字段要重点记住:
locationNameinventoryResult
locationName
locationName 是详情里展示档案具体位置用的字段。
现在后端不会只返回库房或库区这种不完整位置,会尽量补齐到档案所在层位:
- 普通密集架:
库房路径/库区/架体/左侧或右侧/几节/几层 - 如果档案有格信息:继续补到
几格 - 回转柜:
库房路径/库区/几层/几格
实现位置:
service/system/inventory_plan.gobuildInventoryArchiveLocation
处理逻辑:
- 新建盘点单时,明细快照里的
LocationNameSnapshot会保存补齐后的具体位置。 - 编辑盘点单时,重新生成明细快照,也会保存补齐后的具体位置。
- 获取详情时,后端会按
ArchiveID再查一次档案当前位置。 - 如果档案当前库位能拼出完整位置,详情返回当前完整位置。
- 如果拼不出来,才回退到明细快照里的
LocationNameSnapshot。
这样旧盘点单即使之前快照位置不完整,打开详情时也能尽量显示当前档案的完整位置。
inventoryResult
inventoryResult 是盘点结果字段,详情里的状态筛选和状态标签都应该看这个字段,不应该再拿档案本身的 status 当盘点结果。
当前支持的业务结果:
正常盘亏位置变化盘盈未知未盘点
当前创建或编辑盘点单时,所有明细默认写入:
inventoryResult = 未盘点
后续进入真正执行盘点流程后,后台应该根据扫描结果更新每条明细的 inventoryResult。
判断方向应该是:
- 扫到并且位置一致:
正常 - 应该在本盘点单里但没有扫到:
盘亏 - 扫到了但位置和原位置不一致:
位置变化 - 扫到了但不属于本盘点单范围:
盘盈 - 数据无法明确归类或设备返回异常:
未知 - 还没有参与扫描判断:
未盘点
也就是说:
status表示档案自身状态,例如在库、借出、销毁等inventoryResult表示这次盘点产生的结果
这两个字段不能混用。
8. 这次前后端对齐后的结果
现在前后端在盘点单 CRUD 上已经形成统一约定:
- 新增和编辑共用同一套字段
- 编辑必须传
id - 详情接口返回完整回填数据
- 列表接口返回
list + total + stats - 删除受状态约束
- 详情列表的位置显示使用
locationName - 详情列表的盘点结果显示和筛选使用
inventoryResult
这也是为什么前端现在能稳定做到:
- 列表筛选
- 新增盘点单
- 编辑待执行盘点单
- 删除盘点单
- 查看详情
9. 后面如果还要继续改,优先看哪里
如果要改接口出入口,先看:
router/system/common.goapi/system/inventory_plan.go
如果要改业务规则,先看:
service/system/inventory_plan.go
如果要改前后端字段契约,先看:
model/system/request/inventory_plan.gomodel/system/response/inventory_plan.go
一句话总结
盘点单后端现在已经不是占位接口,而是完整的真实 CRUD 服务:主表和明细快照一起维护,编辑只允许待执行状态,删除禁止执行中状态,列表、详情、创建、更新、删除都已经形成稳定的数据契约;详情接口已经补齐 locationName 到具体层位,并把盘点结果独立为 inventoryResult,后续可以继续往“执行态盘点流程”和“统计字段真实化”上扩展。
