Files
mce/internal/controller/comment_upload_controller.go
Victor_Jay 97adf54d6d fix: golangci-lint 零告警通过 (57→0) + gofumpt/goimports 全量格式化
## CI 修复 (P0/P1)

P0 — 编译阻塞:
  - interfaces.go: postUseCase ISP 接口移除不用的 Create/Update/Delete

P1 — 必须修复:
  - ST1000: 为 13 个包添加包注释 (common/config/model/service/...)
  - ST1005: redis_store.go 全部错误消息改为小写开头
  - errcheck (19处): defer Close()→闭包忽略, notifier.Create→_=, r.Run→检查error
  - errorlint (9处): switch-on-error→errors.Is 链, ==→errors.Is
  - ST1020/ST1022: 导出符号注释以符号名开头

P2 — 安全评审:
  - gosec (12处): G203/G301/G304/G306 添加 nolint 注释并附理由

P3 — 清理:
  - unused: 移除 hasUnicode/energyStore/current/energyAdminUseCase
  - gofumpt + goimports 全量格式化 (35+ 文件)
2026-06-22 02:27:35 +08:00

120 lines
3.1 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 (
"bytes"
"image"
"image/jpeg"
"image/png"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"metazone.cc/mce/internal/common"
"github.com/gin-gonic/gin"
)
// UploadImage 上传评论图片 POST /api/comments/upload-image
// 需要 LV4+Exp ≥ 1500复用帖子图片上传的 WebP 转码逻辑
func (ctrl *CommentController) UploadImage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
// LV4+ 检查Exp ≥ 1500
expVal, exists := c.Get("exp")
if !exists {
common.Error(c, http.StatusForbidden, "无法获取经验值")
return
}
exp := expVal.(int)
if exp < 1500 {
common.Error(c, http.StatusForbidden, "需 Lv4 以上才能上传评论图片")
return
}
file, header, err := c.Request.FormFile("file")
if err != nil {
common.Error(c, http.StatusBadRequest, "请选择文件")
return
}
defer func() { _ = file.Close() }()
// 检查扩展名
ext := strings.ToLower(filepath.Ext(header.Filename))
allowedExt := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true}
if !allowedExt[ext] {
common.Error(c, http.StatusBadRequest, "仅支持 jpg/jpeg/png/gif/webp 格式")
return
}
// 限制 5MB
if header.Size > 5<<20 {
common.Error(c, http.StatusBadRequest, "图片大小不能超过 5MB")
return
}
data, err := io.ReadAll(file)
if err != nil {
common.Error(c, http.StatusInternalServerError, "读取文件失败")
return
}
storageDir := "storage/uploads/posts"
if err := os.MkdirAll(storageDir, 0o755); err != nil { //nolint:gosec // 上传目录标准权限
common.Error(c, http.StatusInternalServerError, "存储初始化失败")
return
}
ts := time.Now().UnixMilli()
isJPEGPNG := ext == ".jpg" || ext == ".jpeg" || ext == ".png"
var savePath, url string
// JPEG/PNG 转 WebP
if isJPEGPNG {
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
common.Error(c, http.StatusBadRequest, "图片格式无效")
return
}
webpData := encodeWebPBinary(img, len(data))
if webpData != nil && len(webpData) < len(data) {
filename := strconv.FormatUint(uint64(uid), 10) + "_" + strconv.FormatInt(ts, 10) + ".webp"
savePath = filepath.Join(storageDir, filename)
_ = os.WriteFile(savePath, webpData, 0o644) //nolint:gosec // 上传文件标准权限
url = "/uploads/posts/" + filename
}
}
// 回退存储
if savePath == "" {
dataOut := data
if isJPEGPNG {
decImg, _, decErr := image.Decode(bytes.NewReader(data))
if decErr == nil {
var buf bytes.Buffer
if ext == ".png" {
_ = png.Encode(&buf, decImg)
} else {
_ = jpeg.Encode(&buf, decImg, &jpeg.Options{Quality: 92})
}
dataOut = buf.Bytes()
}
}
filename := strconv.FormatUint(uint64(uid), 10) + "_" + strconv.FormatInt(ts, 10) + ext
savePath = filepath.Join(storageDir, filename)
_ = os.WriteFile(savePath, dataOut, 0o644) //nolint:gosec // 上传文件标准权限
url = "/uploads/posts/" + filename
}
// 返回标准 JSON前端将 URL 插入评论文本)
common.Ok(c, gin.H{"url": url})
}