diff --git a/docs/组合包动态处理机制说明.md b/docs/组合包动态处理机制说明.md new file mode 100644 index 0000000..a83034d --- /dev/null +++ b/docs/组合包动态处理机制说明.md @@ -0,0 +1,176 @@ +# 组合包动态处理机制说明 + +## 🎉 重大更新 + +组合包系统现在支持**动态处理机制**!这意味着: +- ✅ **零编码**:大部分组合包无需编写任何Go代码 +- ✅ **配置驱动**:只需在数据库配置即可立即使用 +- ✅ **灵活扩展**:特殊需求仍可通过自定义处理器实现 + +## 🔧 工作原理 + +### 处理优先级 +1. **自定义处理器优先**:如果注册了专门的处理器,优先使用 +2. **通用处理器兜底**:COMB开头的API自动使用通用组合包处理器 +3. **数据库驱动**:根据数据库配置自动调用子产品处理器 + +### 系统架构 +``` +API请求 (COMBXXXX) + ↓ +优先查找自定义处理器 + ↓ (未找到) +检查是否COMB开头 + ↓ (是) +通用组合包处理器 + ↓ +查询数据库获取子产品配置 + ↓ +并发调用子产品处理器 + ↓ +聚合结果并返回 +``` + +## 📋 使用方法 + +### 方案1:纯配置组合包(推荐) + +**步骤1:创建组合包产品** +```sql +INSERT INTO products ( + id, code, name, description, + is_package, is_enabled, is_visible, + price, category_id +) VALUES ( + 'uuid1', 'COMB1234', '身份验证组合包', '包含身份证二要素和手机三要素验证', + true, true, true, + 5.00, 'category_id' +); +``` + +**步骤2:配置子产品** +```sql +INSERT INTO product_package_items (package_id, product_id, sort_order) VALUES +('uuid1', 'product_id_1', 1), -- FLXG162A 身份证二要素 +('uuid1', 'product_id_2', 2); -- FLXG54F5 手机三要素 +``` + +**步骤3:直接使用** +```bash +# 立即可用,无需任何代码编写! +POST /api/v1/COMB1234 +{ + "id_card": "123456789012345678", + "name": "张三", + "mobile_no": "13800138000" +} +``` + +### 方案2:自定义逻辑组合包 + +如果需要对结果进行后处理,才需要编写代码: + +**步骤1:创建处理器文件** +```go +// internal/domains/api/services/processors/comb/comb1234_processor.go +func ProcessCOMB1234Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + // 参数验证 + var paramsDto dto.COMB1234Req + 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) + } + + // 调用组合包服务 + combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, "COMB1234") + if err != nil { + return nil, err + } + + // 自定义后处理逻辑 + for _, resp := range combinedResult.Responses { + if resp.ApiCode == "FLXG162A" && resp.Success { + // 添加自定义字段 + if data, ok := resp.Data.(map[string]interface{}); ok { + data["processed_by"] = "COMB1234" + } + } + } + + return json.Marshal(combinedResult) +} +``` + +**步骤2:注册处理器** +```go +// api_request_service.go 中添加 +"COMB1234": comb.ProcessCOMB1234Request, // 有自定义逻辑 +``` + +**步骤3:添加DTO(如需要)** +```go +// dto/api_request_dto.go 中添加 +type COMB1234Req 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"` +} +``` + +## 📊 现有组合包状态 + +### ✅ 已迁移到动态处理 +- **COMB298Y**:删除了专门的处理器文件,现在使用通用处理器 + +### 🔧 保留自定义处理器 +- **COMB86PM**:有特殊逻辑(重命名ApiCode),保留自定义处理器 + +## 🎯 响应格式 + +所有组合包都返回统一的响应格式: + +```json +{ + "responses": [ + { + "api_code": "FLXG162A", + "success": true, + "data": { + // 子产品的响应数据 + } + }, + { + "api_code": "FLXG54F5", + "success": true, + "data": { + // 子产品的响应数据 + } + }, + { + "api_code": "YYSY4B37", + "success": false, + "error": "数据源异常" + } + ] +} +``` + +## 🚀 优势总结 + +1. **开发效率**:90%的组合包无需编写代码 +2. **维护成本**:减少重复代码,统一处理逻辑 +3. **业务灵活**:数据库配置即时生效 +4. **向后兼容**:现有自定义处理器继续工作 +5. **扩展性强**:特殊需求仍可通过自定义处理器实现 + +## ⚡ 性能特性 + +- **并发处理**:所有子产品并发调用 +- **独立失败**:单个子产品失败不影响其他 +- **智能排序**:通过sort_order控制响应顺序 +- **错误隔离**:每个子产品的错误独立处理 + +现在,创建一个新的组合包就像配置数据库一样简单!🎉 diff --git a/go.mod b/go.mod index df81c1a..c30bf6b 100644 --- a/go.mod +++ b/go.mod @@ -97,6 +97,7 @@ require ( github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect diff --git a/go.sum b/go.sum index 0ef7dc3..5daf6b0 100644 --- a/go.sum +++ b/go.sum @@ -227,6 +227,8 @@ github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqj github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index f8830cb..6d9ef26 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -277,6 +277,31 @@ type IVYZ7F3AReq struct { Authorized string `json:"authorized" validate:"required,oneof=0 1"` } +// 新增的QYGL处理器DTO +type QYGL5A3CReq struct { + EntCode string `json:"ent_code" validate:"required,validUSCI"` + PageSize int `json:"page_size" validate:"omitempty,min=1,max=100"` + PageNum int `json:"page_num" validate:"omitempty,min=1"` +} + +type QYGL8B4DReq struct { + EntCode string `json:"ent_code" validate:"required,validUSCI"` + PageSize int `json:"page_size" validate:"omitempty,min=1,max=100"` + PageNum int `json:"page_num" validate:"omitempty,min=1"` +} + +type QYGL9E2FReq struct { + EntCode string `json:"ent_code" validate:"required,validUSCI"` + PageSize int `json:"page_size" validate:"omitempty,min=1,max=100"` + PageNum int `json:"page_num" validate:"omitempty,min=1"` +} + +type QYGL7C1AReq struct { + EntCode string `json:"ent_code" validate:"required,validUSCI"` + PageSize int `json:"page_size" validate:"omitempty,min=1,max=100"` + PageNum int `json:"page_num" validate:"omitempty,min=1"` +} + type YYSY4F2EReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` IDCard string `json:"id_card" validate:"required,validIDCard"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index 6d2ebeb..f4a5207 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -2,6 +2,7 @@ package services import ( "context" + "encoding/json" "fmt" "tyapi-server/internal/application/api/commands" @@ -119,6 +120,10 @@ func registerAllProcessors(combService *comb.CombService) { "QYGL8271": qygl.ProcessQYGL8271Request, "QYGLB4C0": qygl.ProcessQYGLB4C0Request, "QYGL23T7": qygl.ProcessQYGL23T7Request, // 企业三要素验证 + "QYGL5A3C": qygl.ProcessQYGL5A3CRequest, // 对外投资历史 + "QYGL8B4D": qygl.ProcessQYGL8B4DRequest, // 融资历史 + "QYGL9E2F": qygl.ProcessQYGL9E2FRequest, // 行政处罚 + "QYGL7C1A": qygl.ProcessQYGL7C1ARequest, // 经营异常 "COMENT01": qygl.ProcessCOMENT01Request, // 企业风险报告 // YYSY系列处理器 @@ -151,9 +156,8 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZ5E3F": ivyz.ProcessIVYZ5E3FRequest, "IVYZ7F3A": ivyz.ProcessIVYZ7F3ARequest, - // COMB系列处理器 - "COMB298Y": comb.ProcessCOMB298YRequest, - "COMB86PM": comb.ProcessCOMB86PMRequest, + // COMB系列处理器 - 只注册有自定义逻辑的组合包 + "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode // QCXG系列处理器 "QCXG7A2B": qcxg.ProcessQCXG7A2BRequest, @@ -185,10 +189,31 @@ var RequestProcessors map[string]processors.ProcessorFunc // PreprocessRequestApi 调用指定的请求处理函数 func (a *ApiRequestService) PreprocessRequestApi(ctx context.Context, apiCode string, params []byte, options *commands.ApiCallOptions, callContext *processors.CallContext) ([]byte, error) { + // 设置Options和CallContext到依赖容器 + deps := a.processorDeps.WithOptions(options).WithCallContext(callContext) + + // 1. 优先查找已注册的自定义处理器 if processor, exists := RequestProcessors[apiCode]; exists { - // 设置Options和CallContext到依赖容器 - deps := a.processorDeps.WithOptions(options).WithCallContext(callContext) return processor(ctx, params, deps) } + + // 2. 检查是否为组合包(COMB开头),使用通用组合包处理器 + if len(apiCode) >= 4 && apiCode[:4] == "COMB" { + return a.processGenericCombRequest(ctx, apiCode, params, deps) + } + return nil, fmt.Errorf("%s: 未找到处理器: %s", ErrSystem, apiCode) } + +// processGenericCombRequest 通用组合包处理器 +func (a *ApiRequestService) processGenericCombRequest(ctx context.Context, apiCode string, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + // 调用组合包服务处理请求 + // 这里不需要验证参数,因为组合包的参数验证由各个子处理器负责 + combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, apiCode) + if err != nil { + return nil, err + } + + // 直接返回组合结果,无任何自定义处理 + return json.Marshal(combinedResult) +} diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 1dd57f7..1344ffd 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -88,6 +88,10 @@ func (s *FormConfigServiceImpl) getDTOStruct(apiCode string) interface{} { "QYGL8271": &dto.QYGL8271Req{}, "QYGLB4C0": &dto.QYGLB4C0Req{}, "QYGL23T7": &dto.QYGL23T7Req{}, + "QYGL5A3C": &dto.QYGL5A3CReq{}, + "QYGL8B4D": &dto.QYGL8B4DReq{}, + "QYGL9E2F": &dto.QYGL9E2FReq{}, + "QYGL7C1A": &dto.QYGL7C1AReq{}, "YYSY4B37": &dto.YYSY4B37Req{}, "YYSY4B21": &dto.YYSY4B21Req{}, "YYSY6F2E": &dto.YYSY6F2EReq{}, @@ -131,7 +135,19 @@ func (s *FormConfigServiceImpl) getDTOStruct(apiCode string) interface{} { "FLXG8B4D": &dto.FLXG8B4DReq{}, } - return dtoMap[apiCode] + // 优先返回已配置的DTO + if dto, exists := dtoMap[apiCode]; exists { + return dto + } + + // 检查是否为通用组合包(COMB开头且未单独配置) + if len(apiCode) >= 4 && apiCode[:4] == "COMB" { + // 对于通用组合包,返回一个通用的空结构体,表示无需特定参数验证 + // 因为组合包的参数验证由各个子处理器负责 + return &struct{}{} + } + + return nil } // parseDTOFields 通过反射解析DTO结构体字段 diff --git a/internal/domains/api/services/processors/comb/README.md b/internal/domains/api/services/processors/comb/README.md new file mode 100644 index 0000000..7732bd2 --- /dev/null +++ b/internal/domains/api/services/processors/comb/README.md @@ -0,0 +1,74 @@ +# 组合包处理器说明 + +## 🚀 动态组合包机制 + +从现在开始,组合包支持**动态处理机制**,大大简化了组合包的开发和维护工作。 + +## 📋 工作原理 + +### 1. 自动识别 +- 所有以 `COMB` 开头的API编码会被自动识别为组合包 +- 系统会自动调用通用组合包处理器处理请求 + +### 2. 处理流程 +1. **优先级检查**:首先检查是否有注册的自定义处理器 +2. **通用处理**:如果没有自定义处理器,且API编码以COMB开头,使用通用处理器 +3. **数据库驱动**:根据数据库中的组合包配置自动调用相应的子产品处理器 + +## 🛠️ 使用方式 + +### 普通组合包(无自定义逻辑) +**只需要在数据库配置,无需编写任何代码!** + +1. 在 `products` 表中创建组合包产品: + ```sql + INSERT INTO products (code, name, is_package, ...) + VALUES ('COMB1234', '新组合包', true, ...); + ``` + +2. 在 `product_package_items` 表中配置子产品: + ```sql + INSERT INTO product_package_items (package_id, product_id, sort_order) + VALUES + ('组合包产品ID', '子产品1ID', 1), + ('组合包产品ID', '子产品2ID', 2); + ``` + +3. **直接调用**:无需任何额外编码,API立即可用! + +### 自定义组合包(有特殊逻辑) +如果需要对组合包结果进行后处理,才需要编写代码: + +1. **创建处理器文件**:`combXXXX_processor.go` +2. **注册处理器**:在 `api_request_service.go` 中注册 +3. **实现自定义逻辑**:在处理器中实现特殊业务逻辑 + +## 📁 现有组合包示例 + +### COMB86PM(自定义处理器) +```go +// 有自定义逻辑:重命名子产品ApiCode +for _, resp := range combinedResult.Responses { + if resp.ApiCode == "FLXGBC21" { + resp.ApiCode = "FLXG54F5" + } +} +``` + +### COMB298Y(通用处理器) +- **无需编码**:已删除专门的处理器文件 +- **自动处理**:通过数据库配置自动工作 + +## ✅ 优势 + +1. **零配置**:普通组合包只需数据库配置,无需编码 +2. **灵活性**:特殊需求仍可通过自定义处理器实现 +3. **维护性**:减少重复代码,统一处理逻辑 +4. **扩展性**:新增组合包极其简单,配置即用 + +## 🔧 开发建议 + +1. **优先使用通用处理器**:除非有特殊业务逻辑,否则不要编写自定义处理器 +2. **命名规范**:组合包编码必须以 `COMB` 开头 +3. **数据库配置**:确保组合包在数据库中正确配置了 `is_package=true` 和子产品关系 +4. **排序控制**:通过 `sort_order` 字段控制子产品在响应中的顺序 diff --git a/internal/domains/api/services/processors/comb/comb298y_processor.go b/internal/domains/api/services/processors/comb/comb298y_processor.go deleted file mode 100644 index 0e17edf..0000000 --- a/internal/domains/api/services/processors/comb/comb298y_processor.go +++ /dev/null @@ -1,30 +0,0 @@ -package comb - -import ( - "context" - "encoding/json" - "errors" - - "tyapi-server/internal/domains/api/dto" - "tyapi-server/internal/domains/api/services/processors" -) - -// ProcessCOMB298YRequest COMB298Y API处理方法 -func ProcessCOMB298YRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { - var paramsDto dto.COMB298YReq - 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) - } - - // 调用组合包服务处理请求 - // Options会自动传递给所有子处理器 - combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, "COMB298Y") - if err != nil { - return nil, err - } - return json.Marshal(combinedResult) -} diff --git a/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go b/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go new file mode 100644 index 0000000..32fde94 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go @@ -0,0 +1,63 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL5A3CRequest QYGL5A3C API处理方法 - 对外投资历史 +func ProcessQYGL5A3CRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL5A3CReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 对外投资历史 + response, err := deps.TianYanChaService.CallAPI(ctx, "InvestHistory", apiParams) + if err != nil { + if err.Error() == "数据源异常" { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go b/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go new file mode 100644 index 0000000..a5508a3 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go @@ -0,0 +1,63 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL7C1ARequest QYGL7C1A API处理方法 - 经营异常 +func ProcessQYGL7C1ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL7C1AReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 经营异常 + response, err := deps.TianYanChaService.CallAPI(ctx, "AbnormalInfo", apiParams) + if err != nil { + if err.Error() == "数据源异常" { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go b/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go new file mode 100644 index 0000000..27af31a --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go @@ -0,0 +1,63 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL8B4DRequest QYGL8B4D API处理方法 - 融资历史 +func ProcessQYGL8B4DRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL8B4DReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 融资历史 + response, err := deps.TianYanChaService.CallAPI(ctx, "FinancingHistory", apiParams) + if err != nil { + if err.Error() == "数据源异常" { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go b/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go new file mode 100644 index 0000000..354ebaa --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go @@ -0,0 +1,63 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL9E2FRequest QYGL9E2F API处理方法 - 行政处罚 +func ProcessQYGL9E2FRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL9E2FReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 行政处罚 + response, err := deps.TianYanChaService.CallAPI(ctx, "PunishmentInfo", apiParams) + if err != nil { + if err.Error() == "数据源异常" { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/infrastructure/external/tianyancha/tianyancha_service.go b/internal/infrastructure/external/tianyancha/tianyancha_service.go index 1b8ad64..745bf07 100644 --- a/internal/infrastructure/external/tianyancha/tianyancha_service.go +++ b/internal/infrastructure/external/tianyancha/tianyancha_service.go @@ -21,7 +21,11 @@ var ( // APIEndpoints 天眼查 API 端点映射 var APIEndpoints = map[string]string{ - "VerifyThreeElements": "/open/ic/verify/2.0", // 企业三要素验证 + "VerifyThreeElements": "/open/ic/verify/2.0", // 企业三要素验证 + "InvestHistory": "/open/hi/invest/2.0", // 对外投资历史 + "FinancingHistory": "/open/cd/findHistoryRongzi/2.0", // 融资历史 + "PunishmentInfo": "/open/mr/punishmentInfo/3.0", // 行政处罚 + "AbnormalInfo": "/open/mr/abnormal/2.0", // 经营异常 } // TianYanChaConfig 天眼查配置