add fix id_car

This commit is contained in:
2025-12-24 17:50:33 +08:00
parent 311d7a9b01
commit 8556e7331d
6 changed files with 166 additions and 56 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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):

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}