【行政】办公用品、用印管理
办公用品、用印管理,由 yudao-module-oa 后端模块的 supply、seal 包实现,前端实现在 @/views/oa/supply/item、@/views/oa/supply/apply、@/views/oa/supply/issue、@/views/oa/seal、@/views/oa/seal/apply 目录。
办公用品按「资料维护 → 领用申请 → 审批 → 发放 → 归还」处理;用印按「印章资料 → 用印申请 → 审批」处理。审批通过并不直接扣减库存,库存只在实际发放、归还和入库时变更。
本文涉及表如下图所示:
# 1. 办公用品信息
办公用品信息,由 OaSupplyItemController 提供接口(/oa/supply-item)。
# 1.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
办公用品表 oa_supply_item,保存用品台账的规格、单位、库存和预警数量:
CREATE TABLE `oa_supply_item` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`dept_id` bigint NOT NULL COMMENT '所属部门编号',
`name` varchar(128) NOT NULL COMMENT '物品名称',
`no` varchar(64) DEFAULT NULL COMMENT '物品编码',
`category` int NOT NULL COMMENT '类别',
`manage_type` int NOT NULL COMMENT '管理类型',
`model` varchar(128) COMMENT '规格型号',
`unit` varchar(32) COMMENT '计量单位',
`reference_price` decimal(18,2) COMMENT '参考单价',
`stock_quantity` int NOT NULL DEFAULT 0 COMMENT '库存数量',
`min_stock_quantity` int NOT NULL DEFAULT 0 COMMENT '最低库存预警',
`pic_url` varchar(2048) COMMENT '物品图片 URL',
`status` int NOT NULL DEFAULT 0 COMMENT '状态',
`sort` int NOT NULL DEFAULT 0 COMMENT '显示顺序',
`remark` varchar(500) COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='OA 办公用品';
① dept_id 关联 system_dept 表的 id 字段,表示用品所属部门。no 物品编码允许为空;若填写,由 OaSupplyItemServiceImpl 的 validateSupplyItemNoUnique 校验同租户内唯一。
② 枚举 category 类别(OaSupplyCategoryEnum),对应字典 oa_supply_category:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | STATIONERY | 文具类 |
2 | PRINTING_CONSUMABLE | 打印耗材 |
3 | DAILY_NECESSITY | 生活用品 |
4 | COMPUTER_ACCESSORY | 电脑办公 |
5 | OFFICE_EQUIPMENT | 办公设备/电器 |
6 | FINANCIAL_SUPPLY | 财务用品 |
③ 枚举 manage_type 管理类型(OaSupplyManageTypeEnum),对应字典 oa_supply_manage_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | CONSUMABLE | 消耗品 |
2 | BORROWABLE | 借用品 |
3 | ASSET_CANDIDATE | 资产品 |
管理类型决定发放后的明细去向:只有借用品发放后进入「待归还」,消耗品、资产品发放后进入「已领用」。详见 §2.3 状态流转。
④ stock_quantity 是当前库存。入库(stockInSupplyItem)和借用品归还(returnSupply)增加库存,发放(issueSupply)通过 updateSupplyItemStockQuantity 原子扣减;扣减后若为负数则抛出 SUPPLY_STOCK_INSUFFICIENT,不允许库存变负。
⑤ 枚举 status 启停状态(CommonStatusEnum),对应通用字典 common_status:
| 值 | 枚举 | 说明 |
|---|---|---|
0 | ENABLE | 开启 |
1 | DISABLE | 关闭 |
领用申请校验物品时要求 status = 开启(0),停用物品不能再被选入新申请。删除台账前还会检查是否仍有待发放明细或未归还的借用品。
# 1.2 管理后台
对应 [OA 办公协同 -> 办公用品管理 -> 办公用品信息] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/supply/item 目录。
# 列表

# 新增与修改
点击【新增】打开 OaSupplyItemForm.vue,填写物品编码、名称、类别、管理类型、规格型号、单位、价格、库存和预警数量。修改复用同一表单。

# 入库
点击【入库】打开 OaSupplyStockForm.vue,填写入库数量和备注,调用 PUT /oa/supply-item/stock-in 增加库存。

