- 新增 CreateOrderReq 结构体用于统一订单创建参数- 修改 CreateOrder 方法签名,使用结构体传参替代多个参数 - 更新 jd_cookie 相关枚举值,增加 JdCookieStatusUnknown 状态 - 调整 OrderInfo 和 JdOrderInfo 模型字段,增强数据一致性 -优化订单与京东订单关联逻辑,移除冗余的 CurrentOrderId 字段 - 移除 ShouldExtractCard 方法,改为内部私有方法 shouldExtractCard- 精简 Callback 方法参数,移除不必要的 userOrderId 和 amount 参数 - 修复订单历史记录中订单号关联问题,直接使用 orderId 字段查询 - 更新控制器层参数传递方式,适配新的服务层接口定义 - 调整卡密提取逻辑,去除对用户订单实体的依赖 - 完善订单状态检查机制,提高卡密提取安全性 - 优化数据库查询逻辑,减少不必要的关联查询操作
816 lines
25 KiB
Go
816 lines
25 KiB
Go
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 "未知状态"
|
||
}
|
||
}
|