[Claude] AI Vibe Coding 实战: SMS 套件提示词与开发方案

我在为 CabloyJS 全栈框架开发 SMS 套件(通用底座 + 验证码 + 认证)时,输入的提示词和 Claude Code 返回的开发方案非常具有典型性。现在整理出来,供大家参考。

提示词如下:

有如下开发任务,请你仔细分析,并整理出合理的方案:
  1. 目标:先实现 SMS 系统底座,然后基于 SMS 系统底座实现多个业务能力。
    • 系统底座:SMS provider / clients / scene 。
    • 业务能力:
      • sms captcha:短信验证码。
      • sms auth:用户注册与登录。
  2. 套件 / 模块结构:
    • 套件:cabloy-sms 。
    • 模块:
      • cabloy-sms:提供 SMS 系统底座。
      • sms-captcha:短信验证码。
      • sms-auth:用户注册与登录。
      • sms-mock:提供 Mock SMS Provider ,在控制台打印验证码,并把验证码发送给前端,让前端将验证码直接渲染到输入框中。

Claude Code 返回的“开发方案”如下:

一、总体建议

建议保留你提出的 cabloy-sms 套件 + 四个模块,采用以下分层:

  • cabloy-sms:短信传输底座,解决“通过哪个供应商账号、使用哪个模板发送短信”。
  • sms-captcha:短信验证能力,解决“验证码发给谁、用于什么操作、如何安全验证”。
  • sms-auth:手机号认证能力,解决“验证成功后登录哪个账号,是否创建新账号”。
  • sms-mock:开发和测试用短信 Provider ,替代真实短信传输,但不替代验证码校验及认证流程。

最重要的设计原则是:

底座不依赖验证码,验证码不依赖用户认证,Mock 不改变真实业务流程。

当前仓库为 Cabloy Basic 。结合现有源码,建议复用 Vona 的 provider/client/scene 扩展方式、a-captcha 验证码体系和 a-auth 认证链,不另建短信验证码缓存体系或会话系统。

不过,现有 a-captcha 尚不具备手机号绑定和原子消费等短信登录必需的保障,需要做一组有界、向后兼容的扩展。


二、套件与模块结构

你给出的名称符合当前命名规则,可以直接保留。

层级 名称 说明
Suite cabloy-sms providerId=cabloy ,suiteName=sms
Module cabloy-sms 通用短信底座
Module sms-captcha 短信验证码
Module sms-auth 短信注册与登录
Module sms-mock Mock 短信 Provider

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 系统。

依赖方向

  • sms-captcha 必须依赖短信底座及现有验证码体系。
  • sms-auth 必须依赖短信验证码及现有认证体系。
  • sms-mock 只需依赖短信底座。
  • 底座、验证码、认证模块都不能反向强依赖 sms-mock 。

Mock 是否参与运行,应由应用组合和服务端配置决定,而不是业务模块默认强制启用。


三、cabloy-sms:provider / clients / scene 如何划分

1. Provider:供应商协议实现

Provider 表示发送协议的实现,例如:

  • sms-mock:mock
  • 将来的阿里云短信 Provider
  • 将来的腾讯云短信 Provider

Provider 负责:

  • 将统一发送请求转换为供应商请求。
  • 调用 SDK 或 HTTP API 。
  • 将供应商结果转换为统一结果。
  • 隐藏供应商错误和敏感信息。

Provider 不负责生成验证码、判断注册登录、查找用户。

建议建立 smsProvider 和 smsScene 两类扩展,沿用现有 decorator 、类型注册、onion metadata 和 CLI 模板机制,而不是自建独立插件管理器。

现有参考:

vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts:6

2. Client:某个 Provider 下的命名账号配置

Client 不是新的业务服务,也不必单独成为 bean scene ,而是 Provider 下的命名配置。

例如,同一个真实 Provider 可以有:

  • default:默认账号。
  • marketing:另一供应商账号或专用发送配置。

Client 保存:

  • 凭据。
  • 区域、接入地址。
  • 必要的供应商默认选项。
  • 是否启用等配置。

