From 8556e7331d7ffdc7fb82c873584f2848324b04df Mon Sep 17 00:00:00 2001 From: 18278715334 <18278715334@163.com> Date: Wed, 24 Dec 2025 17:50:33 +0800 Subject: [PATCH] add fix id_car --- config.yaml | 2 +- .../product/component_report_order_service.go | 21 --- .../processors/ivyz/ivyz3p9m_processor.go | 8 +- .../processors/qcxg/qcxg4896_processor.go | 11 +- .../external/muzi/muzi_service.go | 22 ++- internal/shared/component_report/handler.go | 158 +++++++++++++++--- 6 files changed, 166 insertions(+), 56 deletions(-) diff --git a/config.yaml b/config.yaml index 2ad6133..8a54a41 100644 --- a/config.yaml +++ b/config.yaml @@ -437,7 +437,7 @@ zhicha: # 🌐 木子数据配置 # =========================================== muzi: - url: "https://carv.m0101.com/magic/carv/pubin/service/academic" + url: "https://carv.m0101.com/magic/carv/pubin/service" app_id: "713014138179585" app_secret: "bd4090ac652c404c80e90ebbdcd6ba1d" timeout: 60s diff --git a/internal/application/product/component_report_order_service.go b/internal/application/product/component_report_order_service.go index 855e470..74c8395 100644 --- a/internal/application/product/component_report_order_service.go +++ b/internal/application/product/component_report_order_service.go @@ -504,27 +504,7 @@ func (s *ComponentReportOrderService) CreatePaymentOrder(ctx context.Context, re zap.String("code_url", codeURL), ) } - - // 创建一个临时下载记录,用于跟踪支付状态,但不生成报告文件 - download := &productEntities.ComponentReportDownload{ - UserID: req.UserID, - ProductID: req.ProductID, - ProductCode: product.Code, - ProductName: product.Name, - OrderID: &createdPurchaseOrder.ID, // 关联购买订单ID - OrderNumber: &outTradeNo, // 外部订单号 - ExpiresAt: calculateExpiryTime(), // 30天后过期 - // 注意:这里不设置FilePath,因为文件将在支付成功后生成 - } - - err = s.componentReportRepo.Create(ctx, download) - if err != nil { - s.logger.Error("创建下载记录失败", zap.Error(err)) - // 不中断流程,即使创建下载记录失败也继续返回订单信息 - } - // 返回支付响应,包含支付URL - // 使用购买订单ID而不是下载记录ID,以便前端可以正确检查支付状态 response := &CreatePaymentOrderResponse{ OrderID: createdPurchaseOrder.ID, // 修改为购买订单ID OrderNo: createdPurchaseOrder.OrderNo, @@ -535,7 +515,6 @@ func (s *ComponentReportOrderService) CreatePaymentOrder(ctx context.Context, re } s.logger.Info("========== 支付订单创建完成 ==========", - zap.String("download_id", download.ID), zap.String("purchase_order_id", createdPurchaseOrder.ID), zap.String("order_no", createdPurchaseOrder.OrderNo), zap.String("user_id", req.UserID), diff --git a/internal/domains/api/services/processors/ivyz/ivyz3p9m_processor.go b/internal/domains/api/services/processors/ivyz/ivyz3p9m_processor.go index 25db4cf..6341304 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz3p9m_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz3p9m_processor.go @@ -36,6 +36,11 @@ func ProcessIVYZ3P9MRequest(ctx context.Context, params []byte, deps *processors if returnType == "" { returnType = "1" } + paramSign := map[string]interface{}{ + "returnType": returnType, + "realName": encryptedName, + "certCode": encryptedCertCode, + } reqData := map[string]interface{}{ "realName": encryptedName, @@ -43,7 +48,8 @@ func ProcessIVYZ3P9MRequest(ctx context.Context, params []byte, deps *processors "returnType": returnType, } - respData, err := deps.MuziService.CallAPI(ctx, "PC0041", reqData) + + respData, err := deps.MuziService.CallAPI(ctx, "PC0041", "/academic",reqData,paramSign) if err != nil { switch { case errors.Is(err, muzi.ErrDatasource): diff --git a/internal/domains/api/services/processors/qcxg/qcxg4896_processor.go b/internal/domains/api/services/processors/qcxg/qcxg4896_processor.go index 6b90be1..92189c2 100644 --- a/internal/domains/api/services/processors/qcxg/qcxg4896_processor.go +++ b/internal/domains/api/services/processors/qcxg/qcxg4896_processor.go @@ -21,17 +21,21 @@ func ProcessQCXG4896Request(ctx context.Context, params []byte, deps *processors if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } + - + paramSign := map[string]interface{}{ + "paramName": "licenseNo", + "paramValue": paramsDto.PlateNo, + } reqData := map[string]interface{}{ - "paramName": "licenseNo", + "paramName": "licenseNo", "paramValue": paramsDto.PlateNo, "startTime": strings.Split(paramsDto.AuthDate, "-")[0], "endTime": strings.Split(paramsDto.AuthDate, "-")[1], } - respData, err := deps.MuziService.CallAPI(ctx, "PC0031", reqData) + respData, err := deps.MuziService.CallAPI(ctx, "PC0031", "/hailingScoreBySearch", reqData,paramSign) if err != nil { switch { case errors.Is(err, muzi.ErrDatasource): @@ -44,4 +48,5 @@ func ProcessQCXG4896Request(ctx context.Context, params []byte, deps *processors } return respData, nil + } diff --git a/internal/infrastructure/external/muzi/muzi_service.go b/internal/infrastructure/external/muzi/muzi_service.go index fd6388f..59b34f1 100644 --- a/internal/infrastructure/external/muzi/muzi_service.go +++ b/internal/infrastructure/external/muzi/muzi_service.go @@ -15,6 +15,7 @@ import ( "reflect" "sort" "strconv" + "strings" "time" "tyapi-server/internal/shared/external_logger" @@ -90,13 +91,14 @@ func (m *MuziService) generateRequestID() string { } // CallAPI 调用木子数据接口 -func (m *MuziService) CallAPI(ctx context.Context, prodCode string, params map[string]interface{}) (json.RawMessage, error) { +func (m *MuziService) CallAPI(ctx context.Context, prodCode string, path string, params map[string]interface{},paramSign map[string]interface{}) (json.RawMessage, error) { requestID := m.generateRequestID() now := time.Now() timestamp := strconv.FormatInt(now.UnixMilli(), 10) flatParams := flattenParams(params) - signParts := collectSignatureValues(params) + + signParts := collectSignatureValues(paramSign) signature := m.GenerateSignature(prodCode, timestamp, signParts...) // 从上下文获取链路ID @@ -128,7 +130,21 @@ func (m *MuziService) CallAPI(ctx context.Context, prodCode string, params map[s return nil, err } - req, reqErr := http.NewRequestWithContext(ctx, http.MethodPost, m.config.URL, bytes.NewBuffer(bodyBytes)) + // 构建完整的URL,拼接路径参数 + fullURL := m.config.URL + if path != "" { + // 确保路径以/开头 + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + // 确保URL不以/结尾,避免双斜杠 + if strings.HasSuffix(fullURL, "/") { + fullURL = fullURL[:len(fullURL)-1] + } + fullURL += path + } + + req, reqErr := http.NewRequestWithContext(ctx, http.MethodPost, fullURL, bytes.NewBuffer(bodyBytes)) if reqErr != nil { err := errors.Join(ErrSystem, reqErr) if m.logger != nil { diff --git a/internal/shared/component_report/handler.go b/internal/shared/component_report/handler.go index 603c92c..587f6f8 100644 --- a/internal/shared/component_report/handler.go +++ b/internal/shared/component_report/handler.go @@ -300,49 +300,87 @@ func (h *ComponentReportHandler) GenerateAndDownloadZip(c *gin.Context) { return } - // 检查是否已存在有效的下载记录 - download, err := h.componentReportRepo.GetActiveDownload(c.Request.Context(), userID, req.ProductID) + // 直接检查用户是否有已支付的购买记录,而不是查询下载记录 + orders, _, err := h.purchaseOrderRepo.GetByUserID(c.Request.Context(), userID, 100, 0) if err != nil { - h.logger.Error("查询用户下载记录失败", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", req.ProductID)) + h.logger.Error("查询用户购买记录失败", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", req.ProductID)) c.JSON(http.StatusInternalServerError, gin.H{ "code": 500, - "message": "查询下载记录失败", + "message": "查询购买记录失败", }) return } - // 如果不存在有效的下载记录,尝试创建一个 - if download == nil { - download, err = h.createDownloadRecordIfEligible(c.Request.Context(), userID, req.ProductID) - if err != nil { - h.logger.Error("创建下载记录失败", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", req.ProductID)) + // 查找有效的已支付订单 + var validOrder *finance_entities.PurchaseOrder + for _, order := range orders { + if order.ProductID == req.ProductID && order.Status == finance_entities.PurchaseOrderStatusPaid { + validOrder = order + break + } + } - if strings.Contains(err.Error(), "无购买记录") || strings.Contains(err.Error(), "购买未支付") { - c.JSON(http.StatusForbidden, gin.H{ - "code": 403, - "message": "无下载权限,请先完成购买", - }) - } else { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "创建下载记录失败", - }) + // 如果没有找到已支付订单,尝试创建一个(可能订单刚创建但状态未更新) + if validOrder == nil { + // 查找该产品的待支付订单 + var pendingOrder *finance_entities.PurchaseOrder + for _, order := range orders { + if order.ProductID == req.ProductID && order.Status == finance_entities.PurchaseOrderStatusCreated { + pendingOrder = order + break } - return + } + + // 如果有待支付订单,尝试主动查询支付状态 + if pendingOrder != nil { + h.logger.Info("发现待支付订单,尝试主动查询支付状态", + zap.String("order_id", pendingOrder.ID), + zap.String("pay_channel", pendingOrder.PayChannel)) + + // 如果是支付宝订单,主动查询状态 + if pendingOrder.PayChannel == "alipay" && h.aliPayService != nil { + // 这里可以调用支付宝查询服务,但为了简化,我们只记录日志 + h.logger.Info("支付宝订单状态待查询,但当前实现简化处理", + zap.String("order_id", pendingOrder.ID)) + } + // 如果是微信订单,主动查询状态 + if pendingOrder.PayChannel == "wechat" && h.wechatPayService != nil { + // 这里可以调用微信查询服务,但为了简化,我们只记录日志 + h.logger.Info("微信订单状态待查询,但当前实现简化处理", + zap.String("order_id", pendingOrder.ID)) + } + } + + h.logger.Error("用户没有已支付的购买记录", zap.String("user_id", userID), zap.String("product_id", req.ProductID)) + c.JSON(http.StatusForbidden, gin.H{ + "code": 403, + "message": "无下载权限,请先完成购买", + }) + return + } + + // 创建下载记录(仅用于记录,不影响下载流程) + download, err := h.componentReportRepo.GetActiveDownload(c.Request.Context(), userID, req.ProductID) + if err != nil { + h.logger.Warn("查询现有下载记录失败,将创建新记录", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", req.ProductID)) + download = nil + } + + // 如果不存在有效的下载记录,创建一个 + if download == nil { + download, err = h.createDownloadRecordForPaidOrder(c.Request.Context(), validOrder) + if err != nil { + h.logger.Warn("创建下载记录失败,但继续处理下载请求", zap.Error(err), zap.String("order_id", validOrder.ID)) } } // 检查下载记录是否仍有效 - if !download.CanDownload() { - h.logger.Warn("下载记录已过期", + if download != nil && !download.CanDownload() { + h.logger.Warn("下载记录已过期,但基于已支付订单继续处理", zap.String("user_id", userID), zap.String("product_id", req.ProductID), ) - c.JSON(http.StatusForbidden, gin.H{ - "code": 403, - "message": "下载权限已过期,请重新购买", - }) - return + // 不再阻止下载,因为已确认用户有有效的已支付订单 } // 更新下载次数和最后下载时间 @@ -1513,3 +1551,69 @@ func calculateExpiryTime() *time.Time { expiry := now.AddDate(0, 0, 30) // 30天后过期 return &expiry } + +// createDownloadRecordForPaidOrder 为已支付订单创建下载记录 +func (h *ComponentReportHandler) createDownloadRecordForPaidOrder(ctx context.Context, order *finance_entities.PurchaseOrder) (*entities.ComponentReportDownload, error) { + // 获取产品信息 + product, err := h.productRepo.GetByID(ctx, order.ProductID) + if err != nil { + return nil, fmt.Errorf("获取产品信息失败: %w", err) + } + + // 创建下载记录 + download := &entities.ComponentReportDownload{ + UserID: order.UserID, + ProductID: order.ProductID, + ProductCode: order.ProductCode, + ProductName: order.ProductName, + OrderID: &order.ID, + OrderNumber: &order.OrderNo, + ExpiresAt: calculateExpiryTime(), + } + + // 如果是组合包,获取子产品信息 + if product.IsPackage { + packageItems, err := h.getSubProductsByProductID(ctx, order.ProductID) + if err == nil && len(packageItems) > 0 { + var subProductIDs []string + var subProductCodes []string + + // 获取子产品的详细信息 + for _, item := range packageItems { + if item.Product != nil { + subProductIDs = append(subProductIDs, item.Product.ID) + subProductCodes = append(subProductCodes, item.Product.Code) + } else { + // 如果关联的Product为nil,需要单独查询 + subProduct, err := h.productRepo.GetByID(ctx, item.ProductID) + if err != nil { + h.logger.Warn("获取子产品信息失败", zap.Error(err), zap.String("product_id", item.ProductID)) + continue + } + subProductIDs = append(subProductIDs, subProduct.ID) + subProductCodes = append(subProductCodes, subProduct.Code) + } + } + + subProductIDsJSON, _ := json.Marshal(subProductIDs) + subProductCodesJSON, _ := json.Marshal(subProductCodes) + download.SubProductIDs = string(subProductIDsJSON) + download.SubProductCodes = string(subProductCodesJSON) + } + } + + // 保存下载记录 + err = h.componentReportRepo.Create(ctx, download) + if err != nil { + return nil, fmt.Errorf("创建下载记录失败: %w", err) + } + + h.logger.Info("创建下载记录成功", + zap.String("user_id", order.UserID), + zap.String("product_id", order.ProductID), + zap.String("order_id", order.ID), + zap.String("download_id", download.ID), + ) + + return download, nil +}