优化结构

This commit is contained in:
Yvan
2026-08-23 00:38:33 +08:00
parent 64459574b2
commit 4e5071ee65
22 changed files with 301 additions and 127 deletions
+2 -1
View File
@@ -25,7 +25,8 @@ reload locks. Do not split them into packages only to reduce file count.
- `system` 只拥有 `sys_*` 系统表、系统仓储、种子和系统维护清理;
- `integration` 唯一拥有 `sys_integration_configs` 表模型、配置仓储和通信默认值;
- `task` 只拥有定时任务与任务日志表;
- `payment` 只拥有 `pay_orders` 等支付持久化,读取集成配置时复用 integration 的表模型。
- `payment` 只拥有 `pay_orders` 等支付持久化,通过 `biz/integration.PaymentConfigReader`
读取支付配置,不感知 integration 的 PO 或表结构。
配置文件迁移、数据库切换、Redis/Mongo/对象存储重载仍属于根 data 的生命周期编排,
不等同于某个业务模块的表仓储。
+42
View File
@@ -2,6 +2,7 @@ package integration
import (
"encoding/json"
"errors"
integrationbiz "kra/internal/biz/integration"
"kra/pkg/database/migration"
@@ -15,6 +16,7 @@ func Migrations() []migration.Step {
return migration.CreateMissingTables(db, &ConfigPO{})
}},
{ID: "202608210001_communication_integration_defaults", Migrate: ensureCommunicationIntegrationConfigs},
{ID: "202608200004_payment_defaults", Migrate: ensurePaymentIntegrationConfigs},
}
}
@@ -43,3 +45,43 @@ func ensureCommunicationIntegrationConfigs(db *gorm.DB) error {
}
return nil
}
func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
for _, definition := range integrationbiz.IntegrationDefinitions(integrationbiz.IntegrationKindPayment) {
provider := definition.Provider
var row ConfigPO
err := db.Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).First(&row).Error
defaults := integrationbiz.DefaultIntegrationConfig(integrationbiz.IntegrationKindPayment, provider)
if errors.Is(err, gorm.ErrRecordNotFound) {
encoded, marshalErr := json.Marshal(defaults)
if marshalErr != nil {
return marshalErr
}
if err := db.Create(&ConfigPO{Kind: integrationbiz.IntegrationKindPayment, Provider: provider, Config: string(encoded)}).Error; err != nil {
return err
}
continue
}
if err != nil {
return err
}
values := integrationObject(json.RawMessage(row.Config))
changed := false
for key, value := range defaults {
if _, exists := values[key]; !exists {
values[key] = value
changed = true
}
}
if changed {
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
}
+10 -7
View File
@@ -3,19 +3,15 @@ package data
import (
"testing"
bizpayment "kra/internal/biz/payment"
dataintegration "kra/internal/data/integration"
integrationmodule "kra/internal/modules/integration"
paymentmodule "kra/internal/modules/payment"
systemmodule "kra/internal/modules/system"
taskmodule "kra/internal/modules/task"
"kra/internal/modules"
"kra/pkg/database/migration"
platformmodule "kra/pkg/module"
)
func testCatalog() platformmodule.Catalog {
return platformmodule.Catalog{Definitions: []platformmodule.Definition{
systemmodule.Definition(), integrationmodule.Definition(), taskmodule.Definition(), paymentmodule.Definition(),
}}
return modules.Catalog()
}
func TestMigrateAllRunsModuleSchemasWithoutBootstrapSeed(t *testing.T) {
@@ -48,6 +44,13 @@ func TestMigrateAllRunsModuleSchemasWithoutBootstrapSeed(t *testing.T) {
if len(communicationRows) != 3 {
t.Fatalf("communication integration rows = %d, want 3", len(communicationRows))
}
var paymentRows int64
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", "payment").Count(&paymentRows).Error; err != nil {
t.Fatal(err)
}
if paymentRows != int64(len(bizpayment.SupportedPaymentProviders)) {
t.Fatalf("payment integration rows = %d, want %d", paymentRows, len(bizpayment.SupportedPaymentProviders))
}
for _, row := range communicationRows {
if row.Enabled || row.Config == "" {
t.Fatalf("default communication integration = %#v", row)
-1
View File
@@ -11,6 +11,5 @@ func Migrations() []migration.Step {
{ID: "202608200003_payment_schema", Migrate: func(db *gorm.DB) error {
return migration.CreateMissingTables(db, &paymentOrderPO{})
}},
{ID: "202608200004_payment_defaults", Migrate: ensurePaymentIntegrationConfigs},
}
}
+11 -5
View File
@@ -271,16 +271,22 @@ func recordPaymentTestError(ctx context.Context, data Provider, provider, tradeN
}
func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]any, error) {
var row dataintegration.ConfigPO
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).First(&row).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
if r == nil || r.config == nil {
return nil, errors.New("支付配置仓储未接入")
}
config, err := r.config.ReadPaymentConfig(ctx, provider)
if err != nil {
if errors.Is(err, integrationbiz.ErrPaymentConfigNotFound) {
return nil, bizpayment.ErrPaymentProviderNotFound
}
return nil, err
}
if config == nil {
return nil, errors.New("支付配置为空")
}
values := map[string]any{}
if err := json.Unmarshal([]byte(row.Config), &values); err != nil {
return nil, fmt.Errorf("支付配置格式错误: %w", err)
if err := json.Unmarshal(config.Values, &values); err != nil || values == nil {
return nil, errors.New("支付配置格式错误")
}
return values, nil
}
@@ -2,11 +2,9 @@ package payment
import (
"fmt"
"testing"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
dataintegration "kra/internal/data/integration"
)
type Data struct{ gormDB *reloadableDB }
@@ -30,32 +28,3 @@ func openWithDriver(driver, dsn string) (*gorm.DB, error) {
}
return gorm.Open(sqlite.Open(dsn), &gorm.Config{})
}
func openIntegrationConfigTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&dataintegration.ConfigPO{}); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if sqlDB, err := db.DB(); err == nil {
_ = sqlDB.Close()
}
})
return db
}
func migrateAll(db *gorm.DB) error {
if err := db.AutoMigrate(&dataintegration.ConfigPO{}); err != nil {
return err
}
for _, step := range Migrations() {
if err := step.Migrate(db); err != nil {
return err
}
}
return nil
}
+11 -3
View File
@@ -112,7 +112,7 @@ func (i *tokenIssuer) ReissueToken(source *system.AuthClaims, authorityID uint)
AuthorityID: authorityID, BufferTime: int64(source.BufferTime / time.Second), UserType: source.UserType,
MustChangePwd: source.MustChangePwd, PasswordVersion: source.PasswordVersion,
RegisteredClaims: jwt.RegisteredClaims{
Audience: jwt.ClaimStrings(append([]string(nil), source.Audience...)), Issuer: source.Issuer,
Audience: jwt.ClaimStrings{security.TokenAudience}, Issuer: settings.Issuer,
IssuedAt: jwt.NewNumericDate(source.IssuedAt), NotBefore: jwt.NewNumericDate(source.NotBefore), ExpiresAt: jwt.NewNumericDate(source.ExpiresAt),
},
}
@@ -124,7 +124,8 @@ func (i *tokenIssuer) ReissueToken(source *system.AuthClaims, authorityID uint)
}
func (i *tokenIssuer) ParseToken(token string) (*system.AuthClaims, error) {
claims, err := security.Parse(token, i.settings.JWTSettings().SigningKey)
settings := i.settings.JWTSettings()
claims, err := security.ParseWithIssuer(token, settings.SigningKey, settings.Issuer)
if err != nil {
switch {
case errors.Is(err, security.ErrTokenExpired):
@@ -144,5 +145,12 @@ func (i *tokenIssuer) ParseToken(token string) (*system.AuthClaims, error) {
if claims.IssuedAt != nil {
issuedAt = claims.IssuedAt.Time
}
return &system.AuthClaims{UUID: claims.UUID, ID: claims.ID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, UserType: claims.UserType, BufferTime: time.Duration(claims.BufferTime) * time.Second, MustChangePwd: claims.MustChangePwd, PasswordVersion: claims.PasswordVersion, Issuer: claims.Issuer, Audience: audience, IssuedAt: issuedAt, NotBefore: claims.NotBefore.Time, ExpiresAt: claims.ExpiresAt.Time}, nil
notBefore, expiresAt := time.Time{}, time.Time{}
if claims.NotBefore != nil {
notBefore = claims.NotBefore.Time
}
if claims.ExpiresAt != nil {
expiresAt = claims.ExpiresAt.Time
}
return &system.AuthClaims{UUID: claims.UUID, ID: claims.ID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, UserType: claims.UserType, BufferTime: time.Duration(claims.BufferTime) * time.Second, MustChangePwd: claims.MustChangePwd, PasswordVersion: claims.PasswordVersion, Issuer: claims.Issuer, Audience: audience, IssuedAt: issuedAt, NotBefore: notBefore, ExpiresAt: expiresAt}, nil
}