275 lines
7.1 KiB
Go
275 lines
7.1 KiB
Go
package queue
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dujiao-next/internal/config"
|
|
"github.com/dujiao-next/internal/constants"
|
|
|
|
"github.com/hibiken/asynq"
|
|
)
|
|
|
|
const (
|
|
// DefaultQueue 默认队列名称
|
|
DefaultQueue = constants.QueueDefault
|
|
)
|
|
|
|
// Client 队列客户端封装
|
|
type Client struct {
|
|
client *asynq.Client
|
|
enabled bool
|
|
defaultQueue string
|
|
}
|
|
|
|
// NewClient 创建队列客户端
|
|
func NewClient(cfg *config.QueueConfig) (*Client, error) {
|
|
if cfg == nil || !cfg.Enabled {
|
|
return &Client{enabled: false, defaultQueue: DefaultQueue}, nil
|
|
}
|
|
opt := buildRedisOpt(cfg)
|
|
client := asynq.NewClient(opt)
|
|
return &Client{
|
|
client: client,
|
|
enabled: true,
|
|
defaultQueue: DefaultQueue,
|
|
}, nil
|
|
}
|
|
|
|
// Enabled 判断是否启用
|
|
func (c *Client) Enabled() bool {
|
|
return c != nil && c.enabled && c.client != nil
|
|
}
|
|
|
|
// Close 关闭客户端
|
|
func (c *Client) Close() error {
|
|
if c == nil || c.client == nil {
|
|
return nil
|
|
}
|
|
return c.client.Close()
|
|
}
|
|
|
|
// EnqueueOrderStatusEmail 推送订单状态邮件任务
|
|
func (c *Client) EnqueueOrderStatusEmail(payload OrderStatusEmailPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewOrderStatusEmailTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 限制重试次数与保留期,避免 SMTP 长时间不可用时邮件任务堆积成雪崩;
|
|
// 调用方可通过 opts 覆盖。
|
|
options := append([]asynq.Option{
|
|
asynq.Queue(c.defaultQueue),
|
|
asynq.MaxRetry(3),
|
|
asynq.Retention(24 * time.Hour),
|
|
}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueOrderAutoFulfill 推送自动交付任务
|
|
func (c *Client) EnqueueOrderAutoFulfill(payload OrderAutoFulfillPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewOrderAutoFulfillTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueAffiliateOrderPaid 推送佣金创建重试任务
|
|
func (c *Client) EnqueueAffiliateOrderPaid(payload AffiliateOrderPaidPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewAffiliateOrderPaidTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueOrderTimeoutCancel 推送订单超时取消任务
|
|
func (c *Client) EnqueueOrderTimeoutCancel(payload OrderTimeoutCancelPayload, delay time.Duration) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
if delay < 0 {
|
|
delay = 0
|
|
}
|
|
task, err := NewOrderTimeoutCancelTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := []asynq.Option{asynq.Queue(c.defaultQueue), asynq.ProcessIn(delay)}
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueWalletRechargeExpire 推送钱包充值超时过期任务
|
|
func (c *Client) EnqueueWalletRechargeExpire(payload WalletRechargeExpirePayload, delay time.Duration) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
if delay < 0 {
|
|
delay = 0
|
|
}
|
|
task, err := NewWalletRechargeExpireTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := []asynq.Option{asynq.Queue(c.defaultQueue), asynq.ProcessIn(delay)}
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueNotificationDispatch 推送通知中心分发任务
|
|
func (c *Client) EnqueueNotificationDispatch(payload NotificationDispatchPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewNotificationDispatchTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueProcurementSubmit 推送采购提交任务
|
|
func (c *Client) EnqueueProcurementSubmit(payload ProcurementSubmitPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewProcurementSubmitTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 采购单服务自行管理重试逻辑,asynq 仅处理瞬态错误(DB/Redis 不可达等)
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue), asynq.MaxRetry(3)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueProcurementPollStatus 推送采购状态轮询任务
|
|
func (c *Client) EnqueueProcurementPollStatus(payload ProcurementPollStatusPayload, delay time.Duration) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
if delay < 0 {
|
|
delay = 0
|
|
}
|
|
task, err := NewProcurementPollStatusTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := []asynq.Option{asynq.Queue(c.defaultQueue), asynq.ProcessIn(delay)}
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueDownstreamCallback 推送下游回调通知任务
|
|
func (c *Client) EnqueueDownstreamCallback(payload DownstreamCallbackPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewDownstreamCallbackTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueReconciliationRun 入队对账执行任务
|
|
func (c *Client) EnqueueReconciliationRun(payload ReconciliationRunPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewReconciliationRunTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueBotNotify 入队 Bot 交付通知任务
|
|
func (c *Client) EnqueueBotNotify(payload BotNotifyPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewBotNotifyTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue), asynq.MaxRetry(5)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// EnqueueTelegramBroadcast 入队 Telegram 群发任务。
|
|
func (c *Client) EnqueueTelegramBroadcast(payload TelegramBroadcastPayload, opts ...asynq.Option) error {
|
|
if !c.Enabled() {
|
|
return nil
|
|
}
|
|
task, err := NewTelegramBroadcastTask(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options := append([]asynq.Option{asynq.Queue(c.defaultQueue), asynq.MaxRetry(3)}, opts...)
|
|
_, err = c.client.Enqueue(task, options...)
|
|
return err
|
|
}
|
|
|
|
// BuildServerConfig 生成队列服务配置
|
|
func BuildServerConfig(cfg *config.QueueConfig) (asynq.RedisClientOpt, asynq.Config) {
|
|
opt := buildRedisOpt(cfg)
|
|
concurrency := 10
|
|
if cfg != nil && cfg.Concurrency > 0 {
|
|
concurrency = cfg.Concurrency
|
|
}
|
|
queues := map[string]int{DefaultQueue: 1}
|
|
if cfg != nil && len(cfg.Queues) > 0 {
|
|
queues = cfg.Queues
|
|
}
|
|
return opt, asynq.Config{
|
|
Concurrency: concurrency,
|
|
Queues: queues,
|
|
}
|
|
}
|
|
|
|
func buildRedisOpt(cfg *config.QueueConfig) asynq.RedisClientOpt {
|
|
host := "127.0.0.1"
|
|
port := 6379
|
|
password := ""
|
|
db := 0
|
|
if cfg != nil {
|
|
if strings.TrimSpace(cfg.Host) != "" {
|
|
host = strings.TrimSpace(cfg.Host)
|
|
}
|
|
if cfg.Port > 0 {
|
|
port = cfg.Port
|
|
}
|
|
password = cfg.Password
|
|
db = cfg.DB
|
|
}
|
|
return asynq.RedisClientOpt{
|
|
Addr: fmt.Sprintf("%s:%d", host, port),
|
|
Password: password,
|
|
DB: db,
|
|
}
|
|
}
|