抽奖问题的复盘

2026-02-28T20:50:00

一次“看起来没问题,实际抽奖错人”的排障复盘:Excel 导入、Unicode 与精确匹配
今天我们定位并修复了一个抽奖页的实际生产问题:在 excluded_teachers 已配置 张三 的情况下,张三 仍然被抽中。

一、现象

  • 页面:/lottery
  • 配置:config/lottery.php 中已配置 excluded_teachers 包含 张三
  • 操作:上传 名单.xls 后抽
  • 结果:张三 仍有机会被选中

二、先排除错误方向
最开始容易怀疑是配置文件写错位置(例如 log.php),但实际抽奖读取的是 lottery 配置:

  • 后端把 config('lottery') 注入前端
  • 前端使用 excluded_teachers 做过滤
  • 所以问题不在 log.php

三、根因定位
根因不是“业务逻辑没写”,而是“字符串没有被正确拆分 + 精确匹配脆弱”。

  1. 成员列拆分正则存在编码风险
  • 原逻辑依赖中文标点字面字符进行 split
  • 文件历史编码/复制过程可能导致“看起来是同一个符号,实际码点不同”
  • 结果:如 张三,李四,王五 被当成一个整体字符串,没有拆成 3 个人
  1. 后续过滤是精确匹配
  • 过滤逻辑是 excludedSet.has(d.teacher)
  • d.teacher 变成整串时,当然匹配不到单独的 张三
  1. 同类风险也存在于 fixedTeachers / fixedGroups
  • fixedTeachers 之前是 d.teacher === fixedName
  • fixedGroups 之前是 groupsMap.has(fixedGroupName)
  • 都会受到空格、全角空格、字符形似异码点等问题影响

四、修复方案
本次修复分三层,目标是“抗编码污染 + 抗空白差异 + 保持原业务行为”。

  1. 分隔符改为 Unicode 码位写法
  • 将成员拆分正则改为:/[\u3001\uFF0C,\s]+/
  • 含义:按 ,、空白拆分
  • 好处:不依赖字面中文符号,减少编码转换导致的隐患
  1. 姓名与组名统一标准化后匹配
  • 新增标准化函数,去除半角/全角空白等
  • excluded_teachersfixed_teachersfixed_groups 均改为标准化后比较
  1. 组判重 usedGroups 也改为标准化键
  • 避免同组因为字符串形态差异被误判为不同组

五、为什么“编辑器看着正常”仍会出错

  • 编辑器显示的是“渲染效果”,不是“底层码点”
  • 正则和 split 比较的是真实字符码点
  • 所以会出现“肉眼一样,匹配失败”的情况

六、经验总结

  1. 不要把“看起来一样”当作“机器认为一样”
  2. 对中文标点分隔,优先使用 Unicode 码位表达
  3. 对关键匹配字段(人名、组名)先做标准化再比较
  4. 前端导入链路要有可观测性(建议保留 debug 日志或导入后样本检查)

七、这次问题的本质一句话
不是配置没写生效,而是“导入文本没有按预期拆分 + 精确匹配过于脆弱”,导致排除名单没有命中到真实候选人。

当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »