add article
This commit is contained in:
58
internal/infrastructure/task/article_task_handler.go
Normal file
58
internal/infrastructure/task/article_task_handler.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ArticlePublisher 文章发布接口
|
||||
type ArticlePublisher interface {
|
||||
PublishArticleByID(ctx context.Context, articleID string) error
|
||||
}
|
||||
|
||||
// ArticleTaskHandler 文章任务处理器
|
||||
type ArticleTaskHandler struct {
|
||||
publisher ArticlePublisher
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewArticleTaskHandler 创建文章任务处理器
|
||||
func NewArticleTaskHandler(
|
||||
publisher ArticlePublisher,
|
||||
logger *zap.Logger,
|
||||
) *ArticleTaskHandler {
|
||||
return &ArticleTaskHandler{
|
||||
publisher: publisher,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleArticlePublish 处理文章定时发布任务
|
||||
func (h *ArticleTaskHandler) HandleArticlePublish(ctx context.Context, t *asynq.Task) error {
|
||||
var payload map[string]interface{}
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
h.logger.Error("解析任务载荷失败", zap.Error(err))
|
||||
return fmt.Errorf("解析任务载荷失败: %w", err)
|
||||
}
|
||||
|
||||
articleID, ok := payload["article_id"].(string)
|
||||
if !ok {
|
||||
h.logger.Error("任务载荷中缺少文章ID")
|
||||
return fmt.Errorf("任务载荷中缺少文章ID")
|
||||
}
|
||||
|
||||
// 执行文章发布
|
||||
if err := h.publisher.PublishArticleByID(ctx, articleID); err != nil {
|
||||
h.logger.Error("定时发布文章失败",
|
||||
zap.String("article_id", articleID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("定时发布文章失败: %w", err)
|
||||
}
|
||||
|
||||
h.logger.Info("定时发布文章成功", zap.String("article_id", articleID))
|
||||
return nil
|
||||
}
|
||||
75
internal/infrastructure/task/asynq_client.go
Normal file
75
internal/infrastructure/task/asynq_client.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AsynqClient Asynq 客户端
|
||||
type AsynqClient struct {
|
||||
client *asynq.Client
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAsynqClient 创建 Asynq 客户端
|
||||
func NewAsynqClient(redisAddr string, logger *zap.Logger) *AsynqClient {
|
||||
client := asynq.NewClient(asynq.RedisClientOpt{Addr: redisAddr})
|
||||
return &AsynqClient{
|
||||
client: client,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭客户端
|
||||
func (c *AsynqClient) Close() error {
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
// ScheduleArticlePublish 调度文章定时发布任务
|
||||
func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID string, publishTime time.Time) error {
|
||||
payload := map[string]interface{}{
|
||||
"article_id": articleID,
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
c.logger.Error("序列化任务载荷失败", zap.Error(err))
|
||||
return fmt.Errorf("创建任务失败: %w", err)
|
||||
}
|
||||
|
||||
task := asynq.NewTask(TaskTypeArticlePublish, payloadBytes)
|
||||
|
||||
// 计算延迟时间
|
||||
delay := publishTime.Sub(time.Now())
|
||||
if delay <= 0 {
|
||||
return fmt.Errorf("定时发布时间不能早于当前时间")
|
||||
}
|
||||
|
||||
// 设置任务选项
|
||||
opts := []asynq.Option{
|
||||
asynq.ProcessIn(delay),
|
||||
asynq.MaxRetry(3),
|
||||
asynq.Timeout(5 * time.Minute),
|
||||
}
|
||||
|
||||
info, err := c.client.Enqueue(task, opts...)
|
||||
if err != nil {
|
||||
c.logger.Error("调度定时发布任务失败",
|
||||
zap.String("article_id", articleID),
|
||||
zap.Time("publish_time", publishTime),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("调度任务失败: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("定时发布任务调度成功",
|
||||
zap.String("article_id", articleID),
|
||||
zap.Time("publish_time", publishTime),
|
||||
zap.String("task_id", info.ID))
|
||||
|
||||
return nil
|
||||
}
|
||||
98
internal/infrastructure/task/asynq_worker.go
Normal file
98
internal/infrastructure/task/asynq_worker.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AsynqWorker Asynq Worker
|
||||
type AsynqWorker struct {
|
||||
server *asynq.Server
|
||||
mux *asynq.ServeMux
|
||||
taskHandler *ArticleTaskHandler
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAsynqWorker 创建 Asynq Worker
|
||||
func NewAsynqWorker(
|
||||
redisAddr string,
|
||||
taskHandler *ArticleTaskHandler,
|
||||
logger *zap.Logger,
|
||||
) *AsynqWorker {
|
||||
server := asynq.NewServer(
|
||||
asynq.RedisClientOpt{Addr: redisAddr},
|
||||
asynq.Config{
|
||||
Concurrency: 10, // 并发数
|
||||
Queues: map[string]int{
|
||||
"critical": 6,
|
||||
"default": 3,
|
||||
"low": 1,
|
||||
},
|
||||
Logger: NewAsynqLogger(logger),
|
||||
},
|
||||
)
|
||||
|
||||
mux := asynq.NewServeMux()
|
||||
|
||||
return &AsynqWorker{
|
||||
server: server,
|
||||
mux: mux,
|
||||
taskHandler: taskHandler,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterHandlers 注册任务处理器
|
||||
func (w *AsynqWorker) RegisterHandlers() {
|
||||
// 注册文章定时发布任务处理器
|
||||
w.mux.HandleFunc(TaskTypeArticlePublish, w.taskHandler.HandleArticlePublish)
|
||||
|
||||
w.logger.Info("任务处理器注册完成")
|
||||
}
|
||||
|
||||
// Start 启动 Worker
|
||||
func (w *AsynqWorker) Start() error {
|
||||
w.RegisterHandlers()
|
||||
|
||||
w.logger.Info("启动 Asynq Worker")
|
||||
return w.server.Run(w.mux)
|
||||
}
|
||||
|
||||
// Stop 停止 Worker
|
||||
func (w *AsynqWorker) Stop() {
|
||||
w.logger.Info("停止 Asynq Worker")
|
||||
w.server.Stop()
|
||||
w.server.Shutdown()
|
||||
}
|
||||
|
||||
// AsynqLogger Asynq 日志适配器
|
||||
type AsynqLogger struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAsynqLogger 创建 Asynq 日志适配器
|
||||
func NewAsynqLogger(logger *zap.Logger) *AsynqLogger {
|
||||
return &AsynqLogger{logger: logger}
|
||||
}
|
||||
|
||||
func (l *AsynqLogger) Debug(args ...interface{}) {
|
||||
l.logger.Debug(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *AsynqLogger) Info(args ...interface{}) {
|
||||
l.logger.Info(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *AsynqLogger) Warn(args ...interface{}) {
|
||||
l.logger.Warn(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *AsynqLogger) Error(args ...interface{}) {
|
||||
l.logger.Error(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l *AsynqLogger) Fatal(args ...interface{}) {
|
||||
l.logger.Fatal(fmt.Sprint(args...))
|
||||
}
|
||||
7
internal/infrastructure/task/task_types.go
Normal file
7
internal/infrastructure/task/task_types.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package task
|
||||
|
||||
// 任务类型常量
|
||||
const (
|
||||
// TaskTypeArticlePublish 文章定时发布任务
|
||||
TaskTypeArticlePublish = "article:publish"
|
||||
)
|
||||
Reference in New Issue
Block a user