Files
tyc-server-v2/app/main/api/internal/logic/tianyuan/vehiclecallbacklogic.go
2026-02-12 15:16:54 +08:00

165 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
)
}
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取回调 Body 失败: %v", err)
}
// 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(bodyBytes)
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(bodyBytes),
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
}