fix comb form_config_service

This commit is contained in:
2025-11-01 16:57:58 +08:00
parent da37b4d7bc
commit 90bb1d017e
4 changed files with 112 additions and 25 deletions

View File

@@ -85,7 +85,7 @@ func (a *Application) Run() error {
a.logger.Error("Failed to start container", zap.Error(err))
return err
}
a.logger.Info("Container started successfully, setting up graceful shutdown...")
// 设置优雅关闭
@@ -232,7 +232,7 @@ func (a *Application) autoMigrate(db *gorm.DB) error {
&financeEntities.AlipayOrder{},
&financeEntities.InvoiceApplication{},
&financeEntities.UserInvoiceInfo{},
// 产品域
&productEntities.Product{},
&productEntities.ProductPackageItem{},
@@ -240,18 +240,18 @@ func (a *Application) autoMigrate(db *gorm.DB) error {
&productEntities.Subscription{},
&productEntities.ProductDocumentation{},
&productEntities.ProductApiConfig{},
// 文章域
&articleEntities.Article{},
&articleEntities.Category{},
&articleEntities.Tag{},
&articleEntities.ScheduledTask{},
// 统计域
&statisticsEntities.StatisticsMetric{},
&statisticsEntities.StatisticsDashboard{},
&statisticsEntities.StatisticsReport{},
// api
&apiEntities.ApiUser{},
&apiEntities.ApiCall{},

View File

@@ -912,7 +912,7 @@ func (s *ApiApplicationServiceImpl) DecryptParams(ctx context.Context, userID st
// GetFormConfig 获取指定API的表单配置
func (s *ApiApplicationServiceImpl) GetFormConfig(ctx context.Context, apiCode string) (*dto.FormConfigResponse, error) {
// 调用领域服务获取表单配置
config, err := s.formConfigService.GetFormConfig(apiCode)
config, err := s.formConfigService.GetFormConfig(ctx, apiCode)
if err != nil {
s.logger.Error("获取表单配置失败", zap.String("api_code", apiCode), zap.Error(err))
return nil, err

View File

@@ -1,9 +1,11 @@
package services
import (
"context"
"reflect"
"strings"
"tyapi-server/internal/domains/api/dto"
product_services "tyapi-server/internal/domains/product/services"
)
// FormField 表单字段配置
@@ -26,21 +28,35 @@ type FormConfig struct {
// FormConfigService 表单配置服务接口
type FormConfigService interface {
GetFormConfig(apiCode string) (*FormConfig, error)
GetFormConfig(ctx context.Context, apiCode string) (*FormConfig, error)
}
// FormConfigServiceImpl 表单配置服务实现
type FormConfigServiceImpl struct{}
type FormConfigServiceImpl struct {
productManagementService *product_services.ProductManagementService
}
// NewFormConfigService 创建表单配置服务
func NewFormConfigService() FormConfigService {
return &FormConfigServiceImpl{}
func NewFormConfigService(productManagementService *product_services.ProductManagementService) FormConfigService {
return &FormConfigServiceImpl{
productManagementService: productManagementService,
}
}
// NewFormConfigServiceWithoutDependencies 创建表单配置服务(不注入依赖,用于测试)
func NewFormConfigServiceWithoutDependencies() FormConfigService {
return &FormConfigServiceImpl{
productManagementService: nil,
}
}
// GetFormConfig 获取指定API的表单配置
func (s *FormConfigServiceImpl) GetFormConfig(apiCode string) (*FormConfig, error) {
func (s *FormConfigServiceImpl) GetFormConfig(ctx context.Context, apiCode string) (*FormConfig, error) {
// 根据API代码获取对应的DTO结构体
dtoStruct := s.getDTOStruct(apiCode)
dtoStruct, err := s.getDTOStruct(ctx, apiCode)
if err != nil {
return nil, err
}
if dtoStruct == nil {
return nil, nil
}
@@ -57,7 +73,7 @@ func (s *FormConfigServiceImpl) GetFormConfig(apiCode string) (*FormConfig, erro
}
// getDTOStruct 根据API代码获取对应的DTO结构体
func (s *FormConfigServiceImpl) getDTOStruct(apiCode string) interface{} {
func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string) (interface{}, error) {
// 建立API代码到DTO结构体的映射
dtoMap := map[string]interface{}{
"IVYZ9363": &dto.IVYZ9363Req{},
@@ -158,17 +174,16 @@ func (s *FormConfigServiceImpl) getDTOStruct(apiCode string) interface{} {
// 优先返回已配置的DTO
if dto, exists := dtoMap[apiCode]; exists {
return dto
return dto, nil
}
// 检查是否为通用组合包COMB开头且未单独配置
if len(apiCode) >= 4 && apiCode[:4] == "COMB" {
// 对于通用组合包,返回一个通用的空结构体,表示无需特定参数验证
// 因为组合包的参数验证由各个子处理器负责
return &struct{}{}
// 动态从数据库获取组合包的子产品信息并合并DTO
return s.mergeCombPackageDTOs(ctx, apiCode, dtoMap)
}
return nil
return nil, nil
}
// parseDTOFields 通过反射解析DTO结构体字段
@@ -451,3 +466,74 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s
return "请输入" + s.generateFieldLabel(jsonTag)
}
// mergeCombPackageDTOs 动态合并组合包的子产品DTO结构体
func (s *FormConfigServiceImpl) mergeCombPackageDTOs(ctx context.Context, apiCode string, dtoMap map[string]interface{}) (interface{}, error) {
// 如果productManagementService为nil测试环境返回空结构体
if s.productManagementService == nil {
return &struct{}{}, nil
}
// 1. 从数据库获取组合包产品信息
packageProduct, err := s.productManagementService.GetProductByCode(ctx, apiCode)
if err != nil {
// 如果获取失败,返回空结构体
return &struct{}{}, nil
}
// 2. 检查是否为组合包
if !packageProduct.IsPackage {
return &struct{}{}, nil
}
// 3. 获取组合包的所有子产品
packageItems, err := s.productManagementService.GetPackageItems(ctx, packageProduct.ID)
if err != nil || len(packageItems) == 0 {
return &struct{}{}, nil
}
// 4. 收集所有子产品的DTO字段并去重
// 使用map记录已存在的字段key为json tag
fieldMap := make(map[string]reflect.StructField)
for _, item := range packageItems {
subProductCode := item.Product.Code
// 在dtoMap中查找子产品的DTO
if subDTO, exists := dtoMap[subProductCode]; exists {
// 解析DTO的字段
dtoType := reflect.TypeOf(subDTO).Elem()
for i := 0; i < dtoType.NumField(); i++ {
field := dtoType.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag != "" && jsonTag != "-" {
// 去除omitempty等选项
jsonTag = strings.Split(jsonTag, ",")[0]
// 如果字段不存在或已存在但新字段有required标记则覆盖
if existingField, exists := fieldMap[jsonTag]; !exists {
fieldMap[jsonTag] = field
} else {
// 如果新字段有required且旧字段没有则用新字段
newValidate := field.Tag.Get("validate")
oldValidate := existingField.Tag.Get("validate")
if strings.Contains(newValidate, "required") && !strings.Contains(oldValidate, "required") {
fieldMap[jsonTag] = field
}
}
}
}
}
}
// 5. 动态创建结构体
fields := make([]reflect.StructField, 0, len(fieldMap))
for _, field := range fieldMap {
fields = append(fields, field)
}
// 创建结构体类型
structType := reflect.StructOf(fields)
// 创建并返回结构体实例
structValue := reflect.New(structType)
return structValue.Interface(), nil
}

View File

@@ -1,14 +1,15 @@
package services
import (
"context"
"testing"
)
func TestFormConfigService_GetFormConfig(t *testing.T) {
service := NewFormConfigService()
service := NewFormConfigServiceWithoutDependencies()
// 测试获取存在的API配置
config, err := service.GetFormConfig("IVYZ9363")
config, err := service.GetFormConfig(context.Background(), "IVYZ9363")
if err != nil {
t.Fatalf("获取表单配置失败: %v", err)
}
@@ -47,7 +48,7 @@ func TestFormConfigService_GetFormConfig(t *testing.T) {
}
// 测试获取不存在的API配置
config, err = service.GetFormConfig("NONEXISTENT")
config, err = service.GetFormConfig(context.Background(), "NONEXISTENT")
if err != nil {
t.Fatalf("获取不存在的API配置不应返回错误: %v", err)
}
@@ -58,9 +59,9 @@ func TestFormConfigService_GetFormConfig(t *testing.T) {
}
func TestFormConfigService_FieldValidation(t *testing.T) {
service := NewFormConfigService()
service := NewFormConfigServiceWithoutDependencies()
config, err := service.GetFormConfig("FLXG3D56")
config, err := service.GetFormConfig(context.Background(), "FLXG3D56")
if err != nil {
t.Fatalf("获取表单配置失败: %v", err)
}
@@ -96,9 +97,9 @@ func TestFormConfigService_FieldValidation(t *testing.T) {
}
func TestFormConfigService_FieldLabels(t *testing.T) {
service := NewFormConfigService()
service := NewFormConfigServiceWithoutDependencies()
config, err := service.GetFormConfig("IVYZ9363")
config, err := service.GetFormConfig(context.Background(), "IVYZ9363")
if err != nil {
t.Fatalf("获取表单配置失败: %v", err)
}