feat(models): 模型目录与计费 - 按展示名分组并隔离管理员积分成本

This commit is contained in:
ddCat
2026-09-19 09:54:27 +08:00
parent 5ad05efb58
commit 237f2e2a6e
49 changed files with 802 additions and 276 deletions

View File

@@ -0,0 +1,49 @@
package database
import (
"testing"
"infinite-canvas/backend/internal/model"
)
func TestChannelCreditCostMigrationPreservesSalesAndUnknownHistoricalCosts(t *testing.T) {
db, err := Open(Config{Driver: "sqlite", DSN: ":memory:"})
if err != nil {
t.Fatal(err)
}
for _, sql := range []string{
`CREATE TABLE channel_model_price_tiers (id TEXT PRIMARY KEY, unit_price_microcredits INTEGER, deleted_at DATETIME)`,
`CREATE TABLE billing_orders (id TEXT PRIMARY KEY, actual_amount_microcredits INTEGER)`,
`INSERT INTO channel_model_price_tiers VALUES ('tier',900000,NULL)`,
`INSERT INTO billing_orders VALUES ('order',13500000)`,
} {
if err := db.Exec(sql).Error; err != nil {
t.Fatal(err)
}
}
for i := 0; i < 2; i++ {
if err := migrateChannelCreditCost(db); err != nil {
t.Fatal(err)
}
}
var tier model.ChannelModelPriceTier
var order model.BillingOrder
if err := db.First(&tier, "id = ?", "tier").Error; err != nil {
t.Fatal(err)
}
if err := db.First(&order, "id = ?", "order").Error; err != nil {
t.Fatal(err)
}
if tier.UnitPriceMicrocredits != 900000 || order.ActualAmountMicrocredits != 13500000 || tier.CostPricing.Configured || order.CostPricing.Configured || order.CostBillingMode != "" {
t.Fatalf("migration changed history: %#v %#v", tier, order)
}
if err := db.Table("channel_model_price_tiers").Where("id = ?", "tier").Updates(map[string]any{"cost_configured": true, "cost_unit_price_microcredits": 123456}).Error; err != nil {
t.Fatal(err)
}
if err := db.First(&tier, "id = ?", "tier").Error; err != nil {
t.Fatal(err)
}
if !tier.CostPricing.Configured || tier.CostPricing.UnitPriceMicrocredits != 123456 {
t.Fatalf("cost columns not mapped: %#v", tier)
}
}

View File

@@ -11,7 +11,7 @@ import (
"gorm.io/gorm"
)
const CurrentSchemaVersion int64 = 26
const CurrentSchemaVersion int64 = 27
const baselineSchemaChecksum = "sha256:open-ai-canvas-schema-v1-20260830"
const schemaMigrationAppliedAtIndexChecksum = "sha256:schema-migrations-applied-at-index-v2-20260830"
@@ -92,6 +92,27 @@ var schemaMigrations = []migration{
{version: 24, name: "channel_model_label", checksum: "sha256:channel-model-label-v24", apply: migrateChannelModelLabel},
{version: 25, name: "video_token_formula_snapshot", checksum: "sha256:video-token-formula-snapshot-v25", apply: migrateVideoTokenFormulaSnapshot},
{version: 26, name: "channel_model_description", checksum: "sha256:channel-model-description-v26", apply: migrateChannelModelDescription},
{version: 27, name: "channel_credit_cost", checksum: "sha256:channel-credit-cost-v27", apply: migrateChannelCreditCost},
}
func migrateChannelCreditCost(tx *gorm.DB) error {
for _, entity := range []any{&model.ChannelModelPriceTier{}, &model.BillingOrder{}} {
for _, column := range []string{"cost_configured", "cost_unit_price_microcredits", "cost_input_token_price_microcredits", "cost_output_token_price_microcredits", "cost_cached_token_price_microcredits"} {
if !tx.Migrator().HasColumn(entity, column) {
if err := tx.Migrator().AddColumn(entity, column); err != nil {
return err
}
}
}
}
for _, column := range []string{"CostBillingMode", "CostQuantity", "CostVideoFormulaTokens"} {
if !tx.Migrator().HasColumn(&model.BillingOrder{}, column) {
if err := tx.Migrator().AddColumn(&model.BillingOrder{}, column); err != nil {
return err
}
}
}
return nil
}
func migrateChannelModelDescription(tx *gorm.DB) error {

View File

@@ -446,7 +446,7 @@ func TestMigrateSchemaV4AddsResourceUploadKeyToExistingSchema(t *testing.T) {
if err := db.Exec(`CREATE TABLE resources (id TEXT PRIMARY KEY, user_id TEXT NOT NULL)`).Error; err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&model.ModelChannel{}, &model.ChannelModel{}, &model.BillingOrder{}); err != nil {
if err := db.AutoMigrate(&model.ModelChannel{}, &model.ChannelModel{}, &model.ChannelModelPriceTier{}, &model.BillingOrder{}); err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&schemaMigration{}); err != nil {
@@ -490,7 +490,7 @@ func TestMigrateSchemaRepairsLegacyAssetFoldersMigrationOrder(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&model.Resource{}, &model.Asset{}, &model.AssetFolder{}, &model.ModelChannel{}, &model.ChannelModel{}, &model.BillingOrder{}); err != nil {
if err := db.AutoMigrate(&model.Resource{}, &model.Asset{}, &model.AssetFolder{}, &model.ModelChannel{}, &model.ChannelModel{}, &model.ChannelModelPriceTier{}, &model.BillingOrder{}); err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&schemaMigration{}); err != nil {