From 90d0324a1a5b4eea0d31526e1fd785c91503c790 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Thu, 20 Nov 2025 18:02:18 +0800 Subject: [PATCH] add ivyz9k2l --- internal/domains/api/dto/api_request_dto.go | 6 ++ .../api/services/api_request_service.go | 1 + .../api/services/form_config_service.go | 5 ++ .../processors/ivyz/ivyz9k2l_processor.go | 71 +++++++++++++++++++ .../shared/validator/custom_validators.go | 52 ++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 internal/domains/api/services/processors/ivyz/ivyz9k2l_processor.go diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index f0ff2fa..2b77f22 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -299,6 +299,12 @@ type IVYZ3A7FReq struct { IDCard string `json:"id_card" validate:"required,validIDCard"` } +type IVYZ9K2LReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + PhotoData string `json:"photo_data" validate:"omitempty,validBase64Image"` +} + type IVYZ9D2EReq struct { Name string `json:"name" validate:"required,min=1,validName"` IDCard string `json:"id_card" validate:"required,validIDCard"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index 4c58191..facda19 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -185,6 +185,7 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZ81NC": ivyz.ProcessIVYZ81NCRequest, "IVYZ6G7H": ivyz.ProcessIVYZ6G7HRequest, "IVYZ8I9J": ivyz.ProcessIVYZ8I9JRequest, + "IVYZ9K2L": ivyz.ProcessIVYZ9K2LRequest, // COMB系列处理器 - 只注册有自定义逻辑的组合包 "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index a346eb9..1e5e85c 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -155,6 +155,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "IVYZ3P9M": &dto.IVYZ3P9MReq{}, "IVYZ3A7F": &dto.IVYZ3A7FReq{}, "IVYZ9D2E": &dto.IVYZ9D2EReq{}, + "IVYZ9K2L": &dto.IVYZ9K2LReq{}, "DWBG7F3A": &dto.DWBG7F3AReq{}, "YYSY8F3A": &dto.YYSY8F3AReq{}, "QCXG9P1C": &dto.QCXG9P1CReq{}, @@ -368,6 +369,7 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string { "plate_type": "号牌类型", "vin_code": "车辆识别代号VIN码", "return_type": "返回类型", + "photo_data": "人脸图片", } if label, exists := labelMap[jsonTag]; exists { @@ -409,6 +411,7 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso "plate_type": "01", "vin_code": "LSGBF53M8DS123456", "return_type": "1", + "photo_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", } if example, exists := exampleMap[jsonTag]; exists { @@ -459,6 +462,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st "plate_type": "请选择号牌类型(01或02)", "vin_code": "请输入17位车辆识别代号VIN码", "return_type": "请选择返回类型", + "photo_data": "请输入base64编码的人脸图片(支持JPG、BMP、PNG格式)", } if placeholder, exists := placeholderMap[jsonTag]; exists { @@ -511,6 +515,7 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s "plate_type": "号牌类型:01-小型汽车;02-大型汽车(可选)", "vin_code": "请输入17位车辆识别代号VIN码(Vehicle Identification Number)", "return_type": "返回类型:1-专业和学校名称数据返回编码形式(默认);2-专业和学校名称数据返回中文名称", + "photo_data": "人脸图片(选填):base64编码的图片数据,仅支持JPG、BMP、PNG三种格式", } if desc, exists := descMap[jsonTag]; exists { diff --git a/internal/domains/api/services/processors/ivyz/ivyz9k2l_processor.go b/internal/domains/api/services/processors/ivyz/ivyz9k2l_processor.go new file mode 100644 index 0000000..ca49324 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz9k2l_processor.go @@ -0,0 +1,71 @@ +package ivyz + +import ( + "context" + "encoding/json" + "errors" + "strconv" + "time" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/westdex" +) + +// ProcessIVYZ9K2LRequest IVYZ9K2L API处理方法 - 身份认证三要素(人脸图像版) +func ProcessIVYZ9K2LRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ9K2LReq + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if err := deps.Validator.ValidateStruct(paramsDto); err != nil { + return nil, errors.Join(processors.ErrInvalidParam, err) + } + + // 加密姓名 + encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 加密身份证号 + encryptedIDCard, err := deps.WestDexService.Encrypt(paramsDto.IDCard) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 生成时间戳(毫秒) + timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) + + // 获取自定义编号(从 WestDexService 配置中获取 secret_id) + config := deps.WestDexService.GetConfig() + customNumber := config.SecretID + + // 构建请求数据 + reqData := map[string]interface{}{ + "timeStamp": timestamp, + "customNumber": customNumber, + "xM": encryptedName, + "gMSFZHM": encryptedIDCard, + } + + // 如果提供了人脸图片,添加到请求数据中 + if paramsDto.PhotoData != "" { + reqData["photoData"] = paramsDto.PhotoData + } + + respBytes, err := deps.WestDexService.CallAPI(ctx, "idCardThreeElements", reqData) + if err != nil { + switch { + case errors.Is(err, westdex.ErrDatasource): + return nil, errors.Join(processors.ErrDatasource, err) + case errors.Is(err, westdex.ErrSystem): + return nil, errors.Join(processors.ErrSystem, err) + default: + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/shared/validator/custom_validators.go b/internal/shared/validator/custom_validators.go index 0195d5f..6f047ca 100644 --- a/internal/shared/validator/custom_validators.go +++ b/internal/shared/validator/custom_validators.go @@ -1,6 +1,7 @@ package validator import ( + "encoding/base64" "fmt" "net/url" "regexp" @@ -93,6 +94,9 @@ func RegisterCustomValidators(validate *validator.Validate) { // 企业名称验证器 validate.RegisterValidation("enterprise_name", validateEnterpriseName) validate.RegisterValidation("validEnterpriseName", validateEnterpriseName) + + // Base64图片格式验证器(JPG、BMP、PNG) + validate.RegisterValidation("validBase64Image", validateBase64Image) } // validatePhone 手机号验证 @@ -938,3 +942,51 @@ func (bv *BusinessValidator) ValidateStruct(data interface{}) error { func (bv *BusinessValidator) ValidateField(field interface{}, tag string) error { return bv.validator.Var(field, tag) } + +// validateBase64Image Base64图片格式验证器(JPG、BMP、PNG) +func validateBase64Image(fl validator.FieldLevel) bool { + base64Str := fl.Field().String() + + // 如果为空,由 omitempty 处理 + if base64Str == "" { + return true + } + + // 去除首尾空格 + base64Str = strings.TrimSpace(base64Str) + if base64Str == "" { + return false + } + + // 解码 base64 字符串 + decoded, err := base64.StdEncoding.DecodeString(base64Str) + if err != nil { + return false + } + + // 检查数据长度(至少要有文件头) + if len(decoded) < 4 { + return false + } + + // 检查文件头,判断图片格式 + // JPG: FF D8 FF + // PNG: 89 50 4E 47 + // BMP: 42 4D (BM) + if len(decoded) >= 3 && decoded[0] == 0xFF && decoded[1] == 0xD8 && decoded[2] == 0xFF { + // JPG格式 + return true + } + + if len(decoded) >= 4 && decoded[0] == 0x89 && decoded[1] == 0x50 && decoded[2] == 0x4E && decoded[3] == 0x47 { + // PNG格式 + return true + } + + if len(decoded) >= 2 && decoded[0] == 0x42 && decoded[1] == 0x4D { + // BMP格式 + return true + } + + return false +}