后端与全仓代码质量清理(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
+2 -1
View File
@@ -6,6 +6,7 @@ package analytics
import (
"context"
"errors"
"fmt"
"time"
@@ -15,7 +16,7 @@ import (
func userAccessLogConn() error {
if db.ChConn == nil {
return fmt.Errorf("clickhouse native connection is not initialized")
return errors.New("clickhouse native connection is not initialized")
}
return nil
}
@@ -50,7 +50,7 @@ func GetDailyTrend(ctx context.Context, days int) ([]DailyTrend, error) {
defer func() { _ = rows.Close() }()
trendMap := make(map[string]uint64, days)
for i := 0; i < days; i++ {
for i := range days {
dateStr := time.Now().AddDate(0, 0, -i).Format("2006-01-02")
trendMap[dateStr] = 0
}
@@ -5,6 +5,7 @@ package analytics
import (
"context"
"errors"
"fmt"
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
@@ -17,7 +18,7 @@ func BatchInsert(ctx context.Context, logs []analyticsmodel.UserAccessLog) error
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.UserAccessLog{}.BatchInsertSQL())
@@ -5,6 +5,7 @@ package analytics
import (
"context"
"errors"
"fmt"
"github.com/Rain-kl/Wavelet/internal/infra/config"
@@ -19,7 +20,7 @@ type ClickHouseOperationalStats = analyticsmodel.ClickHouseOperationalStats
// GetClickHouseOperationalStats returns operational metrics for the configured database.
func GetClickHouseOperationalStats(ctx context.Context) (*ClickHouseOperationalStats, error) {
if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse native connection is not initialized")
return nil, errors.New("clickhouse native connection is not initialized")
}
database := config.Config.ClickHouse.Database
stats := &ClickHouseOperationalStats{Database: database}
@@ -5,6 +5,7 @@ package analytics
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -19,7 +20,7 @@ type NodeAccessLogRegionCount = analyticsmodel.NodeAccessLogRegionCount
func nodeAccessLogConn() (driver.Conn, error) {
if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse connection is not initialized")
return nil, errors.New("clickhouse connection is not initialized")
}
return db.ChConn, nil
}
@@ -50,8 +50,8 @@ func TestBatchInsertNodeAccessLogs_UsesModelBatchSQL(t *testing.T) {
require.Len(t, mockBatch.rows, 1)
assert.Equal(t, "node-a", mockBatch.rows[0][1])
require.Len(t, mockBatch.rows[0], 14)
assert.Equal(t, "", mockBatch.rows[0][7]) // user_agent
assert.Equal(t, "", mockBatch.rows[0][8]) // cache_status
assert.Empty(t, mockBatch.rows[0][7]) // user_agent
assert.Empty(t, mockBatch.rows[0][8]) // cache_status
assert.Equal(t, uint64(2048), mockBatch.rows[0][10]) // bytes_sent
assert.Equal(t, uint64(0), mockBatch.rows[0][11]) // request_length
assert.Equal(t, uint32(0), mockBatch.rows[0][12]) // request_time_ms
@@ -5,6 +5,7 @@ package analytics
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -20,7 +21,7 @@ func BatchInsertNodeAccessLogs(ctx context.Context, logs []analyticsmodel.NodeAc
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeAccessLog{}.BatchInsertSQL())
@@ -5,8 +5,9 @@ package analytics
import (
"context"
"errors"
"fmt"
"sort"
"slices"
"time"
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
@@ -16,7 +17,7 @@ import (
func observabilityConn() (driver.Conn, error) {
if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse connection is not initialized")
return nil, errors.New("clickhouse connection is not initialized")
}
return db.ChConn, nil
}
@@ -367,7 +368,7 @@ func mergeNodeMetricHourlyPreferRollup(rollup, raw []NodeMetricHourly) []NodeMet
}
result := make([]NodeMetricHourly, 0, len(order))
// Keep chronological order of first-seen keys; re-sort by hour for stability.
sort.Slice(order, func(i, j int) bool { return order[i] < order[j] })
slices.Sort(order)
for _, key := range order {
result = append(result, byHour[key])
}
@@ -55,8 +55,8 @@ func TestListNodeMetricHourly_PrefersRollup(t *testing.T) {
rows, err := ListNodeMetricHourly(ctx, NodeObservabilityFilter{Since: since})
require.NoError(t, err)
require.Len(t, rows, 1)
assert.Equal(t, 42.5, rows[0].AverageCPUUsagePercent)
assert.Equal(t, 60.0, rows[0].AverageMemoryUsagePercent)
assert.InDelta(t, 42.5, rows[0].AverageCPUUsagePercent, 1e-9)
assert.InDelta(t, 60.0, rows[0].AverageMemoryUsagePercent, 1e-9)
assert.Equal(t, int64(100), rows[0].NetworkRxBytes)
assert.Equal(t, 2, rows[0].ReportedNodes)
require.Len(t, mock.queries, 1)
@@ -92,10 +92,10 @@ func TestListNodeMetricHourly_MergesRawGapsWithPartialRollup(t *testing.T) {
require.NoError(t, err)
require.Len(t, rows, 2)
assert.Equal(t, rawHour, rows[0].Hour)
assert.Equal(t, 12.0, rows[0].AverageCPUUsagePercent)
assert.InDelta(t, 12.0, rows[0].AverageCPUUsagePercent, 1e-9)
// Overlapping hour prefers rollup (99) over raw (50).
assert.Equal(t, rollupHour, rows[1].Hour)
assert.Equal(t, 99.0, rows[1].AverageCPUUsagePercent)
assert.InDelta(t, 99.0, rows[1].AverageCPUUsagePercent, 1e-9)
require.GreaterOrEqual(t, len(mock.queries), 2)
assert.Contains(t, mock.queries[1], "lagInFrame")
}
@@ -112,9 +112,9 @@ func TestMergeNodeMetricHourlyPreferRollup(t *testing.T) {
)
require.Len(t, merged, 2)
assert.Equal(t, h1, merged[0].Hour)
assert.Equal(t, 10.0, merged[0].AverageCPUUsagePercent)
assert.InDelta(t, 10.0, merged[0].AverageCPUUsagePercent, 1e-9)
assert.Equal(t, h2, merged[1].Hour)
assert.Equal(t, 80.0, merged[1].AverageCPUUsagePercent)
assert.InDelta(t, 80.0, merged[1].AverageCPUUsagePercent, 1e-9)
}
func TestHourlyRollupCoversWindow(t *testing.T) {
@@ -147,7 +147,7 @@ func TestListNodeMetricHourly_FallsBackToRawOnRollupError(t *testing.T) {
rows, err := ListNodeMetricHourly(ctx, NodeObservabilityFilter{})
require.NoError(t, err)
require.Len(t, rows, 1)
assert.Equal(t, 10.0, rows[0].AverageCPUUsagePercent)
assert.InDelta(t, 10.0, rows[0].AverageCPUUsagePercent, 1e-9)
assert.Equal(t, int64(3), rows[0].DiskReadBytes)
require.GreaterOrEqual(t, len(mock.queries), 2)
assert.Contains(t, mock.queries[0], nodeMetricCapacityHourlyTableName())
@@ -5,6 +5,7 @@ package analytics
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -30,7 +31,7 @@ func BatchInsertNodeMetricSnapshots(ctx context.Context, snapshots []analyticsmo
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeMetricSnapshot{}.BatchInsertSQL())
@@ -102,7 +103,7 @@ func BatchInsertNodeEdgeHealth(ctx context.Context, rows []analyticsmodel.NodeEd
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeEdgeHealth{}.BatchInsertSQL())
if err != nil {
@@ -160,7 +161,7 @@ func BatchInsertNodeObsFrps(ctx context.Context, observations []analyticsmodel.N
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrps{}.BatchInsertSQL())
@@ -223,7 +224,7 @@ func BatchInsertNodeObsFrpc(ctx context.Context, observations []analyticsmodel.N
return nil
}
if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized")
return errors.New("clickhouse connection is not initialized")
}
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrpc{}.BatchInsertSQL())
+11 -2
View File
@@ -48,6 +48,7 @@ var (
authSourceListenerOnce sync.Once
authSourceListenerCtx context.Context
authSourceListenerCancel context.CancelFunc
authSourceListenerDone chan struct{}
)
func cloneAuthSources(sources []model.AuthSource) []model.AuthSource {
@@ -116,15 +117,19 @@ func ensureAuthSourceCacheListener() {
func startAuthSourceCacheInvalidationListener() {
authSourceListenerCtx, authSourceListenerCancel = context.WithCancel(context.Background())
authSourceListenerDone = make(chan struct{})
go func() {
pubsub := db.Redis.Subscribe(authSourceListenerCtx, authSourceInvalidationChannel)
listenerCtx := authSourceListenerCtx
defer close(authSourceListenerDone)
pubsub := db.Redis.Subscribe(listenerCtx, authSourceInvalidationChannel)
defer func() {
_ = pubsub.Close()
}()
go func() {
<-authSourceListenerCtx.Done()
<-listenerCtx.Done()
_ = pubsub.Close()
}()
@@ -257,7 +262,11 @@ func InvalidateAuthSourceCache(ctx context.Context) error {
func StopAuthSourceCacheListener() {
if authSourceListenerCancel != nil {
authSourceListenerCancel()
if authSourceListenerDone != nil {
<-authSourceListenerDone
}
authSourceListenerCancel = nil
authSourceListenerDone = nil
}
authSourceListenerOnce = sync.Once{}
}
@@ -1536,7 +1536,7 @@ func (s *userAccessLogGormStore) GetDailyTrend(ctx context.Context, days int) ([
counts[r.Date] = r.Cnt
}
out := make([]analyticsmodel.DailyTrend, 0, days)
for i := 0; i < days; i++ {
for i := range days {
d := start.AddDate(0, 0, i).Format("2006-01-02")
out = append(out, analyticsmodel.DailyTrend{Date: d, Count: counts[d]})
}
+2 -8
View File
@@ -418,14 +418,8 @@ func openFlareAccessLogPaginateBounds(total int, page int, pageSize int) (int, i
if pageSize <= 0 {
return 0, total
}
start := page * pageSize
if start > total {
start = total
}
end := start + pageSize
if end > total {
end = total
}
start := min(page*pageSize, total)
end := min(start+pageSize, total)
return start, end
}
@@ -1,3 +1,6 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package repository
import (
+4 -2
View File
@@ -1,9 +1,11 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package repository
import (
"context"
"errors"
"fmt"
"gorm.io/gorm"
@@ -124,7 +126,7 @@ func ListZoneDomainsByIDs(ctx context.Context, domainIDs []uint) ([]model.ZoneDo
for _, id := range domainIDs {
domain, ok := byID[id]
if !ok {
return nil, fmt.Errorf("one or more zone domains do not exist")
return nil, errors.New("one or more zone domains do not exist")
}
ordered = append(ordered, domain)
}
+11 -2
View File
@@ -90,6 +90,7 @@ var (
systemConfigListenerOnce sync.Once
systemConfigListenerCtx context.Context
systemConfigListenerCancel context.CancelFunc
systemConfigListenerDone chan struct{}
)
func ensureSystemConfigCacheListener() {
@@ -102,15 +103,19 @@ func startSystemConfigCacheInvalidationListener() {
}
systemConfigListenerCtx, systemConfigListenerCancel = context.WithCancel(context.Background())
systemConfigListenerDone = make(chan struct{})
go func() {
pubsub := db.Redis.Subscribe(systemConfigListenerCtx, SystemConfigBroadcastChannel)
listenerCtx := systemConfigListenerCtx
defer close(systemConfigListenerDone)
pubsub := db.Redis.Subscribe(listenerCtx, SystemConfigBroadcastChannel)
defer func() {
_ = pubsub.Close()
}()
go func() {
<-systemConfigListenerCtx.Done()
<-listenerCtx.Done()
_ = pubsub.Close()
}()
@@ -135,7 +140,11 @@ func startSystemConfigCacheInvalidationListener() {
func StopSystemConfigCacheListener() {
if systemConfigListenerCancel != nil {
systemConfigListenerCancel()
if systemConfigListenerDone != nil {
<-systemConfigListenerDone
}
systemConfigListenerCancel = nil
systemConfigListenerDone = nil
}
systemConfigListenerOnce = sync.Once{}
}
+3
View File
@@ -1,3 +1,6 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package repository
import (
+3 -2
View File
@@ -23,6 +23,7 @@ import (
)
func setupTaskExecutionTestEnvironment(t *testing.T) func() {
t.Helper()
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
@@ -323,7 +324,7 @@ func TestAppendTaskExecutionLogNonExistent(t *testing.T) {
// Redis 缓冲不依赖数据库记录是否已经创建。
err := AppendTaskExecutionLog(ctx, "nonexistent_task", "测试日志")
assert.NoError(t, err)
require.NoError(t, err)
err = FlushTaskExecutionLog(ctx, "nonexistent_task")
assert.Error(t, err)
@@ -454,7 +455,7 @@ func TestListTaskExecutionsDefaultPaging(t *testing.T) {
items, total, err := ListTaskExecutions(ctx, model.ListTaskExecutionsRequest{})
require.NoError(t, err)
assert.Equal(t, int64(0), total)
assert.Len(t, items, 0)
assert.Empty(t, items)
}
func TestCleanupTaskExecutionLogs(t *testing.T) {