- 添加骆驼加油API端点设计及系统架构文档 - 新增定时任务模块详细说明及任务流程图 - 完成订单管理服务功能及架构介绍 - 增加账号管理模块设计与状态机说明 - 集成Pig接码平台文档,介绍验证码检测流程 - 详细列出各组件的依赖关系及性能优化措施 - 提供故障排除指南及系统扩展性总结
410 lines
17 KiB
Go
410 lines
17 KiB
Go
package card_apple_order
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"kami/internal/consts"
|
||
"kami/internal/errHandler"
|
||
"kami/internal/model"
|
||
"kami/internal/model/entity"
|
||
"kami/internal/service"
|
||
"kami/utility/cache"
|
||
"kami/utility/config"
|
||
"kami/utility/integration/apple"
|
||
"kami/utility/pool"
|
||
"time"
|
||
|
||
"github.com/duke-git/lancet/v2/slice"
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/errors/gcode"
|
||
"github.com/gogf/gf/v2/os/gcron"
|
||
"github.com/gogf/gf/v2/os/gctx"
|
||
"github.com/gogf/gf/v2/os/glog"
|
||
"github.com/shopspring/decimal"
|
||
)
|
||
|
||
// handleRedeemResult 处理核销结果,根据状态码分类进行对应处理
|
||
func (h *sAppleOrder) handleRedeemResult(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo) error {
|
||
// 调用 Apple 服务进行核销(同步等待)
|
||
redeemClient := apple.NewClient()
|
||
// 准备推送请求
|
||
redeemReq := &apple.RedeemReq{
|
||
Account: accountInfo.Account,
|
||
Password: accountInfo.Password,
|
||
OrderId: orderEntity.OrderNo,
|
||
RedemptionCode: orderEntity.CardPass,
|
||
}
|
||
resp, err := redeemClient.Redeem(ctx, redeemReq)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 根据状态码分类处理
|
||
switch {
|
||
// 1. 成功状态(CodeSuccess = 0)
|
||
case resp.Code == apple.CodeSuccess:
|
||
return h.handleRedeemSuccess(ctx, orderEntity, accountInfo, resp.Data.Amount, resp.Data.BalanceBefore, resp.Data.BalanceAfter)
|
||
|
||
// 2. 网络请求或系统资源错误(5000-5999)
|
||
case resp.Code >= 5000 && resp.Code < 6000:
|
||
return h.handleSystemError(ctx, orderEntity, accountInfo, resp.Code, resp.Message)
|
||
|
||
// 3. 苹果账户原因错误(8001-8005)
|
||
case resp.Code >= 8001 && resp.Code <= 8005:
|
||
return h.handleAccountError(ctx, orderEntity, accountInfo, resp.Code, resp.Message)
|
||
|
||
// 4. 充值限制错误(8010-8012)
|
||
case resp.Code >= 8010 && resp.Code <= 8012:
|
||
return h.handleRedeemLimitError(ctx, orderEntity, accountInfo, resp.Code, resp.Message)
|
||
|
||
// 5. 卡密错误(8013-8014)
|
||
case resp.Code >= 8013 && resp.Code <= 8014:
|
||
return h.handleCardCodeError(ctx, orderEntity, accountInfo, resp.Code, resp.Message)
|
||
|
||
// 未知错误
|
||
default:
|
||
return h.handleRedeemFailed(ctx, orderEntity, accountInfo, resp.Message)
|
||
}
|
||
}
|
||
|
||
// handleRedeemSuccess 处理核销成功的情况
|
||
func (h *sAppleOrder) handleRedeemSuccess(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, amount, balanceBefore, balanceAfter float64) error {
|
||
// 将返回的金额字符串转换为 float64(假数据处理)
|
||
actualAmount := orderEntity.CardAmount // 使用卡密面额作为默认金额
|
||
|
||
// 判断金额是否一致
|
||
var orderStatus consts.AppleRechargeOrderStatus
|
||
var historyOperation consts.AppleOrderOperation
|
||
var remark string
|
||
|
||
if actualAmount == orderEntity.CardAmount {
|
||
orderStatus = consts.AppleRechargeOrderSuccess
|
||
historyOperation = consts.AppleRechargeOperationItunesSucceed
|
||
} else {
|
||
orderStatus = consts.AppleRechargeOrderAmountDifferent
|
||
historyOperation = consts.AppleRechargeOperationItunesSucceedButWrongAmount
|
||
remark = "金额异议"
|
||
}
|
||
|
||
// 更新订单和账户余额
|
||
err := h.UpdateActualAmountAndHistoryAndWallet(ctx, &model.AppleAccountUpdateAmountAndHistoryRecord{
|
||
OrderInfo: &model.AppleAccountUpdateAmountRecord{
|
||
OrderNo: orderEntity.OrderNo,
|
||
Amount: actualAmount,
|
||
Status: orderStatus,
|
||
Remark: fmt.Sprintf("卡密:%s,面额:%.2f,实际充值:%.2f,充值账户:%s,%s", orderEntity.CardPass, orderEntity.CardAmount, actualAmount, accountInfo.Account, remark),
|
||
},
|
||
AccountId: accountInfo.Id,
|
||
HistoryRemark: remark,
|
||
HistoryOperation: historyOperation,
|
||
BalanceFromItunes: balanceAfter,
|
||
})
|
||
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单成功记录失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 异步回调上游
|
||
_ = h.CallBackOrderToUpstream(ctx, orderEntity.OrderNo)
|
||
|
||
glog.Info(ctx, fmt.Sprintf("订单核销成功处理完毕 - 订单号: %s", orderEntity.OrderNo))
|
||
return nil
|
||
}
|
||
|
||
// handleRedeemFailed 处理核销失败的情况
|
||
func (h *sAppleOrder) handleRedeemFailed(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, errMsg string) error {
|
||
// 订单退回待处理状态
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, consts.AppleRechargeOrderWaiting, fmt.Sprintf("核销失败:%s", errMsg), nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单失败状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationItunesFail,
|
||
Remark: fmt.Sprintf("核销失败:%s", errMsg),
|
||
}, nil)
|
||
|
||
glog.Info(ctx, fmt.Sprintf("订单核销失败,已退回待处理 - 订单号: %s", orderEntity.OrderNo))
|
||
return nil
|
||
}
|
||
|
||
// handleAccountInvalid 处理账号失效的情况
|
||
func (h *sAppleOrder) handleAccountInvalid(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, errMsg string) error {
|
||
// 标记当前账号为失效(密码错误或被禁用)
|
||
_ = service.AppleAccount().ModifyStatus(ctx, accountInfo.Id, consts.AppleAccountWrongPassword, nil)
|
||
|
||
// 订单重新设为待调度状态
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, consts.AppleRechargeOrderWaiting, "账号失效,待重新分配", nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单待处理状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数,允许重新分配
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationWrongPassword,
|
||
Remark: fmt.Sprintf("账号失效:%s", errMsg),
|
||
}, nil)
|
||
|
||
glog.Info(ctx, fmt.Sprintf("账号失效,订单已退回待重新分配 - 订单号: %s, 账号: %s", orderEntity.OrderNo, accountInfo.Account))
|
||
return nil
|
||
}
|
||
|
||
// handleSystemError 处理系统资源错误(5000-5999)
|
||
// 订单退回待处理,等待系统重新调度重试
|
||
func (h *sAppleOrder) handleSystemError(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, code apple.Code, message string) error {
|
||
// 订单退回待处理状态
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, consts.AppleRechargeOrderWaiting, fmt.Sprintf("系统错误:%s", message), nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationItunesFail,
|
||
Remark: fmt.Sprintf("系统错误(状态码: %d):%s", code, message),
|
||
}, nil)
|
||
|
||
glog.Warning(ctx, fmt.Sprintf("系统错误,订单已退回待处理 - 订单号: %s, 状态码: %d", orderEntity.OrderNo, code))
|
||
return nil
|
||
}
|
||
|
||
// handleAccountError 处理苹果账户原因错误(8001-8005)
|
||
// 标记账户失效,订单退回待处理,等待重新分配其他有效账户
|
||
func (h *sAppleOrder) handleAccountError(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, code apple.Code, message string) error {
|
||
// 根据错误码标记不同的账户失效状态
|
||
var accountStatus consts.AppleAccountStatus
|
||
switch code {
|
||
case apple.CodeApplePasswordError, apple.CodeApplePasswordExpired:
|
||
accountStatus = consts.AppleAccountWrongPassword // 账号密码错误
|
||
case apple.CodeAppleAccountForbidden:
|
||
accountStatus = consts.AppleAccountForbidden // 账号被禁用
|
||
case apple.CodeAppleAccountLocked:
|
||
accountStatus = consts.AppleAccountLimited // 账号被封锁(作为受限处理)
|
||
case apple.CodeAppleStatusUnknown:
|
||
accountStatus = consts.AppleAccountNormal // 状态未知,保持正常以便后续重试
|
||
default:
|
||
accountStatus = consts.AppleAccountWrongPassword
|
||
}
|
||
|
||
// 标记账户为失效状态
|
||
_ = service.AppleAccount().ModifyStatus(ctx, accountInfo.Id, accountStatus, nil)
|
||
|
||
// 订单重新设为待调度状态
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, consts.AppleRechargeOrderWaiting, "苹果账户原因导致失效,待重新分配", nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数,允许重新分配
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationWrongPassword,
|
||
Remark: fmt.Sprintf("苹果账户错误(状态码: %d):%s", code, message),
|
||
}, nil)
|
||
|
||
glog.Warning(ctx, fmt.Sprintf("苹果账户原因导致失效,订单已退回待重新分配 - 订单号: %s, 账号: %s, 状态码: %d", orderEntity.OrderNo, accountInfo.Account, code))
|
||
return nil
|
||
}
|
||
|
||
// handleRedeemLimitError 处理充值限制错误(8010-8012)
|
||
// 根据限制类型标记账户,订单根据业务规则决定重试或失败
|
||
func (h *sAppleOrder) handleRedeemLimitError(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, code apple.Code, message string) error {
|
||
// 根据限制类型标记账户状态
|
||
var accountStatus consts.AppleAccountStatus
|
||
var accountRemark string
|
||
|
||
switch code {
|
||
case apple.CodeAppleRedeemLimitExceeded:
|
||
// 充值次数限制 - 永久受限
|
||
accountStatus = consts.AppleAccountForbiddenByTooManyRecharge
|
||
accountRemark = "苹果账户充值次数限制(永久)"
|
||
case apple.CodeAppleRedeemLimitExceededTemporarily:
|
||
// 临时限制 - 标记为临时冻结,暂停兑换
|
||
accountStatus = consts.AppleAccountTmpStoppedByTooManyRequest
|
||
_, _ = gcron.AddOnce(gctx.GetInitCtx(), "@every 2m", func(ctx2 context.Context) {
|
||
// 获取追踪能力
|
||
_ = service.AppleAccount().ModifyStatus(ctx2, orderEntity.AccountId, consts.AppleAccountNormal, nil)
|
||
})
|
||
accountRemark = "苹果账户临时充值限制(2分钟后恢复)"
|
||
case apple.CodeAppleRedeemLimitExceededPerMinute:
|
||
// 一分钟限制 - 标记为临时冻结
|
||
_, _ = gcron.AddOnce(gctx.GetInitCtx(), "@every 1m", func(ctx2 context.Context) {
|
||
// 获取追踪能力
|
||
_ = service.AppleAccount().ModifyStatus(ctx2, orderEntity.AccountId, consts.AppleAccountNormal, nil)
|
||
})
|
||
accountStatus = consts.AppleAccountTmpLimited
|
||
accountRemark = "苹果账户一分钟充值限制(1分钟后恢复)"
|
||
default:
|
||
accountStatus = consts.AppleAccountLimited
|
||
accountRemark = fmt.Sprintf("苹果账户充值限制(状态码: %d)", code)
|
||
}
|
||
|
||
// 标记账户限制状态
|
||
_ = service.AppleAccount().ModifyStatus(ctx, accountInfo.Id, accountStatus, nil)
|
||
|
||
// 订单退回待处理,后续可根据限制类型进行重试或转移到其他账户
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, consts.AppleRechargeOrderWaiting, fmt.Sprintf("账户受限:%s", message), nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationItunesFail,
|
||
Remark: fmt.Sprintf("%s - %s", accountRemark, message),
|
||
}, nil)
|
||
|
||
glog.Warning(ctx, fmt.Sprintf("账户充值限制,订单已退回待处理 - 订单号: %s, 账号: %s, 限制类型: %d", orderEntity.OrderNo, accountInfo.Account, code))
|
||
return nil
|
||
}
|
||
|
||
// handleCardCodeError 处理卡密错误(8013-8014)
|
||
// 直接按订单失败处理,更新订单状态为失败
|
||
func (h *sAppleOrder) handleCardCodeError(ctx context.Context, orderEntity *entity.V1CardAppleRechargeInfo, accountInfo *entity.V1CardAppleAccountInfo, code apple.Code, message string) error {
|
||
// 根据卡密错误类型确定订单失败状态
|
||
var orderStatus consts.AppleRechargeOrderStatus = consts.AppleRechargeOrderFail
|
||
var operationRemark string
|
||
|
||
switch code {
|
||
case apple.CodeAppleRedeemAlreadyClaimed:
|
||
operationRemark = "卡密已被兑换"
|
||
case apple.CodeAppleRedeemNotFound:
|
||
operationRemark = "卡密不存在"
|
||
default:
|
||
operationRemark = fmt.Sprintf("卡密错误(状态码: %d)", code)
|
||
}
|
||
|
||
// 更新订单为失败状态
|
||
err := h.ModifyOrderStatus(ctx, orderEntity.OrderNo, orderStatus, operationRemark, nil)
|
||
if err != nil {
|
||
glog.Error(ctx, fmt.Sprintf("更新订单失败状态失败 - 订单号: %s, 错误: %v", orderEntity.OrderNo, err))
|
||
return err
|
||
}
|
||
|
||
// 减少分配计数(卡密错误不再重试)
|
||
_ = h.DecrementDistributionCount(ctx, orderEntity.OrderNo)
|
||
|
||
// 添加历史记录
|
||
_ = h.AddHistory(ctx, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderEntity.OrderNo,
|
||
RechargeId: int(orderEntity.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationItunesFail,
|
||
Remark: fmt.Sprintf("卡密错误 - %s", message),
|
||
}, nil)
|
||
|
||
glog.Warning(ctx, fmt.Sprintf("卡密错误,订单已失败 - 订单号: %s, 账号: %s, 错误: %s", orderEntity.OrderNo, accountInfo.Account, operationRemark))
|
||
return nil
|
||
}
|
||
|
||
// ProcessOrderWithPush 处理订单:根据订单类型判断是立即进行核销处理还是创建定时任务异步处理
|
||
// 参数说明:
|
||
// - orderEntity: 待处理的订单信息
|
||
// - accountInfo: 分配的苹果账号信息
|
||
// - immediate: 是否立即处理(true: 立即核销,false: 异步定时处理)
|
||
func (h *sAppleOrder) ProcessOrderWithPush(ctx context.Context) (err error) {
|
||
orderEntities, _ := h.GetAccordingOrders(ctx)
|
||
if len(orderEntities) == 0 {
|
||
return
|
||
}
|
||
poolClient := pool.New("apple_order_process_push_pool", 1)
|
||
for _, orderInfo := range orderEntities {
|
||
_ = poolClient.Add(ctx, func(ctx context.Context) {
|
||
err = h.processOrderWithAccount(ctx, orderInfo)
|
||
if err != nil {
|
||
glog.Error(ctx, "处理订单失败", err)
|
||
}
|
||
})
|
||
}
|
||
return
|
||
}
|
||
|
||
func (h *sAppleOrder) processOrderWithAccount(ctx context.Context, orderInfo *entity.V1CardAppleRechargeInfo) (err error) {
|
||
keys := cache.NewCache().GetPrefixKey(ctx, cache.PrefixAppleAccount)
|
||
keysStr := slice.Map(slice.Filter(keys, func(index int, item interface{}) bool {
|
||
_, ok := item.(string)
|
||
return ok
|
||
}), func(index int, item interface{}) string {
|
||
return item.(string)
|
||
})
|
||
accountInfo, err := service.AppleAccount().GetAccordingAccount(ctx, decimal.NewFromFloat(orderInfo.Balance), keysStr)
|
||
if err != nil {
|
||
glog.Error(ctx, "获取订单账户失败", err)
|
||
return
|
||
}
|
||
cacheVar, err := cache.NewCache().Get(ctx, cache.PrefixAppleAccount.Key(accountInfo.Id))
|
||
if err == nil && !cacheVar.IsNil() {
|
||
glog.Warning(ctx, "账户正在处理中", accountInfo.Account)
|
||
}
|
||
//3分钟超时
|
||
_ = cache.NewCache().Set(ctx, cache.PrefixAppleAccount.Key(accountInfo.Id), 1, time.Minute*3)
|
||
defer func() {
|
||
_, _ = cache.NewCache().Remove(ctx, cache.PrefixAppleAccount.Key(accountInfo.Id))
|
||
}()
|
||
if err = h.DistributionAccordingAccount(ctx, accountInfo, orderInfo); err != nil {
|
||
err = errHandler.WrapError(ctx, gcode.CodeInternalError, err, "分配订单账户失败")
|
||
return
|
||
}
|
||
err = config.GetDatabaseV1().Transaction(ctx, func(ctx2 context.Context, tx gdb.TX) error {
|
||
err = service.AppleOrder().ModifyOrderStatus(ctx2, orderInfo.OrderNo, consts.AppleRechargeOrderProcessing, "", tx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
err = service.AppleOrder().AddHistory(ctx2, &model.AppleCardRechargeHistoryInput{
|
||
AccountID: accountInfo.Id,
|
||
OrderNo: orderInfo.OrderNo,
|
||
RechargeId: int(orderInfo.Id),
|
||
AccountName: accountInfo.Account,
|
||
Operation: consts.AppleRechargeOperationStartRechargeByItunes,
|
||
Remark: fmt.Sprintf("分配账户:%s,账户余额:%.2f", accountInfo.Account, accountInfo.BalanceItunes),
|
||
}, tx)
|
||
return err
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return h.handleRedeemResult(ctx, orderInfo, accountInfo)
|
||
}
|