运营人员配置预订字段时,字段标签既出现在管理表单,也会进入消费者填写页面。若创建、编辑和历史归档对“重名”的定义不同,用户可能在管理端保存成功,却在另一个入口遇到冲突。这个案例的关键是先定义同一条规则,再决定每一层承担什么。
本案例把标签首尾空格去掉,再按不区分大小写的方式比较;范围限定在同一公司。归档字段仍占用名称,避免旧订单引用的字段与新字段同名。编辑当前字段且标签实际未变化时,不应因历史数据而阻止保存;改成其他字段已有的标签才报冲突。
| 场景 | 期望结果 |
|---|---|
新建 Email 后再建 email | 拒绝,提示标签已存在 |
| 标签已归档,再用相同名称新建 | 仍拒绝 |
| 编辑字段但保持原标签 | 允许保存其他设置 |
| 把另一字段改名为已有标签 | 拒绝并定位到标签输入框 |
| 不同公司的同名字段 | 各自独立判断 |
提交前先查询已有字段,包括归档项;结果分页时要检查完整列表,不能只看第一页。发现冲突就把错误显示在标签输入框旁并聚焦该字段,而不是给出笼统 toast。编辑且名称未变时跳过这次预检查,避免无意义的请求。
这一步只能改善操作体验。列表可能刚好过期,也可能因网络故障不可用;此时写接口仍是最终判断者。后端返回明确的冲突码后,前端同样要把错误映射回标签字段。
创建和改名都在事务中按公司取得同一把锁,再检查标签是否已存在。比较范围包含归档记录;改名时排除当前字段。这样两个遵循同一流程的请求不会同时通过检查。冲突以稳定的 409 业务错误返回,前端不需要解析错误文案来猜测原因。
这里的锁是应用写入路径的约定,不等同于数据库唯一索引。新增批量导入、脚本或其他写入入口时,必须复用这条规则;并发双请求测试也应单独验证。若将来把归一化值持久化,并能安全处理旧数据,可再评估数据库约束作为更强的最终保护。
前端 toLowerCase() 与 Python 的 casefold() 并不对所有 Unicode 字符给出相同结果。预检查可以沿用界面可用的近似规则,但不能把它当成服务端判定的复制品。对差异字符、空白字符和历史数据,应以服务端规则为准,并给前端一个明确的冲突响应。
验收时分别覆盖前端的提前拦截、服务端冲突后的字段级错误、归档字段、大小写与空格、未改名编辑和跨公司隔离。代码与这些路径测试能证明规则被接入;是否在目标环境生效,还需要部署和实际操作验证。