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 8e0ed38..a327f5e 100644 --- a/docs/superpowers/specs/2026-06-12-change-password-design.md +++ b/docs/superpowers/specs/2026-06-12-change-password-design.md @@ -53,7 +53,7 @@ │ │ │ │ │ │ 1.点击"修改密码" │ │ │ │ ├───────────────────────────→│ │ │ │ - │ │ 弹窗打开(展示5个UI区:手机号+验证码一行、短信码、3 个密码框) │ │ │ + │ │ 弹窗打开(5个UI区) │ │ │ │ │ │ │ │ │ 2.点"获取验证码" │ │ │ │ ├───────────────────────────→│ │ │ │ @@ -182,9 +182,14 @@ grep -rn "SaveVerifyToken\|GetVerifyToken\|DeleteVerifyToken\|VerifyToken(\|Save ```go func (s *userService) UpdatePassword(ctx context.Context, req *pb.UpdatePasswordRequest, userID int64) (*pb.UpdatePasswordResponse, error) { - // 0. (新增) 验证短信 token + // 0. (新增) 查询用户(后续需 user.Mobile 校验 verify_token) user, err := s.userRepo.GetByID(userID) - if err != nil { ... } + if err != nil { + if errors.Is(err, appErrors.ErrUserNotFound) { + return nil, appErrors.ErrUserNotFound + } + return nil, fmt.Errorf("failed to get user: %w", err) + } if req.VerifyToken == "" { return nil, appErrors.ErrInvalidVerifyToken @@ -313,17 +318,13 @@ func ToStatusCode(err error) pb.StatusCode { **错误映射表(实施后)**: -| 错误 | 旧映射 | 新映射 | 说明 | -|---|---|---|---| -| `ErrInvalidPassword`(Login 用) | 401 | 401 | 不变:登录场景下密码错 = 鉴权失败 | - | 错误 | 旧映射 | 新映射 | 说明 | |---|---|---|---| | `ErrInvalidPassword`(Login 用) | 401 | 401 | 不变:登录场景下密码错 = 鉴权失败 | | `ErrInvalidOldPassword`(新,ChangePassword 用) | — | **400** | 改密场景下旧密码错 = 业务校验(用户已通过 AuthMiddleware 鉴权) | +| `ErrPasswordTooShort` | 400 | 400 | 不变 | | `ErrInvalidVerifyToken`(新) | (旧方案 401) | **400** | 改密场景下短信 token 错 = 业务校验(用户已通过 AuthMiddleware 鉴权) | | `ErrSameAsOldPassword`(新) | 400 | 400 | 不变 | -| `ErrPasswordTooShort` | 400 | 400 | 不变 | | `ErrUserInactive` | 500(实际落 default) | **403** | 修复:此前 ToStatusCode 没有 case 落到 default 500,本次显式映射 403;配合 §12 Login 流程一起修复 | | `ErrInvalidToken` / `ErrTokenExpired` / `ErrTokenMismatch` | 401 | 401 | 不变:真鉴权问题 | @@ -813,8 +814,9 @@ 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`(Register 中 `VerifyToken(ctx, mobile, token)` → `VerifyToken(ctx, "register", mobile, token)`,共 1 处) | -| Service | 新增 | `backend/services/userService/service/user_service_password_test.go` | +| Service | 修改 | `backend/services/userService/service/auth_service.go`(Register 中 `VerifyToken(ctx, mobile, token)` → `VerifyToken(ctx, "register", mobile, token)`,共 1 处;§12 修复 Login 流程 2 处 `return` 改用 typed error) | +| Service | 新增 | `backend/services/userService/service/user_service_password_test.go`(§4.6 10 个用例) | +| Service | 新增 | `backend/services/userService/service/auth_service_login_test.go`(§12.4 2 个用例,banned/frozen 返 403) | | Provider | 修改 | `backend/services/userService/provider/user_provider.go`(传 ctx) | | Errors | 修改 | `backend/pkg/errors/errors.go`(新增 3 个错误码 `ErrInvalidVerifyToken`/`ErrSameAsOldPassword`/`ErrInvalidOldPassword`;`ToStatusCode` 中 `ErrInvalidVerifyToken` 改映射 400;`ToStatusCode` 改用 `errors.Is` 全面识别 wrapped error;新增 `NewAccountBannedError` / `NewAccountFrozenError` 2 个 helper) | | Frontend utils | 修改 | `frontend/utils/api.js`(拦截器去掉 400 自动登出,保留 401/403 + `updatePasswordApi` 增 `verify_token` 字段) | @@ -826,10 +828,15 @@ export function updatePasswordApi(oldPassword, newPassword, verifyToken) { - [ ] proto 重新生成后,所有引用 `UpdatePasswordRequest` 的代码编译通过 - [ ] Redis key 升级:老的 `sms:register:*` / `verify:register:*` 数据保留(Register 仍用 scene=register);新加的 `sms:password:*` / `verify:password:*` 独立 -- [ ] 后端单测全绿 +- [ ] **API 接口回归**: Login / Register / RefreshToken / Logout / GetMyProfile / UpdateNickname 等所有走 `ToStatusCode` 的接口,在 `errors.Is` 改造后行为不变(单测 + 灰度验证) +- [ ] **登录态回归**: 在修复 BUG #1(拦截器去掉 400 自动登出)后,跑一遍以下场景确认无回归: + - 输错密码登录(Login 接口返 401)→ 前端拦截器跳登录 ✅ + - 账号被冻结登录(§12 修复后应返 403,而非 500)→ 弹封禁原因 + 跳登录 ✅ + - 业务校验错(改密输错旧密码,本次返 400)→ toast 提示,不跳登录 ✅ +- [ ] 后端单测全绿(含 §4.6 10 个 + §12.4 2 个) - [ ] 前端手动验证 10 条 checklist 通过 - [ ] Swagger 文档重新生成 -- [ ] 灰度发布,先开 10% 流量观察错误率 +- [ ] 灰度发布,先开 10% 流量观察错误率,重点监控 401/403/500 比例(应:401 不变,403 上升,500 下降) ---