add fix id_car
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -23,6 +23,10 @@ func ProcessQCXG4896Request(ctx context.Context, params []byte, deps *processors
|
||||
}
|
||||
|
||||
|
||||
paramSign := map[string]interface{}{
|
||||
"paramName": "licenseNo",
|
||||
"paramValue": paramsDto.PlateNo,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"paramName": "licenseNo",
|
||||
@@ -31,7 +35,7 @@ func ProcessQCXG4896Request(ctx context.Context, params []byte, deps *processors
|
||||
"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
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(), "购买未支付") {
|
||||
// 如果没有找到已支付订单,尝试创建一个(可能订单刚创建但状态未更新)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有待支付订单,尝试主动查询支付状态
|
||||
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": "无下载权限,请先完成购买",
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user