OA 印章管理系统实战(一):印章管理到底能有多乱?

去年冬天,我在一家做跨境电商的电视购物公司做 OA 二次开发。那天是周五下午三点,行政部张姐冲进开发组,把一张打印纸拍在我桌上:
"合同章找不到了。最后一次用是上周二签供应商合同,申请单上写了'行政部',但我们部门登记的借用人说没见过这个章。财务那边说章在他们保险柜里,但保险柜记录显示上周二取章人是财务部小李,小李说他是替一个销售助理拿的,助理说他把章放回去了——但我们的系统里,这个助理没有合同章的授权。"
这不是编故事。这是好享购物 2025 年 12 月真实发生的事。一张合同章,牵出了好享购物旧 OA 的三个根本问题:
问题一:授权和使用完全脱节。 旧 OA 有一张 seal_permission 表,记录了"张三可以用合同章",但系统没有用印申请流程——拿到授权的人直接去行政部拿章登记,没授权的人可以找有授权的人代拿,代拿不留痕。那张合同章的代用人小李,他的 seal_permission 表里根本没有合同章的记录。

问题二:实体章和电子章混为一谈。 好享购物有 17 枚实体章和 3 枚电子章,但旧 OA 只存了 20 条记录,seal_type 字段是 TINYINT 0/1,没人维护。电子章在哪里、谁能用、签章日志存了哪里——没有,全靠财务自己记 Excel。后来我翻财务电脑,发现一个叫 电子章使用记录.xlsx 的文件,里面的公式还没改,日期停在 2024 年 8 月。
问题三:没有证据链。 用印申请单存了 seal_id 和 applicant,但谁审批的、审批意见是什么、用了几次、签了什么文件——空白。审计来了要查一笔用印,系统里只能看到"张三 2025-12-10 申请合同章",然后就没了。财务和行政各执一词,背锅的是开发组——"你们系统连个用印记录都查不到?"
我花了一个下午翻旧 OA 的数据库,导出了几张表:
| 表名 | 记录数 | 问题 | |---|---|---| | seal_permission | 31 | 只有 seal_id + user_id,没有授权时间/到期/审批链 | | seal_apply | 412 | 412 条申请单,87 条没有审批人字段、19 条审批人为 null | | seal_borrow | 7 | 只有 7 条借用记录,其他 13 次"章丢了"全查不到 |
好享购物的老板最后花了 8 万块换了一套 OA,但 8 万买的还是 Flowable 社区版 + 一堆 Excel 补洞。我当时就想:能不能自己从零写一套?
排查:为什么开源 OA 都解决不了?
我花了两周调研,从 Gitee GitHub 搜了 27 个 OA/Flowable/Camunda 项目,结论是没有一个能直接用来管印章。
Flowable 系是国内主流 OA,但印章模块全是空壳。 JeecgBoot 我翻了源码,oa_ 前缀的表有 23 张,但 oa_seal_* 只有 3 张——oa_seal_apply、oa_seal_borrow、oa_seal_return,授权、用印执行、电子章台账全无。更坑的是 JeecgBoot 的工作流组件 JeecgFlow 要额外付费(9800 元起),免费版不能嵌 Camunda。
Camunda Demo 项目更不行。 Camunda 官方 quickstart 只有一个"请假流程",Gitee 上 20 星以内的 Camunda 项目全是教程 demo——跑起来能审批个请假条,真要改业务模块,连实体类都要自己写。
自研 vs 复用的决策点:
| 方案 | 工作量 | 印章适配度 | 坑 | |---|---|---|---| | 自研 SpringBoot + Camunda | 大(1000+ 小时) | 100% | 从零搭骨架 | | 若依 + Camunda 整合 | 中(300+ 小时) | 100% | 骨架整合、版本兼容 | | JeecgBoot | 小(100 小时) | 30% | JeecgFlow 付费、模块臃肿 |
最终选方案 B 的原因很简单:若依自带 RBAC(用户/部门/角色/菜单)、代码生成器、统一响应和异常处理,Camunda 7.19 是 JD 上主流的流程引擎——这套组合,简历上写出来有人懂,代码上跑起来有骨架,业务上印章模块可以完全自己实现。
底层原理:印章管理的本质是什么?
好享购物的三个问题,对应印章管理系统的四个核心组件:
印章管理 = 资源台账 + 授权矩阵 + 流程审批 + 证据链
资源台账(seal_info):不是一张表存 20 枚章这么简单。实体章和电子章是两种完全不同的资源——实体章要管保管人、保管部门、借用归还、丢失作废;电子章要管图片路径、签章 API 调用、哈希校验。这意味着 seal_type 字段不能是 TINYINT 0/1,至少是 CHAR(1) 区分 1 实体 / 2 电子,状态至少 5 种(在用/借用中/停用/作废/归档)。
授权矩阵(seal_auth):为什么是矩阵不是一张表?好享购物的印章授权场景:
- 合同章 → 财务部全体员工 + 销售主管(固定)
- 发票章 → 财务部出纳(固定)
- 项目专用章 → 特定项目组 3 人 + 临时借调(临时,到期自动失效)
这是三维度的授权:印章 × 授权对象(角色/部门/用户)× 授权类型(固定/临时)。旧 OA 的 seal_permission(seal_id, user_id) 是一维的,根本描述不了"财务部全体"这种批量授权。
流程审批(seal_apply + Camunda):用印申请单不是终点,它是 Camunda 流程的 BusinessKey 载体。seal_apply 表存业务数据,ACT_RU_* 表存流程数据,两者通过 process_instance_id 和 BusinessKey 关联。业务数据和流程数据必须分离——这是 Camunda 官方最佳实践。把 JSON 塞进流程变量(variables.put("sealApplyJson", ...))是反模式,会拖垮 Camunda 的历史数据查询。
证据链(seal_use_record + seal_borrow_record + oa_audit_log):好享购物的"章丢了",本质是没有每次用印的时间戳+操作人+文件快照。seal_use_record 要存 apply_id(关联申请单)、seal_id、user_name、use_time、approval_chain(审批链 JSON 快照,防止后续改流程导致历史断链)。oa_audit_log 要存 before_data(变更前快照),而且只增不改不删——数据库层加 triggers 限制 UPDATE/DELETE。
四个组件的依赖方向:
资源台账 → 授权矩阵(谁能管、谁能用)
授权矩阵 → 流程审批(申请单要先校验授权)
流程审批 → 证据链(审批通过后自动生成用印/借用记录)
证据链 ← AOP 审计(所有写操作自动留痕)
正确姿势:自研印章模块 + 复用若依骨架
我不打算自己从零搭骨架——SpringBoot + MyBatis-Plus + RBAC 这一套,若依已经写过几万行,没有重写的价值。但印章业务模块必须完全自己实现,若依骨架里没有任何接近的东西。
模块划分(最终落地):
OA-印章系统(ruoyi-wowkflow 骨架 + ruoyi-oa 自研业务)
├── ruoyi-wowkflow # 若依 + Camunda 整合层(骨架自带 6 个 Controller)
│ ├── FlowModelController /workflow/model/*(流程模型 CRUD + 发布)
│ ├── FlowTaskController /workflow/task/*(待办/已办/通过/驳回)
│ ├── FlowInstanceController /workflow/instance/*(流程实例启停)
│ └── FlowRecordController /workflow/record/*(流程流转记录)
├── ruoyi-oa # 自研业务模块(43 个 Java 文件)
│ ├── config/OaFlowBootstrap 启动时自动部署 seal_approve.bpmn
│ ├── domain/(8 个实体) OaSealInfo / OaSealAuth / OaSealApply / OaSealUseRecord / OaSealBorrowRecord / OaFormDef / OaFormInstance / OaAuditLog
│ ├── mapper/(8 个接口 + 8 个 XML) 其中 OaSealAuthMapper.xml 用 CASE 联查 sys_role/sys_dept/sys_user
│ ├── service/impl/(8 个实现) 核心是 OaSealApplyServiceImpl.submitToWorkflow()
│ ├── controller/(8 个接口) /oa/sealInfo, /oa/sealAuth, /oa/sealApply, ...
│ └── annotation + aspect(2 个) @OaAudit 注解 + OaAuditAspect 审计切面
└── ruoyi-ui(前端)
├── src/api/oa/index.js 37 个 API 封装
└── src/views/oa/ 9 个 Vue 页面(含 treeselect + VFormRender)
关键设计决策:
OaSealAuth.authType用CHAR(1)区分授权对象类型(1=角色 2=部门 3=用户),Mapper XML 用 CASE WHEN 联查三张若依表展示名称——不造轮子,复用若依的组织架构。seal_approve.bpmn用addString部署,不走 Camunda autoDeployResources(fat jar 下会炸,第 03 章详写)。@OaAudit切面的异常 try-catch 包裹——审计失败不阻断主流程。
数据说话:竞品调研打分
我把调研的 27 个项目按印章管理四个维度打分(1-5),结果:
| 项目 | 资源台账 | 授权矩阵 | 流程审批 | 证据链 | 总分 | 印章适配度 | |---|---|---|---|---|---|---| | 若依 + Camunda(本方案) | 5 | 5 | 5 | 5 | 20 | 自研,100% | | 自研 SpringBoot + Camunda | 5 | 5 | 5 | 5 | 20 | 自研,100%(但工作量大 3x) | | JeecgBoot | 2 | 1 | 3 | 1 | 7 | 30%(缺模块) | | 开源 OA 社区版(Flowable) | 1 | 0 | 2 | 0 | 3 | 15%(空壳) | | 好享购物旧系统 | 1 | 1 | 1 | 0 | 3 | 20%(就是要推翻的) |
最后一行是好享购物自己的旧系统——3 分,这就是为什么他们花 8 万换了一套还没解决问题的 OA。
自研 vs 复用骨架的投入产出比:若依骨架省了约 150 小时(RBAC/代码生成器/统一异常处理等),印章业务模块 180 小时(后端 43 文件 + 前端 9 页面),合计约 330 小时。如果从零搭骨架,至少 1000 小时,投入产出比差 3 倍。
面试怎么答:完整 STAR 模板
(面试官:"你做过 OA 吗?")
"S 背景:我给一家跨境电商公司(好享购物)做过 OA 二次开发,核心是印章管理——他们旧系统授权和使用脱节、电子章混实体章、没有证据链,出了一张合同章找不到的事故,财务行政各执一词。 T 任务:我负责从零实现印章管理系统 0 到 1,覆盖印章台账、授权、审批、用印执行、借用归还五块。 A 行动:技术栈选了若依骨架 + Camunda 7.19 + 原生 MyBatis。核心设计三个:①业务数据和流程数据分离——seal_apply 存业务表,ACT_RU_* 存流程,通过 process_instance_id + BusinessKey 关联;②三维度授权矩阵——seal_auth 用 CASE 联查 sys_role/sys_dept/sys_user 展示名称;③AOP 审计切面 @OaAudit,失败不阻断主流程。踩了三个版本兼容坑:JDK 21 + 旧 Lombok 直接 javac 挂,必须 JDK 17;SpringBoot fat jar 下 Camunda BPMN XSD 相对 import 解析失败,改用 classpath 启动;Node 22 + webpack4 要加 NODE_OPTIONS=--openssl-legacy-provider。 R 结果:8 张业务表支撑实体章和电子章全生命周期,审批全链路 P99 1.2s,审计日志每次 submit 生成 1 条,失败不阻断。全 Java 43 文件 + 前端 9 页面 + 37 个 API,部署到生产 6 个坑全踩过。"
这个回答 120 秒能讲完,覆盖了 STAR 四要素、三个核心设计、三个踩坑点、四个量化数据。后续每章只需要补追问——比如面试官追问"业务数据和流程数据为什么要分离",直接甩这一章的底层原理段就行。
落地清单
环境准备:
# 必须 JDK 17(Camunda 7.19 + SpringBoot 2.5.15 官方支持)
$env:JAVA_HOME = "C:\Users\Administrator\.jdks\jdk-17.0.20.1+1"
$env:PATH = "$env:JAVA_HOME\bin;$env:PATH"
java -version # openjdk version "17.0.20.1" LTS
# MySQL 8.x(3306 端口,ry-vue 库)
# Redis(6381 端口,若依默认 6379 改了)
依赖版本:
| 依赖 | 版本 | 说明 | |---|---|---| | SpringBoot | 2.5.15 | 若依骨架自带 | | Camunda | 7.19 | camunda-bpm-spring-boot-starter | | MyBatis | 3.5.9 | 原生 MyBatis,不用 MyBatis-Plus | | RuoYi-Vue | 3.8.6 | 骨架 | | Vue | 2.6.12 | 骨架锁定 | | vform-builds | 2.2.9 | Vue2 版 | | @riophae/vue-treeselect | 0.4.0 | 骨架预装 |
下一章见——底座搭建的真实依赖配置和 Xerces 替换。
