diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml
index 2596a18c9..345c76904 100644
--- a/.github/workflows/backend-ci.yml
+++ b/.github/workflows/backend-ci.yml
@@ -17,9 +17,10 @@ jobs:
go-version-file: backend/go.mod
check-latest: false
cache: true
+ cache-dependency-path: backend/go.sum
- name: Verify Go version
run: |
- go version | grep -q 'go1.25.7'
+ go version | grep -q 'go1.26.0'
- name: Unit tests
working-directory: backend
run: make test-unit
@@ -36,12 +37,14 @@ jobs:
go-version-file: backend/go.mod
check-latest: false
cache: true
+ cache-dependency-path: backend/go.sum
- name: Verify Go version
run: |
- go version | grep -q 'go1.25.7'
+ go version | grep -q 'go1.26.0'
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
- version: v2.7
+ version: v2.7.2
+ install-mode: goinstall
args: --timeout=5m
working-directory: backend
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 50bb73e0c..5b92aa495 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -115,7 +115,7 @@ jobs:
- name: Verify Go version
run: |
- go version | grep -q 'go1.25.7'
+ go version | grep -q 'go1.26.0'
# Docker setup for GoReleaser
- name: Set up QEMU
diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml
index 05dd1d1af..38da7b162 100644
--- a/.github/workflows/security-scan.yml
+++ b/.github/workflows/security-scan.yml
@@ -22,7 +22,7 @@ jobs:
cache-dependency-path: backend/go.sum
- name: Verify Go version
run: |
- go version | grep -q 'go1.25.7'
+ go version | grep -q 'go1.26.0'
- name: Run govulncheck
working-directory: backend
run: |
diff --git a/DEV_GUIDE.md b/DEV_GUIDE.md
index 541bf1faa..a0e226b14 100644
--- a/DEV_GUIDE.md
+++ b/DEV_GUIDE.md
@@ -53,7 +53,7 @@ npm install -g pnpm
### CI 要求
-- Go 版本必须是 **1.25.7**
+- Go 版本必须是 **1.26.0**
- 前端使用 `pnpm install --frozen-lockfile`,必须提交 `pnpm-lock.yaml`
### 本地测试命令
diff --git a/Dockerfile b/Dockerfile
index c9fcf3017..f91254b6e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,7 +7,7 @@
# =============================================================================
ARG NODE_IMAGE=node:24-alpine
-ARG GOLANG_IMAGE=golang:1.25.7-alpine
+ARG GOLANG_IMAGE=golang:1.26.0-alpine
ARG ALPINE_IMAGE=alpine:3.20
ARG GOPROXY=https://goproxy.cn,direct
ARG GOSUMDB=sum.golang.google.cn
diff --git a/README.md b/README.md
index 36949b0a4..f4433feaf 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-[](https://golang.org/)
+[](https://golang.org/)
[](https://vuejs.org/)
[](https://www.postgresql.org/)
[](https://redis.io/)
@@ -44,7 +44,7 @@ Sub2API is an AI API gateway platform designed to distribute and manage API quot
| Component | Technology |
|-----------|------------|
-| Backend | Go 1.25.7, Gin, Ent |
+| Backend | Go 1.26.0, Gin, Ent |
| Frontend | Vue 3.4+, Vite 5+, TailwindCSS |
| Database | PostgreSQL 15+ |
| Cache/Queue | Redis 7+ |
@@ -298,7 +298,7 @@ Build and run from source code for development or customization.
#### Prerequisites
-- Go 1.21+
+- Go 1.26.0
- Node.js 18+
- PostgreSQL 15+
- Redis 7+
diff --git a/README_CN.md b/README_CN.md
index 1e0d1d62a..69b6dad4c 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,7 +2,7 @@
-[](https://golang.org/)
+[](https://golang.org/)
[](https://vuejs.org/)
[](https://www.postgresql.org/)
[](https://redis.io/)
@@ -44,7 +44,7 @@ Sub2API 是一个 AI API 网关平台,用于分发和管理 AI 产品订阅(
| 组件 | 技术 |
|------|------|
-| 后端 | Go 1.25.7, Gin, Ent |
+| 后端 | Go 1.26.0, Gin, Ent |
| 前端 | Vue 3.4+, Vite 5+, TailwindCSS |
| 数据库 | PostgreSQL 15+ |
| 缓存/队列 | Redis 7+ |
@@ -305,7 +305,7 @@ rm -rf data/ postgres_data/ redis_data/
#### 前置条件
-- Go 1.21+
+- Go 1.26.0
- Node.js 18+
- PostgreSQL 15+
- Redis 7+
diff --git a/backend/.golangci.yml b/backend/.golangci.yml
index 3ec692a84..ac929fa4e 100644
--- a/backend/.golangci.yml
+++ b/backend/.golangci.yml
@@ -75,10 +75,6 @@ linters:
# Default: false
check-escaping-errors: true
staticcheck:
- # https://staticcheck.dev/docs/configuration/options/#dot_import_whitelist
- # Default: ["github.com/mmcloughlin/avo/build", "github.com/mmcloughlin/avo/operand", "github.com/mmcloughlin/avo/reg"]
- dot-import-whitelist:
- - fmt
# https://staticcheck.dev/docs/configuration/options/#initialisms
# Default: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS" ]
@@ -94,6 +90,7 @@ linters:
- all
- -ST1000 # Package comment format
- -ST1003 # Poorly chosen identifier (ApiKey vs APIKey)
+ - -ST1016 # Methods on the same type should have the same receiver name
- -ST1020 # Comment on exported method format
- -ST1021 # Comment on exported type format
- -ST1022 # Comment on exported variable format
@@ -225,7 +222,8 @@ linters:
- SA4005
# A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?.
# https://staticcheck.dev/docs/checks/#SA4006
- - SA4006
+ # Go 1.26 的 new(value) 语法会触发 staticcheck 的误报(SA4006),先临时关闭。
+ - -SA4006
# The variable in the loop condition never changes, are you incrementing the wrong variable?.
# https://staticcheck.dev/docs/checks/#SA4008
- SA4008
@@ -255,7 +253,8 @@ linters:
- SA4016
# Discarding the return values of a function without side effects, making the call pointless.
# https://staticcheck.dev/docs/checks/#SA4017
- - SA4017
+ # Go 1.26 的 new(value) 语法会触发 staticcheck 的误报(SA4017),先临时关闭。
+ - -SA4017
# Self-assignment of variables.
# https://staticcheck.dev/docs/checks/#SA4018
- SA4018
diff --git a/backend/Dockerfile b/backend/Dockerfile
index aeb20fdb6..04db10c8f 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.25.7-alpine
+FROM golang:1.26.0-alpine
WORKDIR /app
diff --git a/backend/go.mod b/backend/go.mod
index 08d54b91a..238035fcf 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -1,6 +1,6 @@
module github.com/Wei-Shaw/sub2api
-go 1.25.7
+go 1.26.0
require (
entgo.io/ent v0.14.5
diff --git a/backend/internal/handler/admin/account_data.go b/backend/internal/handler/admin/account_data.go
index b5d1dd0a4..8f506ef17 100644
--- a/backend/internal/handler/admin/account_data.go
+++ b/backend/internal/handler/admin/account_data.go
@@ -140,8 +140,7 @@ func (h *AccountHandler) ExportData(c *gin.Context) {
}
var expiresAt *int64
if acc.ExpiresAt != nil {
- v := acc.ExpiresAt.Unix()
- expiresAt = &v
+ expiresAt = new(acc.ExpiresAt.Unix())
}
dataAccounts = append(dataAccounts, DataAccount{
Name: acc.Name,
diff --git a/backend/internal/handler/admin/account_handler.go b/backend/internal/handler/admin/account_handler.go
index 85400c6fa..c2fbc5350 100644
--- a/backend/internal/handler/admin/account_handler.go
+++ b/backend/internal/handler/admin/account_handler.go
@@ -312,8 +312,7 @@ func (h *AccountHandler) Create(c *gin.Context) {
})
if err != nil {
// 检查是否为混合渠道错误
- var mixedErr *service.MixedChannelError
- if errors.As(err, &mixedErr) {
+ if mixedErr, ok := errors.AsType[*service.MixedChannelError](err); ok {
// 返回特殊错误码要求确认
c.JSON(409, gin.H{
"error": "mixed_channel_warning",
@@ -376,8 +375,7 @@ func (h *AccountHandler) Update(c *gin.Context) {
})
if err != nil {
// 检查是否为混合渠道错误
- var mixedErr *service.MixedChannelError
- if errors.As(err, &mixedErr) {
+ if mixedErr, ok := errors.AsType[*service.MixedChannelError](err); ok {
// 返回特殊错误码要求确认
c.JSON(409, gin.H{
"error": "mixed_channel_warning",
diff --git a/backend/internal/handler/admin/announcement_handler.go b/backend/internal/handler/admin/announcement_handler.go
index 0b5d0fbcc..db5e227ef 100644
--- a/backend/internal/handler/admin/announcement_handler.go
+++ b/backend/internal/handler/admin/announcement_handler.go
@@ -118,12 +118,10 @@ func (h *AnnouncementHandler) Create(c *gin.Context) {
}
if req.StartsAt != nil && *req.StartsAt > 0 {
- t := time.Unix(*req.StartsAt, 0)
- input.StartsAt = &t
+ input.StartsAt = new(time.Unix(*req.StartsAt, 0))
}
if req.EndsAt != nil && *req.EndsAt > 0 {
- t := time.Unix(*req.EndsAt, 0)
- input.EndsAt = &t
+ input.EndsAt = new(time.Unix(*req.EndsAt, 0))
}
created, err := h.announcementService.Create(c.Request.Context(), input)
@@ -169,8 +167,7 @@ func (h *AnnouncementHandler) Update(c *gin.Context) {
var cleared *time.Time = nil
input.StartsAt = &cleared
} else {
- t := time.Unix(*req.StartsAt, 0)
- ptr := &t
+ ptr := new(time.Unix(*req.StartsAt, 0))
input.StartsAt = &ptr
}
}
@@ -180,8 +177,7 @@ func (h *AnnouncementHandler) Update(c *gin.Context) {
var cleared *time.Time = nil
input.EndsAt = &cleared
} else {
- t := time.Unix(*req.EndsAt, 0)
- ptr := &t
+ ptr := new(time.Unix(*req.EndsAt, 0))
input.EndsAt = &ptr
}
}
diff --git a/backend/internal/handler/admin/dashboard_handler.go b/backend/internal/handler/admin/dashboard_handler.go
index 183651865..f635be93a 100644
--- a/backend/internal/handler/admin/dashboard_handler.go
+++ b/backend/internal/handler/admin/dashboard_handler.go
@@ -227,8 +227,7 @@ func (h *DashboardHandler) GetUsageTrend(c *gin.Context) {
}
if billingTypeStr := c.Query("billing_type"); billingTypeStr != "" {
if v, err := strconv.ParseInt(billingTypeStr, 10, 8); err == nil {
- bt := int8(v)
- billingType = &bt
+ billingType = new(int8(v))
} else {
response.BadRequest(c, "Invalid billing_type")
return
@@ -287,8 +286,7 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
}
if billingTypeStr := c.Query("billing_type"); billingTypeStr != "" {
if v, err := strconv.ParseInt(billingTypeStr, 10, 8); err == nil {
- bt := int8(v)
- billingType = &bt
+ billingType = new(int8(v))
} else {
response.BadRequest(c, "Invalid billing_type")
return
diff --git a/backend/internal/handler/admin/group_handler.go b/backend/internal/handler/admin/group_handler.go
index 7daaf2811..0c3498cf0 100644
--- a/backend/internal/handler/admin/group_handler.go
+++ b/backend/internal/handler/admin/group_handler.go
@@ -96,8 +96,7 @@ func (h *GroupHandler) List(c *gin.Context) {
var isExclusive *bool
if isExclusiveStr != "" {
- val := isExclusiveStr == "true"
- isExclusive = &val
+ isExclusive = new(isExclusiveStr == "true")
}
groups, total, err := h.adminService.ListGroups(c.Request.Context(), page, pageSize, platform, status, search, isExclusive)
diff --git a/backend/internal/handler/admin/ops_alerts_handler.go b/backend/internal/handler/admin/ops_alerts_handler.go
index c9da19c75..2670d45a5 100644
--- a/backend/internal/handler/admin/ops_alerts_handler.go
+++ b/backend/internal/handler/admin/ops_alerts_handler.go
@@ -435,8 +435,7 @@ func (h *OpsHandler) UpdateAlertEventStatus(c *gin.Context) {
var resolvedAt *time.Time
if payload.Status == service.OpsAlertStatusResolved || payload.Status == service.OpsAlertStatusManualResolved {
- now := time.Now().UTC()
- resolvedAt = &now
+ resolvedAt = new(time.Now().UTC())
}
if err := h.opsService.UpdateAlertEventStatus(c.Request.Context(), id, payload.Status, resolvedAt); err != nil {
response.ErrorFrom(c, err)
@@ -479,8 +478,7 @@ func (h *OpsHandler) CreateAlertSilence(c *gin.Context) {
createdBy := (*int64)(nil)
if subject, ok := middleware.GetAuthSubjectFromContext(c); ok {
- uid := subject.UserID
- createdBy = &uid
+ createdBy = new(subject.UserID)
}
silence := &service.OpsAlertSilence{
@@ -531,11 +529,9 @@ func (h *OpsHandler) ListAlertEvents(c *gin.Context) {
vv := strings.ToLower(v)
switch vv {
case "true", "1":
- b := true
- filter.EmailSent = &b
+ filter.EmailSent = new(true)
case "false", "0":
- b := false
- filter.EmailSent = &b
+ filter.EmailSent = new(false)
default:
response.BadRequest(c, "Invalid email_sent")
return
diff --git a/backend/internal/handler/admin/ops_handler.go b/backend/internal/handler/admin/ops_handler.go
index 44accc8f7..0d1546127 100644
--- a/backend/internal/handler/admin/ops_handler.go
+++ b/backend/internal/handler/admin/ops_handler.go
@@ -142,11 +142,9 @@ func (h *OpsHandler) GetErrorLogs(c *gin.Context) {
if v := strings.TrimSpace(c.Query("resolved")); v != "" {
switch strings.ToLower(v) {
case "1", "true", "yes":
- b := true
- filter.Resolved = &b
+ filter.Resolved = new(true)
case "0", "false", "no":
- b := false
- filter.Resolved = &b
+ filter.Resolved = new(false)
default:
response.BadRequest(c, "Invalid resolved")
return
@@ -243,11 +241,9 @@ func (h *OpsHandler) ListRequestErrors(c *gin.Context) {
if v := strings.TrimSpace(c.Query("resolved")); v != "" {
switch strings.ToLower(v) {
case "1", "true", "yes":
- b := true
- filter.Resolved = &b
+ filter.Resolved = new(true)
case "0", "false", "no":
- b := false
- filter.Resolved = &b
+ filter.Resolved = new(false)
default:
response.BadRequest(c, "Invalid resolved")
return
@@ -522,11 +518,9 @@ func (h *OpsHandler) ListUpstreamErrors(c *gin.Context) {
if v := strings.TrimSpace(c.Query("resolved")); v != "" {
switch strings.ToLower(v) {
case "1", "true", "yes":
- b := true
- filter.Resolved = &b
+ filter.Resolved = new(true)
case "0", "false", "no":
- b := false
- filter.Resolved = &b
+ filter.Resolved = new(false)
default:
response.BadRequest(c, "Invalid resolved")
return
@@ -836,8 +830,7 @@ func (h *OpsHandler) UpdateErrorResolution(c *gin.Context) {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
- uid := subject.UserID
- if err := h.opsService.UpdateErrorResolution(c.Request.Context(), id, req.Resolved, &uid, nil); err != nil {
+ if err := h.opsService.UpdateErrorResolution(c.Request.Context(), id, req.Resolved, new(subject.UserID), nil); err != nil {
response.ErrorFrom(c, err)
return
}
diff --git a/backend/internal/handler/admin/promo_handler.go b/backend/internal/handler/admin/promo_handler.go
index 3eafa3801..efb1ee43c 100644
--- a/backend/internal/handler/admin/promo_handler.go
+++ b/backend/internal/handler/admin/promo_handler.go
@@ -107,8 +107,7 @@ func (h *PromoHandler) Create(c *gin.Context) {
}
if req.ExpiresAt != nil {
- t := time.Unix(*req.ExpiresAt, 0)
- input.ExpiresAt = &t
+ input.ExpiresAt = new(time.Unix(*req.ExpiresAt, 0))
}
code, err := h.promoService.Create(c.Request.Context(), input)
@@ -148,8 +147,7 @@ func (h *PromoHandler) Update(c *gin.Context) {
// 0 表示清除过期时间
input.ExpiresAt = nil
} else {
- t := time.Unix(*req.ExpiresAt, 0)
- input.ExpiresAt = &t
+ input.ExpiresAt = new(time.Unix(*req.ExpiresAt, 0))
}
}
diff --git a/backend/internal/handler/admin/usage_cleanup_handler_test.go b/backend/internal/handler/admin/usage_cleanup_handler_test.go
index ed1c7cc22..7f158a01f 100644
--- a/backend/internal/handler/admin/usage_cleanup_handler_test.go
+++ b/backend/internal/handler/admin/usage_cleanup_handler_test.go
@@ -44,8 +44,7 @@ func (s *cleanupRepoStub) CreateTask(ctx context.Context, task *service.UsageCle
task.CreatedAt = time.Now().UTC()
}
task.UpdatedAt = task.CreatedAt
- clone := *task
- s.created = append(s.created, &clone)
+ s.created = append(s.created, new(*task))
return nil
}
diff --git a/backend/internal/handler/admin/usage_handler.go b/backend/internal/handler/admin/usage_handler.go
index 3f3238ddc..83f3910e9 100644
--- a/backend/internal/handler/admin/usage_handler.go
+++ b/backend/internal/handler/admin/usage_handler.go
@@ -117,8 +117,7 @@ func (h *UsageHandler) List(c *gin.Context) {
response.BadRequest(c, "Invalid billing_type")
return
}
- bt := int8(val)
- billingType = &bt
+ billingType = new(int8(val))
}
// Parse date range
@@ -230,8 +229,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
response.BadRequest(c, "Invalid billing_type")
return
}
- bt := int8(val)
- billingType = &bt
+ billingType = new(int8(val))
}
// Parse date range
diff --git a/backend/internal/handler/admin/user_attribute_handler.go b/backend/internal/handler/admin/user_attribute_handler.go
index 2f326279d..005fc0653 100644
--- a/backend/internal/handler/admin/user_attribute_handler.go
+++ b/backend/internal/handler/admin/user_attribute_handler.go
@@ -199,8 +199,7 @@ func (h *UserAttributeHandler) UpdateDefinition(c *gin.Context) {
Enabled: req.Enabled,
}
if req.Type != nil {
- t := service.UserAttributeType(*req.Type)
- input.Type = &t
+ input.Type = new(service.UserAttributeType(*req.Type))
}
def, err := h.attrService.UpdateDefinition(c.Request.Context(), id, input)
diff --git a/backend/internal/handler/auth_linuxdo_oauth.go b/backend/internal/handler/auth_linuxdo_oauth.go
index 0ccf47e4a..000ecb2bb 100644
--- a/backend/internal/handler/auth_linuxdo_oauth.go
+++ b/backend/internal/handler/auth_linuxdo_oauth.go
@@ -180,8 +180,7 @@ func (h *AuthHandler) LinuxDoOAuthCallback(c *gin.Context) {
tokenResp, err := linuxDoExchangeCode(c.Request.Context(), cfg, code, redirectURI, codeVerifier)
if err != nil {
description := ""
- var exchangeErr *linuxDoTokenExchangeError
- if errors.As(err, &exchangeErr) && exchangeErr != nil {
+ if exchangeErr, ok := errors.AsType[*linuxDoTokenExchangeError](err); ok && exchangeErr != nil {
log.Printf(
"[LinuxDo OAuth] token exchange failed: status=%d provider_error=%q provider_description=%q body=%s",
exchangeErr.StatusCode,
diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go
index 2caf6847b..6a0b5040e 100644
--- a/backend/internal/handler/dto/mappers.go
+++ b/backend/internal/handler/dto/mappers.go
@@ -33,15 +33,13 @@ func UserFromService(u *service.User) *User {
if len(u.APIKeys) > 0 {
out.APIKeys = make([]APIKey, 0, len(u.APIKeys))
for i := range u.APIKeys {
- k := u.APIKeys[i]
- out.APIKeys = append(out.APIKeys, *APIKeyFromService(&k))
+ out.APIKeys = append(out.APIKeys, *APIKeyFromService(new(u.APIKeys[i])))
}
}
if len(u.Subscriptions) > 0 {
out.Subscriptions = make([]UserSubscription, 0, len(u.Subscriptions))
for i := range u.Subscriptions {
- s := u.Subscriptions[i]
- out.Subscriptions = append(out.Subscriptions, *UserSubscriptionFromService(&s))
+ out.Subscriptions = append(out.Subscriptions, *UserSubscriptionFromService(new(u.Subscriptions[i])))
}
}
return out
@@ -91,8 +89,7 @@ func GroupFromServiceShallow(g *service.Group) *Group {
if g == nil {
return nil
}
- out := groupFromServiceBase(g)
- return &out
+ return new(groupFromServiceBase(g))
}
func GroupFromService(g *service.Group) *Group {
@@ -120,8 +117,7 @@ func GroupFromServiceAdmin(g *service.Group) *AdminGroup {
if len(g.AccountGroups) > 0 {
out.AccountGroups = make([]AccountGroup, 0, len(g.AccountGroups))
for i := range g.AccountGroups {
- ag := g.AccountGroups[i]
- out.AccountGroups = append(out.AccountGroups, *AccountGroupFromService(&ag))
+ out.AccountGroups = append(out.AccountGroups, *AccountGroupFromService(new(g.AccountGroups[i])))
}
}
return out
@@ -203,13 +199,11 @@ func AccountFromServiceShallow(a *service.Account) *Account {
}
// TLS指纹伪装开关
if a.IsTLSFingerprintEnabled() {
- enabled := true
- out.EnableTLSFingerprint = &enabled
+ out.EnableTLSFingerprint = new(true)
}
// 会话ID伪装开关
if a.IsSessionIDMaskingEnabled() {
- enabled := true
- out.EnableSessionIDMasking = &enabled
+ out.EnableSessionIDMasking = new(true)
}
}
@@ -225,8 +219,7 @@ func AccountFromService(a *service.Account) *Account {
if len(a.AccountGroups) > 0 {
out.AccountGroups = make([]AccountGroup, 0, len(a.AccountGroups))
for i := range a.AccountGroups {
- ag := a.AccountGroups[i]
- out.AccountGroups = append(out.AccountGroups, *AccountGroupFromService(&ag))
+ out.AccountGroups = append(out.AccountGroups, *AccountGroupFromService(new(a.AccountGroups[i])))
}
}
if len(a.Groups) > 0 {
@@ -242,8 +235,7 @@ func timeToUnixSeconds(value *time.Time) *int64 {
if value == nil {
return nil
}
- ts := value.Unix()
- return &ts
+ return new(value.Unix())
}
func AccountGroupFromService(ag *service.AccountGroup) *AccountGroup {
@@ -313,8 +305,7 @@ func RedeemCodeFromService(rc *service.RedeemCode) *RedeemCode {
if rc == nil {
return nil
}
- out := redeemCodeFromServiceBase(rc)
- return &out
+ return new(redeemCodeFromServiceBase(rc))
}
// RedeemCodeFromServiceAdmin converts a service RedeemCode to DTO for admin users.
@@ -412,8 +403,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
if l == nil {
return nil
}
- u := usageLogFromServiceUser(l)
- return &u
+ return new(usageLogFromServiceUser(l))
}
// UsageLogFromServiceAdmin converts a service UsageLog to DTO for admin users.
@@ -476,8 +466,7 @@ func UserSubscriptionFromService(sub *service.UserSubscription) *UserSubscriptio
if sub == nil {
return nil
}
- out := userSubscriptionFromServiceBase(sub)
- return &out
+ return new(userSubscriptionFromServiceBase(sub))
}
// UserSubscriptionFromServiceAdmin converts a service UserSubscription to DTO for admin users.
diff --git a/backend/internal/handler/gateway_handler.go b/backend/internal/handler/gateway_handler.go
index c2b6bf096..e31365d24 100644
--- a/backend/internal/handler/gateway_handler.go
+++ b/backend/internal/handler/gateway_handler.go
@@ -358,8 +358,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
accountReleaseFunc()
}
if err != nil {
- var failoverErr *service.UpstreamFailoverError
- if errors.As(err, &failoverErr) {
+ if failoverErr, ok2 := errors.AsType[*service.UpstreamFailoverError](err); ok2 {
lastFailoverErr = failoverErr
if needForceCacheBilling(hasBoundSession, failoverErr) {
forceCacheBilling = true
@@ -561,8 +560,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
accountReleaseFunc()
}
if err != nil {
- var promptTooLongErr *service.PromptTooLongError
- if errors.As(err, &promptTooLongErr) {
+ if promptTooLongErr, ok2 := errors.AsType[*service.PromptTooLongError](err); ok2 {
log.Printf("Prompt too long from antigravity: group=%d fallback_group_id=%v fallback_used=%v", currentAPIKey.GroupID, fallbackGroupID, fallbackUsed)
if !fallbackUsed && fallbackGroupID != nil && *fallbackGroupID > 0 {
fallbackGroup, err := h.gatewayService.ResolveGroupByID(c.Request.Context(), *fallbackGroupID)
@@ -596,8 +594,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
_ = h.antigravityGatewayService.WriteMappedClaudeError(c, account, promptTooLongErr.StatusCode, promptTooLongErr.RequestID, promptTooLongErr.Body)
return
}
- var failoverErr *service.UpstreamFailoverError
- if errors.As(err, &failoverErr) {
+ if failoverErr, ok2 := errors.AsType[*service.UpstreamFailoverError](err); ok2 {
lastFailoverErr = failoverErr
if needForceCacheBilling(hasBoundSession, failoverErr) {
forceCacheBilling = true
@@ -733,8 +730,7 @@ func cloneAPIKeyWithGroup(apiKey *service.APIKey, group *service.Group) *service
return apiKey
}
cloned := *apiKey
- groupID := group.ID
- cloned.GroupID = &groupID
+ cloned.GroupID = new(group.ID)
cloned.Group = group
return &cloned
}
diff --git a/backend/internal/handler/gemini_v1beta_handler.go b/backend/internal/handler/gemini_v1beta_handler.go
index 3d25505ba..b1fa8c848 100644
--- a/backend/internal/handler/gemini_v1beta_handler.go
+++ b/backend/internal/handler/gemini_v1beta_handler.go
@@ -441,8 +441,7 @@ func (h *GatewayHandler) GeminiV1BetaModels(c *gin.Context) {
accountReleaseFunc()
}
if err != nil {
- var failoverErr *service.UpstreamFailoverError
- if errors.As(err, &failoverErr) {
+ if failoverErr, ok2 := errors.AsType[*service.UpstreamFailoverError](err); ok2 {
failedAccountIDs[account.ID] = struct{}{}
if needForceCacheBilling(hasBoundSession, failoverErr) {
forceCacheBilling = true
diff --git a/backend/internal/handler/openai_gateway_handler.go b/backend/internal/handler/openai_gateway_handler.go
index c08a8b0ec..ebb677502 100644
--- a/backend/internal/handler/openai_gateway_handler.go
+++ b/backend/internal/handler/openai_gateway_handler.go
@@ -287,8 +287,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
accountReleaseFunc()
}
if err != nil {
- var failoverErr *service.UpstreamFailoverError
- if errors.As(err, &failoverErr) {
+ if failoverErr, ok2 := errors.AsType[*service.UpstreamFailoverError](err); ok2 {
failedAccountIDs[account.ID] = struct{}{}
lastFailoverErr = failoverErr
if switchCount >= maxAccountSwitches {
diff --git a/backend/internal/handler/ops_error_logger.go b/backend/internal/handler/ops_error_logger.go
index cb62ceaeb..90c9a1682 100644
--- a/backend/internal/handler/ops_error_logger.go
+++ b/backend/internal/handler/ops_error_logger.go
@@ -378,8 +378,7 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
var accountID *int64
if len(events) > 0 {
if last := events[len(events)-1]; last != nil && last.AccountID > 0 {
- v := last.AccountID
- accountID = &v
+ accountID = new(last.AccountID)
}
}
if accountID == nil {
@@ -404,8 +403,7 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
last := events[len(events)-1]
if last != nil {
if last.UpstreamStatusCode > 0 {
- code := last.UpstreamStatusCode
- upstreamStatusCode = &code
+ upstreamStatusCode = new(last.UpstreamStatusCode)
}
if msg := strings.TrimSpace(last.Message); msg != "" {
upstreamErrorMessage = &msg
@@ -421,13 +419,11 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
switch t := v.(type) {
case int:
if t > 0 {
- code := t
- upstreamStatusCode = &code
+ upstreamStatusCode = new(t)
}
case int64:
if t > 0 {
- code := int(t)
- upstreamStatusCode = &code
+ upstreamStatusCode = new(int(t))
}
}
}
@@ -435,16 +431,14 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
if upstreamErrorMessage == nil {
if v, ok := c.Get(service.OpsUpstreamErrorMessageKey); ok {
if s, ok := v.(string); ok && strings.TrimSpace(s) != "" {
- msg := strings.TrimSpace(s)
- upstreamErrorMessage = &msg
+ upstreamErrorMessage = new(strings.TrimSpace(s))
}
}
}
if upstreamErrorDetail == nil {
if v, ok := c.Get(service.OpsUpstreamErrorDetailKey); ok {
if s, ok := v.(string); ok && strings.TrimSpace(s) != "" {
- detail := strings.TrimSpace(s)
- upstreamErrorDetail = &detail
+ upstreamErrorDetail = new(strings.TrimSpace(s))
}
}
}
@@ -640,13 +634,11 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
switch t := v.(type) {
case int:
if t > 0 {
- code := t
- entry.UpstreamStatusCode = &code
+ entry.UpstreamStatusCode = new(t)
}
case int64:
if t > 0 {
- code := int(t)
- entry.UpstreamStatusCode = &code
+ entry.UpstreamStatusCode = new(int(t))
}
}
}
@@ -671,16 +663,13 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
last := events[len(events)-1]
if last != nil {
if entry.UpstreamStatusCode == nil && last.UpstreamStatusCode > 0 {
- code := last.UpstreamStatusCode
- entry.UpstreamStatusCode = &code
+ entry.UpstreamStatusCode = new(last.UpstreamStatusCode)
}
if entry.UpstreamErrorMessage == nil && strings.TrimSpace(last.Message) != "" {
- msg := strings.TrimSpace(last.Message)
- entry.UpstreamErrorMessage = &msg
+ entry.UpstreamErrorMessage = new(strings.TrimSpace(last.Message))
}
if entry.UpstreamErrorDetail == nil && strings.TrimSpace(last.Detail) != "" {
- detail := strings.TrimSpace(last.Detail)
- entry.UpstreamErrorDetail = &detail
+ entry.UpstreamErrorDetail = new(strings.TrimSpace(last.Detail))
}
}
}
@@ -756,8 +745,7 @@ func extractOpsRetryRequestHeaders(c *gin.Context) *string {
if err != nil {
return nil
}
- s := string(raw)
- return &s
+ return new(string(raw))
}
type parsedOpsError struct {
diff --git a/backend/internal/handler/request_body_limit.go b/backend/internal/handler/request_body_limit.go
index d746673b3..a0c2075a5 100644
--- a/backend/internal/handler/request_body_limit.go
+++ b/backend/internal/handler/request_body_limit.go
@@ -7,8 +7,7 @@ import (
)
func extractMaxBytesError(err error) (*http.MaxBytesError, bool) {
- var maxErr *http.MaxBytesError
- if errors.As(err, &maxErr) {
+ if maxErr, ok := errors.AsType[*http.MaxBytesError](err); ok {
return maxErr, true
}
return nil, false
diff --git a/backend/internal/handler/subscription_handler.go b/backend/internal/handler/subscription_handler.go
index b40df8333..27d994b16 100644
--- a/backend/internal/handler/subscription_handler.go
+++ b/backend/internal/handler/subscription_handler.go
@@ -164,8 +164,7 @@ func (h *SubscriptionHandler) GetSummary(c *gin.Context) {
// Format expiration time
if !sub.ExpiresAt.IsZero() {
- formatted := sub.ExpiresAt.Format("2006-01-02T15:04:05Z07:00")
- item.ExpiresAt = &formatted
+ item.ExpiresAt = new(sub.ExpiresAt.Format("2006-01-02T15:04:05Z07:00"))
}
// Track total usage (use monthly as the most comprehensive)
diff --git a/backend/internal/handler/totp_handler.go b/backend/internal/handler/totp_handler.go
index 5c5eb567a..c1c4e6fe2 100644
--- a/backend/internal/handler/totp_handler.go
+++ b/backend/internal/handler/totp_handler.go
@@ -48,8 +48,7 @@ func (h *TotpHandler) GetStatus(c *gin.Context) {
}
if status.EnabledAt != nil {
- ts := status.EnabledAt.Unix()
- resp.EnabledAt = &ts
+ resp.EnabledAt = new(status.EnabledAt.Unix())
}
response.Success(c, resp)
diff --git a/backend/internal/handler/usage_handler.go b/backend/internal/handler/usage_handler.go
index 129dbfa65..7a57b4737 100644
--- a/backend/internal/handler/usage_handler.go
+++ b/backend/internal/handler/usage_handler.go
@@ -82,8 +82,7 @@ func (h *UsageHandler) List(c *gin.Context) {
response.BadRequest(c, "Invalid billing_type")
return
}
- bt := int8(val)
- billingType = &bt
+ billingType = new(int8(val))
}
// Parse date range
diff --git a/backend/internal/pkg/antigravity/client.go b/backend/internal/pkg/antigravity/client.go
index ac32fae59..fb0374501 100644
--- a/backend/internal/pkg/antigravity/client.go
+++ b/backend/internal/pkg/antigravity/client.go
@@ -174,14 +174,12 @@ func isConnectionError(err error) bool {
}
// 检查超时错误
- var netErr net.Error
- if errors.As(err, &netErr) && netErr.Timeout() {
+ if netErr, ok := errors.AsType[net.Error](err); ok && netErr.Timeout() {
return true
}
// 检查连接错误(DNS 失败、连接拒绝)
- var opErr *net.OpError
- if errors.As(err, &opErr) {
+ if _, ok := errors.AsType[*net.OpError](err); ok {
return true
}
diff --git a/backend/internal/repository/account_repo.go b/backend/internal/repository/account_repo.go
index d28ae0425..4e27f59ae 100644
--- a/backend/internal/repository/account_repo.go
+++ b/backend/internal/repository/account_repo.go
@@ -1370,8 +1370,7 @@ func (r *accountRepository) loadTempUnschedStates(ctx context.Context, accountID
}
var untilPtr *time.Time
if until.Valid {
- tmp := until.Time
- untilPtr = &tmp
+ untilPtr = new(until.Time)
}
if reason.Valid {
out[id] = tempUnschedSnapshot{until: untilPtr, reason: reason.String}
@@ -1494,8 +1493,6 @@ func accountEntityToService(m *dbent.Account) *service.Account {
return nil
}
- rateMultiplier := m.RateMultiplier
-
return &service.Account{
ID: m.ID,
Name: m.Name,
@@ -1507,7 +1504,7 @@ func accountEntityToService(m *dbent.Account) *service.Account {
ProxyID: m.ProxyID,
Concurrency: m.Concurrency,
Priority: m.Priority,
- RateMultiplier: &rateMultiplier,
+ RateMultiplier: new(m.RateMultiplier),
Status: m.Status,
ErrorMessage: derefString(m.ErrorMessage),
LastUsedAt: m.LastUsedAt,
diff --git a/backend/internal/repository/error_translate.go b/backend/internal/repository/error_translate.go
index b8065ffe2..be2a79b0f 100644
--- a/backend/internal/repository/error_translate.go
+++ b/backend/internal/repository/error_translate.go
@@ -83,8 +83,7 @@ func isUniqueConstraintViolation(err error) bool {
// 优先检测 PostgreSQL 特定错误码(最精确)。
// 错误码 23505 对应 unique_violation。
// 参考:https://www.postgresql.org/docs/current/errcodes-appendix.html
- var pgErr *pq.Error
- if errors.As(err, &pgErr) {
+ if pgErr, ok := errors.AsType[*pq.Error](err); ok {
return pgErr.Code == "23505"
}
diff --git a/backend/internal/repository/gateway_cache_integration_test.go b/backend/internal/repository/gateway_cache_integration_test.go
index 2fdaa3d1e..0eebc33f6 100644
--- a/backend/internal/repository/gateway_cache_integration_test.go
+++ b/backend/internal/repository/gateway_cache_integration_test.go
@@ -104,7 +104,6 @@ func (s *GatewayCacheSuite) TestGetSessionAccountID_CorruptedValue() {
require.False(s.T(), errors.Is(err, redis.Nil), "expected parsing error, not redis.Nil")
}
-
func TestGatewayCacheSuite(t *testing.T) {
suite.Run(t, new(GatewayCacheSuite))
}
diff --git a/backend/internal/repository/ops_repo.go b/backend/internal/repository/ops_repo.go
index b04154b76..888df9f02 100644
--- a/backend/internal/repository/ops_repo.go
+++ b/backend/internal/repository/ops_repo.go
@@ -240,40 +240,32 @@ LIMIT $` + itoa(len(args)+1) + ` OFFSET $` + itoa(len(args)+2)
return nil, err
}
if resolvedAt.Valid {
- t := resolvedAt.Time
- item.ResolvedAt = &t
+ item.ResolvedAt = new(resolvedAt.Time)
}
if resolvedBy.Valid {
- v := resolvedBy.Int64
- item.ResolvedByUserID = &v
+ item.ResolvedByUserID = new(resolvedBy.Int64)
}
item.ResolvedByUserName = resolvedByName
if resolvedRetryID.Valid {
- v := resolvedRetryID.Int64
- item.ResolvedRetryID = &v
+ item.ResolvedRetryID = new(resolvedRetryID.Int64)
}
item.StatusCode = int(statusCode.Int64)
if clientIP.Valid {
- s := clientIP.String
- item.ClientIP = &s
+ item.ClientIP = new(clientIP.String)
}
if userID.Valid {
- v := userID.Int64
- item.UserID = &v
+ item.UserID = new(userID.Int64)
}
item.UserEmail = userEmail
if apiKeyID.Valid {
- v := apiKeyID.Int64
- item.APIKeyID = &v
+ item.APIKeyID = new(apiKeyID.Int64)
}
if accountID.Valid {
- v := accountID.Int64
- item.AccountID = &v
+ item.AccountID = new(accountID.Int64)
}
item.AccountName = accountName
if groupID.Valid {
- v := groupID.Int64
- item.GroupID = &v
+ item.GroupID = new(groupID.Int64)
}
item.GroupName = groupName
out = append(out, &item)
@@ -423,64 +415,49 @@ LIMIT 1`
out.StatusCode = int(statusCode.Int64)
if resolvedAt.Valid {
- t := resolvedAt.Time
- out.ResolvedAt = &t
+ out.ResolvedAt = new(resolvedAt.Time)
}
if resolvedBy.Valid {
- v := resolvedBy.Int64
- out.ResolvedByUserID = &v
+ out.ResolvedByUserID = new(resolvedBy.Int64)
}
if resolvedRetryID.Valid {
- v := resolvedRetryID.Int64
- out.ResolvedRetryID = &v
+ out.ResolvedRetryID = new(resolvedRetryID.Int64)
}
if clientIP.Valid {
- s := clientIP.String
- out.ClientIP = &s
+ out.ClientIP = new(clientIP.String)
}
if upstreamStatusCode.Valid && upstreamStatusCode.Int64 > 0 {
- v := int(upstreamStatusCode.Int64)
- out.UpstreamStatusCode = &v
+ out.UpstreamStatusCode = new(int(upstreamStatusCode.Int64))
}
if userID.Valid {
- v := userID.Int64
- out.UserID = &v
+ out.UserID = new(userID.Int64)
}
if apiKeyID.Valid {
- v := apiKeyID.Int64
- out.APIKeyID = &v
+ out.APIKeyID = new(apiKeyID.Int64)
}
if accountID.Valid {
- v := accountID.Int64
- out.AccountID = &v
+ out.AccountID = new(accountID.Int64)
}
if groupID.Valid {
- v := groupID.Int64
- out.GroupID = &v
+ out.GroupID = new(groupID.Int64)
}
if authLatency.Valid {
- v := authLatency.Int64
- out.AuthLatencyMs = &v
+ out.AuthLatencyMs = new(authLatency.Int64)
}
if routingLatency.Valid {
- v := routingLatency.Int64
- out.RoutingLatencyMs = &v
+ out.RoutingLatencyMs = new(routingLatency.Int64)
}
if upstreamLatency.Valid {
- v := upstreamLatency.Int64
- out.UpstreamLatencyMs = &v
+ out.UpstreamLatencyMs = new(upstreamLatency.Int64)
}
if responseLatency.Valid {
- v := responseLatency.Int64
- out.ResponseLatencyMs = &v
+ out.ResponseLatencyMs = new(responseLatency.Int64)
}
if ttft.Valid {
- v := ttft.Int64
- out.TimeToFirstTokenMs = &v
+ out.TimeToFirstTokenMs = new(ttft.Int64)
}
if requestBodyBytes.Valid {
- v := int(requestBodyBytes.Int64)
- out.RequestBodyBytes = &v
+ out.RequestBodyBytes = new(int(requestBodyBytes.Int64))
}
// Normalize request_body to empty string when stored as JSON null.
@@ -669,56 +646,43 @@ LIMIT 1`
}
out.RequestedByUserID = requestedBy.Int64
if pinnedAccountID.Valid {
- v := pinnedAccountID.Int64
- out.PinnedAccountID = &v
+ out.PinnedAccountID = new(pinnedAccountID.Int64)
}
if startedAt.Valid {
- t := startedAt.Time
- out.StartedAt = &t
+ out.StartedAt = new(startedAt.Time)
}
if finishedAt.Valid {
- t := finishedAt.Time
- out.FinishedAt = &t
+ out.FinishedAt = new(finishedAt.Time)
}
if durationMs.Valid {
- v := durationMs.Int64
- out.DurationMs = &v
+ out.DurationMs = new(durationMs.Int64)
}
if success.Valid {
- v := success.Bool
- out.Success = &v
+ out.Success = new(success.Bool)
}
if httpStatusCode.Valid {
- v := int(httpStatusCode.Int64)
- out.HTTPStatusCode = &v
+ out.HTTPStatusCode = new(int(httpStatusCode.Int64))
}
if upstreamRequestID.Valid {
- s := upstreamRequestID.String
- out.UpstreamRequestID = &s
+ out.UpstreamRequestID = new(upstreamRequestID.String)
}
if usedAccountID.Valid {
- v := usedAccountID.Int64
- out.UsedAccountID = &v
+ out.UsedAccountID = new(usedAccountID.Int64)
}
if responsePreview.Valid {
- s := responsePreview.String
- out.ResponsePreview = &s
+ out.ResponsePreview = new(responsePreview.String)
}
if responseTruncated.Valid {
- v := responseTruncated.Bool
- out.ResponseTruncated = &v
+ out.ResponseTruncated = new(responseTruncated.Bool)
}
if resultRequestID.Valid {
- s := resultRequestID.String
- out.ResultRequestID = &s
+ out.ResultRequestID = new(resultRequestID.String)
}
if resultErrorID.Valid {
- v := resultErrorID.Int64
- out.ResultErrorID = &v
+ out.ResultErrorID = new(resultErrorID.Int64)
}
if errorMessage.Valid {
- s := errorMessage.String
- out.ErrorMessage = &s
+ out.ErrorMessage = new(errorMessage.String)
}
return &out, nil
@@ -836,51 +800,42 @@ LIMIT $2`
item.RequestedByUserID = requestedBy.Int64
if pinnedAccountID.Valid {
- v := pinnedAccountID.Int64
- item.PinnedAccountID = &v
+ item.PinnedAccountID = new(pinnedAccountID.Int64)
}
item.PinnedAccountName = pinnedAccountName
if startedAt.Valid {
- t := startedAt.Time
- item.StartedAt = &t
+ item.StartedAt = new(startedAt.Time)
}
if finishedAt.Valid {
- t := finishedAt.Time
- item.FinishedAt = &t
+ item.FinishedAt = new(finishedAt.Time)
}
if durationMs.Valid {
- v := durationMs.Int64
- item.DurationMs = &v
+ item.DurationMs = new(durationMs.Int64)
}
if success.Valid {
- v := success.Bool
- item.Success = &v
+ item.Success = new(success.Bool)
}
if httpStatusCode.Valid {
- v := int(httpStatusCode.Int64)
- item.HTTPStatusCode = &v
+ item.HTTPStatusCode = new(int(httpStatusCode.Int64))
}
if upstreamRequestID.Valid {
item.UpstreamRequestID = &upstreamRequestID.String
}
if usedAccountID.Valid {
- v := usedAccountID.Int64
- item.UsedAccountID = &v
+ item.UsedAccountID = new(usedAccountID.Int64)
}
item.UsedAccountName = usedAccountName
if responsePreview.Valid {
item.ResponsePreview = &responsePreview.String
}
if responseTruncated.Valid {
- v := responseTruncated.Bool
- item.ResponseTruncated = &v
+ item.ResponseTruncated = new(responseTruncated.Bool)
}
if resultRequestID.Valid {
item.ResultRequestID = &resultRequestID.String
}
if resultErrorID.Valid {
- v := resultErrorID.Int64
- item.ResultErrorID = &v
+ item.ResultErrorID = new(resultErrorID.Int64)
}
if errorMessage.Valid {
item.ErrorMessage = &errorMessage.String
diff --git a/backend/internal/repository/ops_repo_alerts.go b/backend/internal/repository/ops_repo_alerts.go
index bd98b7e4c..cd430ed0d 100644
--- a/backend/internal/repository/ops_repo_alerts.go
+++ b/backend/internal/repository/ops_repo_alerts.go
@@ -69,8 +69,7 @@ ORDER BY id DESC`
return nil, err
}
if lastTriggeredAt.Valid {
- v := lastTriggeredAt.Time
- rule.LastTriggeredAt = &v
+ rule.LastTriggeredAt = new(lastTriggeredAt.Time)
}
if len(filtersRaw) > 0 && string(filtersRaw) != "null" {
var decoded map[string]any
@@ -176,8 +175,7 @@ RETURNING
return nil, err
}
if lastTriggeredAt.Valid {
- v := lastTriggeredAt.Time
- out.LastTriggeredAt = &v
+ out.LastTriggeredAt = new(lastTriggeredAt.Time)
}
if len(filtersRaw) > 0 && string(filtersRaw) != "null" {
var decoded map[string]any
@@ -282,8 +280,7 @@ RETURNING
}
if lastTriggeredAt.Valid {
- v := lastTriggeredAt.Time
- out.LastTriggeredAt = &v
+ out.LastTriggeredAt = new(lastTriggeredAt.Time)
}
if len(filtersRaw) > 0 && string(filtersRaw) != "null" {
var decoded map[string]any
@@ -388,16 +385,13 @@ LIMIT ` + limitArg
return nil, err
}
if metricValue.Valid {
- v := metricValue.Float64
- ev.MetricValue = &v
+ ev.MetricValue = new(metricValue.Float64)
}
if thresholdValue.Valid {
- v := thresholdValue.Float64
- ev.ThresholdValue = &v
+ ev.ThresholdValue = new(thresholdValue.Float64)
}
if resolvedAt.Valid {
- v := resolvedAt.Time
- ev.ResolvedAt = &v
+ ev.ResolvedAt = new(resolvedAt.Time)
}
if len(dimensionsRaw) > 0 && string(dimensionsRaw) != "null" {
var decoded map[string]any
@@ -691,8 +685,7 @@ RETURNING id, rule_id, platform, group_id, region, until, COALESCE(reason,''), c
return nil, err
}
if groupID.Valid {
- v := groupID.Int64
- out.GroupID = &v
+ out.GroupID = new(groupID.Int64)
}
if region.Valid {
v := strings.TrimSpace(region.String)
@@ -701,8 +694,7 @@ RETURNING id, rule_id, platform, group_id, region, until, COALESCE(reason,''), c
}
}
if createdBy.Valid {
- v := createdBy.Int64
- out.CreatedBy = &v
+ out.CreatedBy = new(createdBy.Int64)
}
return &out, nil
}
@@ -768,16 +760,13 @@ func scanOpsAlertEvent(row opsAlertEventRow) (*service.OpsAlertEvent, error) {
return nil, err
}
if metricValue.Valid {
- v := metricValue.Float64
- ev.MetricValue = &v
+ ev.MetricValue = new(metricValue.Float64)
}
if thresholdValue.Valid {
- v := thresholdValue.Float64
- ev.ThresholdValue = &v
+ ev.ThresholdValue = new(thresholdValue.Float64)
}
if resolvedAt.Valid {
- v := resolvedAt.Time
- ev.ResolvedAt = &v
+ ev.ResolvedAt = new(resolvedAt.Time)
}
if len(dimensionsRaw) > 0 && string(dimensionsRaw) != "null" {
var decoded map[string]any
diff --git a/backend/internal/repository/ops_repo_dashboard.go b/backend/internal/repository/ops_repo_dashboard.go
index 85791a9a6..0d4e06939 100644
--- a/backend/internal/repository/ops_repo_dashboard.go
+++ b/backend/internal/repository/ops_repo_dashboard.go
@@ -536,35 +536,29 @@ func aggregateHourlyRows(rows []opsHourlyMetricsRow) opsDashboardPartial {
// duration
if p50W > 0 {
- v := int(math.Round(p50Sum / float64(p50W)))
- out.duration.P50 = &v
+ out.duration.P50 = new(int(math.Round(p50Sum / float64(p50W))))
}
if p90W > 0 {
- v := int(math.Round(p90Sum / float64(p90W)))
- out.duration.P90 = &v
+ out.duration.P90 = new(int(math.Round(p90Sum / float64(p90W))))
}
out.duration.P95 = p95Max
out.duration.P99 = p99Max
if avgW > 0 {
- v := int(math.Round(avgSum / float64(avgW)))
- out.duration.Avg = &v
+ out.duration.Avg = new(int(math.Round(avgSum / float64(avgW))))
}
out.duration.Max = maxMax
// ttft
if ttftP50W > 0 {
- v := int(math.Round(ttftP50Sum / float64(ttftP50W)))
- out.ttft.P50 = &v
+ out.ttft.P50 = new(int(math.Round(ttftP50Sum / float64(ttftP50W))))
}
if ttftP90W > 0 {
- v := int(math.Round(ttftP90Sum / float64(ttftP90W)))
- out.ttft.P90 = &v
+ out.ttft.P90 = new(int(math.Round(ttftP90Sum / float64(ttftP90W))))
}
out.ttft.P95 = ttftP95Max
out.ttft.P99 = ttftP99Max
if ttftAvgW > 0 {
- v := int(math.Round(ttftAvgSum / float64(ttftAvgW)))
- out.ttft.Avg = &v
+ out.ttft.Avg = new(int(math.Round(ttftAvgSum / float64(ttftAvgW))))
}
out.ttft.Max = ttftMaxMax
@@ -648,8 +642,7 @@ func combineApproxPercentiles(segments []opsPercentileSegment) service.OpsPercen
if w <= 0 {
return nil
}
- out := int(math.Round(sum / float64(w)))
- return &out
+ return new(int(math.Round(sum / float64(w))))
}
maxInt := func(get func(service.OpsPercentiles) *int) *int {
@@ -660,8 +653,7 @@ func combineApproxPercentiles(segments []opsPercentileSegment) service.OpsPercen
continue
}
if max == nil || *v > *max {
- c := *v
- max = &c
+ max = new(*v)
}
}
return max
@@ -762,8 +754,7 @@ AND duration_ms IS NOT NULL`
duration.P99 = floatToIntPtr(p99)
duration.Avg = floatToIntPtr(avg)
if max.Valid {
- v := int(max.Int64)
- duration.Max = &v
+ duration.Max = new(int(max.Int64))
}
}
@@ -794,8 +785,7 @@ AND first_token_ms IS NOT NULL`
ttft.P99 = floatToIntPtr(p99)
ttft.Avg = floatToIntPtr(avg)
if max.Valid {
- v := int(max.Int64)
- ttft.Max = &v
+ ttft.Max = new(int(max.Int64))
}
}
@@ -995,8 +985,7 @@ func floatToIntPtr(v sql.NullFloat64) *int {
if !v.Valid {
return nil
}
- n := int(math.Round(v.Float64))
- return &n
+ return new(int(math.Round(v.Float64)))
}
func safeDivideFloat64(numerator float64, denominator float64) float64 {
diff --git a/backend/internal/repository/ops_repo_metrics.go b/backend/internal/repository/ops_repo_metrics.go
index f1e57c386..ee73ad102 100644
--- a/backend/internal/repository/ops_repo_metrics.go
+++ b/backend/internal/repository/ops_repo_metrics.go
@@ -227,60 +227,46 @@ LIMIT 1`
}
if cpu.Valid {
- v := cpu.Float64
- out.CPUUsagePercent = &v
+ out.CPUUsagePercent = new(cpu.Float64)
}
if memUsed.Valid {
- v := memUsed.Int64
- out.MemoryUsedMB = &v
+ out.MemoryUsedMB = new(memUsed.Int64)
}
if memTotal.Valid {
- v := memTotal.Int64
- out.MemoryTotalMB = &v
+ out.MemoryTotalMB = new(memTotal.Int64)
}
if memPct.Valid {
- v := memPct.Float64
- out.MemoryUsagePercent = &v
+ out.MemoryUsagePercent = new(memPct.Float64)
}
if dbOK.Valid {
- v := dbOK.Bool
- out.DBOK = &v
+ out.DBOK = new(dbOK.Bool)
}
if redisOK.Valid {
- v := redisOK.Bool
- out.RedisOK = &v
+ out.RedisOK = new(redisOK.Bool)
}
if redisTotal.Valid {
- v := int(redisTotal.Int64)
- out.RedisConnTotal = &v
+ out.RedisConnTotal = new(int(redisTotal.Int64))
}
if redisIdle.Valid {
- v := int(redisIdle.Int64)
- out.RedisConnIdle = &v
+ out.RedisConnIdle = new(int(redisIdle.Int64))
}
if dbActive.Valid {
- v := int(dbActive.Int64)
- out.DBConnActive = &v
+ out.DBConnActive = new(int(dbActive.Int64))
}
if dbIdle.Valid {
- v := int(dbIdle.Int64)
- out.DBConnIdle = &v
+ out.DBConnIdle = new(int(dbIdle.Int64))
}
if dbWaiting.Valid {
- v := int(dbWaiting.Int64)
- out.DBConnWaiting = &v
+ out.DBConnWaiting = new(int(dbWaiting.Int64))
}
if goroutines.Valid {
- v := int(goroutines.Int64)
- out.GoroutineCount = &v
+ out.GoroutineCount = new(int(goroutines.Int64))
}
if queueDepth.Valid {
- v := int(queueDepth.Int64)
- out.ConcurrencyQueueDepth = &v
+ out.ConcurrencyQueueDepth = new(int(queueDepth.Int64))
}
if accountSwitchCount.Valid {
- v := accountSwitchCount.Int64
- out.AccountSwitchCount = &v
+ out.AccountSwitchCount = new(accountSwitchCount.Int64)
}
return &out, nil
@@ -391,28 +377,22 @@ ORDER BY job_name ASC`
}
if lastRun.Valid {
- v := lastRun.Time
- item.LastRunAt = &v
+ item.LastRunAt = new(lastRun.Time)
}
if lastSuccess.Valid {
- v := lastSuccess.Time
- item.LastSuccessAt = &v
+ item.LastSuccessAt = new(lastSuccess.Time)
}
if lastErrorAt.Valid {
- v := lastErrorAt.Time
- item.LastErrorAt = &v
+ item.LastErrorAt = new(lastErrorAt.Time)
}
if lastError.Valid {
- v := lastError.String
- item.LastError = &v
+ item.LastError = new(lastError.String)
}
if lastDuration.Valid {
- v := lastDuration.Int64
- item.LastDurationMs = &v
+ item.LastDurationMs = new(lastDuration.Int64)
}
if lastResult.Valid {
- v := lastResult.String
- item.LastResult = &v
+ item.LastResult = new(lastResult.String)
}
out = append(out, &item)
diff --git a/backend/internal/repository/ops_repo_request_details.go b/backend/internal/repository/ops_repo_request_details.go
index d8d5d111b..5e6ced3cd 100644
--- a/backend/internal/repository/ops_repo_request_details.go
+++ b/backend/internal/repository/ops_repo_request_details.go
@@ -193,15 +193,13 @@ LIMIT $%d OFFSET $%d
if !v.Valid {
return nil
}
- i := int(v.Int64)
- return &i
+ return new(int(v.Int64))
}
toInt64Ptr := func(v sql.NullInt64) *int64 {
if !v.Valid {
return nil
}
- i := v.Int64
- return &i
+ return new(v.Int64)
}
out := make([]*service.OpsRequestDetail, 0, pageSize)
diff --git a/backend/internal/repository/scheduler_outbox_repo.go b/backend/internal/repository/scheduler_outbox_repo.go
index d7bc97dad..edd1e8a6a 100644
--- a/backend/internal/repository/scheduler_outbox_repo.go
+++ b/backend/internal/repository/scheduler_outbox_repo.go
@@ -46,12 +46,10 @@ func (r *schedulerOutboxRepository) ListAfter(ctx context.Context, afterID int64
return nil, err
}
if accountID.Valid {
- v := accountID.Int64
- event.AccountID = &v
+ event.AccountID = new(accountID.Int64)
}
if groupID.Valid {
- v := groupID.Int64
- event.GroupID = &v
+ event.GroupID = new(groupID.Int64)
}
if len(payloadRaw) > 0 {
var payload map[string]any
diff --git a/backend/internal/repository/usage_cleanup_repo.go b/backend/internal/repository/usage_cleanup_repo.go
index 9c0213573..435449f28 100644
--- a/backend/internal/repository/usage_cleanup_repo.go
+++ b/backend/internal/repository/usage_cleanup_repo.go
@@ -96,8 +96,7 @@ func (r *usageCleanupRepository) ListTasks(ctx context.Context, params paginatio
task.ErrorMsg = &errMsg.String
}
if canceledBy.Valid {
- v := canceledBy.Int64
- task.CanceledBy = &v
+ task.CanceledBy = new(canceledBy.Int64)
}
if canceledAt.Valid {
task.CanceledAt = &canceledAt.Time
diff --git a/backend/internal/repository/usage_cleanup_repo_ent_test.go b/backend/internal/repository/usage_cleanup_repo_ent_test.go
index 6c20b2b9a..d5238328a 100644
--- a/backend/internal/repository/usage_cleanup_repo_ent_test.go
+++ b/backend/internal/repository/usage_cleanup_repo_ent_test.go
@@ -209,11 +209,6 @@ func TestUsageCleanupRepositoryEntListInvalidFilters(t *testing.T) {
func TestUsageCleanupTaskFromEntFull(t *testing.T) {
start := time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC)
end := start.Add(24 * time.Hour)
- errMsg := "failed"
- canceledBy := int64(2)
- canceledAt := start.Add(time.Minute)
- startedAt := start.Add(2 * time.Minute)
- finishedAt := start.Add(3 * time.Minute)
filters := service.UsageCleanupFilters{StartTime: start, EndTime: end}
filtersJSON, err := json.Marshal(filters)
require.NoError(t, err)
@@ -224,11 +219,11 @@ func TestUsageCleanupTaskFromEntFull(t *testing.T) {
Filters: filtersJSON,
CreatedBy: 11,
DeletedRows: 7,
- ErrorMessage: &errMsg,
- CanceledBy: &canceledBy,
- CanceledAt: &canceledAt,
- StartedAt: &startedAt,
- FinishedAt: &finishedAt,
+ ErrorMessage: new("failed"),
+ CanceledBy: new(int64(2)),
+ CanceledAt: new(start.Add(time.Minute)),
+ StartedAt: new(start.Add(2 * time.Minute)),
+ FinishedAt: new(start.Add(3 * time.Minute)),
CreatedAt: start,
UpdatedAt: end,
})
diff --git a/backend/internal/repository/usage_cleanup_repo_test.go b/backend/internal/repository/usage_cleanup_repo_test.go
index 0ca30ec7d..d2199e81c 100644
--- a/backend/internal/repository/usage_cleanup_repo_test.go
+++ b/backend/internal/repository/usage_cleanup_repo_test.go
@@ -404,12 +404,11 @@ func TestUsageCleanupRepositoryDeleteUsageLogsBatch(t *testing.T) {
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := start.Add(24 * time.Hour)
userID := int64(3)
- model := " gpt-4 "
filters := service.UsageCleanupFilters{
StartTime: start,
EndTime: end,
UserID: &userID,
- Model: &model,
+ Model: new(" gpt-4 "),
}
mock.ExpectQuery("DELETE FROM usage_logs").
@@ -446,7 +445,6 @@ func TestBuildUsageCleanupWhere(t *testing.T) {
apiKeyID := int64(2)
accountID := int64(3)
groupID := int64(4)
- model := " gpt-4 "
stream := true
billingType := int8(2)
@@ -457,7 +455,7 @@ func TestBuildUsageCleanupWhere(t *testing.T) {
APIKeyID: &apiKeyID,
AccountID: &accountID,
GroupID: &groupID,
- Model: &model,
+ Model: new(" gpt-4 "),
Stream: &stream,
BillingType: &billingType,
})
@@ -469,12 +467,10 @@ func TestBuildUsageCleanupWhere(t *testing.T) {
func TestBuildUsageCleanupWhereModelEmpty(t *testing.T) {
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := start.Add(24 * time.Hour)
- model := " "
-
where, args := buildUsageCleanupWhere(service.UsageCleanupFilters{
StartTime: start,
EndTime: end,
- Model: &model,
+ Model: new(" "),
})
require.Equal(t, "created_at >= $1 AND created_at <= $2", where)
diff --git a/backend/internal/repository/usage_log_repo.go b/backend/internal/repository/usage_log_repo.go
index 2db1764fa..98a26028b 100644
--- a/backend/internal/repository/usage_log_repo.go
+++ b/backend/internal/repository/usage_log_repo.go
@@ -2265,20 +2265,16 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
log.RequestID = requestID.String
}
if groupID.Valid {
- value := groupID.Int64
- log.GroupID = &value
+ log.GroupID = new(groupID.Int64)
}
if subscriptionID.Valid {
- value := subscriptionID.Int64
- log.SubscriptionID = &value
+ log.SubscriptionID = new(subscriptionID.Int64)
}
if durationMs.Valid {
- value := int(durationMs.Int64)
- log.DurationMs = &value
+ log.DurationMs = new(int(durationMs.Int64))
}
if firstTokenMs.Valid {
- value := int(firstTokenMs.Int64)
- log.FirstTokenMs = &value
+ log.FirstTokenMs = new(int(firstTokenMs.Int64))
}
if userAgent.Valid {
log.UserAgent = &userAgent.String
@@ -2368,8 +2364,7 @@ func nullFloat64Ptr(v sql.NullFloat64) *float64 {
if !v.Valid {
return nil
}
- out := v.Float64
- return &out
+ return new(v.Float64)
}
func nullString(v *string) sql.NullString {
diff --git a/backend/internal/repository/user_attribute_repo.go b/backend/internal/repository/user_attribute_repo.go
index 0b616caf7..f47157d7a 100644
--- a/backend/internal/repository/user_attribute_repo.go
+++ b/backend/internal/repository/user_attribute_repo.go
@@ -374,11 +374,9 @@ func getInt(m map[string]any, key string) *int {
case int:
return &n
case int64:
- i := int(n)
- return &i
+ return new(int(n))
case float64:
- i := int(n)
- return &i
+ return new(int(n))
}
}
return nil
diff --git a/backend/internal/server/middleware/api_key_auth_google_test.go b/backend/internal/server/middleware/api_key_auth_google_test.go
index 38b93cb25..fe5f4dd33 100644
--- a/backend/internal/server/middleware/api_key_auth_google_test.go
+++ b/backend/internal/server/middleware/api_key_auth_google_test.go
@@ -180,8 +180,7 @@ func TestApiKeyAuthWithSubscriptionGoogleSetsGroupContext(t *testing.T) {
if key != apiKey.Key {
return nil, service.ErrAPIKeyNotFound
}
- clone := *apiKey
- return &clone, nil
+ return new(*apiKey), nil
},
},
nil,
diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go
index 138d5bcb0..d9c4514d4 100644
--- a/backend/internal/service/account.go
+++ b/backend/internal/service/account.go
@@ -194,8 +194,7 @@ func (a *Account) GetCredentialAsTime(key string) *time.Time {
}
// 尝试 Unix 时间戳(纯数字字符串)
if ts, err := strconv.ParseInt(s, 10, 64); err == nil {
- t := time.Unix(ts, 0)
- return &t
+ return new(time.Unix(ts, 0))
}
return nil
}
diff --git a/backend/internal/service/account_billing_rate_multiplier_test.go b/backend/internal/service/account_billing_rate_multiplier_test.go
index 731cfa7a2..7c245be45 100644
--- a/backend/internal/service/account_billing_rate_multiplier_test.go
+++ b/backend/internal/service/account_billing_rate_multiplier_test.go
@@ -15,13 +15,11 @@ func TestAccount_BillingRateMultiplier_DefaultsToOneWhenNil(t *testing.T) {
}
func TestAccount_BillingRateMultiplier_AllowsZero(t *testing.T) {
- v := 0.0
- a := Account{RateMultiplier: &v}
+ a := Account{RateMultiplier: new(0.0)}
require.Equal(t, 0.0, a.BillingRateMultiplier())
}
func TestAccount_BillingRateMultiplier_NegativeFallsBackToOne(t *testing.T) {
- v := -1.0
- a := Account{RateMultiplier: &v}
+ a := Account{RateMultiplier: new(-1.0)}
require.Equal(t, 1.0, a.BillingRateMultiplier())
}
diff --git a/backend/internal/service/account_usage_service.go b/backend/internal/service/account_usage_service.go
index 304c57811..000be549c 100644
--- a/backend/internal/service/account_usage_service.go
+++ b/backend/internal/service/account_usage_service.go
@@ -250,8 +250,7 @@ func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64) (*U
}
// 3. 构建 UsageInfo(每次都重新计算 RemainingSeconds)
- now := time.Now()
- usage := s.buildUsageInfo(apiResp, &now)
+ usage := s.buildUsageInfo(apiResp, new(time.Now()))
// 4. 添加窗口统计(有独立缓存,1 分钟)
s.addWindowStats(ctx, account, usage)
@@ -331,8 +330,7 @@ func (s *AccountUsageService) getGeminiUsage(ctx context.Context, account *Accou
// getAntigravityUsage 获取 Antigravity 账户额度
func (s *AccountUsageService) getAntigravityUsage(ctx context.Context, account *Account) (*UsageInfo, error) {
if s.antigravityQuotaFetcher == nil || !s.antigravityQuotaFetcher.CanFetch(account) {
- now := time.Now()
- return &UsageInfo{UpdatedAt: &now}, nil
+ return &UsageInfo{UpdatedAt: new(time.Now())}, nil
}
// 1. 检查缓存(10 分钟)
@@ -589,10 +587,9 @@ func buildGeminiUsageProgress(used, limit int64, resetAt time.Time, tokens int64
if remainingSeconds < 0 {
remainingSeconds = 0
}
- resetCopy := resetAt
return &UsageProgress{
Utilization: utilization,
- ResetsAt: &resetCopy,
+ ResetsAt: new(resetAt),
RemainingSeconds: remainingSeconds,
UsedRequests: used,
LimitRequests: limit,
diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go
index 06354e1e0..2a5f8946b 100644
--- a/backend/internal/service/admin_service.go
+++ b/backend/internal/service/admin_service.go
@@ -468,8 +468,7 @@ func (s *adminServiceImpl) UpdateUser(ctx context.Context, id int64, input *Upda
Status: StatusUsed,
UsedBy: &user.ID,
}
- now := time.Now()
- adjustmentRecord.UsedAt = &now
+ adjustmentRecord.UsedAt = new(time.Now())
if err := s.redeemCodeRepo.Create(ctx, adjustmentRecord); err != nil {
log.Printf("failed to create concurrency adjustment redeem code: %v", err)
}
@@ -551,8 +550,7 @@ func (s *adminServiceImpl) UpdateUserBalance(ctx context.Context, userID int64,
UsedBy: &user.ID,
Notes: notes,
}
- now := time.Now()
- adjustmentRecord.UsedAt = &now
+ adjustmentRecord.UsedAt = new(time.Now())
if err := s.redeemCodeRepo.Create(ctx, adjustmentRecord); err != nil {
log.Printf("failed to create balance adjustment redeem code: %v", err)
@@ -1085,8 +1083,7 @@ func (s *adminServiceImpl) CreateAccount(ctx context.Context, input *CreateAccou
Schedulable: true,
}
if input.ExpiresAt != nil && *input.ExpiresAt > 0 {
- expiresAt := time.Unix(*input.ExpiresAt, 0)
- account.ExpiresAt = &expiresAt
+ account.ExpiresAt = new(time.Unix(*input.ExpiresAt, 0))
}
if input.AutoPauseOnExpired != nil {
account.AutoPauseOnExpired = *input.AutoPauseOnExpired
@@ -1164,8 +1161,7 @@ func (s *adminServiceImpl) UpdateAccount(ctx context.Context, id int64, input *U
if *input.ExpiresAt <= 0 {
account.ExpiresAt = nil
} else {
- expiresAt := time.Unix(*input.ExpiresAt, 0)
- account.ExpiresAt = &expiresAt
+ account.ExpiresAt = new(time.Unix(*input.ExpiresAt, 0))
}
}
if input.AutoPauseOnExpired != nil {
@@ -1605,10 +1601,9 @@ func (s *adminServiceImpl) TestProxy(ctx context.Context, id int64) (*ProxyTestR
}, nil
}
- latency := latencyMs
s.saveProxyLatency(ctx, id, &ProxyLatencyInfo{
Success: true,
- LatencyMs: &latency,
+ LatencyMs: new(latencyMs),
Message: "Proxy is accessible",
IPAddress: exitInfo.IP,
Country: exitInfo.Country,
@@ -1643,10 +1638,9 @@ func (s *adminServiceImpl) probeProxyLatency(ctx context.Context, proxy *Proxy)
return
}
- latency := latencyMs
s.saveProxyLatency(ctx, proxy.ID, &ProxyLatencyInfo{
Success: true,
- LatencyMs: &latency,
+ LatencyMs: new(latencyMs),
Message: "Proxy is accessible",
IPAddress: exitInfo.IP,
Country: exitInfo.Country,
diff --git a/backend/internal/service/announcement_service.go b/backend/internal/service/announcement_service.go
index c2588e6ca..341f0004a 100644
--- a/backend/internal/service/announcement_service.go
+++ b/backend/internal/service/announcement_service.go
@@ -249,8 +249,7 @@ func (s *AnnouncementService) ListForUser(ctx context.Context, userID int64, unr
}
var ptr *time.Time
if ok {
- t := readAt
- ptr = &t
+ ptr = new(readAt)
}
out = append(out, UserAnnouncement{
Announcement: a,
@@ -351,8 +350,7 @@ func (s *AnnouncementService) ListUserReadStatus(
readAt, ok := readMap[u.ID]
var ptr *time.Time
if ok {
- t := readAt
- ptr = &t
+ ptr = new(readAt)
}
out = append(out, AnnouncementUserReadStatus{
diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go
index 9e7ed7d5a..7385af999 100644
--- a/backend/internal/service/antigravity_gateway_service.go
+++ b/backend/internal/service/antigravity_gateway_service.go
@@ -105,8 +105,7 @@ func (e *AntigravityAccountSwitchError) Error() string {
// IsAntigravityAccountSwitchError 检查错误是否为账号切换信号
func IsAntigravityAccountSwitchError(err error) (*AntigravityAccountSwitchError, bool) {
- var switchErr *AntigravityAccountSwitchError
- if errors.As(err, &switchErr) {
+ if switchErr, ok := errors.AsType[*AntigravityAccountSwitchError](err); ok {
return switchErr, true
}
return nil, false
@@ -769,8 +768,7 @@ func isAntigravityConnectionError(err error) bool {
}
// 检查超时错误
- var netErr net.Error
- if errors.As(err, &netErr) && netErr.Timeout() {
+ if netErr, ok := errors.AsType[net.Error](err); ok && netErr.Timeout() {
return true
}
@@ -2905,8 +2903,7 @@ func (s *AntigravityGatewayService) handleGeminiStreamingResponse(c *gin.Context
}
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
cw.Fprintf("data: %s\n\n", payload)
@@ -3035,8 +3032,7 @@ func (s *AntigravityGatewayService) handleGeminiStreamToNonStreaming(c *gin.Cont
// 记录首 token 时间
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
last = parsed
@@ -3496,8 +3492,7 @@ func (s *AntigravityGatewayService) handleClaudeStreamToNonStreaming(c *gin.Cont
// 记录首 token 时间
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
last = parsed
@@ -3693,8 +3688,7 @@ func (s *AntigravityGatewayService) handleClaudeStreamingResponse(c *gin.Context
claudeEvents := processor.ProcessLine(strings.TrimRight(ev.line, "\r\n"))
if len(claudeEvents) > 0 {
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
cw.Write(claudeEvents)
}
@@ -4060,8 +4054,7 @@ func (s *AntigravityGatewayService) streamUpstreamResponse(c *gin.Context, resp
// 记录首 token 时间
if firstTokenMs == nil && len(line) > 0 {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
// 尝试从 message_delta 或 message_stop 事件提取 usage
diff --git a/backend/internal/service/antigravity_quota_fetcher.go b/backend/internal/service/antigravity_quota_fetcher.go
index 07eb563d0..63dfc50e3 100644
--- a/backend/internal/service/antigravity_quota_fetcher.go
+++ b/backend/internal/service/antigravity_quota_fetcher.go
@@ -50,9 +50,8 @@ func (f *AntigravityQuotaFetcher) FetchQuota(ctx context.Context, account *Accou
// buildUsageInfo 将 API 响应转换为 UsageInfo
func (f *AntigravityQuotaFetcher) buildUsageInfo(modelsResp *antigravity.FetchAvailableModelsResponse) *UsageInfo {
- now := time.Now()
info := &UsageInfo{
- UpdatedAt: &now,
+ UpdatedAt: new(time.Now()),
AntigravityQuota: make(map[string]*AntigravityModelQuota),
}
diff --git a/backend/internal/service/api_key_service.go b/backend/internal/service/api_key_service.go
index cb1dd60a0..4a339dda3 100644
--- a/backend/internal/service/api_key_service.go
+++ b/backend/internal/service/api_key_service.go
@@ -315,8 +315,7 @@ func (s *APIKeyService) Create(ctx context.Context, userID int64, req CreateAPIK
// Set expiration time if specified
if req.ExpiresInDays != nil && *req.ExpiresInDays > 0 {
- expiresAt := time.Now().AddDate(0, 0, *req.ExpiresInDays)
- apiKey.ExpiresAt = &expiresAt
+ apiKey.ExpiresAt = new(time.Now().AddDate(0, 0, *req.ExpiresInDays))
}
if err := s.apiKeyRepo.Create(ctx, apiKey); err != nil {
diff --git a/backend/internal/service/crs_sync_service.go b/backend/internal/service/crs_sync_service.go
index 040b2357b..68e0f2386 100644
--- a/backend/internal/service/crs_sync_service.go
+++ b/backend/internal/service/crs_sync_service.go
@@ -1055,8 +1055,7 @@ func (s *CRSSyncService) mapOrCreateProxy(ctx context.Context, enabled bool, cac
p.Port == port &&
p.Username == username &&
p.Password == password {
- id := p.ID
- return &id, nil
+ return new(p.ID), nil
}
}
@@ -1075,8 +1074,7 @@ func (s *CRSSyncService) mapOrCreateProxy(ctx context.Context, enabled bool, cac
}
*cached = append(*cached, *proxy)
- id := proxy.ID
- return &id, nil
+ return new(proxy.ID), nil
}
func defaultProxyName(base, protocol, host string, port int) string {
diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go
index 56af4610b..ea9469832 100644
--- a/backend/internal/service/gateway_service.go
+++ b/backend/internal/service/gateway_service.go
@@ -4356,8 +4356,7 @@ func (s *GatewayService) handleStreamingResponse(ctx context.Context, resp *http
}
if data != "" {
if firstTokenMs == nil && data != "[DONE]" {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
s.parseSSEUsage(data, usage)
}
@@ -4587,12 +4586,10 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
}
// 创建使用日志
- durationMs := int(result.Duration.Milliseconds())
var imageSize *string
if result.ImageSize != "" {
imageSize = &result.ImageSize
}
- accountRateMultiplier := account.BillingRateMultiplier()
usageLog := &UsageLog{
UserID: user.ID,
APIKeyID: apiKey.ID,
@@ -4610,10 +4607,10 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
TotalCost: cost.TotalCost,
ActualCost: cost.ActualCost,
RateMultiplier: multiplier,
- AccountRateMultiplier: &accountRateMultiplier,
+ AccountRateMultiplier: new(account.BillingRateMultiplier()),
BillingType: billingType,
Stream: result.Stream,
- DurationMs: &durationMs,
+ DurationMs: new(int(result.Duration.Milliseconds())),
FirstTokenMs: result.FirstTokenMs,
ImageCount: result.ImageCount,
ImageSize: imageSize,
@@ -4768,12 +4765,10 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
}
// 创建使用日志
- durationMs := int(result.Duration.Milliseconds())
var imageSize *string
if result.ImageSize != "" {
imageSize = &result.ImageSize
}
- accountRateMultiplier := account.BillingRateMultiplier()
usageLog := &UsageLog{
UserID: user.ID,
APIKeyID: apiKey.ID,
@@ -4791,10 +4786,10 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
TotalCost: cost.TotalCost,
ActualCost: cost.ActualCost,
RateMultiplier: multiplier,
- AccountRateMultiplier: &accountRateMultiplier,
+ AccountRateMultiplier: new(account.BillingRateMultiplier()),
BillingType: billingType,
Stream: result.Stream,
- DurationMs: &durationMs,
+ DurationMs: new(int(result.Duration.Milliseconds())),
FirstTokenMs: result.FirstTokenMs,
ImageCount: result.ImageCount,
ImageSize: imageSize,
diff --git a/backend/internal/service/gemini_messages_compat_service.go b/backend/internal/service/gemini_messages_compat_service.go
index f3abd1dc4..fd6d25beb 100644
--- a/backend/internal/service/gemini_messages_compat_service.go
+++ b/backend/internal/service/gemini_messages_compat_service.go
@@ -1938,8 +1938,7 @@ func (s *GeminiMessagesCompatService) handleStreamingResponse(c *gin.Context, re
}
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
writeSSE(c.Writer, "content_block_delta", map[string]any{
"type": "content_block_delta",
@@ -2480,8 +2479,7 @@ func (s *GeminiMessagesCompatService) handleNativeStreamingResponse(c *gin.Conte
}
if firstTokenMs == nil {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
if isOAuth {
@@ -2768,8 +2766,7 @@ func ParseGeminiRateLimitResetTime(body []byte) *int64 {
if dur, err := time.ParseDuration(v); err == nil {
// Use ceil to avoid undercounting fractional seconds (e.g. 10.1s should not become 10s),
// which can affect scheduling decisions around thresholds (like 10s).
- ts := time.Now().Unix() + int64(math.Ceil(dur.Seconds()))
- return &ts
+ return new(time.Now().Unix() + int64(math.Ceil(dur.Seconds())))
}
}
}
@@ -2782,8 +2779,7 @@ func ParseGeminiRateLimitResetTime(body []byte) *int64 {
matches := retryInRegex.FindStringSubmatch(string(body))
if len(matches) == 2 {
if dur, err := time.ParseDuration(matches[1] + "s"); err == nil {
- ts := time.Now().Unix() + int64(math.Ceil(dur.Seconds()))
- return &ts
+ return new(time.Now().Unix() + int64(math.Ceil(dur.Seconds())))
}
}
@@ -2800,8 +2796,7 @@ func looksLikeGeminiDailyQuota(message string) bool {
func nextGeminiDailyResetUnix() *int64 {
reset := geminiDailyResetTime(time.Now())
- ts := reset.Unix()
- return &ts
+ return new(reset.Unix())
}
func ensureGeminiFunctionCallThoughtSignatures(body []byte) []byte {
diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go
index 6c4fe256c..dbe6bd9be 100644
--- a/backend/internal/service/openai_gateway_service.go
+++ b/backend/internal/service/openai_gateway_service.go
@@ -1379,8 +1379,7 @@ func (s *OpenAIGatewayService) handleStreamingResponse(ctx context.Context, resp
// Record first token time
if firstTokenMs == nil && data != "" && data != "[DONE]" {
- ms := int(time.Since(startTime).Milliseconds())
- firstTokenMs = &ms
+ firstTokenMs = new(int(time.Since(startTime).Milliseconds()))
}
s.parseSSEUsage(data, usage)
} else {
@@ -1756,8 +1755,6 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
}
// Create usage log
- durationMs := int(result.Duration.Milliseconds())
- accountRateMultiplier := account.BillingRateMultiplier()
usageLog := &UsageLog{
UserID: user.ID,
APIKeyID: apiKey.ID,
@@ -1776,10 +1773,10 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
TotalCost: cost.TotalCost,
ActualCost: cost.ActualCost,
RateMultiplier: multiplier,
- AccountRateMultiplier: &accountRateMultiplier,
+ AccountRateMultiplier: new(account.BillingRateMultiplier()),
BillingType: billingType,
Stream: result.Stream,
- DurationMs: &durationMs,
+ DurationMs: new(int(result.Duration.Milliseconds())),
FirstTokenMs: result.FirstTokenMs,
CreatedAt: time.Now(),
}
diff --git a/backend/internal/service/openai_gateway_service_test.go b/backend/internal/service/openai_gateway_service_test.go
index ae69a9867..574faff56 100644
--- a/backend/internal/service/openai_gateway_service_test.go
+++ b/backend/internal/service/openai_gateway_service_test.go
@@ -206,9 +206,6 @@ func (c *stubGatewayCache) DeleteSessionAccountID(ctx context.Context, groupID i
func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulable(t *testing.T) {
now := time.Now()
- resetAt := now.Add(10 * time.Minute)
- groupID := int64(1)
-
rateLimited := Account{
ID: 1,
Platform: PlatformOpenAI,
@@ -217,7 +214,7 @@ func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulable(t *testing.T)
Schedulable: true,
Concurrency: 1,
Priority: 0,
- RateLimitResetAt: &resetAt,
+ RateLimitResetAt: new(now.Add(10 * time.Minute)),
}
available := Account{
ID: 2,
@@ -234,7 +231,7 @@ func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulable(t *testing.T)
concurrencyService: NewConcurrencyService(stubConcurrencyCache{}),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-5.2", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-5.2", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -251,9 +248,6 @@ func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulable(t *testing.T)
func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulableWhenNoConcurrencyService(t *testing.T) {
now := time.Now()
- resetAt := now.Add(10 * time.Minute)
- groupID := int64(1)
-
rateLimited := Account{
ID: 1,
Platform: PlatformOpenAI,
@@ -262,7 +256,7 @@ func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulableWhenNoConcurre
Schedulable: true,
Concurrency: 1,
Priority: 0,
- RateLimitResetAt: &resetAt,
+ RateLimitResetAt: new(now.Add(10 * time.Minute)),
}
available := Account{
ID: 2,
@@ -279,7 +273,7 @@ func TestOpenAISelectAccountWithLoadAwareness_FiltersUnschedulableWhenNoConcurre
// concurrencyService is nil, forcing the non-load-batch selection path.
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-5.2", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-5.2", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -328,7 +322,6 @@ func TestOpenAISelectAccountForModelWithExclusions_StickyUnschedulableClearsSess
func TestOpenAISelectAccountWithLoadAwareness_StickyUnschedulableClearsSession(t *testing.T) {
sessionHash := "session-2"
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusDisabled, Schedulable: true, Concurrency: 1},
@@ -345,7 +338,7 @@ func TestOpenAISelectAccountWithLoadAwareness_StickyUnschedulableClearsSession(t
concurrencyService: NewConcurrencyService(stubConcurrencyCache{}),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, sessionHash, "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), sessionHash, "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -395,7 +388,6 @@ func TestOpenAISelectAccountForModelWithExclusions_NoModelSupport(t *testing.T)
}
func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorFallback(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 2},
@@ -413,7 +405,7 @@ func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorFallback(t *testing.
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "fallback", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "fallback", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -432,7 +424,6 @@ func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorFallback(t *testing.
}
func TestOpenAISelectAccountWithLoadAwareness_NoSlotFallbackWait(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -452,7 +443,7 @@ func TestOpenAISelectAccountWithLoadAwareness_NoSlotFallbackWait(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -492,7 +483,6 @@ func TestOpenAISelectAccountForModelWithExclusions_SetsStickyBinding(t *testing.
func TestOpenAISelectAccountWithLoadAwareness_StickyWaitPlan(t *testing.T) {
sessionHash := "sticky-wait"
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -512,7 +502,7 @@ func TestOpenAISelectAccountWithLoadAwareness_StickyWaitPlan(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, sessionHash, "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), sessionHash, "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -525,7 +515,6 @@ func TestOpenAISelectAccountWithLoadAwareness_StickyWaitPlan(t *testing.T) {
}
func TestOpenAISelectAccountWithLoadAwareness_PrefersLowerLoad(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -546,7 +535,7 @@ func TestOpenAISelectAccountWithLoadAwareness_PrefersLowerLoad(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "load", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "load", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -633,11 +622,9 @@ func TestOpenAISelectAccountForModelWithExclusions_NoAccounts(t *testing.T) {
}
func TestOpenAISelectAccountWithLoadAwareness_NoCandidates(t *testing.T) {
- groupID := int64(1)
- resetAt := time.Now().Add(1 * time.Hour)
repo := stubOpenAIAccountRepo{
accounts: []Account{
- {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1, RateLimitResetAt: &resetAt},
+ {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1, RateLimitResetAt: new(time.Now().Add(1 * time.Hour))},
},
}
cache := &stubGatewayCache{}
@@ -649,7 +636,7 @@ func TestOpenAISelectAccountWithLoadAwareness_NoCandidates(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err == nil {
t.Fatalf("expected error for no candidates")
}
@@ -659,7 +646,6 @@ func TestOpenAISelectAccountWithLoadAwareness_NoCandidates(t *testing.T) {
}
func TestOpenAISelectAccountWithLoadAwareness_AllFullWaitPlan(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -678,7 +664,7 @@ func TestOpenAISelectAccountWithLoadAwareness_AllFullWaitPlan(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -688,7 +674,6 @@ func TestOpenAISelectAccountWithLoadAwareness_AllFullWaitPlan(t *testing.T) {
}
func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorNoAcquire(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -706,7 +691,7 @@ func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorNoAcquire(t *testing
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -716,7 +701,6 @@ func TestOpenAISelectAccountWithLoadAwareness_LoadBatchErrorNoAcquire(t *testing
}
func TestOpenAISelectAccountWithLoadAwareness_MissingLoadInfo(t *testing.T) {
- groupID := int64(1)
repo := stubOpenAIAccountRepo{
accounts: []Account{
{ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
@@ -737,7 +721,7 @@ func TestOpenAISelectAccountWithLoadAwareness_MissingLoadInfo(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
@@ -747,12 +731,10 @@ func TestOpenAISelectAccountWithLoadAwareness_MissingLoadInfo(t *testing.T) {
}
func TestOpenAISelectAccountForModelWithExclusions_LeastRecentlyUsed(t *testing.T) {
- oldTime := time.Now().Add(-2 * time.Hour)
- newTime := time.Now().Add(-1 * time.Hour)
repo := stubOpenAIAccountRepo{
accounts: []Account{
- {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Priority: 1, LastUsedAt: &newTime},
- {ID: 2, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Priority: 1, LastUsedAt: &oldTime},
+ {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Priority: 1, LastUsedAt: new(time.Now().Add(-1 * time.Hour))},
+ {ID: 2, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Priority: 1, LastUsedAt: new(time.Now().Add(-2 * time.Hour))},
},
}
cache := &stubGatewayCache{}
@@ -772,11 +754,9 @@ func TestOpenAISelectAccountForModelWithExclusions_LeastRecentlyUsed(t *testing.
}
func TestOpenAISelectAccountWithLoadAwareness_PreferNeverUsed(t *testing.T) {
- groupID := int64(1)
- lastUsed := time.Now().Add(-1 * time.Hour)
repo := stubOpenAIAccountRepo{
accounts: []Account{
- {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1, LastUsedAt: &lastUsed},
+ {ID: 1, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1, LastUsedAt: new(time.Now().Add(-1 * time.Hour))},
{ID: 2, Platform: PlatformOpenAI, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 1},
},
}
@@ -794,7 +774,7 @@ func TestOpenAISelectAccountWithLoadAwareness_PreferNeverUsed(t *testing.T) {
concurrencyService: NewConcurrencyService(concurrencyCache),
}
- selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), &groupID, "", "gpt-4", nil)
+ selection, err := svc.SelectAccountWithLoadAwareness(context.Background(), new(int64(1)), "", "gpt-4", nil)
if err != nil {
t.Fatalf("SelectAccountWithLoadAwareness error: %v", err)
}
diff --git a/backend/internal/service/ops_account_availability.go b/backend/internal/service/ops_account_availability.go
index da66ec4dd..2559c84e3 100644
--- a/backend/internal/service/ops_account_availability.go
+++ b/backend/internal/service/ops_account_availability.go
@@ -39,8 +39,6 @@ func (s *OpsService) GetAccountAvailabilityStats(ctx context.Context, platformFi
}
now := time.Now()
- collectedAt := now
-
platform := make(map[string]*PlatformAvailability)
group := make(map[int64]*GroupAvailability)
account := make(map[int64]*AccountAvailability)
@@ -154,7 +152,7 @@ func (s *OpsService) GetAccountAvailabilityStats(ctx context.Context, platformFi
account[acc.ID] = item
}
- return platform, group, account, &collectedAt, nil
+ return platform, group, account, new(now), nil
}
type OpsAccountAvailability struct {
diff --git a/backend/internal/service/ops_aggregation_service.go b/backend/internal/service/ops_aggregation_service.go
index 972462ec8..2736f9845 100644
--- a/backend/internal/service/ops_aggregation_service.go
+++ b/backend/internal/service/ops_aggregation_service.go
@@ -219,30 +219,26 @@ func (s *OpsAggregationService) aggregateHourly() {
dur := durationMs
if aggErr != nil {
- msg := truncateString(aggErr.Error(), 2048)
- errAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), 2*time.Second)
defer hbCancel()
_ = s.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsAggHourlyJobName,
LastRunAt: &runAt,
- LastErrorAt: &errAt,
- LastError: &msg,
+ LastErrorAt: new(finishedAt),
+ LastError: new(truncateString(aggErr.Error(), 2048)),
LastDurationMs: &dur,
})
return
}
- successAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), 2*time.Second)
defer hbCancel()
- result := truncateString(fmt.Sprintf("window=%s..%s", start.Format(time.RFC3339), end.Format(time.RFC3339)), 2048)
_ = s.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsAggHourlyJobName,
LastRunAt: &runAt,
- LastSuccessAt: &successAt,
+ LastSuccessAt: new(finishedAt),
LastDurationMs: &dur,
- LastResult: &result,
+ LastResult: new(truncateString(fmt.Sprintf("window=%s..%s", start.Format(time.RFC3339), end.Format(time.RFC3339)), 2048)),
})
}
@@ -317,30 +313,26 @@ func (s *OpsAggregationService) aggregateDaily() {
dur := durationMs
if aggErr != nil {
- msg := truncateString(aggErr.Error(), 2048)
- errAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), 2*time.Second)
defer hbCancel()
_ = s.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsAggDailyJobName,
LastRunAt: &runAt,
- LastErrorAt: &errAt,
- LastError: &msg,
+ LastErrorAt: new(finishedAt),
+ LastError: new(truncateString(aggErr.Error(), 2048)),
LastDurationMs: &dur,
})
return
}
- successAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), 2*time.Second)
defer hbCancel()
- result := truncateString(fmt.Sprintf("window=%s..%s", start.Format(time.RFC3339), end.Format(time.RFC3339)), 2048)
_ = s.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsAggDailyJobName,
LastRunAt: &runAt,
- LastSuccessAt: &successAt,
+ LastSuccessAt: new(finishedAt),
LastDurationMs: &dur,
- LastResult: &result,
+ LastResult: new(truncateString(fmt.Sprintf("window=%s..%s", start.Format(time.RFC3339), end.Format(time.RFC3339)), 2048)),
})
}
diff --git a/backend/internal/service/ops_alert_evaluator_service.go b/backend/internal/service/ops_alert_evaluator_service.go
index 7c62e2479..b5f31e586 100644
--- a/backend/internal/service/ops_alert_evaluator_service.go
+++ b/backend/internal/service/ops_alert_evaluator_service.go
@@ -298,8 +298,7 @@ func (s *OpsAlertEvaluatorService) evaluateOnce(interval time.Duration) {
// Not breached: resolve active event if present.
if activeEvent != nil {
- resolvedAt := now
- if err := s.opsRepo.UpdateAlertEventStatus(ctx, activeEvent.ID, OpsAlertStatusResolved, &resolvedAt); err != nil {
+ if err := s.opsRepo.UpdateAlertEventStatus(ctx, activeEvent.ID, OpsAlertStatusResolved, new(now)); err != nil {
log.Printf("[OpsAlertEvaluator] resolve event failed (event=%d): %v", activeEvent.ID, err)
} else {
eventsResolved++
@@ -398,18 +397,15 @@ func parseOpsAlertRuleScope(filters map[string]any) (platform string, groupID *i
switch t := v.(type) {
case float64:
if t > 0 {
- id := int64(t)
- groupID = &id
+ groupID = new(int64(t))
}
case int64:
if t > 0 {
- id := t
- groupID = &id
+ groupID = new(t)
}
case int:
if t > 0 {
- id := int64(t)
- groupID = &id
+ groupID = new(int64(t))
}
case string:
n, err := strconv.ParseInt(strings.TrimSpace(t), 10, 64)
@@ -826,8 +822,6 @@ func (s *OpsAlertEvaluatorService) recordHeartbeatSuccess(runAt time.Time, durat
if s == nil || s.opsRepo == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
msg := strings.TrimSpace(result)
@@ -838,8 +832,8 @@ func (s *OpsAlertEvaluatorService) recordHeartbeatSuccess(runAt time.Time, durat
_ = s.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsAlertEvaluatorJobName,
LastRunAt: &runAt,
- LastSuccessAt: &now,
- LastDurationMs: &durMs,
+ LastSuccessAt: new(time.Now().UTC()),
+ LastDurationMs: new(duration.Milliseconds()),
LastResult: &msg,
})
}
@@ -848,17 +842,14 @@ func (s *OpsAlertEvaluatorService) recordHeartbeatError(runAt time.Time, duratio
if s == nil || s.opsRepo == nil || err == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
- msg := truncateString(err.Error(), 2048)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsAlertEvaluatorJobName,
LastRunAt: &runAt,
- LastErrorAt: &now,
- LastError: &msg,
- LastDurationMs: &durMs,
+ LastErrorAt: new(time.Now().UTC()),
+ LastError: new(truncateString(err.Error(), 2048)),
+ LastDurationMs: new(duration.Milliseconds()),
})
}
diff --git a/backend/internal/service/ops_cleanup_service.go b/backend/internal/service/ops_cleanup_service.go
index 1ade7176d..768405ec6 100644
--- a/backend/internal/service/ops_cleanup_service.go
+++ b/backend/internal/service/ops_cleanup_service.go
@@ -334,17 +334,14 @@ func (s *OpsCleanupService) recordHeartbeatSuccess(runAt time.Time, duration tim
if s == nil || s.opsRepo == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
- result := truncateString(counts.String(), 2048)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsCleanupJobName,
LastRunAt: &runAt,
- LastSuccessAt: &now,
- LastDurationMs: &durMs,
- LastResult: &result,
+ LastSuccessAt: new(time.Now().UTC()),
+ LastDurationMs: new(duration.Milliseconds()),
+ LastResult: new(truncateString(counts.String(), 2048)),
})
}
@@ -352,16 +349,13 @@ func (s *OpsCleanupService) recordHeartbeatError(runAt time.Time, duration time.
if s == nil || s.opsRepo == nil || err == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
- msg := truncateString(err.Error(), 2048)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsCleanupJobName,
LastRunAt: &runAt,
- LastErrorAt: &now,
- LastError: &msg,
- LastDurationMs: &durMs,
+ LastErrorAt: new(time.Now().UTC()),
+ LastError: new(truncateString(err.Error(), 2048)),
+ LastDurationMs: new(duration.Milliseconds()),
})
}
diff --git a/backend/internal/service/ops_concurrency.go b/backend/internal/service/ops_concurrency.go
index f6541d088..99788c671 100644
--- a/backend/internal/service/ops_concurrency.go
+++ b/backend/internal/service/ops_concurrency.go
@@ -116,7 +116,6 @@ func (s *OpsService) GetConcurrencyStats(
return nil, nil, nil, nil, err
}
- collectedAt := time.Now()
loadMap := s.getAccountsLoadMapBestEffort(ctx, accounts)
platform := make(map[string]*PlatformConcurrencyInfo)
@@ -253,7 +252,7 @@ func (s *OpsService) GetConcurrencyStats(
}
}
- return platform, group, account, &collectedAt, nil
+ return platform, group, account, new(time.Now()), nil
}
// listAllActiveUsersForOps returns all active users with their concurrency settings.
@@ -355,7 +354,6 @@ func (s *OpsService) GetUserConcurrencyStats(ctx context.Context) (map[int64]*Us
return nil, nil, err
}
- collectedAt := time.Now()
loadMap := s.getUsersLoadMapBestEffort(ctx, users)
result := make(map[int64]*UserConcurrencyInfo)
@@ -392,5 +390,5 @@ func (s *OpsService) GetUserConcurrencyStats(ctx context.Context) (map[int64]*Us
result[u.ID] = info
}
- return result, &collectedAt, nil
+ return result, new(time.Now()), nil
}
diff --git a/backend/internal/service/ops_metrics_collector.go b/backend/internal/service/ops_metrics_collector.go
index 30adaae06..db92d9d6b 100644
--- a/backend/internal/service/ops_metrics_collector.go
+++ b/backend/internal/service/ops_metrics_collector.go
@@ -193,28 +193,25 @@ func (c *OpsMetricsCollector) collectOnce() {
runAt := startedAt
if err != nil {
- msg := truncateString(err.Error(), 2048)
- errAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), opsMetricsCollectorHeartbeatTimeout)
defer hbCancel()
_ = c.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsMetricsCollectorJobName,
LastRunAt: &runAt,
- LastErrorAt: &errAt,
- LastError: &msg,
+ LastErrorAt: new(finishedAt),
+ LastError: new(truncateString(err.Error(), 2048)),
LastDurationMs: &dur,
})
log.Printf("[OpsMetricsCollector] collect failed: %v", err)
return
}
- successAt := finishedAt
hbCtx, hbCancel := context.WithTimeout(context.Background(), opsMetricsCollectorHeartbeatTimeout)
defer hbCancel()
_ = c.opsRepo.UpsertJobHeartbeat(hbCtx, &OpsUpsertJobHeartbeatInput{
JobName: opsMetricsCollectorJobName,
LastRunAt: &runAt,
- LastSuccessAt: &successAt,
+ LastSuccessAt: new(finishedAt),
LastDurationMs: &dur,
})
}
@@ -380,8 +377,7 @@ func (c *OpsMetricsCollector) collectConcurrencyQueueDepth(parentCtx context.Con
return nil
}
if len(accounts) == 0 {
- zero := 0
- return &zero
+ return new(0)
}
batch := make([]AccountWithConcurrency, 0, len(accounts))
@@ -399,8 +395,7 @@ func (c *OpsMetricsCollector) collectConcurrencyQueueDepth(parentCtx context.Con
})
}
if len(batch) == 0 {
- zero := 0
- return &zero
+ return new(0)
}
loadMap, err := c.concurrencyService.GetAccountsLoadBatch(ctx, batch)
@@ -423,8 +418,7 @@ func (c *OpsMetricsCollector) collectConcurrencyQueueDepth(parentCtx context.Con
if total > maxInt {
total = maxInt
}
- v := int(total)
- return &v
+ return new(int(total))
}
type opsCollectedPercentiles struct {
@@ -479,12 +473,10 @@ WHERE created_at >= $1 AND created_at < $2
duration.p95 = floatToIntPtr(p95)
duration.p99 = floatToIntPtr(p99)
if avg.Valid {
- v := roundTo1DP(avg.Float64)
- duration.avg = &v
+ duration.avg = new(roundTo1DP(avg.Float64))
}
if max.Valid {
- v := int(max.Int64)
- duration.max = &v
+ duration.max = new(int(max.Int64))
}
}
@@ -512,12 +504,10 @@ WHERE created_at >= $1 AND created_at < $2
ttft.p95 = floatToIntPtr(p95)
ttft.p99 = floatToIntPtr(p99)
if avg.Valid {
- v := roundTo1DP(avg.Float64)
- ttft.avg = &v
+ ttft.avg = new(roundTo1DP(avg.Float64))
}
if max.Valid {
- v := int(max.Int64)
- ttft.max = &v
+ ttft.max = new(int(max.Int64))
}
}
@@ -600,21 +590,17 @@ func (c *OpsMetricsCollector) collectSystemStats(ctx context.Context) (*opsColle
cgroupUsed, cgroupTotal, cgroupOK := readCgroupMemoryBytes()
if cgroupOK {
- usedMB := int64(cgroupUsed / bytesPerMB)
- out.memoryUsedMB = &usedMB
+ out.memoryUsedMB = new(int64(cgroupUsed / bytesPerMB))
if cgroupTotal > 0 {
- totalMB := int64(cgroupTotal / bytesPerMB)
- out.memoryTotalMB = &totalMB
- pct := roundTo1DP(float64(cgroupUsed) / float64(cgroupTotal) * 100)
- out.memoryUsagePercent = &pct
+ out.memoryTotalMB = new(int64(cgroupTotal / bytesPerMB))
+ out.memoryUsagePercent = new(roundTo1DP(float64(cgroupUsed) / float64(cgroupTotal) * 100))
}
}
// Fallback to host metrics if cgroup metrics are unavailable (or incomplete).
if out.cpuUsagePercent == nil {
if cpuPercents, err := cpu.PercentWithContext(ctx, 0, false); err == nil && len(cpuPercents) > 0 {
- v := roundTo1DP(cpuPercents[0])
- out.cpuUsagePercent = &v
+ out.cpuUsagePercent = new(roundTo1DP(cpuPercents[0]))
}
}
@@ -622,20 +608,16 @@ func (c *OpsMetricsCollector) collectSystemStats(ctx context.Context) (*opsColle
if out.memoryUsedMB == nil || out.memoryTotalMB == nil || out.memoryUsagePercent == nil {
if vm, err := mem.VirtualMemoryWithContext(ctx); err == nil && vm != nil {
if out.memoryUsedMB == nil {
- usedMB := int64(vm.Used / bytesPerMB)
- out.memoryUsedMB = &usedMB
+ out.memoryUsedMB = new(int64(vm.Used / bytesPerMB))
}
if out.memoryTotalMB == nil {
- totalMB := int64(vm.Total / bytesPerMB)
- out.memoryTotalMB = &totalMB
+ out.memoryTotalMB = new(int64(vm.Total / bytesPerMB))
}
if out.memoryUsagePercent == nil {
if out.memoryUsedMB != nil && out.memoryTotalMB != nil && *out.memoryTotalMB > 0 {
- pct := roundTo1DP(float64(*out.memoryUsedMB) / float64(*out.memoryTotalMB) * 100)
- out.memoryUsagePercent = &pct
+ out.memoryUsagePercent = new(roundTo1DP(float64(*out.memoryUsedMB) / float64(*out.memoryTotalMB) * 100))
} else {
- pct := roundTo1DP(vm.UsedPercent)
- out.memoryUsagePercent = &pct
+ out.memoryUsagePercent = new(roundTo1DP(vm.UsedPercent))
}
}
}
@@ -693,8 +675,7 @@ func (c *OpsMetricsCollector) tryCgroupCPUPercent(now time.Time) *float64 {
if pct > 100 {
pct = 100
}
- v := roundTo1DP(pct)
- return &v
+ return new(roundTo1DP(pct))
}
func readCgroupMemoryBytes() (usedBytes uint64, totalBytes uint64, ok bool) {
@@ -909,8 +890,7 @@ func floatToIntPtr(v sql.NullFloat64) *int {
if !v.Valid {
return nil
}
- n := int(math.Round(v.Float64))
- return &n
+ return new(int(math.Round(v.Float64)))
}
func roundTo1DP(v float64) float64 {
@@ -932,16 +912,13 @@ func truncateString(s string, max int) string {
}
func boolPtr(v bool) *bool {
- out := v
- return &out
+ return new(v)
}
func intPtr(v int) *int {
- out := v
- return &out
+ return new(v)
}
func float64Ptr(v float64) *float64 {
- out := v
- return &out
+ return new(v)
}
diff --git a/backend/internal/service/ops_retry.go b/backend/internal/service/ops_retry.go
index 23a524ad2..dc831277e 100644
--- a/backend/internal/service/ops_retry.go
+++ b/backend/internal/service/ops_retry.go
@@ -196,11 +196,8 @@ func (s *OpsService) RetryUpstreamEvent(ctx context.Context, requestedByUserID i
}
override := *errorLog
- override.RequestBody = upstreamBody
- pinned := ev.AccountID
-
- // Persist as upstream_event, execute as upstream pinned retry.
- return s.retryWithErrorLog(ctx, requestedByUserID, errorID, OpsRetryModeUpstreamEvent, OpsRetryModeUpstream, &pinned, &override)
+ override.RequestBody = upstreamBody // Persist as upstream_event, execute as upstream pinned retry.
+ return s.retryWithErrorLog(ctx, requestedByUserID, errorID, OpsRetryModeUpstreamEvent, OpsRetryModeUpstream, new(ev.AccountID), &override)
}
func (s *OpsService) retryWithErrorLog(ctx context.Context, requestedByUserID int64, errorID int64, mode string, execMode string, pinnedAccountID *int64, errorLog *OpsErrorLogDetail) (*OpsRetryResult, error) {
@@ -250,8 +247,7 @@ func (s *OpsService) retryWithErrorLog(ctx context.Context, requestedByUserID in
StartedAt: startedAt,
})
if err != nil {
- var pqErr *pq.Error
- if errors.As(err, &pqErr) && string(pqErr.Code) == "23505" {
+ if pqErr, ok := errors.AsType[*pq.Error](err); ok && string(pqErr.Code) == "23505" {
return nil, infraerrors.Conflict("OPS_RETRY_IN_PROGRESS", "A retry is already in progress for this error")
}
return nil, infraerrors.InternalServer("OPS_RETRY_CREATE_ATTEMPT_FAILED", "Failed to create retry attempt").WithCause(err)
@@ -294,8 +290,7 @@ func (s *OpsService) retryWithErrorLog(ctx context.Context, requestedByUserID in
var updateErrMsg *string
if strings.TrimSpace(result.ErrorMessage) != "" {
- msg := result.ErrorMessage
- updateErrMsg = &msg
+ updateErrMsg = new(result.ErrorMessage)
}
// Keep legacy result_request_id empty; use upstream_request_id instead.
var resultRequestID *string
@@ -306,23 +301,18 @@ func (s *OpsService) retryWithErrorLog(ctx context.Context, requestedByUserID in
}
success := strings.EqualFold(finalStatus, opsRetryStatusSucceeded)
- httpStatus := result.HTTPStatusCode
- upstreamReqID := result.UpstreamRequestID
usedAccountID := result.UsedAccountID
- preview := result.ResponsePreview
- truncated := result.ResponseTruncated
-
if err := s.opsRepo.UpdateRetryAttempt(updateCtx, &OpsUpdateRetryAttemptInput{
ID: attemptID,
Status: finalStatus,
FinishedAt: finishedAt,
DurationMs: result.DurationMs,
Success: &success,
- HTTPStatusCode: &httpStatus,
- UpstreamRequestID: &upstreamReqID,
+ HTTPStatusCode: new(result.HTTPStatusCode),
+ UpstreamRequestID: new(result.UpstreamRequestID),
UsedAccountID: usedAccountID,
- ResponsePreview: &preview,
- ResponseTruncated: &truncated,
+ ResponsePreview: new(result.ResponsePreview),
+ ResponseTruncated: new(result.ResponseTruncated),
ResultRequestID: resultRequestID,
ErrorMessage: updateErrMsg,
}); err != nil {
@@ -434,9 +424,8 @@ func (s *OpsService) executePinnedRetry(ctx context.Context, reqType opsRetryReq
defer release()
}
- usedID := account.ID
exec := s.executeWithAccount(ctx, reqType, errorLog, body, account)
- exec.usedAccountID = &usedID
+ exec.usedAccountID = new(account.ID)
if exec.status == "" {
exec.status = opsRetryStatusFailed
}
@@ -489,8 +478,7 @@ func (s *OpsService) executeClientRetry(ctx context.Context, reqType opsRetryReq
if exec != nil {
if exec.status == opsRetryStatusSucceeded {
- usedID := account.ID
- exec.usedAccountID = &usedID
+ exec.usedAccountID = new(account.ID)
return exec
}
// If the gateway services ask for failover, try another account.
@@ -499,8 +487,7 @@ func (s *OpsService) executeClientRetry(ctx context.Context, reqType opsRetryReq
switches++
continue
}
- usedID := account.ID
- exec.usedAccountID = &usedID
+ exec.usedAccountID = new(account.ID)
return exec
}
diff --git a/backend/internal/service/ops_scheduled_report_service.go b/backend/internal/service/ops_scheduled_report_service.go
index 98b2045de..3d584b011 100644
--- a/backend/internal/service/ops_scheduled_report_service.go
+++ b/backend/internal/service/ops_scheduled_report_service.go
@@ -280,8 +280,7 @@ func (s *OpsScheduledReportService) listScheduledReports(ctx context.Context, no
var lastRunPtr *time.Time
if !lastRun.IsZero() {
- lastCopy := lastRun
- lastRunPtr = &lastCopy
+ lastRunPtr = new(lastRun)
}
out = append(out, &opsScheduledReport{
@@ -391,11 +390,9 @@ func (s *OpsScheduledReportService) generateReportHTML(ctx context.Context, repo
return buildOpsSummaryEmailHTML(report.Name, start, end, overview), nil
case "error_digest":
// Lightweight digest: list recent errors (status>=400) and breakdown by type.
- startTime := start
- endTime := end
filter := &OpsErrorLogFilter{
- StartTime: &startTime,
- EndTime: &endTime,
+ StartTime: new(start),
+ EndTime: new(end),
Page: 1,
PageSize: 100,
}
@@ -664,8 +661,6 @@ func (s *OpsScheduledReportService) recordHeartbeatSuccess(runAt time.Time, dura
if s == nil || s.opsService == nil || s.opsService.opsRepo == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
msg := strings.TrimSpace(result)
@@ -676,8 +671,8 @@ func (s *OpsScheduledReportService) recordHeartbeatSuccess(runAt time.Time, dura
_ = s.opsService.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsScheduledReportJobName,
LastRunAt: &runAt,
- LastSuccessAt: &now,
- LastDurationMs: &durMs,
+ LastSuccessAt: new(time.Now().UTC()),
+ LastDurationMs: new(duration.Milliseconds()),
LastResult: &msg,
})
}
@@ -686,17 +681,14 @@ func (s *OpsScheduledReportService) recordHeartbeatError(runAt time.Time, durati
if s == nil || s.opsService == nil || s.opsService.opsRepo == nil || err == nil {
return
}
- now := time.Now().UTC()
- durMs := duration.Milliseconds()
- msg := truncateString(err.Error(), 2048)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.opsService.opsRepo.UpsertJobHeartbeat(ctx, &OpsUpsertJobHeartbeatInput{
JobName: opsScheduledReportJobName,
LastRunAt: &runAt,
- LastErrorAt: &now,
- LastError: &msg,
- LastDurationMs: &durMs,
+ LastErrorAt: new(time.Now().UTC()),
+ LastError: new(truncateString(err.Error(), 2048)),
+ LastDurationMs: new(duration.Milliseconds()),
})
}
diff --git a/backend/internal/service/ops_service.go b/backend/internal/service/ops_service.go
index 9c121b8b8..41a118177 100644
--- a/backend/internal/service/ops_service.go
+++ b/backend/internal/service/ops_service.go
@@ -235,8 +235,7 @@ func (s *OpsService) RecordError(ctx context.Context, entry *OpsInsertErrorLogIn
continue
}
- evCopy := out
- sanitized = append(sanitized, &evCopy)
+ sanitized = append(sanitized, new(out))
}
entry.UpstreamErrorsJSON = marshalOpsUpstreamErrors(sanitized)
diff --git a/backend/internal/service/ops_settings.go b/backend/internal/service/ops_settings.go
index a6a4a0d71..1ad5480fa 100644
--- a/backend/internal/service/ops_settings.go
+++ b/backend/internal/service/ops_settings.go
@@ -483,15 +483,11 @@ func (s *OpsService) UpdateOpsAdvancedSettings(ctx context.Context, cfg *OpsAdva
const SettingKeyOpsMetricThresholds = "ops_metric_thresholds"
func defaultOpsMetricThresholds() *OpsMetricThresholds {
- slaMin := 99.5
- ttftMax := 500.0
- reqErrMax := 5.0
- upstreamErrMax := 5.0
return &OpsMetricThresholds{
- SLAPercentMin: &slaMin,
- TTFTp99MsMax: &ttftMax,
- RequestErrorRatePercentMax: &reqErrMax,
- UpstreamErrorRatePercentMax: &upstreamErrMax,
+ SLAPercentMin: new(99.5),
+ TTFTp99MsMax: new(500.0),
+ RequestErrorRatePercentMax: new(5.0),
+ UpstreamErrorRatePercentMax: new(5.0),
}
}
diff --git a/backend/internal/service/ops_upstream_context.go b/backend/internal/service/ops_upstream_context.go
index 3514df791..714601bd7 100644
--- a/backend/internal/service/ops_upstream_context.go
+++ b/backend/internal/service/ops_upstream_context.go
@@ -149,8 +149,7 @@ func marshalOpsUpstreamErrors(events []*OpsUpstreamErrorEvent) *string {
if err != nil || len(raw) == 0 {
return nil
}
- s := string(raw)
- return &s
+ return new(string(raw))
}
func ParseOpsUpstreamErrors(raw string) ([]*OpsUpstreamErrorEvent, error) {
diff --git a/backend/internal/service/ratelimit_service.go b/backend/internal/service/ratelimit_service.go
index 12c48ab83..bc84c0ea3 100644
--- a/backend/internal/service/ratelimit_service.go
+++ b/backend/internal/service/ratelimit_service.go
@@ -440,9 +440,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *Account, head
}
// 根据重置时间反推5h窗口
- windowEnd := resetAt
- windowStart := resetAt.Add(-5 * time.Hour)
- if err := s.accountRepo.UpdateSessionWindow(ctx, account.ID, &windowStart, &windowEnd, "rejected"); err != nil {
+ if err := s.accountRepo.UpdateSessionWindow(ctx, account.ID, new(resetAt.Add(-5*time.Hour)), new(resetAt), "rejected"); err != nil {
slog.Warn("rate_limit_update_session_window_failed", "account_id", account.ID, "error", err)
}
@@ -527,8 +525,7 @@ func parseOpenAIRateLimitResetTime(body []byte) *int64 {
// 优先使用 resets_at(Unix 时间戳)
if resetsAt, ok := errObj["resets_at"].(float64); ok {
- ts := int64(resetsAt)
- return &ts
+ return new(int64(resetsAt))
}
if resetsAt, ok := errObj["resets_at"].(string); ok {
if ts, err := strconv.ParseInt(resetsAt, 10, 64); err == nil {
@@ -538,13 +535,11 @@ func parseOpenAIRateLimitResetTime(body []byte) *int64 {
// 如果没有 resets_at,尝试使用 resets_in_seconds
if resetsInSeconds, ok := errObj["resets_in_seconds"].(float64); ok {
- ts := time.Now().Unix() + int64(resetsInSeconds)
- return &ts
+ return new(time.Now().Unix() + int64(resetsInSeconds))
}
if resetsInSeconds, ok := errObj["resets_in_seconds"].(string); ok {
if sec, err := strconv.ParseInt(resetsInSeconds, 10, 64); err == nil {
- ts := time.Now().Unix() + sec
- return &ts
+ return new(time.Now().Unix() + sec)
}
}
diff --git a/backend/internal/service/ratelimit_service_openai_test.go b/backend/internal/service/ratelimit_service_openai_test.go
index 009020686..a09374cb3 100644
--- a/backend/internal/service/ratelimit_service_openai_test.go
+++ b/backend/internal/service/ratelimit_service_openai_test.go
@@ -143,20 +143,13 @@ func TestCalculateOpenAI429ResetTime_ReversedWindowOrder(t *testing.T) {
func TestNormalizedCodexLimits(t *testing.T) {
// Test the Normalize() method directly
- pUsed := 100.0
- pReset := 384607
- pWindow := 10080
- sUsed := 3.0
- sReset := 17369
- sWindow := 300
-
snapshot := &OpenAICodexUsageSnapshot{
- PrimaryUsedPercent: &pUsed,
- PrimaryResetAfterSeconds: &pReset,
- PrimaryWindowMinutes: &pWindow,
- SecondaryUsedPercent: &sUsed,
- SecondaryResetAfterSeconds: &sReset,
- SecondaryWindowMinutes: &sWindow,
+ PrimaryUsedPercent: new(100.0),
+ PrimaryResetAfterSeconds: new(384607),
+ PrimaryWindowMinutes: new(10080),
+ SecondaryUsedPercent: new(3.0),
+ SecondaryResetAfterSeconds: new(17369),
+ SecondaryWindowMinutes: new(300),
}
normalized := snapshot.Normalize()
@@ -181,12 +174,9 @@ func TestNormalizedCodexLimits(t *testing.T) {
func TestNormalizedCodexLimits_OnlyPrimaryData(t *testing.T) {
// Test when only primary has data, no window_minutes
- pUsed := 80.0
- pReset := 50000
-
snapshot := &OpenAICodexUsageSnapshot{
- PrimaryUsedPercent: &pUsed,
- PrimaryResetAfterSeconds: &pReset,
+ PrimaryUsedPercent: new(80.0),
+ PrimaryResetAfterSeconds: new(50000),
// No window_minutes, no secondary data
}
@@ -213,12 +203,9 @@ func TestNormalizedCodexLimits_OnlyPrimaryData(t *testing.T) {
func TestNormalizedCodexLimits_OnlySecondaryData(t *testing.T) {
// Test when only secondary has data, no window_minutes
- sUsed := 60.0
- sReset := 3000
-
snapshot := &OpenAICodexUsageSnapshot{
- SecondaryUsedPercent: &sUsed,
- SecondaryResetAfterSeconds: &sReset,
+ SecondaryUsedPercent: new(60.0),
+ SecondaryResetAfterSeconds: new(3000),
// No window_minutes, no primary data
}
@@ -243,16 +230,11 @@ func TestNormalizedCodexLimits_OnlySecondaryData(t *testing.T) {
func TestNormalizedCodexLimits_BothDataNoWindowMinutes(t *testing.T) {
// Test when both have data but no window_minutes
- pUsed := 100.0
- pReset := 400000
- sUsed := 50.0
- sReset := 10000
-
snapshot := &OpenAICodexUsageSnapshot{
- PrimaryUsedPercent: &pUsed,
- PrimaryResetAfterSeconds: &pReset,
- SecondaryUsedPercent: &sUsed,
- SecondaryResetAfterSeconds: &sReset,
+ PrimaryUsedPercent: new(100.0),
+ PrimaryResetAfterSeconds: new(400000),
+ SecondaryUsedPercent: new(50.0),
+ SecondaryResetAfterSeconds: new(10000),
// No window_minutes
}
diff --git a/backend/internal/service/usage_cleanup_service_test.go b/backend/internal/service/usage_cleanup_service_test.go
index c6c309b60..740c5976b 100644
--- a/backend/internal/service/usage_cleanup_service_test.go
+++ b/backend/internal/service/usage_cleanup_service_test.go
@@ -105,8 +105,7 @@ func (s *cleanupRepoStub) CreateTask(ctx context.Context, task *UsageCleanupTask
if task.UpdatedAt.IsZero() {
task.UpdatedAt = task.CreatedAt
}
- clone := *task
- s.created = append(s.created, &clone)
+ s.created = append(s.created, new(*task))
return nil
}
@@ -232,17 +231,14 @@ func TestUsageCleanupServiceCreateTaskSanitizeFilters(t *testing.T) {
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := start.Add(24 * time.Hour)
- userID := int64(-1)
apiKeyID := int64(10)
- model := " gpt-4 "
- billingType := int8(-2)
filters := UsageCleanupFilters{
StartTime: start,
EndTime: end,
- UserID: &userID,
+ UserID: new(int64(-1)),
APIKeyID: &apiKeyID,
- Model: &model,
- BillingType: &billingType,
+ Model: new(" gpt-4 "),
+ BillingType: new(int8(-2)),
}
task, err := svc.CreateTask(context.Background(), filters, 9)
@@ -645,12 +641,11 @@ func TestUsageCleanupServiceCancelTaskConflict(t *testing.T) {
}
func TestUsageCleanupServiceCancelTaskRepoConflict(t *testing.T) {
- shouldCancel := false
repo := &cleanupRepoStub{
statusByID: map[int64]string{
7: UsageCleanupStatusPending,
},
- cancelResult: &shouldCancel,
+ cancelResult: new(false),
}
cfg := &config.Config{UsageCleanup: config.UsageCleanupConfig{Enabled: true}}
svc := NewUsageCleanupService(repo, nil, nil, cfg)
@@ -753,16 +748,13 @@ func TestUsageCleanupServiceDefaultsAndLifecycle(t *testing.T) {
}
func TestSanitizeUsageCleanupFiltersModelEmpty(t *testing.T) {
- model := " "
apiKeyID := int64(-5)
- accountID := int64(-1)
- groupID := int64(-2)
filters := UsageCleanupFilters{
UserID: &apiKeyID,
APIKeyID: &apiKeyID,
- AccountID: &accountID,
- GroupID: &groupID,
- Model: &model,
+ AccountID: new(int64(-1)),
+ GroupID: new(int64(-2)),
+ Model: new(" "),
}
sanitizeUsageCleanupFilters(&filters)
@@ -776,23 +768,16 @@ func TestSanitizeUsageCleanupFiltersModelEmpty(t *testing.T) {
func TestDescribeUsageCleanupFiltersAllFields(t *testing.T) {
start := time.Date(2024, 2, 1, 10, 0, 0, 0, time.UTC)
end := start.Add(2 * time.Hour)
- userID := int64(1)
- apiKeyID := int64(2)
- accountID := int64(3)
- groupID := int64(4)
- model := " gpt-4 "
- stream := true
- billingType := int8(2)
filters := UsageCleanupFilters{
StartTime: start,
EndTime: end,
- UserID: &userID,
- APIKeyID: &apiKeyID,
- AccountID: &accountID,
- GroupID: &groupID,
- Model: &model,
- Stream: &stream,
- BillingType: &billingType,
+ UserID: new(int64(1)),
+ APIKeyID: new(int64(2)),
+ AccountID: new(int64(3)),
+ GroupID: new(int64(4)),
+ Model: new(" gpt-4 "),
+ Stream: new(true),
+ BillingType: new(int8(2)),
}
desc := describeUsageCleanupFilters(filters)
diff --git a/backend/internal/service/user_subscription.go b/backend/internal/service/user_subscription.go
index ec547d81a..e137cbab5 100644
--- a/backend/internal/service/user_subscription.go
+++ b/backend/internal/service/user_subscription.go
@@ -75,24 +75,21 @@ func (s *UserSubscription) DailyResetTime() *time.Time {
if s.DailyWindowStart == nil {
return nil
}
- t := s.DailyWindowStart.Add(24 * time.Hour)
- return &t
+ return new(s.DailyWindowStart.Add(24 * time.Hour))
}
func (s *UserSubscription) WeeklyResetTime() *time.Time {
if s.WeeklyWindowStart == nil {
return nil
}
- t := s.WeeklyWindowStart.Add(7 * 24 * time.Hour)
- return &t
+ return new(s.WeeklyWindowStart.Add(7 * 24 * time.Hour))
}
func (s *UserSubscription) MonthlyResetTime() *time.Time {
if s.MonthlyWindowStart == nil {
return nil
}
- t := s.MonthlyWindowStart.Add(30 * 24 * time.Hour)
- return &t
+ return new(s.MonthlyWindowStart.Add(30 * 24 * time.Hour))
}
func (s *UserSubscription) CheckDailyLimit(group *Group, additionalCost float64) bool {