fix(pool): 优化重试次数及订单绑定逻辑

- 将最大重试次数从3次调整为5次,提高任务处理容错能力
- 限制核弹卡发送任务循环次数从5次减少到3次,避免过度重试
- 增加用户订单重试次数计数,超过最大重试立即返回失败
- 修正redis订单数据结构变量名,确保数据一致性
- 优化绑定订单ID和卡信息ID失败后的重试和日志处理
- 支持wtr支付接口动态设置请求URL,增加灵活性
- 移除main.go中http性能分析监听,减少无用服务运行
- 修改wtr支付测试用例渠道号及接口地址,便于测试调试
- 统一日志打印规范,提升调试体验
This commit is contained in:
danial
2025-12-16 01:26:53 +08:00
parent 37463857c0
commit fe8b732da4
7 changed files with 39 additions and 28 deletions

View File

@@ -266,7 +266,7 @@ func (s *SendCardTaskTypeNuclear) channelOne(ctx context.Context, orderItem Orde
time.Sleep(time.Second*30 - time.Since(orderItem.CreateTime)) time.Sleep(time.Second*30 - time.Since(orderItem.CreateTime))
} }
for range 5 { for range 3 {
needChangeProxyId := utils.GenerateId() needChangeProxyId := utils.GenerateId()
webClient := resty.New().SetTimeout(10 * time.Second).SetHeaders(map[string]string{ webClient := resty.New().SetTimeout(10 * time.Second).SetHeaders(map[string]string{
"user-agent": useragent.GetUserAgentByPlatform(useragent.PlatformPhone), "user-agent": useragent.GetUserAgentByPlatform(useragent.PlatformPhone),

View File

@@ -49,12 +49,16 @@ func (s *SendCardTaskTypeWtr) CreateOrder(ctx context.Context, roadUid string, f
"nonceStr": utils.GenerateId(), "nonceStr": utils.GenerateId(),
} }
formData["sign"] = (&SendCardTaskTypeWtr{}).sign(ctx, formData, gojson.Json(roadInfo.Params).Get("key").Tostring()) formData["sign"] = s.sign(ctx, formData, gojson.Json(roadInfo.Params).Get("key").Tostring())
webClient := resty.New().EnableTrace().SetTimeout(time.Second * 5).SetRetryCount(1). webClient := resty.New().EnableTrace().SetTimeout(time.Second * 5).SetRetryCount(1).
SetFormData(formData) SetFormData(formData)
otelresty.TraceClient(webClient) otelresty.TraceClient(webClient)
createdUrl := "https://api.wtrpay.xyz/pay"
if gojson.Json(roadInfo.Params).Get("createdurl").Tostring() != "" {
createdUrl = gojson.Json(roadInfo.Params).Get("createdUrl").Tostring()
}
response, err := webClient.R().SetContext(ctx).Post("https://api.wtrpay.xyz/pay") response, err := webClient.R().SetContext(ctx).Post(createdUrl)
if err != nil { if err != nil {
otelTrace.Logger.WithContext(ctx).Error("下单失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("下单失败", zap.Error(err))

View File

@@ -16,7 +16,7 @@ func TestSendCardTaskTypeWtr_CreateOrder(t *testing.T) {
"merId": "20250967", "merId": "20250967",
"orderId": utils.GenerateId()[:16], "orderId": utils.GenerateId()[:16],
"orderAmt": "30", "orderAmt": "30",
"channel": "923", "channel": "968",
"desc": "这是个标题", "desc": "这是个标题",
"ip": utils.GenerateIpv4(), "ip": utils.GenerateIpv4(),
"notifyUrl": "https://www.showdoc.com.cn/api/notify", "notifyUrl": "https://www.showdoc.com.cn/api/notify",
@@ -26,7 +26,7 @@ func TestSendCardTaskTypeWtr_CreateOrder(t *testing.T) {
formData["sign"] = (&SendCardTaskTypeUp{}).sign(t.Context(), formData, "otLhjzAKvIYOQisJgHxEdVFZaTUclmMb") formData["sign"] = (&SendCardTaskTypeUp{}).sign(t.Context(), formData, "otLhjzAKvIYOQisJgHxEdVFZaTUclmMb")
response, err := resty.New().EnableTrace().SetTimeout(time.Second * 5).SetRetryCount(3). response, err := resty.New().EnableTrace().SetTimeout(time.Second * 5).SetRetryCount(3).
SetFormData(formData).R().Post("https://alsopay.wtrpay.xyz/pay") SetFormData(formData).R().Post("http://47.76.69.194:26895/api/pay")
responseStruct := struct { responseStruct := struct {
Code int64 `json:"code"` Code int64 `json:"code"`
Msg string `json:"msg"` Msg string `json:"msg"`

View File

@@ -43,7 +43,7 @@ func DefaultConfig() *Config {
MaxFaceValueConcurrency: 100, // 每个面值最多20个并发 MaxFaceValueConcurrency: 100, // 每个面值最多20个并发
MatchOrderTimeout: 2 * time.Minute, // 订单匹配超时时间30秒 MatchOrderTimeout: 2 * time.Minute, // 订单匹配超时时间30秒
MaxRetryCount: 3, // 最大重试3 MaxRetryCount: 5, // 最大重试5
RetryInterval: 1 * time.Second, // 重试间隔1秒 RetryInterval: 1 * time.Second, // 重试间隔1秒
} }
} }

View File

@@ -449,8 +449,8 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
) )
// 获取用户订单 // 获取用户订单
var task card_sender.SendCardTask var customerTask card_sender.SendCardTask
if err = s.redisClient.LPopUnmarshal(ctx, customerKey, &task); err != nil { if err = s.redisClient.LPopUnmarshal(ctx, customerKey, &customerTask); err != nil {
otelTrace.Logger.WithContext(ctx).Error("获取用户订单失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("获取用户订单失败", zap.Error(err))
// 获取用户订单失败时,将生产订单重新放回池中 // 获取用户订单失败时,将生产订单重新放回池中
if err = s.redisClient.LPush(ctx, produceKey, produceOrderItem); err != nil { if err = s.redisClient.LPush(ctx, produceKey, produceOrderItem); err != nil {
@@ -459,18 +459,28 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
return return
} }
span.SetAttributes(attribute.String("bankOrderId", task.LocalOrderID)) span.SetAttributes(attribute.String("bankOrderId", customerTask.LocalOrderID))
span.AddEvent("acquire user order") span.AddEvent("acquire user order")
if task.SendCardTaskType == "" { if customerTask.SendCardTaskType == "" {
task.SendCardTaskType = channel customerTask.SendCardTaskType = channel
}
if customerTask.DispatchCount > s.config.MaxRetryCount {
otelTrace.Logger.WithContext(ctx).Error("用户订单已重试次数过多", zap.String("customerKey", customerKey),
zap.Any("customerTask", customerTask), zap.Int("DispatchCount", customerTask.DispatchCount),
)
if err = sendCardTaskType.BindOrderFailed(ctx, produceOrderItem, customerTask, "提交失败,重试失败"); err != nil {
otelTrace.Logger.WithContext(ctx).Error("绑定失败", zap.Error(err))
}
return
} }
// 绑定订单ID和卡片信息ID // 绑定订单ID和卡片信息ID
bindKey := fmt.Sprintf("%s:%s", s.config.OrderBindKey, produceOrderItem.OrderID) bindKey := fmt.Sprintf("%s:%s", s.config.OrderBindKey, produceOrderItem.OrderID)
if err = s.redisClient.Set(ctx, bindKey, task.LocalOrderID, s.config.OrderBindKeyActiveTime); err != nil { if err = s.redisClient.Set(ctx, bindKey, customerTask.LocalOrderID, s.config.OrderBindKeyActiveTime); err != nil {
otelTrace.Logger.WithContext(ctx).Error("绑定订单ID和卡片信息ID失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("绑定订单ID和卡片信息ID失败", zap.Error(err))
// 如果绑定失败,将订单重新放回池中 // 如果绑定失败,将订单重新放回池中
if err = s.redisClient.RPush(ctx, customerKey, task); err != nil { if err = s.redisClient.RPush(ctx, customerKey, customerTask); err != nil {
otelTrace.Logger.WithContext(ctx).Error("用户订单重新入池失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("用户订单重新入池失败", zap.Error(err))
} }
if err = s.redisClient.RPush(ctx, produceKey, produceOrderItem); err != nil { if err = s.redisClient.RPush(ctx, produceKey, produceOrderItem); err != nil {
@@ -479,7 +489,7 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
return return
} }
if err = sendCardTaskType.BindPoolOrderId(ctx, produceOrderItem, task); err != nil { if err = sendCardTaskType.BindPoolOrderId(ctx, produceOrderItem, customerTask); err != nil {
otelTrace.Logger.WithContext(ctx).Error("绑定订单ID和卡片信息ID失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("绑定订单ID和卡片信息ID失败", zap.Error(err))
return return
} }
@@ -496,11 +506,11 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
// 处理发卡任务,带重试机制 // 处理发卡任务,带重试机制
var retryCount int var retryCount int
for retryCount < s.config.MaxRetryCount { for retryCount < s.config.MaxRetryCount {
err = sendCardTaskType.HandleSendCardTask(ctx, produceOrderItem, task) err = sendCardTaskType.HandleSendCardTask(ctx, produceOrderItem, customerTask)
if err == nil { if err == nil {
// 如果需要查询,发布查询事件 // 如果需要查询,发布查询事件
if task.NeedQuery { if customerTask.NeedQuery {
queryEvent := &OrderQueryEvent{ queryEvent := &OrderQueryEvent{
BaseEvent: BaseEvent{ BaseEvent: BaseEvent{
eventType: EventTypeOrderQuery, eventType: EventTypeOrderQuery,
@@ -508,8 +518,8 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
}, },
RoadUid: roadUid, RoadUid: roadUid,
OrderID: produceOrderItem.OrderID, OrderID: produceOrderItem.OrderID,
LocalOrderID: task.LocalOrderID, LocalOrderID: customerTask.LocalOrderID,
Channel: task.SendCardTaskType, Channel: customerTask.SendCardTaskType,
QueryCount: 0, QueryCount: 0,
LastQueryTime: time.Now(), LastQueryTime: time.Now(),
RemoteOrderID: produceOrderItem.RemoteOrderID, RemoteOrderID: produceOrderItem.RemoteOrderID,
@@ -533,10 +543,11 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
continue continue
} }
if strings.Contains(err.Error(), "重新下单提交卡密") { if strings.Contains(err.Error(), "重新下单") {
// 如果绑定失败,将订单重新放回池中 // 如果绑定失败,将订单重新放回池中
if err = s.redisClient.LPush(ctx, customerKey, task); err != nil { customerTask.DispatchCount += 1
_ = s.unboundPoolOrderId(ctx, task.SendCardTaskType, produceOrderItem, task) if err = s.redisClient.LPush(ctx, customerKey, customerTask); err != nil {
_ = s.unboundPoolOrderId(ctx, customerTask.SendCardTaskType, produceOrderItem, customerTask)
otelTrace.Logger.WithContext(ctx).Error("用户订单重新入池失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("用户订单重新入池失败", zap.Error(err))
} }
return return
@@ -547,7 +558,7 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
if err != nil { if err != nil {
errMsg = err.Error() errMsg = err.Error()
} }
if err = sendCardTaskType.BindOrderFailed(ctx, produceOrderItem, task, errMsg); err != nil { if err = sendCardTaskType.BindOrderFailed(ctx, produceOrderItem, customerTask, errMsg); err != nil {
otelTrace.Logger.WithContext(ctx).Error("绑定失败", zap.Error(err)) otelTrace.Logger.WithContext(ctx).Error("绑定失败", zap.Error(err))
} }
} }
@@ -580,7 +591,7 @@ func (s *OrderPoolServiceImpl) SubmitOrder(ctx context.Context, task card_sender
var err error var err error
var orderItem card_sender.OrderPoolItem var orderItem card_sender.OrderPoolItem
maxRetries := 3 maxRetries := 5
for attempt := 0; attempt < maxRetries; attempt++ { for attempt := 0; attempt < maxRetries; attempt++ {
span.AddEvent("attempt_start", trace.WithAttributes(attribute.Int("attempt", attempt+1))) span.AddEvent("attempt_start", trace.WithAttributes(attribute.Int("attempt", attempt+1)))

View File

@@ -135,7 +135,7 @@ func (f *WtrImpl) PayNotify() {
f.Ctx.WriteString("fail") f.Ctx.WriteString("fail")
return return
} }
otelTrace.Logger.WithContext(ctx).Info("wtr回调", zap.Any("resp", resp)) otelTrace.Logger.WithContext(ctx).Info("回调", zap.Any("resp", resp))
localId, err := orderPoolService.GetLocalIdByOrderId(ctx, resp.OrderId) localId, err := orderPoolService.GetLocalIdByOrderId(ctx, resp.OrderId)
var orderInfo order.OrderInfo var orderInfo order.OrderInfo
if err != nil { if err != nil {

View File

@@ -18,7 +18,6 @@ import (
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"log" "log"
"net/http"
_ "net/http/pprof" _ "net/http/pprof"
"time" "time"
) )
@@ -30,9 +29,6 @@ func main() {
log.Printf("初始化代理池失败: %v", err) log.Printf("初始化代理池失败: %v", err)
return return
} }
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
// 初始化 OpenTelemetry // 初始化 OpenTelemetry
cleanup1, cleanup2, cleanup3 := otelTrace.InitTracer() cleanup1, cleanup2, cleanup3 := otelTrace.InitTracer()
defer func() { defer func() {