恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
OA系统审批流数据库设计:核心表结构、规则配置与索引优化
首页
资讯中心
/
OA系统审批流数据库设计:核心表结构、规则配置与索引优化
OA系统审批流数据库设计:核心表结构、规则配置与索引优化
发布时间:2026/10/13 22:42:32
简介《OA系统中流程审批数据库的设计》是一份面向企业信息化从业者、OA系统设计与开发人员的数据库设计文档重点解决流程审批模块中数据建模与表结构规划问题。资源以单个PDF文件呈现压缩包大小仅35KB内容精炼而系统。文档围绕流程实例表、活动实例表、审批任务表、规则配置表、历史版本表及用户权限表六大核心模块展开逐一说明字段设计、状态追踪、审批决策与规则映射的实现思路并兼顾历史追溯、权限安全与后期扩展性适合需要在现有OA框架中优化审批数据模型的读者参考。清晰的结构和贴近实际业务的讲解方式可以帮助读者快速建立流程审批数据库的整体设计框架。目前已有744人学习使用显示出较高的参考价值。1. 流程审批数据库设计OA系统最容易被低估的模块做过OA系统的人都知道流程审批模块是真正的黑匣子——表面上就是“提交、审批、归档”三个动作实际上牵涉到流程实例、活动节点、审批任务、规则路由、历史版本、权限校验一大堆表结构的设计。我拆过不少OA项目发现一个规律凡是审批流跑得稳的系统数据库设计一定在早期就把流程实例表、活动实例表、审批任务表这三者的关系理清楚了凡是后期改得痛不欲生的基本都是把审批状态直接塞在业务表里或者把规则写死在代码里不敢碰也碰不得。这份《OA系统中流程审批数据库的设计》讲的就是这件事适合正在做OA系统、或者准备重构审批模块的开发者。它把一套完整的审批流数据库模型拆开给你看包含流程实例、活动实例、审批任务、规则配置、历史版本、用户权限六张核心表的设计思路从表结构到关系映射都有覆盖。看完你能直接照着搭一套能跑的审批库而不是停留在概念层面。2. 核心表结构设计流程实例、活动实例与审批任务的职责拆分2.1 流程实例表审批流的“主档”该放什么字段流程实例表是整个审批数据库的锚点。很多人设计这张表的时候容易犯一个错误——什么都往里塞流程业务数据、当前审批人、审批意见全堆在一起结果就是表越来越胖查询越来越慢业务逻辑还拆不开。按照资料里的思路流程实例表只做一件事记录“一次审批从发起到结束”的完整生命周期。它的核心字段包括流程ID、发起人、开始时间、结束时间、状态字段以及关联业务数据的外键字段。CREATE TABLE process_instance ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 流程实例ID, process_def_id VARCHAR(64) NOT NULL COMMENT 流程定义ID对应具体流程模板, business_no VARCHAR(64) NOT NULL COMMENT 业务单号如订单号、合同编号, business_type VARCHAR(32) NOT NULL COMMENT 业务类型用于区分不同审批流程, initiator_id BIGINT NOT NULL COMMENT 发起人用户ID, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0新建 1进行中 2已完成 3已撤销, start_time DATETIME NOT NULL COMMENT 发起时间, end_time DATETIME NULL COMMENT 结束时间, current_node_id BIGINT NULL COMMENT 当前所在节点ID方便快速定位, deleted TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除标记, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_business (business_type, business_no), KEY idx_initiator (initiator_id, status), KEY idx_status (status, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT流程实例表;这段设计的核心在于business_no和business_type两个字段的组合。它们的作用是建立审批流程与具体业务数据的桥梁——比如采购审批对应采购单号合同审批对应合同编号。我在实际项目中习惯再加一个current_node_id字段用来记录当前进行到的节点这样查询“我发起的流程现在走到哪了”的时候不需要去活动实例表里子查询直接拎这个字段就能展示。代价是每次节点流转时需要多一次 UPDATE但这个代价在绝大多数OA场景下是值得的。状态字段status建议用 TINYINT 存数字而不是字符串理由很简单数字在 InnoDB 里只占 1 字节而字符串最少也要 4 字节UTF-8 下的 done 是 4 字节。而且数字状态可以直接放在索引里做范围查询比如查“所有已完成且在某个时间段内的流程”走联合索引效率会高不少。2.2 活动实例表按步骤记录执行轨迹活动实例表是容易被忽略的一张表。很多初级设计者在做审批流的时候只建了任务表觉得有了任务表就能追踪进度实际上任务表只能告诉你“哪个节点谁审了”不能告诉你“当前流程到底执行到第几步”。活动实例表解决的是“流程步骤”的追踪问题它记录的是流程实例在执行过程中的每一个活动节点相当于流程的“过程日志”。CREATE TABLE activity_instance ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 活动实例ID, process_instance_id BIGINT NOT NULL COMMENT 所属流程实例ID, activity_def_id VARCHAR(64) NOT NULL COMMENT 活动定义ID对应流程模板中的节点, activity_name VARCHAR(128) NOT NULL COMMENT 活动名称如部门经理审批, executor_id BIGINT NULL COMMENT 执行人ID执行人是空的说明是系统自动节点, start_time DATETIME NOT NULL COMMENT 活动开始时间, finish_time DATETIME NULL COMMENT 活动完成时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 活动状态0待执行 1执行中 2已完成 3已跳过, remark VARCHAR(255) NULL COMMENT 备注记录跳转原因等, KEY idx_process (process_instance_id, start_time), KEY idx_executor (executor_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT活动实例表;这张表的核心价值在于两点。第一它能做效率分析——通过计算finish_time和start_time的差值可以统计出每个节点的平均审批耗时找出流程中的瓶颈环节。第二它能支撑流程的复杂度控制——比如我们可以在流程发起时不立即生成所有活动实例而是只生成当前节点的实例每完成一步再创建下一个。这种“按需实例化”的方式更适合复杂的OA流程因为很多审批分支要等前面的条件判断完才能确定要不要走。状态字段除了常见的待执行、已完成我建议保留一个已跳过状态。这个状态在处理“或签”“会签”这类复杂节点特别好用。比如会签节点有三个人要审其中两个人通过就放行那么第三个人的活动状态就可以标记为“已跳过”这样既保留了完整轨迹又不会让统计口径出错。2.3 审批任务表把“谁该干什么”落到实处的关键三张核心表里审批任务表是跟业务逻辑耦合最深的一张。它的职责是存储每个审批节点的具体任务信息——谁审、审什么、结论是什么、意见是什么。换句话说活动实例表回答的是“流程走到哪了”审批任务表回答的是“这一步具体是谁做的、怎么做的”。CREATE TABLE approval_task ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 任务ID, activity_instance_id BIGINT NOT NULL COMMENT 所属活动实例ID, task_def_id VARCHAR(64) NOT NULL COMMENT 任务定义ID, approver_id BIGINT NOT NULL COMMENT 审批人用户ID, approver_name VARCHAR(64) NOT NULL COMMENT 审批人姓名冗余字段, approve_result TINYINT NULL COMMENT 审批结果0拒绝 1同意 2驳回 3转交, approve_comment VARCHAR(500) NULL COMMENT 审批意见, task_status TINYINT NOT NULL DEFAULT 0 COMMENT 任务状态0待处理 1已处理 2已取消, receive_time DATETIME NOT NULL COMMENT 接收时间, handle_time DATETIME NULL COMMENT 处理时间, KEY idx_activity (activity_instance_id), KEY idx_approver (approver_id, task_status), KEY idx_status (task_status, receive_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT审批任务表;这里有两个设计细节值得展开。第一个是approver_name这个冗余字段。规范化设计理论上不应该存这个名字查用户表 JOIN 就能拿到但实际生产中我是建议存上的——审批意见是需要长期留痕的数据万一员工离职、用户记录被清理审批记录里的名字不能跟着消失。第二个是approve_result的取值很多人只设计“同意、拒绝”两个值我建议加上“驳回”和“转交”。转交是OA里特别常见的操作比如审批人觉得这事该财务审把任务转给财务如果只存“拒绝”流程逻辑上就得走“不同意”分支但从业务语义上讲根本不是一回事。从规范化的角度来看这套设计的核心是流程实例表为根活动实例表为中间层审批任务表为叶子。这种分层结构最大的好处是支持“并行审批”——多个审批任务挂在同一个活动实例下系统判断活动是否完成就看挂载的任务状态是否全部满足条件互不干扰。3. 规则配置与历史版本流程灵活性和可追溯性的底层支撑3.1 规则配置表用数据库驱动流程走向设计审批数据库的时候如果只做三张主表系统只能跑固定流程——A审完给BB审完给C结束了。但真实企业环境里的审批流没这么简单金额超过五万要总监审批超过二十万要总经理审批还要经过财务会签某些特定项目的合同要跳过部门审批直接走法务。这些规则如果写死在代码里每调整一次都要发版很不现实。规则配置表的价值就是把“什么时候走哪个分支、谁能审”这些决策逻辑搬进数据库让管理员通过后台界面就能调整。一个可落地的做法是采用“表达式配置 解析执行”的方式而不是设计一堆复杂的条件字段。表达式配置让规则的表达能力更强而且和具体业务解耦。CREATE TABLE approval_rule ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 规则ID, process_def_id VARCHAR(64) NOT NULL COMMENT 所属流程定义ID, rule_name VARCHAR(128) NOT NULL COMMENT 规则名称, rule_type TINYINT NOT NULL COMMENT 规则类型1条件路由 2审批权限控制, condition_expr VARCHAR(500) NOT NULL COMMENT 条件表达式如 amount 50000 level 3, action_expr VARCHAR(500) NOT NULL COMMENT 动作表达式如 route_to:node_004 或 allow:role_finance, priority INT NOT NULL DEFAULT 100 COMMENT 优先级数字小的先执行, enabled TINYINT NOT NULL DEFAULT 1 COMMENT 是否启用, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_process_rule (process_def_id, rule_type, priority) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT审批规则配置表;condition_expr字段是整个设计的重点。我见过两种主流方案一种是存 JSON 结构把条件拆成field value field2 value2这样的数组另一种是直接存可解析的表达式由规则引擎在运行时解析执行。从灵活性角度直接存表达式显然是更强的方案——新增条件类型不需要改表结构只需要让规则引擎支持新的比较操作符。从安全角度看存表达式必须警惕注入风险推荐用安全的表达式解析库而不要自己用 eval 之类的函数去执行。rule_type字段把规则分成两类也是为了可维护性。条件路由规则负责决定流程下一步去哪个节点审批权限规则负责决定当前节点谁能审批。实际项目里这两类规则的调整频率都挺高合在一张表里没问题但通过类型区分能让后台配置界面更清晰。3.2 历史版本表为审批留一条后悔药审批流程最忌讳的就是“数据被覆盖”。举个例子一份采购合同发起了流程第一次审批被部门经理驳回发起人修改后又重新提交这时候流程实例表里只有一条记录指向的是最新一次提交。如果运营想知道第一次提交的合同金额是多少、驳回意见是什么光看主表是看不到的。历史版本表就是为此存在的——每次流程状态发生重大变化比如驳回修改、节点跳转都把变化前后的数据快照下来。CREATE TABLE process_history_version ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 版本ID, process_instance_id BIGINT NOT NULL COMMENT 流程实例ID, version_no INT NOT NULL COMMENT 版本号从1开始递增, snapshot_data JSON NOT NULL COMMENT 该版本下的完整业务数据快照, change_log VARCHAR(500) NOT NULL COMMENT 变更说明如被部门经理驳回后修改合同金额, operator_id BIGINT NOT NULL COMMENT 操作人ID, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_process_version (process_instance_id, version_no), KEY idx_operator (operator_id, created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT流程历史版本表;snapshot_data字段用 JSON 类型存储这是这套设计里比较现代化的一点。流程实例表里的business_no只存了业务单号具体的业务数据长什么样在主表里是看不到的。用 JSON 快照把每次提交时业务数据的关键字段存下来后面做审计、做回溯、甚至做个历史版本对比功能都特别方便。在 MySQL 5.7 之后 JSON 类型支持字段提取函数可以直接在 SQL 里做 JSON 条件过滤比用 TEXT 存拼接字符串靠谱得多。版本记录时机也要想清楚。我的做法是在每个审批动作完成后触发一次版本记录——无论是同意、驳回还是转交这就保证任何时间点的流程状态都能被还原。追加个细节版本表不建议存全量大字段比如合同正文的 PDF 内容不要塞进 JSON 快照快照只存结构化的关键字段金额、数量、有效期这些大文件走对象存储表里存文件路径就够了。这样做既保证了追溯能力又控制住了表体积。3.3 用户表与角色权限表审批链路上的安全护栏审批数据库如果只关注流程本身而忽略权限设计很容易出现越权审批这类生产事故。用户表和角色权限表的职责就是确保“谁有资格审、谁能看这个单子”。这里的设计要点是把“用户”和“角色”拆成两张大表中间用关联表连接而不是在用户表里塞一个role字段。CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID, username VARCHAR(64) NOT NULL UNIQUE COMMENT 登录账号, real_name VARCHAR(64) NOT NULL COMMENT 真实姓名, dept_id BIGINT NOT NULL COMMENT 所属部门ID, enabled TINYINT NOT NULL DEFAULT 1 COMMENT 账号状态, KEY idx_dept (dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE sys_role ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 角色ID, role_code VARCHAR(64) NOT NULL UNIQUE COMMENT 角色编码如 ROLE_MANAGER, role_name VARCHAR(64) NOT NULL COMMENT 角色名称, permission_json JSON NOT NULL COMMENT 权限定义如 {view: true, approve: true} ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT角色表; CREATE TABLE sys_user_role ( user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, PRIMARY KEY (user_id, role_id), KEY idx_role (role_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户角色关联表;权限模型有 RBAC 和 ABAC 两大流派。资料里的设计思路更接近 RBAC——角色绑定用户角色拥有固定的操作权限查看、编辑、审批。对于大多数OA场景RBAC 已经够用而且模型简单团队容易理解和维护。如果遇到特别复杂的场景比如“金额超过十万且本人在场时经理才能审批”这种就要上 ABAC 策略引擎了——把规则配置表里的condition_expr和permission_json结合来做动态判定。权限字段用 JSON 存储有一个好处扩展系统新增操作类型时不需要做 DDL 迁移。比如原来只有view、approve两种操作后来要加一个forward转交操作用 JSON 就是加个键值对的事如果用独立的权限明细表就要插一批新记录。当然 JSON 的劣势是无法在 SQL 级别做字段约束校验这个需要在应用层补齐。4. 避坑指南审批流数据库设计的五个常见坑4.1 坑一把所有业务数据塞进流程实例表现象流程实例表越来越胖光字段就有四十多个业务扩展一点就要加列。流程和业务的耦合越来越重通用流程引擎完全做不起来。原因设计时想着“一次查完不用关联”把合同编号、金额、供应商、付款方式等业务字段全部建到了流程实例表里。不同业务类型的字段差异很大导致表里大量字段对某个业务是 NULL白占存储空间还把索引搞得很臃肿。解决流程实例表只保留business_no和business_type业务细节建模到独立的业务表通过业务单号关联。泛型设计的好处是新增一个审批类型不用动表结构只需要新加一张业务表流程引擎完全不用改。4.2 坑二用 DELETE 物理删除审批记录现象后台误操作删了一条审批记录审计查不到记录财务对账也對不上了没有任何办法恢复。原因审批数据本质上属于“电子凭证”有法律效力和审计要求。当初直接 DELETE 是为了省事想让表“干净”但审批表最不需要的就是物理删除。解决全部换成逻辑删除加deleted字段默认填 0删除操作变成 UPDATE 置 1。查询时统一带where deleted 0过滤。如果需要恢复一条 UPDATE 就能还原不用从 binlog 里翻数据。4.3 坑三状态字段直接存中文现象status字段的值是“新建”“进行中”“已完成”排序查不出顺序范围统计也没法做。原因图省事觉得开发的时候写中文一眼能看懂。但数据库里用 TINYINT 或 VARCHAR 存数字状态再用枚举类和字典表映射才是可维护的设计。解决状态字段全部改用数字枚举比如 0-新建、1-进行中、2-已完成、3-已撤销、4-已终止。代码层用枚举常量定义好在查询语句里对照。应用显示层做字典翻译页面展示还是中文数据层保持纯净。4.4 坑四不建联合索引按状态查流程巨慢现象流程量大之后“查某个用户待审批的任务”这个高频接口响应超过三秒数据库 CPU 被打满。原因只在主键上建了索引按approver_idtask_status查的时候全表扫描。虽然单看数据量不大但OA系统里“待办查询”可能是被轮询的接口频率极高每次都全表扫性能就崩了。解决按高频查询路径建联合索引。待办查询是where approver_id ? and task_status in (待处理)那就在审批任务表上建idx_approver_status (approver_id, task_status)。如果还要按时间排序看“最近待办”改成idx_approver_status_time (approver_id, task_status, receive_time)。4.5 坑五规则配置表用递归自关联现象规则配置变成了无限级树形结构parent_id 指来指去调用链超长改一个规则要debug很久。原因最初想把流程分支做得足够灵活设计成每层节点都能配多条子规则表结构变成自关联嵌套。结果是开发和后续维护都崩溃了因为没人能一眼看出完整的规则链是什么样子。解决用显式节点编号替代树形递归。流程模板定义好node_001 - node_002 - node_003的有向结构规则表通过condition_expr的“路由目标”字段指定下一个节点路由逻辑平铺展开完全不要自关联。表达能力确实比递归设计略弱但可维护性大幅提升——然后你会发现绝大多数业务根本用不到那点少出来的灵活性。5. 索引设计与验证技巧让你设计的审批库真的能跑5.1 从索引到分区把查询性能逼到极限前面避坑部分提了联合索引这里再系统过一遍审批场景的索引设计策略。审批流数据库的查询模式非常固定高频查询就三类查待办、查已办、查我发起的流程。每类查询的筛选条件不同联合索引的字段顺序要跟查询条件逐一匹配。查询场景筛选条件建议索引我的待办approver_id task_status receive_timeidx_approver_status_time (approver_id, task_status, receive_time)我已办理approver_id handle_timeidx_approver_handle (approver_id, handle_time)我发起的流程initiator_id start_timeidx_initiator_time (initiator_id, start_time)按业务单号查流程business_type business_noidx_business (business_type, business_no)建索引的时候要记住最左前缀原则idx_approver_status_time这个索引对approver_id task_status的查询也有效但如果你直接只用receive_time查就完全走不上这个索引。所以设计索引时先确认查询语句里 WHERE 条件的顺序把等值条件的字段放最前面范围条件的字段放最后面。数据量超过千万级时可以考虑对流程实例表按时间做范围分区。分区策略按start_time做 RANGE 分区比较合适因为审批数据天然有时间属性归档也方便。举例按年分区当月数据落在当年分区老数据落历史分区DELETE 清理历史数据时直接 DROP PARTITION 比 DELETE FROM WHERE 快几个数量级而且不影响在线数据。ALTER TABLE process_instance PARTITION BY RANGE (YEAR(start_time)) ( PARTITION p2022 VALUES LESS THAN (2023), PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025), PARTITION p_future VALUES LESS THAN MAXVALUE );分区不是银弹主键必须包含分区键是它的一个限制——上面的例子意味着PRIMARY KEY必须改成PRIMARY KEY (id, start_time)。还有跨分区查询如果没带分区的等值或范围条件会扫描所有分区反而更慢。所以分区策略建议在数据量真的到了千万级再用初期做好联合索引已经能应对绝大多数场景。5.2 验证设计用一个SQL跑通端到端审批流表结构设计完怎么确认这套库真的能支撑完整审批流我自己的习惯是写三条 SQL 做端到端验证能跑通就说明表结构没大问题。第一条验证流程实例创建是否正确-- 验证1创建流程实例后能正确查到发起人的“我发起的流程” INSERT INTO process_instance (process_def_id, business_no, business_type, initiator_id, status, start_time, current_node_id) VALUES (PURCHASE_ APPROVAL_001, PO-2024-001, purchase, 1001, 0, NOW(), NULL); SELECT id, process_def_id, business_no, status FROM process_instance WHERE initiator_id 1001 AND status 0; -- 期望结果返回刚插入的记录状态为“新建”第二条验证活动实例推进是否连通流程和任务-- 验证2流程推进到下一步插入新的活动实例和审批任务 INSERT INTO activity_instance (process_instance_id, activity_def_id, activity_name, executor_id, start_time, status) VALUES (1, node_002, 部门经理审批, 2001, NOW(), 1); INSERT INTO approval_task (activity_instance_id, task_def_id, approver_id, approver_name, task_status, receive_time) VALUES (1, task_002, 2001, 张三, 0, NOW()); -- 关联查询能看到当前流程节点和待办人 SELECT pi.business_no, ai.activity_name, at.approver_name FROM process_instance pi JOIN activity_instance ai ON pi.id ai.process_instance_id JOIN approval_task at ON ai.id at.activity_instance_id WHERE pi.id 1 AND at.task_status 0;第三条验证审批完成后流程状态正确关闭-- 验证3任务处理完活动完成流程实例标记为“已完成” UPDATE approval_task SET task_status 1, approve_result 1, handle_time NOW() WHERE id 1 AND approver_id 2001; UPDATE activity_instance SET status 2, finish_time NOW() WHERE id 1; UPDATE process_instance SET status 2, end_time NOW(), current_node_id NULL WHERE id 1; -- 确认流程实例彻底关闭 SELECT id, status, end_time FROM process_instance WHERE id 1; -- 期望结果status 2已完成end_time 非空这三条验证能把流程主链路跑通——发起、推进、审批、完成。如果再加上一条驳回分支的验证任务审批结果为“驳回”流程实例状态回到某个前置节点然后循环第二步基本就能覆盖百分之八十的日常审批场景。5.3 从设计到落地的几个实操习惯直接照着资料建库容易忽略两件事——字段注释和事务边界。表结构建好后每张表、每个字段都要写 COMMENT这不仅是给后来人看的也是给三个月后的自己看的——审批逻辑牵涉到状态流转字段含义一旦模糊后面分析问题就要翻代码。事务边界更要提前理清楚。审批动作不是一个 UPDATE 就能搞定的它至少是“插入审批任务记录 → 更新活动状态 → 更新流程实例状态”三步。这三步必须在一个数据库事务里完成任何一个失败都要回滚。我自己在做这样的功能时会强制要求 DAO 层方法名带上操作标识比如approveWithTransaction团队 review 代码的时候一眼就能看出哪个方法是需要事务的。搭配上面的事务要求还要在设计阶段就确认一个偏好——审批流表做主从分离还是单库部署。如果公司的OA和业务系统共用同一个库需要确认表前缀比如都加oa_前缀避开命名冲突如果单独部署就按独立库来设计不用考虑业务库的兼容性表名字段可以更自由一些。这个决定要在建表前拍板因为后面改起来很痛苦。做了这么多年 OA 系统项目我的一个铁律是任何审批流的表结构变更都必须先出迁移脚本、再改代码逻辑绝不直接在线改表结构。尤其是遇到像审批任务表这种每日写入量很大的表用在线 DDL 工具做结构变更比直接 ALTER TABLE 安全得多。从那以后我每次做完表设计都会强制自己先写一遍端到端的验证脚本把三张主表的关联查询跑通了再交付——这个习惯帮我挡掉了不少上线后的低级错误希望也能帮到你。本文还有配套的精品资源点击获取