From c875151daa3e1c06d85b26c9f6c7e76ce42a1ab2 Mon Sep 17 00:00:00 2001 From: zheng020 Date: Fri, 12 Jun 2026 12:36:29 +0800 Subject: [PATCH] =?UTF-8?q?docs(change-password):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=90=88=E5=B9=B6=205=20=E4=B8=AA=20spec=20review=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 4.2: 明确 VerifyToken 函数改造为 (ctx, scene, mobile, token) - 4.3: 补 scene="password" 传入,加 scene 一致性注释 - 5.3.4: 确认 sendCodeApi/verifyCodeApi 已支持 scene,updatePasswordApi 需扩展为 3 参数 - 7: 增加 401 业务码多义性说明,提示 res.message 必须稳定 - 10: auth_service.go 改动明确为 Register 中 VerifyToken 调用加 scene 参数 --- .../2026-06-12-change-password-design.md | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-06-12-change-password-design.md b/docs/superpowers/specs/2026-06-12-change-password-design.md index 074f11f..687ec9d 100644 --- a/docs/superpowers/specs/2026-06-12-change-password-design.md +++ b/docs/superpowers/specs/2026-06-12-change-password-design.md @@ -158,7 +158,13 @@ func verifyTokenKey(scene, mobile string) string { `SaveVerifyToken / GetVerifyToken / DeleteVerifyToken` 函数签名增加 `scene string` 参数,内部用新 key 函数。 -**影响面**:`sms_service.go` 中的 3 处调用同步加 `scene` 参数(Register 仍用 `scene="register"`,改密用 `scene="password"`)。 +`VerifyToken(ctx, mobile, token)`(定义在 [sms_service.go:247](backend/services/userService/service/sms_service.go#L247))同步改造为 `VerifyToken(ctx, scene, mobile, token)`,内部按 `verify:{scene}:{mobile}` 拼 key 做 Get+Delete 一次性消费。 + +**影响面**: +- `sms_service.go` 中 `SendCode / VerifyCode / VerifyToken` 全部加 `scene` 参数 +- `auth_service.go` 的 `Register` 中 `VerifyToken(ctx, req.Mobile, req.VerifyToken)` → `VerifyToken(ctx, "register", req.Mobile, req.VerifyToken)` +- Register 仍用 `scene="register"`,改密用 `scene="password"`,两个 scene 的 Redis key 互不污染 +- 老数据兼容:老的 `sms:register:*` / `verify:register:*` 在过渡期可保留(只要还有未过期的 token),过渡期后自然过期(60s/300s TTL) ### 4.3 Service 层 UpdatePassword @@ -173,7 +179,8 @@ func (s *userService) UpdatePassword(ctx context.Context, req *pb.UpdatePassword if req.VerifyToken == "" { return nil, appErrors.ErrInvalidVerifyToken } - if err := service.VerifyToken(ctx, user.Mobile, req.VerifyToken); err != nil { + // scene="password" 必须与前端 verifyCodeApi 调用时传的一致 + if err := service.VerifyToken(ctx, "password", user.Mobile, req.VerifyToken); err != nil { return nil, appErrors.ErrInvalidVerifyToken } @@ -275,8 +282,6 @@ const NO_AUTO_LOGOUT_PATHS = [ const shouldAutoLogout = (code, url) => { if (code !== 401 && code !== 400 && code !== 403) return false return !NO_AUTO_LOGOUT_PATHS.some(p => url.includes(p)) -} - } else if (shouldAutoLogout(res.data.code, options.url)) { // 跳登录页逻辑(原样) } else if (res.data.code === 401 || res.data.code === 400 || res.data.code === 403) { @@ -506,10 +511,17 @@ const confirmChangePassword = async () => { #### 5.3.4 新增 import ```js +// 已有 sendCodeApi(mobile, scene) / verifyCodeApi(mobile, code, scene),支持任意 scene 字符串 +// 已有 updatePasswordApi,本节中扩展为 3 参数(老调用点需同步更新) import { sendCodeApi, verifyCodeApi, updatePasswordApi, ... } from '@/utils/api' import { validatePassword } from '@/utils/validator.js' // 已有 ``` +**API 函数兼容性确认**: +- `sendCodeApi(mobile, scene)`:见 [api.js:187-196](frontend/utils/api.js#L187-L196),已支持任意 `scene` 字符串(后端在 SendCode 中按 scene 路由),无需改动 +- `verifyCodeApi(mobile, code, scene)`:见 [api.js:199-209](frontend/utils/api.js#L199-L209),已支持任意 `scene`,无需改动 +- `updatePasswordApi(oldPassword, newPassword, verifyToken)`:**新签名**,§5.3.5 中给出实现;如有其他调用点需同步更新 + #### 5.3.5 API 函数扩展 [frontend/utils/api.js](frontend/utils/api.js#L281-L291) @@ -598,6 +610,8 @@ export function updatePasswordApi(oldPassword, newPassword, verifyToken) { | `ErrUserInactive` | 403 | 账号已被禁用 | | 其他 5xx | 500 | 修改失败,请稍后重试 | +**注意**:本端点 `/api/v1/account/password` 在 BUG #1 修复后(§5.1 白名单)即便返回 401/400 也不会触发"自动登出"。同一业务码 401 含义可能不同(可能是 `ErrInvalidVerifyToken` 也可能是 `ErrInvalidPassword`),前端只通过 `res.message` 文案区分,不走 status code 硬区分。实现阶段必须保证后端 `err.Error()` 文案稳定并与 §7 表格一致。 + --- ## 8. 异常路径与降级 @@ -635,7 +649,7 @@ export function updatePasswordApi(oldPassword, newPassword, verifyToken) { | Service | 修改 | `backend/services/userService/service/sms_redis.go` | | Service | 修改 | `backend/services/userService/service/sms_service.go` | | Service | 修改 | `backend/services/userService/service/user_service.go` | -| Service | 修改 | `backend/services/userService/service/auth_service.go`(SMS 调用加 scene) | +| Service | 修改 | `backend/services/userService/service/auth_service.go`(Register 中 `VerifyToken(ctx, mobile, token)` → `VerifyToken(ctx, "register", mobile, token)`,共 1 处) | | Service | 新增 | `backend/services/userService/service/user_service_password_test.go` | | Provider | 修改 | `backend/services/userService/provider/user_provider.go`(传 ctx) | | Errors | 修改 | `backend/pkg/errors/errors.go`(新增 2 个错误码 + 状态映射) |