Files
kami_backend/internal/logic/jd_cookie/order_jd.go
danial bc2d58753b feat(jd_cookie):重构订单创建逻辑并优化相关模型
- 新增 CreateOrderReq 结构体用于统一订单创建参数- 修改 CreateOrder 方法签名,使用结构体传参替代多个参数
- 更新 jd_cookie 相关枚举值,增加 JdCookieStatusUnknown 状态
- 调整 OrderInfo 和 JdOrderInfo 模型字段,增强数据一致性
-优化订单与京东订单关联逻辑,移除冗余的 CurrentOrderId 字段
- 移除 ShouldExtractCard 方法,改为内部私有方法 shouldExtractCard- 精简 Callback 方法参数,移除不必要的 userOrderId 和 amount 参数
- 修复订单历史记录中订单号关联问题,直接使用 orderId 字段查询
- 更新控制器层参数传递方式,适配新的服务层接口定义
- 调整卡密提取逻辑,去除对用户订单实体的依赖
- 完善订单状态检查机制,提高卡密提取安全性
- 优化数据库查询逻辑,减少不必要的关联查询操作
2025-10-18 14:13:40 +08:00

816 lines
25 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package jd_cookie
import (
"context"
"fmt"
v1 "kami/api/jd_cookie/v1"
"kami/internal/consts"
"kami/internal/dao"
"kami/internal/model"
"kami/internal/model/do"
"kami/internal/model/entity"
"kami/utility/cache"
"kami/utility/config"
"slices"
"time"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"github.com/xuri/excelize/v2"
)
// GetJdOrder 获取单个京东订单
func (s *sJdCookie) GetJdOrder(ctx context.Context, jdOrderId string) (order *v1.JdOrderInfo, err error) {
if jdOrderId == "" {
return nil, gerror.New("京东订单号不能为空")
}
// 查询京东订单
jdOrderEntity, err := s.getJdOrderByJdOrderId(ctx, jdOrderId)
if err != nil {
return nil, gerror.Wrap(err, "查询京东订单失败")
}
if jdOrderEntity == nil {
return nil, gerror.New(consts.ErrCodeJdOrderNotFound)
}
order = &v1.JdOrderInfo{
Id: jdOrderEntity.Id,
JdOrderId: jdOrderEntity.JdOrderId,
PayId: jdOrderEntity.PayId,
Amount: gconv.Float64(jdOrderEntity.Amount),
Category: jdOrderEntity.Category,
CookieId: jdOrderEntity.CookieId,
Status: consts.JdOrderStatus(jdOrderEntity.Status),
WxPayUrl: jdOrderEntity.WxPayUrl,
WxPayExpireAt: jdOrderEntity.WxPayExpireAt,
OrderExpireAt: jdOrderEntity.OrderExpireAt,
OrderId: jdOrderEntity.OrderId,
CardNo: jdOrderEntity.CardNo,
CardPassword: jdOrderEntity.CardPassword,
CardExtractedAt: jdOrderEntity.CardExtractedAt,
CreatedAt: jdOrderEntity.CreatedAt,
UpdatedAt: jdOrderEntity.UpdatedAt,
DeletedAt: jdOrderEntity.DeletedAt,
}
// 查询关联的Cookie信息
if jdOrderEntity.CookieId != "" {
var cookieEntity *entity.V1JdCookieAccount
err = dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1JdCookieAccount.Columns().CookieId, jdOrderEntity.CookieId).
Scan(&cookieEntity)
if err == nil && cookieEntity != nil {
order.Cookie = &v1.CookieAccountInfo{
Id: cookieEntity.Id,
CookieId: cookieEntity.CookieId,
AccountName: cookieEntity.AccountName,
Status: consts.JdCookieStatus(cookieEntity.Status),
CreatedAt: cookieEntity.CreatedAt,
UpdatedAt: cookieEntity.UpdatedAt,
}
}
}
return
}
// ListJdOrder 京东订单列表查询
func (s *sJdCookie) ListJdOrder(ctx context.Context, page, size int, status consts.JdOrderStatus, startTime, endTime, orderId string) (list []*v1.JdOrderInfo, total int, err error) {
if page <= 0 {
page = 1
}
if size <= 0 {
size = 20
}
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
// 构建查询条件
if status > 0 {
m = m.Where(dao.V1JdCookieJdOrder.Columns().Status, int(status))
}
if startTime != "" {
m = m.WhereGTE(dao.V1JdCookieJdOrder.Columns().CreatedAt, startTime)
}
if endTime != "" {
m = m.WhereLTE(dao.V1JdCookieJdOrder.Columns().CreatedAt, endTime)
}
if orderId != "" {
// 直接根据order_id字段筛选
m = m.Where(dao.V1JdCookieJdOrder.Columns().OrderId, orderId)
}
// 查询总数
total, err = m.Count()
if err != nil {
return nil, 0, gerror.Wrap(err, "查询京东订单总数失败")
}
// 查询列表数据
var jdOrders []*entity.V1JdCookieJdOrder
err = m.Page(page, size).
OrderDesc(dao.V1JdCookieJdOrder.Columns().CreatedAt).
Scan(&jdOrders)
if err != nil {
return nil, 0, gerror.Wrap(err, "查询京东订单列表失败")
}
// 收集所有Cookie ID
cookieIds := make([]string, 0, len(jdOrders))
cookieMap := make(map[string]*entity.V1JdCookieAccount)
for _, jdOrder := range jdOrders {
if jdOrder.CookieId != "" {
cookieIds = append(cookieIds, jdOrder.CookieId)
}
}
// 批量查询Cookie信息
if len(cookieIds) > 0 {
var cookies []*entity.V1JdCookieAccount
err = dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
WhereIn(dao.V1JdCookieAccount.Columns().CookieId, cookieIds).
Scan(&cookies)
if err == nil {
for _, cookie := range cookies {
cookieMap[cookie.CookieId] = cookie
}
}
}
// 转换为响应格式
list = make([]*v1.JdOrderInfo, 0, len(jdOrders))
for _, jdOrderEntity := range jdOrders {
info := &v1.JdOrderInfo{
Id: jdOrderEntity.Id,
JdOrderId: jdOrderEntity.JdOrderId,
PayId: jdOrderEntity.PayId,
Amount: gconv.Float64(jdOrderEntity.Amount),
Category: jdOrderEntity.Category,
CookieId: jdOrderEntity.CookieId,
Status: consts.JdOrderStatus(jdOrderEntity.Status),
WxPayUrl: jdOrderEntity.WxPayUrl,
WxPayExpireAt: jdOrderEntity.WxPayExpireAt,
OrderExpireAt: jdOrderEntity.OrderExpireAt,
OrderId: jdOrderEntity.OrderId,
PaidAt: jdOrderEntity.PaidAt,
CardNo: jdOrderEntity.CardNo,
CardPassword: jdOrderEntity.CardPassword,
CardExtractedAt: jdOrderEntity.CardExtractedAt,
CreatedAt: jdOrderEntity.CreatedAt,
UpdatedAt: jdOrderEntity.UpdatedAt,
DeletedAt: jdOrderEntity.DeletedAt,
}
// 设置关联的Cookie信息
if cookie, exists := cookieMap[jdOrderEntity.CookieId]; exists && cookie != nil {
info.Cookie = &v1.CookieAccountInfo{
Id: cookie.Id,
CookieId: cookie.CookieId,
AccountName: cookie.AccountName,
Status: consts.JdCookieStatus(cookie.Status),
CreatedAt: cookie.CreatedAt,
UpdatedAt: cookie.UpdatedAt,
}
}
list = append(list, info)
}
return
}
// CheckJdOrderPayment 检查京东订单支付状态
func (s *sJdCookie) CheckJdOrderPayment(ctx context.Context, jdOrderId string) (isPaid bool, paymentStatus consts.JdOrderStatus, message string, canReuse bool, err error) {
if jdOrderId == "" {
return false, 0, "京东订单号不能为空", false, gerror.New("京东订单号不能为空")
}
// 获取京东订单信息
jdOrder, err := s.getJdOrderByJdOrderId(ctx, jdOrderId)
if err != nil {
return false, 0, "查询京东订单失败", false, gerror.Wrap(err, "查询京东订单失败")
}
if jdOrder == nil {
return false, 0, "京东订单不存在", false, gerror.New(consts.ErrCodeJdOrderNotFound)
}
// 获取Cookie信息
cookieInfo, err := s.getCookieById(ctx, jdOrder.CookieId)
if err != nil || cookieInfo == nil {
return false, 0, "Cookie不存在", false, gerror.New(consts.ErrCodeCookieNotFound)
}
// 调用京东接口检查支付状态使用京东客户端返回的真实订单ID
_, err = s.callJdCheckPayment(ctx, &model.CallJdCheckPaymentReq{
RealJdOrderId: jdOrder.RealJdOrderId,
CookieValue: cookieInfo.CookieValue,
Category: consts.RedeemOrderCardCategory(jdOrder.Category),
})
if err != nil {
return false, 0, "检查支付状态失败", false, gerror.Wrap(err, "调用京东接口失败")
}
//// 更新本地订单状态
//if paymentResp.OrderStatus != jdOrder.Status {
// err = s.UpdateJdOrderStatus(ctx, jdOrderId, paymentResp.PaymentStatus, "")
// if err != nil {
// glog.Warning(ctx, "更新京东订单状态失败", err)
// }
//
// // 记录状态变更历史
// var changeType string
// if paymentResp.IsPaid {
// changeType = string(consts.JdOrderChangeTypePay)
// } else if paymentResp.PaymentStatus == int(consts.JdOrderStatusExpired) {
// changeType = string(consts.JdOrderChangeTypeExpire)
// } else {
// changeType = string(consts.JdOrderChangeTypeInvalid)
// }
//
// // 获取关联的用户订单ID
// orderId := ""
// if jdOrder.CurrentOrderId > 0 {
// var order *entity.V1JdCookieOrder
// _ = dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
// Where(dao.V1JdCookieOrder.Columns().Id, jdOrder.CurrentOrderId).
// Scan(&order)
// if order != nil {
// orderId = order.OrderId
// // 同时记录用户订单的状态变更历史
// var orderChangeType string
// if paymentResp.IsPaid {
// orderChangeType = string(consts.OrderChangeTypePay)
// } else if paymentResp.PaymentStatus == int(consts.JdOrderStatusExpired) {
// orderChangeType = string(consts.OrderChangeTypeExpire)
// }
// _ = s.RecordOrderHistory(ctx, orderId, orderChangeType, jdOrderId)
// }
// }
//
// _ = s.RecordJdOrderHistory(ctx, jdOrderId, changeType, orderId, jdOrder.WxPayUrl)
//}
//
//// 判断是否可以复用:未支付且在超时时间内
//canReuse = !paymentResp.IsPaid && paymentResp.CanReuse
//if !paymentResp.IsPaid && jdOrder.CreatedAt != nil {
// // 检查是否超过复用超时时间15分钟
// elapsedMinutes := gtime.Now().Sub(jdOrder.CreatedAt).Minutes()
// if elapsedMinutes > float64(consts.JdOrderReuseTimeout) {
// canReuse = true
// } else {
// canReuse = false
// }
//}
return false, 0, "", canReuse, nil
//return paymentResp.IsPaid, paymentResp.PaymentStatus, paymentResp.OrderStatus, canReuse, nil
}
// BatchCheckPaymentStatus 批量检查京东订单支付状态(定时任务)
func (s *sJdCookie) BatchCheckPaymentStatus(ctx context.Context) error {
glog.Info(ctx, "开始批量检查京东订单支付状态")
// 配置参数
const (
PageSize = 100 // 单页处理订单数量
)
// 筛选待检查订单
jdOrderModel := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
// 构建基础查询条件
baseQuery := jdOrderModel.
Where(dao.V1JdCookieJdOrder.Columns().Status, int(consts.JdOrderStatusPending)). // 状态为待支付
WhereGT(dao.V1JdCookieJdOrder.Columns().CreatedAt, gtime.Now().Add(-24*time.Hour)) // 24小时内创建
// 查询总数量
totalCount, err := baseQuery.Count()
if err != nil {
glog.Error(ctx, "查询待检查订单总数失败", err)
return err
}
if totalCount == 0 {
glog.Info(ctx, "没有需要检查的订单")
return nil
}
glog.Info(ctx, "找到待检查订单", g.Map{"totalCount": totalCount})
// 分页处理所有订单
page := 1
totalSuccessCount := 0
totalErrorCount := 0
totalProcessed := 0
for {
// 查询当前页的订单
var pendingOrders []*entity.V1JdCookieJdOrder
err := baseQuery.
OrderAsc(dao.V1JdCookieJdOrder.Columns().CreatedAt). // 优先处理早期订单
Page(page, PageSize).
Scan(&pendingOrders)
if err != nil {
glog.Error(ctx, "查询待检查订单失败", g.Map{
"page": page,
"error": err,
})
return err
}
if len(pendingOrders) == 0 {
// 没有更多数据,结束循环
break
}
glog.Info(ctx, "处理当前页订单", g.Map{
"page": page,
"pageSize": len(pendingOrders),
})
// 处理当前页订单
pageSuccessCount := 0
pageErrorCount := 0
for _, jdOrder := range pendingOrders {
// 检查并更新支付状态
err = s.ExtractCardInfo(ctx, jdOrder.JdOrderId)
if err != nil {
pageErrorCount++
totalErrorCount++
glog.Error(ctx, "检查订单支付状态失败", g.Map{
"jdOrderId": jdOrder.JdOrderId,
"error": err,
})
continue
}
pageSuccessCount++
totalSuccessCount++
}
totalProcessed += len(pendingOrders)
glog.Info(ctx, "当前页处理完成", g.Map{
"page": page,
"pageSize": len(pendingOrders),
"pageSuccess": pageSuccessCount,
"pageError": pageErrorCount,
"totalProcessed": totalProcessed,
"totalCount": totalCount,
})
// 如果当前页数据少于PageSize说明已经是最后一页
if len(pendingOrders) < PageSize {
break
}
page++
}
glog.Info(ctx, "批量检查完成", g.Map{
"totalCount": totalCount,
"processed": totalProcessed,
"successCount": totalSuccessCount,
"errorCount": totalErrorCount,
"processedTime": gtime.Now().Format("Y-m-d H:i:s"),
"pages": page,
})
return nil
}
// ExtractCardInfo 提取卡密信息
func (s *sJdCookie) ExtractCardInfo(ctx context.Context, jdOrderId string) error {
if jdOrderId == "" {
return gerror.New("京东订单号不能为空")
}
// 检查是否已经被锁定
cacheKey := cache.PrefixJdCardExtract.Key(jdOrderId)
cacheValue, _ := cache.NewCache().Get(ctx, cacheKey)
if cacheValue != nil && !cacheValue.IsNil() {
glog.Debug(ctx, "卡密提取正在进行中,跳过", g.Map{"jdOrderId": jdOrderId})
return nil
}
// 设置锁定缓存,防止重复提取
_ = cache.NewCache().Set(ctx, cacheKey, gtime.Now().String(), 1*time.Minute)
defer func() {
_, _ = cache.NewCache().Remove(ctx, cacheKey)
}()
// 获取京东订单信息
jdOrder, err := s.getJdOrderByJdOrderId(ctx, jdOrderId)
if err != nil {
return gerror.Wrap(err, "查询京东订单失败")
}
if jdOrder == nil {
return gerror.New(consts.ErrCodeJdOrderNotFound)
}
// 检查是否已经提取过卡密
if jdOrder.CardNo != "" || jdOrder.CardPassword != "" {
glog.Info(ctx, "卡密已经提取,跳过", g.Map{"jdOrderId": jdOrderId})
return nil
}
// 检查订单状态是否允许提取卡密
if !s.shouldExtractCard(ctx, jdOrder) {
glog.Debug(ctx, "订单状态不允许提取卡密", g.Map{
"jdOrderId": jdOrderId,
"status": jdOrder,
})
return nil
}
// 获取Cookie信息
cookieInfo, err := s.getCookieById(ctx, jdOrder.CookieId)
if err != nil || cookieInfo == nil {
return gerror.New(consts.ErrCodeCookieNotFound)
}
resp, err := s.callJdCheckPayment(ctx, &model.CallJdCheckPaymentReq{
JdOrderId: jdOrder.JdOrderId,
RealJdOrderId: jdOrder.RealJdOrderId,
CookieValue: cookieInfo.CookieValue,
Category: consts.RedeemOrderCardCategory(jdOrder.Category),
})
if err != nil {
if resp != nil {
if resp.IsCkFailed {
s.handleCookieFailure(ctx, cookieInfo.CookieId, jdOrderId, true, resp.Remark)
_ = s.UpdateJdOrderStatus(ctx, jdOrderId, consts.JdOrderStatusCkFailed, "", resp.Remark)
_, _ = dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1JdCookieOrder.Columns().OrderId, jdOrder.OrderId).
Update(do.V1JdCookieOrder{
Status: consts.OrderStatusCkFailed,
})
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypeCkFailed, jdOrderId)
}
}
return gerror.Wrap(err, "查询京东订单支付状态失败")
}
if !slices.Contains([]string{"待使用", "待发送"}, resp.OrderStatus) {
return nil
}
if resp.OrderStatus == "等待发码" && jdOrder.Status != int(consts.JdOrderStatusPaid) {
// 保存卡密信息到数据库
_, err = dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).
Update(do.V1JdCookieJdOrder{
PaidAt: gtime.Now(),
Status: consts.JdOrderStatusPaid,
})
if jdOrder.OrderId != "" {
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypePay, jdOrder.OrderId, jdOrder.WxPayUrl, "")
_, _ = dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1JdCookieOrder.Columns().OrderId, jdOrder.OrderId).
Update(do.V1JdCookieOrder{
Status: consts.OrderStatusPaid,
})
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypePay, jdOrderId)
}
return nil
}
if resp.CardNo == "" || resp.CardPassword == "" {
return gerror.New("获取卡密信息为空")
}
affected, _ := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1JdCookieOrder.Columns().OrderId, jdOrder.OrderId).
WhereNot(dao.V1JdCookieOrder.Columns().Status, consts.OrderStatusPaid).
UpdateAndGetAffected(do.V1JdCookieOrder{
Status: consts.OrderStatusPaid,
})
if affected > 0 {
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypePay, jdOrderId)
}
// 保存卡密信息到数据库
_, err = dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1JdCookieJdOrder.Columns().JdOrderId, jdOrderId).
Update(do.V1JdCookieJdOrder{
Status: consts.JdOrderStatusSent,
CardNo: resp.CardNo,
CardPassword: resp.CardPassword,
CardExtractedAt: gtime.Now(),
})
if err != nil {
return gerror.Wrap(err, "保存卡密信息失败")
}
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeSend, jdOrder.OrderId, jdOrder.WxPayUrl, "")
//提取成功要回调
go s.Callback(ctx, jdOrder.OrderId)
glog.Info(ctx, "卡密提取成功", g.Map{
"jdOrderId": jdOrderId,
"cardNo": resp.CardNo,
"orderId": jdOrder.OrderId,
})
return nil
}
// Callback TODO:临时的回调
func (s *sJdCookie) Callback(ctx context.Context, orderId string) {
var order *entity.V1JdCookieOrder
if err := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Scan(&order); err != nil || order == nil || order.Id == 0 {
glog.Error(ctx, "查询订单失败", g.Map{"orderId": orderId, "err": err})
return
}
var data *entity.V1OrderInfo
if err := dao.V1OrderInfo.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1OrderInfo.Columns().BankOrderId, order.UserOrderId).Scan(&data); err != nil || data == nil || data.Id == 0 {
glog.Error(ctx, "查询订单失败", g.Map{"userOrderId": order.UserOrderId, "err": err})
return
}
response, _ := gclient.New().Get(ctx, "http://kami_gateway:12309/appleCard/notify", g.Map{
"attach": data.BankOrderId,
"merchantId": orderId,
"amount": order.Amount,
"status": "1",
"sign": "123456",
})
glog.Info(ctx, "回调成功", g.Map{"response": response.ReadAllString()})
}
// shouldExtractCard 判断是否需要提取卡密
func (s *sJdCookie) shouldExtractCard(ctx context.Context, jdOrder *entity.V1JdCookieJdOrder) bool {
if jdOrder == nil {
return false
}
// 检查是否已经提取过卡密
if jdOrder.CardNo != "" || jdOrder.CardPassword != "" {
return false
}
// 检查订单状态:必须是已支付或待支付
if jdOrder.Status != int(consts.JdOrderStatusPending) && jdOrder.Status != int(consts.JdOrderStatusPaid) {
return false
}
return true
}
// CleanupExpiredOrders 清理过期订单(定时任务)
func (s *sJdCookie) CleanupExpiredOrders(ctx context.Context) error {
// 清理过期的京东订单
jdOrderModel := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
// 先查询即将过期的京东订单,用于记录历史
var expiredJdOrders []*entity.V1JdCookieJdOrder
err := jdOrderModel.
Where(dao.V1JdCookieJdOrder.Columns().Status, int(consts.JdOrderStatusPending)).
WhereLT(dao.V1JdCookieJdOrder.Columns().OrderExpireAt, gtime.Now()).
Scan(&expiredJdOrders)
if err != nil {
glog.Error(ctx, "查询过期京东订单失败", err)
return err
}
// 批量更新过期状态
_, err = jdOrderModel.
Where(dao.V1JdCookieJdOrder.Columns().Status, int(consts.JdOrderStatusPending)).
WhereLT(dao.V1JdCookieJdOrder.Columns().OrderExpireAt, gtime.Now()).
Update(g.Map{
dao.V1JdCookieJdOrder.Columns().Status: int(consts.JdOrderStatusExpired),
})
if err != nil {
glog.Error(ctx, "清理过期京东订单失败", err)
return err
}
// 为每个过期的京东订单记录历史
for _, jdOrder := range expiredJdOrders {
orderId := jdOrder.OrderId
if jdOrder.OrderId != "" {
// 同时记录用户订单的历史
_ = s.RecordOrderHistory(ctx, orderId, consts.OrderChangeTypeExpire, jdOrder.JdOrderId)
}
_ = s.RecordJdOrderHistory(ctx, jdOrder.JdOrderId, consts.JdOrderChangeTypeExpire, orderId, jdOrder.WxPayUrl, "")
}
// 清理过期的用户订单
orderModel := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
// 查询即将过期的用户订单
var expiredOrders []*entity.V1JdCookieOrder
err = orderModel.
Where(dao.V1JdCookieOrder.Columns().Status, int(consts.OrderStatusPending)).
WhereLT(dao.V1JdCookieOrder.Columns().CreatedAt, gtime.Now().Add(-time.Hour*24)).
Scan(&expiredOrders)
if err != nil {
glog.Error(ctx, "查询过期用户订单失败", err)
return err
}
// 批量更新过期状态
_, err = orderModel.
Where(dao.V1JdCookieOrder.Columns().Status, int(consts.OrderStatusPending)).
WhereLT(dao.V1JdCookieOrder.Columns().CreatedAt, gtime.Now().Add(-time.Hour*24)).
Update(do.V1JdCookieJdOrder{
Status: consts.OrderStatusExpired,
})
if err != nil {
glog.Error(ctx, "清理过期用户订单失败", err)
return err
}
// 为每个过期的用户订单记录历史
for _, order := range expiredOrders {
_ = s.RecordOrderHistory(ctx, order.OrderId, consts.OrderChangeTypeExpire, order.JdOrderId)
}
glog.Info(ctx, "清理过期订单完成", g.Map{
"expiredJdOrderCount": len(expiredJdOrders),
"expiredOrderCount": len(expiredOrders),
})
return nil
}
// ReleaseExpiredJdOrders 释放过期京东订单的关联(使其可以被复用)
func (s *sJdCookie) ReleaseExpiredJdOrders(ctx context.Context) error {
// 查找超过30分钟未支付的京东订单
expireTime := gtime.Now().Add(-time.Minute * consts.JdOrderReuseTimeout)
// 释放这些订单的关联
jdOrderModel := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
_, err := jdOrderModel.
Where(dao.V1JdCookieJdOrder.Columns().Status, int(consts.JdOrderStatusPending)).
WhereNotNull(dao.V1JdCookieJdOrder.Columns().OrderId).
WhereLT(dao.V1JdCookieJdOrder.Columns().CreatedAt, expireTime).
Update(do.V1JdCookieJdOrder{
OrderId: nil,
})
if err != nil {
glog.Error(ctx, "释放过期京东订单关联失败", err)
return err
}
return nil
}
// ExportJdOrder 导出京东订单数据为Excel
func (s *sJdCookie) ExportJdOrder(ctx context.Context, status consts.JdOrderStatus, startTime, endTime, orderId string) (fileName string, content []byte, err error) {
// 构建查询条件
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
if status > 0 {
m = m.Where(dao.V1JdCookieJdOrder.Columns().Status, int(status))
}
if startTime != "" {
m = m.WhereGTE(dao.V1JdCookieJdOrder.Columns().CreatedAt, startTime)
}
if endTime != "" {
m = m.WhereLTE(dao.V1JdCookieJdOrder.Columns().CreatedAt, endTime)
}
if orderId != "" {
// 直接根据order_id字段筛选
m = m.Where(dao.V1JdCookieJdOrder.Columns().OrderId, orderId)
}
// 查询所有符合条件的订单
var jdOrders []*entity.V1JdCookieJdOrder
err = m.OrderDesc(dao.V1JdCookieJdOrder.Columns().CreatedAt).Scan(&jdOrders)
if err != nil {
return "", nil, gerror.Wrap(err, "查询京东订单列表失败")
}
// 收集所有Cookie ID
cookieIds := make([]string, 0, len(jdOrders))
cookieMap := make(map[string]*entity.V1JdCookieAccount)
for _, jdOrder := range jdOrders {
if jdOrder.CookieId != "" {
cookieIds = append(cookieIds, jdOrder.CookieId)
}
}
// 批量查询Cookie信息
if len(cookieIds) > 0 {
var cookies []*entity.V1JdCookieAccount
err = dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
WhereIn(dao.V1JdCookieAccount.Columns().CookieId, cookieIds).
Scan(&cookies)
if err == nil {
for _, cookie := range cookies {
cookieMap[cookie.CookieId] = cookie
}
}
}
// 创建Excel文件
f := excelize.NewFile()
defer func() {
if err := f.Close(); err != nil {
glog.Error(ctx, "关闭Excel文件失败", err)
}
}()
// 设置工作表名称
sheetName := "京东订单数据"
f.SetSheetName("Sheet1", sheetName)
// 设置表头
headers := []string{"京东订单号", "用户订单号", "卡号", "卡密", "ck名称", "京东订单状态"}
for i, header := range headers {
cell := fmt.Sprintf("%s1", string(rune('A'+i)))
f.SetCellValue(sheetName, cell, header)
}
// 填充数据
for i, jdOrder := range jdOrders {
row := i + 2
// 京东订单号
f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), jdOrder.JdOrderId)
// 用户订单号
f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), jdOrder.OrderId)
// 卡号
f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), jdOrder.CardNo)
// 卡密
f.SetCellValue(sheetName, fmt.Sprintf("D%d", row), jdOrder.CardPassword)
// ck名称
cookieName := ""
if cookie, exists := cookieMap[jdOrder.CookieId]; exists && cookie != nil {
cookieName = cookie.AccountName
}
f.SetCellValue(sheetName, fmt.Sprintf("E%d", row), cookieName)
// 京东订单状态
statusText := s.getJdOrderStatusText(consts.JdOrderStatus(jdOrder.Status))
f.SetCellValue(sheetName, fmt.Sprintf("F%d", row), statusText)
}
// 生成文件名
fileName = fmt.Sprintf("京东订单数据_%s.xlsx", gtime.Now().Format("Y-m-d_H-i-s"))
// 保存到字节数组
buf, err := f.WriteToBuffer()
if err != nil {
return "", nil, gerror.Wrap(err, "生成Excel文件失败")
}
return fileName, buf.Bytes(), nil
}
// createEmptyExcel 创建空的Excel文件
func (s *sJdCookie) createEmptyExcel(ctx context.Context) (string, []byte, error) {
f := excelize.NewFile()
defer func() {
if err := f.Close(); err != nil {
glog.Error(ctx, "关闭Excel文件失败", err)
}
}()
// 设置工作表名称
sheetName := "京东订单数据"
f.SetSheetName("Sheet1", sheetName)
// 设置表头
headers := []string{"京东订单号", "用户订单号", "卡号", "卡密", "ck名称", "京东订单状态"}
for i, header := range headers {
cell := fmt.Sprintf("%s1", string(rune('A'+i)))
f.SetCellValue(sheetName, cell, header)
}
// 生成文件名
fileName := fmt.Sprintf("京东订单数据_%s.xlsx", gtime.Now().Format("Y-m-d_H-i-s"))
// 保存到字节数组
buf, err := f.WriteToBuffer()
if err != nil {
return "", nil, gerror.Wrap(err, "生成Excel文件失败")
}
return fileName, buf.Bytes(), nil
}
// getJdOrderStatusText 获取京东订单状态文本
func (s *sJdCookie) getJdOrderStatusText(status consts.JdOrderStatus) string {
switch status {
case consts.JdOrderStatusPending:
return "待支付"
case consts.JdOrderStatusPaid:
return "已支付"
case consts.JdOrderStatusSent:
return "已发货"
case consts.JdOrderStatusExpired:
return "已过期"
case consts.JdOrderStatusCanceled:
return "已取消"
default:
return "未知状态"
}
}