真正在生产环境跑得稳的自动分配需用数据库层trigger而非应用层代码,因其能统一覆盖多入口线索、避免并发竞态、天然支持事务级审计回溯,且必须采用before insert确保分配与插入原子性。

INSERT 语句一执行,线索就该进销售池——但直接在应用层写分配逻辑容易绕过权限校验、漏掉并发冲突、还难追溯。真正在生产环境跑得稳的自动分配,得靠数据库层的 TRIGGER 配合预置规则表,而不是靠后端代码“抢着改”。
为什么不用应用层分配而选 TRIGGER?
不是技术炫技,是解决三个实际卡点:
• 新线索可能来自多入口(表单、API、CSV导入),应用层逻辑容易漏覆盖
• 多人同时导入时,应用层查-判-插存在竞态窗口,SELECT ... FOR UPDATE 又拖慢吞吐
• 审计要求“谁在何时因何规则被分配”,TRIGGER 能天然绑定事务上下文,日志可回溯
BEFORE INSERT 还是 AFTER INSERT?
必须用 BEFORE INSERT:
• 分配动作要和插入同事务,失败则整条记录回滚,避免线索“进库没主”
• AFTER INSERT 无法修改即将插入的行,你没法把 owner_id 塞进去
• Zoho、纷享销客等系统底层建模也默认走 BEFORE 模式做字段预填充
分配逻辑怎么落到数据库里?
别在 TRIGGER 体里硬编码 if-else。真实项目都拆成两层:
• 第一层:规则表 lead_assignment_rules,字段含 region、industry、min_value、salesperson_id、priority
• 第二层:TRIGGER 里只做一次 SELECT ... ORDER BY priority LIMIT 1 查匹配项
• 关键细节:industry 字段允许为 NULL 表示“通配”,查时用 COALESCE(NEW.industry, 'any') = rules.industry 统一处理
• 避坑点:MySQL 8.0+ 支持 ROW_NUMBER() OVER(),但触发器里不能用窗口函数,得靠 ORDER BY + LIMIT 替代
怎么应对轮询(Round Robin)这类动态规则?
轮询不能靠静态规则表硬写,得加状态追踪:
• 单独建表 salesperson_load,存 salesperson_id 和 last_assigned_at
• TRIGGER 中先 UPDATE salesperson_load SET last_assigned_at = NOW() WHERE salesperson_id = ?,再用子查询取 MIN(last_assigned_at) 找最闲的人
• 注意:这个 UPDATE 必须加 FOR UPDATE 锁,否则高并发下多人读到同一最小值
• 不推荐用自增ID模除法——销售离职或新增时,模数一变全乱套
lead_assignment_rules 表的变更监听起来,推到应用层刷新内存缓存——那已经是另一层同步机制了。











