优化结构

This commit is contained in:
Yvan
2026-08-30 01:08:10 +08:00
parent 509df03d61
commit e0c35e4e9b
23 changed files with 1513 additions and 38 deletions
@@ -51,3 +51,93 @@ func TestIntegrationConfigSavePublishesUnmaskedRuntimeValues(t *testing.T) {
t.Fatalf("runtime password = %#v", stored["password"])
}
}
func TestCommunicationDefaultsIncludeKafka(t *testing.T) {
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
if err != nil {
t.Fatal(err)
}
if err = db.AutoMigrate(&ConfigPO{}); err != nil {
t.Fatal(err)
}
if err = ensureCommunicationIntegrationConfigs(db); err != nil {
t.Fatal(err)
}
var row ConfigPO
if err = db.Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindMQ, "kafka").First(&row).Error; err != nil {
t.Fatal(err)
}
if row.Enabled || row.Config == "" {
t.Fatalf("kafka default row = %#v", row)
}
}
func TestKafkaDefaultMigrationDoesNotRecreateOtherProviders(t *testing.T) {
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
if err != nil {
t.Fatal(err)
}
if err = db.AutoMigrate(&ConfigPO{}); err != nil {
t.Fatal(err)
}
if err = ensureKafkaIntegrationConfig(db); err != nil {
t.Fatal(err)
}
var kafkaRows, otherRows int64
if err = db.Model(&ConfigPO{}).Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindMQ, "kafka").Count(&kafkaRows).Error; err != nil {
t.Fatal(err)
}
if err = db.Model(&ConfigPO{}).Where("provider <> ?", "kafka").Count(&otherRows).Error; err != nil {
t.Fatal(err)
}
if kafkaRows != 1 || otherRows != 0 {
t.Fatalf("migration rows kafka=%d other=%d", kafkaRows, otherRows)
}
}
func TestCommunicationDefaultUpgradeRepairsRequiredEmptyFields(t *testing.T) {
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
if err != nil {
t.Fatal(err)
}
if err = db.AutoMigrate(&ConfigPO{}); err != nil {
t.Fatal(err)
}
legacy := `{"enabled":false,"broker":"","client_id":"","username":"","password":"","keep_alive":30,"clean_session":true,"connect_timeout":10}`
if err = db.Create(&ConfigPO{Kind: integrationbiz.IntegrationKindMQ, Provider: "emqx", Config: legacy}).Error; err != nil {
t.Fatal(err)
}
if err = upgradeCommunicationIntegrationDefaults(db); err != nil {
t.Fatal(err)
}
var row ConfigPO
if err = db.Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindMQ, "emqx").First(&row).Error; err != nil {
t.Fatal(err)
}
values := map[string]any{}
if err = json.Unmarshal([]byte(row.Config), &values); err != nil {
t.Fatal(err)
}
if values["broker"] != "tcp://127.0.0.1:1883" || values["client_id"] != "kra" || values["reconnect_interval"] != float64(5) {
t.Fatalf("upgraded emqx values = %#v", values)
}
if values["username"] != "" || values["password"] != "" {
t.Fatalf("optional credentials were overwritten: %#v", values)
}
if _, exists := values["enabled"]; exists {
t.Fatalf("legacy enabled field remained: %#v", values)
}
if err = db.Delete(&row).Error; err != nil {
t.Fatal(err)
}
if err = upgradeCommunicationIntegrationDefaults(db); err != nil {
t.Fatal(err)
}
var count int64
if err = db.Model(&ConfigPO{}).Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindMQ, "emqx").Count(&count).Error; err != nil {
t.Fatal(err)
}
if count != 0 {
t.Fatalf("upgrade recreated deleted emqx row: %d", count)
}
}
+81 -14
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"reflect"
"strings"
integrationbiz "kra/internal/biz/integration"
"kra/pkg/database/migration"
@@ -18,7 +19,8 @@ func Migrations() []migration.Step {
}},
{ID: "202608210001_communication_integration_defaults", Migrate: ensureCommunicationIntegrationConfigs},
{ID: "202608200004_payment_defaults", Migrate: ensurePaymentIntegrationConfigs},
{ID: "202608290004_kafka_integration_default", Migrate: ensureCommunicationIntegrationConfigs},
{ID: "202608290004_kafka_integration_default", Migrate: ensureKafkaIntegrationConfig},
{ID: "202608290005_communication_integration_default_upgrade", Migrate: upgradeCommunicationIntegrationDefaults},
}
}
@@ -30,25 +32,90 @@ func ensureCommunicationIntegrationConfigs(db *gorm.DB) error {
{integrationbiz.IntegrationKindWebSocket, "melody"},
}
for _, item := range defaults {
var row ConfigPO
err := db.Where("kind = ? AND provider = ?", item.kind, item.provider).First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
values, marshalErr := json.Marshal(integrationbiz.DefaultIntegrationConfig(item.kind, item.provider))
if marshalErr != nil {
return marshalErr
}
if err = db.Create(&ConfigPO{Kind: item.kind, Provider: item.provider, Enabled: false, Config: string(values)}).Error; err != nil {
return err
}
continue
}
if err != nil {
if err := ensureIntegrationConfig(db, item.kind, item.provider); err != nil {
return err
}
}
return nil
}
func ensureKafkaIntegrationConfig(db *gorm.DB) error {
return ensureIntegrationConfig(db, integrationbiz.IntegrationKindMQ, "kafka")
}
func ensureIntegrationConfig(db *gorm.DB, kind, provider string) error {
var row ConfigPO
err := db.Where("kind = ? AND provider = ?", kind, provider).First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
values, marshalErr := json.Marshal(integrationbiz.DefaultIntegrationConfig(kind, provider))
if marshalErr != nil {
return marshalErr
}
return db.Create(&ConfigPO{Kind: kind, Provider: provider, Enabled: false, Config: string(values)}).Error
}
return err
}
func upgradeCommunicationIntegrationDefaults(db *gorm.DB) error {
for _, kind := range []string{integrationbiz.IntegrationKindMQ, integrationbiz.IntegrationKindWebSocket} {
for _, definition := range integrationbiz.IntegrationDefinitions(kind) {
var row ConfigPO
err := db.Where("kind = ? AND provider = ?", kind, definition.Provider).First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
continue
}
if err != nil {
return err
}
values := integrationObject(json.RawMessage(row.Config))
changed := false
for key, value := range definition.Defaults {
if _, exists := values[key]; !exists {
values[key] = value
changed = true
}
}
for _, field := range definition.Fields {
if !field.Required || !emptyIntegrationConfigValue(values[field.Key]) || emptyIntegrationConfigValue(definition.Defaults[field.Key]) {
continue
}
values[field.Key] = definition.Defaults[field.Key]
changed = true
}
if _, exists := values["enabled"]; exists {
delete(values, "enabled")
changed = true
}
if !changed {
continue
}
encoded, marshalErr := json.Marshal(values)
if marshalErr != nil {
return marshalErr
}
if err := db.Model(&row).Update("config", string(encoded)).Error; err != nil {
return err
}
}
}
return nil
}
func emptyIntegrationConfigValue(value any) bool {
switch typed := value.(type) {
case nil:
return true
case string:
return strings.TrimSpace(typed) == ""
case []any:
return len(typed) == 0
case []string:
return len(typed) == 0
default:
return false
}
}
func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
for _, definition := range integrationbiz.IntegrationDefinitions(integrationbiz.IntegrationKindPayment) {
provider := definition.Provider
+4 -4
View File
@@ -34,15 +34,15 @@ func TestMigrateAllRunsModuleSchemasWithoutBootstrapSeed(t *testing.T) {
if err = db.Table(migration.TableName).Count(&versions).Error; err != nil {
t.Fatal(err)
}
if versions != 11 {
t.Fatalf("migration versions = %d, want 11", versions)
if versions != 13 {
t.Fatalf("migration versions = %d, want 13", versions)
}
var communicationRows []dataintegration.ConfigPO
if err = db.Where("kind IN ?", []string{"mq", "websocket"}).Order("kind, provider").Find(&communicationRows).Error; err != nil {
t.Fatal(err)
}
if len(communicationRows) != 3 {
t.Fatalf("communication integration rows = %d, want 3", len(communicationRows))
if len(communicationRows) != 4 {
t.Fatalf("communication integration rows = %d, want 4", len(communicationRows))
}
var paymentRows int64
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", "payment").Count(&paymentRows).Error; err != nil {