Files
tyapi-server/internal/domains/api/services/form_config_service.go
2025-12-09 14:42:32 +08:00

636 lines
23 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package services
import (
"context"
"reflect"
"strings"
"tyapi-server/internal/domains/api/dto"
product_services "tyapi-server/internal/domains/product/services"
)
// FormField 表单字段配置
type FormField struct {
Name string `json:"name"`
Label string `json:"label"`
Type string `json:"type"`
Required bool `json:"required"`
Validation string `json:"validation"`
Description string `json:"description"`
Example string `json:"example"`
Placeholder string `json:"placeholder"`
}
// FormConfig 表单配置
type FormConfig struct {
ApiCode string `json:"api_code"`
Fields []FormField `json:"fields"`
}
// FormConfigService 表单配置服务接口
type FormConfigService interface {
GetFormConfig(ctx context.Context, apiCode string) (*FormConfig, error)
}
// FormConfigServiceImpl 表单配置服务实现
type FormConfigServiceImpl struct {
productManagementService *product_services.ProductManagementService
}
// NewFormConfigService 创建表单配置服务
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(ctx context.Context, apiCode string) (*FormConfig, error) {
// 根据API代码获取对应的DTO结构体
dtoStruct, err := s.getDTOStruct(ctx, apiCode)
if err != nil {
return nil, err
}
if dtoStruct == nil {
return nil, nil
}
// 通过反射解析结构体字段
fields := s.parseDTOFields(dtoStruct)
config := &FormConfig{
ApiCode: apiCode,
Fields: fields,
}
return config, nil
}
// getDTOStruct 根据API代码获取对应的DTO结构体
func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string) (interface{}, error) {
// 建立API代码到DTO结构体的映射
dtoMap := map[string]interface{}{
"IVYZ9363": &dto.IVYZ9363Req{},
"IVYZ385E": &dto.IVYZ385EReq{},
"IVYZ5733": &dto.IVYZ5733Req{},
"FLXG3D56": &dto.FLXG3D56Req{},
"FLXG75FE": &dto.FLXG75FEReq{},
"FLXG0V3B": &dto.FLXG0V3BReq{},
"FLXG0V4B": &dto.FLXG0V4BReq{},
"FLXG54F5": &dto.FLXG54F5Req{},
"FLXG162A": &dto.FLXG162AReq{},
"FLXG0687": &dto.FLXG0687Req{},
"FLXGBC21": &dto.FLXGBC21Req{},
"FLXG970F": &dto.FLXG970FReq{},
"FLXG5876": &dto.FLXG5876Req{},
"FLXG9687": &dto.FLXG9687Req{},
"FLXGC9D1": &dto.FLXGC9D1Req{},
"FLXGCA3D": &dto.FLXGCA3DReq{},
"FLXGDEC7": &dto.FLXGDEC7Req{},
"JRZQ0A03": &dto.JRZQ0A03Req{},
"JRZQ4AA8": &dto.JRZQ4AA8Req{},
"JRZQ8203": &dto.JRZQ8203Req{},
"JRZQDCBE": &dto.JRZQDCBEReq{},
"QYGL2ACD": &dto.QYGL2ACDReq{},
"QYGL6F2D": &dto.QYGL6F2DReq{},
"QYGL45BD": &dto.QYGL45BDReq{},
"QYGL8261": &dto.QYGL8261Req{},
"QYGL8271": &dto.QYGL8271Req{},
"QYGLB4C0": &dto.QYGLB4C0Req{},
"QYGL23T7": &dto.QYGL23T7Req{},
"QYGL5A3C": &dto.QYGL5A3CReq{},
"QYGL8B4D": &dto.QYGL8B4DReq{},
"QYGL9E2F": &dto.QYGL9E2FReq{},
"QYGL7C1A": &dto.QYGL7C1AReq{},
"QYGL3F8E": &dto.QYGL3F8EReq{},
"YYSY4B37": &dto.YYSY4B37Req{},
"YYSY4B21": &dto.YYSY4B21Req{},
"YYSY6F2E": &dto.YYSY6F2EReq{},
"YYSY09CD": &dto.YYSY09CDReq{},
"IVYZ0B03": &dto.IVYZ0B03Req{},
"YYSYBE08": &dto.YYSYBE08Req{},
"YYSYD50F": &dto.YYSYD50FReq{},
"YYSYF7DB": &dto.YYSYF7DBReq{},
"IVYZ9A2B": &dto.IVYZ9A2BReq{},
"IVYZ7F2A": &dto.IVYZ7F2AReq{},
"IVYZ4E8B": &dto.IVYZ4E8BReq{},
"IVYZ1C9D": &dto.IVYZ1C9DReq{},
"IVYZGZ08": &dto.IVYZGZ08Req{},
"FLXG8A3F": &dto.FLXG8A3FReq{},
"FLXG5B2E": &dto.FLXG5B2EReq{},
"COMB298Y": &dto.COMB298YReq{},
"COMB86PM": &dto.COMB86PMReq{},
"QCXG7A2B": &dto.QCXG7A2BReq{},
"COMENT01": &dto.COMENT01Req{},
"JRZQ09J8": &dto.JRZQ09J8Req{},
"FLXGDEA8": &dto.FLXGDEA8Req{},
"FLXGDEA9": &dto.FLXGDEA9Req{},
"JRZQ1D09": &dto.JRZQ1D09Req{},
"IVYZ2A8B": &dto.IVYZ2A8BReq{},
"IVYZ7C9D": &dto.IVYZ7C9DReq{},
"IVYZ5E3F": &dto.IVYZ5E3FReq{},
"YYSY4F2E": &dto.YYSY4F2EReq{},
"YYSY8B1C": &dto.YYSY8B1CReq{},
"YYSY6D9A": &dto.YYSY6D9AReq{},
"YYSY3E7F": &dto.YYSY3E7FReq{},
"FLXG5A3B": &dto.FLXG5A3BReq{},
"FLXG9C1D": &dto.FLXG9C1DReq{},
"FLXG2E8F": &dto.FLXG2E8FReq{},
"JRZQ3C7B": &dto.JRZQ3C7BReq{},
"JRZQ8A2D": &dto.JRZQ8A2DReq{},
"JRZQ5E9F": &dto.JRZQ5E9FReq{},
"JRZQ4B6C": &dto.JRZQ4B6CReq{},
"JRZQ7F1A": &dto.JRZQ7F1AReq{},
"DWBG6A2C": &dto.DWBG6A2CReq{},
"DWBG8B4D": &dto.DWBG8B4DReq{},
"FLXG8B4D": &dto.FLXG8B4DReq{},
"IVYZ81NC": &dto.IVYZ81NCReq{},
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
"IVYZ9D2E": &dto.IVYZ9D2EReq{},
"IVYZ9K2L": &dto.IVYZ9K2LReq{},
"DWBG7F3A": &dto.DWBG7F3AReq{},
"YYSY8F3A": &dto.YYSY8F3AReq{},
"QCXG9P1C": &dto.QCXG9P1CReq{},
"JRZQ9E2A": &dto.JRZQ9E2AReq{},
"YYSY9A1B": &dto.YYSY9A1BReq{},
"YYSY8C2D": &dto.YYSY8C2DReq{},
"YYSY7D3E": &dto.YYSY7D3EReq{},
"YYSY9E4A": &dto.YYSY9E4AReq{},
"JRZQ6F2A": &dto.JRZQ6F2AReq{},
"JRZQ8B3C": &dto.JRZQ8B3CReq{},
"JRZQ9D4E": &dto.JRZQ9D4EReq{},
"FLXG7E8F": &dto.FLXG7E8FReq{},
"QYGL5F6A": &dto.QYGL5F6AReq{},
"IVYZ6G7H": &dto.IVYZ6G7HReq{},
"IVYZ8I9J": &dto.IVYZ8I9JReq{},
"JRZQ0L85": &dto.JRZQ0L85Req{},
"COMBHZY2": &dto.COMBHZY2Req{}, // 自此无imp11.28
"QCXG8A3D": &dto.QCXG8A3DReq{},
"QCXG6B4E": &dto.QCXG6B4EReq{},
"QYGL2B5C": &dto.QYGL2B5CReq{},
"JRZQ2F8A": &dto.JRZQ2F8AReq{},
"JRZQ1E7B": &dto.JRZQ1E7BReq{},
"JRZQ3C9R": &dto.JRZQ3C9RReq{},
"IVYZ2C1P": &dto.IVYZ2C1PReq{},
"YYSY9F1B": &dto.YYSY9F1BReq{},
"YYSY6F2B": &dto.YYSY6F2BReq{},
"QYGL6S1B": &dto.QYGL6S1BReq{},
"JRZQ0B6Y": &dto.JRZQ0B6YReq{},
"JRZQ9A1W": &dto.JRZQ9A1WReq{},
"JRZQ8F7C": &dto.JRZQ8F7CReq{}, //综合多头
"FLXGK5D2": &dto.FLXGK5D2Req{},
"FLXG3A9B": &dto.FLXG3A9BReq{},
"IVYZP2Q6": &dto.IVYZP2Q6Req{},
"JRZQ1W4X": &dto.JRZQ1W4XReq{}, //全景档案
"QYGL2S0W": &dto.QYGL2S0WReq{}, //失信被执行企业个人查询
"QYGL9T1Q": &dto.QYGL9T1QReq{}, //全国企业借贷意向验证查询_V1
"QYGL5A9T": &dto.QYGL5A9TReq{}, //全国企业各类工商风险统计数量查询
"JRZQ3P01": &dto.JRZQ3P01Req{}, //天远风控决策
"JRZQ3AG6": &dto.JRZQ3AG6Req{}, //轻松查公积
"IVYZ2B2T": &dto.IVYZ2B2TReq{}, //能力资质核验(学历)
"IVYZ5A9O": &dto.IVYZ5A9tReq{}, //全国⾃然⼈⻛险评估评分模型
"IVYZ6M8P": &dto.IVYZ6M8PReq{}, //职业资格证书
"QYGL5CMP": &dto.QYGL5CMPReq{}, //企业五要素验证
}
// 优先返回已配置的DTO
if dto, exists := dtoMap[apiCode]; exists {
return dto, nil
}
// 检查是否为通用组合包COMB开头且未单独配置
if len(apiCode) >= 4 && apiCode[:4] == "COMB" {
// 动态从数据库获取组合包的子产品信息并合并DTO
return s.mergeCombPackageDTOs(ctx, apiCode, dtoMap)
}
return nil, nil
}
// parseDTOFields 通过反射解析DTO结构体字段
func (s *FormConfigServiceImpl) parseDTOFields(dtoStruct interface{}) []FormField {
var fields []FormField
t := reflect.TypeOf(dtoStruct).Elem()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 获取JSON标签
jsonTag := field.Tag.Get("json")
if jsonTag == "" {
continue
}
// 获取验证标签
validateTag := field.Tag.Get("validate")
// 解析验证规则
required := strings.Contains(validateTag, "required")
validation := s.parseValidationRules(validateTag)
// 根据字段类型和验证规则生成前端字段类型
fieldType := s.getFieldType(field.Type, validation)
// 生成字段标签(将下划线转换为中文)
label := s.generateFieldLabel(jsonTag)
// 生成示例值
example := s.generateExampleValue(field.Type, jsonTag)
// 生成占位符
placeholder := s.generatePlaceholder(jsonTag, fieldType)
// 生成字段描述
description := s.generateDescription(jsonTag, validation)
formField := FormField{
Name: jsonTag,
Label: label,
Type: fieldType,
Required: required,
Validation: validation,
Description: description,
Example: example,
Placeholder: placeholder,
}
fields = append(fields, formField)
}
return fields
}
// parseValidationRules 解析验证规则
func (s *FormConfigServiceImpl) parseValidationRules(validateTag string) string {
if validateTag == "" {
return ""
}
// 将验证规则转换为前端可理解的格式
rules := strings.Split(validateTag, ",")
var frontendRules []string
for _, rule := range rules {
rule = strings.TrimSpace(rule)
switch {
case rule == "required":
frontendRules = append(frontendRules, "必填")
case strings.HasPrefix(rule, "min="):
min := strings.TrimPrefix(rule, "min=")
frontendRules = append(frontendRules, "最小长度"+min)
case strings.HasPrefix(rule, "max="):
max := strings.TrimPrefix(rule, "max=")
frontendRules = append(frontendRules, "最大长度"+max)
case rule == "validMobileNo":
frontendRules = append(frontendRules, "手机号格式")
case rule == "validIDCard":
frontendRules = append(frontendRules, "身份证格式")
case rule == "validName":
frontendRules = append(frontendRules, "姓名格式")
case rule == "validUSCI":
frontendRules = append(frontendRules, "统一社会信用代码格式")
case rule == "validEnterpriseName" || rule == "enterprise_name":
frontendRules = append(frontendRules, "企业名称格式")
case rule == "validBankCard":
frontendRules = append(frontendRules, "银行卡号格式")
case rule == "validDate":
frontendRules = append(frontendRules, "日期格式")
case rule == "validAuthDate":
frontendRules = append(frontendRules, "授权日期格式")
case rule == "validTimeRange":
frontendRules = append(frontendRules, "时间范围格式")
case rule == "validMobileType":
frontendRules = append(frontendRules, "手机类型")
case rule == "validUniqueID":
frontendRules = append(frontendRules, "唯一标识格式")
case rule == "validReturnURL":
frontendRules = append(frontendRules, "返回链接格式")
case rule == "validAuthorizationURL":
frontendRules = append(frontendRules, "授权链接格式")
case rule == "validBase64Image":
frontendRules = append(frontendRules, "Base64图片格式JPG、BMP、PNG")
case strings.HasPrefix(rule, "oneof="):
values := strings.TrimPrefix(rule, "oneof=")
frontendRules = append(frontendRules, "可选值: "+values)
}
}
return strings.Join(frontendRules, "、")
}
// getFieldType 根据字段类型和验证规则确定前端字段类型
func (s *FormConfigServiceImpl) getFieldType(fieldType reflect.Type, validation string) string {
switch fieldType.Kind() {
case reflect.String:
if strings.Contains(validation, "手机号") {
return "tel"
} else if strings.Contains(validation, "身份证") {
return "text"
} else if strings.Contains(validation, "姓名") {
return "text"
} else if strings.Contains(validation, "时间范围格式") {
return "text" // time_range是HH:MM-HH:MM格式使用文本输入
} else if strings.Contains(validation, "授权日期格式") {
return "text" // auth_date是YYYYMMDD-YYYYMMDD格式使用文本输入
} else if strings.Contains(validation, "日期") {
return "date"
} else if strings.Contains(validation, "链接") {
return "url"
} else if strings.Contains(validation, "可选值") {
return "select"
} else if strings.Contains(validation, "Base64图片") || strings.Contains(validation, "base64") {
return "textarea"
}
return "text"
case reflect.Int64:
return "number"
case reflect.Bool:
return "checkbox"
default:
return "text"
}
}
// generateFieldLabel 生成字段标签
func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
// 将下划线命名转换为中文标签
labelMap := map[string]string{
"mobile_no": "手机号码",
"id_card": "身份证号",
"name": "姓名",
"man_name": "男方姓名",
"woman_name": "女方姓名",
"man_id_card": "男方身份证",
"woman_id_card": "女方身份证",
"ent_name": "企业名称",
"legal_person": "法人姓名",
"ent_code": "企业代码",
"auth_date": "授权日期",
"time_range": "时间范围",
"authorized": "是否授权",
"authorization_url": "授权链接",
"unique_id": "唯一标识",
"return_url": "返回链接",
"mobile_type": "手机类型",
"start_date": "开始日期",
"years": "年数",
"bank_card": "银行卡号",
"user_type": "关系类型",
"vehicle_type": "车辆类型",
"page_num": "页码",
"page_size": "每页数量",
"use_scenario": "使用场景",
"auth_authorize_file_code": "授权文件编码",
"plate_no": "车牌号",
"plate_type": "号牌类型",
"vin_code": "车辆识别代号VIN码",
"return_type": "返回类型",
"photo_data": "人脸图片",
"owner_type": "企业主类型",
"type": "查询类型",
"query_reason_id": "查询原因ID",
}
if label, exists := labelMap[jsonTag]; exists {
return label
}
// 如果没有预定义,尝试自动转换
return strings.ReplaceAll(jsonTag, "_", " ")
}
// generateExampleValue 生成示例值
func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jsonTag string) string {
exampleMap := map[string]string{
"mobile_no": "13800138000",
"id_card": "110101199001011234",
"name": "张三",
"man_name": "张三",
"woman_name": "李四",
"ent_name": "示例企业有限公司",
"legal_person": "王五",
"ent_code": "91110000123456789X",
"auth_date": "20240101-20241231",
"time_range": "09:00-18:00",
"authorized": "1",
"years": "5",
"bank_card": "6222021234567890123",
"mobile_type": "移动",
"start_date": "2024-01-01",
"unique_id": "UNIQUE123456",
"return_url": "https://example.com/return",
"authorization_url": "https://example.com/auth20250101.pdf 注意请不要使用示例链接示例链接仅作为参考格式。必须为实际的被查询人授权具有法律效益的授权书文件链接如访问不到或为不实授权书将追究责任。协议必须为http https",
"user_type": "1",
"vehicle_type": "0",
"page_num": "1",
"page_size": "10",
"use_scenario": "1",
"auth_authorize_file_code": "AUTH123456",
"plate_no": "京A12345",
"plate_type": "01",
"vin_code": "LSGBF53M8DS123456",
"return_type": "1",
"photo_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"ownerType": "1",
"type": "per",
"query_reason_id": "1",
}
if example, exists := exampleMap[jsonTag]; exists {
return example
}
// 根据字段类型生成默认示例
switch fieldType.Kind() {
case reflect.String:
return "示例值"
case reflect.Int64:
return "123"
case reflect.Bool:
return "true"
default:
return "示例值"
}
}
// generatePlaceholder 生成占位符
func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType string) string {
placeholderMap := map[string]string{
"mobile_no": "请输入11位手机号码",
"id_card": "请输入18位身份证号码",
"name": "请输入真实姓名",
"man_name": "请输入男方真实姓名",
"woman_name": "请输入女方真实姓名",
"ent_name": "请输入企业全称",
"legal_person": "请输入法人真实姓名",
"ent_code": "请输入统一社会信用代码",
"auth_date": "请输入授权日期范围YYYYMMDD-YYYYMMDD",
"time_range": "请输入时间范围HH:MM-HH:MM",
"authorized": "请选择是否授权",
"years": "请输入查询年数0-100",
"bank_card": "请输入银行卡号",
"mobile_type": "请选择手机类型",
"start_date": "请选择开始日期",
"unique_id": "请输入唯一标识",
"return_url": "请输入返回链接",
"authorization_url": "请输入授权链接",
"user_type": "请选择关系类型",
"vehicle_type": "请选择车辆类型",
"page_num": "请输入页码",
"page_size": "请输入每页数量1-100",
"use_scenario": "请选择使用场景",
"auth_authorize_file_code": "请输入授权文件编码",
"plate_no": "请输入车牌号",
"plate_type": "请选择号牌类型01或02",
"vin_code": "请输入17位车辆识别代号VIN码",
"return_type": "请选择返回类型",
"photo_data": "请输入base64编码的人脸图片支持JPG、BMP、PNG格式",
"ownerType": "请选择企业主类型",
"type": "请选择查询类型",
"query_reason_id": "请选择查询原因ID",
}
if placeholder, exists := placeholderMap[jsonTag]; exists {
return placeholder
}
// 根据字段类型生成默认占位符
switch fieldType {
case "tel":
return "请输入电话号码"
case "date":
return "请选择日期"
case "url":
return "请输入链接地址"
case "number":
return "请输入数字"
default:
return "请输入" + s.generateFieldLabel(jsonTag)
}
}
// generateDescription 生成字段描述
func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation string) string {
descMap := map[string]string{
"mobile_no": "请输入11位手机号码",
"id_card": "请输入18位身份证号码",
"name": "请输入真实姓名",
"man_name": "请输入男方真实姓名",
"woman_name": "请输入女方真实姓名",
"ent_name": "请输入企业全称",
"legal_person": "请输入法人真实姓名",
"ent_code": "请输入统一社会信用代码",
"auth_date": "请输入授权日期范围格式YYYYMMDD-YYYYMMDD且日期范围必须包括今天",
"time_range": "请输入时间范围格式HH:MM-HH:MM",
"authorized": "请输入是否授权0-未授权1-已授权",
"years": "请输入查询年数0-100",
"bank_card": "请输入银行卡号",
"mobile_type": "请选择手机类型",
"start_date": "请选择开始日期",
"unique_id": "请输入唯一标识",
"return_url": "请输入返回链接",
"authorization_url": "请输入授权链接",
"user_type": "关系类型1-ETC开户人2-车辆所有人3-ETC经办人默认1-ETC开户人",
"vehicle_type": "车辆类型0-客车1-货车2-全部(默认查全部)",
"page_num": "请输入页码从1开始",
"page_size": "请输入每页数量范围1-100",
"use_scenario": "使用场景1-信贷审核2-保险评估3-招聘背景调查4-其他业务场景99-其他",
"auth_authorize_file_code": "请输入授权文件编码",
"plate_no": "请输入车牌号",
"plate_type": "号牌类型01-小型汽车02-大型汽车(可选)",
"vin_code": "请输入17位车辆识别代号VIN码Vehicle Identification Number",
"return_type": "返回类型1-专业和学校名称数据返回编码形式默认2-专业和学校名称数据返回中文名称",
"photo_data": "人脸图片必填base64编码的图片数据仅支持JPG、BMP、PNG三种格式",
"owner_type": "企业主类型编码1-法定代表人2-主要人员3-自然人股东4-法定代表人及自然人股东5-其他",
"type": "查询类型per-人员ent-企业 ",
"query_reason_id": "查询原因ID1-授信审批2-贷中管理3-贷后管理4-异议处理5-担保查询6-租赁资质审查7-融资租赁审批8-借贷撮合查询9-保险审批10-资质审核11-风控审核12-企业背调",
}
if desc, exists := descMap[jsonTag]; exists {
return desc
}
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
}