后端与全仓代码质量清理(golangci 扩展集 · 测试质量 · 并发安全 · 文档同步)

代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。

Experiments: #2-#17, #18, #20, #21, #23
Metric: total_issues 108 → 8 (-92.6%)
This commit is contained in:
ryan
2026-08-16 21:23:37 +08:00
parent 5a8722ff07
commit 2f60329886
292 changed files with 1362 additions and 793 deletions
+3 -2
View File
@@ -52,12 +52,13 @@ func ensureAccessCacheListener() {
}
func startAccessCacheInvalidationListener() {
if db.Redis == nil {
redis := db.Redis // 调用方 goroutine 上捕获,避免 goroutine 内读可变全局(测试会替换 db.Redis)
if redis == nil {
return
}
go func() {
pubsub := db.Redis.Subscribe(
pubsub := redis.Subscribe(
context.Background(),
objectstore.ConfigInvalidationChannel,
fileAccessInvalidationChannel,
+8 -2
View File
@@ -68,7 +68,8 @@ func ServeFileByID(c *gin.Context) {
response.AbortNotFound(c, "文件记录未找到")
return
}
if _, ok := err.(*strconv.NumError); ok {
var numErr *strconv.NumError
if errors.As(err, &numErr) {
response.AbortBadRequest(c, "无效的上传ID")
return
}
@@ -133,6 +134,8 @@ func ServeUpload(c *gin.Context, upload *model.Upload) {
return
}
fallthrough
case fileTypeVideo, fileTypeAudio, fileTypeOther:
serveOriginalWithConditionalCheck(c, upload)
default:
serveOriginalWithConditionalCheck(c, upload)
}
@@ -205,7 +208,10 @@ func EnsureCompressedImageCache(
return nil, false, err
}
res := result.(compressedImageCacheResult)
res, ok := result.(compressedImageCacheResult)
if !ok {
return nil, false, fmt.Errorf("image compression flight returned unexpected type %T", result)
}
return res.bytes, res.cached, res.err
}
@@ -357,6 +357,7 @@ func TestNormalizeImageQuality(t *testing.T) {
}
func configureLocalStorageRoot(t *testing.T, dbConn *gorm.DB, tempDir string) {
t.Helper()
var sc model.SystemConfig
if err := dbConn.Where("key = ?", model.ConfigKeyStorageConfig).First(&sc).Error; err != nil {
t.Fatalf("failed to find storage config: %v", err)
@@ -301,7 +301,7 @@ func UpdateMyFile(c *gin.Context) {
response.AbortNotFound(c, "文件记录未找到")
return
}
if err == ingest.ErrForbidden {
if errors.Is(err, ingest.ErrForbidden) {
response.AbortForbidden(c, "无权操作")
return
}
+3 -2
View File
@@ -178,7 +178,8 @@ func DownloadFile(c *gin.Context) {
response.AbortNotFound(c, "文件记录未找到")
return
}
if _, ok := err.(*strconv.NumError); ok {
var numErr *strconv.NumError
if errors.As(err, &numErr) {
response.AbortBadRequest(c, shared.ErrInvalidFileID)
return
}
@@ -204,7 +205,7 @@ func DownloadFile(c *gin.Context) {
}
}
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.PathEscape(fileName)))
c.Header("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(fileName))
filesrv.ServeUpload(c, upload)
}
@@ -75,6 +75,7 @@ func setupTestRouter(authUser *model.User) *gin.Engine {
}
func createMultipartRequest(t *testing.T, fieldName, fileName string, fileContent []byte, extraFields map[string]string) (string, *bytes.Buffer) {
t.Helper()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
@@ -1086,6 +1087,7 @@ func TestDeleteReservedUploadType(t *testing.T) {
}
func configureLocalStorageRoot(t *testing.T, dbConn *gorm.DB, tempDir string) {
t.Helper()
var sc model.SystemConfig
if err := dbConn.Where("key = ?", model.ConfigKeyStorageConfig).First(&sc).Error; err != nil {
t.Fatalf("failed to find storage config: %v", err)
+2 -2
View File
@@ -59,8 +59,8 @@ func validateAllowedExtension(ctx context.Context, ext string) error {
return nil
}
allowedExts := strings.Split(strings.ToLower(sc.Value), ",")
for _, allowedExt := range allowedExts {
allowedExts := strings.SplitSeq(strings.ToLower(sc.Value), ",")
for allowedExt := range allowedExts {
if strings.TrimSpace(allowedExt) == ext {
return nil
}
+2
View File
@@ -54,6 +54,8 @@ func ingestWithHashPolicy(ctx context.Context, req Request) (Result, error) {
return Result{}, ErrStorageReadOnly
}
return createDedupRecord(ctx, existing, req)
case PolicyCreate:
// 忽略已存在记录,走下方的新建流程。
}
}
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
@@ -513,6 +513,7 @@ func loadTotalStats(ctx context.Context) (totalStatsSnapshot, error) {
}
func setupMockStorage(t *testing.T, putCount *int) (restore func(), disable func()) {
t.Helper()
return setupMockStorageWithDeleteCount(t, putCount, nil)
}
@@ -69,7 +69,7 @@ func (h *MigrationHandler) ValidatePayload(payload []byte) ([]byte, error) {
return payload, err
}
if active {
return payload, fmt.Errorf("storage migration task is already unresolved")
return payload, errors.New("storage migration task is already unresolved")
}
return normalized, nil
}
@@ -193,18 +193,18 @@ func migrateObjects(
const batchSize = 50
const migrationConcurrency = 10
const sha256HexLength = 64
var migrated int64
var migrated atomic.Int64
var lastFilePath string
for {
if err := ctx.Err(); err != nil {
return atomic.LoadInt64(&migrated), fmt.Errorf("storage migration canceled: %w", err)
return migrated.Load(), fmt.Errorf("storage migration canceled: %w", err)
}
task.AppendLog(ctx, "正在查询待迁移对象批次,当前已完成迁移: %d/%d", atomic.LoadInt64(&migrated), total)
task.AppendLog(ctx, "正在查询待迁移对象批次,当前已完成迁移: %d/%d", migrated.Load(), total)
objects, err := repository.ListDistinctActiveStorageObjects(ctx, lastFilePath, batchSize)
if err != nil {
return atomic.LoadInt64(&migrated), fmt.Errorf("query source objects: %w", err)
return migrated.Load(), fmt.Errorf("query source objects: %w", err)
}
if len(objects) == 0 {
task.AppendLog(ctx, "所有对象迁移完毕")
@@ -223,18 +223,18 @@ func migrateObjects(
if err := migrateSingleObject(ctx, sourceBackend, targetBackend, obj, sha256HexLength); err != nil {
return err
}
atomic.AddInt64(&migrated, 1)
migrated.Add(1)
return nil
})
}
if err := g.Wait(); err != nil {
return atomic.LoadInt64(&migrated), err
return migrated.Load(), err
}
task.AppendLog(ctx, "当前批次迁移完成。迁移进度: %d/%d", atomic.LoadInt64(&migrated), total)
task.AppendLog(ctx, "当前批次迁移完成。迁移进度: %d/%d", migrated.Load(), total)
}
return atomic.LoadInt64(&migrated), nil
return migrated.Load(), nil
}
func migrateSingleObject(
+4 -18
View File
@@ -4,6 +4,7 @@
package util
import (
"slices"
"strings"
"github.com/Rain-kl/Wavelet/internal/apps/upload/shared"
@@ -11,32 +12,17 @@ import (
// IsImageExtension reports whether ext is a common image format.
func IsImageExtension(ext string) bool {
for _, imgExt := range []string{"jpg", "jpeg", "png", "webp", "gif"} {
if ext == imgExt {
return true
}
}
return false
return slices.Contains([]string{"jpg", "jpeg", "png", "webp", "gif"}, ext)
}
// IsArchiveExtension reports whether ext is a common archive format.
func IsArchiveExtension(ext string) bool {
for _, e := range []string{"zip", "rar", "7z", "tar", "gz", "tgz", "bz2", "xz"} {
if ext == e {
return true
}
}
return false
return slices.Contains([]string{"zip", "rar", "7z", "tar", "gz", "tgz", "bz2", "xz"}, ext)
}
// IsDocumentExtension reports whether ext is a common document format.
func IsDocumentExtension(ext string) bool {
for _, e := range []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv", "json", "yaml", "yml", "xml"} {
if ext == e {
return true
}
}
return false
return slices.Contains([]string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv", "json", "yaml", "yml", "xml"}, ext)
}
// NormalizeImageQuality normalizes the requested image quality query parameter.