From 5a6e95906c40a3c76b7152c26f736fc1615fab64 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Thu, 16 Oct 2025 18:35:18 +0800 Subject: [PATCH] new --- config.yaml | 44 ++- internal/config/config.go | 28 ++ internal/container/container.go | 5 + internal/domains/api/dto/api_request_dto.go | 30 +- .../api/services/api_request_service.go | 13 +- .../api/services/processors/dependencies.go | 4 + .../processors/dwbg/dwbg7f3a_processor.go | 51 ++++ .../processors/ivyz/ivyz2125_processor.go | 4 +- .../processors/ivyz/ivyz3a7f_processor.go | 50 ++++ .../processors/ivyz/ivyz5733_processor.go | 38 ++- .../processors/ivyz/ivyz81nc_processor.go | 51 ++++ .../processors/ivyz/ivyz9d2e_processor.go | 51 ++++ .../processors/ivyz/ivyzadee_processor.go | 4 +- .../processors/jrzq/jrzq8a2d_processor.go | 15 +- .../processors/yysy/yysy8f3a_processor.go | 51 ++++ .../external/xingwei/xingwei_factory.go | 62 ++++ .../external/xingwei/xingwei_service.go | 279 ++++++++++++++++++ .../external/xingwei/xingwei_test.go | 241 +++++++++++++++ 18 files changed, 990 insertions(+), 31 deletions(-) create mode 100644 internal/domains/api/services/processors/dwbg/dwbg7f3a_processor.go create mode 100644 internal/domains/api/services/processors/ivyz/ivyz3a7f_processor.go create mode 100644 internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go create mode 100644 internal/domains/api/services/processors/ivyz/ivyz9d2e_processor.go create mode 100644 internal/domains/api/services/processors/yysy/yysy8f3a_processor.go create mode 100644 internal/infrastructure/external/xingwei/xingwei_factory.go create mode 100644 internal/infrastructure/external/xingwei/xingwei_service.go create mode 100644 internal/infrastructure/external/xingwei/xingwei_test.go diff --git a/config.yaml b/config.yaml index a95e2a1..3c38b1f 100644 --- a/config.yaml +++ b/config.yaml @@ -53,7 +53,6 @@ logger: log_dir: "logs" # 日志目录 use_daily: true # 是否按日分包 use_color: false # 是否使用彩色输出(仅console格式有效) - # 文件配置 max_size: 100 # 单个文件最大大小(MB) max_backups: 5 # 最大备份文件数 @@ -125,7 +124,7 @@ sms: access_key_id: "LTAI5tKGB3TVJbMHSoZN3yr9" access_key_secret: "OCQ30GWp4yENMjmfOAaagksE18bp65" endpoint_url: "dysmsapi.aliyuncs.com" - sign_name: "天远查" + sign_name: "天远数据" template_code: "SMS_474525324" code_length: 6 expire_time: 5m @@ -236,6 +235,7 @@ development: wechat_work: webhook_url: "" secret: "" + # =========================================== # 📝 e签宝服务配置 # =========================================== @@ -278,9 +278,9 @@ wallet: # 余额预警配置 balance_alert: - default_enabled: true # 默认启用余额预警 - default_threshold: 200.00 # 默认预警阈值 - alert_cooldown_hours: 24 # 预警冷却时间(小时) + default_enabled: true # 默认启用余额预警 + default_threshold: 200.00 # 默认预警阈值 + alert_cooldown_hours: 24 # 预警冷却时间(小时) # =========================================== # 🌍 西部数据配置 @@ -410,3 +410,37 @@ zhicha: max_backups: 5 max_age: 30 compress: true + +# =========================================== +# 🎯 行为数据配置 +# =========================================== +xingwei: + url: "https://sjztyh.chengdaoji.cn/dataCenterManageApi/manage/interface/doc/api/handle" + api_id: "jGtqla2FQv1zuXuH" + api_key: "iR1qS9725N4JA70gwlwohqT3ogl2zBf3" + + # 行为数据日志配置 + logging: + enabled: true + log_dir: "logs/external_services" + service_name: "xingwei" + 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 diff --git a/internal/config/config.go b/internal/config/config.go index 85a73ae..22f6e53 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -33,6 +33,7 @@ type Config struct { Yushan YushanConfig `mapstructure:"yushan"` TianYanCha TianYanChaConfig `mapstructure:"tianyancha"` Alicloud AlicloudConfig `mapstructure:"alicloud"` + Xingwei XingweiConfig `mapstructure:"xingwei"` } // ServerConfig HTTP服务器配置 @@ -438,6 +439,33 @@ type AlicloudConfig struct { AppCode string `mapstructure:"app_code"` } +// XingweiConfig 行为数据配置 +type XingweiConfig struct { + URL string `mapstructure:"url"` + ApiID string `mapstructure:"api_id"` + ApiKey string `mapstructure:"api_key"` + + // 行为数据日志配置 + Logging XingweiLoggingConfig `mapstructure:"logging"` +} + +// XingweiLoggingConfig 行为数据日志配置 +type XingweiLoggingConfig struct { + Enabled bool `mapstructure:"enabled"` + LogDir string `mapstructure:"log_dir"` + UseDaily bool `mapstructure:"use_daily"` + EnableLevelSeparation bool `mapstructure:"enable_level_separation"` + LevelConfigs map[string]XingweiLevelFileConfig `mapstructure:"level_configs"` +} + +// XingweiLevelFileConfig 行为数据级别文件配置 +type XingweiLevelFileConfig struct { + MaxSize int `mapstructure:"max_size"` + MaxBackups int `mapstructure:"max_backups"` + MaxAge int `mapstructure:"max_age"` + Compress bool `mapstructure:"compress"` +} + // DomainConfig 域名配置 type DomainConfig struct { API string `mapstructure:"api"` // API域名 diff --git a/internal/container/container.go b/internal/container/container.go index a6d971b..4734b6f 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -42,6 +42,7 @@ import ( "tyapi-server/internal/infrastructure/external/storage" "tyapi-server/internal/infrastructure/external/tianyancha" "tyapi-server/internal/infrastructure/external/westdex" + "tyapi-server/internal/infrastructure/external/xingwei" "tyapi-server/internal/infrastructure/external/yushan" "tyapi-server/internal/infrastructure/external/zhicha" "tyapi-server/internal/infrastructure/http/handlers" @@ -342,6 +343,10 @@ func NewContainer() *Container { func(cfg *config.Config) (*zhicha.ZhichaService, error) { return zhicha.NewZhichaServiceWithConfig(cfg) }, + // XingweiService - 行为数据服务 + func(cfg *config.Config) (*xingwei.XingweiService, error) { + return xingwei.NewXingweiServiceWithConfig(cfg) + }, func(cfg *config.Config) *yushan.YushanService { return yushan.NewYushanService( cfg.Yushan.URL, diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 12ea324..f18338d 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -68,6 +68,10 @@ type IVYZ5733Req struct { Name string `json:"name" validate:"required,min=1,validName"` IDCard string `json:"id_card" validate:"required,validIDCard"` } +type IVYZ81NCReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` +} type IVYZ9363Req struct { ManName string `json:"man_name" validate:"required,min=1,validName"` ManIDCard string `json:"man_id_card" validate:"required,validIDCard"` @@ -277,6 +281,24 @@ type IVYZ7F3AReq struct { Authorized string `json:"authorized" validate:"required,oneof=0 1"` } +type IVYZ3A7FReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` +} + +type IVYZ9D2EReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + UseScenario string `json:"use_scenario" validate:"required,oneof=1 2 3 4 99"` +} + +// DWBG7F3AReq 行为数据查询请求参数 +type DWBG7F3AReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` +} + // 新增的QYGL处理器DTO type QYGL5A3CReq struct { EntCode string `json:"ent_code" validate:"required,validUSCI"` @@ -355,9 +377,13 @@ type JRZQ3C7BReq struct { type JRZQ8A2DReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` - IDCard string `json:"id_card" validate:"required,validIDCard"` +} + +// YYSY8F3AReq 行为数据查询请求参数 +type YYSY8F3AReq struct { Name string `json:"name" validate:"required,min=1,validName"` - Authorized string `json:"authorized" validate:"required,oneof=0 1"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` } type JRZQ5E9FReq struct { diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index ca87a3e..9a56d25 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -20,6 +20,7 @@ import ( "tyapi-server/internal/infrastructure/external/alicloud" "tyapi-server/internal/infrastructure/external/tianyancha" "tyapi-server/internal/infrastructure/external/westdex" + "tyapi-server/internal/infrastructure/external/xingwei" "tyapi-server/internal/infrastructure/external/yushan" "tyapi-server/internal/infrastructure/external/zhicha" "tyapi-server/internal/shared/interfaces" @@ -49,6 +50,7 @@ func NewApiRequestService( tianYanChaService *tianyancha.TianYanChaService, alicloudService *alicloud.AlicloudService, zhichaService *zhicha.ZhichaService, + xingweiService *xingwei.XingweiService, validator interfaces.RequestValidator, productManagementService *services.ProductManagementService, ) *ApiRequestService { @@ -56,7 +58,7 @@ func NewApiRequestService( combService := comb.NewCombService(productManagementService) // 创建处理器依赖容器 - processorDeps := processors.NewProcessorDependencies(westDexService, yushanService, tianYanChaService, alicloudService, zhichaService, validator, combService) + processorDeps := processors.NewProcessorDependencies(westDexService, yushanService, tianYanChaService, alicloudService, zhichaService, xingweiService, validator, combService) // 统一注册所有处理器 registerAllProcessors(combService) @@ -141,7 +143,8 @@ func registerAllProcessors(combService *comb.CombService) { "YYSY8B1C": yysy.ProcessYYSY8B1CRequest, "YYSY6D9A": yysy.ProcessYYSY6D9ARequest, "YYSY3E7F": yysy.ProcessYYSY3E7FRequest, - + "YYSY8F3A": yysy.ProcessYYSY8F3ARequest, + // IVYZ系列处理器 "IVYZ0B03": ivyz.ProcessIVYZ0B03Request, "IVYZ2125": ivyz.ProcessIVYZ2125Request, @@ -158,7 +161,10 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZ7C9D": ivyz.ProcessIVYZ7C9DRequest, "IVYZ5E3F": ivyz.ProcessIVYZ5E3FRequest, "IVYZ7F3A": ivyz.ProcessIVYZ7F3ARequest, - + "IVYZ3A7F": ivyz.ProcessIVYZ3A7FRequest, + "IVYZ9D2E": ivyz.ProcessIVYZ9D2ERequest, + "IVYZ81NC": ivyz.ProcessIVYZ81NCRequest, + // COMB系列处理器 - 只注册有自定义逻辑的组合包 "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode @@ -168,6 +174,7 @@ func registerAllProcessors(combService *comb.CombService) { // DWBG系列处理器 - 多维报告 "DWBG6A2C": dwbg.ProcessDWBG6A2CRequest, "DWBG8B4D": dwbg.ProcessDWBG8B4DRequest, + "DWBG7F3A": dwbg.ProcessDWBG7F3ARequest, // FLXG系列处理器 - 风险管控 (包含原FXHY功能) "FLXG8B4D": flxg.ProcessFLXG8B4DRequest, diff --git a/internal/domains/api/services/processors/dependencies.go b/internal/domains/api/services/processors/dependencies.go index 843e6b6..3ff3fa6 100644 --- a/internal/domains/api/services/processors/dependencies.go +++ b/internal/domains/api/services/processors/dependencies.go @@ -6,6 +6,7 @@ import ( "tyapi-server/internal/infrastructure/external/alicloud" "tyapi-server/internal/infrastructure/external/tianyancha" "tyapi-server/internal/infrastructure/external/westdex" + "tyapi-server/internal/infrastructure/external/xingwei" "tyapi-server/internal/infrastructure/external/yushan" "tyapi-server/internal/infrastructure/external/zhicha" "tyapi-server/internal/shared/interfaces" @@ -28,6 +29,7 @@ type ProcessorDependencies struct { TianYanChaService *tianyancha.TianYanChaService AlicloudService *alicloud.AlicloudService ZhichaService *zhicha.ZhichaService + XingweiService *xingwei.XingweiService Validator interfaces.RequestValidator CombService CombServiceInterface // Changed to interface to break import cycle Options *commands.ApiCallOptions // 添加Options支持 @@ -41,6 +43,7 @@ func NewProcessorDependencies( tianYanChaService *tianyancha.TianYanChaService, alicloudService *alicloud.AlicloudService, zhichaService *zhicha.ZhichaService, + xingweiService *xingwei.XingweiService, validator interfaces.RequestValidator, combService CombServiceInterface, // Changed to interface ) *ProcessorDependencies { @@ -50,6 +53,7 @@ func NewProcessorDependencies( TianYanChaService: tianYanChaService, AlicloudService: alicloudService, ZhichaService: zhichaService, + XingweiService: xingweiService, Validator: validator, CombService: combService, Options: nil, // 初始化为nil,在调用时设置 diff --git a/internal/domains/api/services/processors/dwbg/dwbg7f3a_processor.go b/internal/domains/api/services/processors/dwbg/dwbg7f3a_processor.go new file mode 100644 index 0000000..4ee2c27 --- /dev/null +++ b/internal/domains/api/services/processors/dwbg/dwbg7f3a_processor.go @@ -0,0 +1,51 @@ +package dwbg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/xingwei" +) + +// ProcessDWBG7F3ARequest DWBG7F3A API处理方法 - 行为数据查询 +func ProcessDWBG7F3ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.DWBG7F3AReq + 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) + } + + // 构建请求数据,使用xingwei服务的正确字段名 + reqData := map[string]interface{}{ + "name": paramsDto.Name, + "idCardNum": paramsDto.IDCard, + "phoneNumber": paramsDto.MobileNo, + } + + // 调用行为数据API,使用指定的project_id + projectID := "CDJ-1101695406546284544" + respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData) + if err != nil { + if errors.Is(err, xingwei.ErrNotFound) { + // 查空情况,返回特定的查空错误 + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, xingwei.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, xingwei.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyz2125_processor.go b/internal/domains/api/services/processors/ivyz/ivyz2125_processor.go index cc9440c..052e8e7 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz2125_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz2125_processor.go @@ -2,14 +2,14 @@ package ivyz import ( "context" - "fmt" + "errors" "tyapi-server/internal/domains/api/services/processors" ) // ProcessIVYZ2125Request IVYZ2125 API处理方法 func ProcessIVYZ2125Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, "服务已停用") + return nil, errors.Join(processors.ErrSystem, errors.New("服务已停用")) // var paramsDto dto.IVYZ2125Req // if err := json.Unmarshal(params, ¶msDto); err != nil { // return nil, errors.Join(processors.ErrSystem, err) diff --git a/internal/domains/api/services/processors/ivyz/ivyz3a7f_processor.go b/internal/domains/api/services/processors/ivyz/ivyz3a7f_processor.go new file mode 100644 index 0000000..af10e3b --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz3a7f_processor.go @@ -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/xingwei" +) + +// ProcessIVYZ3A7FRequest IVYZ3A7F API处理方法 - 行为数据查询 +func ProcessIVYZ3A7FRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ3A7FReq + 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) + } + + // 构建请求数据,使用xingwei服务的正确字段名 + reqData := map[string]interface{}{ + "name": paramsDto.Name, + "idCardNum": paramsDto.IDCard, + } + + // 调用行为数据API,使用指定的project_id + projectID := "CDJ-1104648854749245440" + respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData) + if err != nil { + if errors.Is(err, xingwei.ErrNotFound) { + // 查空情况,返回特定的查空错误 + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, xingwei.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, xingwei.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyz5733_processor.go b/internal/domains/api/services/processors/ivyz/ivyz5733_processor.go index 581f61f..818a84d 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz5733_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz5733_processor.go @@ -34,11 +34,11 @@ func ProcessIVYZ5733Request(ctx context.Context, params []byte, deps *processors reqData := map[string]interface{}{ "data": map[string]interface{}{ "name": encryptedName, - "idcard": encryptedIDCard, + "idNo": encryptedIDCard, }, } - respBytes, err := deps.WestDexService.CallAPI(ctx, "G09XM02", reqData) + respBytes, err := deps.WestDexService.CallAPI(ctx, "G09GZ02", reqData) if err != nil { if errors.Is(err, westdex.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) @@ -47,5 +47,37 @@ func ProcessIVYZ5733Request(ctx context.Context, params []byte, deps *processors } } - return respBytes, nil + // 解析新源响应数据 + var newResp struct { + Status string `json:"status"` + } + if err := json.Unmarshal(respBytes, &newResp); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 转换状态码 + var statusCode string + switch newResp.Status { + case "结婚": + statusCode = "IA" + case "离婚": + statusCode = "IB" + case "未查得": + statusCode = "INR" + default: + statusCode = "INR" + } + + // 构建旧格式响应 + oldResp := map[string]interface{}{ + "code": "0", + "data": map[string]interface{}{ + "data": statusCode + ":匹配不成功", + }, + "seqNo": "", + "message": "成功", + } + + // 返回旧格式响应 + return json.Marshal(oldResp) } \ No newline at end of file diff --git a/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go new file mode 100644 index 0000000..c95b29b --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go @@ -0,0 +1,51 @@ +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/westdex" +) + +// ProcessIVYZ81NCRequest IVYZ81NC API处理方法 +func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ81NCReq + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if err := deps.Validator.ValidateStruct(paramsDto); err != nil { + return nil, errors.Join(processors.ErrInvalidParam, err) + } + + encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + encryptedIDCard, err := deps.WestDexService.Encrypt(paramsDto.IDCard) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + reqData := map[string]interface{}{ + "data": map[string]interface{}{ + "name": encryptedName, + "idcard": encryptedIDCard, + }, + } + + respBytes, err := deps.WestDexService.CallAPI(ctx, "G09XM02", reqData) + if err != nil { + if errors.Is(err, westdex.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} \ No newline at end of file diff --git a/internal/domains/api/services/processors/ivyz/ivyz9d2e_processor.go b/internal/domains/api/services/processors/ivyz/ivyz9d2e_processor.go new file mode 100644 index 0000000..f336116 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz9d2e_processor.go @@ -0,0 +1,51 @@ +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/xingwei" +) + +// ProcessIVYZ9D2ERequest IVYZ9D2E API处理方法 - 行为数据查询 +func ProcessIVYZ9D2ERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ9D2EReq + 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) + } + + // 构建请求数据,使用xingwei服务的正确字段名 + reqData := map[string]interface{}{ + "name": paramsDto.Name, + "idCardNum": paramsDto.IDCard, + "scenario": paramsDto.UseScenario, + } + + // 调用行为数据API,使用指定的project_id + projectID := "CDJ-1104648845446279168" + respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData) + if err != nil { + if errors.Is(err, xingwei.ErrNotFound) { + // 查空情况,返回特定的查空错误 + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, xingwei.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, xingwei.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyzadee_processor.go b/internal/domains/api/services/processors/ivyz/ivyzadee_processor.go index 6b27ad6..e6efdd3 100644 --- a/internal/domains/api/services/processors/ivyz/ivyzadee_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyzadee_processor.go @@ -2,14 +2,14 @@ package ivyz import ( "context" - "fmt" + "errors" "tyapi-server/internal/domains/api/services/processors" ) // ProcessIVYZADEERequest IVYZADEE API处理方法 func ProcessIVYZADEERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, "服务已停用") + return nil, errors.Join(processors.ErrSystem, errors.New("服务已停用")) // var paramsDto dto.IVYZADEEReq // if err := json.Unmarshal(params, ¶msDto); err != nil { // return nil, errors.Join(processors.ErrSystem, err) diff --git a/internal/domains/api/services/processors/jrzq/jrzq8a2d_processor.go b/internal/domains/api/services/processors/jrzq/jrzq8a2d_processor.go index d0bfee8..637d798 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq8a2d_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq8a2d_processor.go @@ -21,26 +21,13 @@ func ProcessJRZQ8A2DRequest(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } - encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo) if err != nil { return nil, errors.Join(processors.ErrSystem, err) } reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "phone": encryptedMobileNo, - "authorized": paramsDto.Authorized, + "phone": encryptedMobileNo, } respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI018", reqData) diff --git a/internal/domains/api/services/processors/yysy/yysy8f3a_processor.go b/internal/domains/api/services/processors/yysy/yysy8f3a_processor.go new file mode 100644 index 0000000..42e95e7 --- /dev/null +++ b/internal/domains/api/services/processors/yysy/yysy8f3a_processor.go @@ -0,0 +1,51 @@ +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/xingwei" +) + +// ProcessYYSY8F3ARequest YYSY8F3A API处理方法 - 行为数据查询 +func ProcessYYSY8F3ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.YYSY8F3AReq + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if err := deps.Validator.ValidateStruct(paramsDto); err != nil { + return nil, errors.Join(processors.ErrInvalidParam, err) + } + + // 构建请求数据,直接传递姓名、身份证、手机号 + reqData := map[string]interface{}{ + "name": paramsDto.Name, + "idCardNum": paramsDto.IDCard, + "phoneNumber": paramsDto.MobileNo, + } + + // 调用行为数据API,使用指定的project_id + projectID := "CDJ-1100244697766359040" + respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData) + if err != nil { + if errors.Is(err, xingwei.ErrNotFound) { + // 查空情况,返回特定的查空错误 + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, xingwei.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, xingwei.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/infrastructure/external/xingwei/xingwei_factory.go b/internal/infrastructure/external/xingwei/xingwei_factory.go new file mode 100644 index 0000000..c96698d --- /dev/null +++ b/internal/infrastructure/external/xingwei/xingwei_factory.go @@ -0,0 +1,62 @@ +package xingwei + +import ( + "tyapi-server/internal/config" + "tyapi-server/internal/shared/external_logger" +) + +// NewXingweiServiceWithConfig 使用配置创建行为数据服务 +func NewXingweiServiceWithConfig(cfg *config.Config) (*XingweiService, error) { + // 将配置类型转换为通用外部服务日志配置 + loggingConfig := external_logger.ExternalServiceLoggingConfig{ + Enabled: cfg.Xingwei.Logging.Enabled, + LogDir: cfg.Xingwei.Logging.LogDir, + ServiceName: "xingwei", + UseDaily: cfg.Xingwei.Logging.UseDaily, + EnableLevelSeparation: cfg.Xingwei.Logging.EnableLevelSeparation, + LevelConfigs: make(map[string]external_logger.ExternalServiceLevelFileConfig), + } + + // 转换级别配置 + for key, value := range cfg.Xingwei.Logging.LevelConfigs { + loggingConfig.LevelConfigs[key] = external_logger.ExternalServiceLevelFileConfig{ + MaxSize: value.MaxSize, + MaxBackups: value.MaxBackups, + MaxAge: value.MaxAge, + Compress: value.Compress, + } + } + + // 创建通用外部服务日志器 + logger, err := external_logger.NewExternalServiceLogger(loggingConfig) + if err != nil { + return nil, err + } + + // 创建行为数据服务 + service := NewXingweiService( + cfg.Xingwei.URL, + cfg.Xingwei.ApiID, + cfg.Xingwei.ApiKey, + logger, + ) + + return service, nil +} + +// NewXingweiServiceWithLogging 使用自定义日志配置创建行为数据服务 +func NewXingweiServiceWithLogging(url, apiID, apiKey string, loggingConfig external_logger.ExternalServiceLoggingConfig) (*XingweiService, error) { + // 设置服务名称 + loggingConfig.ServiceName = "xingwei" + + // 创建通用外部服务日志器 + logger, err := external_logger.NewExternalServiceLogger(loggingConfig) + if err != nil { + return nil, err + } + + // 创建行为数据服务 + service := NewXingweiService(url, apiID, apiKey, logger) + + return service, nil +} diff --git a/internal/infrastructure/external/xingwei/xingwei_service.go b/internal/infrastructure/external/xingwei/xingwei_service.go new file mode 100644 index 0000000..4a1cdce --- /dev/null +++ b/internal/infrastructure/external/xingwei/xingwei_service.go @@ -0,0 +1,279 @@ +package xingwei + +import ( + "bytes" + "context" + "crypto/md5" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "sync/atomic" + "time" + + "tyapi-server/internal/shared/external_logger" +) + +// 行为数据API状态码常量 +const ( + CodeSuccess = 200 // 操作成功 + CodeSystemError = 500 // 系统内部错误 + CodeMerchantError = 3001 // 商家相关报错(商家不存在、商家被禁用、商家余额不足) + CodeAccountExpired = 3002 // 账户已过期 + CodeIPWhitelistMissing = 3003 // 未添加ip白名单 + CodeUnauthorized = 3004 // 未授权调用该接口 + CodeProductIDError = 4001 // 产品id错误 + CodeInterfaceDisabled = 4002 // 接口被停用 + CodeQueryException = 5001 // 接口查询异常,请联系技术人员 + CodeNotFound = 6000 // 未查询到结果 +) + +var ( + ErrDatasource = errors.New("数据源异常") + ErrSystem = errors.New("系统异常") + ErrNotFound = errors.New("未查询到结果") + + // 请求ID计数器,确保唯一性 + requestIDCounter int64 +) + +// XingweiResponse 行为数据API响应结构 +type XingweiResponse struct { + Msg string `json:"msg"` + Code int `json:"code"` + Data interface{} `json:"data"` +} + +// XingweiErrorCode 行为数据错误码定义 +type XingweiErrorCode struct { + Code int + Message string +} + +// 行为数据错误码映射 +var XingweiErrorCodes = map[int]XingweiErrorCode{ + CodeSuccess: {Code: CodeSuccess, Message: "操作成功"}, + CodeSystemError: {Code: CodeSystemError, Message: "系统内部错误"}, + CodeMerchantError: {Code: CodeMerchantError, Message: "商家相关报错(商家不存在、商家被禁用、商家余额不足)"}, + CodeAccountExpired: {Code: CodeAccountExpired, Message: "账户已过期"}, + CodeIPWhitelistMissing: {Code: CodeIPWhitelistMissing, Message: "未添加ip白名单"}, + CodeUnauthorized: {Code: CodeUnauthorized, Message: "未授权调用该接口"}, + CodeProductIDError: {Code: CodeProductIDError, Message: "产品id错误"}, + CodeInterfaceDisabled: {Code: CodeInterfaceDisabled, Message: "接口被停用"}, + CodeQueryException: {Code: CodeQueryException, Message: "接口查询异常,请联系技术人员"}, + CodeNotFound: {Code: CodeNotFound, Message: "未查询到结果"}, +} + +// GetXingweiErrorMessage 根据错误码获取错误消息 +func GetXingweiErrorMessage(code int) string { + if errorCode, exists := XingweiErrorCodes[code]; exists { + return errorCode.Message + } + return fmt.Sprintf("未知错误码: %d", code) +} + +type XingweiConfig struct { + URL string + ApiID string + ApiKey string +} + +type XingweiService struct { + config XingweiConfig + logger *external_logger.ExternalServiceLogger +} + +// NewXingweiService 是一个构造函数,用于初始化 XingweiService +func NewXingweiService(url, apiID, apiKey string, logger *external_logger.ExternalServiceLogger) *XingweiService { + return &XingweiService{ + config: XingweiConfig{ + URL: url, + ApiID: apiID, + ApiKey: apiKey, + }, + logger: logger, + } +} + +// generateRequestID 生成请求ID +func (x *XingweiService) generateRequestID() string { + timestamp := time.Now().UnixNano() + // 使用原子计数器确保唯一性 + counter := atomic.AddInt64(&requestIDCounter, 1) + hash := md5.Sum([]byte(fmt.Sprintf("%d_%d_%s", timestamp, counter, x.config.ApiID))) + return fmt.Sprintf("xingwei_%x", hash[:8]) +} + +// createSign 创建签名:使用MD5算法将apiId、timestamp、apiKey字符串拼接生成sign +// 参考Java示例:DigestUtils.md5Hex(apiId + timestamp + apiKey) +func (x *XingweiService) createSign(timestamp int64) string { + signStr := x.config.ApiID + strconv.FormatInt(timestamp, 10) + x.config.ApiKey + hash := md5.Sum([]byte(signStr)) + return fmt.Sprintf("%x", hash) +} + +// CallAPI 调用行为数据的 API +func (x *XingweiService) CallAPI(ctx context.Context, projectID string, params map[string]interface{}) (resp []byte, err error) { + startTime := time.Now() + requestID := x.generateRequestID() + timestamp := time.Now().UnixMilli() + + // 从ctx中获取transactionId + var transactionID string + if ctxTransactionID, ok := ctx.Value("transaction_id").(string); ok { + transactionID = ctxTransactionID + } + + // 记录请求日志 + if x.logger != nil { + x.logger.LogRequest(requestID, transactionID, "xingwei_api", x.config.URL, params) + } + + // 将请求参数转换为JSON + jsonData, marshalErr := json.Marshal(params) + if marshalErr != nil { + err = errors.Join(ErrSystem, marshalErr) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + // 创建HTTP POST请求 + req, newRequestErr := http.NewRequestWithContext(ctx, "POST", x.config.URL, bytes.NewBuffer(jsonData)) + if newRequestErr != nil { + err = errors.Join(ErrSystem, newRequestErr) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("timestamp", strconv.FormatInt(timestamp, 10)) + req.Header.Set("sign", x.createSign(timestamp)) + req.Header.Set("API-ID", x.config.ApiID) + req.Header.Set("project_id", projectID) + + // 创建HTTP客户端 + client := &http.Client{ + Timeout: 20 * time.Second, + } + + // 发送请求 + httpResp, clientDoErr := client.Do(req) + if clientDoErr != nil { + err = errors.Join(ErrSystem, clientDoErr) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + defer func(Body io.ReadCloser) { + closeErr := Body.Close() + if closeErr != nil { + // 记录关闭错误 + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", errors.Join(ErrSystem, fmt.Errorf("关闭响应体失败: %w", closeErr)), params) + } + } + }(httpResp.Body) + + // 计算请求耗时 + duration := time.Since(startTime) + + // 读取响应体 + bodyBytes, ReadErr := io.ReadAll(httpResp.Body) + if ReadErr != nil { + err = errors.Join(ErrSystem, ReadErr) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + // 记录响应日志 + if x.logger != nil { + x.logger.LogResponse(requestID, transactionID, "xingwei_api", httpResp.StatusCode, bodyBytes, duration) + } + + // 检查HTTP状态码 + if httpResp.StatusCode != http.StatusOK { + err = errors.Join(ErrSystem, fmt.Errorf("行为数据请求失败,状态码: %d", httpResp.StatusCode)) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + // 解析响应结构 + var xingweiResp XingweiResponse + if err := json.Unmarshal(bodyBytes, &xingweiResp); err != nil { + err = errors.Join(ErrSystem, fmt.Errorf("响应解析失败: %w", err)) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + // 检查业务状态码 + switch xingweiResp.Code { + case CodeSuccess: + // 成功响应,返回data字段 + if xingweiResp.Data == nil { + return []byte("{}"), nil + } + + // 将data转换为JSON字节 + dataBytes, err := json.Marshal(xingweiResp.Data) + if err != nil { + err = errors.Join(ErrSystem, fmt.Errorf("data字段序列化失败: %w", err)) + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", err, params) + } + return nil, err + } + + return dataBytes, nil + + case CodeNotFound: + // 未查询到结果,返回查空错误 + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", + errors.Join(ErrNotFound, fmt.Errorf("未查询到结果")), params) + } + return nil, errors.Join(ErrNotFound, fmt.Errorf("未查询到结果")) + + case CodeSystemError: + // 系统内部错误 + errorMsg := GetXingweiErrorMessage(xingweiResp.Code) + systemErr := fmt.Errorf("行为数据系统错误[%d]: %s", xingweiResp.Code, errorMsg) + + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", + errors.Join(ErrSystem, systemErr), params) + } + + return nil, errors.Join(ErrSystem, systemErr) + + default: + // 其他业务错误 + errorMsg := GetXingweiErrorMessage(xingweiResp.Code) + businessErr := fmt.Errorf("行为数据业务错误[%d]: %s", xingweiResp.Code, errorMsg) + + if x.logger != nil { + x.logger.LogError(requestID, transactionID, "xingwei_api", + errors.Join(ErrDatasource, businessErr), params) + } + + return nil, errors.Join(ErrDatasource, businessErr) + } +} + +// GetConfig 获取配置信息 +func (x *XingweiService) GetConfig() XingweiConfig { + return x.config +} diff --git a/internal/infrastructure/external/xingwei/xingwei_test.go b/internal/infrastructure/external/xingwei/xingwei_test.go new file mode 100644 index 0000000..e102c50 --- /dev/null +++ b/internal/infrastructure/external/xingwei/xingwei_test.go @@ -0,0 +1,241 @@ +package xingwei + +import ( + "context" + "encoding/json" + "testing" +) + +func TestXingweiService_CreateSign(t *testing.T) { + // 创建测试配置 - 使用nil logger来避免日志问题 + service := NewXingweiService( + "https://sjztyh.chengdaoji.cn/dataCenterManageApi/manage/interface/doc/api/handle", + "test_api_id", + "test_api_key", + nil, // 使用nil logger + ) + + // 测试签名生成 + timestamp := int64(1743474772049) + sign := service.createSign(timestamp) + + // 验证签名不为空 + if sign == "" { + t.Error("签名不能为空") + } + + // 验证签名长度(MD5应该是32位十六进制字符串) + if len(sign) != 32 { + t.Errorf("签名长度应该是32位,实际是%d位", len(sign)) + } + + t.Logf("生成的签名: %s", sign) +} + +func TestXingweiService_CallAPI(t *testing.T) { + // 创建测试配置 - 使用nil logger来避免日志问题 + service := NewXingweiService( + "https://sjztyh.chengdaoji.cn/dataCenterManageApi/manage/interface/doc/api/handle", + "test_api_id", + "test_api_key", + nil, // 使用nil logger + ) + + // 创建测试上下文 + ctx := context.Background() + + // 测试参数 + projectID := "test_project_id" + params := map[string]interface{}{ + "test_param": "test_value", + } + + // 注意:这个测试会实际发送HTTP请求,所以可能会失败 + // 在实际使用中,应该使用mock或者测试服务器 + resp, err := service.CallAPI(ctx, projectID, params) + + // 由于这是真实的外部API调用,我们主要测试错误处理 + if err != nil { + t.Logf("预期的错误(真实API调用): %v", err) + } else { + t.Logf("API调用成功,响应长度: %d", len(resp)) + } +} + +func TestXingweiService_GenerateRequestID(t *testing.T) { + // 创建测试配置 - 使用nil logger来避免日志问题 + service := NewXingweiService( + "https://sjztyh.chengdaoji.cn/dataCenterManageApi/manage/interface/doc/api/handle", + "test_api_id", + "test_api_key", + nil, // 使用nil logger + ) + + // 测试请求ID生成 + requestID1 := service.generateRequestID() + requestID2 := service.generateRequestID() + + // 验证请求ID不为空 + if requestID1 == "" || requestID2 == "" { + t.Error("请求ID不能为空") + } + + // 验证请求ID应该以xingwei_开头 + if len(requestID1) < 8 || requestID1[:8] != "xingwei_" { + t.Error("请求ID应该以xingwei_开头") + } + + // 验证两次生成的请求ID应该不同 + if requestID1 == requestID2 { + t.Error("两次生成的请求ID应该不同") + } + + t.Logf("请求ID1: %s", requestID1) + t.Logf("请求ID2: %s", requestID2) +} + +func TestGetXingweiErrorMessage(t *testing.T) { + // 测试已知错误码(使用常量) + testCases := []struct { + code int + expected string + }{ + {CodeSuccess, "操作成功"}, + {CodeSystemError, "系统内部错误"}, + {CodeMerchantError, "商家相关报错(商家不存在、商家被禁用、商家余额不足)"}, + {CodeAccountExpired, "账户已过期"}, + {CodeIPWhitelistMissing, "未添加ip白名单"}, + {CodeUnauthorized, "未授权调用该接口"}, + {CodeProductIDError, "产品id错误"}, + {CodeInterfaceDisabled, "接口被停用"}, + {CodeQueryException, "接口查询异常,请联系技术人员"}, + {CodeNotFound, "未查询到结果"}, + {9999, "未知错误码: 9999"}, // 测试未知错误码 + } + + for _, tc := range testCases { + result := GetXingweiErrorMessage(tc.code) + if result != tc.expected { + t.Errorf("错误码 %d 的消息不正确,期望: %s, 实际: %s", tc.code, tc.expected, result) + } + } +} + +func TestXingweiResponseParsing(t *testing.T) { + // 测试响应结构解析 + testCases := []struct { + name string + response string + expectedCode int + }{ + { + name: "成功响应", + response: `{"msg": "操作成功", "code": 200, "data": {"result": "test"}}`, + expectedCode: CodeSuccess, + }, + { + name: "商家错误", + response: `{"msg": "商家相关报错", "code": 3001, "data": null}`, + expectedCode: CodeMerchantError, + }, + { + name: "未查询到结果", + response: `{"msg": "未查询到结果", "code": 6000, "data": null}`, + expectedCode: CodeNotFound, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var resp XingweiResponse + err := json.Unmarshal([]byte(tc.response), &resp) + if err != nil { + t.Errorf("解析响应失败: %v", err) + return + } + + if resp.Code != tc.expectedCode { + t.Errorf("错误码不匹配,期望: %d, 实际: %d", tc.expectedCode, resp.Code) + } + + // 测试错误消息获取 + errorMsg := GetXingweiErrorMessage(resp.Code) + if errorMsg == "" { + t.Errorf("无法获取错误码 %d 的消息", resp.Code) + } + + t.Logf("响应: %+v, 错误消息: %s", resp, errorMsg) + }) + } +} + +// TestXingweiErrorHandling 测试错误处理逻辑 +func TestXingweiErrorHandling(t *testing.T) { + // 注意:这个测试主要验证常量定义和错误消息,不需要实际的服务实例 + + // 测试查空错误 + t.Run("NotFound错误", func(t *testing.T) { + // 模拟返回查空响应 + response := `{"msg": "未查询到结果", "code": 6000, "data": null}` + var xingweiResp XingweiResponse + err := json.Unmarshal([]byte(response), &xingweiResp) + if err != nil { + t.Fatalf("解析响应失败: %v", err) + } + + // 验证状态码 + if xingweiResp.Code != CodeNotFound { + t.Errorf("期望状态码 %d, 实际 %d", CodeNotFound, xingweiResp.Code) + } + + // 验证错误消息 + errorMsg := GetXingweiErrorMessage(xingweiResp.Code) + if errorMsg != "未查询到结果" { + t.Errorf("期望错误消息 '未查询到结果', 实际 '%s'", errorMsg) + } + + t.Logf("查空错误测试通过: 状态码=%d, 消息=%s", xingweiResp.Code, errorMsg) + }) + + // 测试系统错误 + t.Run("SystemError错误", func(t *testing.T) { + response := `{"msg": "系统内部错误", "code": 500, "data": null}` + var xingweiResp XingweiResponse + err := json.Unmarshal([]byte(response), &xingweiResp) + if err != nil { + t.Fatalf("解析响应失败: %v", err) + } + + if xingweiResp.Code != CodeSystemError { + t.Errorf("期望状态码 %d, 实际 %d", CodeSystemError, xingweiResp.Code) + } + + errorMsg := GetXingweiErrorMessage(xingweiResp.Code) + if errorMsg != "系统内部错误" { + t.Errorf("期望错误消息 '系统内部错误', 实际 '%s'", errorMsg) + } + + t.Logf("系统错误测试通过: 状态码=%d, 消息=%s", xingweiResp.Code, errorMsg) + }) + + // 测试成功响应 + t.Run("Success响应", func(t *testing.T) { + response := `{"msg": "操作成功", "code": 200, "data": {"result": "test"}}` + var xingweiResp XingweiResponse + err := json.Unmarshal([]byte(response), &xingweiResp) + if err != nil { + t.Fatalf("解析响应失败: %v", err) + } + + if xingweiResp.Code != CodeSuccess { + t.Errorf("期望状态码 %d, 实际 %d", CodeSuccess, xingweiResp.Code) + } + + errorMsg := GetXingweiErrorMessage(xingweiResp.Code) + if errorMsg != "操作成功" { + t.Errorf("期望错误消息 '操作成功', 实际 '%s'", errorMsg) + } + + t.Logf("成功响应测试通过: 状态码=%d, 消息=%s", xingweiResp.Code, errorMsg) + }) +}