- 头像提交审核不再删除旧文件,审核通过后才删除旧头像文件 - 头像审核被拒时删除已上传的新头像文件,保留旧头像 - CSP style-src 移除 nonce 以启用 unsafe-inline(JS 动态样式需要) - CSP script-src 移除 nonce 改用 unsafe-inline + unsafe-eval(Vditor 编辑器和动态样式需要)
129 lines
3.6 KiB
Go
129 lines
3.6 KiB
Go
package service
|
|
|
|
import (
|
|
"metazone.cc/metalab/internal/common"
|
|
"metazone.cc/metalab/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// review 通用审核操作
|
|
func (s *AuditService) review(reviewerID uint, submissionID uint, approved bool, reason string) error {
|
|
// 1. 查审核记录
|
|
submission, err := s.auditRepo.FindByID(submissionID)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return common.ErrAuditNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
// 2. 必须 pending
|
|
if submission.Status != model.AuditStatusPending {
|
|
return common.ErrAuditNotPending
|
|
}
|
|
|
|
// 3. 查审核人信息
|
|
reviewer, err := s.userRepo.FindByID(reviewerID)
|
|
if err != nil {
|
|
return common.ErrUserNotFound
|
|
}
|
|
|
|
// 4. 标记审核结果
|
|
submission.ReviewedBy = &reviewerID
|
|
submission.ReviewerName = &reviewer.Username
|
|
if approved {
|
|
submission.Status = model.AuditStatusApproved
|
|
// 更新 User 表对应字段
|
|
if err := s.applyApproval(submission); err != nil {
|
|
return err
|
|
}
|
|
// 通知用户审核通过
|
|
if s.notifier != nil {
|
|
s.notifier.NotifyAuditApproved(submission.UserID, submission.AuditType, submission.ID)
|
|
}
|
|
} else {
|
|
submission.Status = model.AuditStatusRejected
|
|
submission.RejectReason = &reason
|
|
|
|
// 改名审核被拒 → 仅非首次改名(已扣费)才退还域能
|
|
// 首次改名免费,被拒不退还
|
|
shouldRefund := false
|
|
if submission.AuditType == model.AuditTypeUsername && s.energyRefundSvc != nil {
|
|
if s.levelSvc != nil {
|
|
hasCompleted, _ := s.levelSvc.HasCompletedTask(submission.UserID, "username")
|
|
shouldRefund = hasCompleted
|
|
}
|
|
if shouldRefund {
|
|
_ = s.energyRefundSvc.RefundOnRenameReject(submission.UserID)
|
|
}
|
|
}
|
|
|
|
// 通知用户审核被拒(仅实际退款时追加域能返还提示)
|
|
if s.notifier != nil {
|
|
reasonMsg := reason
|
|
if shouldRefund {
|
|
reasonMsg += "。扣除的域能已被返还"
|
|
}
|
|
s.notifier.NotifyAuditRejected(submission.UserID, submission.AuditType, reasonMsg, submission.ID)
|
|
}
|
|
|
|
// 头像审核被拒 → 删除提交的新头像文件
|
|
if submission.AuditType == model.AuditTypeAvatar && s.avatarCleaner != nil {
|
|
_ = s.avatarCleaner.DeleteAvatarFile(submission.NewValue)
|
|
}
|
|
}
|
|
return s.auditRepo.Update(submission)
|
|
}
|
|
|
|
// applyApproval 将审核通过的内容写入 User 表
|
|
// 用户名审批前再次检查冲突(防止提交到审批期间被其他用户抢占)
|
|
func (s *AuditService) applyApproval(submission *model.AuditSubmission) error {
|
|
user, err := s.userRepo.FindByID(submission.UserID)
|
|
if err != nil {
|
|
return common.ErrUserNotFound
|
|
}
|
|
|
|
switch submission.AuditType {
|
|
case model.AuditTypeUsername:
|
|
exists, err := s.userRepo.ExistsByUsername(submission.NewValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exists {
|
|
return common.ErrUsernameTaken
|
|
}
|
|
user.Username = submission.NewValue
|
|
case model.AuditTypeAvatar:
|
|
oldAvatar := user.Avatar
|
|
user.Avatar = submission.NewValue
|
|
if err := s.userRepo.Update(user); err != nil {
|
|
return err
|
|
}
|
|
// 审核通过,删除旧头像文件
|
|
if s.avatarCleaner != nil {
|
|
_ = s.avatarCleaner.DeleteAvatarFile(oldAvatar)
|
|
}
|
|
return nil
|
|
case model.AuditTypeBio:
|
|
user.Bio = submission.NewValue
|
|
}
|
|
if err := s.userRepo.Update(user); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 触发首次任务奖励(静默失败不影响主流程)
|
|
if s.levelSvc != nil {
|
|
switch submission.AuditType {
|
|
case model.AuditTypeUsername:
|
|
_, _, _ = s.levelSvc.CompleteTask(submission.UserID, "username")
|
|
case model.AuditTypeAvatar:
|
|
_, _, _ = s.levelSvc.CompleteTask(submission.UserID, "avatar")
|
|
case model.AuditTypeBio:
|
|
_, _, _ = s.levelSvc.CompleteTask(submission.UserID, "bio")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|