建议采用 Provider 下 base + clients 的方式,保留 Provider 与 Client 的类型关联。

不建议首期建立供应商账号管理表、凭据管理后台或持久化 Client 注册中心;配置驱动足以支持当前消费者。

3. Scene:业务发送场景

Scene 表示稳定的业务发送意图,例如:

  • sms-auth:signin
  • 将来的订单通知、配送通知。

Scene 配置:

  • 使用哪个 Provider 、Client 。
  • 短信签名、供应商模板。
  • 模板参数约束。
  • 必要的服务端路由规则。

服务端调用语义可以设计为:

// 拟议接口,非当前已有 API
await this.bean.sms.send({
  scene: 'sms-auth:signin',
  recipient: normalizedPhone,
  params: {
    code,
    minutes: 5,
  },
});

调用方传业务参数,底座解析传输配置。公开请求不得直接指定 Provider 、Client 、模板 ID 、签名或凭据。

另外需要区分:

概念 作用
SMS Scene 选择传输渠道、账号、模板
Captcha Scene 界定验证码可以证明哪个业务操作

两者可以由同一业务模块声明和关联,但不能相互替代,也不需要再引入第三套“短信验证场景”注册体系。

4. 实例隔离与发送结果

Provider 和 Scene 都必须按当前 instance 解析有效配置,不能把首个请求的凭据、手机号或场景配置缓存到共享 bean 字段。

现有 onion 的动态配置合并会加入 instance 配置,直接读取静态 beanOptions.options 则不等价:

vona/src/suite-vendor/a-vona/modules/a-onion/src/service/onion_.ts:252

首期发送建议保持同步,明确区分:

  • 供应商已接受。
  • 明确拒绝。
  • 超时等结果不确定。

“供应商接受”不等于“手机已收到”。 暂不做消息队列、投递回执、自动故障切换和自动重试,尤其不能在超时后盲目重发。


四、sms-captcha:复用验证码体系,但补齐短信语义

1. 已有能力与缺口

现有 a-captcha 已经提供:

  • challenge ID 。
  • Provider 、Scene 。
  • Redis 存储及 TTL 。
  • 验证 DTO 。
  • 表单校验接入。

但当前缓存只有 scene/provider/token/token2 ,没有接收手机号;验证成功采用分开的读取、比较、删除操作,存在并发重复成功的可能。

依据:

vona/src/suite-vendor/a-captcha/modules/a-captcha/src/bean/bean.captcha.ts:19 、:79

因此,不能只新增一个“发送短信”的 CaptchaProvider 就认为短信验证完成。

2. 推荐的职责分配

a-captcha 增加通用、可选的能力:

  • 创建时传入可信上下文。
  • 保存验证绑定信息。
  • 消费时核对服务端计算的预期绑定。
  • 原子状态转换,保证单次消费。
  • Provider/Scene 可声明受控签发、刷新和公开 token 策略。
  • 去除通用验证码日志中的明文 token 。

sms-captcha 负责短信领域规则:

  • 手机号规范化和格式校验。
  • 允许的用途及场景映射。
  • 安全随机验证码生成。
  • 手机号、用途和必要主体的绑定。
  • 发送冷却、配额、验证失败次数。
  • 调用短信底座。
  • 短信专用表单交互。

这样既不重复建设 OTP 系统,也不把手机号规则塞进通用验证码模块。

3. 推荐验证规则

首期建议:

项目 建议
验证码 6 位数字,按字符串处理,安全随机生成
有效期 5 分钟,可配置
重发冷却 60 秒,可配置
错误次数 明确上限,例如 5 次
时间规则 查询、输错均不延长原到期时间
重发 新 challenge 替换旧 challenge ,旧凭据失效
消费 正确验证码最多成功一次
绑定 当前 instance + 规范化手机号 + 用途;按业务增加主体绑定
防滥用 手机号冷却/配额 + IP 限流 + 验证尝试限制

手机号不能只作为发送参数。例如,发给手机号 A 的验证码,即使验证成功,也不能用于手机号 B 的登录。

