优化结构
This commit is contained in:
@@ -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 的生命周期编排,
|
||||
不等同于某个业务模块的表仓储。
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user