Files
mce/internal/controller/settings_api_profile.go
Victor_Jay 6542d1e055 fix: 域能系统全量检查 — 修复 5 个逻辑/交互 Gap
- 修复 Admin 改名路径未扣域能(非审核路径补充 HasCompletedTask + DeductOnRename)
- 赋能按钮改为 Modal 弹窗(轻赋/重赋选择 + 每日上限提示)
- EnergyInfo API 增加 daily_energize_exp/daily_exp_cap_reached 返回值
- "我的域能" TAB 增加经验值和等级展示
- Energize 事务修复:导出 EnergyStore + WithTx 方法确保 DB 操作原子性
- Studio 写文章页新增 LV0 锁止横幅 + 禁用提交按钮
2026-06-01 12:10:16 +08:00

168 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"net/http"
"strconv"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/model"
"github.com/gin-gonic/gin"
)
// 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
}
userID := uid.(uint)
// 判断是否需要审核
shouldAudit, err := sc.auditService.ShouldAudit(userID)
if err != nil {
common.Error(c, http.StatusInternalServerError, "操作失败")
return
}
if shouldAudit {
user, err := sc.authService.GetProfile(userID)
if err != nil {
common.Error(c, http.StatusInternalServerError, "操作失败")
return
}
// 改名:首次免费,非首次扣 6 域能
if req.Username != "" && req.Username != user.Username && sc.energySvc != nil {
hasCompleted, _ := sc.levelSvc.HasCompletedTask(userID, "username")
if hasCompleted {
if err := sc.energySvc.DeductOnRename(userID); err != nil {
switch err {
case common.ErrInsufficientEnergy:
common.Error(c, http.StatusBadRequest, "域能不足,无法改名。可通过每日签到或创作被赋能获取")
default:
common.Error(c, http.StatusInternalServerError, "操作失败")
}
return
}
}
}
if err := sc.auditService.SubmitProfileChanges(userID, user, req.Username, req.Bio); err != nil {
handleAuditSubmitError(c, err)
return
}
common.OkMessage(c, "修改已提交审核,通过前当前信息保持不变")
return
}
// 管理员及以上直接更新
// 先获取当前用户信息,判断是否改名
currentUser, err := sc.authService.GetProfile(userID)
if err != nil {
common.Error(c, http.StatusInternalServerError, "操作失败")
return
}
// 改名:首次免费,非首次扣 6 域能
if req.Username != "" && req.Username != currentUser.Username && sc.energySvc != nil {
hasCompleted, _ := sc.levelSvc.HasCompletedTask(userID, "username")
if hasCompleted {
if err := sc.energySvc.DeductOnRename(userID); err != nil {
switch err {
case common.ErrInsufficientEnergy:
common.Error(c, http.StatusBadRequest, "域能不足,无法改名。可通过每日签到或创作被赋能获取")
default:
common.Error(c, http.StatusInternalServerError, "操作失败")
}
return
}
}
}
if err := sc.authService.UpdateProfile(userID, req.Username, req.Bio); err != nil {
handleSettingsError(c, err)
return
}
// 触发首次任务奖励(静默失败不影响主流程)
if req.Username != "" {
_, _, _ = sc.levelSvc.CompleteTask(userID, "username")
}
if req.Bio != "" {
_, _, _ = sc.levelSvc.CompleteTask(userID, "bio")
}
common.OkMessage(c, "个人资料已更新")
}
// UploadAvatar 上传头像需登录multipart/form-data
// 普通用户走审核流程,管理员及以上直接更新
func (sc *SettingsController) UploadAvatar(c *gin.Context) {
uid, exists := c.Get("uid")
if !exists {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
file, header, err := c.Request.FormFile("avatar")
if err != nil {
common.Error(c, http.StatusBadRequest, "请选择文件")
return
}
defer file.Close()
// 裁切参数(可选,来自前端裁切弹窗)
cropX, _ := strconv.Atoi(c.PostForm("crop_x"))
cropY, _ := strconv.Atoi(c.PostForm("crop_y"))
cropSize, _ := strconv.Atoi(c.PostForm("crop_size"))
userID := uid.(uint)
// 判断是否需要审核
shouldAudit, err := sc.auditService.ShouldAudit(userID)
if err != nil {
common.Error(c, http.StatusInternalServerError, "操作失败")
return
}
if shouldAudit {
// 仅处理图片,不更新用户
avatarURL, err := sc.avatarService.ProcessImage(userID, file, header.Header.Get("Content-Type"), cropX, cropY, cropSize)
if err != nil {
common.Error(c, http.StatusBadRequest, err.Error())
return
}
if err := sc.auditService.Submit(userID, model.AuditTypeAvatar, avatarURL); err != nil {
handleAuditSubmitError(c, err)
return
}
common.OkMessage(c, "头像已提交审核,通过前当前头像保持不变")
return
}
// 管理员及以上直接更新
url, err := sc.avatarService.ProcessAvatar(userID, file, header.Header.Get("Content-Type"), cropX, cropY, cropSize)
if err != nil {
common.Error(c, http.StatusBadRequest, err.Error())
return
}
// 触发首次上传头像奖励
_, _, _ = sc.levelSvc.CompleteTask(userID, "avatar")
common.Ok(c, gin.H{"url": url})
}