- 新增 CreateOrderReq 结构体用于统一订单创建参数- 修改 CreateOrder 方法签名,使用结构体传参替代多个参数 - 更新 jd_cookie 相关枚举值,增加 JdCookieStatusUnknown 状态 - 调整 OrderInfo 和 JdOrderInfo 模型字段,增强数据一致性 -优化订单与京东订单关联逻辑,移除冗余的 CurrentOrderId 字段 - 移除 ShouldExtractCard 方法,改为内部私有方法 shouldExtractCard- 精简 Callback 方法参数,移除不必要的 userOrderId 和 amount 参数 - 修复订单历史记录中订单号关联问题,直接使用 orderId 字段查询 - 更新控制器层参数传递方式,适配新的服务层接口定义 - 调整卡密提取逻辑,去除对用户订单实体的依赖 - 完善订单状态检查机制,提高卡密提取安全性 - 优化数据库查询逻辑,减少不必要的关联查询操作
351 lines
11 KiB
Go
351 lines
11 KiB
Go
package jd_cookie
|
||
|
||
import (
|
||
"context"
|
||
"kami/internal/consts"
|
||
"kami/internal/dao"
|
||
"kami/internal/model"
|
||
"kami/internal/model/do"
|
||
"kami/internal/model/entity"
|
||
"kami/utility/config"
|
||
"kami/utility/utils"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/os/glog"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
)
|
||
|
||
// ====================================================================================
|
||
// Cookie轮询分配相关方法
|
||
// ====================================================================================
|
||
|
||
// GetAvailableCookie 获取可用的Cookie(轮询分配)
|
||
func (s *sJdCookie) GetAvailableCookie(ctx context.Context) (cookieId string, err error) {
|
||
// 首先尝试解锁过期的暂停Cookie
|
||
s.unlockExpiredCookies(ctx)
|
||
|
||
// 获取所有可用的Cookie
|
||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
var availableCookies []*entity.V1JdCookieAccount
|
||
err = m.Where(dao.V1JdCookieAccount.Columns().Status, int(consts.JdCookieStatusNormal)).
|
||
OrderAsc(dao.V1JdCookieAccount.Columns().LastUsedAt).
|
||
Scan(&availableCookies)
|
||
if err != nil {
|
||
return "", gerror.Wrap(err, "查询可用Cookie失败")
|
||
}
|
||
|
||
if len(availableCookies) == 0 {
|
||
return "", gerror.New(consts.ErrCodeCookieNotAvailable)
|
||
}
|
||
|
||
// 选择最久未使用的Cookie
|
||
selectedCookie := availableCookies[0]
|
||
|
||
// 更新最后使用时间
|
||
_, err = m.Where(dao.V1JdCookieAccount.Columns().CookieId, selectedCookie.CookieId).
|
||
Update(&do.V1JdCookieAccount{
|
||
LastUsedAt: gtime.Now(),
|
||
})
|
||
if err != nil {
|
||
glog.Error(ctx, "更新Cookie最后使用时间失败", err)
|
||
}
|
||
|
||
return selectedCookie.CookieId, nil
|
||
}
|
||
|
||
// unlockExpiredCookies 解锁过期的暂停Cookie
|
||
func (s *sJdCookie) unlockExpiredCookies(ctx context.Context) {
|
||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 先查询即将解锁的Cookie,用于记录历史
|
||
var expiredCookies []*entity.V1JdCookieAccount
|
||
err := m.Where(dao.V1JdCookieAccount.Columns().Status, int(consts.JdCookieStatusSuspend)).
|
||
WhereLTE(dao.V1JdCookieAccount.Columns().SuspendUntil, gtime.Now()).
|
||
WhereNotNull(dao.V1JdCookieAccount.Columns().SuspendUntil).
|
||
Scan(&expiredCookies)
|
||
if err != nil {
|
||
glog.Error(ctx, "查询过期暂停Cookie失败", err)
|
||
return
|
||
}
|
||
|
||
// 批量解锁暂停时间已过的Cookie
|
||
_, err = m.Where(dao.V1JdCookieAccount.Columns().Status, int(consts.JdCookieStatusSuspend)).
|
||
WhereLTE(dao.V1JdCookieAccount.Columns().SuspendUntil, gtime.Now()).
|
||
WhereNotNull(dao.V1JdCookieAccount.Columns().SuspendUntil).
|
||
Update(&do.V1JdCookieAccount{
|
||
Status: int(consts.JdCookieStatusNormal),
|
||
SuspendUntil: nil,
|
||
})
|
||
if err != nil {
|
||
glog.Error(ctx, "解锁暂停Cookie失败", err)
|
||
return
|
||
}
|
||
|
||
// 为每个解锁的Cookie记录历史
|
||
for _, cookie := range expiredCookies {
|
||
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
|
||
CookieId: cookie.CookieId,
|
||
ChangeType: consts.CookieChangeTypeResume,
|
||
StatusBefore: consts.JdCookieStatusSuspend,
|
||
StatusAfter: consts.JdCookieStatusNormal,
|
||
UserOrderId: "",
|
||
FailureCount: cookie.FailureCount,
|
||
Remark: "自动解锁过期暂停Cookie",
|
||
})
|
||
}
|
||
|
||
if len(expiredCookies) > 0 {
|
||
glog.Info(ctx, "自动解锁过期暂停Cookie", g.Map{
|
||
"unlockedCount": len(expiredCookies),
|
||
})
|
||
}
|
||
}
|
||
|
||
// ====================================================================================
|
||
// Cookie状态管理相关方法
|
||
// ====================================================================================
|
||
|
||
// UpdateCookieStatus 更新Cookie状态
|
||
func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, status consts.JdCookieStatus, failureCount int, remark string) (err error) {
|
||
if cookieId == "" {
|
||
return gerror.New("Cookie ID不能为空")
|
||
}
|
||
|
||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 获取更新前的Cookie信息
|
||
var oldCookie *entity.V1JdCookieAccount
|
||
err = m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Scan(&oldCookie)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "查询Cookie信息失败")
|
||
}
|
||
if oldCookie == nil {
|
||
return gerror.New("Cookie不存在")
|
||
}
|
||
|
||
updateData := &do.V1JdCookieAccount{
|
||
Status: int(status),
|
||
FailureCount: failureCount,
|
||
}
|
||
|
||
// 如果是暂停状态,设置暂停时间
|
||
if status == consts.JdCookieStatusSuspend {
|
||
updateData.SuspendUntil = gtime.Now().Add(time.Minute * consts.JdCookieSuspendDuration)
|
||
}
|
||
|
||
// 如果是恢复正常状态,清除暂停时间
|
||
if status == consts.JdCookieStatusNormal {
|
||
updateData.SuspendUntil = nil
|
||
}
|
||
|
||
_, err = m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Update(updateData)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "更新Cookie状态失败")
|
||
}
|
||
|
||
// 确定变更类型
|
||
var changeType consts.CookieChangeType
|
||
switch status {
|
||
case consts.JdCookieStatusNormal:
|
||
changeType = consts.CookieChangeTypeResume
|
||
case consts.JdCookieStatusSuspend:
|
||
changeType = consts.CookieChangeTypeSuspend
|
||
case consts.JdCookieStatusExpired:
|
||
changeType = consts.CookieChangeTypeFail
|
||
default:
|
||
changeType = consts.CookieChangeTypeUpdate
|
||
}
|
||
|
||
// 记录状态变更历史
|
||
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
|
||
CookieId: cookieId,
|
||
ChangeType: changeType,
|
||
StatusBefore: consts.JdCookieStatus(oldCookie.Status),
|
||
StatusAfter: status,
|
||
UserOrderId: "",
|
||
FailureCount: failureCount,
|
||
Remark: remark,
|
||
})
|
||
|
||
return
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 京东订单管理相关方法
|
||
// ====================================================================================
|
||
|
||
// CreateJdOrder 创建京东订单
|
||
func (s *sJdCookie) CreateJdOrder(ctx context.Context, jdOrderId, payId, cookieId, category string, amount float64) (err error) {
|
||
if jdOrderId == "" || payId == "" || cookieId == "" {
|
||
return gerror.New("京东订单参数不能为空")
|
||
}
|
||
|
||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
_, err = m.Insert(&do.V1JdCookieJdOrder{
|
||
JdOrderId: jdOrderId,
|
||
PayId: payId,
|
||
Amount: amount,
|
||
Category: category,
|
||
CookieId: cookieId,
|
||
Status: int(consts.JdOrderStatusPending),
|
||
OrderExpireAt: gtime.Now().Add(time.Hour * consts.JdOrderExpireDuration),
|
||
})
|
||
|
||
if err != nil {
|
||
return gerror.Wrap(err, "创建京东订单失败")
|
||
}
|
||
|
||
// 记录京东订单创建历史
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeCreate, payId, "", "")
|
||
|
||
return
|
||
}
|
||
|
||
// UpdateJdOrderStatus 更新京东订单状态
|
||
func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, status consts.JdOrderStatus, wxPayUrl string, remark string) (err error) {
|
||
if jdOrderId == "" {
|
||
return gerror.New("京东订单号不能为空")
|
||
}
|
||
|
||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 获取更新前的订单信息
|
||
var oldOrder *entity.V1JdCookieJdOrder
|
||
err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Scan(&oldOrder)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "查询京东订单失败")
|
||
}
|
||
if oldOrder == nil {
|
||
return gerror.New("京东订单不存在")
|
||
}
|
||
|
||
updateData := &do.V1JdCookieJdOrder{
|
||
Status: int(status),
|
||
}
|
||
|
||
if wxPayUrl != "" {
|
||
updateData.WxPayUrl = wxPayUrl
|
||
updateData.WxPayExpireAt = gtime.Now().Add(time.Minute * consts.WxPayUrlExpireDuration)
|
||
}
|
||
|
||
_, err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Update(updateData)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "更新京东订单状态失败")
|
||
}
|
||
|
||
// 记录状态变更历史
|
||
if oldOrder.Status != int(status) {
|
||
var changeType consts.JdOrderChangeType
|
||
switch status {
|
||
case consts.JdOrderStatusPaid:
|
||
changeType = consts.JdOrderChangeTypePay
|
||
case consts.JdOrderStatusExpired:
|
||
changeType = consts.JdOrderChangeTypeExpire
|
||
case consts.JdOrderStatusCanceled:
|
||
changeType = consts.JdOrderChangeTypeInvalid
|
||
case consts.JdOrderStatusCkFailed:
|
||
changeType = consts.JdOrderChangeTypeInvalid
|
||
default:
|
||
changeType = consts.JdOrderChangeTypeSend
|
||
}
|
||
|
||
// 获取关联的订单ID
|
||
orderId := oldOrder.OrderId
|
||
|
||
payUrl := wxPayUrl
|
||
if payUrl == "" {
|
||
payUrl = oldOrder.WxPayUrl
|
||
}
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, changeType, orderId, payUrl, remark)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 历史记录相关方法
|
||
// ====================================================================================
|
||
|
||
// RecordCookieHistory 记录Cookie变更历史
|
||
func (s *sJdCookie) RecordCookieHistory(ctx context.Context, req *model.RecordCookieHistoryReq) (err error) {
|
||
m := dao.V1JdCookieChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
historyUuid := utils.GenerateRandomUUID()
|
||
_, err = m.Insert(&do.V1JdCookieChangeHistory{
|
||
HistoryUuid: historyUuid,
|
||
CookieId: req.CookieId,
|
||
ChangeType: req.ChangeType,
|
||
StatusBefore: int(req.StatusBefore),
|
||
StatusAfter: int(req.StatusAfter),
|
||
UserOrderId: req.UserOrderId,
|
||
FailureCount: req.FailureCount,
|
||
Remark: req.Remark,
|
||
})
|
||
|
||
if err != nil {
|
||
glog.Error(ctx, "记录Cookie变更历史失败", g.Map{
|
||
"cookieId": req.CookieId,
|
||
"changeType": req.ChangeType,
|
||
"statusBefore": req.StatusBefore,
|
||
"statusAfter": req.StatusAfter,
|
||
"userOrderId": req.UserOrderId,
|
||
"failureCount": req.FailureCount,
|
||
"remark": req.Remark,
|
||
"error": err,
|
||
})
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// RecordJdOrderHistory 记录京东订单变更历史
|
||
func (s *sJdCookie) RecordJdOrderHistory(ctx context.Context, jdOrderId string, changeType consts.JdOrderChangeType, orderId, wxPayUrl, remark string) (err error) {
|
||
m := dao.V1JdCookieJdOrderChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
_, err = m.Insert(&do.V1JdCookieJdOrderChangeHistory{
|
||
HistoryUuid: utils.GenerateRandomUUID(),
|
||
JdOrderId: jdOrderId,
|
||
ChangeType: changeType,
|
||
OrderId: orderId,
|
||
WxPayUrl: wxPayUrl,
|
||
Remark: remark,
|
||
})
|
||
|
||
if err != nil {
|
||
glog.Error(ctx, "记录京东订单变更历史失败", g.Map{
|
||
"jdOrderId": jdOrderId,
|
||
"changeType": changeType,
|
||
"orderId": orderId,
|
||
"error": err,
|
||
})
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// RecordOrderHistory 记录订单变更历史
|
||
func (s *sJdCookie) RecordOrderHistory(ctx context.Context, orderId string, changeType consts.OrderChangeType, jdOrderId string) (err error) {
|
||
m := dao.V1JdCookieOrderChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
historyUuid := utils.GenerateRandomUUID()
|
||
_, err = m.Insert(&do.V1JdCookieOrderChangeHistory{
|
||
HistoryUuid: historyUuid,
|
||
OrderId: orderId,
|
||
ChangeType: changeType,
|
||
JdOrderId: jdOrderId,
|
||
})
|
||
|
||
if err != nil {
|
||
glog.Error(ctx, "记录订单变更历史失败", g.Map{
|
||
"orderId": orderId,
|
||
"changeType": changeType,
|
||
"jdOrderId": jdOrderId,
|
||
"error": err,
|
||
})
|
||
}
|
||
|
||
return
|
||
}
|