From e61f03a2ddcd8a63989cbbc1df3075cfe93d9eae Mon Sep 17 00:00:00 2001 From: 18278715334 <18278715334@163.com> Date: Tue, 6 Jan 2026 16:37:31 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E6=9E=81=E5=85=89=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/domains/api/dto/api_request_dto.go | 77 +++++++++++++++++- .../api/services/api_request_service.go | 13 +++ .../api/services/form_config_service.go | 40 ++++++++++ .../processors/ivyz/ivyz0s0d_processor.go | 45 +++++++++++ .../processors/ivyz/ivyz1j7h_processor.go | 46 +++++++++++ .../processors/ivyz/ivyz9h2m_processor.go | 4 +- .../processors/qcxg/qcxg1h7y_processor.go | 47 +++++++++++ .../processors/qcxg/qcxg1u4u_processor.go | 49 ++++++++++++ .../processors/qcxg/qcxg2t6s_processor.go | 47 +++++++++++ .../processors/qcxg/qcxg3y6b_processor.go | 49 ++++++++++++ .../processors/qcxg/qcxg3z3l_processor.go | 48 +++++++++++ .../processors/qcxg/qcxg4i1z_processor.go | 46 +++++++++++ .../processors/qcxg/qcxgbg2q_processor.go | 80 +++++++++++++++++++ .../processors/qcxg/qcxggj3a_processor.go | 46 +++++++++++ .../processors/qcxg/qcxgjj2a_processor.go | 48 +++++++++++ .../processors/qcxg/qcxgp00w_processor.go | 45 +++++++++++ .../processors/qcxg/qcxgyts2_processor.go | 44 ++++++++++ .../external/jiguang/jiguang_service.go | 8 +- .../shared/validator/custom_validators.go | 67 ++++++++-------- 19 files changed, 808 insertions(+), 41 deletions(-) create mode 100644 internal/domains/api/services/processors/ivyz/ivyz0s0d_processor.go create mode 100644 internal/domains/api/services/processors/ivyz/ivyz1j7h_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg1h7y_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg1u4u_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg2t6s_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg3y6b_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg3z3l_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxg4i1z_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxgbg2q_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxggj3a_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxgjj2a_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxgp00w_processor.go create mode 100644 internal/domains/api/services/processors/qcxg/qcxgyts2_processor.go diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 8a53eca..9b76e79 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -275,6 +275,79 @@ type QCXG4896Req struct { type QCXG5F3AReq struct { IDCard string `json:"id_card" validate:"required,validIDCard"` } + +type QCXGGB2QReq struct { + PlateNo string `json:"plate_no" validate:"required"` + Name string `json:"name" validate:"required,min=1,validName"` + CarPlateType string `json:"carplate_type" validate:"required"` +} +type QCXGJJ2AReq struct { + VinCode string `json:"vin_code" validate:"required"` + EngineNumber string `json:"engine_number" validate:"omitempty"` + NoticeModel string `json:"notice_model" validate:"omitempty"` +} + +type QCXG4I1ZReq struct { + VinCode string `json:"vin_code" validate:"required"` +} + +type QCXG1H7YReq struct { + VinCode string `json:"vin_code" validate:"required"` + PlateNo string `json:"plate_no" validate:"required"` +} + +type QCXG1U4UReq struct { + VinCode string `json:"vin_code" validate:"required"` + PlateNo string `json:"plate_no" validate:"omitempty"` + ReturnURL string `json:"return_url" validate:"required,validReturnURL"` + ImageURL string `json:"image_url" validate:"omitempty,url"` + RegURL string `json:"reg_url" validate:"omitempty,url"` + EngineNumber string `json:"engine_number" validate:"omitempty"` +} + +type IVYZ0S0DReq struct { + IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` +} + +type IVYZ1J7HReq struct { + PlateNo string `json:"plate_no" validate:"required"` + Name string `json:"name" validate:"required,min=1,validName"` + CarPlateType string `json:"carplate_type" validate:"required"` +} + +type QCXG3Z3LReq struct { + VinCode string `json:"vin_code" validate:"required"` + ReturnURL string `json:"return_url" validate:"required,validReturnURL"` + ImageURL string `json:"image_url" validate:"omitempty,url"` + PlateNo string `json:"plate_no" validate:"omitempty"` + EngineNumber string `json:"engine_number" validate:"omitempty"` +} + +type QCXG2T6SReq struct { + VinCode string `json:"vin_code" validate:"required"` + PlateNo string `json:"plate_no" validate:"omitempty"` + ReturnURL string `json:"return_url" validate:"required,validReturnURL"` + ImageURL string `json:"image_url" validate:"required,url"` +} + +type QCXGYTS2Req struct { + VinCode string `json:"vin_code" validate:"omitempty"` + PlateNo string `json:"plate_no" validate:"omitempty"` + Name string `json:"name" validate:"required,min=1,validName"` +} + +type QCXGGJ3AReq struct { + VinCode string `json:"vin_code" validate:"required"` +} + +type QCXGP00WReq struct { + VinCode string `json:"vin_code" validate:"required"` + PlateNo string `json:"plate_no" validate:"required"` + ReturnURL string `json:"return_url" validate:"required,validReturnURL"` + VlPhotoData string `json:"vlphoto_data" validate:"required,validBase64Image"` +} + type QCXG4D2EReq struct { IDCard string `json:"id_card" validate:"required,validIDCard"` UserType string `json:"user_type" validate:"required,oneof=1 2 3"` @@ -755,8 +828,8 @@ type IVYZ6M8PReq struct { } type IVYZ9H2MReq struct { - IDNo string `json:"id_no" validate:"required,validIDCard"` - Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` } type YYSY9E4AReq struct { diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index fad25d2..c222593 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -216,6 +216,8 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZZQT3": ivyz.ProcessIVYZZQT3Request, //人脸比对V3 "IVYZBPQ2": ivyz.ProcessIVYZBPQ2Request, //人脸比对V2 "IVYZSFEL": ivyz.ProcessIVYZSFELRequest, //全国自然人人像三要素核验_V1 + "IVYZ0S0D": ivyz.ProcessIVYZ0S0DRequest, //劳动仲裁信息查询(个人版) + "IVYZ1J7H": ivyz.ProcessIVYZ1J7HRequest, //行驶证核查v2 // COMB系列处理器 - 只注册有自定义逻辑的组合包 "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode @@ -230,6 +232,17 @@ func registerAllProcessors(combService *comb.CombService) { "QCXG4896": qcxg.ProcessQCXG4896Request, "QCXG5F3A": qcxg.ProcessQCXG5F3ARequest, // 极光个人车辆查询 "QCXG4D2E": qcxg.ProcessQCXG4D2ERequest, // 极光名下车辆数量查询 + "QCXGJJ2A": qcxg.ProcessQCXGJJ2ARequest, // vin码查车辆信息(一对多) + "QCXGGJ3A": qcxg.ProcessQCXGGJ3ARequest, // 车辆vin码查询号牌 + "QCXGYTS2": qcxg.ProcessQCXGYTS2Request, // 车辆二要素核验v2 + "QCXGP00W": qcxg.ProcessQCXGP00WRequest, // 车辆出险详版查询 + "QCXGGB2Q": qcxg.ProcessQCXGGB2QRequest, // 车辆二要素核验V1 + "QCXG4I1Z": qcxg.ProcessQCXG4I1ZRequest, // 车辆过户详版查询 + "QCXG1H7Y": qcxg.ProcessQCXG1H7YRequest, // 车辆过户简版查询 + "QCXG3Z3L": qcxg.ProcessQCXG3Z3LRequest, // 车辆维保详细版查询 + "QCXG3Y6B": qcxg.ProcessQCXG3Y6BRequest, // 车辆维保简版查询 + "QCXG2T6S": qcxg.ProcessQCXG2T6SRequest, // 车辆里程记录(品牌查询) + "QCXG1U4U": qcxg.ProcessQCXG1U4URequest, // 车辆里程记录(混合查询) // DWBG系列处理器 - 多维报告 "DWBG6A2C": dwbg.ProcessDWBG6A2CRequest, diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 6e961d4..dc47cfa 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -212,6 +212,19 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "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{}, //车辆里程记录(混合查询) } // 优先返回已配置的DTO @@ -334,6 +347,7 @@ func (s *FormConfigServiceImpl) parseValidationRules(validateTag string) string case strings.HasPrefix(rule, "oneof="): values := strings.TrimPrefix(rule, "oneof=") frontendRules = append(frontendRules, "可选值: "+values) + } } @@ -363,6 +377,8 @@ func (s *FormConfigServiceImpl) getFieldType(fieldType reflect.Type, 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: @@ -416,6 +432,12 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string { "dir": "方向", "min_percent": "股权穿透比例下限", "max_percent": "股权穿透比例上限", + "engine_number": "发动机号码", + "notice_model": "车辆型号", + "vlphoto_data": "行驶证图片", + "carplate_type": "车辆号牌类型", + "image_url": "行驶证图片地址", + "reg_url": "车辆登记证图片地址", } if label, exists := labelMap[jsonTag]; exists { @@ -465,6 +487,12 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso "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", } if example, exists := exampleMap[jsonTag]; exists { @@ -523,6 +551,12 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st "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": "请输入车辆登记证图片地址", } if placeholder, exists := placeholderMap[jsonTag]; exists { @@ -583,6 +617,12 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s "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地址", } if desc, exists := descMap[jsonTag]; exists { diff --git a/internal/domains/api/services/processors/ivyz/ivyz0s0d_processor.go b/internal/domains/api/services/processors/ivyz/ivyz0s0d_processor.go new file mode 100644 index 0000000..80a638d --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz0s0d_processor.go @@ -0,0 +1,45 @@ +package ivyz + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessIVYZ0S0DRequest IVYZ0S0D API处理方法 - 劳动仲裁信息查询(个人版) +func ProcessIVYZ0S0DRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ0S0DReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "id": paramsDto.IDCard, + "name": paramsDto.Name, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "labor-arbitration-information", "labor-arbitration-information", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyz1j7h_processor.go b/internal/domains/api/services/processors/ivyz/ivyz1j7h_processor.go new file mode 100644 index 0000000..d01ce3f --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz1j7h_processor.go @@ -0,0 +1,46 @@ +package ivyz + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessIVYZ1J7HRequest IVYZ1J7H API处理方法 - 行驶证核查v2 +func ProcessIVYZ1J7HRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ1J7HReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "plate": paramsDto.PlateNo, + "plateType": paramsDto.CarPlateType, + "name": paramsDto.Name, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "labor-arbitration-information", "labor-arbitration-information", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyz9h2m_processor.go b/internal/domains/api/services/processors/ivyz/ivyz9h2m_processor.go index 77f3711..62c61aa 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz9h2m_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz9h2m_processor.go @@ -23,8 +23,8 @@ func ProcessIVYZ9H2MRequest(ctx context.Context, params []byte, deps *processors // 构建请求参数 reqData := map[string]interface{}{ - "idNo": paramsDto.IDNo, - "name": paramsDto.Name, + "id_no": paramsDto.IDCard, + "name": paramsDto.Name, } // 调用极光API diff --git a/internal/domains/api/services/processors/qcxg/qcxg1h7y_processor.go b/internal/domains/api/services/processors/qcxg/qcxg1h7y_processor.go new file mode 100644 index 0000000..e84015f --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg1h7y_processor.go @@ -0,0 +1,47 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG1H7YRequest QCXG1H7Y API处理方法 - 车辆过户简版查询 +func ProcessQCXG1H7YRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG1H7YReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "plateNumber": paramsDto.PlateNo, + } + + // 调用极光API + // apiCode: car-vin (用于请求头) + // apiPath: car/car-vin (用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "vehicle-transfer", "vehicle/transfer", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxg1u4u_processor.go b/internal/domains/api/services/processors/qcxg/qcxg1u4u_processor.go new file mode 100644 index 0000000..4d909c7 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg1u4u_processor.go @@ -0,0 +1,49 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG1U4URequest QCXG1U4U API处理方法 - 车辆里程记录(混合查询) +func ProcessQCXG1U4URequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG1U4UReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "licensePlate": paramsDto.PlateNo, + "callbackUrl": paramsDto.ReturnURL, + "imageUrl": paramsDto.ImageURL, + "regUrl": paramsDto.RegURL, + "engine": paramsDto.EngineNumber, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-mileage-b", "vehicle/car-mileage-b", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxg2t6s_processor.go b/internal/domains/api/services/processors/qcxg/qcxg2t6s_processor.go new file mode 100644 index 0000000..ac54ce8 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg2t6s_processor.go @@ -0,0 +1,47 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG2T6SRequest QCXG2T6S API处理方法 - 车辆里程记录(品牌查询) +func ProcessQCXG2T6SRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG2T6SReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "licensePlate": paramsDto.PlateNo, + "callbackUrl": paramsDto.ReturnURL, + "imageUrl": paramsDto.ImageURL, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-mileage-a", "vehicle/car-mileage-a", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxg3y6b_processor.go b/internal/domains/api/services/processors/qcxg/qcxg3y6b_processor.go new file mode 100644 index 0000000..dc6928f --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg3y6b_processor.go @@ -0,0 +1,49 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG3Y6BRequest QCXG3Y6B API处理方法 - 车辆维保简版查询 +func ProcessQCXG3Y6BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG1U4UReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "licensePlate": paramsDto.PlateNo, + "notifyUrl": paramsDto.ReturnURL, + "imageUrl": paramsDto.ImageURL, + "regUrl": paramsDto.RegURL, + "engine": paramsDto.EngineNumber, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-maintenance-info-v2", "vehicle/car-maintenance-info-v2", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxg3z3l_processor.go b/internal/domains/api/services/processors/qcxg/qcxg3z3l_processor.go new file mode 100644 index 0000000..a533157 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg3z3l_processor.go @@ -0,0 +1,48 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG3Z3LRequest QCXG3Z3L API处理方法 - 车辆维保详细版查询 +func ProcessQCXG3Z3LRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG3Z3LReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "notifyUrl": paramsDto.ReturnURL, + "imageUrl": paramsDto.ImageURL, + "plateNo": paramsDto.PlateNo, + "engine": paramsDto.EngineNumber, + } + + // 调用极光API + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-maintenance-info-v2", "vehicle/car-maintenance-info-v2", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxg4i1z_processor.go b/internal/domains/api/services/processors/qcxg/qcxg4i1z_processor.go new file mode 100644 index 0000000..dd4d206 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxg4i1z_processor.go @@ -0,0 +1,46 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXG4I1ZRequest QCXG4I1Z API处理方法 - 车辆过户详版查询 +func ProcessQCXG4I1ZRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXG4I1ZReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vim": paramsDto.VinCode, + } + + // 调用极光API + // apiCode: car-vin (用于请求头) + // apiPath: car/car-vin (用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "transfer-information", "vehicle/transfer-information", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxgbg2q_processor.go b/internal/domains/api/services/processors/qcxg/qcxgbg2q_processor.go new file mode 100644 index 0000000..45261eb --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxgbg2q_processor.go @@ -0,0 +1,80 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// CarPlateTypeMap 号牌类型代码到名称的映射 +var CarPlateTypeMap = map[string]string{ + "01": "大型汽车", + "02": "小型汽车", + "03": "使馆汽车", + "04": "领馆汽车", + "05": "境外汽车", + "06": "外籍汽车", + "07": "普通摩托车", + "08": "轻便摩托车", + "09": "使馆摩托车", + "10": "领馆摩托车", + "11": "境外摩托车", + "12": "外籍摩托车", + "13": "低速车", + "14": "拖拉机", + "15": "挂车", + "16": "教练汽车", + "17": "教练摩托车", + "20": "临时入境汽车", + "21": "临时入境摩托车", + "22": "临时行驶车", + "23": "警用汽车", + "24": "警用摩托", + "51": "新能源大型车", + "52": "新能源小型车", +} + +// getCarPlateTypeName 根据号牌类型代码获取中文名称 +func getCarPlateTypeName(code string) string { + if name, exists := CarPlateTypeMap[code]; exists { + return name + } + return code // 如果找不到,返回原值 +} + +// ProcessQCXGGB2QRequest QCXGGB2Q API处理方法 - 车辆二要素核验V1 +func ProcessQCXGGB2QRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGGB2QReq + 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) + } + + // 构建请求参数,将号牌类型代码转换为中文名称 + reqData := map[string]interface{}{ + "plateNumber": paramsDto.PlateNo, + "owner": paramsDto.Name, + "plateType": getCarPlateTypeName(paramsDto.CarPlateType), + } + + respBytes, err := deps.JiguangService.CallAPI(ctx, "vehicle-factor-two-auth", "vehicle/factor-two-auth", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxggj3a_processor.go b/internal/domains/api/services/processors/qcxg/qcxggj3a_processor.go new file mode 100644 index 0000000..005041c --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxggj3a_processor.go @@ -0,0 +1,46 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXGGJ3ARequest QCXGGJ3A API处理方法 - 车辆vin码查询号牌 +func ProcessQCXGGJ3ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGGJ3AReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + } + + // 调用极光API + // apiCode: car-vin (用于请求头) + // apiPath: car/car-vin (用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-vin", "vehicle/car-vin", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxgjj2a_processor.go b/internal/domains/api/services/processors/qcxg/qcxgjj2a_processor.go new file mode 100644 index 0000000..7de2c75 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxgjj2a_processor.go @@ -0,0 +1,48 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXGJJ2ARequest QCXGJJ2A API处理方法 - vin码查车辆信息(一对多) +func ProcessQCXGJJ2ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGJJ2AReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "engineNumber": paramsDto.EngineNumber, + "noticeModel": paramsDto.NoticeModel, + } + + // 调用极光API + // apiCode: carInfo-vin (用于请求头) + // apiPath: car/carInfo-vin (用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "carInfo-vin", "car/carInfo-vin", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxgp00w_processor.go b/internal/domains/api/services/processors/qcxg/qcxgp00w_processor.go new file mode 100644 index 0000000..caaf1e9 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxgp00w_processor.go @@ -0,0 +1,45 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXGP00WRequest QCXGP00W API处理方法 - 车辆出险详版查询 +func ProcessQCXGP00WRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGP00WReq + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "licenseNo": paramsDto.PlateNo, + "notifyUrl": paramsDto.ReturnURL, + "image": paramsDto.VlPhotoData, + } + + respBytes, err := deps.JiguangService.CallAPI(ctx, "car-accident-order-high", "car-accident-precision-order", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qcxg/qcxgyts2_processor.go b/internal/domains/api/services/processors/qcxg/qcxgyts2_processor.go new file mode 100644 index 0000000..0cc00b2 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxgyts2_processor.go @@ -0,0 +1,44 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/jiguang" +) + +// ProcessQCXGYTS2Request QCXGYTS2 API处理方法 - 车辆二要素核验v2 +func ProcessQCXGYTS2Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGYTS2Req + 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) + } + + // 构建请求参数 + reqData := map[string]interface{}{ + "vin": paramsDto.VinCode, + "plate": paramsDto.PlateNo, + "name": paramsDto.Name, + } + + respBytes, err := deps.JiguangService.CallAPI(ctx, "vehicle-person-and-vehicle-verification-v2", "vehicle/person-and-vehicle-verification-v2", reqData) + if err != nil { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 极光服务已经返回了 data 字段的 JSON,直接返回即可 + return respBytes, nil +} diff --git a/internal/infrastructure/external/jiguang/jiguang_service.go b/internal/infrastructure/external/jiguang/jiguang_service.go index 7d3549f..060e1fe 100644 --- a/internal/infrastructure/external/jiguang/jiguang_service.go +++ b/internal/infrastructure/external/jiguang/jiguang_service.go @@ -24,10 +24,10 @@ var ( // JiguangResponse 极光API响应结构 type JiguangResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` - OrderID string `json:"order_id"` - Data map[string]interface{} `json:"data"` + Code int `json:"code"` + Msg string `json:"msg"` + OrderID string `json:"order_id"` + Data interface{} `json:"data"` } // JiguangConfig 极光服务配置 diff --git a/internal/shared/validator/custom_validators.go b/internal/shared/validator/custom_validators.go index 6f047ca..80c8ea5 100644 --- a/internal/shared/validator/custom_validators.go +++ b/internal/shared/validator/custom_validators.go @@ -186,6 +186,7 @@ func validateIDCardChecksum(idCard string) bool { return byte(lastChar) == checksum } + // validatePrice 价格验证 func validatePrice(fl validator.FieldLevel) bool { price := fl.Field().Float() @@ -559,18 +560,18 @@ func validateReturnURL(fl validator.FieldLevel) bool { // validateEnterpriseName 企业名称验证器 func validateEnterpriseName(fl validator.FieldLevel) bool { enterpriseName := fl.Field().String() - + // 去除首尾空格 trimmedName := strings.TrimSpace(enterpriseName) if trimmedName == "" { return false } - + // 长度验证:2-100个字符 if len(trimmedName) < 2 || len(trimmedName) > 100 { return false } - + // 检查是否包含非法字符(允许中英文括号) invalidChars := []string{ "`", "~", "!", "@", "#", "$", "%", "^", "&", "*", @@ -581,13 +582,13 @@ func validateEnterpriseName(fl validator.FieldLevel) bool { return false } } - + // 必须包含至少一个中文字符或英文字母 hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName) if !hasValidChar { return false } - + // 验证企业名称的基本格式(支持各种类型的企业) // 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等 validSuffixes := []string{ @@ -598,7 +599,7 @@ func validateEnterpriseName(fl validator.FieldLevel) bool { "Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise", } - + // 检查是否以合法的企业类型结尾(不强制要求,因为有些企业名称可能没有标准后缀) // 但如果有后缀,必须是合法的 hasValidSuffix := false @@ -613,7 +614,7 @@ func validateEnterpriseName(fl validator.FieldLevel) bool { break } } - + // 如果名称中包含常见的企业类型关键词,则必须是合法的后缀 enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"} containsKeyword := false @@ -623,12 +624,12 @@ func validateEnterpriseName(fl validator.FieldLevel) bool { break } } - + // 如果包含企业关键词但没有合法后缀,则验证失败 if containsKeyword && !hasValidSuffix { return false } - + return true } @@ -657,7 +658,7 @@ func ValidatePassword(password string) error { hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password) hasLower := regexp.MustCompile(`[a-z]`).MatchString(password) hasDigit := regexp.MustCompile(`\d`).MatchString(password) - + if !hasUpper { return fmt.Errorf("密码必须包含大写字母") } @@ -790,7 +791,7 @@ func ValidateRequired(value interface{}, fieldName string) error { if value == nil { return fmt.Errorf("%s不能为空", fieldName) } - + switch v := value.(type) { case string: if strings.TrimSpace(v) == "" { @@ -801,7 +802,7 @@ func ValidateRequired(value interface{}, fieldName string) error { return fmt.Errorf("%s不能为空", fieldName) } } - + return nil } @@ -821,7 +822,7 @@ func ValidateSliceNotEmpty(slice interface{}, fieldName string) error { if slice == nil { return fmt.Errorf("%s不能为空", fieldName) } - + switch v := slice.(type) { case []string: if len(v) == 0 { @@ -832,7 +833,7 @@ func ValidateSliceNotEmpty(slice interface{}, fieldName string) error { return fmt.Errorf("%s不能为空", fieldName) } } - + return nil } @@ -841,13 +842,13 @@ func ValidateEnterpriseName(enterpriseName string) error { if enterpriseName == "" { return fmt.Errorf("企业名称不能为空") } - + // 去除首尾空格 trimmedName := strings.TrimSpace(enterpriseName) if trimmedName == "" { return fmt.Errorf("企业名称不能为空") } - + // 长度验证:2-100个字符 if len(trimmedName) < 2 { return fmt.Errorf("企业名称长度不能少于2个字符") @@ -855,7 +856,7 @@ func ValidateEnterpriseName(enterpriseName string) error { if len(trimmedName) > 100 { return fmt.Errorf("企业名称长度不能超过100个字符") } - + // 检查是否包含非法字符(允许中英文括号) invalidChars := []string{ "`", "~", "!", "@", "#", "$", "%", "^", "&", "*", @@ -866,13 +867,13 @@ func ValidateEnterpriseName(enterpriseName string) error { return fmt.Errorf("企业名称不能包含特殊字符: %s", char) } } - + // 必须包含至少一个中文字符或英文字母 hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName) if !hasValidChar { return fmt.Errorf("企业名称必须包含中文字符或英文字母") } - + // 验证企业名称的基本格式(支持各种类型的企业) // 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等 validSuffixes := []string{ @@ -883,7 +884,7 @@ func ValidateEnterpriseName(enterpriseName string) error { "Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise", } - + // 检查是否以合法的企业类型结尾 hasValidSuffix := false for _, suffix := range validSuffixes { @@ -897,7 +898,7 @@ func ValidateEnterpriseName(enterpriseName string) error { break } } - + // 如果名称中包含常见的企业类型关键词,则必须是合法的后缀 enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"} containsKeyword := false @@ -907,12 +908,12 @@ func ValidateEnterpriseName(enterpriseName string) error { break } } - + // 如果包含企业关键词但没有合法后缀,则验证失败 if containsKeyword && !hasValidSuffix { return fmt.Errorf("企业名称格式不正确,请使用标准的企业类型后缀(如:有限公司、工作室等)") } - + return nil } @@ -922,9 +923,9 @@ func ValidateEnterpriseName(enterpriseName string) error { func NewBusinessValidator() *BusinessValidator { // 确保全局校验器已初始化 InitGlobalValidator() - + return &BusinessValidator{ - validator: GetGlobalValidator(), // 使用全局校验器 + validator: GetGlobalValidator(), // 使用全局校验器 } } @@ -946,29 +947,29 @@ func (bv *BusinessValidator) ValidateField(field interface{}, tag string) error // 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 @@ -977,16 +978,16 @@ func validateBase64Image(fl validator.FieldLevel) bool { // 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 }