- 新增用户订单号字段以区分内部订单号 - 修改订单表结构添加 user_order_id 字段及索引 - 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象 - 新增模型定义用于封装订单创建与支付结果 - 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑 - 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用
373 lines
13 KiB
Go
373 lines
13 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/integration/originalJd"
|
||
"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/gconv"
|
||
)
|
||
|
||
// ====================================================================================
|
||
// 数据库查询辅助方法
|
||
// ====================================================================================
|
||
|
||
// getOrderByOrderId 根据订单号查询订单
|
||
func (s *sJdCookie) getOrderByOrderId(ctx context.Context, orderId string) (order *entity.V1JdCookieOrder, err error) {
|
||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
err = m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Scan(&order)
|
||
return
|
||
}
|
||
|
||
// getOrderByUserOrderId 根据用户订单号查询订单
|
||
func (s *sJdCookie) getOrderByUserOrderId(ctx context.Context, userOrderId string) (order *entity.V1JdCookieOrder, err error) {
|
||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
err = m.Where(dao.V1JdCookieOrder.Columns().UserOrderId, userOrderId).Scan(&order)
|
||
err = utils.HandleNoRowsError(err)
|
||
return
|
||
}
|
||
|
||
// getJdOrderByJdOrderId 根据京东订单号查询京东订单
|
||
func (s *sJdCookie) getJdOrderByJdOrderId(ctx context.Context, jdOrderId string) (jdOrder *entity.V1JdCookieJdOrder, err error) {
|
||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Scan(&jdOrder)
|
||
return
|
||
}
|
||
|
||
// getCookieById 根据Cookie ID获取Cookie信息
|
||
func (s *sJdCookie) getCookieById(ctx context.Context, cookieId string) (cookie *entity.V1JdCookieAccount, err error) {
|
||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
err = m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Scan(&cookie)
|
||
return
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 数据库更新辅助方法
|
||
// ====================================================================================
|
||
|
||
// createJdOrderRecord 创建京东订单记录
|
||
func (s *sJdCookie) createJdOrderRecord(ctx context.Context, jdOrderId, payId, cookieId string, category consts.RedeemOrderCardCategory, amount float64, wxPayUrl string) error {
|
||
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),
|
||
WxPayUrl: wxPayUrl,
|
||
WxPayExpireAt: gtime.Now().Add(time.Minute * consts.WxPayUrlExpireDuration),
|
||
OrderExpireAt: gtime.Now().Add(time.Hour * consts.JdOrderExpireDuration),
|
||
})
|
||
return err
|
||
}
|
||
|
||
// createOrderRecord 创建订单记录
|
||
func (s *sJdCookie) createOrderRecord(ctx context.Context, internalOrderId, userOrderId string, amount float64, category consts.RedeemOrderCardCategory, jdOrderId, wxPayUrl string) error {
|
||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
_, err := m.Insert(&do.V1JdCookieOrder{
|
||
OrderId: internalOrderId,
|
||
UserOrderId: userOrderId,
|
||
Amount: amount,
|
||
Category: category,
|
||
JdOrderId: jdOrderId,
|
||
Status: int(consts.OrderStatusPending),
|
||
WxPayUrl: wxPayUrl,
|
||
})
|
||
return err
|
||
}
|
||
|
||
// updateOrderLastRequest 更新订单最后请求时间
|
||
func (s *sJdCookie) updateOrderLastRequest(ctx context.Context, orderId string) error {
|
||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(&do.V1JdCookieOrder{
|
||
LastRequestAt: gtime.Now(),
|
||
})
|
||
return err
|
||
}
|
||
|
||
// updateOrderJdOrderId 更新订单关联的京东订单ID和支付链接
|
||
func (s *sJdCookie) updateOrderJdOrderId(ctx context.Context, orderId, jdOrderId, wxPayUrl string) error {
|
||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(&do.V1JdCookieOrder{
|
||
JdOrderId: jdOrderId,
|
||
WxPayUrl: wxPayUrl,
|
||
})
|
||
return err
|
||
}
|
||
|
||
// updateJdOrderPaymentUrl 更新京东订单的支付链接
|
||
func (s *sJdCookie) updateJdOrderPaymentUrl(ctx context.Context, jdOrderId, wxPayUrl string) error {
|
||
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 || oldOrder == nil {
|
||
glog.Warning(ctx, "查询京东订单失败,无法记录历史", err)
|
||
// 即使查询失败也继续更新
|
||
}
|
||
|
||
_, err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Update(&do.V1JdCookieJdOrder{
|
||
WxPayUrl: wxPayUrl,
|
||
WxPayExpireAt: gtime.Now().Add(time.Minute * consts.WxPayUrlExpireDuration),
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 记录支付链接更新历史
|
||
if oldOrder != nil && oldOrder.WxPayUrl != wxPayUrl {
|
||
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
|
||
}
|
||
}
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeReplace, orderId, wxPayUrl)
|
||
}
|
||
|
||
return err
|
||
}
|
||
|
||
// updateJdOrderCurrentOrderId 更新京东订单的当前关联订单ID
|
||
func (s *sJdCookie) updateJdOrderCurrentOrderId(ctx context.Context, jdOrderId, orderId string) error {
|
||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 如果orderId为空,表示解绑
|
||
if orderId == "" {
|
||
// 获取更新前的京东订单信息
|
||
var oldJdOrder *entity.V1JdCookieJdOrder
|
||
err := m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Scan(&oldJdOrder)
|
||
if err != nil || oldJdOrder == nil {
|
||
glog.Warning(ctx, "查询京东订单失败,无法记录历史", err)
|
||
// 即使查询失败也继续更新
|
||
}
|
||
|
||
// 解绑:设置为null
|
||
_, err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Update(&do.V1JdCookieJdOrder{
|
||
CurrentOrderId: nil,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 记录解绑历史
|
||
if oldJdOrder != nil && oldJdOrder.CurrentOrderId > 0 {
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeUnbind, "", oldJdOrder.WxPayUrl)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 查找订单ID对应的内部ID
|
||
var order *entity.V1JdCookieOrder
|
||
err := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||
Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).
|
||
Scan(&order)
|
||
if err != nil || order == nil {
|
||
return gerror.New("订单不存在")
|
||
}
|
||
|
||
// 获取更新前的京东订单信息
|
||
var oldJdOrder *entity.V1JdCookieJdOrder
|
||
err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Scan(&oldJdOrder)
|
||
if err != nil || oldJdOrder == nil {
|
||
glog.Warning(ctx, "查询京东订单失败,无法记录历史", err)
|
||
// 即使查询失败也继续更新
|
||
}
|
||
|
||
_, err = m.Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).Update(&do.V1JdCookieJdOrder{
|
||
CurrentOrderId: order.Id,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 记录订单绑定历史
|
||
var changeType consts.JdOrderChangeType
|
||
if oldJdOrder != nil {
|
||
if oldJdOrder.CurrentOrderId == 0 {
|
||
changeType = consts.JdOrderChangeTypeBind // 首次绑定
|
||
} else if oldJdOrder.CurrentOrderId != order.Id {
|
||
changeType = consts.JdOrderChangeTypeReplace // 更换绑定
|
||
}
|
||
} else {
|
||
changeType = consts.JdOrderChangeTypeBind // 首次绑定
|
||
}
|
||
|
||
// 获取京东订单的支付链接
|
||
wxPayUrl := ""
|
||
if oldJdOrder != nil {
|
||
wxPayUrl = oldJdOrder.WxPayUrl
|
||
}
|
||
_ = s.RecordJdOrderHistory(ctx, jdOrderId, changeType, orderId, wxPayUrl)
|
||
|
||
return err
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 订单复用相关方法
|
||
// ====================================================================================
|
||
|
||
// findReusableJdOrder 查找可复用的京东订单
|
||
func (s *sJdCookie) findReusableJdOrder(ctx context.Context, amount float64, category consts.RedeemOrderCardCategory) (jdOrder *entity.V1JdCookieJdOrder, err error) {
|
||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 查找符合条件的京东订单:
|
||
// 1. 金额和品类相同
|
||
// 2. 状态为待支付
|
||
// 3. 没有当前关联订单或者关联订单已过期
|
||
// 4. 订单未过期
|
||
err = m.Where(dao.V1JdCookieJdOrder.Columns().Amount, amount).
|
||
Where(dao.V1JdCookieJdOrder.Columns().Category, category).
|
||
Where(dao.V1JdCookieJdOrder.Columns().Status, int(consts.JdOrderStatusPending)).
|
||
WhereNull(dao.V1JdCookieJdOrder.Columns().CurrentOrderId).
|
||
WhereGT(dao.V1JdCookieJdOrder.Columns().OrderExpireAt, gtime.Now()).
|
||
OrderAsc(dao.V1JdCookieJdOrder.Columns().CreatedAt).
|
||
Limit(1).
|
||
Scan(&jdOrder)
|
||
|
||
return
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 京东接口调用方法
|
||
// ====================================================================================
|
||
|
||
// callJdCreateOrder 调用京东下单接口
|
||
func (s *sJdCookie) callJdCreateOrder(ctx context.Context, cookieId string, amount float64, category consts.RedeemOrderCardCategory) (jdOrderId, payId, wxPayUrl string, err error) {
|
||
cookie, err := s.getCookieById(ctx, cookieId)
|
||
if err != nil {
|
||
return
|
||
}
|
||
appResp, err := originalJd.NewClient().AppleRecharge(ctx, &originalJd.AppleRechargeReq{
|
||
FacePrice: amount,
|
||
Category: category,
|
||
OrderNum: utils.GenerateRandomUUID(),
|
||
Cookies: cookie.CookieValue,
|
||
})
|
||
glog.Info(ctx, "下单接口返回", appResp)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if appResp.Code != consts.CardCookieJDStatusSuccess || appResp.Deeplink == "" {
|
||
return "", "", "", gerror.New("下单失败," + appResp.Msg)
|
||
}
|
||
jdOrderId = appResp.OrderId
|
||
payId = appResp.PayId
|
||
wxPayUrl = appResp.Deeplink
|
||
return
|
||
}
|
||
|
||
// refreshPaymentUrl 刷新支付链接
|
||
func (s *sJdCookie) refreshPaymentUrl(ctx context.Context, jdOrderId, payId, cookieId, orderId string) (wxPayUrl string, err error) {
|
||
cookie, err := s.getCookieById(ctx, cookieId)
|
||
if err != nil {
|
||
return
|
||
}
|
||
jdOrderIdInt := gconv.Int64(jdOrderId)
|
||
|
||
appResp, err := originalJd.NewClient().RefreshPayment(ctx, &originalJd.RefreshPaymentReq{
|
||
Cookies: cookie.CookieValue,
|
||
OrderId: jdOrderIdInt,
|
||
PayId: payId,
|
||
UserOrderId: orderId,
|
||
})
|
||
|
||
glog.Info(ctx, "刷新支付链接", g.Map{
|
||
"appResp": appResp,
|
||
"cookie": cookie,
|
||
"jdOrderId": jdOrderId,
|
||
})
|
||
if err != nil {
|
||
return
|
||
}
|
||
if appResp.Code != consts.CardCookieJDStatusSuccess || appResp.Deeplink == "" {
|
||
return "", gerror.New("刷新失败," + appResp.Msg)
|
||
}
|
||
wxPayUrl = appResp.Deeplink
|
||
return
|
||
}
|
||
|
||
// callJdCheckPayment 调用京东接口检查支付状态
|
||
func (s *sJdCookie) callJdCheckPayment(ctx context.Context, jdOrderId, cookieValue string, category consts.RedeemOrderCardCategory) (*originalJd.AppleRechargeCardInfoResp, error) {
|
||
// 创建京东客户端
|
||
client := originalJd.NewClient()
|
||
|
||
// 调用检查支付状态接口
|
||
req := &originalJd.AppleRechargeCardInfoReq{
|
||
OrderId: utils.GenerateRandomUUID(),
|
||
JdOrderId: jdOrderId,
|
||
Cookies: cookieValue,
|
||
Category: category,
|
||
}
|
||
|
||
resp, err := client.GetCardInfo(ctx, req)
|
||
if err != nil {
|
||
glog.Error(ctx, "调用京东检查支付状态接口失败", g.Map{
|
||
"jdOrderId": jdOrderId,
|
||
"error": err,
|
||
})
|
||
return nil, err
|
||
}
|
||
|
||
glog.Info(ctx, "京东检查支付状态成功", resp)
|
||
|
||
return resp, nil
|
||
}
|
||
|
||
// ====================================================================================
|
||
// Cookie处理方法
|
||
// ====================================================================================
|
||
|
||
// handleCookieFailure 处理Cookie失败
|
||
func (s *sJdCookie) handleCookieFailure(ctx context.Context, cookieId, orderId string) {
|
||
// 获取当前Cookie信息
|
||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
var account *entity.V1JdCookieAccount
|
||
err := m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Scan(&account)
|
||
if err != nil || account == nil {
|
||
return
|
||
}
|
||
|
||
newFailureCount := account.FailureCount + 1
|
||
statusBefore := account.Status
|
||
var statusAfter int
|
||
|
||
if newFailureCount >= consts.JdCookieMaxFailureCount {
|
||
// 失败次数过多,标记为失效
|
||
statusAfter = int(consts.JdCookieStatusExpired)
|
||
_, _ = m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Update(&do.V1JdCookieAccount{
|
||
Status: statusAfter,
|
||
FailureCount: newFailureCount,
|
||
})
|
||
} else {
|
||
// 暂停Cookie
|
||
statusAfter = int(consts.JdCookieStatusSuspend)
|
||
suspendUntil := gtime.Now().Add(time.Minute * consts.JdCookieSuspendDuration)
|
||
_, _ = m.Where(dao.V1JdCookieAccount.Columns().CookieId, cookieId).Update(&do.V1JdCookieAccount{
|
||
Status: statusAfter,
|
||
FailureCount: newFailureCount,
|
||
SuspendUntil: suspendUntil,
|
||
})
|
||
}
|
||
|
||
// 记录失败历史
|
||
_ = s.RecordCookieHistory(ctx, cookieId, consts.CookieChangeTypeFail, statusBefore, statusAfter, orderId, newFailureCount)
|
||
}
|