- 新增用户订单号字段以区分内部订单号 - 修改订单表结构添加 user_order_id 字段及索引 - 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象 - 新增模型定义用于封装订单创建与支付结果 - 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑 - 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用
341 lines
10 KiB
Go
341 lines
10 KiB
Go
package jd_cookie
|
||
|
||
import (
|
||
"context"
|
||
"kami/internal/consts"
|
||
"kami/internal/dao"
|
||
"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"
|
||
"github.com/gogf/gf/v2/util/guid"
|
||
)
|
||
|
||
// ====================================================================================
|
||
// 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, cookie.CookieId, consts.CookieChangeTypeResume,
|
||
int(consts.JdCookieStatusSuspend), int(consts.JdCookieStatusNormal), "", cookie.FailureCount)
|
||
}
|
||
|
||
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) (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状态失败")
|
||
}
|
||
|
||
// 记录状态变更历史
|
||
if oldCookie.Status != int(status) || oldCookie.FailureCount != failureCount {
|
||
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, cookieId, changeType, oldCookie.Status, int(status), "", failureCount)
|
||
}
|
||
|
||
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) (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
|
||
default:
|
||
changeType = consts.JdOrderChangeTypeReplace
|
||
}
|
||
|
||
// 获取当前关联的订单ID
|
||
orderId := ""
|
||
if oldOrder.CurrentOrderId > 0 {
|
||
// 查询订单表获取订单号
|
||
var order *entity.V1JdCookieOrder
|
||
_ = dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||
Where(dao.V1JdCookieOrder.Columns().Id, oldOrder.CurrentOrderId).
|
||
Scan(&order)
|
||
if order != nil {
|
||
orderId = order.OrderId
|
||
}
|
||
}
|
||
|
||
payUrl := wxPayUrl
|
||
if payUrl == "" {
|
||
payUrl = oldOrder.WxPayUrl
|
||
}
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, changeType, orderId, payUrl)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 历史记录相关方法
|
||
// ====================================================================================
|
||
|
||
// RecordCookieHistory 记录Cookie变更历史
|
||
func (s *sJdCookie) RecordCookieHistory(ctx context.Context, cookieId string, changeType consts.CookieChangeType, statusBefore, statusAfter int, userOrderId string, failureCount int) (err error) {
|
||
m := dao.V1JdCookieChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
historyUuid := utils.GenerateRandomUUID()
|
||
_, err = m.Insert(&do.V1JdCookieChangeHistory{
|
||
HistoryUuid: historyUuid,
|
||
CookieId: cookieId,
|
||
ChangeType: changeType,
|
||
StatusBefore: statusBefore,
|
||
StatusAfter: statusAfter,
|
||
UserOrderId: userOrderId,
|
||
FailureCount: failureCount,
|
||
})
|
||
|
||
if err != nil {
|
||
glog.Error(ctx, "记录Cookie变更历史失败", g.Map{
|
||
"cookieId": cookieId,
|
||
"changeType": changeType,
|
||
"statusBefore": statusBefore,
|
||
"statusAfter": statusAfter,
|
||
"error": err,
|
||
})
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// RecordJdOrderHistory 记录京东订单变更历史
|
||
func (s *sJdCookie) RecordJdOrderHistory(ctx context.Context, jdOrderId string, changeType consts.JdOrderChangeType, orderId, wxPayUrl string) (err error) {
|
||
m := dao.V1JdCookieJdOrderChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
historyUuid := guid.S()
|
||
_, err = m.Insert(&do.V1JdCookieJdOrderChangeHistory{
|
||
HistoryUuid: historyUuid,
|
||
JdOrderId: jdOrderId,
|
||
ChangeType: changeType,
|
||
OrderId: orderId,
|
||
WxPayUrl: wxPayUrl,
|
||
})
|
||
|
||
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 := guid.S()
|
||
_, 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
|
||
}
|