个人信息页面增强

This commit is contained in:
ryan
2026-06-08 19:38:53 +08:00
parent b96624a251
commit 0f65202659
22 changed files with 1568 additions and 370 deletions
@@ -147,9 +147,9 @@ func TestListSystemConfigs(t *testing.T) {
var configs []model.SystemConfig
json.Unmarshal(dataBytes, &configs)
// Defaults seed 21 configurations
if len(configs) != 21 {
t.Errorf("expected 21 default configs, got %d", len(configs))
// Defaults seed 22 configurations
if len(configs) != 22 {
t.Errorf("expected 22 default configs, got %d", len(configs))
}
})
+18 -11
View File
@@ -27,17 +27,18 @@ import (
// PublicConfigResponse 公共配置响应
type PublicConfigResponse struct {
UploadAllowedExtensions string `json:"upload_allowed_extensions"` // 允许上传的图片扩展名
SiteName string `json:"site_name"` // 站点名称
PasswordLoginEnabled bool `json:"password_login_enabled"` // 是否允许密码登录
RegistrationEnabled bool `json:"registration_enabled"` // 是否允许注册
PasswordRegisterEnabled bool `json:"password_register_enabled"` // 是否允许密码注册
OIDCLoginEnabled bool `json:"oidc_login_enabled"` // 是否允许 OIDC 登录
MaxAPIKeysPerUser int `json:"max_api_keys_per_user"` // 每个用户最大 API Key 数量
CapLoginEnabled bool `json:"cap_login_enabled"` // 是否启用人机验证
CapAutoSolve bool `json:"cap_auto_solve"` // 打开页面后是否自动开始计算
EmailLoginVerificationEnabled bool `json:"email_login_verification_enabled"` // 是否启用邮箱登录验证
EmailRegisterVerificationEnabled bool `json:"email_register_verification_enabled"` // 是否启用邮箱注册验证
UploadAllowedExtensions string `json:"upload_allowed_extensions"` // 允许上传的图片扩展名
SiteName string `json:"site_name"` // 站点名称
PasswordLoginEnabled bool `json:"password_login_enabled"` // 是否允许密码登录
RegistrationEnabled bool `json:"registration_enabled"` // 是否允许注册
PasswordRegisterEnabled bool `json:"password_register_enabled"` // 是否允许密码注册
OIDCLoginEnabled bool `json:"oidc_login_enabled"` // 是否允许 OIDC 登录
MaxAPIKeysPerUser int `json:"max_api_keys_per_user"` // 每个用户最大 API Key 数量
CapLoginEnabled bool `json:"cap_login_enabled"` // 是否启用人机验证
CapAutoSolve bool `json:"cap_auto_solve"` // 打开页面后是否自动开始计算
EmailLoginVerificationEnabled bool `json:"email_login_verification_enabled"` // 是否启用邮箱登录验证
EmailRegisterVerificationEnabled bool `json:"email_register_verification_enabled"` // 是否启用邮箱注册验证
MenuDisplayConfig map[string]bool `json:"menu_display_config"` // 目录显示配置
}
// GetPublicConfig 获取公共配置
@@ -116,6 +117,11 @@ func GetPublicConfig(c *gin.Context) {
emailRegisterVerificationEnabled = val
}
menuDisplayConfig, err := model.GetMenuDisplayConfig(ctx)
if err != nil {
menuDisplayConfig = make(map[string]bool)
}
response := PublicConfigResponse{
UploadAllowedExtensions: uploadExtensions,
SiteName: siteName,
@@ -128,6 +134,7 @@ func GetPublicConfig(c *gin.Context) {
CapAutoSolve: capAutoSolve,
EmailLoginVerificationEnabled: emailLoginVerificationEnabled,
EmailRegisterVerificationEnabled: emailRegisterVerificationEnabled,
MenuDisplayConfig: menuDisplayConfig,
}
c.JSON(http.StatusOK, util.OK(response))
+12
View File
@@ -30,9 +30,15 @@ type BasicUserInfo struct {
ID uint64 `json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Email string `json:"email"`
AvatarUrl string `json:"avatar_url"`
IsAdmin bool `json:"is_admin"`
NeedChangePassword bool `json:"need_change_password"`
Bio string `json:"bio"`
Phone string `json:"phone"`
Gender string `json:"gender"`
Website string `json:"website"`
Location string `json:"location"`
}
func BuildBasicUserInfo(user *model.User, needChange bool) BasicUserInfo {
@@ -40,9 +46,15 @@ func BuildBasicUserInfo(user *model.User, needChange bool) BasicUserInfo {
ID: user.ID,
Username: user.Username,
Nickname: user.Nickname,
Email: user.Email,
AvatarUrl: user.AvatarUrl,
IsAdmin: user.IsAdmin,
NeedChangePassword: needChange,
Bio: user.Bio,
Phone: user.Phone,
Gender: user.Gender,
Website: user.Website,
Location: user.Location,
}
}
+85
View File
@@ -549,3 +549,88 @@ func SendEmailCode(c *gin.Context) {
c.JSON(http.StatusOK, util.OKNil())
}
type updateProfileRequest struct {
Nickname string `json:"nickname"`
Email string `json:"email"`
AvatarUrl string `json:"avatar_url"`
Bio string `json:"bio"`
Phone string `json:"phone"`
Gender string `json:"gender"`
Website string `json:"website"`
Location string `json:"location"`
}
// UpdateProfile 修改当前登录用户的个人资料
// @Summary 修改当前登录用户的个人资料
// @Description 修改当前登录用户的昵称、邮箱、头像、简介、电话、性别、个人网站和所在地。
// @Tags user
// @Accept json
// @Produce json
// @Param request body user.updateProfileRequest true "更新请求参数"
// @Success 200 {object} util.ResponseAny{data=oauth.BasicUserInfo} "修改成功,返回更新后的用户信息"
// @Failure 400 {object} util.ResponseAny "邮箱已被占用或参数错误"
// @Failure 401 {object} util.ResponseAny "未登录"
// @Router /api/v1/user/profile [put]
func UpdateProfile(c *gin.Context) {
var req updateProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, util.Err(err.Error()))
return
}
userObj, _ := util.GetFromContext[*model.User](c, oauth.UserObjKey)
if userObj == nil {
c.JSON(http.StatusUnauthorized, util.Err("请先登录"))
return
}
ctx := c.Request.Context()
var dbUser model.User
if err := db.DB(ctx).Where("id = ?", userObj.ID).First(&dbUser).Error; err != nil {
c.JSON(http.StatusOK, util.Err("未找到该用户"))
return
}
// 校验邮箱格式与唯一性
req.Email = strings.TrimSpace(req.Email)
if req.Email != "" && req.Email != dbUser.Email {
if !strings.Contains(req.Email, "@") || !strings.Contains(req.Email, ".") {
c.JSON(http.StatusOK, util.Err("邮箱格式不正确"))
return
}
var count int64
if err := db.DB(ctx).Model(&model.User{}).Where("email = ? AND id != ?", req.Email, dbUser.ID).Count(&count).Error; err != nil {
c.JSON(http.StatusOK, util.Err(err.Error()))
return
}
if count > 0 {
c.JSON(http.StatusOK, util.Err("该邮箱已被其他账号绑定"))
return
}
}
// 更新字段
dbUser.Nickname = strings.TrimSpace(req.Nickname)
if dbUser.Nickname == "" {
dbUser.Nickname = dbUser.Username
}
dbUser.Email = req.Email
dbUser.AvatarUrl = req.AvatarUrl
dbUser.Bio = req.Bio
dbUser.Phone = strings.TrimSpace(req.Phone)
dbUser.Gender = strings.TrimSpace(req.Gender)
dbUser.Website = strings.TrimSpace(req.Website)
dbUser.Location = strings.TrimSpace(req.Location)
if err := db.DB(ctx).Save(&dbUser).Error; err != nil {
c.JSON(http.StatusOK, util.Err(err.Error()))
return
}
session := sessions.Default(c)
needChange := session.Get("need_change_password") == true
c.JSON(http.StatusOK, util.OK(oauth.BuildBasicUserInfo(&dbUser, needChange)))
}