补单逻辑
Build docker and publish / build (20.15.1) (push) Has been cancelled

This commit is contained in:
2026-05-06 05:42:48 -07:00
parent 54daa923da
commit 8bc8e81e95
3 changed files with 76 additions and 29 deletions
@@ -20,13 +20,17 @@ import (
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/uuidx"
"github.com/perfect-panel/server/pkg/xerr"
pkgerrors "github.com/pkg/errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
const genericRecoveryError = "order recovery information is invalid"
const (
recoverySuccessMessage = "恢复完成,请使用该邮箱验证码登录查看订阅。"
recoveryAlreadyDoneMessage = "该订单已恢复,请使用恢复时绑定的邮箱登录查看订阅。"
recoveryInvalidOrderMessage = "订单信息无效,请确认订单号是否正确。"
recoveryInvalidCodeMessage = "邮箱验证码错误或已过期,请重新获取验证码。"
recoverySystemErrorMessage = "恢复暂时失败,请稍后再试或联系客服处理。"
)
type RecoverOrderLogic struct {
logger.Logger
@@ -48,45 +52,51 @@ func (l *RecoverOrderLogic) RecoverOrder(req *types.RecoverOrderRequest) (*types
code := strings.TrimSpace(req.Code)
if orderNo == "" || email == "" || code == "" {
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return recoveryFailed(recoveryInvalidOrderMessage), nil
}
if err := l.verifyEmailCode(email, code); err != nil {
return nil, err
return recoveryFailed(recoveryInvalidCodeMessage), nil
}
item, ok, err := recoveryconfig.FindOrder(orderNo)
if err != nil {
l.Errorw("[RecoverOrder] load recovery config failed", logger.Field("error", err.Error()))
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.ERROR), genericRecoveryError)
return recoveryFailed(recoverySystemErrorMessage), nil
}
if !ok {
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return recoveryFailed(recoveryInvalidOrderMessage), nil
}
if item.OrderStatus != "" && item.OrderStatus != "success" {
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return recoveryFailed(recoveryInvalidOrderMessage), nil
}
if item.Quantity <= 0 || item.OrderMoneyCents <= 0 {
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return recoveryFailed(recoveryInvalidOrderMessage), nil
}
claimOrderNo := item.OutOrderNo
if claimOrderNo == "" {
claimOrderNo = item.OrderNo
}
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, item.SubscribeId)
if err != nil {
l.Errorw("[RecoverOrder] subscribe not found", logger.Field("subscribe_id", item.SubscribeId), logger.Field("error", err.Error()))
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return recoveryFailed(recoverySystemErrorMessage), nil
}
var (
claim recoverymodel.OrderRecoveryClaim
order ordermodel.Order
userSub usermodel.Subscribe
claim recoverymodel.OrderRecoveryClaim
order ordermodel.Order
userSub usermodel.Subscribe
alreadyRecovered bool
)
err = l.svcCtx.DB.WithContext(l.ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Model(&recoverymodel.OrderRecoveryClaim{}).
Where("order_no = ?", orderNo).
Where("order_no = ?", claimOrderNo).
First(&claim).Error; err == nil {
alreadyRecovered = true
return nil
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
@@ -98,7 +108,7 @@ func (l *RecoverOrderLogic) RecoverOrder(req *types.RecoverOrderRequest) (*types
}
claim = recoverymodel.OrderRecoveryClaim{
OrderNo: orderNo,
OrderNo: claimOrderNo,
Email: email,
UserId: u.Id,
SubscribeId: item.SubscribeId,
@@ -128,7 +138,14 @@ func (l *RecoverOrderLogic) RecoverOrder(req *types.RecoverOrderRequest) (*types
})
if err != nil {
l.Errorw("[RecoverOrder] recover failed", logger.Field("order_no", orderNo), logger.Field("error", err.Error()))
return nil, pkgerrors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), genericRecoveryError)
return recoveryFailed(recoverySystemErrorMessage), nil
}
if alreadyRecovered {
return &types.RecoverOrderResponse{
Success: true,
Message: recoveryAlreadyDoneMessage,
}, nil
}
l.clearCaches(&userSub, item.SubscribeId, email)
@@ -137,10 +154,17 @@ func (l *RecoverOrderLogic) RecoverOrder(req *types.RecoverOrderRequest) (*types
return &types.RecoverOrderResponse{
Success: true,
Message: "recovery completed, please login with your email verification code",
Message: recoverySuccessMessage,
}, nil
}
func recoveryFailed(message string) *types.RecoverOrderResponse {
return &types.RecoverOrderResponse{
Success: false,
Message: message,
}
}
func (l *RecoverOrderLogic) createRecoveredOrder(tx *gorm.DB, userId int64, item recoveryconfig.Order, sub *subscribemodel.Subscribe) (ordermodel.Order, error) {
var existing ordermodel.Order
orderNo := item.OutOrderNo
@@ -219,10 +243,6 @@ func (l *RecoverOrderLogic) createOrExtendSubscribe(tx *gorm.DB, userId int64, o
existing.ExpireTime = base.Add(time.Duration(item.Quantity) * 24 * time.Hour)
existing.Status = 1
existing.FinishedAt = nil
existing.NodeGroupId = sub.NodeGroupId
existing.Traffic = sub.Traffic
existing.Download = 0
existing.Upload = 0
if strings.TrimSpace(item.Note) != "" {
existing.Note = strings.TrimSpace(item.Note)
}
@@ -275,7 +295,7 @@ func (l *RecoverOrderLogic) verifyEmailCode(email string, code string) error {
return err
}
if resp == nil || !resp.Status {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), genericRecoveryError)
return errors.New("invalid verification code")
}
return nil
}
+6
View File
@@ -99,6 +99,12 @@ func loadOrders() (map[string]Order, error) {
return nil, fmt.Errorf("duplicate recovery out_order_no %s", item.OutOrderNo)
}
result[item.OutOrderNo] = item
if item.OrderNo != "" && item.OrderNo != item.OutOrderNo {
if _, exists := result[item.OrderNo]; exists {
return nil, fmt.Errorf("duplicate recovery order_no %s", item.OrderNo)
}
result[item.OrderNo] = item
}
}
return result, nil
}