168 lines
5.6 KiB
Go
168 lines
5.6 KiB
Go
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))
|
||
|
||
// 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
|
||
}
|