qnc-server-tob/app/main/api/internal/queue/paySuccessNotify.go

268 lines
9.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 queue
import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"qnc-server/app/main/api/internal/svc"
"qnc-server/app/main/api/internal/types"
"qnc-server/app/main/model"
"qnc-server/pkg/lzkit/crypto"
"qnc-server/pkg/lzkit/lzUtils"
"time"
"github.com/bytedance/sonic"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type PaySuccessNotifyUserHandler struct {
svcCtx *svc.ServiceContext
}
func NewPaySuccessNotifyUserHandler(svcCtx *svc.ServiceContext) *PaySuccessNotifyUserHandler {
return &PaySuccessNotifyUserHandler{
svcCtx: svcCtx,
}
}
var payload struct {
OrderID int64 `json:"order_id"`
}
func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
// 从任务的负载中解码数据
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
return fmt.Errorf("解析任务负载失败: %w", err)
}
order, err := l.svcCtx.OrderModel.FindOne(ctx, payload.OrderID)
if err != nil {
return fmt.Errorf("无效的订单ID: %d, %v", payload.OrderID, err)
}
env := os.Getenv("ENV")
if order.Status != "paid" && env != "development" {
err = fmt.Errorf("无效的订单: %d", payload.OrderID)
logx.Errorf("处理任务失败,原因: %v", err)
return asynq.SkipRetry
}
product, err := l.svcCtx.ProductModel.FindOne(ctx, order.ProductId)
if err != nil {
return fmt.Errorf("找不到相关产品: orderID: %d, productID: %d", payload.OrderID, order.ProductId)
}
redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo)
cache, cacheErr := l.svcCtx.Redis.GetCtx(ctx, redisKey)
if cacheErr != nil {
return fmt.Errorf("获取缓存内容失败: %+v", cacheErr)
}
var data types.QueryCacheLoad
err = sonic.Unmarshal([]byte(cache), &data)
if err != nil {
return fmt.Errorf("解析缓存内容失败: %+v", err)
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return fmt.Errorf("获取AES密钥失败: %+v", decodeErr)
}
decryptData, aesdecryptErr := crypto.AesDecrypt(data.Params, key)
if aesdecryptErr != nil {
return fmt.Errorf("解密参数失败: %+v", aesdecryptErr)
}
// 从数据库中查询完整的查询记录
query, err := l.svcCtx.QueryModel.FindOneByOrderId(ctx, order.Id)
if err != nil {
return fmt.Errorf("获取插入后的查询记录失败: %+v", err)
}
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
if err != nil {
return l.handleError(ctx, err, order, query)
}
// 加密返回响应
encryptData, aesEncryptErr := crypto.AesEncrypt(combinedResponse, key)
if aesEncryptErr != nil {
err = fmt.Errorf("加密响应信息失败: %v", aesEncryptErr)
return l.handleError(ctx, err, order, query)
}
query.QueryData = lzUtils.StringToNullString(encryptData)
updateErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
if updateErr != nil {
err = fmt.Errorf("保存响应数据失败: %v", updateErr)
return l.handleError(ctx, err, order, query)
}
query.QueryState = "success"
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
if updateQueryErr != nil {
updateQueryErr = fmt.Errorf("修改查询状态失败: %v", updateQueryErr)
return l.handleError(ctx, updateQueryErr, order, query)
}
err = l.svcCtx.AgentService.AgentProcess(ctx, order)
if err != nil {
return l.handleError(ctx, err, order, query)
}
_, delErr := l.svcCtx.Redis.DelCtx(ctx, redisKey)
if delErr != nil {
logx.Errorf("删除Redis缓存失败但任务已成功处理订单ID: %d, 错误: %v", order.Id, delErr)
}
return nil
}
// 定义一个中间件函数
func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error, order *model.Order, query *model.Query) error {
logx.Errorf("处理任务失败,原因: %v", err)
redisKey := fmt.Sprintf(types.QueryCacheKey, order.UserId, order.OrderNo)
_, delErr := l.svcCtx.Redis.DelCtx(ctx, redisKey)
if delErr != nil {
logx.Errorf("删除Redis缓存失败订单ID: %d, 错误: %v", order.Id, delErr)
}
if order.Status == "paid" && query.QueryState == "pending" {
// 更新查询状态为失败
query.QueryState = "failed"
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
if updateQueryErr != nil {
logx.Errorf("更新查询状态失败订单ID: %d, 错误: %v", order.Id, updateQueryErr)
return asynq.SkipRetry
}
// 发起退款并创建退款记录
refundErr := l.processRefund(ctx, order, "业务处理失败,自动退款")
if refundErr != nil {
logx.Errorf("退款处理失败订单ID: %d, 错误: %v", order.Id, refundErr)
return asynq.SkipRetry
}
}
return asynq.SkipRetry
}
// processRefund 处理退款逻辑
func (l *PaySuccessNotifyUserHandler) processRefund(ctx context.Context, order *model.Order, refundReason string) error {
refundNo := fmt.Sprintf("refund-%s", order.OrderNo)
if order.PaymentPlatform == "wechat" {
// 微信退款(异步)
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
if refundErr != nil {
// 微信退款调用失败,创建失败记录
createRefundErr := l.createRefundRecord(ctx, order, refundNo, "", model.OrderRefundStatusFailed, refundReason)
if createRefundErr != nil {
logx.Errorf("创建微信退款失败记录时出错: %v", createRefundErr)
}
return refundErr
}
// 微信退款调用成功创建pending记录并更新订单状态
return l.svcCtx.OrderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建pending状态的退款记录
if err := l.createRefundRecordWithSession(ctx, session, order, refundNo, "", model.OrderRefundStatusPending, refundReason); err != nil {
return fmt.Errorf("创建微信退款记录失败: %v", err)
}
// 更新订单状态为退款中
order.Status = model.OrderStatusRefunding
order.RefundTime = sql.NullTime{Time: time.Now(), Valid: true}
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
return fmt.Errorf("更新订单状态失败: %v", err)
}
return nil
})
} else if order.PaymentPlatform == "alipay" {
// 支付宝退款(同步)
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
if refundErr != nil {
// 支付宝退款调用失败,创建失败记录
createRefundErr := l.createRefundRecord(ctx, order, refundNo, "", model.OrderRefundStatusFailed, refundReason)
if createRefundErr != nil {
logx.Errorf("创建支付宝退款失败记录时出错: %v", createRefundErr)
}
return refundErr
}
if refund.IsSuccess() {
// 支付宝退款成功,创建成功记录并更新订单状态
return l.svcCtx.OrderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建成功状态的退款记录
if err := l.createRefundRecordWithSession(ctx, session, order, refundNo, refund.TradeNo, model.OrderRefundStatusSuccess, refundReason); err != nil {
return fmt.Errorf("创建支付宝退款成功记录失败: %v", err)
}
// 更新订单状态为已退款
order.Status = model.OrderStatusRefunded
order.RefundTime = sql.NullTime{Time: time.Now(), Valid: true}
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
return fmt.Errorf("更新订单状态失败: %v", err)
}
return nil
})
} else {
// 支付宝退款失败,创建失败记录
createRefundErr := l.createRefundRecord(ctx, order, refundNo, refund.TradeNo, model.OrderRefundStatusFailed, refundReason)
if createRefundErr != nil {
logx.Errorf("创建支付宝退款失败记录时出错: %v", createRefundErr)
}
return fmt.Errorf("支付宝退款失败: %s", refund.Msg)
}
} else {
return fmt.Errorf("不支持的支付平台: %s", order.PaymentPlatform)
}
}
// createRefundRecord 创建退款记录(无事务)
func (l *PaySuccessNotifyUserHandler) createRefundRecord(ctx context.Context, order *model.Order, refundNo, platformRefundId, status, reason string) error {
refund := &model.OrderRefund{
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: order.Amount,
RefundReason: l.createNullString(reason),
Status: status,
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
_, err := l.svcCtx.OrderRefundModel.Insert(ctx, nil, refund)
return err
}
// createRefundRecordWithSession 创建退款记录(带事务)
func (l *PaySuccessNotifyUserHandler) createRefundRecordWithSession(ctx context.Context, session sqlx.Session, order *model.Order, refundNo, platformRefundId, status, reason string) error {
refund := &model.OrderRefund{
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: order.Amount,
RefundReason: l.createNullString(reason),
Status: status,
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
_, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund)
return err
}
// createNullString 创建 sql.NullString
func (l *PaySuccessNotifyUserHandler) createNullString(value string) sql.NullString {
return sql.NullString{
String: value,
Valid: value != "",
}
}