From f482f0a6e886f358b1eabeb063661467b149c45c Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Mon, 4 Aug 2025 17:16:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=85=A5=E9=98=BF=E9=87=8C=E4=BA=91?= =?UTF-8?q?=E4=BA=8C=E8=A6=81=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yaml | 7 + internal/config/config.go | 6 + internal/container/container.go | 8 + .../api/services/api_request_service.go | 6 +- .../api/services/processors/dependencies.go | 4 + .../processors/flxg/flxg0v4b_processor.go | 7 +- .../processors/qygl/qygl23t7_processor.go | 78 ++++--- .../processors/yysy/yysybe08_processor.go | 90 +++++--- .../yysy/yysybe08_processor_test.go | 158 ++++++++++++++ .../domains/user/entities/contract_info.go | 91 +++++--- .../external/alicloud/README.md | 194 ++++++++++++++++++ .../external/alicloud/alicloud_service.go | 82 ++++++++ .../alicloud/alicloud_service_test.go | 143 +++++++++++++ .../external/alicloud/example.go | 76 +++++++ .../external/alicloud/example_advanced.go | 162 +++++++++++++++ 15 files changed, 1012 insertions(+), 100 deletions(-) create mode 100644 internal/domains/api/services/processors/yysy/yysybe08_processor_test.go create mode 100644 internal/infrastructure/external/alicloud/README.md create mode 100644 internal/infrastructure/external/alicloud/alicloud_service.go create mode 100644 internal/infrastructure/external/alicloud/alicloud_service_test.go create mode 100644 internal/infrastructure/external/alicloud/example.go create mode 100644 internal/infrastructure/external/alicloud/example_advanced.go diff --git a/config.yaml b/config.yaml index f1b5dc3..57cf45a 100644 --- a/config.yaml +++ b/config.yaml @@ -212,3 +212,10 @@ alipay: tianyancha: base_url: http://open.api.tianyancha.com/services api_key: e6a43dc9-786e-4a16-bb12-392b8201d8e2 + +# =========================================== +# ☁️ 阿里云配置 +# =========================================== +alicloud: + host: "https://kzidcardv1.market.alicloudapi.com" + app_code: "d55b58829efb41c8aa8e86769cba4844" diff --git a/internal/config/config.go b/internal/config/config.go index 1bebe25..2a5e2bb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -30,6 +30,7 @@ type Config struct { AliPay AliPayConfig `mapstructure:"alipay"` Yushan YushanConfig `mapstructure:"yushan"` TianYanCha TianYanChaConfig `mapstructure:"tianyancha"` + Alicloud AlicloudConfig `mapstructure:"alicloud"` } // ServerConfig HTTP服务器配置 @@ -329,6 +330,11 @@ type TianYanChaConfig struct { APIKey string `mapstructure:"api_key"` } +type AlicloudConfig struct { + Host string `mapstructure:"host"` + AppCode string `mapstructure:"app_code"` +} + // DomainConfig 域名配置 type DomainConfig struct { API string `mapstructure:"api"` // API域名 diff --git a/internal/container/container.go b/internal/container/container.go index 71b6f87..10b92c4 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -27,6 +27,7 @@ import ( finance_repo "tyapi-server/internal/infrastructure/database/repositories/finance" product_repo "tyapi-server/internal/infrastructure/database/repositories/product" infra_events "tyapi-server/internal/infrastructure/events" + "tyapi-server/internal/infrastructure/external/alicloud" "tyapi-server/internal/infrastructure/external/email" "tyapi-server/internal/infrastructure/external/ocr" "tyapi-server/internal/infrastructure/external/sms" @@ -326,6 +327,13 @@ func NewContainer() *Container { 30*time.Second, // 默认超时时间 ) }, + // AlicloudService - 阿里云服务 + func(cfg *config.Config) *alicloud.AlicloudService { + return alicloud.NewAlicloudService( + cfg.Alicloud.Host, + cfg.Alicloud.AppCode, + ) + }, sharedhttp.NewGinRouter, ), diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index 42fb241..0489480 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -14,6 +14,7 @@ import ( "tyapi-server/internal/domains/api/services/processors/qygl" "tyapi-server/internal/domains/api/services/processors/yysy" "tyapi-server/internal/domains/product/services" + "tyapi-server/internal/infrastructure/external/alicloud" "tyapi-server/internal/infrastructure/external/tianyancha" "tyapi-server/internal/infrastructure/external/westdex" "tyapi-server/internal/infrastructure/external/yushan" @@ -32,6 +33,7 @@ type ApiRequestService struct { westDexService *westdex.WestDexService yushanService *yushan.YushanService tianYanChaService *tianyancha.TianYanChaService + alicloudService *alicloud.AlicloudService validator interfaces.RequestValidator processorDeps *processors.ProcessorDependencies combService *comb.CombService @@ -41,6 +43,7 @@ func NewApiRequestService( westDexService *westdex.WestDexService, yushanService *yushan.YushanService, tianYanChaService *tianyancha.TianYanChaService, + alicloudService *alicloud.AlicloudService, validator interfaces.RequestValidator, productManagementService *services.ProductManagementService, ) *ApiRequestService { @@ -48,7 +51,7 @@ func NewApiRequestService( combService := comb.NewCombService(productManagementService) // 创建处理器依赖容器 - processorDeps := processors.NewProcessorDependencies(westDexService, yushanService, tianYanChaService, validator, combService) + processorDeps := processors.NewProcessorDependencies(westDexService, yushanService, tianYanChaService, alicloudService, validator, combService) // 统一注册所有处理器 registerAllProcessors(combService) @@ -57,6 +60,7 @@ func NewApiRequestService( westDexService: westDexService, yushanService: yushanService, tianYanChaService: tianYanChaService, + alicloudService: alicloudService, validator: validator, processorDeps: processorDeps, combService: combService, diff --git a/internal/domains/api/services/processors/dependencies.go b/internal/domains/api/services/processors/dependencies.go index 9530937..c2c8a39 100644 --- a/internal/domains/api/services/processors/dependencies.go +++ b/internal/domains/api/services/processors/dependencies.go @@ -3,6 +3,7 @@ package processors import ( "context" "tyapi-server/internal/application/api/commands" + "tyapi-server/internal/infrastructure/external/alicloud" "tyapi-server/internal/infrastructure/external/tianyancha" "tyapi-server/internal/infrastructure/external/westdex" "tyapi-server/internal/infrastructure/external/yushan" @@ -19,6 +20,7 @@ type ProcessorDependencies struct { WestDexService *westdex.WestDexService YushanService *yushan.YushanService TianYanChaService *tianyancha.TianYanChaService + AlicloudService *alicloud.AlicloudService Validator interfaces.RequestValidator CombService CombServiceInterface // Changed to interface to break import cycle Options *commands.ApiCallOptions // 添加Options支持 @@ -29,6 +31,7 @@ func NewProcessorDependencies( westDexService *westdex.WestDexService, yushanService *yushan.YushanService, tianYanChaService *tianyancha.TianYanChaService, + alicloudService *alicloud.AlicloudService, validator interfaces.RequestValidator, combService CombServiceInterface, // Changed to interface ) *ProcessorDependencies { @@ -36,6 +39,7 @@ func NewProcessorDependencies( WestDexService: westDexService, YushanService: yushanService, TianYanChaService: tianYanChaService, + AlicloudService: alicloudService, Validator: validator, CombService: combService, Options: nil, // 初始化为nil,在调用时设置 diff --git a/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go b/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go index 1a20164..2b6f4d8 100644 --- a/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go +++ b/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go @@ -36,9 +36,10 @@ func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors reqData := map[string]interface{}{ "data": map[string]interface{}{ - "name": encryptedName, - "idcard": encryptedIDCard, - "inquired_auth": paramsDto.AuthDate, + "name": encryptedName, + "idcard": encryptedIDCard, + "inquired_auth": paramsDto.AuthDate, + // "auth_authorizeFileCode": paramsDto.AuthAuthorizeFileCode, }, } respBytes, err := deps.WestDexService.CallAPI("G22SC01", reqData) diff --git a/internal/domains/api/services/processors/qygl/qygl23t7_processor.go b/internal/domains/api/services/processors/qygl/qygl23t7_processor.go index 48407fe..7faee63 100644 --- a/internal/domains/api/services/processors/qygl/qygl23t7_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl23t7_processor.go @@ -3,13 +3,10 @@ package qygl import ( "context" "encoding/json" - "errors" "fmt" - "time" "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/westdex" "github.com/tidwall/gjson" ) @@ -74,58 +71,55 @@ func ProcessQYGL23T7Request(ctx context.Context, params []byte, deps *processors return createStatusResponse(1), nil } - // 天眼查三要素验证通过,继续调用WestDex身份证二要素验证 - // 加密姓名和身份证号 - encryptedName, err := deps.WestDexService.Encrypt(paramsDto.LegalPerson) - if err != nil { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - - encryptedIDCard, err := deps.WestDexService.Encrypt(paramsDto.IDCard) - if err != nil { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - - // 构建WestDex身份证二要素验证请求参数(参考yysybe08_processor.go) + // 天眼查三要素验证通过,继续调用阿里云身份证二要素验证 + // 构建阿里云二要素验证请求参数 reqData := map[string]interface{}{ - "data": map[string]interface{}{ - "xM": encryptedName, - "gMSFZHM": encryptedIDCard, - "customerNumber": deps.WestDexService.GetConfig().SecretId, - "timeStamp": fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond)), - }, + "name": paramsDto.LegalPerson, + "idcard": paramsDto.IDCard, } - // 调用WestDex身份证二要素验证API - respBytes, err := deps.WestDexService.CallAPI("layoutIdcard", reqData) + // 调用阿里云二要素验证API + respBytes, err := deps.AlicloudService.CallAPI("api-mall/api/id_card/check", reqData) if err != nil { - if !errors.Is(err, westdex.ErrDatasource) { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - } - - // 使用gjson获取resultCode - resultCode := gjson.GetBytes(respBytes, "ctidRequest.ctidAuth.resultCode") - if !resultCode.Exists() { return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) } - // 获取resultCode的第一个字符 - resultCodeStr := resultCode.String() - if len(resultCodeStr) == 0 { + // 解析阿里云响应 + var alicloudResponse struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` + } + + if err := json.Unmarshal(respBytes, &alicloudResponse); err != nil { return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) } - firstChar := string(resultCodeStr[0]) - if firstChar != "0" && firstChar != "5" { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) + // 检查响应状态 + if alicloudResponse.Code != 200 && alicloudResponse.Code != 400 { + return nil, fmt.Errorf("%s: %s", processors.ErrDatasource, alicloudResponse.Msg) } - if firstChar == "0" { - return createStatusResponse(0), nil - } else if firstChar == "5" { + + // 根据阿里云响应结果返回状态 + if alicloudResponse.Code == 400 { + // 身份证号格式错误,返回状态2 return createStatusResponse(2), nil } else { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) + if alicloudResponse.Data.Result == 0 { + // 验证通过,返回状态0 + return createStatusResponse(0), nil + } else { + // 验证失败,返回状态2 + return createStatusResponse(2), nil + } } } diff --git a/internal/domains/api/services/processors/yysy/yysybe08_processor.go b/internal/domains/api/services/processors/yysy/yysybe08_processor.go index 652bf81..92f090b 100644 --- a/internal/domains/api/services/processors/yysy/yysybe08_processor.go +++ b/internal/domains/api/services/processors/yysy/yysybe08_processor.go @@ -3,16 +3,13 @@ package yysy import ( "context" "encoding/json" - "errors" "fmt" - "time" "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/westdex" ) -// ProcessYYSYBE08Request YYSYBE08 API处理方法 +// ProcessYYSYBE08Request YYSYBE08 API处理方法 - 使用阿里云二要素验证 func ProcessYYSYBE08Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { var paramsDto dto.YYSYBE08Req if err := json.Unmarshal(params, ¶msDto); err != nil { @@ -23,31 +20,70 @@ func ProcessYYSYBE08Request(ctx context.Context, params []byte, deps *processors return nil, fmt.Errorf("%s: %w", processors.ErrInvalidParam, err) } - encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name) - if err != nil { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - - encryptedIDCard, err := deps.WestDexService.Encrypt(paramsDto.IDCard) - if err != nil { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - + // 调用阿里云二要素验证API reqData := map[string]interface{}{ - "data": map[string]interface{}{ - "xM": encryptedName, - "gMSFZHM": encryptedIDCard, - "customerNumber": deps.WestDexService.GetConfig().SecretId, - "timeStamp": fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond)), + "name": paramsDto.Name, + "idcard": paramsDto.IDCard, + } + + respBytes, err := deps.AlicloudService.CallAPI("api-mall/api/id_card/check", reqData) + if err != nil { + return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) + } + + // 解析阿里云响应 + var alicloudResponse struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` + } + + if err := json.Unmarshal(respBytes, &alicloudResponse); err != nil { + return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) + } + + // 检查响应状态 + if alicloudResponse.Code != 200 && alicloudResponse.Code != 400 { + return nil, fmt.Errorf("%s: %s", processors.ErrDatasource, alicloudResponse.Msg) + } + + // 构建返回结果 + resultCode := "0XXX" // 默认成功 + resultMsg := "验证通过" + verifyResult := "一致" + if alicloudResponse.Code == 400 { + resultCode = "5XXX" + resultMsg = "请输入有效的身份证号码" + verifyResult = "不一致" + } else { + if alicloudResponse.Data.Result != 0 { + // 验证失败 + resultCode = "5XXX" + resultMsg = "身份证号不匹配" + verifyResult = "不一致" + } + } + // 构建最终响应结构 + response := map[string]interface{}{ + "ctidRequest": map[string]interface{}{ + "ctidAuth": map[string]interface{}{ + "resultCode": resultCode, + "resultMsg": resultMsg, + "name": paramsDto.Name, + "idCard": paramsDto.IDCard, + "verifyResult": verifyResult, + }, }, } - respBytes, err := deps.WestDexService.CallAPI("layoutIdcard", reqData) - if err != nil { - if !errors.Is(err, westdex.ErrDatasource) { - return nil, fmt.Errorf("%s: %w", processors.ErrSystem, err) - } - } - - return respBytes, nil + // 返回JSON格式的响应 + return json.Marshal(response) } diff --git a/internal/domains/api/services/processors/yysy/yysybe08_processor_test.go b/internal/domains/api/services/processors/yysy/yysybe08_processor_test.go new file mode 100644 index 0000000..eb359b3 --- /dev/null +++ b/internal/domains/api/services/processors/yysy/yysybe08_processor_test.go @@ -0,0 +1,158 @@ +package yysy + +import ( + "encoding/json" + "testing" +) + +func TestYYSYBE08ResponseStructure(t *testing.T) { + // 测试响应结构构建逻辑 + resultCode := "1001" + resultMsg := "验证通过" + verifyResult := "一致" + + // 模拟阿里云返回result=0(一致)的情况 + alicloudResult := 0 + if alicloudResult == 0 { + // 验证成功 + resultCode = "1001" + resultMsg = "验证通过" + verifyResult = "一致" + } else { + // 验证失败 + resultCode = "1002" + resultMsg = "身份证号不匹配" + verifyResult = "不一致" + } + + // 构建响应结构 + response := map[string]interface{}{ + "ctidRequest": map[string]interface{}{ + "ctidAuth": map[string]interface{}{ + "resultCode": resultCode, + "resultMsg": resultMsg, + "name": "张荣宏", + "idCard": "45212220000827423X", + "verifyResult": verifyResult, + }, + }, + } + + // 序列化为JSON + jsonData, err := json.Marshal(response) + if err != nil { + t.Fatalf("JSON序列化失败: %v", err) + } + + // 验证JSON结构 + var parsedResponse map[string]interface{} + if err := json.Unmarshal(jsonData, &parsedResponse); err != nil { + t.Fatalf("JSON反序列化失败: %v", err) + } + + // 验证字段存在 + ctidRequest, exists := parsedResponse["ctidRequest"] + if !exists { + t.Fatal("响应中缺少ctidRequest字段") + } + + ctidAuth, exists := ctidRequest.(map[string]interface{})["ctidAuth"] + if !exists { + t.Fatal("响应中缺少ctidAuth字段") + } + + authData := ctidAuth.(map[string]interface{}) + + // 验证字段值 + expectedFields := map[string]string{ + "resultCode": "1001", + "resultMsg": "验证通过", + "name": "张荣宏", + "idCard": "45212220000827423X", + "verifyResult": "一致", + } + + for field, expectedValue := range expectedFields { + if authData[field] != expectedValue { + t.Errorf("字段%s期望值为%s,实际为%s", field, expectedValue, authData[field]) + } + } + + t.Logf("测试成功,响应结构: %s", string(jsonData)) +} + +func TestYYSYBE08ResponseStructure_Failure(t *testing.T) { + // 测试验证失败的情况 + resultCode := "1002" + resultMsg := "身份证号不匹配" + verifyResult := "不一致" + + // 模拟阿里云返回result=1(不一致)的情况 + alicloudResult := 1 + if alicloudResult == 0 { + // 验证成功 + resultCode = "1001" + resultMsg = "验证通过" + verifyResult = "一致" + } else { + // 验证失败 + resultCode = "1002" + resultMsg = "身份证号不匹配" + verifyResult = "不一致" + } + + // 构建响应结构 + response := map[string]interface{}{ + "ctidRequest": map[string]interface{}{ + "ctidAuth": map[string]interface{}{ + "resultCode": resultCode, + "resultMsg": resultMsg, + "name": "张三", + "idCard": "110101199001011235", + "verifyResult": verifyResult, + }, + }, + } + + // 序列化为JSON + jsonData, err := json.Marshal(response) + if err != nil { + t.Fatalf("JSON序列化失败: %v", err) + } + + // 验证JSON结构 + var parsedResponse map[string]interface{} + if err := json.Unmarshal(jsonData, &parsedResponse); err != nil { + t.Fatalf("JSON反序列化失败: %v", err) + } + + // 验证字段存在 + ctidRequest, exists := parsedResponse["ctidRequest"] + if !exists { + t.Fatal("响应中缺少ctidRequest字段") + } + + ctidAuth, exists := ctidRequest.(map[string]interface{})["ctidAuth"] + if !exists { + t.Fatal("响应中缺少ctidAuth字段") + } + + authData := ctidAuth.(map[string]interface{}) + + // 验证字段值 + expectedFields := map[string]string{ + "resultCode": "1002", + "resultMsg": "身份证号不匹配", + "name": "张三", + "idCard": "110101199001011235", + "verifyResult": "不一致", + } + + for field, expectedValue := range expectedFields { + if authData[field] != expectedValue { + t.Errorf("字段%s期望值为%s,实际为%s", field, expectedValue, authData[field]) + } + } + + t.Logf("测试成功,失败响应结构: %s", string(jsonData)) +} \ No newline at end of file diff --git a/internal/domains/user/entities/contract_info.go b/internal/domains/user/entities/contract_info.go index b9ce8b8..75cf69a 100644 --- a/internal/domains/user/entities/contract_info.go +++ b/internal/domains/user/entities/contract_info.go @@ -2,12 +2,18 @@ package entities import ( "fmt" + "math/rand" "time" "github.com/google/uuid" "gorm.io/gorm" ) +// 初始化随机数种子 +func init() { + rand.Seed(time.Now().UnixNano()) +} + // ContractType 合同类型枚举 type ContractType string @@ -20,15 +26,16 @@ const ( // 存储企业签署的合同信息,一个企业可以有多个合同 type ContractInfo struct { // 基础标识 - ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"合同信息唯一标识"` + ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"合同信息唯一标识"` EnterpriseInfoID string `gorm:"type:varchar(36);not null;index" json:"enterprise_info_id" comment:"关联企业信息ID"` - UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"关联用户ID"` + UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"关联用户ID"` // 合同基本信息 - ContractName string `gorm:"type:varchar(255);not null" json:"contract_name" comment:"合同名称"` - ContractType ContractType `gorm:"type:varchar(50);not null;index" json:"contract_type" comment:"合同类型"` - ContractFileID string `gorm:"type:varchar(100);not null" json:"contract_file_id" comment:"合同文件ID"` - ContractFileURL string `gorm:"type:varchar(500);not null" json:"contract_file_url" comment:"合同文件下载链接"` + // ContractCode string `gorm:"type:varchar(255);not null" json:"contract_code" comment:"合同编号"` + ContractName string `gorm:"type:varchar(255);not null" json:"contract_name" comment:"合同名称"` + ContractType ContractType `gorm:"type:varchar(50);not null;index" json:"contract_type" comment:"合同类型"` + ContractFileID string `gorm:"type:varchar(100);not null" json:"contract_file_id" comment:"合同文件ID"` + ContractFileURL string `gorm:"type:varchar(500);not null" json:"contract_file_url" comment:"合同文件下载链接"` // 时间戳字段 CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"` @@ -83,15 +90,19 @@ func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType return nil, fmt.Errorf("无效的合同类型: %s", contractType) } + // 生成合同编码 + // contractCode := GenerateContractCode(contractType) + contractInfo := &ContractInfo{ ID: uuid.New().String(), EnterpriseInfoID: enterpriseInfoID, UserID: userID, - ContractName: contractName, - ContractType: contractType, - ContractFileID: contractFileID, - ContractFileURL: contractFileURL, - domainEvents: make([]interface{}, 0), + // ContractCode: contractCode, + ContractName: contractName, + ContractType: contractType, + ContractFileID: contractFileID, + ContractFileURL: contractFileURL, + domainEvents: make([]interface{}, 0), } // 添加领域事件 @@ -99,9 +110,10 @@ func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType ContractInfoID: contractInfo.ID, EnterpriseInfoID: enterpriseInfoID, UserID: userID, - ContractName: contractName, - ContractType: string(contractType), - CreatedAt: time.Now(), + // ContractCode: contractCode, + ContractName: contractName, + ContractType: string(contractType), + CreatedAt: time.Now(), }) return contractInfo, nil @@ -133,14 +145,14 @@ func (c *ContractInfo) UpdateContractInfo(contractName, contractFileID, contract // 添加领域事件 c.addDomainEvent(&ContractInfoUpdatedEvent{ - ContractInfoID: c.ID, - EnterpriseInfoID: c.EnterpriseInfoID, - UserID: c.UserID, - OldContractName: oldContractName, - NewContractName: contractName, + ContractInfoID: c.ID, + EnterpriseInfoID: c.EnterpriseInfoID, + UserID: c.UserID, + OldContractName: oldContractName, + NewContractName: contractName, OldContractFileID: oldContractFileID, NewContractFileID: contractFileID, - UpdatedAt: time.Now(), + UpdatedAt: time.Now(), }) return nil @@ -186,6 +198,9 @@ func (c *ContractInfo) validateBasicFields() error { if c.UserID == "" { return fmt.Errorf("用户ID不能为空") } + // if c.ContractCode == "" { + // return fmt.Errorf("合同编码不能为空") + // } if c.ContractName == "" { return fmt.Errorf("合同名称不能为空") } @@ -291,12 +306,13 @@ func isValidURL(url string) bool { // ContractInfoCreatedEvent 合同信息创建事件 type ContractInfoCreatedEvent struct { - ContractInfoID string `json:"contract_info_id"` - EnterpriseInfoID string `json:"enterprise_info_id"` - UserID string `json:"user_id"` - ContractName string `json:"contract_name"` - ContractType string `json:"contract_type"` - CreatedAt time.Time `json:"created_at"` + ContractInfoID string `json:"contract_info_id"` + EnterpriseInfoID string `json:"enterprise_info_id"` + UserID string `json:"user_id"` + // ContractCode string `json:"contract_code"` + ContractName string `json:"contract_name"` + ContractType string `json:"contract_type"` + CreatedAt time.Time `json:"created_at"` } // ContractInfoUpdatedEvent 合同信息更新事件 @@ -319,4 +335,25 @@ type ContractInfoDeletedEvent struct { ContractName string `json:"contract_name"` ContractType string `json:"contract_type"` DeletedAt time.Time `json:"deleted_at"` -} \ No newline at end of file +} + +// GenerateContractCode 生成合同编码 +func GenerateContractCode(contractType ContractType) string { + prefix := "CON" + switch contractType { + case ContractTypeCooperation: + prefix += "01" + case ContractTypeReSign: + prefix += "02" + } + + // 获取当前日期,格式为YYMMDD + now := time.Now() + dateStr := now.Format("060102") // YYMMDD格式 + + // 生成一个随机的6位数字 + randNum := fmt.Sprintf("%06d", rand.Intn(1000000)) + + // 格式:CON + 类型标识 + YYMMDD + 6位随机数 + return fmt.Sprintf("%s%s%s", prefix, dateStr, randNum) +} diff --git a/internal/infrastructure/external/alicloud/README.md b/internal/infrastructure/external/alicloud/README.md new file mode 100644 index 0000000..fd9cde4 --- /dev/null +++ b/internal/infrastructure/external/alicloud/README.md @@ -0,0 +1,194 @@ +# 阿里云二要素验证服务 + +这个服务提供了调用阿里云身份证二要素验证API的功能,用于验证姓名和身份证号码是否匹配。 + +## 功能特性 + +- 身份证二要素验证(姓名 + 身份证号) +- 支持详细验证结果返回 +- 支持简单布尔值判断 +- 错误处理和中文错误信息 + +## 配置说明 + +### 必需配置 + +- `Host`: 阿里云API的域名地址 +- `AppCode`: 阿里云市场应用的AppCode + +### 配置示例 + +```go +host := "https://kzidcardv1.market.alicloudapi.com" +appCode := "您的AppCode" +``` + +## 使用方法 + +### 1. 创建服务实例 + +```go +service := NewAlicloudService(host, appCode) +``` + +### 2. 调用API + +#### 身份证二要素验证示例 + +```go +// 构建请求参数 +params := map[string]interface{}{ + "name": "张三", + "idcard": "110101199001011234", +} + +// 调用API +responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) +if err != nil { + log.Printf("验证失败: %v", err) + return +} + +// 解析完整响应结构 +var response struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` +} + +if err := json.Unmarshal(responseBody, &response); err != nil { + log.Printf("响应解析失败: %v", err) + return +} + +// 检查响应状态 +if response.Code != 200 { + log.Printf("API返回错误: code=%d, msg=%s", response.Code, response.Msg) + return +} + +idCardData := response.Data + +// 判断验证结果 +if idCardData.Result == 1 { + fmt.Println("身份证信息验证通过") +} else { + fmt.Println("身份证信息验证失败") +} +``` + +#### 通用API调用 + +```go +// 调用其他阿里云API +params := map[string]interface{}{ + "param1": "value1", + "param2": "value2", +} + +responseBody, err := service.CallAPI("your/api/path", params) +if err != nil { + log.Printf("API调用失败: %v", err) + return +} + +// 根据具体API的响应结构进行解析 +// 每个API的响应结构可能不同,需要根据API文档定义相应的结构体 +var response struct { + Msg string `json:"msg"` + Code int `json:"code"` + Data interface{} `json:"data"` +} + +if err := json.Unmarshal(responseBody, &response); err != nil { + log.Printf("响应解析失败: %v", err) + return +} + +// 处理响应数据 +fmt.Printf("响应数据: %s\n", string(responseBody)) +``` + +## 响应格式 + +### 通用响应结构 + +```json +{ + "msg": "成功", + "success": true, + "code": 200, + "data": { + // 具体的业务数据 + } +} +``` + +### 身份证验证响应示例 + +#### 成功响应 (code: 200) + +```json +{ + "msg": "成功", + "success": true, + "code": 200, + "data": { + "birthday": "19840816", + "result": 1, + "address": "浙江省杭州市淳安县", + "orderNo": "202406271440416095174", + "sex": "男", + "desc": "不一致" + } +} +``` + +#### 参数错误响应 (code: 400) + +```json +{ + "msg": "请输入有效的身份证号码", + "code": 400, + "data": null +} +``` + +### 错误响应 + +```json +{ + "msg": "AppCode无效", + "success": false, + "code": 400 +} +``` + +## 错误处理 + +服务定义了以下错误类型: + +- `ErrDatasource`: 数据源异常 +- `ErrSystem`: 系统异常 +- `ErrInvalid`: 身份证信息不匹配 + +## 注意事项 + +1. 请确保您的AppCode有效且有足够的调用额度 +2. 身份证号码必须是18位有效格式 +3. 姓名必须是真实有效的姓名 +4. 建议在生产环境中添加适当的重试机制和超时设置 +5. 请遵守阿里云API的使用规范和频率限制 + +## 依赖 + +- Go 1.16+ +- 标准库:`net/http`, `encoding/json`, `net/url` \ No newline at end of file diff --git a/internal/infrastructure/external/alicloud/alicloud_service.go b/internal/infrastructure/external/alicloud/alicloud_service.go new file mode 100644 index 0000000..9be4f35 --- /dev/null +++ b/internal/infrastructure/external/alicloud/alicloud_service.go @@ -0,0 +1,82 @@ +package alicloud + +import ( + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +var ( + ErrDatasource = errors.New("数据源异常") + ErrSystem = errors.New("系统异常") +) + +// AlicloudConfig 阿里云配置 +type AlicloudConfig struct { + Host string + AppCode string +} + +// AlicloudService 阿里云服务 +type AlicloudService struct { + config AlicloudConfig +} + +// NewAlicloudService 创建阿里云服务实例 +func NewAlicloudService(host, appCode string) *AlicloudService { + return &AlicloudService{ + config: AlicloudConfig{ + Host: host, + AppCode: appCode, + }, + } +} + +// CallAPI 调用阿里云API的通用方法 +// path: API路径(如 "api-mall/api/id_card/check") +// params: 请求参数 +func (a *AlicloudService) CallAPI(path string, params map[string]interface{}) (respBytes []byte, err error) { + // 构建请求URL + reqURL := a.config.Host + "/" + path + + // 构建请求参数 + formData := url.Values{} + for key, value := range params { + formData.Set(key, fmt.Sprintf("%v", value)) + } + + // 创建HTTP请求 + req, err := http.NewRequest("POST", reqURL, strings.NewReader(formData.Encode())) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrSystem, err.Error()) + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + req.Header.Set("Authorization", "APPCODE "+a.config.AppCode) + + // 发送请求 + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrSystem, err.Error()) + } + defer resp.Body.Close() + + // 读取响应体 + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrSystem, err.Error()) + } + + // 直接返回原始响应body,让调用方自己处理 + return body, nil +} + +// GetConfig 获取配置信息 +func (a *AlicloudService) GetConfig() AlicloudConfig { + return a.config +} \ No newline at end of file diff --git a/internal/infrastructure/external/alicloud/alicloud_service_test.go b/internal/infrastructure/external/alicloud/alicloud_service_test.go new file mode 100644 index 0000000..a1cb9a0 --- /dev/null +++ b/internal/infrastructure/external/alicloud/alicloud_service_test.go @@ -0,0 +1,143 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "testing" +) + +func TestRealAlicloudAPI(t *testing.T) { + // 使用真实的阿里云API配置 + host := "https://kzidcardv1.market.alicloudapi.com" + appCode := "d55b58829efb41c8aa8e86769cba4844" + + service := NewAlicloudService(host, appCode) + + // 测试真实的身份证验证 + name := "张荣宏" + idCard := "45212220000827423X" + + fmt.Printf("开始测试阿里云二要素验证API...\n") + fmt.Printf("姓名: %s\n", name) + fmt.Printf("身份证: %s\n", idCard) + + // 构建请求参数 + params := map[string]interface{}{ + "name": name, + "idcard": idCard, + } + + // 调用真实API + responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) + if err != nil { + t.Logf("API调用失败: %v", err) + fmt.Printf("错误详情: %v\n", err) + t.Fail() + return + } + + // 打印原始响应数据 + fmt.Printf("API响应成功!\n") + fmt.Printf("原始响应数据: %s\n", string(responseBody)) + + // 解析完整响应结构 + var response struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` + } + + if err := json.Unmarshal(responseBody, &response); err != nil { + t.Logf("响应数据解析失败: %v", err) + t.Fail() + return + } + + // 检查响应状态 + if response.Code != 200 { + t.Logf("API返回错误: code=%d, msg=%s", response.Code, response.Msg) + t.Fail() + return + } + + idCardData := response.Data + + // 打印详细响应结果 + fmt.Printf("验证结果: %d\n", idCardData.Result) + fmt.Printf("描述: %s\n", idCardData.Desc) + fmt.Printf("生日: %s\n", idCardData.Birthday) + fmt.Printf("性别: %s\n", idCardData.Sex) + fmt.Printf("地址: %s\n", idCardData.Address) + fmt.Printf("订单号: %s\n", idCardData.OrderNo) + + // 将完整响应转换为JSON并打印 + jsonResponse, _ := json.MarshalIndent(idCardData, "", " ") + fmt.Printf("完整响应JSON:\n%s\n", string(jsonResponse)) + + // 判断验证结果 + if idCardData.Result == 1 { + fmt.Printf("验证结果: 通过\n") + } else { + fmt.Printf("验证结果: 失败\n") + } +} + +// TestAlicloudAPIError 测试错误响应 +func TestAlicloudAPIError(t *testing.T) { + // 使用真实的阿里云API配置 + host := "https://kzidcardv1.market.alicloudapi.com" + appCode := "d55b58829efb41c8aa8e86769cba4844" + + service := NewAlicloudService(host, appCode) + + // 测试无效的身份证号码 + name := "张三" + invalidIdCard := "123456789" + + fmt.Printf("测试错误响应 - 无效身份证号\n") + fmt.Printf("姓名: %s\n", name) + fmt.Printf("身份证: %s\n", invalidIdCard) + + // 构建请求参数 + params := map[string]interface{}{ + "name": name, + "idcard": invalidIdCard, + } + + // 调用真实API + responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) + if err != nil { + fmt.Printf("网络请求错误: %v\n", err) + return + } + + // 解析响应 + var response struct { + Msg string `json:"msg"` + Code int `json:"code"` + Data interface{} `json:"data"` + } + + if err := json.Unmarshal(responseBody, &response); err != nil { + fmt.Printf("响应解析失败: %v\n", err) + return + } + + // 检查是否为错误响应 + if response.Code != 200 { + fmt.Printf("预期的错误响应: code=%d, msg=%s\n", response.Code, response.Msg) + fmt.Printf("错误处理正确: API返回错误状态\n") + } else { + t.Error("期望返回错误,但实际成功") + } +} + + \ No newline at end of file diff --git a/internal/infrastructure/external/alicloud/example.go b/internal/infrastructure/external/alicloud/example.go new file mode 100644 index 0000000..4db61ef --- /dev/null +++ b/internal/infrastructure/external/alicloud/example.go @@ -0,0 +1,76 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "log" +) + +// ExampleUsage 使用示例 +func ExampleUsage() { + // 创建阿里云服务实例 + // 请替换为您的实际配置 + host := "https://kzidcardv1.market.alicloudapi.com" + appCode := "您的AppCode" + + service := NewAlicloudService(host, appCode) + + // 示例:验证身份证信息 + name := "张三" + idCard := "110101199001011234" + + // 构建请求参数 + params := map[string]interface{}{ + "name": name, + "idcard": idCard, + } + + // 调用API + responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) + if err != nil { + log.Printf("验证失败: %v", err) + return + } + + // 解析完整响应结构 + var response struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` + } + + if err := json.Unmarshal(responseBody, &response); err != nil { + log.Printf("响应解析失败: %v", err) + return + } + + // 检查响应状态 + if response.Code != 200 { + log.Printf("API返回错误: code=%d, msg=%s", response.Code, response.Msg) + return + } + + idCardData := response.Data + + fmt.Printf("验证结果: %d\n", idCardData.Result) + fmt.Printf("描述: %s\n", idCardData.Desc) + fmt.Printf("生日: %s\n", idCardData.Birthday) + fmt.Printf("性别: %s\n", idCardData.Sex) + fmt.Printf("地址: %s\n", idCardData.Address) + fmt.Printf("订单号: %s\n", idCardData.OrderNo) + + // 判断验证结果 + if idCardData.Result == 1 { + fmt.Println("身份证信息验证通过") + } else { + fmt.Println("身份证信息验证失败") + } +} \ No newline at end of file diff --git a/internal/infrastructure/external/alicloud/example_advanced.go b/internal/infrastructure/external/alicloud/example_advanced.go new file mode 100644 index 0000000..885ed4d --- /dev/null +++ b/internal/infrastructure/external/alicloud/example_advanced.go @@ -0,0 +1,162 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "log" +) + +// ExampleAdvancedUsage 高级使用示例 +func ExampleAdvancedUsage() { + // 创建阿里云服务实例 + host := "https://kzidcardv1.market.alicloudapi.com" + appCode := "您的AppCode" + + service := NewAlicloudService(host, appCode) + + // 示例1: 身份证二要素验证 + fmt.Println("=== 示例1: 身份证二要素验证 ===") + exampleIdCardCheck(service) + + // 示例2: 其他API调用(假设) + fmt.Println("\n=== 示例2: 其他API调用 ===") + exampleOtherAPI(service) +} + +// exampleIdCardCheck 身份证验证示例 +func exampleIdCardCheck(service *AlicloudService) { + // 构建请求参数 + params := map[string]interface{}{ + "name": "张三", + "idcard": "110101199001011234", + } + + // 调用API + responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) + if err != nil { + log.Printf("身份证验证失败: %v", err) + return + } + + // 解析完整响应结构 + var response struct { + Msg string `json:"msg"` + Success bool `json:"success"` + Code int `json:"code"` + Data struct { + Birthday string `json:"birthday"` + Result int `json:"result"` + Address string `json:"address"` + OrderNo string `json:"orderNo"` + Sex string `json:"sex"` + Desc string `json:"desc"` + } `json:"data"` + } + + if err := json.Unmarshal(responseBody, &response); err != nil { + log.Printf("响应解析失败: %v", err) + return + } + + // 检查响应状态 + if response.Code != 200 { + log.Printf("API返回错误: code=%d, msg=%s", response.Code, response.Msg) + return + } + + idCardData := response.Data + + // 处理验证结果 + fmt.Printf("验证结果: %d (%s)\n", idCardData.Result, idCardData.Desc) + fmt.Printf("生日: %s\n", idCardData.Birthday) + fmt.Printf("性别: %s\n", idCardData.Sex) + fmt.Printf("地址: %s\n", idCardData.Address) + fmt.Printf("订单号: %s\n", idCardData.OrderNo) + + if idCardData.Result == 1 { + fmt.Println("✅ 身份证信息验证通过") + } else { + fmt.Println("❌ 身份证信息验证失败") + } +} + +// exampleOtherAPI 其他API调用示例 +func exampleOtherAPI(service *AlicloudService) { + // 假设调用其他API + params := map[string]interface{}{ + "param1": "value1", + "param2": "value2", + } + + // 调用API + responseBody, err := service.CallAPI("other/api/path", params) + if err != nil { + log.Printf("API调用失败: %v", err) + return + } + + // 根据具体API的响应结构进行解析 + // 这里只是示例,实际使用时需要根据API文档定义相应的结构体 + fmt.Printf("API响应数据: %s\n", string(responseBody)) + + // 示例:解析通用响应结构 + var genericData map[string]interface{} + if err := json.Unmarshal(responseBody, &genericData); err != nil { + log.Printf("响应解析失败: %v", err) + return + } + + fmt.Printf("解析后的数据: %+v\n", genericData) +} + +// ExampleErrorHandling 错误处理示例 +func ExampleErrorHandling() { + host := "https://kzidcardv1.market.alicloudapi.com" + appCode := "您的AppCode" + + service := NewAlicloudService(host, appCode) + + // 测试各种错误情况 + testCases := []struct { + name string + idCard string + desc string + }{ + {"张三", "123456789", "无效身份证号"}, + {"", "110101199001011234", "空姓名"}, + {"张三", "", "空身份证号"}, + } + + for _, tc := range testCases { + fmt.Printf("\n测试: %s\n", tc.desc) + + params := map[string]interface{}{ + "name": tc.name, + "idcard": tc.idCard, + } + + responseBody, err := service.CallAPI("api-mall/api/id_card/check", params) + if err != nil { + fmt.Printf("❌ 网络请求错误: %v\n", err) + continue + } + + // 解析响应 + var response struct { + Msg string `json:"msg"` + Code int `json:"code"` + Data interface{} `json:"data"` + } + + if err := json.Unmarshal(responseBody, &response); err != nil { + fmt.Printf("❌ 响应解析失败: %v\n", err) + continue + } + + if response.Code != 200 { + fmt.Printf("❌ 预期错误: code=%d, msg=%s\n", response.Code, response.Msg) + } else { + fmt.Printf("⚠️ 意外成功\n") + } + } +} \ No newline at end of file