# 停用与删除
在修改表单中停用物品后,不再作为正常可领用物品选择。删除台账前确认历史业务需要,已有领用记录按保存的明细快照展示。
# 2. 领用申请与发放
领用申请与发放,由 OaSupplyApplyController 提供接口(/oa/supply-apply);发放、归还由 OaSupplyIssueController 提供接口(/oa/supply-issue),复用申请明细,不建立独立发放表。
# 2.1 主表表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
领用申请表 oa_supply_apply,保存申请单的领用日期、使用类型和审批状态:
CREATE TABLE `oa_supply_apply` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`no` varchar(64) NOT NULL COMMENT '申请单号',
`dept_id` bigint NOT NULL COMMENT '申请部门编号',
`apply_time` datetime COMMENT '领用日期',
`use_type` int COMMENT '使用类型',
`pickup_method` int COMMENT '领取方式',
`reason` varchar(500) COMMENT '申请事由',
`status` int NOT NULL DEFAULT -1 COMMENT '审批状态',
`process_instance_id` varchar(64) COMMENT 'BPM 流程实例编号',
`file_urls` json COMMENT '附件地址列表',
`remark` varchar(500) COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='OA 用品领用申请';
① no 申请单号由 OaNoRedisDAO 以 YP 为前缀生成,创建时再查一次 selectByNo 做二次校验,重复则抛出 SUPPLY_ITEM_NO_DUPLICATE 异常(该错误码文案为「用品编号已存在」,此处复用自用品编号,并非申请单号有独立错误码)。通用字段 creator 即申请人,修改、删除、提交由 validateSupplyApplyOwner 校验只能本人操作。
② dept_id 关联 system_dept 表的 id 字段,在创建时冗余申请人当时所属部门,不接受前端传入。
③ 枚举 use_type 使用类型(OaSupplyUseTypeEnum),对应字典 oa_supply_use_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | RECEIVE | 领用 |
2 | BORROW | 借用 |
④ pickup_method 领取方式,对应字典 oa_supply_pickup_method(当前数据为 1 自取)。
⑤ status 是 BPM 审批状态(BpmProcessInstanceStatusEnum),列表展示用字典 bpm_process_instance_status。process_instance_id 关联 BPM 流程实例。流程定义 key 为 oa_supply_apply。详见 §2.3 状态流转。
⑥ file_urls 是 JSON 数组,保存附件地址列表,形如 ["http://xxx/a.pdf"]。
该表包含一个子表:
oa_supply_apply_item(用品申请明细):在新增/修改申请的【添加办公用品】中维护,保存申请数量、实发数量、归还数量和明细本地状态。
# 2.2 子表结构
申请明细表 oa_supply_apply_item,逐行保存申请数量、实发数量和归还数量,并冗余物品快照:
CREATE TABLE `oa_supply_apply_item` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`apply_id` bigint NOT NULL COMMENT '领用申请编号',
`item_id` bigint NOT NULL COMMENT '办公用品编号',
`item_name` varchar(128) NOT NULL COMMENT '物品名称快照',
`model` varchar(128) COMMENT '规格型号快照',
`unit` varchar(32) COMMENT '计量单位快照',
`manage_type` int NOT NULL COMMENT '管理类型快照',
`apply_quantity` int COMMENT '申请数量',
`issued_quantity` int NOT NULL DEFAULT 0 COMMENT '实发数量',
`returned_quantity` int NOT NULL DEFAULT 0 COMMENT '累计归还数量',
`status` int NOT NULL DEFAULT -1 COMMENT '明细状态',
`issue_user_id` bigint COMMENT '发放人用户编号',
`issue_time` datetime COMMENT '发放时间',
`issue_remark` varchar(500) COMMENT '发放备注',
`return_remark` varchar(500) COMMENT '归还备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='OA 用品申请明细';
① apply_id 关联 oa_supply_apply 表的 id 字段,item_id 关联 oa_supply_item 表的 id 字段。item_name / model / unit / manage_type 在保存明细时从用品台账冗余快照,后续台账改名不影响历史申请展示。
② issue_user_id 关联 system_users 表的 id 字段,记录实际发放人;issue_time、issue_remark 在发放时写入,return_remark 在归还时写入。
③ 枚举 status 明细本地状态(OaSupplyApplyItemStatusEnum),对应字典 oa_supply_item_status:
| 值 | 枚举 | 说明 |
|---|---|---|
-1 | APPLYING | 申请中 |
0 | PENDING | 待发放 |
1 | RECEIVED | 已领用 |
2 | RETURN_PENDING | 待归还 |
3 | RETURNED | 已归还 |
④ 同一申请内不允许重复选择同一用品(validateSupplyApplyItems 校验 item_id 去重)。编辑草稿时先 deleteByApplyId 再整批重插,明细状态重新初始化为 申请中(-1)。
# 2.3 状态流转
领用申请同时维护两套状态:主表 oa_supply_apply.status 走 BPM,明细 oa_supply_apply_item.status 走本地发放/归还。
主表审批状态(BpmProcessInstanceStatusEnum,字典 bpm_process_instance_status):
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
-1 | NOT_START | 未开始(草稿) | 编辑、删除、提交 |
1 | RUNNING | 审批中 | 取消、查看进度 |
2 | APPROVE | 审批通过 | 发放明细 |
3 | REJECT | 审批不通过 | 编辑后重新提交 |
4 | CANCEL | 已取消 | 编辑后重新提交 |
明细本地状态(OaSupplyApplyItemStatusEnum,字典 oa_supply_item_status):
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
-1 | APPLYING | 申请中 | —(随主表审批) |
0 | PENDING | 待发放 | 发放 |
1 | RECEIVED | 已领用 | — |
2 | RETURN_PENDING | 待归还 | 归还(可分次) |
3 | RETURNED | 已归还 | — |
状态流转说明
【主表 BPM】
草稿(-1) ──提交──→ 审批中(1) ──┬──通过──→ 审批通过(2)
├──不通过→ 审批不通过(3) ──可再编辑──→ 草稿语义
└──取消──→ 已取消(4) ──可再编辑──→ 草稿语义
【明细本地】(仅主表审批通过后推进)
申请中(-1) ──审批通过──→ 待发放(0) ──发放──┬──消耗品/资产品──→ 已领用(1)
└──借用品──────────→ 待归还(2) ──全部归还──→ 已归还(3)
- 创建申请(
createSupplyApply):主表状态固定为未开始(-1),生成YP单号并冗余部门;明细状态初始化为申请中(-1)。创建与提交都不扣库存。 - 编辑 / 删除(
updateSupplyApply/deleteSupplyApply):由validateSupplyApplyEditable限制主表处于未开始(-1)、不通过(3)或已取消(4);删除时同步删除明细。 - 提交(
submitSupplyApply):未开始/驳回/取消→审批中(1),发起流程 keyoa_supply_apply,绑定process_instance_id。 - 取消(
cancelSupplyApply):仅审批中(1)可取消,调用 BPM 取消后由监听器回写已取消(4)。 - 审批结果回写(
updateSupplyApplyStatus):写入主表status;仅当首次变为审批通过(2) 时,把该单全部明细批量改为待发放(0)(updateStatusByApplyId),重复回调不会重置已发放明细。 - 发放(
issueSupply):明细必须是待发放(0)且主表已是审批通过(2)。按实发数量调用updateSupplyItemStockQuantity扣库存;借用品(manage_type = 2)→待归还(2),其它 →已领用(1)。 - 归还(
returnSupply):明细必须是待归还(2),本次归还数量不能超过「实发 − 已归还」;累计归还量回写,全部还完才变为已归还(3),并按本次数量加回库存。可分次归还。
# 2.4 管理后台
对应 [OA 办公协同 -> 办公用品管理 -> 领用申请] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/supply/apply 目录。
# 列表

