package tianyuan import ( "context" "encoding/hex" "encoding/json" "io" "net/http" "time" "tyc-server/app/main/api/internal/service" "tyc-server/app/main/api/internal/svc" "tyc-server/app/main/model" "tyc-server/common/xerr" "tyc-server/pkg/lzkit/crypto" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" ) // VehicleCallbackLogic 处理天远车辆类接口的异步回调 // 设计目标: // - 按 order_no + api_id 找到对应的查询记录 // - 解密原有 query_data([]APIResponseData),更新或追加当前 api_id 的结果 // - 再次加密写回 query.query_data,必要时将 query_state 从 pending 更新为 success type VehicleCallbackLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewVehicleCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VehicleCallbackLogic { return &VehicleCallbackLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // 与 paySuccessNotify 中一致:仅通过异步回调回写结果的车辆接口 var asyncVehicleApiIDs = map[string]bool{ "QCXG1U4U": true, "QCXG3Y6B": true, "QCXG3Z3L": true, "QCXGP00W": true, } // allAsyncVehicleReceived 判断 apiList 中所有“异步车辆接口”是否均已收到回调(Success 为 true) func allAsyncVehicleReceived(apiList []service.APIResponseData) bool { for _, item := range apiList { if asyncVehicleApiIDs[item.ApiID] && !item.Success { return false } } return true } // Handle 入口:直接接收原始 *http.Request,方便读取 query / body func (l *VehicleCallbackLogic) Handle(r *http.Request) error { apiID := r.URL.Query().Get("api_id") orderNo := r.URL.Query().Get("order_no") if apiID == "" || orderNo == "" { return errors.Wrapf( xerr.NewErrMsg("缺少 api_id 或 order_no"), "tianyuan vehicle callback, api_id=%s, order_no=%s", apiID, orderNo, ) } l.Infof("tianyuan vehicle callback start, api_id=%s, order_no=%s", apiID, orderNo) bodyBytes, err := io.ReadAll(r.Body) if err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取回调 Body 失败: %v", err) } l.Infof("tianyuan vehicle callback body received, api_id=%s, order_no=%s, body_len=%d", apiID, orderNo, len(bodyBytes)) // 回调包裹格式通常为 { code:0, msg:\"成功\", order_id:\"...\", data:{...} }, // 这里只把 data 字段提取出来存到 QueryData 里,便于前端直接渲染。 type callbackWrapper struct { Code json.RawMessage `json:"code"` Msg json.RawMessage `json:"msg"` OrderID json.RawMessage `json:"order_id"` Data json.RawMessage `json:"data"` Extra map[string]any `json:"-"` // 预留 } var wrapper callbackWrapper payload := bodyBytes if len(bodyBytes) > 0 { if err := json.Unmarshal(bodyBytes, &wrapper); err != nil { l.Errorf("tianyuan vehicle callback unmarshal body failed, api_id=%s, order_no=%s, err=%v", apiID, orderNo, err) } else if len(wrapper.Data) > 0 { payload = wrapper.Data l.Infof("tianyuan vehicle callback extracted data field, api_id=%s, order_no=%s, data_len=%d", apiID, orderNo, len(payload)) } } // 1. 根据订单号找到订单 order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo) if err != nil { if err == model.ErrNotFound { return errors.Wrapf(xerr.NewErrMsg("未找到订单"), "order_no=%s", orderNo) } return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败, order_no=%s, err=%v", orderNo, err) } // 2. 根据订单ID找到对应查询记录 query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id) if err != nil { if err == model.ErrNotFound { return errors.Wrapf(xerr.NewErrMsg("未找到查询记录"), "order_no=%s, order_id=%d", orderNo, order.Id) } return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 query 记录失败, order_id=%d, err=%v", order.Id, err) } // 3. 获取加密密钥 secretKey := l.svcCtx.Config.Encrypt.SecretKey key, decodeErr := hex.DecodeString(secretKey) if decodeErr != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 AES 密钥失败: %v", decodeErr) } // 4. 解密 query_data,反序列化为 []APIResponseData var apiList []service.APIResponseData if query.QueryData.Valid && query.QueryData.String != "" { decrypted, decErr := crypto.AesDecrypt(query.QueryData.String, key) if decErr != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密 query_data 失败: %v", decErr) } if len(decrypted) > 0 { if err := json.Unmarshal(decrypted, &apiList); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 query_data 为 APIResponseData 列表失败: %v", err) } } } // 5. 更新或追加当前 api_id 的结果 nowStr := time.Now().Format("2006-01-02 15:04:05") updated := false for i := range apiList { if apiList[i].ApiID == apiID { apiList[i].Data = json.RawMessage(payload) apiList[i].Success = true apiList[i].Timestamp = nowStr apiList[i].Error = "" updated = true break } } if !updated { apiList = append(apiList, service.APIResponseData{ ApiID: apiID, Data: json.RawMessage(payload), Success: true, Timestamp: nowStr, }) } // 6. 重新序列化并加密,写回查询记录 merged, err := json.Marshal(apiList) if err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化合并后的 query_data 失败: %v", err) } enc, encErr := crypto.AesEncrypt(merged, key) if encErr != nil { return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密合并后的 query_data 失败: %v", encErr) } query.QueryData.String = enc query.QueryData.Valid = true // 仅当所有异步车辆接口均已有回调结果时,才将 pending 置为 success,并触发代理结算 wasPending := query.QueryState == "pending" didSetSuccess := wasPending && allAsyncVehicleReceived(apiList) if didSetSuccess { query.QueryState = "success" } if err := l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, query); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新查询记录失败, query_id=%d, err=%v", query.Id, err) } if didSetSuccess { if agentErr := l.svcCtx.AgentService.AgentProcess(l.ctx, order); agentErr != nil { l.Errorf("tianyuan vehicle callback, AgentProcess failed, order_no=%s, err=%v", orderNo, agentErr) // 不因代理处理失败而整体失败,回调已成功落库 } } l.Infof("tianyuan vehicle callback handled, order_no=%s, api_id=%s, query_id=%d", orderNo, apiID, query.Id) return nil }