适用版本:EspoCRM 9.2.2+
只做前端 required 的团队,迟早会在报表上被”脏数据”反噬。
TL;DR
- Dynamic Logic 负责体验:用户当场知道”必须填”
- BeforeSave Hook 负责底线:任何入口都无法绕过
- 错误消息用可翻译 key 管理,默认英文输出便于排障
- 测试必须覆盖 UI + API + 边界切换
1. 场景:为什么”只做前端必填”是自欺欺人
当业务规定”阶段 = Closed Lost 时必须填写失败原因”,很多人第一反应是:在前端把字段设成 required。
问题在于:前端不是唯一入口。
- API PATCH/POST
- 批量更新
- 导入
- 自动化脚本
这些路径都可能绕过前端。你最终会得到一堆”Closed Lost 但无失败原因”的脏数据,报表和复盘完全失真。
结论:体验靠前端,底线靠后端。
2. 架构:双层校验
flowchart TD A[用户在UI切换 stage] --> B[Dynamic Logic: required] B -->|实时提示| C[用户填写字段] C --> D[保存] D --> E[BeforeSave Hook] E -->|ok| F[写入数据库] E -->|invalid| G[HTTP 400 Bad Request]
3. 前端:Dynamic Logic(实时必填)
Dynamic Logic 的价值:让用户”当场知道要填什么”,而不是保存时才被打回。
配置示例(公开版占位字段名):
- 字段:
cLostReasons - 条件:
stage == "Closed Lost"
1 | { |
4. 后端:BeforeSave Hook(强制校验,防绕过)
Hook 的价值:无论从哪里保存,规则都必须成立。
原则:
- 只做轻逻辑判断
- 抛出明确错误(建议英文错误信息,便于排障)
- 不做 HTTP、邮件、复杂计算
行为定义(伪代码):
1 | if stage == "Closed Lost" and cLostReasons is empty: |
代码骨架(公开版):
1 |
|
5. 测试矩阵(别只点 UI)
最少要覆盖 3 类入口:
| 入口 | 测试内容 | 期望结果 |
|---|---|---|
| UI | 切换 stage → 保存 | 实时提示 + 拒绝保存 |
| API | PATCH /api/v1/Opportunity/{id} |
HTTP 400 + 错误信息 |
| 边界 | 从 Closed Lost 切回其它 stage | 允许清空原因 |
API 测试示例(公开版占位符):
1 | curl -X PATCH "https://crm.example.com/api/v1/Opportunity/OPP_ID" \ |
6. 你应该坚持的”坏人假设”
把自己当作攻击者/绕过者,永远问一句:
“如果我不用 UI,用 API 直接写入,会不会写出脏数据?”