首期可以继续使用现有 Redis 短期秘密存储,不为摘要存储额外建设一套缓存。但必须有短 TTL 、访问隔离、无明文日志和无持久化验证码历史;若后续要求摘要存储,需要明确拆分“存储校验材料”和“Mock 回填值”,当前接口并未天然支持这一点。

4. 必须关闭通用入口绕过

现有通用 captcha 的创建、刷新、即时验证接口都是公开入口:

vona/src/suite-vendor/a-captcha/modules/a-captcha/src/controller/captcha.ts:15

因此,安全规则必须落在共用服务边界,而不是只加在新短信 Controller 上。

建议首期明确:

  • 普通 create(scene) 不允许无可信上下文创建短信验证码。
  • 普通 refresh(id, scene) 不允许绕过短信冷却重发。
  • 过期或缺失 ID 不得通过 refresh 自动回退成无约束发送。
  • 短信场景暂不支持 verifyImmediate → token2 ,避免引入另一种可重放证明。
  • 短信验证缺少预期手机号/用途绑定时一律失败。

五、sms-mock:模拟传输,不模拟业务成功

1. 正确的运行方式

Mock 应走与真实短信相同的链路:

  1. sms-captcha 生成验证码、建立绑定。
  2. 短信底座解析到 sms-mock Provider 。
  3. Mock 不访问外部供应商。
  4. 在开发控制台打印验证码,手机号脱敏。
  5. 验证码层在允许的条件下将验证码返回前端。
  6. 用户提交后,仍走正常过期、限次、绑定和单次消费验证。

不能用固定万能验证码,也不能让 Mock 登录直接返回成功。

2. 前端回填可以复用现有响应字段

已有 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

短信返码应同时满足:

  1. 本次实际使用的是 sms-mock 。
  2. 非生产环境。
  3. 显式开启开发回填配置。

真实短信即使运行在 dev/test ,也不能因为全局 showToken 而返回验证码。生产环境选择 Mock 应拒绝运行或发送,不能静默回退。

3. 前端必须有短信专用交互

现有图形验证码组件会初始化自动创建、提交失败自动刷新,并将 payload 渲染为图片,不能原样套用到付费短信。

短信组件应提供:

  • 手机号输入。
  • 显式“发送验证码”按钮。
  • 按服务端时间展示冷却。
  • Mock 成功后回填。
  • 手机号修改后清除 challenge 和验证码。
  • 发送失败不自动重试。
  • 页面初始化和普通表单错误不自动发送。

验证码只保存在当前交互状态,不进入 localStorage 、SSR HTML 或持久化用户状态。


六、sms-auth:复用认证链,建立可信手机号身份

1. 推荐产品语义

建议首期采用:

手机号验证成功后登录;该短信身份不存在时自动注册。

它能用一个表单同时完成注册与登录,符合现有通用认证链的基本行为。但这是产品选择,仍需确认;如果要求“注册”和“登录”严格分离,就需要明确已有身份和不存在身份各自的处理规则。

2. 认证链路

新增非 OAuth 的 SMS AuthProvider ,复用:

验证并消费 OTP → 获取或创建身份与用户 → 现有 Passport signin

现有框架已经支持 provider.verify → issuePassport → passport.signin:

vona/src/suite-vendor/a-auth/modules/a-auth/src/service/auth.ts:27

关键要求:

  • OTP 消费必须发生在 Provider 的验证边界,不能只放在新增 Controller 。
  • Provider 、Client 、intention 由服务端控制。
  • 不把请求整体合并为认证配置。
  • 不自行生成另一套 JWT 、Cookie 或会话存储。

3. 不按旧 mobile 字段自动认领账号

现有 homeUser.mobile 只是可选字符串,没有“已验证手机号”的语义:

vona/src/suite/a-home/modules/home-user/src/entity/user.ts:24

建议使用已有认证身份映射:

SMS 身份命名空间 + 规范化手机号 → auth 映射 → userId

由此得到:

  • 已有可信 SMS 映射:登录其关联用户。
  • 没有映射:按确认的策略注册或拒绝。
  • 旧账号的 mobile 相同:不能自动关联、合并或接管。
  • 已有映射却缺失用户:报错,不静默重建账号。

