This commit is contained in:
2025-09-03 13:51:52 +08:00
parent c579e53ad1
commit c1f127e9b1
12 changed files with 645 additions and 23 deletions

View File

@@ -21,6 +21,7 @@ type ArticleApplicationService interface {
PublishArticle(ctx context.Context, cmd *commands.PublishArticleCommand) error
PublishArticleByID(ctx context.Context, articleID string) error
SchedulePublishArticle(ctx context.Context, cmd *commands.SchedulePublishCommand) error
UpdateSchedulePublishArticle(ctx context.Context, cmd *commands.SchedulePublishCommand) error
CancelSchedulePublishArticle(ctx context.Context, cmd *commands.CancelScheduleCommand) error
ArchiveArticle(ctx context.Context, cmd *commands.ArchiveArticleCommand) error
SetFeatured(ctx context.Context, cmd *commands.SetFeaturedCommand) error

View File

@@ -291,12 +291,27 @@ func (s *ArticleApplicationServiceImpl) PublishArticleByID(ctx context.Context,
return fmt.Errorf("文章不存在: %w", err)
}
// 2. 发布文章
// 2. 检查是否已取消定时发布
if !article.IsScheduled() {
s.logger.Info("文章定时发布已取消,跳过执行",
zap.String("id", articleID),
zap.String("status", string(article.Status)))
return nil // 静默返回,不报错
}
// 3. 检查定时发布时间是否匹配
if article.ScheduledAt == nil {
s.logger.Info("文章没有定时发布时间,跳过执行",
zap.String("id", articleID))
return nil
}
// 4. 发布文章
if err := article.Publish(); err != nil {
return fmt.Errorf("发布文章失败: %w", err)
}
// 3. 保存更新
// 5. 保存更新
if err := s.articleRepo.Update(ctx, article); err != nil {
s.logger.Error("更新文章失败", zap.String("id", article.ID), zap.Error(err))
return fmt.Errorf("发布文章失败: %w", err)
@@ -740,6 +755,52 @@ func (s *ArticleApplicationServiceImpl) ListTags(ctx context.Context) (*response
return response, nil
}
// UpdateSchedulePublishArticle 修改定时发布时间
func (s *ArticleApplicationServiceImpl) UpdateSchedulePublishArticle(ctx context.Context, cmd *commands.SchedulePublishCommand) error {
// 1. 解析定时发布时间
scheduledTime, err := cmd.GetScheduledTime()
if err != nil {
s.logger.Error("解析定时发布时间失败", zap.String("scheduled_time", cmd.ScheduledTime), zap.Error(err))
return fmt.Errorf("定时发布时间格式错误: %w", err)
}
// 2. 获取文章
article, err := s.articleRepo.GetByID(ctx, cmd.ID)
if err != nil {
s.logger.Error("获取文章失败", zap.String("id", cmd.ID), zap.Error(err))
return fmt.Errorf("文章不存在: %w", err)
}
// 3. 检查是否已设置定时发布
if !article.IsScheduled() {
return fmt.Errorf("文章未设置定时发布,无法修改时间")
}
// 4. 重新调度定时发布任务
newTaskID, err := s.asynqClient.RescheduleArticlePublish(ctx, cmd.ID, article.TaskID, scheduledTime)
if err != nil {
s.logger.Error("重新调度定时发布任务失败", zap.String("id", cmd.ID), zap.Error(err))
return fmt.Errorf("修改定时发布时间失败: %w", err)
}
// 5. 更新定时发布
if err := article.UpdateSchedulePublish(scheduledTime, newTaskID); err != nil {
return fmt.Errorf("更新定时发布失败: %w", err)
}
// 6. 保存更新
if err := s.articleRepo.Update(ctx, article); err != nil {
s.logger.Error("更新文章失败", zap.String("id", article.ID), zap.Error(err))
return fmt.Errorf("修改定时发布时间失败: %w", err)
}
s.logger.Info("修改定时发布时间成功",
zap.String("id", article.ID),
zap.Time("new_scheduled_time", scheduledTime),
zap.String("new_task_id", newTaskID))
return nil
}
// ==================== 验证方法 ====================
// validateCreateCategory 验证创建分类参数

View File

@@ -0,0 +1,126 @@
package article
import (
"context"
"fmt"
"time"
"tyapi-server/internal/domains/article/entities"
"tyapi-server/internal/domains/article/repositories"
"go.uber.org/zap"
)
// TaskManagementService 任务管理服务
type TaskManagementService struct {
scheduledTaskRepo repositories.ScheduledTaskRepository
logger *zap.Logger
}
// NewTaskManagementService 创建任务管理服务
func NewTaskManagementService(
scheduledTaskRepo repositories.ScheduledTaskRepository,
logger *zap.Logger,
) *TaskManagementService {
return &TaskManagementService{
scheduledTaskRepo: scheduledTaskRepo,
logger: logger,
}
}
// GetTaskStatus 获取任务状态
func (s *TaskManagementService) GetTaskStatus(ctx context.Context, taskID string) (*entities.ScheduledTask, error) {
task, err := s.scheduledTaskRepo.GetByTaskID(ctx, taskID)
if err != nil {
return nil, fmt.Errorf("获取任务状态失败: %w", err)
}
return &task, nil
}
// GetArticleTaskStatus 获取文章的定时任务状态
func (s *TaskManagementService) GetArticleTaskStatus(ctx context.Context, articleID string) (*entities.ScheduledTask, error) {
task, err := s.scheduledTaskRepo.GetByArticleID(ctx, articleID)
if err != nil {
return nil, fmt.Errorf("获取文章定时任务状态失败: %w", err)
}
return &task, nil
}
// CancelTask 取消任务
func (s *TaskManagementService) CancelTask(ctx context.Context, taskID string) error {
if err := s.scheduledTaskRepo.MarkAsCancelled(ctx, taskID); err != nil {
return fmt.Errorf("取消任务失败: %w", err)
}
s.logger.Info("任务已取消", zap.String("task_id", taskID))
return nil
}
// GetActiveTasks 获取活动任务列表
func (s *TaskManagementService) GetActiveTasks(ctx context.Context) ([]entities.ScheduledTask, error) {
tasks, err := s.scheduledTaskRepo.GetActiveTasks(ctx)
if err != nil {
return nil, fmt.Errorf("获取活动任务列表失败: %w", err)
}
return tasks, nil
}
// GetExpiredTasks 获取过期任务列表
func (s *TaskManagementService) GetExpiredTasks(ctx context.Context) ([]entities.ScheduledTask, error) {
tasks, err := s.scheduledTaskRepo.GetExpiredTasks(ctx)
if err != nil {
return nil, fmt.Errorf("获取过期任务列表失败: %w", err)
}
return tasks, nil
}
// CleanupExpiredTasks 清理过期任务
func (s *TaskManagementService) CleanupExpiredTasks(ctx context.Context) error {
expiredTasks, err := s.GetExpiredTasks(ctx)
if err != nil {
return err
}
for _, task := range expiredTasks {
if err := s.scheduledTaskRepo.MarkAsCancelled(ctx, task.TaskID); err != nil {
s.logger.Warn("清理过期任务失败", zap.String("task_id", task.TaskID), zap.Error(err))
continue
}
s.logger.Info("已清理过期任务", zap.String("task_id", task.TaskID))
}
return nil
}
// GetTaskStats 获取任务统计信息
func (s *TaskManagementService) GetTaskStats(ctx context.Context) (map[string]interface{}, error) {
activeTasks, err := s.GetActiveTasks(ctx)
if err != nil {
return nil, err
}
expiredTasks, err := s.GetExpiredTasks(ctx)
if err != nil {
return nil, err
}
stats := map[string]interface{}{
"active_tasks_count": len(activeTasks),
"expired_tasks_count": len(expiredTasks),
"total_tasks_count": len(activeTasks) + len(expiredTasks),
"next_task_time": nil,
"last_cleanup_time": time.Now(),
}
// 计算下一个任务时间
if len(activeTasks) > 0 {
nextTask := activeTasks[0]
for _, task := range activeTasks {
if task.ScheduledAt.Before(nextTask.ScheduledAt) {
nextTask = task
}
}
stats["next_task_time"] = nextTask.ScheduledAt
}
return stats, nil
}