用tinyint存状态码(0=待审、1=通过、2=拒绝、3=撤回),配comment说明;分设status与current_step字段支持多级审批;加can_rollback标记和approval_log表保障回退安全;建联合索引idx_pending(status, current_step, approver_id)优化查询。

审批状态字段怎么建才不踩坑
直接用 TINYINT 存状态码(比如 0=待审、1=通过、2=拒绝、3=撤回),别用字符串或枚举。字符串查起来慢,枚举改起来疼;后期加状态时还得改表结构,ALTER TABLE 锁表风险高。
- 状态值必须有明确业务含义,写进注释里,比如:
COMMENT '0:pending,1:approved,2:rejected,3:withdrawn' - 避免用负数或超大整数当状态码,MySQL 的
TINYINT范围是 -128~127,够用但别越界 - 别把「审批人」和「状态」混在一个字段里——状态是流程节点,人是执行者,分开放才好查、好审计
如何用单张表支撑多级审批(如部门→总监→VP)
靠一个 step_order 字段 + 状态流转控制,而不是为每级审批建一张表。多表意味着 JOIN 多、事务难控、历史记录散落。
- 在审批主表加
current_step TINYINT DEFAULT 0,表示当前卡在哪一级(0=未开始,1=部门审批中,2=总监审批中…) - 每次审批动作只更新本 step 的状态和操作人,同时检查
current_step是否匹配预期,防止跳步或重复提交 - 用触发器或应用层逻辑保证:只有上一级
status = 1(通过)后,current_step才能自增;任一级拒绝就直接置current_step = -1表示终结
审批驳回后想退回上一步,怎么安全实现
不能简单把 current_step 减 1 —— 上一级可能已归档、审批人已离职、或该步骤本身不允许回退。得靠「可回退标记」+ 显式记录路径。
- 加一个
can_rollback BOOLEAN DEFAULT FALSE字段,每次进入新 step 前由业务规则决定是否允许退回(比如总监级驳回可退给部门,但 VP 驳回不可退) - 用单独的
approval_log表存每步操作,含step_id、operator_id、action('approve'/'reject'/'rollback')、created_at,别依赖主表字段拼历史 - 执行回退时,先查
approval_log找上一个非驳回的 step,再校验该 step 的can_rollback是否为TRUE,否则报错'rollback_not_allowed_for_step_x'
查询「我待处理的审批」为什么越来越慢
因为没索引,或者索引没覆盖查询条件。典型场景是查 WHERE status = 0 AND current_step = 2 AND approver_id = 123,但只在 status 上建了单列索引。
- 必须建联合索引:
INDEX idx_pending (status, current_step, approver_id),顺序不能错:等值查询字段放前面,status和current_step是高频过滤条件 - 如果还有按时间排序需求(如
ORDER BY created_at DESC),考虑扩展为:INDEX idx_pending_time (status, current_step, approver_id, created_at) - 定期用
EXPLAIN SELECT ...看是否走了索引;注意status如果区分度极低(比如 95% 都是 0),MySQL 可能放弃索引走全表,这时得结合分区或冗余字段优化
真正麻烦的不是建表,而是状态变更的边界条件:谁能在什么时机改哪个字段、失败后要不要回滚、日志和主表怎么保持一致。这些没法靠 DDL 解决,得在应用代码里死守几条 if 判断和事务范围。











