结论先行
“发一条 WhatsApp,里面放个签署链接”听起来很轻,但如果你真这么做,马上会撞上三个问题:谁有资格发、链接该指向 request 还是 signer、签完/拒签后消息如何回写。企业版 whatsapp_sign 的价值,正是在于它把这三层拆清楚了。
第一层:入口或表面动作
首先,wizard send_via_whatsapp() 并不是直接发消息,而是先 _check_whatsapp_requirements(),确认所有 signer 都有手机号;随后通过 with_context(send_channel='whatsapp').create_request() 先创建标准 sign.request。也就是说,消息发送只是签署请求生命周期上的一个分支,不是独立系统。你没有 sign.request,就没有后续一切 token、附件链接、完成通知。
第二层:真正的业务护栏
其次,sign_request_item._compute_document_link() 和 _compute_attachments_download_link() 会根据 request/item 的 access token 生成公开访问 URL。这里的细节很重要:链接不是简单拼模板变量,而是依赖 sign request / sign request item 的 token 体系,既能区分整单共享,也能区分单签人入口。_send_signature_access_message() 还会在模板未配置或不合规时回退邮件通道,说明 WhatsApp 只是 transport,认证边界仍在 sign 模块。
第三层:状态落点与边界
第三层是回写。签完后 sign_request._send_completed_documents_message(),拒签后 _send_refused_message(),都复用 sign request 的状态机去决定通知内容与对象,而不是让 WhatsApp 成为状态源。到了 public 路由,sign.controllers.main.sign_document_from_mail() / sign_document_public() 又会基于 token、有效期和 request state 做验证。真正允许用户看或签文档的,不是消息平台,而是 public sign controller 的令牌检查。
为什么这套设计更稳
这就是为什么企业版这条链很适合当框架题:消息平台负责触达,sign.request 负责业务状态,controller 负责公开访问验证,三者缺一不可。若你只在 WhatsApp 模板里拼一个静态链接,后面一旦签署顺序、单人 token、过期重发、拒签提醒有变化,整套逻辑都会散架。
实战启示
做二开时最稳的扩展点通常是模板字段、fallback 策略或 request item 级别链接生成,而不是绕过 sign request 自己发消息。真正需要保护的,是“触达渠道可以变,访问凭证和状态机不能散”。
DISCUSSION
评论区