首期不做手机号绑定、换绑、找回、账号合并,并在 Provider 内明确拒绝 associate/migrate ,避免通用入口触发这些操作。

4. 独立处理首次注册并发

同一验证码原子消费,不能替代身份唯一性保障:同一手机号可能持有两个独立的有效验证请求。

需要按当前 instance 和短信身份串行化,事务内重新查询并原子完成:

  • 用户创建。
  • 正常角色关系。
  • auth 映射。
  • 必要的 mobile 资料投影。

事务提交后,再进入既有 signin 。不能仅靠事务或不存在行的锁来防止重复创建,也不应添加跨租户业务唯一约束。

首期优先复用已有身份映射,不新增身份表。若确需新增 mobileVerifiedAt 等持久化字段,实施前再确认所属模块的 fileVersion 策略。

5. 前端登录接入

  • sms-auth 提供短信登录表单及业务封装。
  • 现有 Home Login 页面做最薄的入口接入。
  • 成功后复用 Passport 的登录后处理,不复制 token 写入和 returnTo 逻辑。
  • Web/Admin 共享认证身份,不因页面或短信供应商 Client 不同而重复创建用户。
  • 新用户沿用明确的普通用户角色策略;“短信注册成功”不等于授予管理权限。

七、推荐交付顺序与验收

阶段 交付内容 退出条件
1. 底座 + Mock Provider/Client/Scene 、发送门面、Mock 普通模板短信可通过 Mock 发送;实例配置隔离;失败语义明确
2. 短信验证码 captcha 必需扩展、签发/重发、防滥用、短信组件 回填可用;手机号/用途绑定;原子消费;通用入口不可绕过
3. 短信注册登录 SMS AuthProvider 、身份创建边界、登录入口 可注册及再次登录;不认领旧 mobile ;并发不重复开户;复用会话
4. 首个真实 Provider 按选定供应商接入 Mock 与真实传输遵守相同契约;真实短信不返码、不泄露日志

虽然 Mock 在模块分工中是第四项,实施上应与底座一起完成,让验证码和认证从一开始就有可运行的消费者链路。

关键验收不必铺成庞大矩阵,但必须覆盖:

  • 跨手机号、跨用途、跨 instance 、过期、重放验证失败。
  • 同一 challenge 并发消费最多一次成功。
  • 两个独立有效 OTP 并发首登,最终只有一个用户及身份映射。
  • 通用 captcha 和认证入口不能绕过安全规则。
  • 手机号变化清空旧证明。
  • 真实短信在任何环境均不公开验证码。
  • Mock 仅在明确开发门禁下打印及回填。
  • disabled 用户不能登录,旧密码/GitHub 登录不回归。
  • 发送超时不会触发盲目重试。

八、首期范围与待确认事项

建议首期不包含:供应商管理后台、发送历史后台、营销群发、队列、回执、自动重试/切换、手机号换绑、找回和账号合并。

需要集中确认的产品决策只有三组:

  1. 注册语义:是否采用“短信登录即自动注册”?推荐采用。
  2. 号码范围:首期仅中国大陆,还是支持国际号码?无论哪种,都应明确区号并统一规范化,不能默认对所有输入补 +86 。
  3. 投放范围:短信登录先开放 Web ,还是同时开放 Admin ;首期是否只交付 Mock 闭环,暂不接真实供应商。

名称已经合理,无需另行改名;如有品牌或命名偏好,也可以继续按同一规则调整。

确认后,先通过现有 CLI 创建底座及 Mock ,而不是手工搭目录,例如:

npm run vona :create:suite cabloy-sms
npm run vona :create:module cabloy-sms -- --suite=cabloy-sms
npm 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 。实施前需准备运行时与依赖,本轮未安装依赖、构建或运行测试。


结语

Claude Code 要求我确认三组产品决策:注册语义、号码范围和投放范围,再据此制定进一步的“开发方案”。限于篇幅,本文先整理到这里。如果想看后续,欢迎留言,我会继续整理。

参与讨论