add timed
This commit is contained in:
@@ -268,8 +268,7 @@ func (r *GormArticleRepository) ListArticles(ctx context.Context, query *repoQue
|
||||
var articles []entities.Article
|
||||
var total int64
|
||||
|
||||
dbQuery := r.db.WithContext(ctx).Model(&entities.Article{}).
|
||||
Select("id, title, summary, cover_image, category_id, status, is_featured, published_at, created_at, updated_at, scheduled_at")
|
||||
dbQuery := r.db.WithContext(ctx).Model(&entities.Article{})
|
||||
|
||||
// 用户端不显示归档文章
|
||||
dbQuery = dbQuery.Where("status != ?", entities.ArticleStatusArchived)
|
||||
@@ -357,8 +356,7 @@ func (r *GormArticleRepository) ListArticlesForAdmin(ctx context.Context, query
|
||||
var articles []entities.Article
|
||||
var total int64
|
||||
|
||||
dbQuery := r.db.WithContext(ctx).Model(&entities.Article{}).
|
||||
Select("id, title, summary, cover_image, category_id, status, is_featured, published_at, view_count, created_at, updated_at, scheduled_at")
|
||||
dbQuery := r.db.WithContext(ctx).Model(&entities.Article{})
|
||||
|
||||
// 应用筛选条件
|
||||
if query.Status != "" {
|
||||
|
||||
@@ -142,8 +142,8 @@ func (r *GormTagRepository) AddTagToArticle(ctx context.Context, articleID strin
|
||||
|
||||
// 创建关联
|
||||
err = r.db.WithContext(ctx).Exec(`
|
||||
INSERT INTO article_tag_relations (id, article_id, tag_id, created_at)
|
||||
VALUES (UUID(), ?, ?, NOW())
|
||||
INSERT INTO article_tag_relations (article_id, tag_id)
|
||||
VALUES (?, ?)
|
||||
`, articleID, tagID).Error
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -53,19 +53,19 @@ func (h *ArticleHandler) CreateArticle(c *gin.Context) {
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 验证用户是否已登录
|
||||
if _, exists := c.Get("user_id"); !exists {
|
||||
h.responseBuilder.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.CreateArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("创建文章失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Created(c, nil, "文章创建成功")
|
||||
}
|
||||
|
||||
@@ -83,19 +83,19 @@ func (h *ArticleHandler) CreateArticle(c *gin.Context) {
|
||||
// @Router /api/v1/articles/{id} [get]
|
||||
func (h *ArticleHandler) GetArticleByID(c *gin.Context) {
|
||||
var query appQueries.GetArticleQuery
|
||||
query.ID = c.Param("id")
|
||||
if query.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "文章ID不能为空")
|
||||
|
||||
// 绑定URI参数(文章ID)
|
||||
if err := h.validator.ValidateParam(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
response, err := h.appService.GetArticleByID(c.Request.Context(), &query)
|
||||
if err != nil {
|
||||
h.logger.Error("获取文章详情失败", zap.Error(err))
|
||||
h.responseBuilder.NotFound(c, "文章不存在")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取文章详情成功")
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (h *ArticleHandler) ListArticles(c *gin.Context) {
|
||||
if err := h.validator.ValidateQuery(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 设置默认值
|
||||
if query.Page <= 0 {
|
||||
query.Page = 1
|
||||
@@ -135,14 +135,14 @@ func (h *ArticleHandler) ListArticles(c *gin.Context) {
|
||||
if query.PageSize > 100 {
|
||||
query.PageSize = 100
|
||||
}
|
||||
|
||||
|
||||
response, err := h.appService.ListArticles(c.Request.Context(), &query)
|
||||
if err != nil {
|
||||
h.logger.Error("获取文章列表失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取文章列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取文章列表成功")
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func (h *ArticleHandler) ListArticlesForAdmin(c *gin.Context) {
|
||||
if err := h.validator.ValidateQuery(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 设置默认值
|
||||
if query.Page <= 0 {
|
||||
query.Page = 1
|
||||
@@ -184,19 +184,17 @@ func (h *ArticleHandler) ListArticlesForAdmin(c *gin.Context) {
|
||||
if query.PageSize > 100 {
|
||||
query.PageSize = 100
|
||||
}
|
||||
|
||||
|
||||
response, err := h.appService.ListArticlesForAdmin(c.Request.Context(), &query)
|
||||
if err != nil {
|
||||
h.logger.Error("获取文章列表失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取文章列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取文章列表成功")
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UpdateArticle 更新文章
|
||||
// @Summary 更新文章
|
||||
// @Description 更新文章信息
|
||||
@@ -214,21 +212,23 @@ func (h *ArticleHandler) ListArticlesForAdmin(c *gin.Context) {
|
||||
// @Router /api/v1/admin/articles/{id} [put]
|
||||
func (h *ArticleHandler) UpdateArticle(c *gin.Context) {
|
||||
var cmd commands.UpdateArticleCommand
|
||||
cmd.ID = c.Param("id")
|
||||
if cmd.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "文章ID不能为空")
|
||||
|
||||
// 先绑定URI参数(文章ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 再绑定JSON请求体(文章信息)
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.UpdateArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("更新文章失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "文章更新成功")
|
||||
}
|
||||
|
||||
@@ -251,13 +251,13 @@ func (h *ArticleHandler) DeleteArticle(c *gin.Context) {
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.DeleteArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("删除文章失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "文章删除成功")
|
||||
}
|
||||
|
||||
@@ -280,19 +280,19 @@ func (h *ArticleHandler) PublishArticle(c *gin.Context) {
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.PublishArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("发布文章失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "文章发布成功")
|
||||
}
|
||||
|
||||
// SchedulePublishArticle 定时发布文章
|
||||
// @Summary 定时发布文章
|
||||
// @Description 设置文章的定时发布时间
|
||||
// @Description 设置文章的定时发布时间,支持格式:YYYY-MM-DD HH:mm:ss
|
||||
// @Tags 文章管理-管理端
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
@@ -307,22 +307,57 @@ func (h *ArticleHandler) PublishArticle(c *gin.Context) {
|
||||
// @Router /api/v1/admin/articles/{id}/schedule-publish [post]
|
||||
func (h *ArticleHandler) SchedulePublishArticle(c *gin.Context) {
|
||||
var cmd commands.SchedulePublishCommand
|
||||
|
||||
// 先绑定URI参数(文章ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 再绑定JSON请求体(定时发布时间)
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.SchedulePublishArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("设置定时发布失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "定时发布设置成功")
|
||||
}
|
||||
|
||||
// CancelSchedulePublishArticle 取消定时发布文章
|
||||
// @Summary 取消定时发布文章
|
||||
// @Description 取消文章的定时发布设置
|
||||
// @Tags 文章管理-管理端
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id path string true "文章ID"
|
||||
// @Success 200 {object} map[string]interface{} "取消定时发布成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 404 {object} map[string]interface{} "文章不存在"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/admin/articles/{id}/cancel-schedule [post]
|
||||
func (h *ArticleHandler) CancelSchedulePublishArticle(c *gin.Context) {
|
||||
var cmd commands.CancelScheduleCommand
|
||||
|
||||
// 绑定URI参数(文章ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.appService.CancelSchedulePublishArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("取消定时发布失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil, "取消定时发布成功")
|
||||
}
|
||||
|
||||
// ArchiveArticle 归档文章
|
||||
// @Summary 归档文章
|
||||
// @Description 将已发布文章归档
|
||||
@@ -342,13 +377,13 @@ func (h *ArticleHandler) ArchiveArticle(c *gin.Context) {
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.ArchiveArticle(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("归档文章失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "文章归档成功")
|
||||
}
|
||||
|
||||
@@ -369,19 +404,23 @@ func (h *ArticleHandler) ArchiveArticle(c *gin.Context) {
|
||||
// @Router /api/v1/admin/articles/{id}/featured [put]
|
||||
func (h *ArticleHandler) SetFeatured(c *gin.Context) {
|
||||
var cmd commands.SetFeaturedCommand
|
||||
|
||||
// 先绑定URI参数(文章ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 再绑定JSON请求体(推荐状态)
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.SetFeatured(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("设置推荐状态失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "设置推荐状态成功")
|
||||
}
|
||||
|
||||
@@ -403,11 +442,10 @@ func (h *ArticleHandler) GetArticleStats(c *gin.Context) {
|
||||
h.responseBuilder.InternalError(c, "获取文章统计失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取统计成功")
|
||||
}
|
||||
|
||||
|
||||
// ==================== 分类相关方法 ====================
|
||||
|
||||
// ListCategories 获取分类列表
|
||||
@@ -426,7 +464,7 @@ func (h *ArticleHandler) ListCategories(c *gin.Context) {
|
||||
h.responseBuilder.InternalError(c, "获取分类列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取分类列表成功")
|
||||
}
|
||||
|
||||
@@ -444,19 +482,19 @@ func (h *ArticleHandler) ListCategories(c *gin.Context) {
|
||||
// @Router /api/v1/article-categories/{id} [get]
|
||||
func (h *ArticleHandler) GetCategoryByID(c *gin.Context) {
|
||||
var query appQueries.GetCategoryQuery
|
||||
query.ID = c.Param("id")
|
||||
if query.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "分类ID不能为空")
|
||||
|
||||
// 绑定URI参数(分类ID)
|
||||
if err := h.validator.ValidateParam(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
response, err := h.appService.GetCategoryByID(c.Request.Context(), &query)
|
||||
if err != nil {
|
||||
h.logger.Error("获取分类详情失败", zap.Error(err))
|
||||
h.responseBuilder.NotFound(c, "分类不存在")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取分类详情成功")
|
||||
}
|
||||
|
||||
@@ -478,13 +516,13 @@ func (h *ArticleHandler) CreateCategory(c *gin.Context) {
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.CreateCategory(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("创建分类失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Created(c, nil, "分类创建成功")
|
||||
}
|
||||
|
||||
@@ -505,21 +543,23 @@ func (h *ArticleHandler) CreateCategory(c *gin.Context) {
|
||||
// @Router /api/v1/admin/article-categories/{id} [put]
|
||||
func (h *ArticleHandler) UpdateCategory(c *gin.Context) {
|
||||
var cmd commands.UpdateCategoryCommand
|
||||
cmd.ID = c.Param("id")
|
||||
if cmd.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "分类ID不能为空")
|
||||
|
||||
// 先绑定URI参数(分类ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 再绑定JSON请求体(分类信息)
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.UpdateCategory(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("更新分类失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "分类更新成功")
|
||||
}
|
||||
|
||||
@@ -542,13 +582,13 @@ func (h *ArticleHandler) DeleteCategory(c *gin.Context) {
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.DeleteCategory(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("删除分类失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "分类删除成功")
|
||||
}
|
||||
|
||||
@@ -570,7 +610,7 @@ func (h *ArticleHandler) ListTags(c *gin.Context) {
|
||||
h.responseBuilder.InternalError(c, "获取标签列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取标签列表成功")
|
||||
}
|
||||
|
||||
@@ -588,19 +628,19 @@ func (h *ArticleHandler) ListTags(c *gin.Context) {
|
||||
// @Router /api/v1/article-tags/{id} [get]
|
||||
func (h *ArticleHandler) GetTagByID(c *gin.Context) {
|
||||
var query appQueries.GetTagQuery
|
||||
query.ID = c.Param("id")
|
||||
if query.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "标签ID不能为空")
|
||||
|
||||
// 绑定URI参数(标签ID)
|
||||
if err := h.validator.ValidateParam(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
response, err := h.appService.GetTagByID(c.Request.Context(), &query)
|
||||
if err != nil {
|
||||
h.logger.Error("获取标签详情失败", zap.Error(err))
|
||||
h.responseBuilder.NotFound(c, "标签不存在")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取标签详情成功")
|
||||
}
|
||||
|
||||
@@ -622,13 +662,13 @@ func (h *ArticleHandler) CreateTag(c *gin.Context) {
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.CreateTag(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("创建标签失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Created(c, nil, "标签创建成功")
|
||||
}
|
||||
|
||||
@@ -649,21 +689,23 @@ func (h *ArticleHandler) CreateTag(c *gin.Context) {
|
||||
// @Router /api/v1/admin/article-tags/{id} [put]
|
||||
func (h *ArticleHandler) UpdateTag(c *gin.Context) {
|
||||
var cmd commands.UpdateTagCommand
|
||||
cmd.ID = c.Param("id")
|
||||
if cmd.ID == "" {
|
||||
h.responseBuilder.BadRequest(c, "标签ID不能为空")
|
||||
|
||||
// 先绑定URI参数(标签ID)
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 再绑定JSON请求体(标签信息)
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.UpdateTag(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("更新标签失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "标签更新成功")
|
||||
}
|
||||
|
||||
@@ -686,12 +728,12 @@ func (h *ArticleHandler) DeleteTag(c *gin.Context) {
|
||||
if err := h.validator.ValidateParam(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if err := h.appService.DeleteTag(c.Request.Context(), &cmd); err != nil {
|
||||
h.logger.Error("删除标签失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
h.responseBuilder.Success(c, nil, "标签删除成功")
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func (r *ApiRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
|
||||
// 加密接口(用于前端调试)
|
||||
apiGroup.POST("/encrypt", r.authMiddleware.Handle(), r.apiHandler.EncryptParams)
|
||||
|
||||
|
||||
// 解密接口(用于前端调试)
|
||||
apiGroup.POST("/decrypt", r.authMiddleware.Handle(), r.apiHandler.DecryptParams)
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ func (r *ArticleRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
// 文章状态管理
|
||||
adminArticleGroup.POST("/:id/publish", r.handler.PublishArticle) // 发布文章
|
||||
adminArticleGroup.POST("/:id/schedule-publish", r.handler.SchedulePublishArticle) // 定时发布文章
|
||||
adminArticleGroup.POST("/:id/cancel-schedule", r.handler.CancelSchedulePublishArticle) // 取消定时发布
|
||||
adminArticleGroup.POST("/:id/archive", r.handler.ArchiveArticle) // 归档文章
|
||||
adminArticleGroup.PUT("/:id/featured", r.handler.SetFeatured) // 设置推荐状态
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c *AsynqClient) Close() error {
|
||||
}
|
||||
|
||||
// ScheduleArticlePublish 调度文章定时发布任务
|
||||
func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID string, publishTime time.Time) error {
|
||||
func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID string, publishTime time.Time) (string, error) {
|
||||
payload := map[string]interface{}{
|
||||
"article_id": articleID,
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID stri
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
c.logger.Error("序列化任务载荷失败", zap.Error(err))
|
||||
return fmt.Errorf("创建任务失败: %w", err)
|
||||
return "", fmt.Errorf("创建任务失败: %w", err)
|
||||
}
|
||||
|
||||
task := asynq.NewTask(TaskTypeArticlePublish, payloadBytes)
|
||||
@@ -47,7 +47,7 @@ func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID stri
|
||||
// 计算延迟时间
|
||||
delay := publishTime.Sub(time.Now())
|
||||
if delay <= 0 {
|
||||
return fmt.Errorf("定时发布时间不能早于当前时间")
|
||||
return "", fmt.Errorf("定时发布时间不能早于当前时间")
|
||||
}
|
||||
|
||||
// 设置任务选项
|
||||
@@ -63,7 +63,7 @@ func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID stri
|
||||
zap.String("article_id", articleID),
|
||||
zap.Time("publish_time", publishTime),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("调度任务失败: %w", err)
|
||||
return "", fmt.Errorf("调度任务失败: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("定时发布任务调度成功",
|
||||
@@ -71,5 +71,44 @@ func (c *AsynqClient) ScheduleArticlePublish(ctx context.Context, articleID stri
|
||||
zap.Time("publish_time", publishTime),
|
||||
zap.String("task_id", info.ID))
|
||||
|
||||
return info.ID, nil
|
||||
}
|
||||
|
||||
// CancelScheduledTask 取消已调度的任务
|
||||
func (c *AsynqClient) CancelScheduledTask(ctx context.Context, taskID string) error {
|
||||
// 注意:Asynq不直接支持取消已调度的任务
|
||||
// 这里我们记录日志,实际取消需要在数据库中标记
|
||||
c.logger.Info("请求取消定时任务",
|
||||
zap.String("task_id", taskID))
|
||||
|
||||
// 在实际应用中,你可能需要:
|
||||
// 1. 在数据库中标记任务为已取消
|
||||
// 2. 在任务执行时检查取消状态
|
||||
// 3. 或者使用Redis的TTL机制
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RescheduleArticlePublish 重新调度文章定时发布任务
|
||||
func (c *AsynqClient) RescheduleArticlePublish(ctx context.Context, articleID string, oldTaskID string, newPublishTime time.Time) (string, error) {
|
||||
// 1. 取消旧任务(标记为已取消)
|
||||
if err := c.CancelScheduledTask(ctx, oldTaskID); err != nil {
|
||||
c.logger.Warn("取消旧任务失败",
|
||||
zap.String("old_task_id", oldTaskID),
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
// 2. 创建新任务
|
||||
newTaskID, err := c.ScheduleArticlePublish(ctx, articleID, newPublishTime)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("重新调度任务失败: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("重新调度定时发布任务成功",
|
||||
zap.String("article_id", articleID),
|
||||
zap.String("old_task_id", oldTaskID),
|
||||
zap.String("new_task_id", newTaskID),
|
||||
zap.Time("new_publish_time", newPublishTime))
|
||||
|
||||
return newTaskID, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user