Files
kami_backend/internal/logic/jd_cookie/order_jd.go
danial 2253dc739a feat(jd-cookie):优化订单创建逻辑与状态管理- 新增订单状态 OrderStatusJDOrderFailed用于标识京东订单获取失败
- 新增订单变更类型 OrderChangeTypeJDOrderFailed 用于记录下单失败事件
- 调整订单创建逻辑,支持失败订单重试机制
- 新增 RecordOrderHistoryReq 结构体统一记录订单变更历史参数
- 修改数据库表结构,优化字段类型和索引
- 更新订单创建逻辑,分离本地订单与京东订单创建流程- 增加失败订单重新创建京东订单的处理逻辑
- 调整订单状态检查逻辑,支持更多状态处理
-优化订单历史记录方式,增加备注信息支持
- 更新数据库字符集为 utf8mb4_unicode_ci 提升兼容性
2025-10-18 23:41:31 +08:00

841 lines
26 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, &model.RecordOrderHistoryReq{
OrderId: jdOrder.OrderId,
ChangeType: consts.OrderChangeTypeCkFailed,
JdOrderId: jdOrderId,
Remark: "",
})
}
}
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, &model.RecordOrderHistoryReq{
OrderId: jdOrder.OrderId,
ChangeType: consts.OrderChangeTypePay,
JdOrderId: jdOrderId,
Remark: "",
})
}
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, &model.RecordOrderHistoryReq{
OrderId: jdOrder.OrderId,
ChangeType: consts.OrderChangeTypePay,
JdOrderId: jdOrderId,
Remark: "",
})
}
// 保存卡密信息到数据库
_, 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, &model.RecordOrderHistoryReq{
OrderId: orderId,
ChangeType: consts.OrderChangeTypeExpire,
JdOrderId: jdOrder.JdOrderId,
Remark: "",
})
}
_ = 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, &model.RecordOrderHistoryReq{
OrderId: order.OrderId,
ChangeType: consts.OrderChangeTypeExpire,
JdOrderId: order.JdOrderId,
Remark: "",
})
}
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 "未知状态"
}
}