package admin import ( "encoding/json" "fmt" "net/http" "networkDev/database" "networkDev/models" "networkDev/utils" "strings" ) // UserFragmentHandler 个人资料片段渲染 // - 渲染个人资料与修改密码表单 func UserFragmentHandler(w http.ResponseWriter, r *http.Request) { utils.RenderTemplate(w, "user.html", map[string]interface{}{}) } // UserProfileQueryHandler 查询当前登录管理员的基本信息 // - 返回 uuid/username/role/created_at 四个字段 // - 自动刷新接近过期的JWT令牌 func UserProfileQueryHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } claims, _, err := GetCurrentAdminUserWithRefresh(w, r) if err != nil { utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil) return } // 查询用户完整信息以获取创建时间 db, err := database.GetDB() if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil) return } var user models.User if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) return } utils.JsonResponse(w, http.StatusOK, true, "ok", map[string]interface{}{ "uuid": user.UUID, "username": user.Username, "role": user.Role, "created_at": user.CreatedAt, }) } // UserPasswordUpdateHandler 修改当前登录管理员的密码 // - 接收 JSON: {old_password, new_password, confirm_password} // - 校验旧密码正确性、新密码与确认一致性 // - 成功后更新密码哈希 // - 自动刷新接近过期的JWT令牌 func UserPasswordUpdateHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } claims, _, err := GetCurrentAdminUserWithRefresh(w, r) if err != nil { utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil) return } var body struct { OldPassword string `json:"old_password"` NewPassword string `json:"new_password"` ConfirmPassword string `json:"confirm_password"` } var decodeErr error if decodeErr = json.NewDecoder(r.Body).Decode(&body); decodeErr != nil { utils.JsonResponse(w, http.StatusBadRequest, false, "请求参数错误", nil) return } // 基础校验 if body.OldPassword == "" || body.NewPassword == "" || body.ConfirmPassword == "" { utils.JsonResponse(w, http.StatusBadRequest, false, "旧密码/新密码/确认密码均不能为空", nil) return } if len(body.NewPassword) < 6 { utils.JsonResponse(w, http.StatusBadRequest, false, "新密码长度不能少于6位", nil) return } if body.NewPassword != body.ConfirmPassword { utils.JsonResponse(w, http.StatusBadRequest, false, "两次输入的新密码不一致", nil) return } if body.NewPassword == body.OldPassword { utils.JsonResponse(w, http.StatusBadRequest, false, "新密码不能与旧密码相同", nil) return } db, err := database.GetDB() if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil) return } // 查询当前用户 var user models.User if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) return } // 校验旧密码(使用盐值验证) if !utils.VerifyPasswordWithSalt(body.OldPassword, user.PasswordSalt, user.Password) { utils.JsonResponse(w, http.StatusUnauthorized, false, "旧密码不正确", nil) return } // 生成新的密码盐值 newSalt, err := utils.GenerateRandomSalt() if err != nil { // 添加详细错误日志 fmt.Printf("生成密码盐失败: %v\n", err) utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码盐失败", nil) return } fmt.Printf("成功生成新盐值,长度: %d\n", len(newSalt)) // 使用新盐值生成密码哈希 hash, err := utils.HashPasswordWithSalt(body.NewPassword, newSalt) if err != nil { // 添加详细错误日志 fmt.Printf("生成密码哈希失败: %v, 密码长度: %d, 盐值长度: %d\n", err, len(body.NewPassword), len(newSalt)) utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码哈希失败", nil) return } fmt.Printf("成功生成密码哈希,长度: %d\n", len(hash)) // 更新密码和盐值 if dbErr := db.Model(&models.User{}).Where("uuid = ?", claims.UserUUID).Updates(map[string]interface{}{ "password": hash, "password_salt": newSalt, }).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "更新密码失败", nil) return } // 重新查询用户信息(包含新密码) var updatedUser models.User if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&updatedUser).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "查询用户信息失败", nil) return } // 重新生成JWT令牌(包含新的密码哈希摘要) newToken, err := generateJWTToken(updatedUser) if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "生成新令牌失败", nil) return } // 更新Cookie cookie := &http.Cookie{ Name: "admin_session", Value: newToken, Path: "/", HttpOnly: true, Secure: false, // 生产环境应设置为true(HTTPS) MaxAge: 24 * 60 * 60, // 24小时 } http.SetCookie(w, cookie) // 密码修改成功,已重新生成JWT令牌 utils.JsonResponse(w, http.StatusOK, true, "密码修改成功", nil) } // UserProfileUpdateHandler 修改当前登录管理员的用户名 // - 接收 JSON: {username} // - 校验用户名非空、长度与唯一性 // - 更新数据库后重新签发JWT并写入 Cookie,保持前端展示的一致性 // - 自动刷新接近过期的JWT令牌 func UserProfileUpdateHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } claims, _, err := GetCurrentAdminUserWithRefresh(w, r) if err != nil { utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil) return } var body struct { Username string `json:"username"` OldPassword string `json:"old_password"` } if decodeErr := json.NewDecoder(r.Body).Decode(&body); decodeErr != nil { utils.JsonResponse(w, http.StatusBadRequest, false, "请求参数错误", nil) return } username := strings.TrimSpace(body.Username) if username == "" { utils.JsonResponse(w, http.StatusBadRequest, false, "用户名不能为空", nil) return } if len(username) > 64 { utils.JsonResponse(w, http.StatusBadRequest, false, "用户名长度不能超过64字符", nil) return } db, err := database.GetDB() if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil) return } // 检查唯一性:排除当前用户UUID var cnt int64 if dbErr := db.Model(&models.User{}).Where("username = ? AND uuid <> ?", username, claims.UserUUID).Count(&cnt).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "检查用户名唯一性失败", nil) return } if cnt > 0 { utils.JsonResponse(w, http.StatusBadRequest, false, "用户名已存在,请更换", nil) return } // 如果未变化则直接返回成功(无需校验旧密码) if strings.EqualFold(username, claims.Username) { utils.JsonResponse(w, http.StatusOK, true, "保存成功", map[string]interface{}{ "username": username, }) return } // 修改用户名需要进行当前密码校验 if strings.TrimSpace(body.OldPassword) == "" { utils.JsonResponse(w, http.StatusBadRequest, false, "修改用户名需要提供当前密码", nil) return } // 查询当前用户并校验旧密码 var user models.User if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) return } // 使用盐值验证当前密码 if !utils.VerifyPasswordWithSalt(body.OldPassword, user.PasswordSalt, user.Password) { utils.JsonResponse(w, http.StatusUnauthorized, false, "当前密码不正确", nil) return } // 执行更新 if dbErr := db.Model(&models.User{}).Where("uuid = ?", claims.UserUUID).Update("username", username).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "更新用户名失败", nil) return } // 重新签发JWT并写入Cookie newUser := models.User{UUID: claims.UserUUID, Username: username, Role: claims.Role} token, err := generateJWTToken(newUser) if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "生成新令牌失败", nil) return } cookie := &http.Cookie{ Name: "admin_session", Value: token, Path: "/", HttpOnly: true, Secure: false, MaxAge: 24 * 60 * 60, } http.SetCookie(w, cookie) utils.JsonResponse(w, http.StatusOK, true, "保存成功", map[string]interface{}{ "username": username, }) }