Files
tyapi-server/internal/domains/api/services/form_config_service.go
2026-01-28 16:25:40 +08:00

732 lines
30 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{}, //
"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.IVYZ5A9OReq{}, //全国⾃然⼈⻛险评估评分模型
"IVYZ6M8P": &dto.IVYZ6M8PReq{}, //职业资格证书
"IVYZ9H2M": &dto.IVYZ9H2MReq{}, //极光个人婚姻查询V2版
"QYGL5CMP": &dto.QYGL5CMPReq{}, //企业五要素验证
"QCXG4896": &dto.QCXG4896Req{}, //网约车风险查询
"IVYZZQT3": &dto.IVYZZQT3Req{}, //人脸比对V3
"IVYZBPQ2": &dto.IVYZBPQ2Req{}, //人脸比对V2
"IVYZSFEL": &dto.IVYZSFELReq{}, //全国自然人人像三要素核验_V1
"QYGL66SL": &dto.QYGL66SLReq{}, //全国企业司法模型服务查询_V1
"QCXG5F3A": &dto.QCXG5F3AReq{}, //极光个人车辆查询
"QCXG4D2E": &dto.QCXG4D2EReq{}, //极光名下车辆数量查询
"QYGLP0HT": &dto.QYGLP0HTReq{}, //股权穿透
"QYGL2NAO": &dto.QYGL2naoReq{}, //股权变更
"QYGLNIO8": &dto.QYGLNIO8Req{}, //企业基本信息
"QYGL4B2E": &dto.QYGL5A3CReq{}, //税收违法
"QYGL7D9A": &dto.QYGL5A3CReq{}, //欠税公告
"IVYZ0S0D": &dto.IVYZ0S0DReq{}, //劳动仲裁信息查询(个人版)
"IVYZ1J7H": &dto.IVYZ1J7HReq{}, //行驶证核查v2
"QCXGJJ2A": &dto.QCXGJJ2AReq{}, //vin码查车辆信息(一对多)
"QCXGGJ3A": &dto.QCXGGJ3AReq{}, //车辆vin码查询号牌
"QCXGYTS2": &dto.QCXGYTS2Req{}, //车辆二要素核验v2
"QCXGP00W": &dto.QCXGP00WReq{}, //车辆出险详版查询
"QCXGGB2Q": &dto.QCXGGB2QReq{}, //车辆二要素核验V1
"QCXG4I1Z": &dto.QCXG4I1ZReq{}, //车辆过户详版查询
"QCXG1H7Y": &dto.QCXG1H7YReq{}, //车辆过户简版查询
"QCXG3Z3L": &dto.QCXG3Z3LReq{}, //车辆维保详细版查询
"QCXG3Y6B": &dto.QCXG1U4UReq{}, //车辆维保简版查询
"QCXG2T6S": &dto.QCXG2T6SReq{}, //车辆里程记录(品牌查询)
"QCXG1U4U": &dto.QCXG1U4UReq{}, //车辆里程记录(混合查询)
"JRZQO6L7": &dto.JRZQO6L7Req{}, //全国自然人经济特征评分模型v3 简版
"JRZQO7L1": &dto.JRZQO7L1Req{}, //全国自然人经济特征评分模型v4 详版
"JRZQS7G0": &dto.JRZQS7G0Req{}, //社保综合评分V1
"IVYZ9K7F": &dto.IVYZ9K7FReq{}, //身份证实名认证即时版
"YYSY3M8S": &dto.YYSY3M8SReq{}, //运营商二要素查询
"YYSYC4R9": &dto.YYSYC4R9Req{}, //运营商三要素详版查询
"YYSYH6D2": &dto.YYSYH6D2Req{}, //运营商三要素简版政务版查询
"YYSYP0T4": &dto.YYSYP0T4Req{}, //在网时长查询
"YYSYE7V5": &dto.YYSYE7V5Req{}, //手机在网状态查询
"YYSYS9W1": &dto.YYSYS9W1Req{}, //手机携号转网查询
"YYSYK8R3": &dto.YYSYK8R3Req{}, //手机空号检测查询
"YYSYF2T7": &dto.YYSYF2T7Req{}, //手机二次放号检测查询
"IVYZA1B3": &dto.IVYZA1B3Req{}, //公安三要素人脸识别
"IVYZX5QZ": &dto.IVYZX5QZReq{}, //活体识别
"IVYZN2P8": &dto.IVYZ9K7FReq{}, //身份证实名认证政务版
"YYSYH6F3": &dto.YYSYH6F3Req{}, //运营商三要素简版即时版查询
"IVYZX5Q2": &dto.IVYZX5Q2Req{}, //活体识别步骤二
"PDFG01GZ": &dto.PDFG01GZReq{}, //
"QYGL5S1I": &dto.QYGL5S1IReq{}, //企业司法涉诉V2
}
// 优先返回已配置的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"
} else if strings.Contains(validation, "图片地址") {
return "url"
}
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": "授权日期",
"date_range": "数据范围",
"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",
"flag": "层次",
"dir": "方向",
"min_percent": "股权穿透比例下限",
"max_percent": "股权穿透比例上限",
"engine_number": "发动机号码",
"notice_model": "车辆型号",
"vlphoto_data": "行驶证图片",
"carplate_type": "车辆号牌类型",
"image_url": "行驶证图片地址",
"reg_url": "车辆登记证图片地址",
"token": "token采集及获取结果时所使用的凭证有效期2个小时在此时效内应用侧可以发起采集请求重复的采集所触发的结果会被忽略和结果查询",
}
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",
"date_range": "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",
"flag": "4",
"dir": "down",
"min_percent": "0",
"max_percent": "1",
"engine_number": "1234567890",
"notice_model": "1",
"vlphoto_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"carplate_type": "01",
"image_url": "https://example.com/images/driving_license.jpg",
"reg_url": "https://example.com/images/vehicle_registration.jpg",
"token": "0fc79b80371f45e2ac1c693ef9136b24",
}
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",
"date_range": "请输入日期范围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",
"flag": "请输入层次最大4",
"dir": "请选择方向up-向上down-向下)",
"min_percent": "请输入股权穿透比例下限默认0",
"max_percent": "请输入股权穿透比例上限默认1",
"engine_number": "请输入发动机号码",
"notice_model": "请输入车辆型号",
"vlphoto_data": "请输入行驶证图片",
"carplate_type": "请选择车辆号牌类型01-大型汽车 02-小型汽车 03-使馆汽车 04-领馆汽车 05-境外汽车 06-外籍汽车 07-普通摩托车 08-轻便摩托车 09-使馆摩托车 10-领馆摩托车 11-境外摩托车 12-外籍摩托车 13-低速车 14-拖拉机 15-挂车 16-教练汽车 17-教练摩托车 20-临时入境汽车 21-临时入境摩托车 22-临时行驶车 23-警用汽车 24-警用摩托 51-新能源大型车 52-新能源小型车)",
"image_url": "请输入行驶证图片地址",
"reg_url": "请输入车辆登记证图片地址",
"token": "请输入token",
}
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且日期范围必须包括今天",
"date_range": "请输入日期范围格式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-企业背调",
"flag": "层次最大4",
"dir": "方向up-向上穿透down-向下穿透",
"min_percent": "股权穿透比例下限大于等于默认为0支持小数点后两位以小数指代百分比",
"max_percent": "股权穿透比例上限小于等于默认为1支持小数点后两位以小数指代百分比",
"engine_number": "发动机号码",
"notice_model": "车辆型号",
"vlphoto_data": "行驶证图片:base64编码的图片数据仅支持JPG、BMP、PNG三种格式",
"carplate_type": "车辆号牌类型01-大型汽车02-小型汽车03-使馆汽车04-领馆汽车05-境外汽车06-外籍汽车07-普通摩托车08-轻便摩托车09-使馆摩托车10-领馆摩托车11-境外摩托车12-外籍摩托车13-低速车14-拖拉机15-挂车16-教练汽车17-教练摩托车20-临时入境汽车21-临时入境摩托车22-临时行驶车23-警用汽车24-警用摩托51-新能源大型车52-新能源小型车",
"image_url": "行驶证图片地址必填请提供行驶证的图片URL地址",
"reg_url": "车辆登记证图片地址非必填请提供车辆登记证的图片URL地址",
"token": "token采集及获取结果时所使用的凭证有效期2个小时在此时效内应用侧可以发起采集请求重复的采集所触发的结果会被忽略和结果查询",
}
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
}