存储过程通过查approval_flow_config表获取status_to和next_role实现状态推进,用updlock, holdlock或版本号避免并发错乱,审批后插入notification_queue解耦通知,并统一由usp_update_approval_status按@action参数处理各类状态变更及审计。

存储过程里怎么判断当前审批节点并更新状态
核心是用 SELECT 查出当前待办记录的 current_status 和 next_approver_id,再根据业务规则决定是否推进。别直接写死状态码,用表驱动更稳——建一张 approval_flow_config 表,字段含 status_from、status_to、next_role、auto_advance(是否自动流转)。调用时先查配置,再执行更新:
SELECT @next_status = status_to, @next_role = next_role FROM approval_flow_config WHERE status_from = @current_status AND auto_advance = 1
查不到就停住,不强行推进;查到多个?说明流程定义冲突,该报错而不是静默覆盖。
如何避免并发审批导致状态错乱
多人同时点“同意”,可能都读到同一 current_status,然后都去更新,结果只有最后一条生效,中间的审批动作丢失。必须加行级锁或乐观并发控制:
- 用
UPDATE ... WITH (UPDLOCK, HOLDLOCK)锁住待更新的记录,防止其他会话读到旧状态 - 或者加版本号字段
version_no,更新时带上WHERE version_no = @expected_version,失败就重试或抛异常 - 别用
SELECT + UPDATE两步走,中间有间隙,必须合并成原子操作
审批通过后怎么触发下一级任务生成
状态更新只是第一步,真正要让下一级看到待办,得插入新记录到 approval_tasks 表。关键点是:不能在存储过程中直接发邮件或调外部 API(SQL Server 不适合做这些),而是写入一个 notification_queue 表,由外部服务轮询消费:
INSERT INTO notification_queue (task_id, target_user_id, event_type, created_time) VALUES (@task_id, @next_user_id, 'PENDING_APPROVAL', GETDATE())
如果非要在数据库内通知,可用 sp_send_dbmail,但得确认数据库邮件已启用且账户有权限,否则存储过程会静默失败。
回退、驳回、撤回这些异常路径怎么统一处理
多级流最怕分支逻辑散落在各处。建议把所有状态变更都收口到一个主存储过程,比如叫 usp_update_approval_status,用 @action 参数区分行为:'APPROVE'、'REJECT'、'WITHDRAW'、'REASSIGN'。每种 action 对应不同的状态跳转规则和校验条件:
-
REJECT必须检查当前节点是否允许驳回(有些环节只允许通过或转交) -
WITHDRAW只能由申请人发起,且状态必须是'PENDING'或'IN_PROGRESS' - 所有变更都要写审计日志,至少记
task_id、old_status、new_status、operator_id、updated_time
状态机不是越复杂越好,关键是每个跳转都有明确约束,而不是靠应用层拼凑逻辑。漏掉一个 WHERE 条件,或者没校验角色权限,整个流程就可能被绕过。











