f
This commit is contained in:
43
config.yaml
43
config.yaml
@@ -530,3 +530,46 @@ jiguang:
|
|||||||
max_backups: 5
|
max_backups: 5
|
||||||
max_age: 30
|
max_age: 30
|
||||||
compress: true
|
compress: true
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# ✨ 数脉配置走实时接口
|
||||||
|
# ===========================================
|
||||||
|
shumai:
|
||||||
|
url: "https://api.shumaidata.com"
|
||||||
|
app_id: "pIfqx8MsoTOjhbB762qi5BfkjJ4D7w0O"
|
||||||
|
app_secret: "BnJWo61hUgNEa5fqBCueiT1IZ1e0DxPU"
|
||||||
|
# ===========================================
|
||||||
|
# ✨ 数脉子账号配置走政务
|
||||||
|
# ===========================================
|
||||||
|
# 走政务接口使用这个
|
||||||
|
app_id2: "AwZZRzWkArtFDO2lDcT2jHfuoo9n35Tq"
|
||||||
|
app_secret2: "nCXN6fKLImjfvzI12hj8O1CMl1gJeaWh"
|
||||||
|
|
||||||
|
sign_method: "md5" # 签名方法:md5 或 hmac,默认 hmac
|
||||||
|
timeout: 60s # 请求超时时间,默认 60 秒
|
||||||
|
|
||||||
|
# 数脉日志配置
|
||||||
|
logging:
|
||||||
|
enabled: true
|
||||||
|
log_dir: "logs/external_services"
|
||||||
|
service_name: "shumai"
|
||||||
|
use_daily: true
|
||||||
|
enable_level_separation: true
|
||||||
|
|
||||||
|
# 各级别配置
|
||||||
|
level_configs:
|
||||||
|
info:
|
||||||
|
max_size: 100
|
||||||
|
max_backups: 5
|
||||||
|
max_age: 30
|
||||||
|
compress: true
|
||||||
|
error:
|
||||||
|
max_size: 200
|
||||||
|
max_backups: 10
|
||||||
|
max_age: 90
|
||||||
|
compress: true
|
||||||
|
warn:
|
||||||
|
max_size: 100
|
||||||
|
max_backups: 5
|
||||||
|
max_age: 30
|
||||||
|
compress: true
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type Config struct {
|
|||||||
Alicloud AlicloudConfig `mapstructure:"alicloud"`
|
Alicloud AlicloudConfig `mapstructure:"alicloud"`
|
||||||
Xingwei XingweiConfig `mapstructure:"xingwei"`
|
Xingwei XingweiConfig `mapstructure:"xingwei"`
|
||||||
Jiguang JiguangConfig `mapstructure:"jiguang"`
|
Jiguang JiguangConfig `mapstructure:"jiguang"`
|
||||||
|
Shumai ShumaiConfig `mapstructure:"shumai"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfig HTTP服务器配置
|
// ServerConfig HTTP服务器配置
|
||||||
@@ -550,6 +551,36 @@ type JiguangLevelFileConfig struct {
|
|||||||
Compress bool `mapstructure:"compress"`
|
Compress bool `mapstructure:"compress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShumaiConfig 数脉配置
|
||||||
|
type ShumaiConfig struct {
|
||||||
|
URL string `mapstructure:"url"`
|
||||||
|
AppID string `mapstructure:"app_id"`
|
||||||
|
AppSecret string `mapstructure:"app_secret"`
|
||||||
|
AppID2 string `mapstructure:"app_id2"` // 走政务接口使用这个
|
||||||
|
AppSecret2 string `mapstructure:"app_secret2"` // 走政务接口使用这个
|
||||||
|
SignMethod string `mapstructure:"sign_method"` // md5 或 hmac,默认 hmac
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
|
||||||
|
Logging ShumaiLoggingConfig `mapstructure:"logging"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShumaiLoggingConfig 数脉日志配置
|
||||||
|
type ShumaiLoggingConfig struct {
|
||||||
|
Enabled bool `mapstructure:"enabled"`
|
||||||
|
LogDir string `mapstructure:"log_dir"`
|
||||||
|
UseDaily bool `mapstructure:"use_daily"`
|
||||||
|
EnableLevelSeparation bool `mapstructure:"enable_level_separation"`
|
||||||
|
LevelConfigs map[string]ShumaiLevelFileConfig `mapstructure:"level_configs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShumaiLevelFileConfig 数脉级别文件配置
|
||||||
|
type ShumaiLevelFileConfig struct {
|
||||||
|
MaxSize int `mapstructure:"max_size"`
|
||||||
|
MaxBackups int `mapstructure:"max_backups"`
|
||||||
|
MaxAge int `mapstructure:"max_age"`
|
||||||
|
Compress bool `mapstructure:"compress"`
|
||||||
|
}
|
||||||
|
|
||||||
// DomainConfig 域名配置
|
// DomainConfig 域名配置
|
||||||
type DomainConfig struct {
|
type DomainConfig struct {
|
||||||
API string `mapstructure:"api"` // API域名
|
API string `mapstructure:"api"` // API域名
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import (
|
|||||||
"tyapi-server/internal/infrastructure/external/jiguang"
|
"tyapi-server/internal/infrastructure/external/jiguang"
|
||||||
"tyapi-server/internal/infrastructure/external/muzi"
|
"tyapi-server/internal/infrastructure/external/muzi"
|
||||||
"tyapi-server/internal/infrastructure/external/ocr"
|
"tyapi-server/internal/infrastructure/external/ocr"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
"tyapi-server/internal/infrastructure/external/sms"
|
"tyapi-server/internal/infrastructure/external/sms"
|
||||||
"tyapi-server/internal/infrastructure/external/storage"
|
"tyapi-server/internal/infrastructure/external/storage"
|
||||||
"tyapi-server/internal/infrastructure/external/tianyancha"
|
"tyapi-server/internal/infrastructure/external/tianyancha"
|
||||||
@@ -371,6 +372,10 @@ func NewContainer() *Container {
|
|||||||
func(cfg *config.Config) (*jiguang.JiguangService, error) {
|
func(cfg *config.Config) (*jiguang.JiguangService, error) {
|
||||||
return jiguang.NewJiguangServiceWithConfig(cfg)
|
return jiguang.NewJiguangServiceWithConfig(cfg)
|
||||||
},
|
},
|
||||||
|
// ShumaiService - 数脉服务
|
||||||
|
func(cfg *config.Config) (*shumai.ShumaiService, error) {
|
||||||
|
return shumai.NewShumaiServiceWithConfig(cfg)
|
||||||
|
},
|
||||||
func(cfg *config.Config) *yushan.YushanService {
|
func(cfg *config.Config) *yushan.YushanService {
|
||||||
return yushan.NewYushanService(
|
return yushan.NewYushanService(
|
||||||
cfg.Yushan.URL,
|
cfg.Yushan.URL,
|
||||||
|
|||||||
@@ -853,3 +853,78 @@ type IVYZ9H2MReq struct {
|
|||||||
type YYSY9E4AReq struct {
|
type YYSY9E4AReq struct {
|
||||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YYSY运营商相关API DTO
|
||||||
|
type YYSY3M8SReq struct {
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYC4R9Req struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYH6D2Req struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYP0T4Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYE7V5Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYS9W1Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYK8R3Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYSYF2T7Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
DateRange string `json:"date_range" validate:"required,validAuthDate" `
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数脉 API
|
||||||
|
type IVYZ3M8SReq struct {
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZ9K7FReq struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZA1B3Req struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
PhotoData string `json:"photo_data" validate:"required,validBase64Image"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZC4R9Req struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZP0T4Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZF2T7Req struct {
|
||||||
|
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||||
|
DateRange string `json:"date_range" validate:"required,validAuthDate" `
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVYZX5QZReq struct {
|
||||||
|
ReturnURL string `json:"return_url" validate:"required,validReturnURL"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"tyapi-server/internal/infrastructure/external/alicloud"
|
"tyapi-server/internal/infrastructure/external/alicloud"
|
||||||
"tyapi-server/internal/infrastructure/external/jiguang"
|
"tyapi-server/internal/infrastructure/external/jiguang"
|
||||||
"tyapi-server/internal/infrastructure/external/muzi"
|
"tyapi-server/internal/infrastructure/external/muzi"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
"tyapi-server/internal/infrastructure/external/tianyancha"
|
"tyapi-server/internal/infrastructure/external/tianyancha"
|
||||||
"tyapi-server/internal/infrastructure/external/westdex"
|
"tyapi-server/internal/infrastructure/external/westdex"
|
||||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||||
@@ -56,6 +57,7 @@ func NewApiRequestService(
|
|||||||
zhichaService *zhicha.ZhichaService,
|
zhichaService *zhicha.ZhichaService,
|
||||||
xingweiService *xingwei.XingweiService,
|
xingweiService *xingwei.XingweiService,
|
||||||
jiguangService *jiguang.JiguangService,
|
jiguangService *jiguang.JiguangService,
|
||||||
|
shumaiService *shumai.ShumaiService,
|
||||||
validator interfaces.RequestValidator,
|
validator interfaces.RequestValidator,
|
||||||
productManagementService *services.ProductManagementService,
|
productManagementService *services.ProductManagementService,
|
||||||
) *ApiRequestService {
|
) *ApiRequestService {
|
||||||
@@ -63,7 +65,7 @@ func NewApiRequestService(
|
|||||||
combService := comb.NewCombService(productManagementService)
|
combService := comb.NewCombService(productManagementService)
|
||||||
|
|
||||||
// 创建处理器依赖容器
|
// 创建处理器依赖容器
|
||||||
processorDeps := processors.NewProcessorDependencies(westDexService, muziService, yushanService, tianYanChaService, alicloudService, zhichaService, xingweiService, jiguangService, validator, combService)
|
processorDeps := processors.NewProcessorDependencies(westDexService, muziService, yushanService, tianYanChaService, alicloudService, zhichaService, xingweiService, jiguangService, shumaiService, validator, combService)
|
||||||
|
|
||||||
// 统一注册所有处理器
|
// 统一注册所有处理器
|
||||||
registerAllProcessors(combService)
|
registerAllProcessors(combService)
|
||||||
@@ -186,6 +188,14 @@ func registerAllProcessors(combService *comb.CombService) {
|
|||||||
"YYSY9E4A": yysy.ProcessYYSY9E4ARequest,
|
"YYSY9E4A": yysy.ProcessYYSY9E4ARequest,
|
||||||
"YYSY9F1B": yysy.ProcessYYSY9F1BYequest,
|
"YYSY9F1B": yysy.ProcessYYSY9F1BYequest,
|
||||||
"YYSY6F2B": yysy.ProcessYYSY6F2BRequest,
|
"YYSY6F2B": yysy.ProcessYYSY6F2BRequest,
|
||||||
|
"YYSY3M8S": yysy.ProcessYYSY3M8SRequest, //运营商二要素查询
|
||||||
|
"YYSYC4R9": yysy.ProcessYYSYC4R9Request, //运营商三要素详版查询
|
||||||
|
"YYSYH6D2": yysy.ProcessYYSYH6D2Request, //运营商三要素简版查询
|
||||||
|
"YYSYP0T4": yysy.ProcessYYSYP0T4Request, //在网时长查询
|
||||||
|
"YYSYE7V5": yysy.ProcessYYSYE7V5Request, //手机在网状态查询
|
||||||
|
"YYSYS9W1": yysy.ProcessYYSYS9W1Request, //手机携号转网查询
|
||||||
|
"YYSYK8R3": yysy.ProcessYYSYK8R3Request, //手机空号检测查询
|
||||||
|
"YYSYF2T7": yysy.ProcessYYSYF2T7Request, //手机二次放号检测查询
|
||||||
|
|
||||||
// IVYZ系列处理器
|
// IVYZ系列处理器
|
||||||
"IVYZ0B03": ivyz.ProcessIVYZ0B03Request,
|
"IVYZ0B03": ivyz.ProcessIVYZ0B03Request,
|
||||||
@@ -221,6 +231,9 @@ func registerAllProcessors(combService *comb.CombService) {
|
|||||||
"IVYZSFEL": ivyz.ProcessIVYZSFELRequest, //全国自然人人像三要素核验_V1
|
"IVYZSFEL": ivyz.ProcessIVYZSFELRequest, //全国自然人人像三要素核验_V1
|
||||||
"IVYZ0S0D": ivyz.ProcessIVYZ0S0DRequest, //劳动仲裁信息查询(个人版)
|
"IVYZ0S0D": ivyz.ProcessIVYZ0S0DRequest, //劳动仲裁信息查询(个人版)
|
||||||
"IVYZ1J7H": ivyz.ProcessIVYZ1J7HRequest, //行驶证核查v2
|
"IVYZ1J7H": ivyz.ProcessIVYZ1J7HRequest, //行驶证核查v2
|
||||||
|
"IVYZ9K7F": ivyz.ProcessIVYZ9K7FRequest, //身份证实名认证
|
||||||
|
"IVYZA1B3": ivyz.ProcessIVYZA1B3Request, //公安三要素人脸识别
|
||||||
|
"IVYZN2P8": ivyz.ProcessIVYZN2P8Request, //身份证实名认证即时版本
|
||||||
|
|
||||||
// COMB系列处理器 - 只注册有自定义逻辑的组合包
|
// COMB系列处理器 - 只注册有自定义逻辑的组合包
|
||||||
"COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode
|
"COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode
|
||||||
|
|||||||
@@ -228,6 +228,19 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
|||||||
"JRZQO6L7": &dto.JRZQO6L7Req{}, //全国自然人经济特征评分模型v3 简版
|
"JRZQO6L7": &dto.JRZQO6L7Req{}, //全国自然人经济特征评分模型v3 简版
|
||||||
"JRZQO7L1": &dto.JRZQO7L1Req{}, //全国自然人经济特征评分模型v4 详版
|
"JRZQO7L1": &dto.JRZQO7L1Req{}, //全国自然人经济特征评分模型v4 详版
|
||||||
"JRZQS7G0": &dto.JRZQS7G0Req{}, //社保综合评分V1
|
"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{}, //身份证实名认证即时版本
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先返回已配置的DTO
|
// 优先返回已配置的DTO
|
||||||
@@ -408,6 +421,7 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
|
|||||||
"legal_person": "法人姓名",
|
"legal_person": "法人姓名",
|
||||||
"ent_code": "企业代码",
|
"ent_code": "企业代码",
|
||||||
"auth_date": "授权日期",
|
"auth_date": "授权日期",
|
||||||
|
"date_range": "数据范围",
|
||||||
"time_range": "时间范围",
|
"time_range": "时间范围",
|
||||||
"authorized": "是否授权",
|
"authorized": "是否授权",
|
||||||
"authorization_url": "授权链接",
|
"authorization_url": "授权链接",
|
||||||
@@ -463,6 +477,7 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso
|
|||||||
"legal_person": "王五",
|
"legal_person": "王五",
|
||||||
"ent_code": "91110000123456789X",
|
"ent_code": "91110000123456789X",
|
||||||
"auth_date": "20240101-20241231",
|
"auth_date": "20240101-20241231",
|
||||||
|
"date_range": "20240101-20241231",
|
||||||
"time_range": "09:00-18:00",
|
"time_range": "09:00-18:00",
|
||||||
"authorized": "1",
|
"authorized": "1",
|
||||||
"years": "5",
|
"years": "5",
|
||||||
@@ -527,6 +542,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st
|
|||||||
"legal_person": "请输入法人真实姓名",
|
"legal_person": "请输入法人真实姓名",
|
||||||
"ent_code": "请输入统一社会信用代码",
|
"ent_code": "请输入统一社会信用代码",
|
||||||
"auth_date": "请输入授权日期范围(YYYYMMDD-YYYYMMDD)",
|
"auth_date": "请输入授权日期范围(YYYYMMDD-YYYYMMDD)",
|
||||||
|
"date_range": "请输入日期范围(YYYYMMDD-YYYYMMDD)",
|
||||||
"time_range": "请输入时间范围(HH:MM-HH:MM)",
|
"time_range": "请输入时间范围(HH:MM-HH:MM)",
|
||||||
"authorized": "请选择是否授权",
|
"authorized": "请选择是否授权",
|
||||||
"years": "请输入查询年数(0-100)",
|
"years": "请输入查询年数(0-100)",
|
||||||
@@ -593,6 +609,7 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s
|
|||||||
"legal_person": "请输入法人真实姓名",
|
"legal_person": "请输入法人真实姓名",
|
||||||
"ent_code": "请输入统一社会信用代码",
|
"ent_code": "请输入统一社会信用代码",
|
||||||
"auth_date": "请输入授权日期范围,格式:YYYYMMDD-YYYYMMDD,且日期范围必须包括今天",
|
"auth_date": "请输入授权日期范围,格式:YYYYMMDD-YYYYMMDD,且日期范围必须包括今天",
|
||||||
|
"date_range": "请输入日期范围,格式:YYYYMMDD-YYYYMMDD",
|
||||||
"time_range": "请输入时间范围,格式:HH:MM-HH:MM",
|
"time_range": "请输入时间范围,格式:HH:MM-HH:MM",
|
||||||
"authorized": "请输入是否授权:0-未授权,1-已授权",
|
"authorized": "请输入是否授权:0-未授权,1-已授权",
|
||||||
"years": "请输入查询年数(0-100)",
|
"years": "请输入查询年数(0-100)",
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"tyapi-server/internal/application/api/commands"
|
"tyapi-server/internal/application/api/commands"
|
||||||
"tyapi-server/internal/infrastructure/external/alicloud"
|
"tyapi-server/internal/infrastructure/external/alicloud"
|
||||||
"tyapi-server/internal/infrastructure/external/muzi"
|
|
||||||
"tyapi-server/internal/infrastructure/external/jiguang"
|
"tyapi-server/internal/infrastructure/external/jiguang"
|
||||||
|
"tyapi-server/internal/infrastructure/external/muzi"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
"tyapi-server/internal/infrastructure/external/tianyancha"
|
"tyapi-server/internal/infrastructure/external/tianyancha"
|
||||||
"tyapi-server/internal/infrastructure/external/westdex"
|
"tyapi-server/internal/infrastructure/external/westdex"
|
||||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||||
@@ -34,6 +35,7 @@ type ProcessorDependencies struct {
|
|||||||
ZhichaService *zhicha.ZhichaService
|
ZhichaService *zhicha.ZhichaService
|
||||||
XingweiService *xingwei.XingweiService
|
XingweiService *xingwei.XingweiService
|
||||||
JiguangService *jiguang.JiguangService
|
JiguangService *jiguang.JiguangService
|
||||||
|
ShumaiService *shumai.ShumaiService
|
||||||
Validator interfaces.RequestValidator
|
Validator interfaces.RequestValidator
|
||||||
CombService CombServiceInterface // Changed to interface to break import cycle
|
CombService CombServiceInterface // Changed to interface to break import cycle
|
||||||
Options *commands.ApiCallOptions // 添加Options支持
|
Options *commands.ApiCallOptions // 添加Options支持
|
||||||
@@ -50,6 +52,7 @@ func NewProcessorDependencies(
|
|||||||
zhichaService *zhicha.ZhichaService,
|
zhichaService *zhicha.ZhichaService,
|
||||||
xingweiService *xingwei.XingweiService,
|
xingweiService *xingwei.XingweiService,
|
||||||
jiguangService *jiguang.JiguangService,
|
jiguangService *jiguang.JiguangService,
|
||||||
|
shumaiService *shumai.ShumaiService,
|
||||||
validator interfaces.RequestValidator,
|
validator interfaces.RequestValidator,
|
||||||
combService CombServiceInterface, // Changed to interface
|
combService CombServiceInterface, // Changed to interface
|
||||||
) *ProcessorDependencies {
|
) *ProcessorDependencies {
|
||||||
@@ -62,6 +65,7 @@ func NewProcessorDependencies(
|
|||||||
ZhichaService: zhichaService,
|
ZhichaService: zhichaService,
|
||||||
XingweiService: xingweiService,
|
XingweiService: xingweiService,
|
||||||
JiguangService: jiguangService,
|
JiguangService: jiguangService,
|
||||||
|
ShumaiService: shumaiService,
|
||||||
Validator: validator,
|
Validator: validator,
|
||||||
CombService: combService,
|
CombService: combService,
|
||||||
Options: nil, // 初始化为nil,在调用时设置
|
Options: nil, // 初始化为nil,在调用时设置
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessIVYZ9K7FRequest IVYZ9K7F 身份证实名认证 API处理方法
|
||||||
|
func ProcessIVYZ9K7FRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.IVYZ9K7FReq
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idcard": paramsDto.IDCard,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/id_card/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录情况
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessIVYZA1B3Request IVYZA1B3 公安三要素人脸识别API处理方法
|
||||||
|
func ProcessIVYZA1B3Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.IVYZA1B3Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idcard": paramsDto.IDCard,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
"image": paramsDto.PhotoData,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/face_id_card/compare" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录情况
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
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/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessIVYZN2P8Request IVYZN2P8 身份证实名认证政务版 API处理方法
|
||||||
|
func ProcessIVYZN2P8Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.IVYZ9K7FReq
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idcard": paramsDto.IDCard,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
//走政务接口 - 使用 app_id2 和 app_secret2
|
||||||
|
deps.ShumaiService.UseGovernment()
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/id_card/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录情况
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessIVYZx5qzRequest IVYZx5qz 活体识别API处理方法
|
||||||
|
func ProcessIVYZX5QZRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.IVYZX5QZReq
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"return_url": paramsDto.ReturnURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/liveness/h5/v4/token" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录情况
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -47,17 +47,13 @@ func ProcessJRZQO6L7Request(ctx context.Context, params []byte, deps *processors
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
||||||
ctx = zhicha.WithSkipCode201Check(ctx)
|
// ctx = zhicha.WithSkipCode201Check(ctx)
|
||||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI081", reqData)
|
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI081", reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, zhicha.ErrDatasource) {
|
if errors.Is(err, zhicha.ErrDatasource) {
|
||||||
return nil, errors.Join(processors.ErrDatasource, err)
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, zhicha.ErrNotFound) {
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
return nil, errors.Join(processors.ErrNotFound, err)
|
|
||||||
} else {
|
|
||||||
return nil, errors.Join(processors.ErrSystem, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将响应数据转换为JSON字节
|
// 将响应数据转换为JSON字节
|
||||||
|
|||||||
@@ -48,17 +48,13 @@ func ProcessJRZQO7L1Request(ctx context.Context, params []byte, deps *processors
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
||||||
ctx = zhicha.WithSkipCode201Check(ctx)
|
// ctx = zhicha.WithSkipCode201Check(ctx)
|
||||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI080", reqData)
|
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI080", reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, zhicha.ErrDatasource) {
|
if errors.Is(err, zhicha.ErrDatasource) {
|
||||||
return nil, errors.Join(processors.ErrDatasource, err)
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, zhicha.ErrNotFound) {
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
return nil, errors.Join(processors.ErrNotFound, err)
|
|
||||||
} else {
|
|
||||||
return nil, errors.Join(processors.ErrSystem, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将响应数据转换为JSON字节
|
// 将响应数据转换为JSON字节
|
||||||
|
|||||||
@@ -43,17 +43,13 @@ func ProcessJRZQS7G0Request(ctx context.Context, params []byte, deps *processors
|
|||||||
"authorized": paramsDto.Authorized,
|
"authorized": paramsDto.Authorized,
|
||||||
}
|
}
|
||||||
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
||||||
ctx = zhicha.WithSkipCode201Check(ctx)
|
// ctx = zhicha.WithSkipCode201Check(ctx)
|
||||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI082", reqData)
|
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI082", reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, zhicha.ErrDatasource) {
|
if errors.Is(err, zhicha.ErrDatasource) {
|
||||||
return nil, errors.Join(processors.ErrDatasource, err)
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, zhicha.ErrNotFound) {
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
return nil, errors.Join(processors.ErrNotFound, err)
|
|
||||||
} else {
|
|
||||||
return nil, errors.Join(processors.ErrSystem, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 将响应数据转换为JSON字节
|
// 将响应数据转换为JSON字节
|
||||||
respBytes, err := json.Marshal(respData)
|
respBytes, err := json.Marshal(respData)
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSY3M8SRequest YYSY3M8S 运营商二要素 API处理方法
|
||||||
|
func ProcessYYSY3M8SRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSY3M8SReq
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/mobile_two/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYC4R9Request YYSYC4R9 运营商三要素详版API处理方法
|
||||||
|
func ProcessYYSYC4R9Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYC4R9Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idcard": paramsDto.IDCard,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v2/mobile_three/check/detail" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYE7V5Request YYSYE7V5 手机在网状态查询API处理方法
|
||||||
|
func ProcessYYSYE7V5Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYE7V5Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v1/mobile_status/check" // 接口路径,根据数脉文档填写(
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYF2T7Request YYSYF2T7 手机二次放号检测查询API处理方法
|
||||||
|
func ProcessYYSYF2T7Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYF2T7Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
"date": paramsDto.DateRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/mobile_twice/check" // 接口路径,根据数脉文档填写(
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYH6D2Request YYSYH6D2 运营商三要素简版API处理方法
|
||||||
|
func ProcessYYSYH6D2Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYH6D2Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idcard": paramsDto.IDCard,
|
||||||
|
"name": paramsDto.Name,
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/mobile_three/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYK8R3Request YYSYK8R3 手机空号检测查询API处理方法
|
||||||
|
func ProcessYYSYK8R3Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYK8R3Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/mobile_empty/check" // 接口路径,根据数脉文档填写(
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYP0T4Request YYSYP0T4 在网时长API处理方法
|
||||||
|
func ProcessYYSYP0T4Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYP0T4Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v2/mobile_online/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package yysy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessYYSYS9W1Request YYSYS9W1 手机携号转网查询API处理方法
|
||||||
|
func ProcessYYSYS9W1Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.YYSYS9W1Req
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"mobile": paramsDto.MobileNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||||
|
apiPath := "/v4/mobile-transfer/query" // 接口路径,根据数脉文档填写(
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBytes, nil
|
||||||
|
}
|
||||||
199
internal/infrastructure/external/shumai/crypto.go
vendored
Normal file
199
internal/infrastructure/external/shumai/crypto.go
vendored
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package shumai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SignMethod 签名方法
|
||||||
|
type SignMethod string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SignMethodMD5 SignMethod = "md5"
|
||||||
|
SignMethodHMACMD5 SignMethod = "hmac"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateSignForm 生成表单接口签名(appid & timestamp & app_security)
|
||||||
|
// 拼接规则:appid + "&" + timestamp + "&" + app_security,对拼接串做 MD5,32 位小写十六进制;
|
||||||
|
// 不足 32 位左侧补 0。
|
||||||
|
func GenerateSignForm(appid, timestamp, appSecret string) string {
|
||||||
|
str := appid + "&" + timestamp + "&" + appSecret
|
||||||
|
hash := md5.Sum([]byte(str))
|
||||||
|
sign := strings.ToLower(hex.EncodeToString(hash[:]))
|
||||||
|
if n := 32 - len(sign); n > 0 {
|
||||||
|
sign = strings.Repeat("0", n) + sign
|
||||||
|
}
|
||||||
|
return sign
|
||||||
|
}
|
||||||
|
|
||||||
|
// app_secret: "BnJWo61hUgNEa5fqBCueiT1IZ1e0DxPU"
|
||||||
|
|
||||||
|
// Encrypt 使用 AES/ECB/PKCS5Padding 加密数据
|
||||||
|
// 加密算法:AES,工作模式:ECB(无初始向量),填充方式:PKCS5Padding
|
||||||
|
// 加密 key 是服务商分配的 app_security,AES 加密之后再进行 base64 编码
|
||||||
|
func Encrypt(data, appSecurity string) (string, error) {
|
||||||
|
key := prepareAESKey([]byte(appSecurity))
|
||||||
|
ciphertext, err := aesEncryptECB([]byte(data), key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt 解密 base64 编码的 AES/ECB/PKCS5Padding 加密数据
|
||||||
|
func Decrypt(encodedData, appSecurity string) ([]byte, error) {
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(encodedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key := prepareAESKey([]byte(appSecurity))
|
||||||
|
plaintext, err := aesDecryptECB(ciphertext, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareAESKey 准备 AES 密钥,确保长度为 16/24/32 字节
|
||||||
|
// 如果 key 长度不足,用 0 填充;如果过长,截取前 32 字节
|
||||||
|
func prepareAESKey(key []byte) []byte {
|
||||||
|
keyLen := len(key)
|
||||||
|
if keyLen == 16 || keyLen == 24 || keyLen == 32 {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
if keyLen < 16 {
|
||||||
|
// 不足 16 字节,用 0 填充到 16 字节(AES-128)
|
||||||
|
padded := make([]byte, 16)
|
||||||
|
copy(padded, key)
|
||||||
|
return padded
|
||||||
|
}
|
||||||
|
if keyLen < 24 {
|
||||||
|
// 不足 24 字节,用 0 填充到 24 字节(AES-192)
|
||||||
|
padded := make([]byte, 24)
|
||||||
|
copy(padded, key)
|
||||||
|
return padded
|
||||||
|
}
|
||||||
|
if keyLen < 32 {
|
||||||
|
// 不足 32 字节,用 0 填充到 32 字节(AES-256)
|
||||||
|
padded := make([]byte, 32)
|
||||||
|
copy(padded, key)
|
||||||
|
return padded
|
||||||
|
}
|
||||||
|
// 超过 32 字节,截取前 32 字节(AES-256)
|
||||||
|
return key[:32]
|
||||||
|
}
|
||||||
|
|
||||||
|
// aesEncryptECB 使用 AES ECB 模式加密,PKCS5 填充
|
||||||
|
func aesEncryptECB(plaintext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paddedPlaintext := pkcs5Padding(plaintext, block.BlockSize())
|
||||||
|
ciphertext := make([]byte, len(paddedPlaintext))
|
||||||
|
mode := newECBEncrypter(block)
|
||||||
|
mode.CryptBlocks(ciphertext, paddedPlaintext)
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// aesDecryptECB 使用 AES ECB 模式解密,PKCS5 去填充
|
||||||
|
func aesDecryptECB(ciphertext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(ciphertext)%block.BlockSize() != 0 {
|
||||||
|
return nil, errors.New("ciphertext length is not a multiple of block size")
|
||||||
|
}
|
||||||
|
plaintext := make([]byte, len(ciphertext))
|
||||||
|
mode := newECBDecrypter(block)
|
||||||
|
mode.CryptBlocks(plaintext, ciphertext)
|
||||||
|
return pkcs5Unpadding(plaintext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs5Padding PKCS5 填充
|
||||||
|
func pkcs5Padding(src []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(src)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(src, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs5Unpadding 去除 PKCS5 填充
|
||||||
|
func pkcs5Unpadding(src []byte) []byte {
|
||||||
|
length := len(src)
|
||||||
|
if length == 0 {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
unpadding := int(src[length-1])
|
||||||
|
if unpadding > length {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
return src[:length-unpadding]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECB 模式加密/解密实现
|
||||||
|
type ecb struct {
|
||||||
|
b cipher.Block
|
||||||
|
blockSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newECB(b cipher.Block) *ecb {
|
||||||
|
return &ecb{
|
||||||
|
b: b,
|
||||||
|
blockSize: b.BlockSize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecbEncrypter ecb
|
||||||
|
|
||||||
|
func newECBEncrypter(b cipher.Block) cipher.BlockMode {
|
||||||
|
return (*ecbEncrypter)(newECB(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbEncrypter) BlockSize() int {
|
||||||
|
return x.blockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||||
|
if len(src)%x.blockSize != 0 {
|
||||||
|
panic("crypto/cipher: input not full blocks")
|
||||||
|
}
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
panic("crypto/cipher: output smaller than input")
|
||||||
|
}
|
||||||
|
for len(src) > 0 {
|
||||||
|
x.b.Encrypt(dst, src[:x.blockSize])
|
||||||
|
src = src[x.blockSize:]
|
||||||
|
dst = dst[x.blockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecbDecrypter ecb
|
||||||
|
|
||||||
|
func newECBDecrypter(b cipher.Block) cipher.BlockMode {
|
||||||
|
return (*ecbDecrypter)(newECB(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbDecrypter) BlockSize() int {
|
||||||
|
return x.blockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||||
|
if len(src)%x.blockSize != 0 {
|
||||||
|
panic("crypto/cipher: input not full blocks")
|
||||||
|
}
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
panic("crypto/cipher: output smaller than input")
|
||||||
|
}
|
||||||
|
for len(src) > 0 {
|
||||||
|
x.b.Decrypt(dst, src[:x.blockSize])
|
||||||
|
src = src[x.blockSize:]
|
||||||
|
dst = dst[x.blockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
108
internal/infrastructure/external/shumai/shumai_errors.go
vendored
Normal file
108
internal/infrastructure/external/shumai/shumai_errors.go
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package shumai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShumaiError 数脉服务错误
|
||||||
|
type ShumaiError struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 实现 error 接口
|
||||||
|
func (e *ShumaiError) Error() string {
|
||||||
|
return fmt.Sprintf("数脉错误 [%s]: %s", e.Code, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSuccess 是否成功
|
||||||
|
func (e *ShumaiError) IsSuccess() bool {
|
||||||
|
return e.Code == "0" || e.Code == "200"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNoRecord 是否查无记录
|
||||||
|
func (e *ShumaiError) IsNoRecord() bool {
|
||||||
|
return e.Code == "404"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsParamError 是否参数错误
|
||||||
|
func (e *ShumaiError) IsParamError() bool {
|
||||||
|
return e.Code == "400"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAuthError 是否认证错误
|
||||||
|
func (e *ShumaiError) IsAuthError() bool {
|
||||||
|
return e.Code == "601" || e.Code == "602"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSystemError 是否系统错误
|
||||||
|
func (e *ShumaiError) IsSystemError() bool {
|
||||||
|
return e.Code == "500" || e.Code == "501"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预定义错误
|
||||||
|
var (
|
||||||
|
ErrSuccess = &ShumaiError{Code: "200", Message: "成功"}
|
||||||
|
ErrParamError = &ShumaiError{Code: "400", Message: "参数错误"}
|
||||||
|
ErrNoRecord = &ShumaiError{Code: "404", Message: "请求资源不存在"}
|
||||||
|
ErrSystemError = &ShumaiError{Code: "500", Message: "系统内部错误,请联系服务商"}
|
||||||
|
ErrThirdPartyError = &ShumaiError{Code: "501", Message: "第三方服务异常"}
|
||||||
|
ErrNoPermission = &ShumaiError{Code: "601", Message: "服务商未开通接口权限"}
|
||||||
|
ErrAccountDisabled = &ShumaiError{Code: "602", Message: "账号停用"}
|
||||||
|
ErrInsufficientBalance = &ShumaiError{Code: "603", Message: "余额不足请充值"}
|
||||||
|
ErrInterfaceDisabled = &ShumaiError{Code: "604", Message: "接口停用"}
|
||||||
|
ErrInsufficientQuota = &ShumaiError{Code: "605", Message: "次数不足,请购买套餐"}
|
||||||
|
ErrRateLimitExceeded = &ShumaiError{Code: "606", Message: "调用超限,请联系服务商"}
|
||||||
|
ErrOther = &ShumaiError{Code: "1001", Message: "其他,以实际返回为准"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewShumaiError 创建数脉错误
|
||||||
|
func NewShumaiError(code, message string) *ShumaiError {
|
||||||
|
return &ShumaiError{Code: code, Message: message}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShumaiErrorFromCode 根据状态码创建错误
|
||||||
|
func NewShumaiErrorFromCode(code string) *ShumaiError {
|
||||||
|
switch code {
|
||||||
|
case "0", "200":
|
||||||
|
return ErrSuccess
|
||||||
|
case "400":
|
||||||
|
return ErrParamError
|
||||||
|
case "404":
|
||||||
|
return ErrNoRecord
|
||||||
|
case "500":
|
||||||
|
return ErrSystemError
|
||||||
|
case "501":
|
||||||
|
return ErrThirdPartyError
|
||||||
|
case "601":
|
||||||
|
return ErrNoPermission
|
||||||
|
case "602":
|
||||||
|
return ErrAccountDisabled
|
||||||
|
case "603":
|
||||||
|
return ErrInsufficientBalance
|
||||||
|
case "604":
|
||||||
|
return ErrInterfaceDisabled
|
||||||
|
case "605":
|
||||||
|
return ErrInsufficientQuota
|
||||||
|
case "606":
|
||||||
|
return ErrRateLimitExceeded
|
||||||
|
case "1001":
|
||||||
|
return ErrOther
|
||||||
|
default:
|
||||||
|
return &ShumaiError{Code: code, Message: "未知错误"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsShumaiError 是否为数脉错误
|
||||||
|
func IsShumaiError(err error) bool {
|
||||||
|
_, ok := err.(*ShumaiError)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetShumaiError 获取数脉错误
|
||||||
|
func GetShumaiError(err error) *ShumaiError {
|
||||||
|
if e, ok := err.(*ShumaiError); ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
69
internal/infrastructure/external/shumai/shumai_factory.go
vendored
Normal file
69
internal/infrastructure/external/shumai/shumai_factory.go
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package shumai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tyapi-server/internal/config"
|
||||||
|
"tyapi-server/internal/shared/external_logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewShumaiServiceWithConfig 使用 config 创建数脉服务
|
||||||
|
func NewShumaiServiceWithConfig(cfg *config.Config) (*ShumaiService, error) {
|
||||||
|
loggingConfig := external_logger.ExternalServiceLoggingConfig{
|
||||||
|
Enabled: cfg.Shumai.Logging.Enabled,
|
||||||
|
LogDir: cfg.Shumai.Logging.LogDir,
|
||||||
|
ServiceName: "shumai",
|
||||||
|
UseDaily: cfg.Shumai.Logging.UseDaily,
|
||||||
|
EnableLevelSeparation: cfg.Shumai.Logging.EnableLevelSeparation,
|
||||||
|
LevelConfigs: make(map[string]external_logger.ExternalServiceLevelFileConfig),
|
||||||
|
}
|
||||||
|
for k, v := range cfg.Shumai.Logging.LevelConfigs {
|
||||||
|
loggingConfig.LevelConfigs[k] = external_logger.ExternalServiceLevelFileConfig{
|
||||||
|
MaxSize: v.MaxSize,
|
||||||
|
MaxBackups: v.MaxBackups,
|
||||||
|
MaxAge: v.MaxAge,
|
||||||
|
Compress: v.Compress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger, err := external_logger.NewExternalServiceLogger(loggingConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var signMethod SignMethod
|
||||||
|
if cfg.Shumai.SignMethod == "md5" {
|
||||||
|
signMethod = SignMethodMD5
|
||||||
|
} else {
|
||||||
|
signMethod = SignMethodHMACMD5
|
||||||
|
}
|
||||||
|
timeout := 60 * time.Second
|
||||||
|
if cfg.Shumai.Timeout > 0 {
|
||||||
|
timeout = cfg.Shumai.Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewShumaiService(
|
||||||
|
cfg.Shumai.URL,
|
||||||
|
cfg.Shumai.AppID,
|
||||||
|
cfg.Shumai.AppSecret,
|
||||||
|
signMethod,
|
||||||
|
timeout,
|
||||||
|
logger,
|
||||||
|
cfg.Shumai.AppID2, // 走政务接口使用这个
|
||||||
|
cfg.Shumai.AppSecret2, // 走政务接口使用这个
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShumaiServiceWithLogging 使用自定义日志配置创建数脉服务
|
||||||
|
func NewShumaiServiceWithLogging(url, appID, appSecret string, signMethod SignMethod, timeout time.Duration, loggingConfig external_logger.ExternalServiceLoggingConfig, appID2, appSecret2 string) (*ShumaiService, error) {
|
||||||
|
loggingConfig.ServiceName = "shumai"
|
||||||
|
logger, err := external_logger.NewExternalServiceLogger(loggingConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewShumaiService(url, appID, appSecret, signMethod, timeout, logger, appID2, appSecret2), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShumaiServiceSimple 创建无数脉日志的数脉服务
|
||||||
|
func NewShumaiServiceSimple(url, appID, appSecret string, signMethod SignMethod, timeout time.Duration, appID2, appSecret2 string) *ShumaiService {
|
||||||
|
return NewShumaiService(url, appID, appSecret, signMethod, timeout, nil, appID2, appSecret2)
|
||||||
|
}
|
||||||
279
internal/infrastructure/external/shumai/shumai_service.go
vendored
Normal file
279
internal/infrastructure/external/shumai/shumai_service.go
vendored
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
package shumai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tyapi-server/internal/shared/external_logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDatasource = errors.New("数据源异常")
|
||||||
|
ErrSystem = errors.New("系统异常")
|
||||||
|
ErrNotFound = errors.New("查询为空")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShumaiResponse 数脉 API 通用响应(占位,按实际文档调整)
|
||||||
|
type ShumaiResponse struct {
|
||||||
|
Code int `json:"code"` // 状态码
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShumaiConfig 数脉服务配置
|
||||||
|
type ShumaiConfig struct {
|
||||||
|
URL string
|
||||||
|
AppID string
|
||||||
|
AppSecret string
|
||||||
|
AppID2 string // 走政务接口使用这个
|
||||||
|
AppSecret2 string // 走政务接口使用这个
|
||||||
|
SignMethod SignMethod
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShumaiService 数脉服务
|
||||||
|
type ShumaiService struct {
|
||||||
|
config ShumaiConfig
|
||||||
|
logger *external_logger.ExternalServiceLogger
|
||||||
|
useGovernment bool // 是否使用政务接口(app_id2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShumaiService 创建数脉服务实例
|
||||||
|
// appID2 和 appSecret2 用于政务接口,如果为空则只使用普通接口
|
||||||
|
func NewShumaiService(url, appID, appSecret string, signMethod SignMethod, timeout time.Duration, logger *external_logger.ExternalServiceLogger, appID2, appSecret2 string) *ShumaiService {
|
||||||
|
if signMethod == "" {
|
||||||
|
signMethod = SignMethodHMACMD5
|
||||||
|
}
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = 60 * time.Second
|
||||||
|
}
|
||||||
|
return &ShumaiService{
|
||||||
|
config: ShumaiConfig{
|
||||||
|
URL: url,
|
||||||
|
AppID: appID,
|
||||||
|
AppSecret: appSecret,
|
||||||
|
AppID2: appID2, // 走政务接口使用这个
|
||||||
|
AppSecret2: appSecret2, // 走政务接口使用这个
|
||||||
|
SignMethod: signMethod,
|
||||||
|
Timeout: timeout,
|
||||||
|
},
|
||||||
|
logger: logger,
|
||||||
|
useGovernment: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShumaiService) generateRequestID() string {
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
appID := s.getCurrentAppID()
|
||||||
|
hash := md5.Sum([]byte(fmt.Sprintf("%d_%s", timestamp, appID)))
|
||||||
|
return fmt.Sprintf("shumai_%x", hash[:8])
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCurrentAppID 获取当前使用的 AppID
|
||||||
|
func (s *ShumaiService) getCurrentAppID() string {
|
||||||
|
if s.useGovernment && s.config.AppID2 != "" {
|
||||||
|
return s.config.AppID2
|
||||||
|
}
|
||||||
|
return s.config.AppID
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCurrentAppSecret 获取当前使用的 AppSecret
|
||||||
|
func (s *ShumaiService) getCurrentAppSecret() string {
|
||||||
|
if s.useGovernment && s.config.AppSecret2 != "" {
|
||||||
|
return s.config.AppSecret2
|
||||||
|
}
|
||||||
|
return s.config.AppSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseGovernment 切换到政务接口(使用 app_id2 和 app_secret2)
|
||||||
|
func (s *ShumaiService) UseGovernment() {
|
||||||
|
s.useGovernment = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseNormal 切换到普通接口(使用 app_id 和 app_secret)
|
||||||
|
func (s *ShumaiService) UseNormal() {
|
||||||
|
s.useGovernment = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUsingGovernment 检查是否正在使用政务接口
|
||||||
|
func (s *ShumaiService) IsUsingGovernment() bool {
|
||||||
|
return s.useGovernment
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig 返回当前配置
|
||||||
|
func (s *ShumaiService) GetConfig() ShumaiConfig {
|
||||||
|
return s.config
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallAPIForm 以表单方式调用数脉 API(application/x-www-form-urlencoded)
|
||||||
|
// 在方法内部将 reqFormData 转为表单:先写入业务参数,再追加 appid、timestamp、sign。
|
||||||
|
// 签名算法:md5(appid×tamp&app_security),32 位小写,不足补 0。
|
||||||
|
func (s *ShumaiService) CallAPIForm(ctx context.Context, apiPath string, reqFormData map[string]interface{}) ([]byte, error) {
|
||||||
|
startTime := time.Now()
|
||||||
|
requestID := s.generateRequestID()
|
||||||
|
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||||
|
appID := s.getCurrentAppID()
|
||||||
|
appSecret := s.getCurrentAppSecret()
|
||||||
|
sign := GenerateSignForm(appID, timestamp, appSecret)
|
||||||
|
|
||||||
|
var transactionID string
|
||||||
|
if id, ok := ctx.Value("transaction_id").(string); ok {
|
||||||
|
transactionID = id
|
||||||
|
}
|
||||||
|
|
||||||
|
form := url.Values{}
|
||||||
|
form.Set("appid", appID)
|
||||||
|
form.Set("timestamp", timestamp)
|
||||||
|
form.Set("sign", sign)
|
||||||
|
for k, v := range reqFormData {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
form.Set(k, fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
body := form.Encode()
|
||||||
|
|
||||||
|
baseURL := strings.TrimSuffix(s.config.URL, "/")
|
||||||
|
|
||||||
|
reqURL := baseURL
|
||||||
|
if apiPath != "" {
|
||||||
|
reqURL = baseURL + "/" + strings.TrimPrefix(apiPath, "/")
|
||||||
|
}
|
||||||
|
if apiPath == "" {
|
||||||
|
apiPath = "shumai_form"
|
||||||
|
}
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogRequest(requestID, transactionID, apiPath, reqURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(ErrSystem, err)
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: s.config.Timeout}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
isTimeout := ctx.Err() == context.DeadlineExceeded
|
||||||
|
if !isTimeout {
|
||||||
|
if te, ok := err.(interface{ Timeout() bool }); ok && te.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isTimeout {
|
||||||
|
es := err.Error()
|
||||||
|
if strings.Contains(es, "deadline exceeded") || strings.Contains(es, "timeout") || strings.Contains(es, "canceled") {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isTimeout {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, err)
|
||||||
|
}
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
raw, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(ErrSystem, err)
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("HTTP %d", resp.StatusCode))
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogResponse(requestID, transactionID, apiPath, resp.StatusCode, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shumaiResp ShumaiResponse
|
||||||
|
if err := json.Unmarshal(raw, &shumaiResp); err != nil {
|
||||||
|
err = errors.Join(ErrSystem, fmt.Errorf("响应解析失败: %w", err))
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
codeStr := strconv.Itoa(shumaiResp.Code)
|
||||||
|
msg := shumaiResp.Msg
|
||||||
|
if msg == "" {
|
||||||
|
msg = shumaiResp.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
shumaiErr := NewShumaiErrorFromCode(codeStr)
|
||||||
|
if !shumaiErr.IsSuccess() {
|
||||||
|
if shumaiErr.Message == "未知错误" && msg != "" {
|
||||||
|
shumaiErr = NewShumaiError(codeStr, msg)
|
||||||
|
}
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, shumaiErr, reqFormData)
|
||||||
|
}
|
||||||
|
if shumaiErr.IsNoRecord() {
|
||||||
|
return nil, errors.Join(ErrNotFound, shumaiErr)
|
||||||
|
}
|
||||||
|
return nil, errors.Join(ErrDatasource, shumaiErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shumaiResp.Data == nil {
|
||||||
|
return []byte("{}"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataBytes, err := json.Marshal(shumaiResp.Data)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(ErrSystem, fmt.Errorf("data 序列化失败: %w", err))
|
||||||
|
if s.logger != nil {
|
||||||
|
s.logger.LogError(requestID, transactionID, apiPath, err, reqFormData)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dataBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShumaiService) Encrypt(data string) (string, error) {
|
||||||
|
appSecret := s.getCurrentAppSecret()
|
||||||
|
encryptedValue, err := Encrypt(data, appSecret)
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrSystem
|
||||||
|
}
|
||||||
|
return encryptedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShumaiService) Decrypt(encodedData string) ([]byte, error) {
|
||||||
|
appSecret := s.getCurrentAppSecret()
|
||||||
|
decryptedValue, err := Decrypt(encodedData, appSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrSystem
|
||||||
|
}
|
||||||
|
return decryptedValue, nil
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrDatasource = errors.New("数据源异常")
|
ErrDatasource = errors.New("数据源异常")
|
||||||
ErrSystem = errors.New("系统异常")
|
ErrSystem = errors.New("系统异常")
|
||||||
ErrNotFound = errors.New("查询为空")
|
ErrNotFound = errors.New("查询为空")
|
||||||
)
|
)
|
||||||
|
|
||||||
type WestResp struct {
|
type WestResp struct {
|
||||||
@@ -72,7 +72,6 @@ func (w *WestDexService) generateRequestID() string {
|
|||||||
return fmt.Sprintf("westdex_%x", hash[:8])
|
return fmt.Sprintf("westdex_%x", hash[:8])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// buildRequestURL 构建请求URL
|
// buildRequestURL 构建请求URL
|
||||||
func (w *WestDexService) buildRequestURL(code string) string {
|
func (w *WestDexService) buildRequestURL(code string) string {
|
||||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||||
@@ -132,11 +131,10 @@ func (w *WestDexService) CallAPI(ctx context.Context, code string, reqData map[s
|
|||||||
isTimeout = true
|
isTimeout = true
|
||||||
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
isTimeout = true
|
isTimeout = true
|
||||||
} else if errStr := clientDoErr.Error();
|
} else if errStr := clientDoErr.Error(); errStr == "context deadline exceeded" ||
|
||||||
errStr == "context deadline exceeded" ||
|
errStr == "timeout" ||
|
||||||
errStr == "timeout" ||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
errStr == "Client.Timeout exceeded" ||
|
errStr == "net/http: request canceled" {
|
||||||
errStr == "net/http: request canceled" {
|
|
||||||
isTimeout = true
|
isTimeout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +183,6 @@ func (w *WestDexService) CallAPI(ctx context.Context, code string, reqData map[s
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录响应日志(不记录具体响应数据)
|
// 记录响应日志(不记录具体响应数据)
|
||||||
if w.logger != nil {
|
if w.logger != nil {
|
||||||
w.logger.LogResponseWithID(requestID, transactionID, code, httpResp.StatusCode, duration, westDexResp.ID)
|
w.logger.LogResponseWithID(requestID, transactionID, code, httpResp.StatusCode, duration, westDexResp.ID)
|
||||||
@@ -305,11 +302,10 @@ func (w *WestDexService) G05HZ01CallAPI(ctx context.Context, code string, reqDat
|
|||||||
isTimeout = true
|
isTimeout = true
|
||||||
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
isTimeout = true
|
isTimeout = true
|
||||||
} else if errStr := clientDoErr.Error();
|
} else if errStr := clientDoErr.Error(); errStr == "context deadline exceeded" ||
|
||||||
errStr == "context deadline exceeded" ||
|
errStr == "timeout" ||
|
||||||
errStr == "timeout" ||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
errStr == "Client.Timeout exceeded" ||
|
errStr == "net/http: request canceled" {
|
||||||
errStr == "net/http: request canceled" {
|
|
||||||
isTimeout = true
|
isTimeout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrDatasource = errors.New("数据源异常")
|
ErrDatasource = errors.New("数据源异常")
|
||||||
ErrSystem = errors.New("系统异常")
|
ErrSystem = errors.New("系统异常")
|
||||||
ErrNotFound = errors.New("数据为空")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// contextKey 用于在 context 中存储不跳过 201 错误检查的标志
|
// contextKey 用于在 context 中存储不跳过 201 错误检查的标志
|
||||||
@@ -30,12 +29,6 @@ type contextKey string
|
|||||||
|
|
||||||
const dontSkipCode201CheckKey contextKey = "dont_skip_code_201_check"
|
const dontSkipCode201CheckKey contextKey = "dont_skip_code_201_check"
|
||||||
|
|
||||||
// WithSkipCode201Check 返回一个设置了不跳过 201 错误检查标志的 context
|
|
||||||
// 默认情况下会跳过 201 检查(继续执行),使用此函数后会在 Code == "201" 时返回错误
|
|
||||||
func WithSkipCode201Check(ctx context.Context) context.Context {
|
|
||||||
return context.WithValue(ctx, dontSkipCode201CheckKey, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ZhichaResp struct {
|
type ZhichaResp struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
@@ -200,24 +193,8 @@ func (z *ZhichaService) CallAPI(ctx context.Context, proID string, params map[st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否需要不跳过 201 错误检查(默认跳过,继续执行)
|
|
||||||
// 如果设置了 dontSkipCode201CheckKey,则返回错误
|
|
||||||
dontSkipCode201Check := false
|
|
||||||
if dontSkip, ok := ctx.Value(dontSkipCode201CheckKey).(bool); ok {
|
|
||||||
dontSkipCode201Check = dontSkip
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果设置了不跳过检查,当 Code == "201" 时返回错误
|
|
||||||
if zhichaResp.Code == "201" && dontSkipCode201Check {
|
|
||||||
if z.logger != nil {
|
|
||||||
z.logger.LogError(requestID, transactionID, proID, ErrNotFound, params)
|
|
||||||
}
|
|
||||||
return nil, ErrNotFound
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查业务状态码
|
// 检查业务状态码
|
||||||
if zhichaResp.Code != "200" && zhichaResp.Code != "201" {
|
if zhichaResp.Code != "200" {
|
||||||
// 创建智查金控错误用于日志记录
|
// 创建智查金控错误用于日志记录
|
||||||
zhichaErr := NewZhichaErrorFromCode(zhichaResp.Code)
|
zhichaErr := NewZhichaErrorFromCode(zhichaResp.Code)
|
||||||
if zhichaErr.Code == "未知错误" {
|
if zhichaErr.Code == "未知错误" {
|
||||||
|
|||||||
Reference in New Issue
Block a user