diff --git a/cmd/api/__debug_bin.exe320353000 b/cmd/api/__debug_bin.exe320353000 new file mode 100644 index 0000000..2d755f5 Binary files /dev/null and b/cmd/api/__debug_bin.exe320353000 differ diff --git a/cmd/worker/__debug_bin.exe1068760645 b/cmd/worker/__debug_bin.exe1068760645 new file mode 100644 index 0000000..8c33a7d Binary files /dev/null and b/cmd/worker/__debug_bin.exe1068760645 differ diff --git a/cmd/worker/__debug_bin.exe1835124629 b/cmd/worker/__debug_bin.exe1835124629 new file mode 100644 index 0000000..8c33a7d Binary files /dev/null and b/cmd/worker/__debug_bin.exe1835124629 differ diff --git a/cmd/worker/__debug_bin.exe4056734935 b/cmd/worker/__debug_bin.exe4056734935 new file mode 100644 index 0000000..8c33a7d Binary files /dev/null and b/cmd/worker/__debug_bin.exe4056734935 differ diff --git a/cmd/worker/__debug_bin.exe438186156 b/cmd/worker/__debug_bin.exe438186156 new file mode 100644 index 0000000..8c33a7d Binary files /dev/null and b/cmd/worker/__debug_bin.exe438186156 differ diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index c1d5bb4..9c6e2af 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -58,7 +58,8 @@ services: depends_on: redis: condition: service_healthy - restart: unless-stopped + restart: + unless-stopped # Jaeger 链路追踪 jaeger: diff --git a/internal/application/api/api_application_service.go b/internal/application/api/api_application_service.go index 5c9c338..f6f5dc4 100644 --- a/internal/application/api/api_application_service.go +++ b/internal/application/api/api_application_service.go @@ -46,7 +46,7 @@ type ApiApplicationService interface { // 管理端API调用记录 GetAdminApiCalls(ctx context.Context, filters map[string]interface{}, options shared_interfaces.ListOptions) (*dto.ApiCallListResponse, error) - + // 导出功能 ExportAdminApiCalls(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error) @@ -442,7 +442,7 @@ func (s *ApiApplicationServiceImpl) asyncRecordFailure(ctx context.Context, apiC zap.String("transaction_id", apiCall.TransactionId), zap.String("error_type", errorType), zap.String("error_msg", errorMsg)) - + // 可选:如果需要统计失败请求,可以在这里添加计数器 // s.failureCounter.Inc() } @@ -774,7 +774,7 @@ func (s *ApiApplicationServiceImpl) ExportAdminApiCalls(ctx context.Context, fil const batchSize = 1000 // 每批处理1000条记录 var allCalls []*entities.ApiCall var productNameMap map[string]string - + // 分批获取数据 page := 1 for { @@ -819,7 +819,7 @@ func (s *ApiApplicationServiceImpl) ExportAdminApiCalls(ctx context.Context, fil // 准备导出数据 headers := []string{"企业名称", "产品名称", "交易ID", "客户端IP", "状态", "开始时间", "结束时间"} columnWidths := []float64{30, 20, 40, 15, 10, 20, 20} - + data := make([][]interface{}, len(allCalls)) for i, call := range allCalls { // 从映射中获取企业名称 @@ -1206,8 +1206,8 @@ func (s *ApiApplicationServiceImpl) GetUserBalanceAlertSettings(ctx context.Cont // 获取API用户信息 apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { - s.logger.Error("获取API用户信息失败", - zap.String("user_id", userID), + s.logger.Error("获取API用户信息失败", + zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("获取API用户信息失败: %w", err) } @@ -1218,9 +1218,9 @@ func (s *ApiApplicationServiceImpl) GetUserBalanceAlertSettings(ctx context.Cont // 返回预警设置 settings := map[string]interface{}{ - "enabled": apiUser.BalanceAlertEnabled, - "threshold": apiUser.BalanceAlertThreshold, - "alert_phone": apiUser.AlertPhone, + "enabled": apiUser.BalanceAlertEnabled, + "threshold": apiUser.BalanceAlertThreshold, + "alert_phone": apiUser.AlertPhone, } return settings, nil @@ -1231,8 +1231,8 @@ func (s *ApiApplicationServiceImpl) UpdateUserBalanceAlertSettings(ctx context.C // 获取API用户信息 apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { - s.logger.Error("获取API用户信息失败", - zap.String("user_id", userID), + s.logger.Error("获取API用户信息失败", + zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("获取API用户信息失败: %w", err) } @@ -1243,16 +1243,16 @@ func (s *ApiApplicationServiceImpl) UpdateUserBalanceAlertSettings(ctx context.C // 更新预警设置 if err := apiUser.UpdateBalanceAlertSettings(enabled, threshold, alertPhone); err != nil { - s.logger.Error("更新预警设置失败", - zap.String("user_id", userID), + s.logger.Error("更新预警设置失败", + zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("更新预警设置失败: %w", err) } // 保存到数据库 if err := s.apiUserService.SaveApiUser(ctx, apiUser); err != nil { - s.logger.Error("保存API用户信息失败", - zap.String("user_id", userID), + s.logger.Error("保存API用户信息失败", + zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("保存API用户信息失败: %w", err) } @@ -1271,8 +1271,8 @@ func (s *ApiApplicationServiceImpl) TestBalanceAlertSms(ctx context.Context, use // 获取用户信息以获取企业名称 user, err := s.userRepo.GetByID(ctx, userID) if err != nil { - s.logger.Error("获取用户信息失败", - zap.String("user_id", userID), + s.logger.Error("获取用户信息失败", + zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("获取用户信息失败: %w", err) } @@ -1285,8 +1285,8 @@ func (s *ApiApplicationServiceImpl) TestBalanceAlertSms(ctx context.Context, use // 调用短信服务发送测试短信 if err := s.balanceAlertService.CheckAndSendAlert(ctx, userID, decimal.NewFromFloat(balance)); err != nil { - s.logger.Error("发送测试预警短信失败", - zap.String("user_id", userID), + s.logger.Error("发送测试预警短信失败", + zap.String("user_id", userID), zap.String("phone", phone), zap.Float64("balance", balance), zap.String("alert_type", alertType), diff --git a/internal/application/product/product_application_service_impl.go b/internal/application/product/product_application_service_impl.go index f47f1ed..33ea40a 100644 --- a/internal/application/product/product_application_service_impl.go +++ b/internal/application/product/product_application_service_impl.go @@ -965,7 +965,7 @@ func (s *ProductApplicationServiceImpl) getDTOMap() map[string]interface{} { "JRZQ0A03": &dto.JRZQ0A03Req{}, "JRZQ4AA8": &dto.JRZQ4AA8Req{}, "JRZQ8203": &dto.JRZQ8203Req{}, - "JRZQDBCE": &dto.JRZQDBCEReq{}, + "JRZQDBCE": &dto.JRZQDCBEReq{}, "QYGL2ACD": &dto.QYGL2ACDReq{}, "QYGL6F2D": &dto.QYGL6F2DReq{}, "QYGL45BD": &dto.QYGL45BDReq{}, diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 0d14b2c..f5b7504 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -94,7 +94,7 @@ type JRZQ8203Req struct { IDCard string `json:"id_card" validate:"required,validIDCard"` Name string `json:"name" validate:"required,min=1,validName"` } -type JRZQDBCEReq struct { +type JRZQDCBEReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` IDCard string `json:"id_card" validate:"required,validIDCard"` BankCard string `json:"bank_card" validate:"required,validBankCard"` @@ -300,8 +300,8 @@ type IVYZ3A7FReq struct { } type IVYZ9K2LReq struct { - Name string `json:"name" validate:"required,min=1,validName"` - IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` PhotoData string `json:"photo_data" validate:"required,validBase64Image"` } @@ -311,6 +311,12 @@ type IVYZ9D2EReq struct { UseScenario string `json:"use_scenario" validate:"required,oneof=1 2 3 4 99"` } +type IVYZ2C1PReq struct { + IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` +} + // DWBG7F3AReq 行为数据查询请求参数 type DWBG7F3AReq struct { Name string `json:"name" validate:"required,min=1,validName"` @@ -354,6 +360,15 @@ type YYSY4F2EReq struct { Authorized string `json:"authorized" validate:"required,oneof=0 1"` } +type YYSY9F1BReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + Phone string `json:"phone" validate:"required,min=11,max=11,validMobileNo"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` +} +type YYSY6F2BReq struct { + Phone string `json:"phone" validate:"required,min=11,max=11,validMobileNo"` +} + type YYSY8B1CReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` } @@ -394,6 +409,12 @@ type JRZQ3C7BReq struct { Name string `json:"name" validate:"required,min=1,validName"` Authorized string `json:"authorized" validate:"required,oneof=0 1"` } +type JRZQ3C9RReq struct { + MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` +} type JRZQ8A2DReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` @@ -404,9 +425,32 @@ type JRZQ8A2DReq struct { // YYSY8F3AReq 行为数据查询请求参数 type YYSY8F3AReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"cardNo" validate:"required,validIDCard"` + MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` + CardId string `json:"cardId" validate:"required,validIDCard"` +} + +// 銀行卡黑名單 +type JRZQ0B6YReq 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"` + BankCard string `json:"bank_card" validate:"omitempty,validBankCard"` +} + +// 银行卡鉴权 +type JRZQ9A1WReq 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"` + BankCard string `json:"bank_card" validate:"omitempty,validBankCard"` +} + +// 企业管理董监高司法综合信息核验 +type QYGL6S1BReq struct { + IDCard string `json:"id_card" validate:"required,validIDCard"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` } type JRZQ5E9FReq struct { @@ -460,20 +504,20 @@ type QCXG9P1CReq struct { } type QCXG8A3DReq struct { - PlateNo string `json:"plate_no" validate:"required"` - PlateType string `json:"plate_type" validate:"omitempty,oneof=01 02"` - Authorized string `json:"authorized" validate:"required,oneof=0 1"` + PlateNo string `json:"plate_no" validate:"required"` + PlateType string `json:"plate_type" validate:"omitempty,oneof=01 02"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` } type QCXG6B4EReq struct { - VINCode string `json:"vin_code" validate:"required"` - Authorized string `json:"authorized" validate:"required,oneof=0 1"` + VINCode string `json:"vin_code" validate:"required"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` } type QYGL2B5CReq struct { - EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"` - EntCode string `json:"ent_code" validate:"omitempty,validUSCI"` - Authorized string `json:"authorized" validate:"required,oneof=0 1"` + EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"` + EntCode string `json:"ent_code" validate:"omitempty,validUSCI"` + Authorized string `json:"authorized" validate:"required,oneof=0 1"` } type JRZQ2F8AReq struct { diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index facda19..22351e0 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -125,7 +125,9 @@ func registerAllProcessors(combService *comb.CombService) { "JRZQ0L85": jrzq.ProcessJRZQ0L85Request, "JRZQ2F8A": jrzq.ProcessJRZQ2F8ARequest, "JRZQ1E7B": jrzq.ProcessJRZQ1E7BRequest, - + "JRZQ3C9R": jrzq.ProcessJRZQ3C9RRequest, + "JRZQ0B6Y": jrzq.ProcessJRZQ0B6YRequest, + "JRZQ9A1W": jrzq.ProcessJRZQ9A1WRequest, // QYGL系列处理器 "QYGL8261": qygl.ProcessQYGL8261Request, "QYGL2ACD": qygl.ProcessQYGL2ACDRequest, @@ -144,6 +146,7 @@ func registerAllProcessors(combService *comb.CombService) { "COMENT01": qygl.ProcessCOMENT01Request, // 企业风险报告 "QYGL5F6A": qygl.ProcessQYGL5F6ARequest, // 企业相关查询 "QYGL2B5C": qygl.ProcessQYGL2B5CRequest, // 企业联系人实际经营地址 + "QYGL6S1B": qygl.ProcessQYGL6S1BRequest, //董监高司法综合信息核验 // YYSY系列处理器 "YYSYD50F": yysy.ProcessYYSYD50FRequest, @@ -162,6 +165,8 @@ func registerAllProcessors(combService *comb.CombService) { "YYSY8C2D": yysy.ProcessYYSY8C2DRequest, "YYSY7D3E": yysy.ProcessYYSY7D3ERequest, "YYSY9E4A": yysy.ProcessYYSY9E4ARequest, + "YYSY9F1B": yysy.ProcessYYSY9F1BYequest, + "YYSY6F2B": yysy.ProcessYYSY6F2BRequest, // IVYZ系列处理器 "IVYZ0B03": ivyz.ProcessIVYZ0B03Request, @@ -186,6 +191,7 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZ6G7H": ivyz.ProcessIVYZ6G7HRequest, "IVYZ8I9J": ivyz.ProcessIVYZ8I9JRequest, "IVYZ9K2L": ivyz.ProcessIVYZ9K2LRequest, + "IVYZ2C1P": ivyz.ProcessIVYZ2C1PRequest, // COMB系列处理器 - 只注册有自定义逻辑的组合包 "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 5ea4ee4..9864f7c 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -96,7 +96,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "JRZQ0A03": &dto.JRZQ0A03Req{}, "JRZQ4AA8": &dto.JRZQ4AA8Req{}, "JRZQ8203": &dto.JRZQ8203Req{}, - "JRZQDBCE": &dto.JRZQDBCEReq{}, + "JRZQDCBE": &dto.JRZQDCBEReq{}, "QYGL2ACD": &dto.QYGL2ACDReq{}, "QYGL6F2D": &dto.QYGL6F2DReq{}, "QYGL45BD": &dto.QYGL45BDReq{}, @@ -178,6 +178,13 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "QYGL2B5C": &dto.QYGL2B5CReq{}, "JRZQ2F8A": &dto.JRZQ2F8AReq{}, "JRZQ1E7B": &dto.JRZQ1E7BReq{}, + "JRZQ3C9R": &dto.JRZQ3C9RReq{}, + "IVYZ2C1P": &dto.IVYZ2C1PReq{}, + "YYSY9F1B": &dto.YYSY9F1BReq{}, + "YYSY6F2B": &dto.YYSY6F2BReq{}, + "QYGL6S1B": &dto.QYGL6S1BReq{}, + "JRZQ0B6Y": &dto.JRZQ0B6YReq{}, + "JRZQ9A1W": &dto.JRZQ9A1WReq{}, } // 优先返回已配置的DTO diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index edee08e..567cbda 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -13,6 +13,7 @@ import ( // ProcessDWBG8B4DRequest DWBG8B4D API处理方法 - 谛听多维报告 func ProcessDWBG8B4DRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { var paramsDto dto.DWBG8B4DReq + if err := json.Unmarshal(params, ¶msDto); err != nil { return nil, errors.Join(processors.ErrSystem, err) } diff --git a/internal/domains/api/services/processors/flxg/flxg0v3b_processor.go b/internal/domains/api/services/processors/flxg/flxg0v3b_processor.go index 717d34e..2183764 100644 --- a/internal/domains/api/services/processors/flxg/flxg0v3b_processor.go +++ b/internal/domains/api/services/processors/flxg/flxg0v3b_processor.go @@ -33,8 +33,8 @@ func ProcessFLXG0V3Bequest(ctx context.Context, params []byte, deps *processors. reqData := map[string]interface{}{ "data": map[string]interface{}{ - "name": encryptedName, - "id_card": encryptedIDCard, + "name": encryptedName, + "id_card": encryptedIDCard, }, } diff --git a/internal/domains/api/services/processors/ivyz/ivyz2c1p_processor.go b/internal/domains/api/services/processors/ivyz/ivyz2c1p_processor.go new file mode 100644 index 0000000..bdc73a4 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz2c1p_processor.go @@ -0,0 +1,56 @@ +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/zhicha" +) + +// ProcessIVYZ2C1PRequest IVYZ2C1P API处理方法 - 风控黑名单 +func ProcessIVYZ2C1PRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZ2C1PReq + 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.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) + } + + reqData := map[string]interface{}{ + "name": encryptedName, + "idCard": encryptedIDCard, + "authorized": paramsDto.Authorized, + } + + respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI037", reqData) + if err != nil { + if errors.Is(err, zhicha.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 将响应数据转换为JSON字节 + respBytes, err := json.Marshal(respData) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq0b6y_processor.go b/internal/domains/api/services/processors/jrzq/jrzq0b6y_processor.go new file mode 100644 index 0000000..ddb56a7 --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzq0b6y_processor.go @@ -0,0 +1,63 @@ +package jrzq + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/westdex" +) + +// ProcessJRZQ0B6YRequest JRZQ0B6Y 银行卡黑名单查询V1API处理方法 +func ProcessJRZQ0B6YRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.JRZQ0B6YReq + 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) + } + + encryptedMobileNo, err := deps.WestDexService.Encrypt(paramsDto.MobileNo) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + encryptedBankCard, err := deps.WestDexService.Encrypt(paramsDto.BankCard) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + reqData := map[string]interface{}{ + "data": map[string]interface{}{ + "name": encryptedName, + "cardId": encryptedBankCard, + "cardNo": encryptedIDCard, + "phone": encryptedMobileNo, + }, + } + + respBytes, err := deps.YushanService.CallAPI(ctx, "FIN019", 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 +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq3c7b_processor.go b/internal/domains/api/services/processors/jrzq/jrzq3c7b_processor.go index 8c13bc7..f037aba 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq3c7b_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq3c7b_processor.go @@ -37,9 +37,9 @@ func ProcessJRZQ3C7BRequest(ctx context.Context, params []byte, deps *processors } reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "phone": encryptedMobileNo, + "name": encryptedName, + "idCard": encryptedIDCard, + "phone": encryptedMobileNo, "authorized": paramsDto.Authorized, } diff --git a/internal/domains/api/services/processors/jrzq/jrzq3c9R_processor.go b/internal/domains/api/services/processors/jrzq/jrzq3c9R_processor.go new file mode 100644 index 0000000..aa141c4 --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzq3c9R_processor.go @@ -0,0 +1,62 @@ +package jrzq + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/zhicha" +) + +// ProcessJRZQ3c9RRequest JRZQ3c9R API处理方法 - 支付行为指数 +func ProcessJRZQ3C9RRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.JRZQ3C9RReq + 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.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, + } + + respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI036", reqData) + if err != nil { + if errors.Is(err, zhicha.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 将响应数据转换为JSON字节 + respBytes, err := json.Marshal(respData) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq4b6c_processor.go b/internal/domains/api/services/processors/jrzq/jrzq4b6c_processor.go index 96aff85..b64ad48 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq4b6c_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq4b6c_processor.go @@ -37,9 +37,9 @@ func ProcessJRZQ4B6CRequest(ctx context.Context, params []byte, deps *processors } reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "phone": encryptedMobileNo, + "name": encryptedName, + "idCard": encryptedIDCard, + "phone": encryptedMobileNo, "authorized": paramsDto.Authorized, } diff --git a/internal/domains/api/services/processors/jrzq/jrzq8203_processor.go b/internal/domains/api/services/processors/jrzq/jrzq8203_processor.go index 5eee7bd..686258b 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq8203_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq8203_processor.go @@ -54,4 +54,4 @@ func ProcessJRZQ8203Request(ctx context.Context, params []byte, deps *processors } return respBytes, nil -} \ No newline at end of file +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq9a1w_processor.go b/internal/domains/api/services/processors/jrzq/jrzq9a1w_processor.go new file mode 100644 index 0000000..abd5b9c --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzq9a1w_processor.go @@ -0,0 +1,63 @@ +package jrzq + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/westdex" +) + +// ProcessJRZQ9A1WRequest JRZQ9A1W 银行卡鉴权V1API处理方法 +func ProcessJRZQ9A1WRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.JRZQ9A1WReq + 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) + } + + encryptedMobileNo, err := deps.WestDexService.Encrypt(paramsDto.MobileNo) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + encryptedBankCard, err := deps.WestDexService.Encrypt(paramsDto.BankCard) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + reqData := map[string]interface{}{ + "data": map[string]interface{}{ + "name": encryptedName, + "cardId": encryptedBankCard, + "cardNo": encryptedIDCard, + "phone": encryptedMobileNo, + }, + } + + respBytes, err := deps.YushanService.CallAPI(ctx, "PCB145", 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 +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq9e2a_processor.go b/internal/domains/api/services/processors/jrzq/jrzq9e2a_processor.go index 0b1fa28..26a8ec4 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq9e2a_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq9e2a_processor.go @@ -24,8 +24,8 @@ func ProcessJRZQ9E2ARequest(ctx context.Context, params []byte, deps *processors // 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名 reqData := map[string]interface{}{ "phoneNumber": paramsDto.MobileNo, - "idCardNum": paramsDto.IDCard, - "name": paramsDto.Name, + "idCardNum": paramsDto.IDCard, + "name": paramsDto.Name, "authAuthorizeFileCode": paramsDto.AuthAuthorizeFileCode, } diff --git a/internal/domains/api/services/processors/jrzq/jrzqdcbe_processor.go b/internal/domains/api/services/processors/jrzq/jrzqdcbe_processor.go index 50a9a62..c0b517a 100644 --- a/internal/domains/api/services/processors/jrzq/jrzqdcbe_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzqdcbe_processor.go @@ -12,7 +12,7 @@ import ( // ProcessJRZQDCBERequest JRZQDCBE API处理方法 func ProcessJRZQDCBERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { - var paramsDto dto.JRZQDBCEReq + var paramsDto dto.JRZQDCBEReq if err := json.Unmarshal(params, ¶msDto); err != nil { return nil, errors.Join(processors.ErrSystem, err) } @@ -43,10 +43,10 @@ func ProcessJRZQDCBERequest(ctx context.Context, params []byte, deps *processors reqData := map[string]interface{}{ "data": map[string]interface{}{ - "name": encryptedName, - "idcard": encryptedIDCard, - "mobile": encryptedMobileNo, - "acc_no": encryptedBankCard, + "name": encryptedName, + "idcard": encryptedIDCard, + "mobile": encryptedMobileNo, + "acc_no": encryptedBankCard, }, } @@ -60,4 +60,4 @@ func ProcessJRZQDCBERequest(ctx context.Context, params []byte, deps *processors } return respBytes, nil -} \ No newline at end of file +} diff --git a/internal/domains/api/services/processors/qygl/qygl6s1b_processor.go b/internal/domains/api/services/processors/qygl/qygl6s1b_processor.go new file mode 100644 index 0000000..befb856 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl6s1b_processor.go @@ -0,0 +1,53 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL6S1BRequest QYGL6S1B API处理方法 - 董监高司法综合信息核验 + +func ProcessQYGL6S1BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL6S1BReq + 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) + } + + encryptedPhone, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 构建API调用参数 + apiParams := map[string]string{ + "idCard": encryptedPhone, + "authorized": paramsDto.Authorized, + } + + // 调用天眼查API - + response, err := deps.TianYanChaService.CallAPI(ctx, "ZCI043", apiParams) + if err != nil { + return nil, convertTianYanChaError(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/yysy/yysy6f2b_processor.go b/internal/domains/api/services/processors/yysy/yysy6f2b_processor.go new file mode 100644 index 0000000..aeae52c --- /dev/null +++ b/internal/domains/api/services/processors/yysy/yysy6f2b_processor.go @@ -0,0 +1,49 @@ +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/zhicha" +) + +// ProcessYYSY6F2BRequestYYSY 6F2B API处理方法 - 手机消费区间验证 +func ProcessYYSY6F2BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.YYSY6F2BReq + 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) + } + + encryptedPhone, err := deps.ZhichaService.Encrypt(paramsDto.Phone) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + reqData := map[string]interface{}{ + "phone": encryptedPhone, + } + + respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI041", reqData) + if err != nil { + if errors.Is(err, zhicha.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 将响应数据转换为JSON字节 + respBytes, err := json.Marshal(respData) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/yysy/yysy9f1b_processor.go b/internal/domains/api/services/processors/yysy/yysy9f1b_processor.go new file mode 100644 index 0000000..b289cc2 --- /dev/null +++ b/internal/domains/api/services/processors/yysy/yysy9f1b_processor.go @@ -0,0 +1,57 @@ +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/zhicha" +) + +// ProcessYYSY9F1BYequest YYSY9F1B API处理方法 - 手机二要素验证 + +func ProcessYYSY9F1BYequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.YYSY9F1BReq + 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.ZhichaService.Encrypt(paramsDto.Name) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + encryptedPhone, err := deps.ZhichaService.Encrypt(paramsDto.Phone) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + reqData := map[string]interface{}{ + "name": encryptedName, + "phone": encryptedPhone, + "authorized": paramsDto.Authorized, + } + + respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI040", reqData) + if err != nil { + if errors.Is(err, zhicha.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } else { + return nil, errors.Join(processors.ErrSystem, err) + } + } + + // 将响应数据转换为JSON字节 + respBytes, err := json.Marshal(respData) + 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 011f6ef..2b19f26 100644 --- a/internal/infrastructure/external/tianyancha/tianyancha_service.go +++ b/internal/infrastructure/external/tianyancha/tianyancha_service.go @@ -21,13 +21,13 @@ var ( // APIEndpoints 天眼查 API 端点映射 var APIEndpoints = map[string]string{ - "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", // 经营异常 - "OwnTax": "/open/mr/ownTax", // 欠税公告 - "TaxContravention": "/open/mr/taxContravention", // 税收违法 + "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", // 经营异常 + "OwnTax": "/open/mr/ownTax", // 欠税公告 + "TaxContravention": "/open/mr/taxContravention", // 税收违法 } // TianYanChaConfig 天眼查配置 @@ -56,6 +56,7 @@ type TianYanChaResponse struct { Reason string `json:"reason"` Result interface{} `json:"result"` } + // NewTianYanChaService 创建天眼查服务实例 func NewTianYanChaService(baseURL, token string, timeout time.Duration) *TianYanChaService { if timeout == 0 { @@ -118,14 +119,13 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params isTimeout = true } else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() { isTimeout = true - } else if errStr := err.Error(); - errStr == "context deadline exceeded" || - errStr == "timeout" || - errStr == "Client.Timeout exceeded" || - errStr == "net/http: request canceled" { + } else if errStr := err.Error(); errStr == "context deadline exceeded" || + errStr == "timeout" || + errStr == "Client.Timeout exceeded" || + errStr == "net/http: request canceled" { isTimeout = true } - + if isTimeout { return nil, errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err)) } @@ -156,7 +156,7 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params if tianYanChaResp.ErrorCode == 300000 { return nil, errors.Join(ErrNotFound, fmt.Errorf("天眼查查询为空: %s", tianYanChaResp.Reason)) } - + return &APIResponse{ Success: false, Code: tianYanChaResp.ErrorCode, diff --git a/internal/infrastructure/external/yushan/yushan_service.go b/internal/infrastructure/external/yushan/yushan_service.go index 96e9bd7..e2921f6 100644 --- a/internal/infrastructure/external/yushan/yushan_service.go +++ b/internal/infrastructure/external/yushan/yushan_service.go @@ -55,7 +55,7 @@ func NewYushanService(url, apiKey, acctID string, logger *external_logger.Extern func (y *YushanService) CallAPI(ctx context.Context, code string, params map[string]interface{}) (respBytes []byte, err error) { startTime := time.Now() requestID := y.generateRequestID() - + // 从ctx中获取transactionId var transactionID string if ctxTransactionID, ok := ctx.Value("transaction_id").(string); ok { @@ -131,14 +131,13 @@ func (y *YushanService) CallAPI(ctx context.Context, code string, params map[str isTimeout = true } else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() { isTimeout = true - } else if errStr := err.Error(); - errStr == "context deadline exceeded" || - errStr == "timeout" || - errStr == "Client.Timeout exceeded" || - errStr == "net/http: request canceled" { + } else if errStr := err.Error(); errStr == "context deadline exceeded" || + errStr == "timeout" || + errStr == "Client.Timeout exceeded" || + errStr == "net/http: request canceled" { isTimeout = true } - + if isTimeout { err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err)) } else { diff --git a/scripts/README_admin_token.md b/scripts/README_admin_token.md deleted file mode 100644 index aba057b..0000000 --- a/scripts/README_admin_token.md +++ /dev/null @@ -1,93 +0,0 @@ -# 管理员Token生成脚本使用说明 - -## 概述 - -`generate_admin_token_simple.go` 是一个用于生成管理员JWT token的脚本,方便管理员登录用户账号进行查看和调试。 - -## 功能特性 - -- 支持通过环境变量自定义用户信息 -- 自动加载项目配置文件 -- 生成标准的JWT token -- 提供详细的使用说明和示例 - -## 使用方法 - -### 直接运行 - -```bash -cd tyapi-server-gin -go run scripts/generate_admin_token_simple.go -``` - -### 固定用户信息 - -脚本中已预设了以下用户信息: -- 用户ID: `admin_user_001` -- 手机号: `13800138000` -- 邮箱: `admin@example.com` -- 用户类型: `admin` - -如需修改用户信息,请直接编辑脚本中的相应变量。 - -## 输出示例 - -``` -=== 管理员Token生成成功 === -用户ID: admin_user_001 -手机号: 13800138000 -邮箱: admin@example.com -用户类型: admin -过期时间: 24h0m0s - -=== Token === -eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - -=== 使用说明 === -在API请求的Authorization头部中使用: -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - -=== 示例curl命令 === -curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." http://localhost:8080/api/v1/user/profile -``` - -## 使用场景 - -1. **管理员调试**: 管理员需要以特定用户身份登录系统进行调试 -2. **用户问题排查**: 管理员需要查看特定用户的数据和操作 -3. **API测试**: 开发人员需要测试不同用户类型的API权限 -4. **系统维护**: 在系统维护时需要以特定用户身份执行操作 - -## 安全注意事项 - -⚠️ **重要提醒**: -- 此脚本仅用于开发和调试环境 -- 生产环境中请勿使用此脚本 -- 生成的token具有完整的用户权限,请妥善保管 -- 使用完毕后请及时删除或过期token - -## 编译为可执行文件 - -如果需要频繁使用,可以编译为可执行文件: - -```bash -# 编译 -go build -o bin/generate-admin-token scripts/generate_admin_token_simple.go - -# 使用 -./bin/generate-admin-token -``` - -## 故障排除 - -### 1. 配置文件找不到 -确保在项目根目录下运行脚本,或者确保 `config.yaml` 文件存在。 - -### 2. 权限错误 -确保脚本有读取配置文件的权限。 - -### 3. 依赖问题 -确保所有Go依赖都已正确安装: -```bash -go mod tidy -``` \ No newline at end of file diff --git a/scripts/README_token_generator.md b/scripts/README_token_generator.md deleted file mode 100644 index ac4abca..0000000 --- a/scripts/README_token_generator.md +++ /dev/null @@ -1,72 +0,0 @@ -# Token生成器使用说明 - -## 概述 - -为了方便管理员登录用户账号进行查看和调试,我们创建了一个简单的JWT token生成脚本。 - -## 文件说明 - -- `generate_admin_token_simple.go` - 生成管理员token的脚本 - -## 使用方法 - -### 生成管理员Token - -```bash -cd tyapi-server-gin -go run scripts/generate_admin_token_simple.go -``` - -### 预设用户信息 - -脚本中已预设了以下管理员信息: -- 用户ID: `admin_user_001` -- 手机号: `13800138000` -- 邮箱: `admin@example.com` -- 用户类型: `admin` - -### 输出示例 - -``` -=== 管理员Token生成成功 === -用户ID: admin_user_001 -手机号: 13800138000 -邮箱: admin@example.com -用户类型: admin -过期时间: 168h0m0s - -=== Token === -eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - -=== 使用说明 === -在API请求的Authorization头部中使用: -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - -=== 示例curl命令 === -curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." http://localhost:8080/api/v1/user/profile -``` - -## 使用场景 - -1. **管理员调试**: 管理员需要以特定用户身份登录系统进行调试 -2. **用户问题排查**: 管理员需要查看特定用户的数据和操作 -3. **API测试**: 开发人员需要测试不同用户类型的API权限 - -## 安全提醒 - -⚠️ **重要提醒**: -- 此脚本仅用于开发和调试环境 -- 生产环境中请勿使用此脚本 -- 生成的token具有完整的用户权限,请妥善保管 - -## 修改用户信息 - -如需修改用户信息,请直接编辑 `generate_admin_token_simple.go` 文件中的相应变量: - -```go -// 固定的用户信息 -userID := "admin_user_001" -phone := "13800138000" -email := "admin@example.com" -userType := "admin" -``` \ No newline at end of file diff --git a/scripts/analyze_processors.go b/scripts/analyze_processors.go deleted file mode 100644 index 23ef56b..0000000 --- a/scripts/analyze_processors.go +++ /dev/null @@ -1,603 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "path/filepath" - "regexp" - "sort" - "strings" - - "github.com/shopspring/decimal" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "gorm.io/gorm/schema" -) - -// ProcessorInfo 处理器信息 -type ProcessorInfo struct { - ProductCode string // 产品编号(从文件名提取) - ProductName string // 产品名称(从数据库查询) - Category string // 分类(从数据库查询) - Price string // 价格(从数据库查询) - DataSource string // 数据源(根据service确定) - DataSourceCode string // 数据源编号(CallAPI的第一个参数) - CostPrice string // 成本价(留空) -} - -// Product 产品实体(简化版) -type Product struct { - Code string `gorm:"column:code"` - Name string `gorm:"column:name"` - CategoryID string `gorm:"column:category_id"` - Category *ProductCategory `gorm:"foreignKey:CategoryID"` - Price decimal.Decimal `gorm:"column:price"` -} - -// ProductCategory 产品分类实体(简化版) -type ProductCategory struct { - ID string `gorm:"column:id"` - Name string `gorm:"column:name"` -} - -func main() { - // 获取处理器目录 - processorsDir := filepath.Join("internal", "domains", "api", "services", "processors") - if len(os.Args) > 1 { - processorsDir = os.Args[1] - } - - // 扫描所有处理器文件 - processors, err := scanProcessors(processorsDir) - if err != nil { - fmt.Fprintf(os.Stderr, "扫描处理器失败: %v\n", err) - os.Exit(1) - } - - // 连接数据库 - db, err := connectDB() - if err != nil { - fmt.Fprintf(os.Stderr, "连接数据库失败: %v\n", err) - os.Exit(1) - } - - // 检查数据库产品数量和处理器数量 - ctx := context.Background() - checkProductAndProcessorCounts(ctx, db, processors) - - // 查询产品信息并填充 - fmt.Println("正在查询数据库...") - successCount := 0 - for i := range processors { - product, err := queryProduct(ctx, db, processors[i].ProductCode) - if err == nil && product != nil && product.Name != "" { - processors[i].ProductName = product.Name - if product.Category != nil && product.Category.Name != "" { - processors[i].Category = product.Category.Name - } - // 格式化价格,保留两位小数 - if !product.Price.IsZero() { - processors[i].Price = product.Price.String() - } - successCount++ - } else { - // 如果查询失败,保留空值(不输出错误,避免日志过多) - processors[i].ProductName = "" - processors[i].Category = "" - processors[i].Price = "" - } - } - fmt.Printf("成功查询到 %d/%d 个产品的信息\n", successCount, len(processors)) - - // 按数据源排序 - sortProcessorsByDataSource(processors) - - // 输出表格 - printTable(processors) - - // 同时输出到文件 - writeToFiles(processors) -} - -// sortProcessorsByDataSource 按数据源排序 -func sortProcessorsByDataSource(processors []ProcessorInfo) { - // 定义数据源的排序优先级 - dataSourceOrder := map[string]int{ - "安徽智查": 1, - "羽山数据": 2, - "西部数据": 3, - "四川星维": 4, - "天眼查": 5, - "阿里云": 6, - "木子数据": 7, - "内部处理": 8, - "未知": 9, - } - - sort.Slice(processors, func(i, j int) bool { - orderI, existsI := dataSourceOrder[processors[i].DataSource] - orderJ, existsJ := dataSourceOrder[processors[j].DataSource] - - // 如果数据源不存在,放在最后 - if !existsI { - orderI = 999 - } - if !existsJ { - orderJ = 999 - } - - // 首先按数据源排序 - if orderI != orderJ { - return orderI < orderJ - } - - // 如果数据源相同,按产品编号排序 - return processors[i].ProductCode < processors[j].ProductCode - }) -} - -// writeToFiles 将表格写入文件 -func writeToFiles(processors []ProcessorInfo) { - // 写入 CSV 文件 - csvFile, err := os.Create("processors_table.csv") - if err == nil { - defer csvFile.Close() - csvFile.WriteString("\xEF\xBB\xBF") // UTF-8 BOM - csvFile.WriteString("产品编号,产品名称,分类,价格,数据源,数据源编号,成本价\n") - for _, p := range processors { - productName := strings.ReplaceAll(p.ProductName, ",", ",") - category := strings.ReplaceAll(p.Category, ",", ",") - csvFile.WriteString(fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s\n", - p.ProductCode, - productName, - category, - p.Price, - p.DataSource, - p.DataSourceCode, - p.CostPrice)) - } - fmt.Println("\n✅ CSV 文件已保存到: processors_table.csv") - } - - // 写入 Markdown 文件 - mdFile, err := os.Create("processors_table.md") - if err == nil { - defer mdFile.Close() - mdFile.WriteString("# 处理器产品信息表\n\n") - mdFile.WriteString("| 产品编号 | 产品名称 | 分类 | 价格 | 数据源 | 数据源编号 | 成本价 |\n") - mdFile.WriteString("|---------|---------|------|------|--------|-----------|--------|\n") - for _, p := range processors { - productName := p.ProductName - if productName == "" { - productName = "-" - } - category := p.Category - if category == "" { - category = "-" - } - price := p.Price - if price == "" { - price = "-" - } - dataSource := p.DataSource - if dataSource == "" { - dataSource = "-" - } - dataSourceCode := p.DataSourceCode - if dataSourceCode == "" { - dataSourceCode = "-" - } - costPrice := p.CostPrice - if costPrice == "" { - costPrice = "-" - } - mdFile.WriteString(fmt.Sprintf("| %s | %s | %s | %s | %s | %s | %s |\n", - p.ProductCode, - productName, - category, - price, - dataSource, - dataSourceCode, - costPrice)) - } - fmt.Println("✅ Markdown 文件已保存到: processors_table.md") - } -} - -// scanProcessors 扫描处理器文件 -func scanProcessors(dir string) ([]ProcessorInfo, error) { - var processors []ProcessorInfo - - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - // 跳过非 .go 文件 - if !strings.HasSuffix(path, "_processor.go") { - return nil - } - - // 跳过 comb 和 test 目录 - if strings.Contains(path, string(filepath.Separator)+"comb"+string(filepath.Separator)) { - return nil - } - if strings.Contains(path, string(filepath.Separator)+"test"+string(filepath.Separator)) { - return nil - } - - // 读取文件内容 - content, err := os.ReadFile(path) - if err != nil { - return err - } - - // 提取产品编号(从文件名) - filename := filepath.Base(path) - productCode := extractProductCode(filename) - - // 解析文件内容,提取数据源和数据源编号 - dataSource, dataSourceCode := extractDataSourceInfo(string(content)) - - processors = append(processors, ProcessorInfo{ - ProductCode: productCode, - ProductName: "", - Category: "", - Price: "", - DataSource: dataSource, - DataSourceCode: dataSourceCode, - CostPrice: "", - }) - - return nil - }) - - return processors, err -} - -// extractProductCode 从文件名提取产品编号 -func extractProductCode(filename string) string { - // 例如: dwbg6a2c_processor.go -> DWBG6A2C - name := strings.TrimSuffix(filename, "_processor.go") - return strings.ToUpper(name) -} - -// extractDataSourceInfo 从代码中提取数据源和数据源编号 -func extractDataSourceInfo(content string) (string, string) { - // 先尝试匹配特殊的 CallAPI 方法(如 G05HZ01CallAPI) - specialCallAPIPattern := regexp.MustCompile(`deps\.(\w+Service)\.(\w+CallAPI)\([^,]+,\s*"([^"]+)"`) - specialMatches := specialCallAPIPattern.FindAllStringSubmatch(content, -1) - if len(specialMatches) > 0 { - serviceName := specialMatches[0][1] - dataSourceCode := specialMatches[0][3] - return getDataSourceName(serviceName), dataSourceCode - } - - // 先尝试匹配 AlicloudService 的特殊情况(第一个参数是字符串路径,没有 ctx) - // 匹配: deps.AlicloudService.CallAPI("path", ...) - alicloudPattern := regexp.MustCompile(`deps\.AlicloudService\.CallAPI\(\s*"([^"]+)"`) - alicloudMatches := alicloudPattern.FindAllStringSubmatch(content, -1) - if len(alicloudMatches) > 0 { - path := alicloudMatches[0][1] - parts := strings.Split(path, "/") - dataSourceCode := path - if len(parts) > 0 { - dataSourceCode = parts[len(parts)-1] - } - return "阿里云", dataSourceCode - } - - // 匹配普通的 CallAPI 调用 - // 匹配模式: deps.ServiceName.CallAPI(ctx, "CODE", ...) 或 deps.ServiceName.CallAPI(ctx, projectID, ...) - callAPIPattern := regexp.MustCompile(`deps\.(\w+Service)\.CallAPI\([^,]*,\s*([^,)]+)`) - matches := callAPIPattern.FindAllStringSubmatch(content, -1) - - if len(matches) == 0 { - // 检查是否有直接的 HTTP 请求(如 COMENT01) - if strings.Contains(content, "http.NewRequest") || strings.Contains(content, "http.Client") { - return "内部处理", "" - } - // 检查是否调用了其他处理器(如 QYGL3F8E) - if strings.Contains(content, "Process") && strings.Contains(content, "Request") { - return "内部处理", "" - } - return "未知", "" - } - - // 取第一个匹配的服务 - serviceName := matches[0][1] - codeOrVar := strings.TrimSpace(matches[0][2]) - - var dataSourceCode string - - // 如果是字符串字面量(带引号),直接提取 - if strings.HasPrefix(codeOrVar, `"`) && strings.HasSuffix(codeOrVar, `"`) { - dataSourceCode = strings.Trim(codeOrVar, `"`) - // 对于 AlicloudService,提取路径的最后部分作为数据源编号 - if serviceName == "AlicloudService" { - parts := strings.Split(dataSourceCode, "/") - if len(parts) > 0 { - dataSourceCode = parts[len(parts)-1] - } - } - } else { - // 如果是变量(如 projectID),尝试查找变量定义 - // 对于 XingweiService,查找 projectID 变量 - if strings.Contains(codeOrVar, "projectID") || codeOrVar == "projectID" { - projectIDPattern := regexp.MustCompile(`projectID\s*:=\s*"([^"]+)"`) - projectIDMatches := projectIDPattern.FindAllStringSubmatch(content, -1) - if len(projectIDMatches) > 0 { - // 取最后一个匹配的 projectID(通常是最接近 CallAPI 的那个) - dataSourceCode = projectIDMatches[len(projectIDMatches)-1][1] - } else { - dataSourceCode = "变量未找到" - } - } else { - // 尝试查找变量定义(通用模式) - varPattern := regexp.MustCompile(regexp.QuoteMeta(codeOrVar) + `\s*:=\s*"([^"]+)"`) - varMatches := varPattern.FindAllStringSubmatch(content, -1) - if len(varMatches) > 0 { - dataSourceCode = varMatches[len(varMatches)-1][1] - } else { - dataSourceCode = codeOrVar - } - } - } - - // 根据服务名确定数据源 - dataSource := getDataSourceName(serviceName) - - return dataSource, dataSourceCode -} - -// getDataSourceName 根据服务名获取数据源名称 -func getDataSourceName(serviceName string) string { - switch serviceName { - case "ZhichaService": - return "安徽智查" - case "YushanService": - return "羽山数据" - case "WestDexService": - return "西部数据" - case "XingweiService": - return "四川星维" - case "TianYanChaService": - return "天眼查" - case "AlicloudService": - return "阿里云" - case "MuziService": - return "木子数据" - default: - return "未知" - } -} - -// connectDB 连接数据库 -func connectDB() (*gorm.DB, error) { - // 数据库连接配置 - dsn := "host=1.117.67.95 user=tyapi_user password=Pg9mX4kL8nW2rT5y dbname=tyapi port=25010 sslmode=disable TimeZone=Asia/Shanghai" - - // 配置GORM,使用单数表名(与项目配置一致) - gormConfig := &gorm.Config{ - NamingStrategy: schema.NamingStrategy{ - SingularTable: true, // 使用单数表名 - }, - Logger: logger.Default.LogMode(logger.Silent), // 禁用日志输出,减少噪音 - } - - db, err := gorm.Open(postgres.Open(dsn), gormConfig) - if err != nil { - return nil, fmt.Errorf("连接数据库失败: %w", err) - } - - // 测试连接 - sqlDB, err := db.DB() - if err != nil { - return nil, fmt.Errorf("获取数据库实例失败: %w", err) - } - - if err := sqlDB.Ping(); err != nil { - return nil, fmt.Errorf("数据库连接测试失败: %w", err) - } - - fmt.Println("数据库连接成功") - return db, nil -} - -// checkProductAndProcessorCounts 检查数据库产品数量和处理器数量 -func checkProductAndProcessorCounts(ctx context.Context, db *gorm.DB, processors []ProcessorInfo) { - // 统计处理器数量(排除 comb) - processorCount := len(processors) - fmt.Printf("\n=== 统计信息 ===\n") - fmt.Printf("处理器数量(排除 comb): %d\n", processorCount) - - // 统计数据库中的产品数量(排除组合包) - var productCount int64 - // 先尝试单数表名 - err := db.WithContext(ctx). - Table("product"). - Where("is_package = ? AND deleted_at IS NULL", false). - Count(&productCount).Error - - // 如果单数表名查询失败,尝试复数表名 - if err != nil { - if strings.Contains(err.Error(), "does not exist") { - err = db.WithContext(ctx). - Table("products"). - Where("is_package = ? AND deleted_at IS NULL", false). - Count(&productCount).Error - } - } - - if err == nil { - fmt.Printf("数据库产品数量(排除组合包): %d\n", productCount) - } else { - fmt.Printf("数据库产品数量查询失败: %v\n", err) - } - - // 统计有匹配产品的处理器数量 - matchedCount := 0 - for _, p := range processors { - var count int64 - err := db.WithContext(ctx). - Table("product"). - Where("code = ? AND deleted_at IS NULL", p.ProductCode). - Count(&count).Error - - if err != nil && strings.Contains(err.Error(), "does not exist") { - err = db.WithContext(ctx). - Table("products"). - Where("code = ? AND deleted_at IS NULL", p.ProductCode). - Count(&count).Error - } - - if err == nil && count > 0 { - matchedCount++ - } - } - - fmt.Printf("有匹配产品的处理器数量: %d/%d\n", matchedCount, processorCount) - fmt.Printf("无匹配产品的处理器数量: %d/%d\n", processorCount-matchedCount, processorCount) - fmt.Println() -} - -// queryProduct 查询产品信息 -func queryProduct(ctx context.Context, db *gorm.DB, code string) (*Product, error) { - var product Product - var err error - - // 尝试不同的表名查询(先尝试单数表名,因为用户确认表名是 product) - tableNames := []string{"product", "products"} - for _, tableName := range tableNames { - err = db.WithContext(ctx). - Table(tableName). - Select("code, name, category_id, price"). - Where("code = ? AND deleted_at IS NULL", code). - First(&product).Error - - if err == nil { - // 查询成功,查询分类信息 - if product.CategoryID != "" { - var category ProductCategory - // 尝试不同的分类表名(先尝试单数表名) - categoryTableNames := []string{"product_category", "product_categories"} - for _, catTableName := range categoryTableNames { - err = db.WithContext(ctx). - Table(catTableName). - Select("id, name"). - Where("id = ? AND deleted_at IS NULL", product.CategoryID). - First(&category).Error - if err == nil { - product.Category = &category - break - } - // 如果是表不存在的错误,继续尝试下一个表名 - if err != nil && strings.Contains(err.Error(), "does not exist") { - continue - } - // 如果是记录不存在的错误,也继续尝试(可能是表名不对) - if err == gorm.ErrRecordNotFound { - continue - } - } - // 即使分类查询失败,也返回产品信息(分类可以为空) - } - return &product, nil - } - - // 检查错误类型 - errStr := err.Error() - // 如果是表不存在的错误,继续尝试下一个表名 - if strings.Contains(errStr, "does not exist") { - continue - } - // 如果是记录不存在的错误,也继续尝试(可能是表名不对) - if err == gorm.ErrRecordNotFound { - continue - } - // 其他错误直接返回 - return nil, err - } - - // 所有表名都查询失败 - return nil, gorm.ErrRecordNotFound -} - -// printTable 打印表格 -func printTable(processors []ProcessorInfo) { - // 打印表头 - fmt.Printf("%-15s %-30s %-20s %-15s %-15s %-20s %-15s\n", - "产品编号", "产品名称", "分类", "价格", "数据源", "数据源编号", "成本价") - fmt.Println(strings.Repeat("-", 130)) - - // 打印数据 - for _, p := range processors { - fmt.Printf("%-15s %-30s %-20s %-15s %-15s %-20s %-15s\n", - p.ProductCode, - p.ProductName, - p.Category, - p.Price, - p.DataSource, - p.DataSourceCode, - p.CostPrice) - } - - // 打印 CSV 格式 - fmt.Println("\n=== CSV 格式 ===") - fmt.Println("产品编号,产品名称,分类,价格,数据源,数据源编号,成本价") - for _, p := range processors { - // 转义 CSV 中的特殊字符 - productName := strings.ReplaceAll(p.ProductName, ",", ",") - category := strings.ReplaceAll(p.Category, ",", ",") - fmt.Printf("%s,%s,%s,%s,%s,%s,%s\n", - p.ProductCode, - productName, - category, - p.Price, - p.DataSource, - p.DataSourceCode, - p.CostPrice) - } - - // 打印 Markdown 表格格式 - fmt.Println("\n=== Markdown 表格格式 ===") - fmt.Println("| 产品编号 | 产品名称 | 分类 | 价格 | 数据源 | 数据源编号 | 成本价 |") - fmt.Println("|---------|---------|------|------|--------|-----------|--------|") - for _, p := range processors { - productName := p.ProductName - if productName == "" { - productName = "-" - } - category := p.Category - if category == "" { - category = "-" - } - price := p.Price - if price == "" { - price = "-" - } - dataSource := p.DataSource - if dataSource == "" { - dataSource = "-" - } - dataSourceCode := p.DataSourceCode - if dataSourceCode == "" { - dataSourceCode = "-" - } - costPrice := p.CostPrice - if costPrice == "" { - costPrice = "-" - } - fmt.Printf("| %s | %s | %s | %s | %s | %s | %s |\n", - p.ProductCode, - productName, - category, - price, - dataSource, - dataSourceCode, - costPrice) - } -} - diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 deleted file mode 100644 index 5cddd3f..0000000 --- a/scripts/deploy.ps1 +++ /dev/null @@ -1,255 +0,0 @@ -# TYAPI 生产环境部署脚本 (PowerShell版本) -# 使用方法: .\scripts\deploy.ps1 [版本号] - -param( - [string]$Version = "latest" -) - -# 配置 -$REGISTRY_URL = "docker-registry.tianyuanapi.com" -$IMAGE_NAME = "tyapi-server" -$APP_VERSION = $Version -$BUILD_TIME = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ") - -try { - $GIT_COMMIT = git rev-parse --short HEAD 2>$null - if (-not $GIT_COMMIT) { $GIT_COMMIT = "dev" } -} -catch { - $GIT_COMMIT = "dev" -} - -# 颜色输出函数 -function Write-Info($message) { - Write-Host "[INFO] $message" -ForegroundColor Blue -} - -function Write-Success($message) { - Write-Host "[SUCCESS] $message" -ForegroundColor Green -} - -function Write-Warning($message) { - Write-Host "[WARNING] $message" -ForegroundColor Yellow -} - -function Write-Error($message) { - Write-Host "[ERROR] $message" -ForegroundColor Red -} - -# 检查必要工具 -function Test-Requirements { - Write-Info "检查部署环境..." - - if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { - Write-Error "Docker 未安装或不在 PATH 中" - exit 1 - } - - if (-not (Get-Command docker-compose -ErrorAction SilentlyContinue)) { - Write-Error "docker-compose 未安装或不在 PATH 中" - exit 1 - } - - if (-not (Get-Command git -ErrorAction SilentlyContinue)) { - Write-Warning "Git 未安装,将使用默认提交哈希" - } - - Write-Success "环境检查通过" -} - -# 构建 Docker 镜像 -function Build-Image { - Write-Info "开始构建 Docker 镜像..." - - docker build ` - --build-arg VERSION="$APP_VERSION" ` - --build-arg COMMIT="$GIT_COMMIT" ` - --build-arg BUILD_TIME="$BUILD_TIME" ` - -t "$REGISTRY_URL/$IMAGE_NAME`:$APP_VERSION" ` - -t "$REGISTRY_URL/$IMAGE_NAME`:latest" ` - . - - if ($LASTEXITCODE -ne 0) { - Write-Error "Docker 镜像构建失败" - exit 1 - } - - Write-Success "Docker 镜像构建完成" -} - -# 推送镜像到私有仓库 -function Push-Image { - Write-Info "推送镜像到私有仓库..." - - # 推送版本标签 - docker push "$REGISTRY_URL/$IMAGE_NAME`:$APP_VERSION" - if ($LASTEXITCODE -eq 0) { - Write-Success "已推送版本标签: $APP_VERSION" - } - else { - Write-Error "推送版本标签失败" - exit 1 - } - - # 推送latest标签 - docker push "$REGISTRY_URL/$IMAGE_NAME`:latest" - if ($LASTEXITCODE -eq 0) { - Write-Success "已推送latest标签" - } - else { - Write-Error "推送latest标签失败" - exit 1 - } -} - -# 准备生产环境配置 -function Test-Config { - Write-Info "准备生产环境配置..." - - # 检查.env文件是否存在 - if (-not (Test-Path ".env")) { - if (Test-Path ".env.production") { - Write-Warning ".env文件不存在,正在复制模板..." - Copy-Item ".env.production" ".env" - Write-Warning "请编辑 .env 文件并设置正确的配置值" - exit 1 - } - else { - Write-Error "配置文件 .env 和 .env.production 都不存在" - exit 1 - } - } - - # 验证关键配置 - $envContent = Get-Content ".env" -Raw - if (-not ($envContent -match "^DB_PASSWORD=" -and -not ($envContent -match "your_secure_database_password_here"))) { - Write-Error "请在 .env 文件中设置安全的数据库密码" - exit 1 - } - - if (-not ($envContent -match "^JWT_SECRET=" -and -not ($envContent -match "your_super_secure_jwt_secret"))) { - Write-Error "请在 .env 文件中设置安全的JWT密钥" - exit 1 - } - - Write-Success "配置检查通过" -} - -# 部署到生产环境 -function Start-Deploy { - Write-Info "开始部署到生产环境..." - - # 设置版本环境变量 - $env:APP_VERSION = $APP_VERSION - - # 停止现有服务 - Write-Info "停止现有服务..." - docker-compose -f docker-compose.prod.yml down --remove-orphans - - # 清理未使用的镜像 - Write-Info "清理未使用的Docker资源..." - docker image prune -f - - # 拉取最新镜像 - Write-Info "拉取最新镜像..." - docker-compose -f docker-compose.prod.yml pull - - # 启动服务 - Write-Info "启动生产环境服务..." - docker-compose -f docker-compose.prod.yml up -d - - if ($LASTEXITCODE -ne 0) { - Write-Error "服务启动失败" - exit 1 - } - - # 等待服务启动 - Write-Info "等待服务启动..." - Start-Sleep -Seconds 30 - - # 检查服务状态 - Write-Info "检查服务状态..." - docker-compose -f docker-compose.prod.yml ps - - # 健康检查 - Write-Info "执行健康检查..." - $maxAttempts = 10 - $attempt = 0 - - while ($attempt -lt $maxAttempts) { - try { - $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop - if ($response.StatusCode -eq 200) { - Write-Success "应用健康检查通过" - break - } - } - catch { - $attempt++ - Write-Info "健康检查失败,重试 $attempt/$maxAttempts..." - Start-Sleep -Seconds 10 - } - } - - if ($attempt -eq $maxAttempts) { - Write-Error "应用健康检查失败,请检查日志" - docker-compose -f docker-compose.prod.yml logs tyapi-app - exit 1 - } - - Write-Success "部署完成!" -} - -# 显示部署信息 -function Show-Info { - Write-Info "部署信息:" - Write-Host " 版本: $APP_VERSION" - Write-Host " 提交: $GIT_COMMIT" - Write-Host " 构建时间: $BUILD_TIME" - Write-Host " 镜像: $REGISTRY_URL/$IMAGE_NAME`:$APP_VERSION" - Write-Host "" - Write-Host "🌐 服务访问地址:" - Write-Host " 📱 API服务: http://localhost:8080" - Write-Host " 📚 API文档: http://localhost:8080/swagger/index.html" - Write-Host " 💚 健康检查: http://localhost:8080/health" - Write-Host "" - Write-Host "📊 监控和追踪:" - Write-Host " 📈 Grafana仪表盘: http://localhost:3000" - Write-Host " 🔍 Prometheus监控: http://localhost:9090" - Write-Host " 🔗 Jaeger链路追踪: http://localhost:16686" - Write-Host "" - Write-Host "🛠 管理工具:" - Write-Host " 🗄️ pgAdmin数据库: http://localhost:5050" - Write-Host " 📦 MinIO对象存储: http://localhost:9000" - Write-Host " 🎛️ MinIO控制台: http://localhost:9001" - Write-Host "" - Write-Host "🔧 管理命令:" - Write-Host " 查看日志: docker-compose -f docker-compose.prod.yml logs -f" - Write-Host " 停止服务: docker-compose -f docker-compose.prod.yml down" - Write-Host " 查看状态: docker-compose -f docker-compose.prod.yml ps" - Write-Host " 重启应用: docker-compose -f docker-compose.prod.yml restart tyapi-app" -} - -# 主函数 -function Main { - Write-Info "开始 TYAPI 生产环境部署..." - Write-Info "版本: $APP_VERSION" - - Test-Requirements - Test-Config - Build-Image - Push-Image - Start-Deploy - Show-Info - - Write-Success "🎉 部署成功!" -} - -# 运行主函数 -try { - Main -} -catch { - Write-Error "部署过程中发生错误: $($_.Exception.Message)" - exit 1 -} \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100644 index 4535aba..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/bash - -# TYAPI 生产环境部署脚本 -# 使用方法: ./scripts/deploy.sh [version] - -set -e - -# 配置 -REGISTRY_URL="docker-registry.tianyuanapi.com" -IMAGE_NAME="tyapi-server" -APP_VERSION=${1:-latest} -BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo 'dev') - -# 颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 日志函数 -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 检查必要工具 -check_requirements() { - log_info "检查部署环境..." - - if ! command -v docker &> /dev/null; then - log_error "Docker 未安装或不在 PATH 中" - exit 1 - fi - - if ! command -v docker-compose &> /dev/null; then - log_error "docker-compose 未安装或不在 PATH 中" - exit 1 - fi - - if ! command -v git &> /dev/null; then - log_warning "Git 未安装,将使用默认提交哈希" - fi - - log_success "环境检查通过" -} - -# 构建 Docker 镜像 -build_image() { - log_info "开始构建 Docker 镜像..." - - docker build \ - --build-arg VERSION="$APP_VERSION" \ - --build-arg COMMIT="$GIT_COMMIT" \ - --build-arg BUILD_TIME="$BUILD_TIME" \ - -t "$REGISTRY_URL/$IMAGE_NAME:$APP_VERSION" \ - -t "$REGISTRY_URL/$IMAGE_NAME:latest" \ - . - - log_success "Docker 镜像构建完成" -} - -# 推送镜像到私有仓库 -push_image() { - log_info "推送镜像到私有仓库..." - - # 推送版本标签 - docker push "$REGISTRY_URL/$IMAGE_NAME:$APP_VERSION" - log_success "已推送版本标签: $APP_VERSION" - - # 推送latest标签 - docker push "$REGISTRY_URL/$IMAGE_NAME:latest" - log_success "已推送latest标签" -} - -# 准备生产环境配置 -prepare_config() { - log_info "准备生产环境配置..." - - # 检查.env文件是否存在 - if [ ! -f ".env" ]; then - if [ -f ".env.production" ]; then - log_warning ".env文件不存在,正在复制模板..." - cp .env.production .env - log_warning "请编辑 .env 文件并设置正确的配置值" - exit 1 - else - log_error "配置文件 .env 和 .env.production 都不存在" - exit 1 - fi - fi - - # 验证关键配置 - if ! grep -q "^DB_PASSWORD=" .env || grep -q "your_secure_database_password_here" .env; then - log_error "请在 .env 文件中设置安全的数据库密码" - exit 1 - fi - - if ! grep -q "^JWT_SECRET=" .env || grep -q "your_super_secure_jwt_secret" .env; then - log_error "请在 .env 文件中设置安全的JWT密钥" - exit 1 - fi - - log_success "配置检查通过" -} - -# 部署到生产环境 -deploy() { - log_info "开始部署到生产环境..." - - # 设置版本环境变量 - export APP_VERSION="$APP_VERSION" - - # 停止现有服务 - log_info "停止现有服务..." - docker-compose -f docker-compose.prod.yml down --remove-orphans - - # 清理未使用的镜像 - log_info "清理未使用的Docker资源..." - docker image prune -f - - # 拉取最新镜像 - log_info "拉取最新镜像..." - docker-compose -f docker-compose.prod.yml pull - - # 启动服务 - log_info "启动生产环境服务..." - docker-compose -f docker-compose.prod.yml up -d - - # 等待服务启动 - log_info "等待服务启动..." - sleep 30 - - # 检查服务状态 - log_info "检查服务状态..." - docker-compose -f docker-compose.prod.yml ps - - # 健康检查 - log_info "执行健康检查..." - max_attempts=10 - attempt=0 - - while [ $attempt -lt $max_attempts ]; do - if curl -f http://localhost:8080/health > /dev/null 2>&1; then - log_success "应用健康检查通过" - break - else - attempt=$((attempt + 1)) - log_info "健康检查失败,重试 $attempt/$max_attempts..." - sleep 10 - fi - done - - if [ $attempt -eq $max_attempts ]; then - log_error "应用健康检查失败,请检查日志" - docker-compose -f docker-compose.prod.yml logs tyapi-app - exit 1 - fi - - log_success "部署完成!" -} - -# 显示部署信息 -show_info() { - log_info "部署信息:" - echo " 版本: $APP_VERSION" - echo " 提交: $GIT_COMMIT" - echo " 构建时间: $BUILD_TIME" - echo " 镜像: $REGISTRY_URL/$IMAGE_NAME:$APP_VERSION" - echo "" - echo "🌐 服务访问地址:" - echo " 📱 API服务: http://localhost:8080" - echo " 📚 API文档: http://localhost:8080/swagger/index.html" - echo " 💚 健康检查: http://localhost:8080/health" - echo "" - echo "📊 监控和追踪:" - echo " 📈 Grafana仪表盘: http://localhost:3000" - echo " 🔍 Prometheus监控: http://localhost:9090" - echo " 🔗 Jaeger链路追踪: http://localhost:16686" - echo "" - echo "🛠 管理工具:" - echo " 🗄️ pgAdmin数据库: http://localhost:5050" - echo " 📦 MinIO对象存储: http://localhost:9000" - echo " 🎛️ MinIO控制台: http://localhost:9001" - echo "" - echo "🔧 管理命令:" - echo " 查看日志: docker-compose -f docker-compose.prod.yml logs -f" - echo " 停止服务: docker-compose -f docker-compose.prod.yml down" - echo " 查看状态: docker-compose -f docker-compose.prod.yml ps" - echo " 重启应用: docker-compose -f docker-compose.prod.yml restart tyapi-app" -} - -# 主函数 -main() { - log_info "开始 TYAPI 生产环境部署..." - log_info "版本: $APP_VERSION" - - check_requirements - prepare_config - build_image - push_image - deploy - show_info - - log_success "🎉 部署成功!" -} - -# 运行主函数 -main "$@" \ No newline at end of file diff --git a/scripts/find_missing_cost_price.go b/scripts/find_missing_cost_price.go deleted file mode 100644 index 60ed627..0000000 --- a/scripts/find_missing_cost_price.go +++ /dev/null @@ -1,162 +0,0 @@ -package main - -import ( - "context" - "encoding/csv" - "fmt" - "os" - "strings" - - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "gorm.io/gorm/schema" -) - -// Product 产品实体(简化版) -type Product struct { - Code string `gorm:"column:code"` - Name string `gorm:"column:name"` - CostPrice *float64 `gorm:"column:cost_price"` -} - -func main() { - // 连接数据库 - db, err := connectDB() - if err != nil { - fmt.Fprintf(os.Stderr, "连接数据库失败: %v\n", err) - os.Exit(1) - } - - ctx := context.Background() - - fmt.Println("正在查询数据库中未设置成本价的产品...") - fmt.Println() - - // 查询没有设置成本价的产品(cost_price IS NULL 或 cost_price = 0) - var products []Product - err = db.WithContext(ctx). - Table("product"). - Select("code, name, cost_price"). - Where("deleted_at IS NULL AND (cost_price IS NULL OR cost_price = 0)"). - Order("code ASC"). - Find(&products).Error - - // 如果单数表名查询失败,尝试复数表名 - if err != nil { - if strings.Contains(err.Error(), "does not exist") { - err = db.WithContext(ctx). - Table("products"). - Select("code, name, cost_price"). - Where("deleted_at IS NULL AND (cost_price IS NULL OR cost_price = 0)"). - Order("code ASC"). - Find(&products).Error - } - } - - if err != nil { - fmt.Fprintf(os.Stderr, "查询失败: %v\n", err) - os.Exit(1) - } - - totalCount := len(products) - fmt.Printf("找到 %d 个未设置成本价的产品\n\n", totalCount) - - if totalCount == 0 { - fmt.Println("所有产品都已设置成本价!") - return - } - - // 打印到控制台 - fmt.Println("=== 未设置成本价的产品列表 ===") - fmt.Printf("%-20s %-50s %-15s\n", "产品编号", "产品名称", "成本价") - fmt.Println(strings.Repeat("-", 85)) - - for _, p := range products { - costPriceStr := "-" - if p.CostPrice != nil && *p.CostPrice != 0 { - costPriceStr = fmt.Sprintf("%.2f", *p.CostPrice) - } - productName := p.Name - if productName == "" { - productName = "-" - } - fmt.Printf("%-20s %-50s %-15s\n", p.Code, productName, costPriceStr) - } - - fmt.Println() - - // 保存到 CSV 文件 - csvFile, err := os.Create("missing_cost_price.csv") - if err != nil { - fmt.Fprintf(os.Stderr, "创建 CSV 文件失败: %v\n", err) - return - } - defer csvFile.Close() - - // 写入 UTF-8 BOM - csvFile.WriteString("\xEF\xBB\xBF") - - writer := csv.NewWriter(csvFile) - defer writer.Flush() - - // 写入表头 - headers := []string{"产品编号", "产品名称", "成本价"} - if err := writer.Write(headers); err != nil { - fmt.Fprintf(os.Stderr, "写入 CSV 表头失败: %v\n", err) - return - } - - // 写入数据 - for _, p := range products { - costPriceStr := "" - if p.CostPrice != nil && *p.CostPrice != 0 { - costPriceStr = fmt.Sprintf("%.2f", *p.CostPrice) - } - productName := p.Name - if productName == "" { - productName = "-" - } - record := []string{p.Code, productName, costPriceStr} - if err := writer.Write(record); err != nil { - fmt.Fprintf(os.Stderr, "写入 CSV 数据失败: %v\n", err) - return - } - } - - fmt.Printf("✅ 结果已保存到: missing_cost_price.csv\n") - fmt.Printf("📊 总计: %d 个产品未设置成本价\n", totalCount) -} - -// connectDB 连接数据库 -func connectDB() (*gorm.DB, error) { - // 数据库连接配置 - dsn := "host=1.117.67.95 user=tyapi_user password=Pg9mX4kL8nW2rT5y dbname=tyapi port=25010 sslmode=disable TimeZone=Asia/Shanghai" - - // 配置GORM,使用单数表名(与项目配置一致) - gormConfig := &gorm.Config{ - NamingStrategy: schema.NamingStrategy{ - SingularTable: true, // 使用单数表名 - }, - Logger: logger.Default.LogMode(logger.Info), // 显示 SQL 日志 - } - - db, err := gorm.Open(postgres.Open(dsn), gormConfig) - if err != nil { - return nil, fmt.Errorf("连接数据库失败: %w", err) - } - - // 测试连接 - sqlDB, err := db.DB() - if err != nil { - return nil, fmt.Errorf("获取数据库实例失败: %w", err) - } - - if err := sqlDB.Ping(); err != nil { - return nil, fmt.Errorf("数据库连接测试失败: %w", err) - } - - fmt.Println("数据库连接成功") - return db, nil -} - diff --git a/scripts/generate_admin_token_simple.go b/scripts/generate_admin_token_simple.go deleted file mode 100644 index 8006f83..0000000 --- a/scripts/generate_admin_token_simple.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "tyapi-server/internal/config" - "tyapi-server/internal/shared/middleware" - - "go.uber.org/zap" -) - -func main() { - // 固定的用户信息 - userID := "93b2fb79-4def-4ad2-8004-08943fcf2fa9" - phone := "17537188270" - email := "" - userType := "user" - - // 加载配置文件 - cfg, err := config.LoadConfig() - if err != nil { - log.Fatalf("加载配置文件失败: %v", err) - } - - // 创建logger - logger, err := zap.NewProduction() - if err != nil { - log.Fatalf("创建logger失败: %v", err) - } - defer logger.Sync() - - // 创建JWT认证中间件 - jwtAuth := middleware.NewJWTAuthMiddleware(cfg, logger) - - // 生成token - token, err := jwtAuth.GenerateToken(userID, phone, email, userType) - if err != nil { - log.Fatalf("生成token失败: %v", err) - } - - // 输出结果 - fmt.Println("=== 管理员Token生成成功 ===") - fmt.Printf("用户ID: %s\n", userID) - fmt.Printf("手机号: %s\n", phone) - fmt.Printf("邮箱: %s\n", email) - fmt.Printf("用户类型: %s\n", userType) - fmt.Printf("过期时间: %s\n", cfg.JWT.ExpiresIn.String()) - fmt.Println("\n=== Token ===") - fmt.Println(token) - fmt.Println("\n=== 使用说明 ===") - fmt.Println("在API请求的Authorization头部中使用:") - fmt.Printf("Authorization: Bearer %s\n", token) - fmt.Println("\n=== 示例curl命令 ===") - fmt.Printf("curl -H \"Authorization: Bearer %s\" http://localhost:8080/api/v1/user/profile\n", token) -} \ No newline at end of file diff --git a/scripts/init.sql b/scripts/init.sql deleted file mode 100644 index 82fb120..0000000 --- a/scripts/init.sql +++ /dev/null @@ -1,65 +0,0 @@ --- TYAPI Server Database Initialization Script --- This script runs when PostgreSQL container starts for the first time - --- Create development database if it doesn't exist --- Note: tyapi_dev is already created by POSTGRES_DB environment variable - --- Create test database for running tests --- Note: Skip database creation in init script, handle in application if needed - --- Create production database (for reference) --- CREATE DATABASE tyapi_prod; - --- Connect to development database -\c tyapi_dev; - --- Enable necessary extensions -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -CREATE EXTENSION IF NOT EXISTS "pg_trgm"; - -CREATE EXTENSION IF NOT EXISTS "btree_gin"; - --- Create schemas for better organization -CREATE SCHEMA IF NOT EXISTS public; - -CREATE SCHEMA IF NOT EXISTS logs; - -CREATE SCHEMA IF NOT EXISTS metrics; - --- Set search path -SET search_path TO public, logs, metrics; - --- Test database setup will be handled by application migrations --- when needed, since we don't create it in this init script - --- Continue with development database setup --- (already connected to tyapi_dev) - --- Create application-specific roles (optional) --- CREATE ROLE tyapi_app WITH LOGIN PASSWORD 'app_password'; --- CREATE ROLE tyapi_readonly WITH LOGIN PASSWORD 'readonly_password'; - --- Grant permissions --- GRANT CONNECT ON DATABASE tyapi_dev TO tyapi_app; --- GRANT USAGE ON SCHEMA public TO tyapi_app; --- GRANT CREATE ON SCHEMA public TO tyapi_app; - --- Initial seed data can be added here --- This will be replaced by proper migrations in the application - --- Log the initialization --- Note: pg_stat_statements extension may not be available, skip this insert - --- Create a simple health check function -CREATE OR REPLACE FUNCTION health_check() -RETURNS json AS $$ -BEGIN - RETURN json_build_object( - 'status', 'healthy', - 'database', current_database(), - 'timestamp', now(), - 'version', version() - ); -END; -$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/scripts/log-manager.sh b/scripts/log-manager.sh deleted file mode 100644 index 8318ba0..0000000 --- a/scripts/log-manager.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/bin/bash - -# 日志管理脚本 -# 用于清理旧日志文件和查看日志统计信息 - -LOG_DIR="./logs" -RETENTION_DAYS=30 - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 打印带颜色的消息 -print_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 显示帮助信息 -show_help() { - echo "日志管理脚本" - echo "" - echo "用法: $0 [命令]" - echo "" - echo "命令:" - echo " clean - 清理超过 $RETENTION_DAYS 天的旧日志文件" - echo " stats - 显示日志统计信息" - echo " size - 显示日志目录大小" - echo " list - 列出所有日志文件" - echo " help - 显示此帮助信息" - echo "" - echo "示例:" - echo " $0 clean # 清理旧日志" - echo " $0 stats # 查看统计信息" -} - -# 清理旧日志文件 -clean_old_logs() { - print_info "开始清理超过 $RETENTION_DAYS 天的旧日志文件..." - - if [ ! -d "$LOG_DIR" ]; then - print_error "日志目录 $LOG_DIR 不存在" - return 1 - fi - - # 查找并删除超过指定天数的日志文件 - find "$LOG_DIR" -name "*.log*" -type f -mtime +$RETENTION_DAYS -exec rm -f {} \; - - # 删除空的日期目录 - find "$LOG_DIR" -type d -empty -delete - - print_success "旧日志文件清理完成" -} - -# 显示日志统计信息 -show_stats() { - print_info "日志统计信息:" - echo "" - - if [ ! -d "$LOG_DIR" ]; then - print_error "日志目录 $LOG_DIR 不存在" - return 1 - fi - - # 总文件数 - total_files=$(find "$LOG_DIR" -name "*.log*" -type f | wc -l) - echo "总日志文件数: $total_files" - - # 总大小 - total_size=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1) - echo "日志目录总大小: $total_size" - - # 按日期统计 - echo "" - echo "按日期统计:" - for date_dir in "$LOG_DIR"/*/; do - if [ -d "$date_dir" ]; then - date_name=$(basename "$date_dir") - file_count=$(find "$date_dir" -name "*.log*" -type f | wc -l) - dir_size=$(du -sh "$date_dir" 2>/dev/null | cut -f1) - echo " $date_name: $file_count 个文件, $dir_size" - fi - done - - # 最近修改的文件 - echo "" - echo "最近修改的日志文件:" - find "$LOG_DIR" -name "*.log*" -type f -exec ls -lh {} \; | head -5 -} - -# 显示日志目录大小 -show_size() { - if [ ! -d "$LOG_DIR" ]; then - print_error "日志目录 $LOG_DIR 不存在" - return 1 - fi - - total_size=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1) - print_info "日志目录大小: $total_size" -} - -# 列出所有日志文件 -list_logs() { - if [ ! -d "$LOG_DIR" ]; then - print_error "日志目录 $LOG_DIR 不存在" - return 1 - fi - - print_info "所有日志文件:" - find "$LOG_DIR" -name "*.log*" -type f -exec ls -lh {} \; -} - -# 主函数 -main() { - case "$1" in - "clean") - clean_old_logs - ;; - "stats") - show_stats - ;; - "size") - show_size - ;; - "list") - list_logs - ;; - "help"|"-h"|"--help"|"") - show_help - ;; - *) - print_error "未知命令: $1" - show_help - exit 1 - ;; - esac -} - -# 执行主函数 -main "$@" \ No newline at end of file diff --git a/scripts/migrate_repository.md b/scripts/migrate_repository.md deleted file mode 100644 index e0e227a..0000000 --- a/scripts/migrate_repository.md +++ /dev/null @@ -1,372 +0,0 @@ -# Repository 迁移指南 - -## 从传统模式迁移到 BaseRepositoryImpl 模式 - -### 步骤 1:修改结构体定义 - -**之前:** -```go -type GormExampleRepository struct { - db *gorm.DB - logger *zap.Logger -} -``` - -**之后:** -```go -type GormExampleRepository struct { - *database.BaseRepositoryImpl // 嵌入基础Repository实现 -} -``` - -### 步骤 2:修改构造函数 - -**之前:** -```go -func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository { - return &GormExampleRepository{ - db: db, - logger: logger, - } -} -``` - -**之后:** -```go -func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository { - return &GormExampleRepository{ - BaseRepositoryImpl: database.NewBaseRepositoryImpl(db, logger), - } -} -``` - -### 步骤 3:删除 getDB 方法 - -**删除这样的代码:** -```go -func (r *GormExampleRepository) getDB(ctx context.Context) *gorm.DB { - if tx, ok := database.GetTx(ctx); ok { - return tx - } - return r.db -} -``` - -### 步骤 4:实现 Repository[T] 接口方法 - -#### 基础 CRUD 操作 - -**Create - 之前:** -```go -func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) { - r.logger.Info("创建实体", zap.String("id", entity.ID)) - err := r.getDB(ctx).WithContext(ctx).Create(&entity).Error - return entity, err -} -``` - -**Create - 之后:** -```go -func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) { - r.GetLogger().Info("创建实体", zap.String("id", entity.ID)) - err := r.BaseRepositoryImpl.Create(ctx, &entity) - return entity, err -} -``` - -**GetByID - 之前:** -```go -func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) { - var entity Entity - err := r.getDB(ctx).WithContext(ctx).Where("id = ?", id).First(&entity).Error - return entity, err -} -``` - -**GetByID - 之后:** -```go -func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) { - var entity Entity - err := r.BaseRepositoryImpl.GetByID(ctx, id, &entity) - return entity, err -} -``` - -**Update - 之前:** -```go -func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error { - r.logger.Info("更新实体", zap.String("id", entity.ID)) - return r.getDB(ctx).WithContext(ctx).Save(&entity).Error -} -``` - -**Update - 之后:** -```go -func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error { - r.GetLogger().Info("更新实体", zap.String("id", entity.ID)) - return r.BaseRepositoryImpl.Update(ctx, &entity) -} -``` - -#### 批量操作 - -**CreateBatch:** -```go -func (r *GormExampleRepository) CreateBatch(ctx context.Context, entities []Entity) error { - r.GetLogger().Info("批量创建实体", zap.Int("count", len(entities))) - return r.BaseRepositoryImpl.CreateBatch(ctx, &entities) -} -``` - -**GetByIDs:** -```go -func (r *GormExampleRepository) GetByIDs(ctx context.Context, ids []string) ([]Entity, error) { - var entities []Entity - err := r.BaseRepositoryImpl.GetByIDs(ctx, ids, &entities) - return entities, err -} -``` - -**UpdateBatch:** -```go -func (r *GormExampleRepository) UpdateBatch(ctx context.Context, entities []Entity) error { - r.GetLogger().Info("批量更新实体", zap.Int("count", len(entities))) - return r.BaseRepositoryImpl.UpdateBatch(ctx, &entities) -} -``` - -**DeleteBatch:** -```go -func (r *GormExampleRepository) DeleteBatch(ctx context.Context, ids []string) error { - r.GetLogger().Info("批量删除实体", zap.Strings("ids", ids)) - return r.BaseRepositoryImpl.DeleteBatch(ctx, ids, &Entity{}) -} -``` - -### 步骤 5:实现 BaseRepository 接口方法 - -#### 基础操作 - -**Delete:** -```go -func (r *GormExampleRepository) Delete(ctx context.Context, id string) error { - r.GetLogger().Info("删除实体", zap.String("id", id)) - return r.BaseRepositoryImpl.Delete(ctx, id, &Entity{}) -} -``` - -**Exists:** -```go -func (r *GormExampleRepository) Exists(ctx context.Context, id string) (bool, error) { - return r.BaseRepositoryImpl.Exists(ctx, id, &Entity{}) -} -``` - -**Count:** -```go -func (r *GormExampleRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) { - // 如果需要自定义搜索逻辑 - if options.Search != "" { - return r.CountWhere(ctx, &Entity{}, "name LIKE ? OR description LIKE ?", - "%"+options.Search+"%", "%"+options.Search+"%") - } - return r.BaseRepositoryImpl.Count(ctx, &Entity{}, options) -} -``` - -**SoftDelete:** -```go -func (r *GormExampleRepository) SoftDelete(ctx context.Context, id string) error { - r.GetLogger().Info("软删除实体", zap.String("id", id)) - return r.BaseRepositoryImpl.SoftDelete(ctx, id, &Entity{}) -} -``` - -**Restore:** -```go -func (r *GormExampleRepository) Restore(ctx context.Context, id string) error { - r.GetLogger().Info("恢复实体", zap.String("id", id)) - return r.BaseRepositoryImpl.Restore(ctx, id, &Entity{}) -} -``` - -#### 列表查询 - -**List:** -```go -func (r *GormExampleRepository) List(ctx context.Context, options interfaces.ListOptions) ([]Entity, error) { - var entities []Entity - - // 如果需要自定义搜索逻辑 - if options.Search != "" { - query := r.GetDB(ctx).Model(&Entity{}) - - // 应用筛选条件 - if options.Filters != nil { - for key, value := range options.Filters { - query = query.Where(key+" = ?", value) - } - } - - // 自定义搜索逻辑 - query = query.Where("name LIKE ? OR description LIKE ?", - "%"+options.Search+"%", "%"+options.Search+"%") - - // 应用预加载 - for _, include := range options.Include { - query = query.Preload(include) - } - - // 应用排序 - if options.Sort != "" { - order := "ASC" - if options.Order == "desc" || options.Order == "DESC" { - order = "DESC" - } - query = query.Order(options.Sort + " " + order) - } else { - query = query.Order("created_at DESC") - } - - // 应用分页 - if options.Page > 0 && options.PageSize > 0 { - offset := (options.Page - 1) * options.PageSize - query = query.Offset(offset).Limit(options.PageSize) - } - - return entities, query.Find(&entities).Error - } - - // 使用基础实现 - err := r.BaseRepositoryImpl.List(ctx, &Entity{}, &entities, options) - return entities, err -} -``` - -### 步骤 6:业务方法使用辅助方法 - -**使用 FindOne:** -```go -func (r *GormExampleRepository) GetByCode(ctx context.Context, code string) (*Entity, error) { - var entity Entity - err := r.FindOne(ctx, &entity, "code = ?", code) - if err != nil { - return nil, err - } - return &entity, nil -} -``` - -**使用 FindWhere:** -```go -func (r *GormExampleRepository) GetByStatus(ctx context.Context, status string) ([]Entity, error) { - var entities []Entity - err := r.FindWhere(ctx, &entities, "status = ?", status) - return entities, err -} -``` - -**使用 CountWhere:** -```go -func (r *GormExampleRepository) CountByStatus(ctx context.Context, status string) (int64, error) { - return r.CountWhere(ctx, &Entity{}, "status = ?", status) -} -``` - -**使用 ExistsWhere:** -```go -func (r *GormExampleRepository) ExistsByCode(ctx context.Context, code string) (bool, error) { - return r.ExistsWhere(ctx, &Entity{}, "code = ?", code) -} -``` - -### 步骤 7:复杂查询仍使用 GetDB - -对于复杂查询,继续使用 `r.GetDB(ctx)` 来获取数据库连接: - -```go -func (r *GormExampleRepository) ComplexQuery(ctx context.Context, params QueryParams) ([]Entity, error) { - var entities []Entity - query := r.GetDB(ctx).Model(&Entity{}) - - // 添加复杂的查询逻辑 - if params.Status != "" { - query = query.Where("status = ?", params.Status) - } - - if params.DateRange != nil { - query = query.Where("created_at BETWEEN ? AND ?", params.DateRange.Start, params.DateRange.End) - } - - // 连接查询 - query = query.Joins("LEFT JOIN related_table ON entities.related_id = related_table.id"). - Where("related_table.active = ?", true) - - return entities, query.Find(&entities).Error -} -``` - -## BaseRepositoryImpl 提供的方法 - -### 核心方法 -- `GetDB(ctx)` - 获取数据库连接(自动支持事务) -- `GetLogger()` - 获取日志记录器 -- `WithTx(tx)` - 创建事务版本的Repository - -### 基础 CRUD -- `Create(ctx, entity)` - 创建实体 -- `GetByID(ctx, id, entity)` - 根据ID获取 -- `Update(ctx, entity)` - 更新实体 -- `Delete(ctx, id, entity)` - 删除实体 -- `Exists(ctx, id, entity)` - 检查存在 - -### 批量操作 -- `CreateBatch(ctx, entities)` - 批量创建 -- `GetByIDs(ctx, ids, entities)` - 批量获取 -- `UpdateBatch(ctx, entities)` - 批量更新 -- `DeleteBatch(ctx, ids, entity)` - 批量删除 - -### 查询方法 -- `List(ctx, entity, entities, options)` - 列表查询 -- `Count(ctx, entity, options)` - 计数查询 -- `FindWhere(ctx, entities, condition, args...)` - 条件查询 -- `FindOne(ctx, entity, condition, args...)` - 单个查询 -- `CountWhere(ctx, entity, condition, args...)` - 条件计数 -- `ExistsWhere(ctx, entity, condition, args...)` - 条件存在 - -### 软删除 -- `SoftDelete(ctx, id, entity)` - 软删除 -- `Restore(ctx, id, entity)` - 恢复 - -### 事务辅助 -- `ExecuteInTransaction(ctx, fn)` - 执行事务 -- `IsInTransaction(ctx)` - 检查事务状态 - -## 优势 - -1. **统一事务处理**:所有Repository自动支持事务 -2. **减少代码重复**:移除重复的getDB方法和基础CRUD实现 -3. **提高可维护性**:统一的事务逻辑在一个地方维护 -4. **类型安全**:编译时检查,减少运行时错误 -5. **更清晰的职责**:Repository专注于业务逻辑,基础功能由BaseRepository提供 -6. **完整的接口支持**:自动实现Repository[T]和BaseRepository的所有方法 - -## 迁移检查清单 - -- [ ] 修改结构体定义,嵌入 BaseRepositoryImpl -- [ ] 更新构造函数 -- [ ] 删除 getDB 方法 -- [ ] 实现 Repository[T] 接口的所有方法 -- [ ] 实现 BaseRepository 接口的所有方法 -- [ ] 使用辅助方法替换重复的查询逻辑 -- [ ] 为特殊需求重写搜索逻辑 -- [ ] 运行测试确保功能正常 -- [ ] 更新相关文档 - -## 注意事项 - -1. **方法参数**:BaseRepositoryImpl的方法使用`interface{}`,需要传递指针 -2. **搜索逻辑**:默认搜索逻辑可能不适合所有实体,需要重写 -3. **预加载**:使用ListOptions的Include字段或直接调用GetDB() -4. **事务支持**:所有方法自动支持事务,无需额外处理 -5. **日志记录**:使用GetLogger()而不是直接访问logger字段 \ No newline at end of file diff --git a/scripts/missing_cost_price.csv b/scripts/missing_cost_price.csv deleted file mode 100644 index 46b9fc1..0000000 --- a/scripts/missing_cost_price.csv +++ /dev/null @@ -1,23 +0,0 @@ -产品编号,产品名称,成本价 -COMB298Y,人事背调组合包, -COMB86PM,海之源科技定制组合包, -COMB92KS,小微企业哈密定制, -COMBHZY2,海之源定制大数据组合报告, -COMBQN10,全能个人大数据报告(标准版), -COMBQN11,全能婚恋风险报告(标准版), -COMBQN12,全能入职背调报告(标准版), -COMBQN13,全能小微企业报告(标准版), -COMBQN14,全能家政风险报告(标准版), -COMBQN15,全能消金报告(标准版), -COMBTY11,天远个人风险报告(专业版), -COMBTY12,天远婚恋风险报告(专业版), -COMBTY13,天远入职背调报告(专业版), -COMBTY14,天远老板企业报告(专业版), -COMBTY15,天远家政风险报告(专业版), -COMBTY16,天远贷前风险报告(专业版), -COMENT01,企业风险报告(专业版), -IVYZ4E8B,单人婚姻状态C, -JRZQ8A2D,特殊名单验证B, -QYGL23T7,企业法人四要素高级版, -QYGL3F8E,人企关系加强版, -YYSY9E4A,手机号码归属地, diff --git a/scripts/set_timezone.sql b/scripts/set_timezone.sql deleted file mode 100644 index be76411..0000000 --- a/scripts/set_timezone.sql +++ /dev/null @@ -1,13 +0,0 @@ --- 设置时区为北京时间 -ALTER SYSTEM SET timezone = 'Asia/Shanghai'; - -ALTER SYSTEM SET log_timezone = 'Asia/Shanghai'; - --- 重新加载配置 -SELECT pg_reload_conf (); - --- 验证时区设置 -SELECT name, setting -FROM pg_settings -WHERE - name IN ('timezone', 'log_timezone'); \ No newline at end of file diff --git a/scripts/test_cache.bat b/scripts/test_cache.bat deleted file mode 100644 index da1855c..0000000 --- a/scripts/test_cache.bat +++ /dev/null @@ -1,67 +0,0 @@ -@echo off -REM 缓存测试脚本 (Windows版本) -REM 使用方法: scripts\test_cache.bat [base_url] -REM 默认base_url: http://localhost:8080 - -set BASE_URL=%1 -if "%BASE_URL%"=="" set BASE_URL=http://localhost:8080 -set API_BASE=%BASE_URL%/api/cache-test - -echo 🧪 开始缓存系统测试... -echo 📍 测试地址: %BASE_URL% -echo. - -REM 测试函数 -:test_endpoint -set method=%1 -set endpoint=%2 -set description=%3 - -echo 🔍 测试 %description%... - -if "%method%"=="GET" ( - curl -s "%API_BASE%%endpoint%" -) else if "%method%"=="POST" ( - curl -s -X POST "%API_BASE%%endpoint%" -) else if "%method%"=="DELETE" ( - curl -s -X DELETE "%API_BASE%%endpoint%" -) - -if %errorlevel% equ 0 ( - echo ✅ 成功 -) else ( - echo ❌ 失败 -) -echo. -goto :eof - -REM 1. 测试缓存统计 -call :test_endpoint GET /stats "缓存统计" - -REM 2. 测试基础缓存操作 -call :test_endpoint GET /test "基础缓存操作" - -REM 3. 测试缓存键查询 -call :test_endpoint GET /keys/gorm_cache:* "缓存键查询" - -REM 4. 测试性能测试 -call :test_endpoint POST /performance "缓存性能测试" - -REM 5. 测试表缓存调试 -call :test_endpoint GET /table/users "用户表缓存调试" - -echo 🎉 缓存测试完成! -echo. -echo 📋 测试结果说明: -echo - 如果所有测试都返回200状态码,说明缓存系统正常工作 -echo - 如果某些测试失败,请检查应用是否正在运行 -echo - 查看应用日志获取更详细的调试信息 -echo. -echo 🔧 手动测试命令: -echo curl %API_BASE%/stats -echo curl %API_BASE%/test -echo curl %API_BASE%/keys/gorm_cache:* -echo curl -X POST %API_BASE%/performance -echo curl %API_BASE%/table/users - -pause \ No newline at end of file diff --git a/scripts/test_cache.sh b/scripts/test_cache.sh deleted file mode 100644 index 36d9b05..0000000 --- a/scripts/test_cache.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# 缓存测试脚本 -# 使用方法: ./scripts/test_cache.sh [base_url] -# 默认base_url: http://localhost:8080 - -BASE_URL=${1:-http://localhost:8080} -API_BASE="$BASE_URL/api/cache-test" - -echo "🧪 开始缓存系统测试..." -echo "📍 测试地址: $BASE_URL" -echo "" - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 测试函数 -test_endpoint() { - local method=$1 - local endpoint=$2 - local description=$3 - - echo -n "🔍 测试 $description... " - - if [ "$method" = "GET" ]; then - response=$(curl -s -w "%{http_code}" "$API_BASE$endpoint") - elif [ "$method" = "POST" ]; then - response=$(curl -s -w "%{http_code}" -X POST "$API_BASE$endpoint") - elif [ "$method" = "DELETE" ]; then - response=$(curl -s -w "%{http_code}" -X DELETE "$API_BASE$endpoint") - fi - - # 提取HTTP状态码 - http_code="${response: -3}" - # 提取响应体 - body="${response%???}" - - if [ "$http_code" = "200" ]; then - echo -e "${GREEN}✅ 成功${NC}" - echo " 响应: $body" | head -c 100 - echo "..." - else - echo -e "${RED}❌ 失败 (HTTP $http_code)${NC}" - echo " 错误: $body" - fi - echo "" -} - -# 1. 测试缓存统计 -test_endpoint "GET" "/stats" "缓存统计" - -# 2. 测试基础缓存操作 -test_endpoint "GET" "/test" "基础缓存操作" - -# 3. 测试缓存键查询 -test_endpoint "GET" "/keys/gorm_cache:*" "缓存键查询" - -# 4. 测试性能测试 -test_endpoint "POST" "/performance" "缓存性能测试" - -# 5. 测试表缓存调试 -test_endpoint "GET" "/table/users" "用户表缓存调试" - -echo "🎉 缓存测试完成!" -echo "" -echo "📋 测试结果说明:" -echo " - 如果所有测试都返回200状态码,说明缓存系统正常工作" -echo " - 如果某些测试失败,请检查应用是否正在运行" -echo " - 查看应用日志获取更详细的调试信息" -echo "" -echo "🔧 手动测试命令:" -echo " curl $API_BASE/stats" -echo " curl $API_BASE/test" -echo " curl $API_BASE/keys/gorm_cache:*" -echo " curl -X POST $API_BASE/performance" -echo " curl $API_BASE/table/users" \ No newline at end of file diff --git a/scripts/update_cost_price.go b/scripts/update_cost_price.go deleted file mode 100644 index e545e12..0000000 --- a/scripts/update_cost_price.go +++ /dev/null @@ -1,160 +0,0 @@ -package main - -import ( - "context" - "encoding/csv" - "fmt" - "os" - "strconv" - "strings" - - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "gorm.io/gorm/schema" -) - -func main() { - // 连接数据库 - db, err := connectDB() - if err != nil { - fmt.Fprintf(os.Stderr, "连接数据库失败: %v\n", err) - os.Exit(1) - } - - // 读取 CSV 文件 - csvFile, err := os.Open("成本价.csv") - if err != nil { - fmt.Fprintf(os.Stderr, "打开 CSV 文件失败: %v\n", err) - os.Exit(1) - } - defer csvFile.Close() - - reader := csv.NewReader(csvFile) - reader.LazyQuotes = true - reader.TrimLeadingSpace = true - - // 读取所有记录 - records, err := reader.ReadAll() - if err != nil { - fmt.Fprintf(os.Stderr, "读取 CSV 文件失败: %v\n", err) - os.Exit(1) - } - - if len(records) < 2 { - fmt.Fprintf(os.Stderr, "CSV 文件数据不足(需要至少包含表头和数据行)\n") - os.Exit(1) - } - - ctx := context.Background() - successCount := 0 - failCount := 0 - skipCount := 0 - - fmt.Printf("开始更新成本价...\n") - fmt.Printf("共 %d 条记录(包含表头)\n\n", len(records)) - - // 从第二行开始处理(跳过表头) - for i := 1; i < len(records); i++ { - record := records[i] - if len(record) < 7 { - fmt.Printf("第 %d 行数据列数不足,跳过\n", i+1) - skipCount++ - continue - } - - productCode := strings.TrimSpace(record[0]) - costPriceStr := strings.TrimSpace(record[6]) // 成本价在第7列(索引6) - - // 跳过产品编号为空的行 - if productCode == "" { - fmt.Printf("第 %d 行产品编号为空,跳过\n", i+1) - skipCount++ - continue - } - - // 如果成本价为空,跳过(不更新) - if costPriceStr == "" { - fmt.Printf("产品 %s: 成本价为空,跳过\n", productCode) - skipCount++ - continue - } - - // 解析成本价为浮点数 - costPrice, err := strconv.ParseFloat(costPriceStr, 64) - if err != nil { - fmt.Printf("产品 %s: 成本价格式错误 (%s),跳过: %v\n", productCode, costPriceStr, err) - skipCount++ - continue - } - - // 更新数据库 - result := db.WithContext(ctx). - Table("product"). - Where("code = ? AND deleted_at IS NULL", productCode). - Update("cost_price", costPrice) - - if result.Error != nil { - // 如果单数表名失败,尝试复数表名 - if strings.Contains(result.Error.Error(), "does not exist") { - result = db.WithContext(ctx). - Table("products"). - Where("code = ? AND deleted_at IS NULL", productCode). - Update("cost_price", costPrice) - } - - if result.Error != nil { - fmt.Printf("产品 %s: 更新失败 - %v\n", productCode, result.Error) - failCount++ - continue - } - } - - if result.RowsAffected == 0 { - fmt.Printf("产品 %s: 未找到匹配的记录\n", productCode) - failCount++ - } else { - fmt.Printf("产品 %s: 成功更新成本价为 %.2f (影响 %d 行)\n", productCode, costPrice, result.RowsAffected) - successCount++ - } - } - - fmt.Printf("\n=== 更新完成 ===\n") - fmt.Printf("成功更新: %d 条\n", successCount) - fmt.Printf("更新失败: %d 条\n", failCount) - fmt.Printf("跳过记录: %d 条\n", skipCount) - fmt.Printf("总计处理: %d 条\n", len(records)-1) -} - -// connectDB 连接数据库 -func connectDB() (*gorm.DB, error) { - // 数据库连接配置 - dsn := "host=1.117.67.95 user=tyapi_user password=Pg9mX4kL8nW2rT5y dbname=tyapi port=25010 sslmode=disable TimeZone=Asia/Shanghai" - - // 配置GORM,使用单数表名(与项目配置一致) - gormConfig := &gorm.Config{ - NamingStrategy: schema.NamingStrategy{ - SingularTable: true, // 使用单数表名 - }, - Logger: logger.Default.LogMode(logger.Info), // 显示 SQL 日志 - } - - db, err := gorm.Open(postgres.Open(dsn), gormConfig) - if err != nil { - return nil, fmt.Errorf("连接数据库失败: %w", err) - } - - // 测试连接 - sqlDB, err := db.DB() - if err != nil { - return nil, fmt.Errorf("获取数据库实例失败: %w", err) - } - - if err := sqlDB.Ping(); err != nil { - return nil, fmt.Errorf("数据库连接测试失败: %w", err) - } - - fmt.Println("数据库连接成功") - return db, nil -} - diff --git a/scripts/成本价.csv b/scripts/成本价.csv deleted file mode 100644 index 4bd811d..0000000 --- a/scripts/成本价.csv +++ /dev/null @@ -1,98 +0,0 @@ -产品编号,产品名称,分类,价格,数据源,数据源编号,成本价 -DWBG6A2C,司南报告服务,多维报告,10.8,安徽智查,ZCI102,2.4 -DWBG8B4D,谛听多维报告,多维报告,10.8,安徽智查,ZCI103,2.1 -FLXG2E8F,司法核验报告,风险管控,5,安徽智查,ZCI101,1.2 -FLXG5A3B,个人司法涉诉B,风险管控,2.2,安徽智查,ZCI006,0.42 -FLXG8B4D,涉赌涉诈风险评估,风险管控,1.8,安徽智查,ZCI027,0.3 -FLXG9C1D,法院信息详情高级版,风险管控,1.5,安徽智查,ZCI007,0.23 -FLXGDEA8,公安不良人员名单,风险管控,2,安徽智查,ZCI028,0.45 -FLXGDEA9,公安不良人员名单(加强版),风险管控,2.5,安徽智查,ZCI005,0.45 -IVYZ2A8B,身份二要素认证,身份验证,0.25,安徽智查,ZCI001,0.05 -IVYZ5E3F,单人婚姻状态B,身份验证,2.5,安徽智查,ZCI029,0.55 -IVYZ7C9D,人脸识别验证,身份验证,0.3,安徽智查,ZCI013,0.3 -IVYZ7F3A,学历信息查询B,身份验证,4.5,安徽智查,ZCI035,2.3 -JRZQ09J8,收入评估(社保评级),金融验证,3.5,安徽智查,ZCI031,0.83 -JRZQ1D09,3C租赁申请意向,金融验证,3,安徽智查,ZCI020,0.23 -JRZQ3C7B,借贷意向验证B,金融验证,2.5,安徽智查,ZCI017,0.33 -JRZQ4B6C,探针C风险评估,金融验证,2,安徽智查,ZCI023,0.5 -JRZQ5E9F,借选指数评估,金融验证,3,安徽智查,ZCI021,0.38 -JRZQ7F1A,全景雷达,金融验证,3.5,安徽智查,ZCI008,0.6 -JRZQ8A2D,特殊名单验证B,金融验证,2,安徽智查,ZCI018,0 -QCXG9P1C,名下车辆详版,汽车相关,3.8,安徽智查,ZCI051,1.6 -YYSY3E7F,空号检测服务,运营商验证,0.2,安徽智查,ZCI010,0.055 -YYSY4F2E,运营商三要素验证(详版),运营商验证,0.35,安徽智查,ZCI002,0.16 -YYSY6D9A,全网手机号状态验证,运营商验证,0.6,安徽智查,ZCI030,0.035 -YYSY8B1C,手机在网时长B,运营商验证,0.3,安徽智查,ZCI003,0.1 -YYSY9E4A,手机号码归属地,运营商验证,0.3,安徽智查,ZCI026,0 -FLXG0687,反赌反诈,风险管控,1.8,羽山数据,RIS031,0.3 -FLXGBC21,手机号码特别风险,风险管控,2,羽山数据,MOB032,0.1 -QCXG7A2B,名下车辆,汽车相关,2,羽山数据,CAR061,1.6 -FLXG0V3B,个人不良核验(标准版),风险管控,3,西部数据,G34BJ03,0.8 -FLXG0V4B,个人司法涉诉,风险管控,2.5,西部数据,G22SC01,0.5 -FLXG162A,团伙欺诈评估,风险管控,2.5,西部数据,G32BJ05,0.7 -FLXG3D56,特殊名单验证,金融验证,2.5,西部数据,G26BJ05,0.2 -FLXG54F5,手机号码风险,风险管控,3,西部数据,G03HZ01,0.55 -FLXG5876,易诉人识别,风险管控,2,西部数据,G03XM02,0.6 -FLXG5B2E,自然人限高信息,风险管控,2,西部数据,G36SC01,0.5 -FLXG75FE,涉网风险,风险管控,2,西部数据,FLXG75FE,0.4 -FLXG8A3F,自然人失信信息,风险管控,2,西部数据,G37SC01,0.5 -FLXG9687,电诈风险预警,风险管控,1,西部数据,G31BJ05,0.4 -FLXG970F,风险人员核验,风险管控,2,西部数据,WEST00028,0.35 -FLXGC9D1,黑灰产等级,风险管控,3,西部数据,G30BJ05,0.2 -FLXGCA3D,个人综合涉诉,风险管控,2.5,西部数据,G22BJ03,0.5 -FLXGDEC7,个人不良核验,风险管控,3,西部数据,G23BJ03,0.8 -IVYZ0B03,二要素验证(姓名、手机号),身份验证,0.3,西部数据,G17BJ02,0.29 -IVYZ1C9D,,身份验证,,西部数据,G38SC02, -IVYZ2125,活体+人像核验组件,身份验证,0.3,西部数据,IVYZ2125,0.3 -IVYZ385E,自然人生存状态标识,身份验证,1.5,西部数据,WEST00020,0.3 -IVYZ4E8B,单人婚姻状态C,身份验证,2.5,西部数据,G09GZ02, -IVYZ5733,单人婚姻状态A,身份验证,2.5,西部数据,G09GZ02,1 -IVYZ7F2A,双人婚姻状态B,身份验证,2.5,西部数据,G10GZ02,0.6 -IVYZ81NC,单人婚姻查询(登记时间版),身份验证,4.5,西部数据,G09XM02,1 -IVYZ9363,双人婚姻状态A,身份验证,2.5,西部数据,G10XM02,1 -IVYZ9A2B,学历信息查询A,身份验证,5,西部数据,G11BJ06,3 -IVYZADEE,身份证三要素比对,身份验证,0.3,西部数据,IVYZADEE,0.2 -IVYZGZ08,,身份验证,,西部数据,G08SC02, -JRZQ0A03,借贷意向验证,金融验证,2.5,西部数据,G27BJ05,0.6 -JRZQ4AA8,偿债压力指数,金融验证,3,西部数据,G29BJ05,0.6 -JRZQ8203,借贷行为验证,金融验证,3,西部数据,G28BJ05,1 -JRZQDCBE,银行卡四要素验证,金融验证,0.4,西部数据,G20GZ01,0.3 -QYGL2ACD,企业三要素核验,企业相关,0.2,西部数据,WEST00022,0.1 -QYGL45BD,企业法人四要素核验,企业相关,0.3,西部数据,WEST00021,0.25 -QYGL6F2D,人企关联,企业相关,3,西部数据,G05XM02,0.9 -QYGL8261,企业综合涉诉,企业相关,2.5,西部数据,Q03BJ03,0.5 -QYGL8271,企业司法涉诉(详版),企业相关,2.5,西部数据,Q03SC01,0.5 -QYGLB4C0,股东人企关系精准版,企业相关,3,西部数据,G05HZ01,0.6 -YYSY09CD,运营商三要素验证(简版),运营商验证,0.3,西部数据,G16BJ02,0.3 -YYSY4B21,手机在网状态,运营商验证,0.5,西部数据,G25BJ02,0.055 -YYSY4B37,手机在网时长A,运营商验证,0.3,西部数据,G02BJ02,0.2 -YYSY6F2E,运营商三要素核验(高级版),运营商验证,0.4,西部数据,G15BJ02,0.35 -YYSYD50F,二要素核验(手机号、身份证号),运营商验证,0.35,西部数据,G18BJ02,0.29 -YYSYF7DB,手机二次卡,运营商验证,0.3,西部数据,G19BJ02,0.2 -DWBG7F3A,多头借贷行业风险版,金融验证,2.5,四川星维,CDJ-1101695406546284544,0.45 -FLXG7E8F,个人司法涉诉查询,风险管控,2,四川星维,CDJ-1101695378264092672,0.36 -IVYZ3A7F,学历信息查询(学校名称版),身份验证,5,四川星维,CDJ-1104648854749245440,3 -IVYZ6G7H,单人婚姻状态(补证版),身份验证,3.5,四川星维,CDJ-1104646268587536384,0.7 -IVYZ8I9J,互联网行为推测,身份验证,1.8,四川星维,CDJ-1074522823015198720,0.6 -IVYZ9D2E,,身份验证,,四川星维,CDJ-1104648845446279168,2.2 -JRZQ0L85,个人信用分,金融验证,1.5,四川星维,CDJ-1101695364016041984,0.38 -JRZQ6F2A,借贷意向验证A,金融验证,2,四川星维,CDJ-1101695369065984000,0.25 -JRZQ8B3C,个人消费能力等级,金融验证,3,四川星维,CDJ-1101695392528920576,0.34 -JRZQ9D4E,多头借贷小时级,金融验证,2.5,四川星维,CDJ-1118085532960616448,0.4 -JRZQ9E2A,多头借贷风险信息查询,金融验证,3,四川星维,CDJ-1068350101688086528,0.6 -QYGL5F6A,名下企业关联,企业相关,2.8,四川星维,CDJ-1101695397213958144,0.44 -YYSY7D3E,携号转网查询,运营商验证,0.3,四川星维,CDJ-1100244706893164544,0.02 -YYSY8C2D,运营商三要素(新详版),运营商验证,0.35,四川星维,CDJ-1100244702166183936,0.19 -YYSY8F3A,,运营商验证,,四川星维,CDJ-1100244697766359040,0.14 -YYSY9A1B,运营商三要素验证(简版),运营商验证,0.3,四川星维,CDJ-1100244697766359040,0.14 -QYGL4B2E,,企业相关,,天眼查,TaxContravention, -QYGL5A3C,对外投资历史,企业相关,0.5,天眼查,InvestHistory,0.1 -QYGL7C1A,经营异常,企业相关,0.5,天眼查,AbnormalInfo,0.15 -QYGL7D9A,,企业相关,,天眼查,OwnTax, -QYGL8B4D,融资历史,企业相关,0.5,天眼查,FinancingHistory,0.1 -QYGL9E2F,行政处罚,企业相关,0.5,天眼查,PunishmentInfo,0.15 -QYGL23T7,企业法人四要素高级版,企业相关,0.3,阿里云,check, -YYSYBE08,二要素核验(姓名、身份证号),运营商验证,0.25,阿里云,check,0.03 -IVYZ3P9M,学历信息查询(实时版),身份验证,5,木子数据,PC0041,1.72 -COMENT01,企业风险报告(专业版),组合包,30,内部处理,, -QYGL3F8E,人企关系加强版,企业相关,10.8,内部处理,, diff --git a/worker b/worker new file mode 100644 index 0000000..851a8a2 Binary files /dev/null and b/worker differ