feat: 金额 decimal 精确计费、failover 按失败类型封顶、渠道小时桶预聚合、分层守护测试
- 计费:19 个金额字段 float64→decimal.Decimal,DB 列保持 numeric(20,8) 零迁移; API 金额仍为 JSON number(MarshalJSONWithoutQuotes),前端零改动 - failover:保留时间预算,新增 OT_PROXY_FAILOVER_CAPS_NET/SERVER/RATE/QUOTA 按失败类型封顶换渠道次数(429/配额类瞬时失败时间预算形同虚设) - 预聚合:新增 usage_channel_hourlies(schema v10),记账事务内同步 upsert, 长窗口渠道统计走桶不再扫明细,spent_last_30d 改扫 usage_daily,v10 一次性回填幂等 - 守护:internal/arch_test.go 用 go/parser 强制分层依赖(fail-closed)
This commit is contained in:
1 parent
5d6e5bdb04
commit
b2e500ff51
25 files changed
+841
-253
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openteam/server/internal/pkg/resp"
|
||||
"github.com/openteam/server/internal/store"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -38,7 +39,7 @@ func (h *Handler) AdminModels(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
used := len(chs) > 0
|
||||
needsPricing := used && m.InputPrice == 0 && m.OutputPrice == 0 && m.CacheReadPrice == 0
|
||||
needsPricing := used && m.InputPrice.IsZero() && m.OutputPrice.IsZero() && m.CacheReadPrice.IsZero()
|
||||
denied := containsStr(deny, m.Name) || (len(allow) > 0 && !containsStr(allow, m.Name))
|
||||
out = append(out, gin.H{
|
||||
"id": m.ID, "name": m.Name,
|
||||
@@ -81,7 +82,7 @@ func (h *Handler) AdminModels(c *gin.Context) {
|
||||
usedIDs[u.ModelID] = true
|
||||
}
|
||||
for _, m := range ms {
|
||||
if usedIDs[m.ID] && m.InputPrice == 0 && m.OutputPrice == 0 && m.CacheReadPrice == 0 {
|
||||
if usedIDs[m.ID] && m.InputPrice.IsZero() && m.OutputPrice.IsZero() && m.CacheReadPrice.IsZero() {
|
||||
unpriced++
|
||||
}
|
||||
}
|
||||
@@ -121,16 +122,16 @@ func containsStr(list []string, s string) bool {
|
||||
// AdminCreateModel POST /api/v1/admin/models
|
||||
func (h *Handler) AdminCreateModel(c *gin.Context) {
|
||||
var req struct {
|
||||
Name string `json:"name" binding:"required,min=1,max=128"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
OutputPrice float64 `json:"output_price"`
|
||||
CacheReadPrice float64 `json:"cache_read_price"`
|
||||
CacheWritePrice float64 `json:"cache_write_price"`
|
||||
ContextLength int `json:"context_length"`
|
||||
SupportsTools bool `json:"supports_tools"`
|
||||
SupportsVision bool `json:"supports_vision"`
|
||||
SupportsReasoning bool `json:"supports_reasoning"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Name string `json:"name" binding:"required,min=1,max=128"`
|
||||
InputPrice decimal.Decimal `json:"input_price"`
|
||||
OutputPrice decimal.Decimal `json:"output_price"`
|
||||
CacheReadPrice decimal.Decimal `json:"cache_read_price"`
|
||||
CacheWritePrice decimal.Decimal `json:"cache_write_price"`
|
||||
ContextLength int `json:"context_length"`
|
||||
SupportsTools bool `json:"supports_tools"`
|
||||
SupportsVision bool `json:"supports_vision"`
|
||||
SupportsReasoning bool `json:"supports_reasoning"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
resp.Fail(c, http.StatusBadRequest, "invalid input: "+err.Error())
|
||||
@@ -159,16 +160,16 @@ func (h *Handler) AdminUpdateModel(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
InputPrice *float64 `json:"input_price"`
|
||||
OutputPrice *float64 `json:"output_price"`
|
||||
CacheReadPrice *float64 `json:"cache_read_price"`
|
||||
CacheWritePrice *float64 `json:"cache_write_price"`
|
||||
ContextLength *int `json:"context_length"`
|
||||
SupportsTools *bool `json:"supports_tools"`
|
||||
SupportsVision *bool `json:"supports_vision"`
|
||||
SupportsReasoning *bool `json:"supports_reasoning"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Sort *int `json:"sort"`
|
||||
InputPrice *decimal.Decimal `json:"input_price"`
|
||||
OutputPrice *decimal.Decimal `json:"output_price"`
|
||||
CacheReadPrice *decimal.Decimal `json:"cache_read_price"`
|
||||
CacheWritePrice *decimal.Decimal `json:"cache_write_price"`
|
||||
ContextLength *int `json:"context_length"`
|
||||
SupportsTools *bool `json:"supports_tools"`
|
||||
SupportsVision *bool `json:"supports_vision"`
|
||||
SupportsReasoning *bool `json:"supports_reasoning"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Sort *int `json:"sort"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
resp.Fail(c, http.StatusBadRequest, "invalid input")
|
||||
|
||||
Reference in new issue
Block a user