add 极光数据

This commit is contained in:
2026-01-06 16:37:31 +08:00
parent 4fcf370dcd
commit e61f03a2dd
19 changed files with 808 additions and 41 deletions

View File

@@ -275,6 +275,79 @@ type QCXG4896Req struct {
type QCXG5F3AReq struct { type QCXG5F3AReq struct {
IDCard string `json:"id_card" validate:"required,validIDCard"` 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 { type QCXG4D2EReq struct {
IDCard string `json:"id_card" validate:"required,validIDCard"` IDCard string `json:"id_card" validate:"required,validIDCard"`
UserType string `json:"user_type" validate:"required,oneof=1 2 3"` UserType string `json:"user_type" validate:"required,oneof=1 2 3"`
@@ -755,8 +828,8 @@ type IVYZ6M8PReq struct {
} }
type IVYZ9H2MReq struct { type IVYZ9H2MReq struct {
IDNo string `json:"id_no" validate:"required,validIDCard"` IDCard string `json:"id_card" validate:"required,validIDCard"`
Name string `json:"name" validate:"required,min=1,validName"` Name string `json:"name" validate:"required,min=1,validName"`
} }
type YYSY9E4AReq struct { type YYSY9E4AReq struct {

View File

@@ -216,6 +216,8 @@ func registerAllProcessors(combService *comb.CombService) {
"IVYZZQT3": ivyz.ProcessIVYZZQT3Request, //人脸比对V3 "IVYZZQT3": ivyz.ProcessIVYZZQT3Request, //人脸比对V3
"IVYZBPQ2": ivyz.ProcessIVYZBPQ2Request, //人脸比对V2 "IVYZBPQ2": ivyz.ProcessIVYZBPQ2Request, //人脸比对V2
"IVYZSFEL": ivyz.ProcessIVYZSFELRequest, //全国自然人人像三要素核验_V1 "IVYZSFEL": ivyz.ProcessIVYZSFELRequest, //全国自然人人像三要素核验_V1
"IVYZ0S0D": ivyz.ProcessIVYZ0S0DRequest, //劳动仲裁信息查询(个人版)
"IVYZ1J7H": ivyz.ProcessIVYZ1J7HRequest, //行驶证核查v2
// COMB系列处理器 - 只注册有自定义逻辑的组合包 // COMB系列处理器 - 只注册有自定义逻辑的组合包
"COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑重命名ApiCode "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑重命名ApiCode
@@ -230,6 +232,17 @@ func registerAllProcessors(combService *comb.CombService) {
"QCXG4896": qcxg.ProcessQCXG4896Request, "QCXG4896": qcxg.ProcessQCXG4896Request,
"QCXG5F3A": qcxg.ProcessQCXG5F3ARequest, // 极光个人车辆查询 "QCXG5F3A": qcxg.ProcessQCXG5F3ARequest, // 极光个人车辆查询
"QCXG4D2E": qcxg.ProcessQCXG4D2ERequest, // 极光名下车辆数量查询 "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系列处理器 - 多维报告 // DWBG系列处理器 - 多维报告
"DWBG6A2C": dwbg.ProcessDWBG6A2CRequest, "DWBG6A2C": dwbg.ProcessDWBG6A2CRequest,

View File

@@ -212,6 +212,19 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
"QYGLNIO8": &dto.QYGLNIO8Req{}, //企业基本信息 "QYGLNIO8": &dto.QYGLNIO8Req{}, //企业基本信息
"QYGL4B2E": &dto.QYGL5A3CReq{}, //税收违法 "QYGL4B2E": &dto.QYGL5A3CReq{}, //税收违法
"QYGL7D9A": &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 // 优先返回已配置的DTO
@@ -334,6 +347,7 @@ func (s *FormConfigServiceImpl) parseValidationRules(validateTag string) string
case strings.HasPrefix(rule, "oneof="): case strings.HasPrefix(rule, "oneof="):
values := strings.TrimPrefix(rule, "oneof=") values := strings.TrimPrefix(rule, "oneof=")
frontendRules = append(frontendRules, "可选值: "+values) frontendRules = append(frontendRules, "可选值: "+values)
} }
} }
@@ -363,6 +377,8 @@ func (s *FormConfigServiceImpl) getFieldType(fieldType reflect.Type, validation
return "select" return "select"
} else if strings.Contains(validation, "Base64图片") || strings.Contains(validation, "base64") { } else if strings.Contains(validation, "Base64图片") || strings.Contains(validation, "base64") {
return "textarea" return "textarea"
} else if strings.Contains(validation, "图片地址") {
return "url"
} }
return "text" return "text"
case reflect.Int64: case reflect.Int64:
@@ -416,6 +432,12 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
"dir": "方向", "dir": "方向",
"min_percent": "股权穿透比例下限", "min_percent": "股权穿透比例下限",
"max_percent": "股权穿透比例上限", "max_percent": "股权穿透比例上限",
"engine_number": "发动机号码",
"notice_model": "车辆型号",
"vlphoto_data": "行驶证图片",
"carplate_type": "车辆号牌类型",
"image_url": "行驶证图片地址",
"reg_url": "车辆登记证图片地址",
} }
if label, exists := labelMap[jsonTag]; exists { if label, exists := labelMap[jsonTag]; exists {
@@ -465,6 +487,12 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso
"dir": "down", "dir": "down",
"min_percent": "0", "min_percent": "0",
"max_percent": "1", "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 { if example, exists := exampleMap[jsonTag]; exists {
@@ -523,6 +551,12 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st
"dir": "请选择方向up-向上down-向下)", "dir": "请选择方向up-向上down-向下)",
"min_percent": "请输入股权穿透比例下限默认0", "min_percent": "请输入股权穿透比例下限默认0",
"max_percent": "请输入股权穿透比例上限默认1", "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 { if placeholder, exists := placeholderMap[jsonTag]; exists {
@@ -583,6 +617,12 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s
"dir": "方向up-向上穿透down-向下穿透", "dir": "方向up-向上穿透down-向下穿透",
"min_percent": "股权穿透比例下限大于等于默认为0支持小数点后两位以小数指代百分比", "min_percent": "股权穿透比例下限大于等于默认为0支持小数点后两位以小数指代百分比",
"max_percent": "股权穿透比例上限小于等于默认为1支持小数点后两位以小数指代百分比", "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 { if desc, exists := descMap[jsonTag]; exists {

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -23,8 +23,8 @@ func ProcessIVYZ9H2MRequest(ctx context.Context, params []byte, deps *processors
// 构建请求参数 // 构建请求参数
reqData := map[string]interface{}{ reqData := map[string]interface{}{
"idNo": paramsDto.IDNo, "id_no": paramsDto.IDCard,
"name": paramsDto.Name, "name": paramsDto.Name,
} }
// 调用极光API // 调用极光API

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -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, &paramsDto); 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
}

View File

@@ -24,10 +24,10 @@ var (
// JiguangResponse 极光API响应结构 // JiguangResponse 极光API响应结构
type JiguangResponse struct { type JiguangResponse struct {
Code int `json:"code"` Code int `json:"code"`
Msg string `json:"msg"` Msg string `json:"msg"`
OrderID string `json:"order_id"` OrderID string `json:"order_id"`
Data map[string]interface{} `json:"data"` Data interface{} `json:"data"`
} }
// JiguangConfig 极光服务配置 // JiguangConfig 极光服务配置

View File

@@ -186,6 +186,7 @@ func validateIDCardChecksum(idCard string) bool {
return byte(lastChar) == checksum return byte(lastChar) == checksum
} }
// validatePrice 价格验证 // validatePrice 价格验证
func validatePrice(fl validator.FieldLevel) bool { func validatePrice(fl validator.FieldLevel) bool {
price := fl.Field().Float() price := fl.Field().Float()
@@ -559,18 +560,18 @@ func validateReturnURL(fl validator.FieldLevel) bool {
// validateEnterpriseName 企业名称验证器 // validateEnterpriseName 企业名称验证器
func validateEnterpriseName(fl validator.FieldLevel) bool { func validateEnterpriseName(fl validator.FieldLevel) bool {
enterpriseName := fl.Field().String() enterpriseName := fl.Field().String()
// 去除首尾空格 // 去除首尾空格
trimmedName := strings.TrimSpace(enterpriseName) trimmedName := strings.TrimSpace(enterpriseName)
if trimmedName == "" { if trimmedName == "" {
return false return false
} }
// 长度验证2-100个字符 // 长度验证2-100个字符
if len(trimmedName) < 2 || len(trimmedName) > 100 { if len(trimmedName) < 2 || len(trimmedName) > 100 {
return false return false
} }
// 检查是否包含非法字符(允许中英文括号) // 检查是否包含非法字符(允许中英文括号)
invalidChars := []string{ invalidChars := []string{
"`", "~", "!", "@", "#", "$", "%", "^", "&", "*", "`", "~", "!", "@", "#", "$", "%", "^", "&", "*",
@@ -581,13 +582,13 @@ func validateEnterpriseName(fl validator.FieldLevel) bool {
return false return false
} }
} }
// 必须包含至少一个中文字符或英文字母 // 必须包含至少一个中文字符或英文字母
hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName) hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName)
if !hasValidChar { if !hasValidChar {
return false return false
} }
// 验证企业名称的基本格式(支持各种类型的企业) // 验证企业名称的基本格式(支持各种类型的企业)
// 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等 // 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等
validSuffixes := []string{ validSuffixes := []string{
@@ -598,7 +599,7 @@ func validateEnterpriseName(fl validator.FieldLevel) bool {
"Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp", "Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp",
"Company", "Studio", "Workshop", "Enterprise", "Company", "Studio", "Workshop", "Enterprise",
} }
// 检查是否以合法的企业类型结尾(不强制要求,因为有些企业名称可能没有标准后缀) // 检查是否以合法的企业类型结尾(不强制要求,因为有些企业名称可能没有标准后缀)
// 但如果有后缀,必须是合法的 // 但如果有后缀,必须是合法的
hasValidSuffix := false hasValidSuffix := false
@@ -613,7 +614,7 @@ func validateEnterpriseName(fl validator.FieldLevel) bool {
break break
} }
} }
// 如果名称中包含常见的企业类型关键词,则必须是合法的后缀 // 如果名称中包含常见的企业类型关键词,则必须是合法的后缀
enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"} enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"}
containsKeyword := false containsKeyword := false
@@ -623,12 +624,12 @@ func validateEnterpriseName(fl validator.FieldLevel) bool {
break break
} }
} }
// 如果包含企业关键词但没有合法后缀,则验证失败 // 如果包含企业关键词但没有合法后缀,则验证失败
if containsKeyword && !hasValidSuffix { if containsKeyword && !hasValidSuffix {
return false return false
} }
return true return true
} }
@@ -657,7 +658,7 @@ func ValidatePassword(password string) error {
hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password) hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
hasLower := regexp.MustCompile(`[a-z]`).MatchString(password) hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
hasDigit := regexp.MustCompile(`\d`).MatchString(password) hasDigit := regexp.MustCompile(`\d`).MatchString(password)
if !hasUpper { if !hasUpper {
return fmt.Errorf("密码必须包含大写字母") return fmt.Errorf("密码必须包含大写字母")
} }
@@ -790,7 +791,7 @@ func ValidateRequired(value interface{}, fieldName string) error {
if value == nil { if value == nil {
return fmt.Errorf("%s不能为空", fieldName) return fmt.Errorf("%s不能为空", fieldName)
} }
switch v := value.(type) { switch v := value.(type) {
case string: case string:
if strings.TrimSpace(v) == "" { if strings.TrimSpace(v) == "" {
@@ -801,7 +802,7 @@ func ValidateRequired(value interface{}, fieldName string) error {
return fmt.Errorf("%s不能为空", fieldName) return fmt.Errorf("%s不能为空", fieldName)
} }
} }
return nil return nil
} }
@@ -821,7 +822,7 @@ func ValidateSliceNotEmpty(slice interface{}, fieldName string) error {
if slice == nil { if slice == nil {
return fmt.Errorf("%s不能为空", fieldName) return fmt.Errorf("%s不能为空", fieldName)
} }
switch v := slice.(type) { switch v := slice.(type) {
case []string: case []string:
if len(v) == 0 { if len(v) == 0 {
@@ -832,7 +833,7 @@ func ValidateSliceNotEmpty(slice interface{}, fieldName string) error {
return fmt.Errorf("%s不能为空", fieldName) return fmt.Errorf("%s不能为空", fieldName)
} }
} }
return nil return nil
} }
@@ -841,13 +842,13 @@ func ValidateEnterpriseName(enterpriseName string) error {
if enterpriseName == "" { if enterpriseName == "" {
return fmt.Errorf("企业名称不能为空") return fmt.Errorf("企业名称不能为空")
} }
// 去除首尾空格 // 去除首尾空格
trimmedName := strings.TrimSpace(enterpriseName) trimmedName := strings.TrimSpace(enterpriseName)
if trimmedName == "" { if trimmedName == "" {
return fmt.Errorf("企业名称不能为空") return fmt.Errorf("企业名称不能为空")
} }
// 长度验证2-100个字符 // 长度验证2-100个字符
if len(trimmedName) < 2 { if len(trimmedName) < 2 {
return fmt.Errorf("企业名称长度不能少于2个字符") return fmt.Errorf("企业名称长度不能少于2个字符")
@@ -855,7 +856,7 @@ func ValidateEnterpriseName(enterpriseName string) error {
if len(trimmedName) > 100 { if len(trimmedName) > 100 {
return fmt.Errorf("企业名称长度不能超过100个字符") return fmt.Errorf("企业名称长度不能超过100个字符")
} }
// 检查是否包含非法字符(允许中英文括号) // 检查是否包含非法字符(允许中英文括号)
invalidChars := []string{ invalidChars := []string{
"`", "~", "!", "@", "#", "$", "%", "^", "&", "*", "`", "~", "!", "@", "#", "$", "%", "^", "&", "*",
@@ -866,13 +867,13 @@ func ValidateEnterpriseName(enterpriseName string) error {
return fmt.Errorf("企业名称不能包含特殊字符: %s", char) return fmt.Errorf("企业名称不能包含特殊字符: %s", char)
} }
} }
// 必须包含至少一个中文字符或英文字母 // 必须包含至少一个中文字符或英文字母
hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName) hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName)
if !hasValidChar { if !hasValidChar {
return fmt.Errorf("企业名称必须包含中文字符或英文字母") return fmt.Errorf("企业名称必须包含中文字符或英文字母")
} }
// 验证企业名称的基本格式(支持各种类型的企业) // 验证企业名称的基本格式(支持各种类型的企业)
// 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等 // 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等
validSuffixes := []string{ validSuffixes := []string{
@@ -883,7 +884,7 @@ func ValidateEnterpriseName(enterpriseName string) error {
"Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp", "Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp",
"Company", "Studio", "Workshop", "Enterprise", "Company", "Studio", "Workshop", "Enterprise",
} }
// 检查是否以合法的企业类型结尾 // 检查是否以合法的企业类型结尾
hasValidSuffix := false hasValidSuffix := false
for _, suffix := range validSuffixes { for _, suffix := range validSuffixes {
@@ -897,7 +898,7 @@ func ValidateEnterpriseName(enterpriseName string) error {
break break
} }
} }
// 如果名称中包含常见的企业类型关键词,则必须是合法的后缀 // 如果名称中包含常见的企业类型关键词,则必须是合法的后缀
enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"} enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"}
containsKeyword := false containsKeyword := false
@@ -907,12 +908,12 @@ func ValidateEnterpriseName(enterpriseName string) error {
break break
} }
} }
// 如果包含企业关键词但没有合法后缀,则验证失败 // 如果包含企业关键词但没有合法后缀,则验证失败
if containsKeyword && !hasValidSuffix { if containsKeyword && !hasValidSuffix {
return fmt.Errorf("企业名称格式不正确,请使用标准的企业类型后缀(如:有限公司、工作室等)") return fmt.Errorf("企业名称格式不正确,请使用标准的企业类型后缀(如:有限公司、工作室等)")
} }
return nil return nil
} }
@@ -922,9 +923,9 @@ func ValidateEnterpriseName(enterpriseName string) error {
func NewBusinessValidator() *BusinessValidator { func NewBusinessValidator() *BusinessValidator {
// 确保全局校验器已初始化 // 确保全局校验器已初始化
InitGlobalValidator() InitGlobalValidator()
return &BusinessValidator{ return &BusinessValidator{
validator: GetGlobalValidator(), // 使用全局校验器 validator: GetGlobalValidator(), // 使用全局校验器
} }
} }
@@ -946,29 +947,29 @@ func (bv *BusinessValidator) ValidateField(field interface{}, tag string) error
// validateBase64Image Base64图片格式验证器JPG、BMP、PNG // validateBase64Image Base64图片格式验证器JPG、BMP、PNG
func validateBase64Image(fl validator.FieldLevel) bool { func validateBase64Image(fl validator.FieldLevel) bool {
base64Str := fl.Field().String() base64Str := fl.Field().String()
// 如果为空,由 omitempty 处理 // 如果为空,由 omitempty 处理
if base64Str == "" { if base64Str == "" {
return true return true
} }
// 去除首尾空格 // 去除首尾空格
base64Str = strings.TrimSpace(base64Str) base64Str = strings.TrimSpace(base64Str)
if base64Str == "" { if base64Str == "" {
return false return false
} }
// 解码 base64 字符串 // 解码 base64 字符串
decoded, err := base64.StdEncoding.DecodeString(base64Str) decoded, err := base64.StdEncoding.DecodeString(base64Str)
if err != nil { if err != nil {
return false return false
} }
// 检查数据长度(至少要有文件头) // 检查数据长度(至少要有文件头)
if len(decoded) < 4 { if len(decoded) < 4 {
return false return false
} }
// 检查文件头,判断图片格式 // 检查文件头,判断图片格式
// JPG: FF D8 FF // JPG: FF D8 FF
// PNG: 89 50 4E 47 // PNG: 89 50 4E 47
@@ -977,16 +978,16 @@ func validateBase64Image(fl validator.FieldLevel) bool {
// JPG格式 // JPG格式
return true return true
} }
if len(decoded) >= 4 && decoded[0] == 0x89 && decoded[1] == 0x50 && decoded[2] == 0x4E && decoded[3] == 0x47 { if len(decoded) >= 4 && decoded[0] == 0x89 && decoded[1] == 0x50 && decoded[2] == 0x4E && decoded[3] == 0x47 {
// PNG格式 // PNG格式
return true return true
} }
if len(decoded) >= 2 && decoded[0] == 0x42 && decoded[1] == 0x4D { if len(decoded) >= 2 && decoded[0] == 0x42 && decoded[1] == 0x4D {
// BMP格式 // BMP格式
return true return true
} }
return false return false
} }