This commit is contained in:
2026-02-12 15:16:54 +08:00
parent 07bf234b30
commit ca6dcc1b24
21 changed files with 2594 additions and 111 deletions

View File

@@ -0,0 +1,164 @@
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
}