# 新增与修改
点击【新增】打开 OaSupplyApplyForm.vue,填写领用日期、使用类型、领取方式和事由,通过【添加办公用品】选择物品并填写数量,点击【保存】。

# 提交与审批进度
在列表点击【提交】,调用 PUT /oa/supply-apply/submit 发起 oa_supply_apply 流程。点击【进度】查看审批;审批中可点击【取消】,由 BPM 回写取消结果。
# 发放
对应 [OA 办公协同 -> 办公用品管理 -> 领用发放] 菜单,页面在 @/views/oa/supply/issue。点击待发放明细的【发放】打开 OaSupplyIssueForm.vue,填写实发数量和备注,调用 PUT /oa/supply-issue/issue,按实发数量扣库存。

# 归还
借用品点击【归还】打开 OaSupplyReturnForm.vue,填写本次归还数量。调用 PUT /oa/supply-issue/return 后累计归还数量并补回库存;可以分次归还,本次数量不能超过剩余未归还数量。

# 3. 印章信息
印章信息,由 OaSealController 提供接口(/oa/seal)。
# 3.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
印章表 oa_seal,保存印章台账的编号、类型、保管人和在库状态:
CREATE TABLE `oa_seal` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`dept_id` bigint NOT NULL COMMENT '所属部门编号',
`no` varchar(64) NOT NULL COMMENT '印章编号',
`name` varchar(128) NOT NULL COMMENT '印章名称',
`type` int NOT NULL COMMENT '印章类型',
`category` int DEFAULT NULL COMMENT '印章分类',
`keeper_user_id` bigint NOT NULL COMMENT '保管人用户编号',
`keeper_dept_id` bigint NOT NULL COMMENT '保管部门编号',
`status` int NOT NULL DEFAULT 0 COMMENT '状态',
`purchase_time` datetime DEFAULT NULL COMMENT '购买时间',
`enable_time` datetime DEFAULT NULL COMMENT '启用时间',
`disable_time` datetime DEFAULT NULL COMMENT '停用时间',
`pic_url` varchar(2048) DEFAULT NULL COMMENT '印章照片地址',
`sort` int NOT NULL DEFAULT 0 COMMENT '显示顺序',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_no` (`tenant_id`, `no`)
) ENGINE=InnoDB COMMENT='印章';
① no 印章编号由 OaNoRedisDAO 以 YZ 为前缀生成,uk_no 唯一索引保证租户内编号唯一,创建时再由 validateSealNoUnique 二次校验,重复则抛出 SEAL_NO_DUPLICATE。
② dept_id、keeper_dept_id 关联 system_dept 表的 id 字段;keeper_user_id 关联 system_users 表的 id 字段。所属部门可以与保管部门不同,创建/修改时分别调用 deptApi.validateDeptList、adminUserApi.validateUser 校验。
③ type 印章类型对应字典 oa_seal_type,category 印章分类对应字典 oa_seal_category。
④ 枚举 status 台账状态(OaSealStatusEnum),对应字典 oa_seal_status:
| 值 | 枚举 | 说明 |
|---|---|---|
0 | AVAILABLE | 在库 |
1 | DISABLED | 停用 |
2 | IN_USE | 使用中 |
台账状态不随申请自动变更
当前代码中,用印申请的提交、审批通过不会自动把 oa_seal.status 改成「使用中」或「在库」。台账状态完全由印章表单手工维护。
# 3.2 管理后台
对应 [OA 办公协同 -> 印章管理 -> 印章信息] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/seal 目录。
# 列表

# 新增与修改
点击【新增】打开 OaSealForm.vue,维护印章名称、类型、保管人、部门、状态、购买日期、启停日期和照片。印章编号由后端生成并校验唯一。

# 删除
点击【删除】并确认后逻辑删除印章台账。历史用印申请保留其申请信息与审批记录。
# 4. 用印申请
用印申请,由 OaSealApplyController 提供接口(/oa/seal-apply)。
# 4.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
用印申请表 oa_seal_apply,保存用印事由、文件信息、预计与实际用印时间,并冗余印章快照:
CREATE TABLE `oa_seal_apply` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`no` varchar(64) NOT NULL COMMENT '申请单号',
`user_id` bigint NOT NULL COMMENT '申请人用户编号',
`dept_id` bigint DEFAULT NULL COMMENT '申请部门编号',
`seal_id` bigint NOT NULL COMMENT '印章编号',
`seal_no` varchar(64) NOT NULL COMMENT '印章编号快照',
`seal_name` varchar(128) NOT NULL COMMENT '印章名称快照',
`keeper_user_id` bigint NOT NULL COMMENT '保管人用户编号快照',
`reason` varchar(500) NOT NULL COMMENT '用印事由',
`type` tinyint NOT NULL COMMENT '用印类型',
`mode` tinyint NOT NULL COMMENT '用印方式',
`document_title` varchar(255) DEFAULT NULL COMMENT '文件标题',
`document_type` varchar(64) DEFAULT NULL COMMENT '文件类型',
`document_count` int NOT NULL COMMENT '文件份数',
`contract_price` decimal(18,2) DEFAULT NULL COMMENT '合同金额',
`contract_party` varchar(255) DEFAULT NULL COMMENT '合同对方',
`expected_use_time` datetime NOT NULL COMMENT '预计用印时间',
`actual_use_time` datetime DEFAULT NULL COMMENT '实际用印时间',
`expected_return_time` datetime DEFAULT NULL COMMENT '预计归还时间',
`actual_return_time` datetime DEFAULT NULL COMMENT '实际归还时间',
`use_status` tinyint NOT NULL DEFAULT 0 COMMENT '用印业务状态',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '审批状态',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM 流程实例编号',
`urgent` bit NOT NULL DEFAULT b'0' COMMENT '是否紧急',
`file_urls` json DEFAULT NULL COMMENT '附件地址列表',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_no` (`tenant_id`, `no`),
UNIQUE KEY `uk_process_instance` (`tenant_id`, `process_instance_id`),
KEY `idx_user_id` (`tenant_id`, `user_id`),
KEY `idx_seal_id` (`tenant_id`, `seal_id`)
) ENGINE=InnoDB COMMENT='用印申请';
① no 申请单号由 OaNoRedisDAO 以 YY 为前缀生成,uk_no 唯一索引保证租户内唯一,创建时再查 selectByNo,重复则抛出 SEAL_APPLY_NO_DUPLICATE。user_id 关联 system_users 表的 id 字段,dept_id 关联 system_dept 表的 id 字段,均在创建时取自当前登录人,不接受前端指定。idx_user_id 支撑【我的申请】查询。
② seal_id 关联 oa_seal 表的 id 字段;seal_no / seal_name / keeper_user_id 在创建、修改、提交时从印章台账冗余快照。idx_seal_id 支撑按印章反查申请及外借冲突检查。
③ 枚举 type 用印类型(OaSealApplyTypeEnum),对应字典 oa_seal_apply_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | CONTRACT | 合同用印 |
2 | AGREEMENT | 协议用印 |
3 | CERTIFICATE | 证明用印 |
4 | AUTHORIZATION | 授权用印 |
5 | OTHER | 其他用印 |
非合同类型保存时会清空 contract_price、contract_party。
④ 枚举 mode 用印方式(OaSealUseModeEnum),对应字典 oa_seal_use_mode:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | ONSITE | 现场用印 |
2 | BORROW | 外借 |
外借才需要 expected_return_time;非外借保存时会清空预计归还时间。外借在提交时校验时段冲突(validateTimeConflict):审批中或已通过的外借单占用预计时段(闭区间),草稿不占用。
⑤ status 是 BPM 审批状态,use_status 是本地用印业务状态,二者分别保存。流程 key 为 oa_seal_apply。uk_process_instance 保证流程实例编号唯一。
⑥ file_urls 是 JSON 数组,保存附件地址列表。
# 4.2 状态流转
用印申请由 OaSealApplyServiceImpl 控制。
审批状态(BpmProcessInstanceStatusEnum,字典 bpm_process_instance_status):
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
-1 | NOT_START | 未开始(草稿) | 编辑、删除、提交 |
1 | RUNNING | 审批中 | 撤销、查看进度 |
2 | APPROVE | 审批通过 | —(表示允许用印) |
3 | REJECT | 审批不通过 | — |
4 | CANCEL | 已取消 | — |
用印业务状态(OaSealUseStatusEnum,字典 oa_seal_use_status):
| 状态值 | 枚举 | 说明 |
|---|---|---|
0 | PENDING | 待处理 |
1 | COMPLETED | 已完成 |
2 | BORROWED | 外借中 |
3 | RETURNED | 已归还 |
4 | OVERDUE | 已逾期 |
状态流转说明
【BPM】
草稿(-1) ──提交──→ 审批中(1) ──┬──通过──→ 审批通过(2)
├──不通过→ 审批不通过(3)
└──撤销──→ 已取消(4)
【use_status】
创建时固定为 待处理(0)
※ 登记接口目前未实现,菜单「用印及归还登记」闲置(status=停用、visible=false)
※ 因此 use_status 设计已有,但不会随审批或登记自动推进到 已完成/外借中/已归还/已逾期
- 创建申请(
createSealApply):status = 未开始(-1),use_status = 待处理(0),生成YY单号并写入印章快照。草稿不占用外借时段。 - 编辑 / 删除(
updateSealApply/deleteSealApply):仅草稿(validateDraft)允许。 - 提交(
submitSealApply):先validateTimeConflict(仅外借且预计用印、归还时间齐全时检查),再改为审批中(1),发起oa_seal_apply流程。外借在审批中即参与冲突占用。 - 撤销(
cancelSealApply):仅审批中(1),取消 BPM 后由事件回写。 - 审批结果回写(
updateSealApplyStatus):只更新status,不修改use_status,也不改印章台账状态。审批通过仅表示允许用印,不等同于实际借出或归还。
用印登记尚未落地
菜单中存在「用印及归还登记」(权限 oa:seal-apply:register),但当前后端没有登记接口,该菜单处于停用且不可见。文档保留 use_status 字段与枚举说明,便于后续补齐登记流转。
# 4.3 管理后台
对应 [OA 办公协同 -> 印章管理 -> 用印申请] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/seal/apply 目录。
# 列表

# 新增与修改
点击【新增】打开 OaSealApplyForm.vue,选择印章,填写用印事由、类型、方式、文件标题、份数和预计用印时间;合同、借用等字段按所选业务填写。保存后生成未提交草稿。

# 提交与撤销
在列表点击【提交】,调用 PUT /oa/seal-apply/submit 发起 oa_seal_apply 流程。审批中可撤销,调用 /oa/seal-apply/cancel;点击【进度】查看 BPM 流程。

# 查看与删除
详情展示申请资料、附件和审批状态。仅未提交草稿允许修改、删除,已提交申请保留审批记录。