适用版本:EspoCRM 9.2.2+
只做前端 required 的团队,迟早会被”脏数据”反噬。
TL;DR
- Dynamic Logic 负责体验:用户当场知道”必须填”
- BeforeSave Hook 负责底线:任何入口都无法绕过
- 错误消息用可翻译 key 管理,默认英文输出便于排障
- 测试必须覆盖 UI + API + 边界切换
1. 场景:为什么”只做前端必填”是自欺欺人
当业务规定”阶段 = Final Rejected 时必须填写拒绝原因”,很多人第一反应是:在前端把字段设成 required。
问题在于:前端不是唯一入口。
- API PATCH/POST
- 批量更新
- 导入
- 自动化脚本
这些路径都可能绕过前端。你最终会得到一堆”Final Rejected 但无拒绝原因”的脏数据,报表和复盘完全失真。
结论:体验靠前端,底线靠后端。
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 的价值:让用户”当场知道要填什么”,而不是保存时才被打回。
配置示例(公开版占位字段名):
- 字段:
rejectionReason - 条件:
stage == "Final Rejected"
1 | { |
4. 后端:BeforeSave Hook(强制校验,防绕过)
Hook 的价值:无论从哪里保存,规则都必须成立。
原则:
- 只做轻逻辑判断
- 抛出明确错误
- 不做 HTTP、邮件、复杂计算
行为定义(伪代码):
1 | if stage == "Final Rejected" and rejectionReason is empty: |
代码骨架(公开版):
1 |
|
5. 测试矩阵(别只点 UI)
最少要覆盖 3 类入口:
| 入口 | 测试内容 | 期望结果 |
|---|---|---|
| UI | 切换 stage → 保存 | 实时提示 + 拒绝保存 |
| API | PATCH /api/v1/Opportunity/{id} |
HTTP 400 + 错误信息 |
| 边界 | 从 Final Rejected 切回其它 stage | 允许清空原因 |
API 测试示例(公开版占位符):
1 | curl -X PATCH "https://crm.example.com/api/v1/Opportunity/OPP_ID" \ |
6. 总结
- 不要相信前端输入:这是安全和数据完整性的第一原则。
- Dynamic Logic 是“防呆”,BeforeSave Hook 是“防漏”。
- 随着系统复杂度增加(引入 API 集成、数据导入),这种“双层校验”模式将成为维护数据质量的基石。
- 建议:从今天开始,检查你所有的 required 字段,看看它们是否在后端也有同样的守护。