feat(settings): 用户名校验强化、个性签名编辑、字符计数器及提示优化

- 用户名校验改用白名单正则(中英文/数字/下划线/连字符),字符数计法统一
- 简介→个性签名,新增 textarea 可编辑,上限 128 字符
- 用户名+签名各增加实时字符计数器(x/16、x/128)
- API 合并为 PUT /api/settings/profile,一次提交两字段,按变更写入
- Toast 移至 NAV 下方,独立样式,5 秒自动消失
- 移除用户 ID 灰色底色,提示改为「个人资料已更新」
- 纯文本存储 + Go 模板自动转义防 XSS
This commit is contained in:
2026-05-27 00:03:09 +08:00
parent 3937945b43
commit debaa17127
6 changed files with 347 additions and 12 deletions

View File

@ -9,9 +9,10 @@ import (
"github.com/gin-gonic/gin"
)
// profileProvider SettingsController 对 Service 层的最小依赖ISP1 个方法)
// profileProvider SettingsController 对 Service 层的最小依赖ISP2 个方法)
type profileProvider interface {
GetProfile(userID uint) (*model.User, error)
UpdateProfile(userID uint, username, bio string) error
}
// SettingsController 个人设置控制器
@ -55,3 +56,42 @@ func (sc *SettingsController) SettingsPage(c *gin.Context) {
"StatusName": model.StatusDisplayNames[user.Status],
}))
}
// UpdateProfile 修改个人资料(用户名 + 个性签名,需登录)
func (sc *SettingsController) UpdateProfile(c *gin.Context) {
uid, exists := c.Get("uid")
if !exists {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
var req struct {
Username string `json:"username"`
Bio string `json:"bio"`
}
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, http.StatusBadRequest, "参数错误")
return
}
if err := sc.authService.UpdateProfile(uid.(uint), req.Username, req.Bio); err != nil {
handleSettingsError(c, err)
return
}
common.OkMessage(c, "个人资料已更新")
}
// handleSettingsError 统一处理 settings 接口的 service 层错误
func handleSettingsError(c *gin.Context, err error) {
switch err {
case common.ErrUsernameTaken:
common.Error(c, http.StatusConflict, err.Error())
case common.ErrUsernameInvalid:
common.Error(c, http.StatusBadRequest, err.Error())
case common.ErrBioTooLong:
common.Error(c, http.StatusBadRequest, err.Error())
default:
common.Error(c, http.StatusInternalServerError, "操作失败")
}
}