审批系统这种项目看起来是标准CRUD真正动手做才发现深坑一个接一个。前阵子我帮一个同学梳理基于SpringBoot的行政审批系统毕业设计选题对比了网上大量现成模板后我们发现这类“面向机关单位的轻量化审批流转系统”最核心的价值不在增删改查而在“流程怎么流转、权限怎么隔离、数据怎么留痕”这三件容易被低估的事。这篇文章就围绕这套政务事项在线审批平台完整复盘从需求拆解到技术选型、数据建模、权限设计、页面交互、内网部署的整个过程。想拿这个题目做毕设的同学可以参考在职开发如果要给单位做轻量审批流转系统也能直接用上这里面的思路。1. 行政审批系统的“轻量”背后藏着哪些必须提前拆解的需求1.1 审批流转的真实复杂度从状态机说起很多人第一次看审批系统第一反应是“不就是状态机嘛”——提交之后变待审通过之后变已办结驳回之后变已退回最多再加几个分支状态一张表放个status字段就能搞定。这个判断只对了一半状态确实能表达“当前在哪”但状态机替代不了“从哪来、经过哪些环节、每个环节是谁处理、为什么被退回”而后面这些恰恰是行政审批系统真正要管的事。我拆了几个实际单位的办理流程后发现同一个事项往往不是一个线性流程。拿最常见的公文流转举例它可能是“窗口受理→主办岗初审→部门负责人复核→分管核查”这种单线流程也可能是“多部门会签、任一部门反对则退回”的并行分支还可能是“材料不齐全需要补正、补正后自动回到原环节”这种非线性的情况。除了常规的同意和退回还有转办、委托、撤回、挂起、催办、分派等边界动作。如果一开始就按“一张表、一个字段”来设计等流程种类一多改起来会非常痛苦。这个认知直接决定后续设计方向。你必须把“业务的当前状态”和“流程实例的流转位置”拆成两套东西来看业务表只记录这个申请办到了什么程度流程表负责记录现在流转到哪个环节、历史走过哪些节点。两种概念混在一张表里是所有后期重构的根源。1.2 “机关单位”这个定语带来的四个隐性约束标题里的“面向机关单位”不是装饰词它决定了系统在非功能层面有四个隐形要求而且这些要求比功能本身更影响设计。第一是内网部署。机关单位的网络环境通常不能随便访问外网Maven依赖、npm包都需要提前准备好离线版本线上服务器也大多只有JDK8环境。这就迫使你在技术选型时不能盲目追新SpringBoot选2.x还是3.x、前端组件库选哪个版本都要提前确认部署环境的底线。第二是操作留痕完整。审批系统对“谁在什么时间对哪个办件做了什么操作”有明确的过程留痕要求审批记录表、操作日志表不是可有可无的附属品而是系统的核心表。我见过一些半成品把审批记录做成前端弹窗提醒刷新页面什么都没了这在真实环境下完全不合格。第三是组织架构动态变化。机关单位的人员调动、借调、临时授权非常频繁今天你所在的科室明天可能合并某个审批角色可能在一段时间内由别人代理。用户表、部门表、角色表如果不单独建而是硬编码到菜单里后面光维护权限就要崩溃。第四是时限监管。审批不能无限期拖着系统要能体现出“办理时限”和“超时提醒”的概念哪怕只是按工作日计算截止时间并标红。这是行政审批类系统区别于普通内部OA的一个典型特征也是答辩时容易被追问的点。这些隐性约束通常在需求文档里不会被写出来但如果你在架构阶段忽视了它们后面每一项都会变成返工重做的理由。2. 技术选型的复盘哪些组件组合能稳妥支撑这套系统2.1 后端骨架SpringBoot版本与ORM框架的搭配逻辑技术选型这个环节我踩过一次特别典型的坑。刚开始图新鲜想用SpringBoot 3.x结果发现内网服务器是JDK8SpringBoot 3.x最低要求JDK17整个技术栈被迫推翻重来。后来老实换回SpringBoot 2.7.x一切才顺畅起来。所以这里我的第一建议是先问清楚目标运行环境能装什么JDK再决定SpringBoot版本。毕业设计阶段如果拿不准直接用2.7.x配JDK8是稳妥的网上能找到的资料也多不容易卡壳。持久层ORM我推荐MyBatis-Plus不推荐JPA。理由是审批系统里大量列表查询带有复杂的动态筛选条件比如按事项名称模糊查、按提交日期范围查、按当前审批状态查、按部门ID过滤这些条件在运行时才确定。MyBatis-Plus的QueryWrapper能非常直观地拼条件还能用Lambda表达式避免字段名写错JPA在这种动态查询场景下要么写Specification要么用原生SQL拼接代码量并不小对很多人来说反而更难掌握。而MyBatis-Plus自带的分页插件和乐观锁插件也是审批系统的刚需前者解决办件列表分页后者解决并发审批时的数据一致性。数据库层面建议MySQL 5.7或8.0存储引擎用InnoDB字符集统一utf8mb4。审批意见里可能包含生僻字和特殊符号utf8mb4才能完整支持。如果演示环境只能用MySQL 5.7也没问题业务表结构不复杂5.7完全跑得动。2.2 权限中间件与前端方案的取舍权限这块是行政审批系统最容易做错的地方。常见选择是Spring Security和Sa-Token。我的建议是Sa-Token理由很直接Sa-Token是专门为SpringBoot设计的轻量鉴权框架登录认证、权限校验、踢人下线、账号封禁这些功能开箱即用代码注解简单学习成本比Spring Security低很多。行政审批系统的权限核心是“登录校验 角色鉴权 接口拦截”Sa-Token用几行配置就能全搞定剩下的精力可以专注在业务设计和数据权限上。Spring Security不是不好它胜在生态完整、功能强大但配置复杂度和概念量对于这样一个小型系统来说确实偏重光是弄懂SecurityContext和Filter链就要花不少时间。如果毕业答辩被问到“为什么不用Spring Security”你可以答“本项目属于轻量化审批系统权限模型以RBAC为主Sa-Token能在更小体积内提供同等能力”这是合理的取舍。前端方案就两条路。一条是用Vue 3配合Element Plus从零搭建后台界面页面可控、代码干净答辩展示时系统有辨识度另一条是基于开源后台模板改造效率高但要注意彻底替换默认密码、包名和版权标识否则答辩时被问到“这个框架是您自己写的吗”会非常尴尬。我的态度是毕业设计选Vue3 Element Plus Vite自己搭一套别偷懒省下的时间后续都会在学习成本上还回去。2.3 实际项目里的最终技术栈参考我梳理一下最终采用的组合供直接参考层次选型选择理由基础框架SpringBoot 2.7.x兼容JDK8文档丰富社区问题积累多ORMMyBatis-Plus 3.5.x动态查询方便自带分页和乐观锁数据库MySQL 8.0 / 5.7部署门槛低流程型数据用关系型模型天然合适鉴权Sa-Token 1.39.x配置简单支持注解鉴权和高并发登录校验前端Vue 3 Element Plus组件完善审批表格、表单、时间轴都有现成组件构建工具Maven Vite都是主流标配离线和内网构建也方便这套组合的整体思路是“够用、好解释、别给自己挖坑”尤其是答辩环节老师问到的任何组件你都能讲明白为什么选它这在毕业设计里的价值比炫技高得多。3. 数据模型拆分审批流转的核心是“状态”与“轨迹”分开记3.1 四组核心表业务数据与流程数据分离数据表设计是整个系统最重要的决策这一节值得反复看。我按职责把表分成四组分组核心表职责说明基础数据组sys_user、sys_dept、sys_role、sys_menu、sys_user_role、sys_role_menu组织架构、用户信息、角色菜单标准的RBAC体系事项配置组approval_item、approval_item_node定义有哪些审批事项、每个事项的流转环节业务数据组biz_apply、biz_form_data、biz_attachment申请单主表、动态表单字段值、上传附件审批流转组proc_instance、proc_task、proc_record流程实例、当前任务、历史审批记录最核心的设计原则是审批流转数据必须和业务数据分开存放。业务数据负责描述“这份申请是什么”流程数据负责描述“这份申请走到了哪、谁处理过、结果是什么”。两者通过一个业务编号字段关联。这样做有几个直接好处业务数据和流程数据可以分别扩展比如表单字段增加不影响流程表结构。流程历史可以完整保留哪怕业务数据被反复修改审批轨迹还是原始链条。做超时统计和数据大屏时直接查流程表即可不用碰业务表复杂的表单字段。这个理念我在很多开源项目里都见过但同学们自己写代码的时候经常受不了shell的诱惑想着“直接加一个status字段多简单”。等流程一旦出现驳回和撤回需求status字段就开始打架到底哪个状态是当前哪个状态是历史这就是我反复强调分离的原因。3.2 流程任务的字段设计与状态机的落点有了四组关系之后重点来了任务节点表proc_task怎么设计。我给出核心字段设计task_id任务ID主键instance_id流程实例ID关联proc_instancenode_code当前环节编码比如“apply”“dept_check”“final_approve”assignee_id当前办理人IDcandidate_dept_id候选办理部门ID用于部门角色处理status任务状态pending / done / cancelleddeadline办理截止时间用于超时提醒version乐观锁版本号防止多人同时处理同一任务proc_instance表则要记录流程的启动者、当前节点、流程状态、开始时间、结束时间以及关联的业务主键。状态字段有一个关键设计proc_instance里的status只存大阶段状态比如“审批中”“已办结”“已终止”而业务表里的status只表达“草稿”“审批中”“已办结”“已驳回”这一层面向用户的语义。真正当前的流转位置永远从proc_task表查而不是从状态字段推断。这样设计的核心原因在于如果状态字段承载了“当前在哪个环节”的信息那么每驳回一次、每转办一次都要改写状态字段历史轨迹只能靠额外日志补救。而用任务表记录当前环节驳回时不是“把状态改回去”而是“创建一个新任务指向指定的历史节点”业务数据的状态保持不变——因为流程确实还没结束只是回到了前面的环节。一句话总结状态是结果任务是过程过程的记录不该被覆盖掉。3.3 最容易忽略的乐观锁与唯一约束审批系统是典型的多人并发操作场景。两个审批人同时打开同一个待办一个点了“同意”另一个也点了“同意”如果后端不做控制流程可能被推进两次。解决方式是在任务表加version字段更新时执行UPDATE proc_task SET statusdone, versionversion1 WHERE task_id? AND version?影响行数为0则说明任务已被处理提示冲突即可。MyBatis-Plus的乐观锁插件就是干这个的一个注解就能开启。另一个容易忽略的点是业务主表的唯一约束。办件编号一定要全局唯一建议直接使用数据库的雪花ID或者时间戳加随机数的组合不要用简单的自增ID当业务编号对外展示否则容易暴露办件总量也容易出现多库合并时撞号。附件表的业务编号外键也建议加索引审批详情页通常会一次性加载该申请下所有附件没有索引时数据量稍大就会变慢。虽然这些细节不出现在功能列表里但验收演示时数据量一上去性能差距立竿见影。4. 审批流程引擎方案自研状态机怎么设计何时才需要Flowable4.1 自研路由表与动作粒度一个300行的轻量流转内核审批流程的实现是另一个分水岭。我先把结论放着如果你的流程种类在10个以内且流程形态以“单线审批 驳回 退回”为主不要引入Flowable自己写一个状态机内核大概300行核心代码就能搞定。自研状态机的基本结构是一张路由表 一组动作方法。路由表approval_item_node负责定义每个事项的节点顺序和相邻关系大致字段包括item_id事项ID、node_code节点编码、node_name节点名称、sort_order排序、allow_reject_to允许退回到哪些节点用逗号分隔的节点编码。这张表的价值在于把流程定义从代码里抽离出来新增一个审批事项时只需要在库里配置链条不需要改Java代码。动作方法则围绕流转操作定义submit、approve、reject、withdraw、delegate、transfer、supplement。每个方法统一走一个处理入口比如processService.complete(taskId, action, opinion, operatorId)在这个方法内部完成三件事校验当前任务状态和操作人权限、写入审批记录、根据动作类型创建下一个任务或结束流程实例。关键约束是除了这个入口任何业务代码都不能直接修改任务状态把状态流转收敛到一个服务类里整个系统的流程逻辑才不会散落得到处都是。这个自研方案的好处在于核心逻辑少答辩时能讲清楚每一个方法的作用没有引入BPMN概念不增加评审的理解成本代码完全可控遇到特殊需求可以快速扩展。4.2 引入Flowable的边界条件与代价Flowable和Activiti这类工作流引擎价值在于处理复杂流程比如并行分支、会签投票、条件网关、子流程等。如果审批事项里出现“多个部门同时会签”“任一部门否决则流程终止”“根据申请金额走不同分支”这类需求自研状态机虽然也能实现但代码会越来越绕这时候工作流引擎就能体现出优势。但Flowable的代价也必须清楚一套完整工作流引擎内置几十张ACT_开头的表光依赖项就够你下载半天BPMN流程图需要专门的建模工具设计XML文件的学习曲线陡系统运行时还要维护流程定义版本、部署、挂起激活这些额外的操作。对于一个毕业设计或者轻量化系统来说这些复杂度很可能盖过业务逻辑本身。有一个典型的反问如果一个功能只需要“提交-科室审批-分管审批-办结”用Flowable硬套先要学BPMN语法再要搞网关事件还未必比状态机写得快。答辩时老师问“为什么选Flowable”如果回答“因为是主流框架”这不算好理由如果流程模型里根本没有并行和网关这个选择本身就值得被质疑。4.3 我最后采用的折中方案我自己在做一个中等复杂度的项目时采用折中方案以自研状态机为主处理90%的普通单线流程只把“需要多个部门并行会签”的少量事项做成一个特殊的任务模型在任务表里加一个task_group_id同组任务全部完成后才推进到下一环节。这个方案不需要引入Flowable同时覆盖了并行审批的演示效果。这个折中方案的实现思路是创建会签任务时为每个候选审批人生成一个proc_task记录这些记录共享同一个task_group_id和instance_id。每一笔审批结束只标记自己的任务为done完成后检查同组是否还有未处理的任务如果没有则在路由表的指引下由程序自动创建下一个节点任务。这种并行节点的状态检查代码量不多但能让流程设计增加很大的扩展空间。如果你硬要从Flowable和自研状态机二选一我给一个决策表判断维度自研状态机Flowable流程复杂度适合线性、单线程、少量驳回适合并行、网关、会签、子流程学习成本低半天上手高需要理解BPMN模型部署复杂度无额外依赖大量内置表和配置文件答辩解释成本低代码逻辑可控高需要解释引擎机制性能开销低相对更大扩展能力中等复杂流程需要重构高标准化模型结论很明确毕业设计级别除非任务书上明确要求“基于Activiti/Flowable开发”否则优先自研状态机。5. 权限与数据隔离菜单权限只是入门待办数据隔离才是分水岭5.1 RBAC的落地方式和按钮控制行政审批系统的权限设计包含两个层次菜单/按钮权限和数据权限。很多系统只做了第一层第二层完全没做这在审批场景里是致命伤。菜单和按钮权限就是标准的RBAC用户、角色、菜单/权限点之间用关联表建立关系。落地时后端接口加鉴权注解比如Sa-Token的SaCheckPermission(approval:apply:submit)前端再根据当前用户拥有的权限点动态控制“提交”“同意”“退回”按钮的显示。注意按钮权限不能只靠前端隐藏接口层面必须二次校验因为前端隐藏只保护UI不显示防不住有人直接调接口。审批记录表也要带上操作人ID和操作人姓名快照。这里有个设计细节审批人姓名应当冗余存储在审批记录表里而不是通过用户ID实时联表查询。原因很简单机关单位人员调整频繁一个人离职或调岗后历史审批轨迹上的名字仍然必须原样保留实时连表就会变成“查不到人”。5.2 待办列表的数据权限过滤从assignee到部门ancestors数据权限是审批系统真正见功底的地方。逻辑并不复杂但做错的人非常多最常见的错误是“所有待办任务全表查出来再在前端按当前登录人过滤”。这在演示数据少的时候看不出问题一旦数据量上百页面就明显卡顿而且接口暴露了本不应该暴露的数据不合规。正确的做法是在SQL层面过滤核心是三个维度。第一个人待办proc_task.status pending AND proc_task.assignee_id ?这查询当前登录人直接负责的任务。第二部门待办proc_task.status pending AND proc_task.candidate_dept_id ?再加上部门层级过滤。如果单位分三级架构部门表需要设计parent_id和ancestors字段如“1,2,5”表示路径查询本级部门及以下子部门的任务时用ancestors LIKE ?或者先查出子部门ID集合再IN查询。直接用部门名称字符串模糊查是低效且不可靠的写法。第三角色待办比如“党委办公室审核”这个环节不是某个人专属而是办公室一个角色都可以处理任务表里通过candidate_role_id关联角色查询时判断登录人角色集合是否包含这个候选角色。这三种维度在一个查询里体现为WHERE (assignee_id ? OR candidate_dept_id IN (?) OR candidate_role_id IN (?)) AND status pending。真正好的待办查询接口应该只返回当前人“能处理”的任务而不是返回全量任务再去过滤。5.3 临时授权与委托办理的设计机关单位人员出差、休假是常态所以“临时授权”和“委托办理”必须提前设计。常见模式是审批人A在系统里发起委托指定给B设置有效时间段在有效期内A名下新产生的待办任务自动进入B的待办列表。实现上可以在proc_task表加一个字段delegated_to_id任务生成时判断原审批人是否有有效委托关系有则直接创建给受托人。这样做的优点是委托信息在任务创建那一刻被固化之后A出差回来也不会影响历史任务的处理归属同时审批记录里要保留“原办理人A”“实际办理人B”两个字段保证责任可追溯。临时授权则是对角色的授权比如A临时接管部门审批角色本质上是在角色-用户关系表里增加一条带时间范围的授权记录。需要注意授权到期要有一个定时任务自动回收别让授权记录永远生效。6. 在线审批的页面交互审批详情页怎么设计才像“正经系统”6.1 详情页的信息分层与流程轨迹展示审批系统的用户体验集中体现在审批详情页。这个页面通常信息密度很高如果不做分层很容易变成一屏表格挤在一起使用方根本不知道要看的重点在哪。我习惯把详情页分成三个区域。顶部是一个办件状态栏显示办件编号、事项名称、当前环节、剩余时限和整体流程状态先用视觉焦点告诉用户“这东西现在到哪了”。中部是业务表单区展示申请人填写的具体内容包括动态表单字段和附件列表。底部是流转记录区展示从提交到现在所有审批轨迹每一条记录包含审批人、所属环节、动作提交/同意/退回、审批意见、操作时间。流程轨迹展示我建议用时间轴组件每一步审批记录按时间倒序排列。后端接口一次性返回该办件的完整流转历史前端不需要按节点逐个请求。这里的后端接口要返回一个结构化的列表字段包括nodeName、actionName、operatorName、createTime、opinion前端直接渲染即可。一个常见的败笔是审批意见列表和业务表单混在同一张表里用户要看意见还得翻半天。把“表单”和“意见轨迹”在视觉上彻底分开这是审批页面的基础体验分。6.2 时限计算、超时提醒与防重复提交审批时限要用工作日计算不能简单用自然日。机关单位周末不办公如果一个环节承诺3个工作日办结被提交在周五那截止日期应该是下周三而不是周一。实现上写一个工作日工具类输入开始时间和工作日数量循环跳过周六日再把可配置的节假日表加进来输出截止时间。节假日表直接用一张简单的holiday_config表维护每年年底录入即可。超时提醒用Spring的Scheduled定时任务实现每五分钟扫描一次proc_task表找出statuspending且deadline小于当前时间的任务生成提醒记录并推送到首页待办区的超时标签。这里要防止同一个超时任务被重复提醒提醒表需要加一个唯一约束比如task_id remind_type唯一索引。防重复提交也是审批操作里的刚需。前端在提交按钮点击后立刻置灰这只解决了一部分问题后端接口层面同样需要控制我用乐观锁处理即前面提到的版本号更新核心逻辑是处理任务时执行带version条件的update成功才继续后续的业务操作失败则直接返回“该任务已被处理”。把并发冲突挡在数据层之前。6.3 我在前端实现中踩的三个具体坑第一个坑是附件预览。内网办公环境浏览器版本普遍不高PDF预览最好用iframe srcpdf地址兼容性差建议直接把附件列表做成下载按钮为主预览按钮放在次要位置避免因浏览器兼容问题影响验收演示。如果一定要预览优先使用后端将PDF转图片再渲染可靠性更高。第二个坑是动态表单渲染。不同事项的申请表单字段不同前端要用JSON Schema或类似的结构动态生成表单而不是为每个事项写死一个Vue组件。字段配置存在biz_form_data表里key-value结构或JSON字段存储前端根据配置循环渲染输入框、下拉框、日期选择器。这个设计不复杂但能极大提升系统的通用性。第三个坑是流程轨迹时间轴没预留驳回分支。自研状态机支持驳回之后流程可能从“环节C”回到“环节A”时间轴组件如果默认按线性渲染就会错乱。我最后是让后端返回每条记录时带上nodeCode和actionType前端做条件判断驳回或退回的记录用不同的样式标识比如红色文字“退回至初审环节”并在该记录下方缩进展示其后续任务。7. 从开发环境到内网部署离线依赖与验收前的自检清单7.1 离线环境下的依赖打包与版本锁定机关单位的内网环境通常和外网物理隔离这就意味着你准备的所有依赖必须在开发机上提前下载。我的做法分三步走。第一步在能联网的机器上先把整个项目构建一遍然后执行mvn dependency:go-offline -Dmaven.repo.local./offline-repo把项目依赖的所有jar包下载到一个本地目录连同项目源码一起拷贝到内网机器。第二步在代码目录下把settings.xml里的本地仓库路径指向./offline-repo同时把Maven的mirror配置全部注释掉否则构建时Maven仍会尝试访问远程仓库然后卡住。这一步是最容易忽略的很多人明明已经准备了完整jar包构建还是失败原因就是Maven在尝试联网下载某一个快照版本。第三步版本锁定要极其严格。内网环境不能临时升降版本所以pom.xml里的每个依赖都应该写明确版本号不使用SNAPSHOT版本。SpringBoot和MyBatis-Plus、Sa-Token的版本兼容性最好提前在开发机验证好别拿到内网才突然冒出依赖冲突。7.2 日志审计、登录限制与定时备份部署之后还有些安全细节必须处理这些往往是验收专家重点盯的部分。第一操作日志必须完整。我在项目里用AOP的Around注解统一拦截所有写操作的Controller方法自动记录操作人、操作时间、请求方法、请求参数、操作结果和IP地址落库到sys_oper_log表。审批动作的日志尤其重要审批记录的写入和操作日志的写入尽量放在一个事务里避免出现审批记录存在但日志缺失的情况。第二登录失败次数限制。Sa-Token自带账号封禁功能配置连续输错5次密码锁定账号15分钟就能满足大多数安全要求。同时密码加密必须用BCrypt等自适应哈希算法不能是MD5。第三数据库定时备份。写一个shell脚本调用mysqldump每天凌晨2点导出全库备份保留最近7份再配合cron定时执行。演示环境可以不用这么复杂但答辩时能说出一套备份方案印象分会高不少。7.3 模拟数据与答辩验收的准备最后这条特别重要演示数据要花心思构造。很多同学开发完系统发现待办列表是空的审批流程全走完了演示时老师看不到系统在运转。我的建议是准备三类模拟数据每类都要有代表性。第一类“待办充足”的数据造5-8个不同事项的申请单分别停在流程的不同节点上确保每个审批人登录后待办列表都有内容可点。比如A登录看到3个待办B登录看到2个待办各角色都有事可做。第二类“完整链路”的数据准备1-2个从提交到办结的完整申请每一步审批意见都写上合理内容用来展示流程轨迹时间轴和办结统计。第三类“异常状态”的数据造一个超时未处理的办件用来展示超时提醒再造一个被退回、随后补正重新提交的办件用来展示驳回和二次提交的完整轨迹。演示前模拟数据一定要跑一遍确认每个列表的默认排序和状态显示符合预期别等答辩现场才发现某个办件的审批记录时间是乱序的。8. 几个做这类系统时最容易被忽视的实操体会写到这里我再分享几个我反复跟人强调的小细节。第一画流程图的时间绝不能省。开始写代码之前先把你要做的3到5个事项的完整流程画在纸上包括每个环节的审批人、退回路径、超时期限然后对照数据表结构检查有没有字段描述这个流转关系。表格画不出来说明设计还没到位写代码只会更乱。第二审批记录表和操作日志表是两回事。审批记录是业务数据描述审批环节的意见和结果面向的是审批流程本身操作日志是审计数据记录系统中的一切关键操作面向的是系统安全和追溯。两者字段、用途、保留策略都可以不同不要合并成一张表。第三数据库字段的注释一定要写清楚。审批系统表多字段含义不算直观比如node_code、task_group_id这种字段两个月后回头看可能都要猜。给每个字段写一行注释虽然不是功能但这是评审专家会看的细节。第四别在单个Controller里堆逻辑。我会把流程动作都放进独立的ProcessServiceController只做参数接收和响应封装。一个审批动作涉及的任务校验、记录写入、下一步节点创建全部在Service内完成。代码结构清晰了后面加需求、改流程自己维护起来才不崩溃。行政审批系统本质上不是一个创新难度极高的系统它的复杂度集中在流程建模、数据权限和过程审计这三个点。把这三点做好代码层面其实不需要很惊艳系统就能在真实场景里稳定跑起来。希望这套从需求拆解到落地部署的复盘能帮你少踩一些我踩过的坑。
阅读完成 · 觉得有帮助?