我在为 CabloyJS 全栈框架开发 SMS 套件(通用底座 + 验证码 + 认证)时,输入的提示词和 Claude Code 返回的开发方案非常具有典型性。现在整理出来,供大家参考。
提示词如下:
[indent]有如下开发任务,请你仔细分析,并整理出合理的方案:
[list=1]
[*]目标:先实现 SMS 系统底座,然后基于 SMS 系统底座实现多个业务能力。
[list]
[*]系统底座:SMS provider / clients / scene 。
[*]业务能力:
[list]
[*]sms captcha:短信验证码。
[*]sms auth:用户注册与登录。
[/list]
[/list]
[*]套件 / 模块结构:
[list]
[*]套件:cabloy-sms 。
[*]模块:
[list]
[*]cabloy-sms:提供 SMS 系统底座。
[*]sms-captcha:短信验证码。
[*]sms-auth:用户注册与登录。
[*]sms-mock:提供 Mock SMS Provider ,在控制台打印验证码,并把验证码发送给前端,让前端将验证码直接渲染到输入框中。
[/list]
[/list]
[/list][/indent]Claude Code 返回的“开发方案”如下:
[size=5]一、总体建议[/size]
建议保留你提出的 cabloy-sms 套件 + 四个模块,采用以下分层:
[list]
[*]cabloy-sms:短信传输底座,解决“通过哪个供应商账号、使用哪个模板发送短信”。
[*]sms-captcha:短信验证能力,解决“验证码发给谁、用于什么操作、如何安全验证”。
[*]sms-auth:手机号认证能力,解决“验证成功后登录哪个账号,是否创建新账号”。
[*]sms-mock:开发和测试用短信 Provider ,替代真实短信传输,但不替代验证码校验及认证流程。
[/list]最重要的设计原则是:
[indent]底座不依赖验证码,验证码不依赖用户认证,Mock 不改变真实业务流程。
[/indent]当前仓库为 Cabloy Basic 。结合现有源码,建议复用 Vona 的 provider/client/scene 扩展方式、a-captcha 验证码体系和 a-auth 认证链,不另建短信验证码缓存体系或会话系统。
不过,现有 a-captcha 尚不具备手机号绑定和原子消费等短信登录必需的保障,需要做一组有界、向后兼容的扩展。
<hr>[size=5]二、套件与模块结构[/size]
你给出的名称符合当前命名规则,可以直接保留。
[table][tr]层级名称说明[/tr][tr][td]Suite[/td][td]cabloy-sms[/td][td]providerId=cabloy ,suiteName=sms[/td][/tr][tr][td]Module[/td][td]cabloy-sms[/td][td]通用短信底座[/td][/tr][tr][td]Module[/td][td]sms-captcha[/td][td]短信验证码[/td][/tr][tr][td]Module[/td][td]sms-auth[/td][td]短信注册与登录[/td][/tr][tr][td]Module[/td][td]sms-mock[/td][td]Mock 短信 Provider[/td][/tr][/table]Suite 与其中一个 Module 同名没有问题,完整包名分别是 vona-suite-cabloy-sms 、vona-module-cabloy-sms ,不会混淆。
后端模块统一放在:
vona/src/suite/cabloy-sms/modules/
前端有实际代码的对应模块放在:
zova/src/suite/cabloy-sms/modules/
不要求四个模块都建立空的前端对应模块。 首期前端主要需要 sms-captcha 的验证码交互和 sms-auth 的登录表单; sms-mock 的回填不必另造一套前端 Mock 系统。
[size=4]依赖方向[/size]
[list]
[*]sms-captcha 必须依赖短信底座及现有验证码体系。
[*]sms-auth 必须依赖短信验证码及现有认证体系。
[*]sms-mock 只需依赖短信底座。
[*]底座、验证码、认证模块都不能反向强依赖 sms-mock 。
[/list]Mock 是否参与运行,应由应用组合和服务端配置决定,而不是业务模块默认强制启用。
<hr>[size=5]三、cabloy-sms:provider / clients / scene 如何划分[/size]
[size=4]1. Provider:供应商协议实现[/size]
Provider 表示发送协议的实现,例如:
[list]
[*]sms-mock:mock
[*]将来的阿里云短信 Provider
[*]将来的腾讯云短信 Provider
[/list]Provider 负责:
[list]
[*]将统一发送请求转换为供应商请求。
[*]调用 SDK 或 HTTP API 。
[*]将供应商结果转换为统一结果。
[*]隐藏供应商错误和敏感信息。
[/list]Provider 不负责生成验证码、判断注册登录、查找用户。
建议建立 smsProvider 和 smsScene 两类扩展,沿用现有 decorator 、类型注册、onion metadata 和 CLI 模板机制,而不是自建独立插件管理器。
现有参考:
vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts:6
[size=4]2. Client:某个 Provider 下的命名账号配置[/size]
Client 不是新的业务服务,也不必单独成为 bean scene ,而是 Provider 下的命名配置。
例如,同一个真实 Provider 可以有:
[list]
[*]default:默认账号。
[*]marketing:另一供应商账号或专用发送配置。
[/list]Client 保存:
[list]
[*]凭据。
[*]区域、接入地址。
[*]必要的供应商默认选项。
[*]是否启用等配置。
[/list]建议采用 Provider 下 base + clients 的方式,保留 Provider 与 Client 的类型关联。
不建议首期建立供应商账号管理表、凭据管理后台或持久化 Client 注册中心;配置驱动足以支持当前消费者。
[size=4]3. Scene:业务发送场景[/size]
Scene 表示稳定的业务发送意图,例如:
[list]
[*]sms-auth:signin
[*]将来的订单通知、配送通知。
[/list]Scene 配置:
[list]
[*]使用哪个 Provider 、Client 。
[*]短信签名、供应商模板。
[*]模板参数约束。
[*]必要的服务端路由规则。
[/list]服务端调用语义可以设计为:
// 拟议接口,非当前已有 APIawait this.bean.sms.send({ scene: 'sms-auth:signin', recipient: normalizedPhone, params: { code, minutes: 5, },});调用方传业务参数,底座解析传输配置。公开请求不得直接指定 Provider 、Client 、模板 ID 、签名或凭据。
另外需要区分:
[table][tr]概念作用[/tr][tr][td]SMS Scene[/td][td]选择传输渠道、账号、模板[/td][/tr][tr][td]Captcha Scene[/td][td]界定验证码可以证明哪个业务操作[/td][/tr][/table]两者可以由同一业务模块声明和关联,但不能相互替代,也不需要再引入第三套“短信验证场景”注册体系。
[size=4]4. 实例隔离与发送结果[/size]
Provider 和 Scene 都必须按当前 instance 解析有效配置,不能把首个请求的凭据、手机号或场景配置缓存到共享 bean 字段。
现有 onion 的动态配置合并会加入 instance 配置,直接读取静态 beanOptions.options 则不等价:
vona/src/suite-vendor/a-vona/modules/a-onion/src/service/onion_.ts:252
首期发送建议保持同步,明确区分:
[list]
[*]供应商已接受。
[*]明确拒绝。
[*]超时等结果不确定。
[/list]“供应商接受”不等于“手机已收到”。 暂不做消息队列、投递回执、自动故障切换和自动重试,尤其不能在超时后盲目重发。
<hr>[size=5]四、sms-captcha:复用验证码体系,但补齐短信语义[/size]
[size=4]1. 已有能力与缺口[/size]
现有 a-captcha 已经提供:
[list]
[*]challenge ID 。
[*]Provider 、Scene 。
[*]Redis 存储及 TTL 。
[*]验证 DTO 。
[*]表单校验接入。
[/list]但当前缓存只有 scene/provider/token/token2 ,没有接收手机号;验证成功采用分开的读取、比较、删除操作,存在并发重复成功的可能。
依据:
vona/src/suite-vendor/a-captcha/modules/a-captcha/src/bean/bean.captcha.ts:19 、:79
因此,不能只新增一个“发送短信”的 CaptchaProvider 就认为短信验证完成。
[size=4]2. 推荐的职责分配[/size]
a-captcha 增加通用、可选的能力:
[list]
[*]创建时传入可信上下文。
[*]保存验证绑定信息。
[*]消费时核对服务端计算的预期绑定。
[*]原子状态转换,保证单次消费。
[*]Provider/Scene 可声明受控签发、刷新和公开 token 策略。
[*]去除通用验证码日志中的明文 token 。
[/list]sms-captcha 负责短信领域规则:
[list]
[*]手机号规范化和格式校验。
[*]允许的用途及场景映射。
[*]安全随机验证码生成。
[*]手机号、用途和必要主体的绑定。
[*]发送冷却、配额、验证失败次数。
[*]调用短信底座。
[*]短信专用表单交互。
[/list]这样既不重复建设 OTP 系统,也不把手机号规则塞进通用验证码模块。
[size=4]3. 推荐验证规则[/size]
首期建议:
[table][tr]项目建议[/tr][tr][td]验证码[/td][td]6 位数字,按字符串处理,安全随机生成[/td][/tr][tr][td]有效期[/td][td]5 分钟,可配置[/td][/tr][tr][td]重发冷却[/td][td]60 秒,可配置[/td][/tr][tr][td]错误次数[/td][td]明确上限,例如 5 次[/td][/tr][tr][td]时间规则[/td][td]查询、输错均不延长原到期时间[/td][/tr][tr][td]重发[/td][td]新 challenge 替换旧 challenge ,旧凭据失效[/td][/tr][tr][td]消费[/td][td]正确验证码最多成功一次[/td][/tr][tr][td]绑定[/td][td]当前 instance + 规范化手机号 + 用途;按业务增加主体绑定[/td][/tr][tr][td]防滥用[/td][td]手机号冷却/配额 + IP 限流 + 验证尝试限制[/td][/tr][/table]手机号不能只作为发送参数。例如,发给手机号 A 的验证码,即使验证成功,也不能用于手机号 B 的登录。
首期可以继续使用现有 Redis 短期秘密存储,不为摘要存储额外建设一套缓存。但必须有短 TTL 、访问隔离、无明文日志和无持久化验证码历史;若后续要求摘要存储,需要明确拆分“存储校验材料”和“Mock 回填值”,当前接口并未天然支持这一点。
[size=4]4. 必须关闭通用入口绕过[/size]
现有通用 captcha 的创建、刷新、即时验证接口都是公开入口:
vona/src/suite-vendor/a-captcha/modules/a-captcha/src/controller/captcha.ts:15
因此,安全规则必须落在共用服务边界,而不是只加在新短信 Controller 上。
建议首期明确:
[list]
[*]普通 create(scene) 不允许无可信上下文创建短信验证码。
[*]普通 refresh(id, scene) 不允许绕过短信冷却重发。
[*]过期或缺失 ID 不得通过 refresh 自动回退成无约束发送。
[*]短信场景暂不支持 verifyImmediate → token2 ,避免引入另一种可重放证明。
[*]短信验证缺少预期手机号/用途绑定时一律失败。
[/list]<hr>[size=5]五、sms-mock:模拟传输,不模拟业务成功[/size]
[size=4]1. 正确的运行方式[/size]
Mock 应走与真实短信相同的链路:
[list=1]
[*]sms-captcha 生成验证码、建立绑定。
[*]短信底座解析到 sms-mock Provider 。
[*]Mock 不访问外部供应商。
[*]在开发控制台打印验证码,手机号脱敏。
[*]验证码层在允许的条件下将验证码返回前端。
[*]用户提交后,仍走正常过期、限次、绑定和单次消费验证。
[/list]不能用固定万能验证码,也不能让 Mock 登录直接返回成功。
[size=4]2. 前端回填可以复用现有响应字段[/size]
已有 captcha 响应支持可选 token ,当前表单也会将其写入输入框:
zova/src/suite/cabloy-basic/modules/basic-captcha/src/component/formFieldCaptcha/controller.tsx:96
因此不必引入第二套验证码响应协议。
不过当前 showToken 默认在所有非生产环境开启,不能直接沿用到真实短信:
vona/src/suite-vendor/a-captcha/modules/a-captcha/src/config/config.ts:3
短信返码应同时满足:
[list=1]
[*]本次实际使用的是 sms-mock 。
[*]非生产环境。
[*]显式开启开发回填配置。
[/list]真实短信即使运行在 dev/test ,也不能因为全局 showToken 而返回验证码。生产环境选择 Mock 应拒绝运行或发送,不能静默回退。
[size=4]3. 前端必须有短信专用交互[/size]
现有图形验证码组件会初始化自动创建、提交失败自动刷新,并将 payload 渲染为图片,不能原样套用到付费短信。
短信组件应提供:
[list]
[*]手机号输入。
[*]显式“发送验证码”按钮。
[*]按服务端时间展示冷却。
[*]Mock 成功后回填。
[*]手机号修改后清除 challenge 和验证码。
[*]发送失败不自动重试。
[*]页面初始化和普通表单错误不自动发送。
[/list]验证码只保存在当前交互状态,不进入 localStorage 、SSR HTML 或持久化用户状态。
<hr>[size=5]六、sms-auth:复用认证链,建立可信手机号身份[/size]
[size=4]1. 推荐产品语义[/size]
建议首期采用:
[indent]手机号验证成功后登录;该短信身份不存在时自动注册。
[/indent]它能用一个表单同时完成注册与登录,符合现有通用认证链的基本行为。但这是产品选择,仍需确认;如果要求“注册”和“登录”严格分离,就需要明确已有身份和不存在身份各自的处理规则。
[size=4]2. 认证链路[/size]
新增非 OAuth 的 SMS AuthProvider ,复用:
[indent]验证并消费 OTP → 获取或创建身份与用户 → 现有 Passport signin
[/indent]现有框架已经支持 provider.verify → issuePassport → passport.signin:
vona/src/suite-vendor/a-auth/modules/a-auth/src/service/auth.ts:27
关键要求:
[list]
[*]OTP 消费必须发生在 Provider 的验证边界,不能只放在新增 Controller 。
[*]Provider 、Client 、intention 由服务端控制。
[*]不把请求整体合并为认证配置。
[*]不自行生成另一套 JWT 、Cookie 或会话存储。
[/list][size=4]3. 不按旧 mobile 字段自动认领账号[/size]
现有 homeUser.mobile 只是可选字符串,没有“已验证手机号”的语义:
vona/src/suite/a-home/modules/home-user/src/entity/user.ts:24
建议使用已有认证身份映射:
SMS 身份命名空间 + 规范化手机号 → auth 映射 → userId由此得到:
[list]
[*]已有可信 SMS 映射:登录其关联用户。
[*]没有映射:按确认的策略注册或拒绝。
[*]旧账号的 mobile 相同:不能自动关联、合并或接管。
[*]已有映射却缺失用户:报错,不静默重建账号。
[/list]首期不做手机号绑定、换绑、找回、账号合并,并在 Provider 内明确拒绝 associate/migrate ,避免通用入口触发这些操作。
[size=4]4. 独立处理首次注册并发[/size]
同一验证码原子消费,不能替代身份唯一性保障:同一手机号可能持有两个独立的有效验证请求。
需要按当前 instance 和短信身份串行化,事务内重新查询并原子完成:
[list]
[*]用户创建。
[*]正常角色关系。
[*]auth 映射。
[*]必要的 mobile 资料投影。
[/list]事务提交后,再进入既有 signin 。不能仅靠事务或不存在行的锁来防止重复创建,也不应添加跨租户业务唯一约束。
首期优先复用已有身份映射,不新增身份表。若确需新增 mobileVerifiedAt 等持久化字段,实施前再确认所属模块的 fileVersion 策略。
[size=4]5. 前端登录接入[/size]
[list]
[*]sms-auth 提供短信登录表单及业务封装。
[*]现有 Home Login 页面做最薄的入口接入。
[*]成功后复用 Passport 的登录后处理,不复制 token 写入和 returnTo 逻辑。
[*]Web/Admin 共享认证身份,不因页面或短信供应商 Client 不同而重复创建用户。
[*]新用户沿用明确的普通用户角色策略;“短信注册成功”不等于授予管理权限。
[/list]<hr>[size=5]七、推荐交付顺序与验收[/size]
[table][tr]阶段交付内容退出条件[/tr][tr][td]1. 底座 + Mock[/td][td]Provider/Client/Scene 、发送门面、Mock[/td][td]普通模板短信可通过 Mock 发送;实例配置隔离;失败语义明确[/td][/tr][tr][td]2. 短信验证码[/td][td]captcha 必需扩展、签发/重发、防滥用、短信组件[/td][td]回填可用;手机号/用途绑定;原子消费;通用入口不可绕过[/td][/tr][tr][td]3. 短信注册登录[/td][td]SMS AuthProvider 、身份创建边界、登录入口[/td][td]可注册及再次登录;不认领旧 mobile ;并发不重复开户;复用会话[/td][/tr][tr][td]4. 首个真实 Provider[/td][td]按选定供应商接入[/td][td]Mock 与真实传输遵守相同契约;真实短信不返码、不泄露日志[/td][/tr][/table]虽然 Mock 在模块分工中是第四项,实施上应与底座一起完成,让验证码和认证从一开始就有可运行的消费者链路。
关键验收不必铺成庞大矩阵,但必须覆盖:
[list]
[*]跨手机号、跨用途、跨 instance 、过期、重放验证失败。
[*]同一 challenge 并发消费最多一次成功。
[*]两个独立有效 OTP 并发首登,最终只有一个用户及身份映射。
[*]通用 captcha 和认证入口不能绕过安全规则。
[*]手机号变化清空旧证明。
[*]真实短信在任何环境均不公开验证码。
[*]Mock 仅在明确开发门禁下打印及回填。
[*]disabled 用户不能登录,旧密码/GitHub 登录不回归。
[*]发送超时不会触发盲目重试。
[/list]<hr>[size=5]八、首期范围与待确认事项[/size]
[b]建议首期不包含:[/b]供应商管理后台、发送历史后台、营销群发、队列、回执、自动重试/切换、手机号换绑、找回和账号合并。
需要集中确认的产品决策只有三组:
[list=1]
[*]注册语义:是否采用“短信登录即自动注册”?推荐采用。
[*]号码范围:首期仅中国大陆,还是支持国际号码?无论哪种,都应明确区号并统一规范化,不能默认对所有输入补 +86 。
[*]投放范围:短信登录先开放 Web ,还是同时开放 Admin ;首期是否只交付 Mock 闭环,暂不接真实供应商。
[/list]名称已经合理,无需另行改名;如有品牌或命名偏好,也可以继续按同一规则调整。
确认后,先通过现有 CLI 创建底座及 Mock ,而不是手工搭目录,例如:
npm run vona :create:suite cabloy-smsnpm run vona :create:module cabloy-sms -- --suite=cabloy-smsnpm run vona :create:module sms-mock -- --suite=cabloy-sms之后逐阶段生成其余模块、执行聚焦后端测试和关键流程 E2E 。后端契约变化先生成前端消费者;涉及前端反向依赖时,先构建受影响的 Web/Admin ,再运行 npm run deps:vona 。
本轮仅完成分析,没有修改文件。CLI 命令源码已确认存在,但实际执行 npm run vona 、npm run zova 均因缺少 @cabloy/cli 失败;当前 Node 为 v24.8.0 ,低于仓库要求的 >=24.11.0 。实施前需准备运行时与依赖,本轮未安装依赖、构建或运行测试。
<hr>[size=5]结语[/size]
Claude Code 要求我确认三组产品决策:注册语义、号码范围和投放范围,再据此制定进一步的“开发方案”。限于篇幅,本文先整理到这里。如果想看后续,欢迎留言,我会继续整理。
⚠️内容来自互联网,如有侵权请点击反馈!侵权投诉