This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -277,9 +277,22 @@
|
||||
const emailInput = document.getElementById("email");
|
||||
const orderInput = document.getElementById("order-no");
|
||||
const codeInput = document.getElementById("code");
|
||||
const API_BASE = "https://tapi.hifast.biz";
|
||||
|
||||
let countdownTimer = null;
|
||||
|
||||
const ERROR_MESSAGE_MAP = {
|
||||
"Param Error": "请检查订单号、邮箱和验证码是否填写正确。",
|
||||
"Verify code error": "邮箱验证码错误或已过期,请重新获取验证码。",
|
||||
"Too Many Requests": "验证码发送太频繁,请稍后再试。",
|
||||
"This account has reached the limit of sending times today":
|
||||
"该邮箱今天获取验证码次数已达上限,请明天再试。"
|
||||
};
|
||||
|
||||
function friendlyMessage(message, fallback) {
|
||||
return ERROR_MESSAGE_MAP[message] || message || fallback;
|
||||
}
|
||||
|
||||
function showStatus(type, message) {
|
||||
statusBox.className = `status show ${type}`;
|
||||
statusBox.textContent = message;
|
||||
@@ -296,7 +309,7 @@
|
||||
}
|
||||
|
||||
async function postJson(path, body) {
|
||||
const response = await fetch(path, {
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
@@ -305,7 +318,9 @@
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || payload.code !== 200) {
|
||||
throw new Error(payload.msg || "请求失败,请稍后再试");
|
||||
throw new Error(
|
||||
friendlyMessage(payload.msg, "请求失败,请稍后再试。")
|
||||
);
|
||||
}
|
||||
return payload.data || {};
|
||||
}
|
||||
@@ -361,13 +376,19 @@
|
||||
email,
|
||||
code
|
||||
});
|
||||
showStatus(
|
||||
"success",
|
||||
result.message || "恢复完成,请使用该邮箱验证码登录查看订阅。"
|
||||
);
|
||||
const message =
|
||||
result.message || "恢复完成,请使用该邮箱验证码登录查看订阅。";
|
||||
if (result.success === false) {
|
||||
showStatus("error", message);
|
||||
return;
|
||||
}
|
||||
showStatus("success", message);
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
showStatus("error", "订单或邮箱验证码无效,请确认后重试。");
|
||||
showStatus(
|
||||
"error",
|
||||
error.message || "恢复失败,请检查信息后重试。"
|
||||
);
|
||||
} finally {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = "恢复订阅";
|
||||
|
||||
Reference in New Issue
Block a user