# 📋 实施计划:后台管理端 - 添加修改用户手机号功能 ## 任务类型 - [x] 后端 (→ Codex) - [ ] 前端 - [ ] 全栈 --- ## 📊 需求描述 为后台管理端添加修改用户手机号的功能,允许管理员直接修改用户的手机号。 --- ## 🎯 技术方案 ### API 设计 ```http PUT /api/admin/users/{user_id}/mobile Content-Type: application/json Authorization: Bearer {admin_token} Request Body: { "mobile": "13800138000" } Response: { "success": true, "message": "手机号更新成功" } ``` ### 安全校验 1. ✅ 管理员权限验证(RBAC: `user:edit`) 2. ✅ 手机号格式验证(11位数字,1开头) 3. ✅ 手机号唯一性检查(不能与其他用户重复) 4. ✅ 用户存在性验证 --- ## 🔧 实施步骤 ### 步骤 1:添加 API 处理函数 **文件**:`internal/api/admin/users_admin.go` **位置**:Line 1893 后(在 `UpdateUserRemark()` 函数之后) **代码**: ```go // updateUserMobileRequest 更新用户手机号请求 type updateUserMobileRequest struct { Mobile string `json:"mobile" binding:"required"` } // UpdateUserMobile 更新用户手机号 // @Summary 更新用户手机号 // @Description 管理员修改用户手机号 // @Tags 管理端.用户 // @Accept json // @Produce json // @Param user_id path integer true "用户ID" // @Param body body updateUserMobileRequest true "手机号信息" // @Success 200 {object} map[string]any // @Failure 400 {object} code.Failure // @Router /api/admin/users/{user_id}/mobile [put] // @Security LoginVerifyToken func (h *handler) UpdateUserMobile() core.HandlerFunc { return func(ctx core.Context) { userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "用户ID无效")) return } req := new(updateUserMobileRequest) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } // 验证手机号格式(11位数字,1开头) matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, req.Mobile) if !matched { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "手机号格式不正确")) return } // 检查手机号是否被其他用户占用 existedUser, _ := h.readDB.Users.WithContext(ctx.RequestContext()). Where(h.readDB.Users.Mobile.Eq(req.Mobile)). Where(h.readDB.Users.ID.Neq(userID)). First() if existedUser != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "该手机号已被其他用户使用")) return } // 更新用户手机号 _, err = h.writeDB.Users.WithContext(ctx.RequestContext()). Where(h.writeDB.Users.ID.Eq(userID)). Update(h.writeDB.Users.Mobile, req.Mobile) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 20303, "更新失败: "+err.Error())) return } ctx.Payload(map[string]any{ "success": true, "message": "手机号更新成功", }) } } ``` --- ### 步骤 2:添加导入依赖 **文件**:`internal/api/admin/users_admin.go` **位置**:文件顶部 import 区域 **修改**:在现有 import 中添加 `regexp` 包 ```go import ( "fmt" "math" "net/http" "regexp" // ← 添加这一行 "strconv" "strings" "time" "bindbox-game/internal/code" // ... 其他导入保持不变 ) ``` --- ### 步骤 3:注册路由 **文件**:`internal/router/router.go` **位置**:在 adminAuthApiRouter 用户管理路由组中 **查找位置参考**: ```go // 找到类似这样的路由组(用户管理相关) adminAuthApiRouter.PUT("/users/:user_id/douyin_user_id", ...) adminAuthApiRouter.PUT("/users/:user_id/remark", ...) adminAuthApiRouter.PUT("/users/:user_id/status", ...) ``` **添加路由**: ```go // 在上述路由附近添加 adminAuthApiRouter.PUT("/users/:user_id/mobile", intc.RequireAdminAction("user:edit"), adminHandler.UpdateUserMobile()) ``` --- ### 步骤 4:更新 Swagger 文档 **文件**:运行命令生成文档 ```bash make gen-swagger ``` --- ## 📋 关键文件清单 | 文件路径 | 操作 | 说明 | |---------|------|------| | `internal/api/admin/users_admin.go` | 新增函数 | 添加 `UpdateUserMobile()` 及请求结构体 | | `internal/api/admin/users_admin.go` | 修改导入 | 添加 `regexp` 包 | | `internal/router/router.go` | 新增路由 | 注册 `PUT /users/:user_id/mobile` | --- ## ✅ 验证清单 完成后请验证: - [ ] API 接口可正常访问 - [ ] 手机号格式验证生效(非法格式返回错误) - [ ] 手机号唯一性检查生效(重复手机号返回错误) - [ ] 更新成功后数据库中手机号已变更 - [ ] Swagger 文档已更新 - [ ] 权限验证生效(非管理员或无 `user:edit` 权限无法访问) --- ## ⚠️ 风险与缓解 | 风险 | 缓解措施 | |------|----------| | 手机号格式错误 | 正则验证 `^1[3-9]\d{9}$` | | 手机号被占用 | 查询检查 `WHERE mobile = ? AND id != ?` | | 权限滥用 | RBAC 中间件 `RequireAdminAction("user:edit")` | | 更新失败 | 捕获异常并返回错误码 20303 | --- ## 📝 测试用例 ### 正常流程 ```bash curl -X PUT http://localhost:9991/api/admin/users/123/mobile \ -H "Authorization: Bearer {admin_token}" \ -H "Content-Type: application/json" \ -d '{"mobile":"13800138000"}' # 预期响应 { "success": true, "message": "手机号更新成功" } ``` ### 异常流程 **1. 手机号格式错误** ```json {"mobile": "123"} → {"code": 10001, "message": "手机号格式不正确"} ``` **2. 手机号已被占用** ```json {"mobile": "13800138000"} // 已被用户456使用 → {"code": 10001, "message": "该手机号已被其他用户使用"} ``` **3. 用户ID不存在** ```bash PUT /api/admin/users/999999/mobile → 更新成功但影响行数为0(GORM特性,不会报错) ``` --- ## 🚀 后续优化建议(可选) 1. **操作日志记录**:记录管理员修改手机号的操作到审计日志 2. **短信验证**:要求新手机号验证码确认(防止恶意修改) 3. **旧手机号通知**:向旧手机号发送变更通知短信 4. **限制修改频率**:同一用户手机号修改间隔限制(如7天) --- ## 📌 备注 - 本方案仅实现管理端修改功能,不包含用户端自助修改 - 遵循现有代码风格(参考 `UpdateUserRemark()` 和 `UpdateUserDouyinID()`) - 使用 GORM Gen 生成的 DAO 进行数据库操作 - 错误码 20303 用于手机号更新失败(延续现有错误码序列 20301, 20302)