//nolint:unused package handlers import ( "tyapi-server/internal/application/article" "tyapi-server/internal/application/article/dto/commands" appQueries "tyapi-server/internal/application/article/dto/queries" _ "tyapi-server/internal/application/article/dto/responses" "tyapi-server/internal/shared/interfaces" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // ArticleHandler 文章HTTP处理器 type ArticleHandler struct { appService article.ArticleApplicationService responseBuilder interfaces.ResponseBuilder validator interfaces.RequestValidator logger *zap.Logger } // NewArticleHandler 创建文章HTTP处理器 func NewArticleHandler( appService article.ArticleApplicationService, responseBuilder interfaces.ResponseBuilder, validator interfaces.RequestValidator, logger *zap.Logger, ) *ArticleHandler { return &ArticleHandler{ appService: appService, responseBuilder: responseBuilder, validator: validator, logger: logger, } } // CreateArticle 创建文章 // @Summary 创建文章 // @Description 创建新的文章 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Param request body commands.CreateArticleCommand true "创建文章请求" // @Success 201 {object} map[string]interface{} "文章创建成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/admin/articles [post] func (h *ArticleHandler) CreateArticle(c *gin.Context) { var cmd commands.CreateArticleCommand 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, "文章创建成功") } // GetArticleByID 获取文章详情 // @Summary 获取文章详情 // @Description 根据ID获取文章详情 // @Tags 文章管理-用户端 // @Accept json // @Produce json // @Param id path string true "文章ID" // @Success 200 {object} responses.ArticleInfoResponse "获取文章详情成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 404 {object} map[string]interface{} "文章不存在" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @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不能为空") 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, "获取文章详情成功") } // ListArticles 获取文章列表 // @Summary 获取文章列表 // @Description 分页获取文章列表,支持多种筛选条件 // @Tags 文章管理-用户端 // @Accept json // @Produce json // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(10) // @Param status query string false "文章状态" // @Param category_id query string false "分类ID" // @Param tag_id query string false "标签ID" // @Param title query string false "标题关键词" // @Param summary query string false "摘要关键词" // @Param is_featured query bool false "是否推荐" // @Param order_by query string false "排序字段" // @Param order_dir query string false "排序方向" // @Success 200 {object} responses.ArticleListResponse "获取文章列表成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/articles [get] func (h *ArticleHandler) ListArticles(c *gin.Context) { var query appQueries.ListArticleQuery if err := h.validator.ValidateQuery(c, &query); err != nil { return } // 设置默认值 if query.Page <= 0 { query.Page = 1 } if query.PageSize <= 0 { query.PageSize = 10 } 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, "获取文章列表成功") } // ListArticlesForAdmin 获取文章列表(管理员端) // @Summary 获取文章列表(管理员端) // @Description 分页获取文章列表,支持多种筛选条件,包含所有状态的文章 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(10) // @Param status query string false "文章状态" // @Param category_id query string false "分类ID" // @Param tag_id query string false "标签ID" // @Param title query string false "标题关键词" // @Param summary query string false "摘要关键词" // @Param is_featured query bool false "是否推荐" // @Param order_by query string false "排序字段" // @Param order_dir query string false "排序方向" // @Success 200 {object} responses.ArticleListResponse "获取文章列表成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/admin/articles [get] func (h *ArticleHandler) ListArticlesForAdmin(c *gin.Context) { var query appQueries.ListArticleQuery if err := h.validator.ValidateQuery(c, &query); err != nil { return } // 设置默认值 if query.Page <= 0 { query.Page = 1 } if query.PageSize <= 0 { query.PageSize = 10 } 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 更新文章信息 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Param id path string true "文章ID" // @Param request body commands.UpdateArticleCommand true "更新文章请求" // @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} [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不能为空") return } 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, "文章更新成功") } // DeleteArticle 删除文章 // @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} [delete] func (h *ArticleHandler) DeleteArticle(c *gin.Context) { var cmd commands.DeleteArticleCommand 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, "文章删除成功") } // PublishArticle 发布文章 // @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}/publish [post] func (h *ArticleHandler) PublishArticle(c *gin.Context) { var cmd commands.PublishArticleCommand 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 设置文章的定时发布时间 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Param id path string true "文章ID" // @Param request body commands.SchedulePublishCommand true "定时发布请求" // @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}/schedule-publish [post] func (h *ArticleHandler) SchedulePublishArticle(c *gin.Context) { var cmd commands.SchedulePublishCommand if err := h.validator.ValidateParam(c, &cmd); err != nil { return } 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, "定时发布设置成功") } // ArchiveArticle 归档文章 // @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}/archive [post] func (h *ArticleHandler) ArchiveArticle(c *gin.Context) { var cmd commands.ArchiveArticleCommand 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, "文章归档成功") } // SetFeatured 设置推荐状态 // @Summary 设置推荐状态 // @Description 设置文章的推荐状态 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Param id path string true "文章ID" // @Param request body commands.SetFeaturedCommand true "设置推荐状态请求" // @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}/featured [put] func (h *ArticleHandler) SetFeatured(c *gin.Context) { var cmd commands.SetFeaturedCommand if err := h.validator.ValidateParam(c, &cmd); err != nil { return } 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, "设置推荐状态成功") } // GetArticleStats 获取文章统计 // @Summary 获取文章统计 // @Description 获取文章相关统计数据 // @Tags 文章管理-管理端 // @Accept json // @Produce json // @Security Bearer // @Success 200 {object} responses.ArticleStatsResponse "获取统计成功" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/admin/articles/stats [get] func (h *ArticleHandler) GetArticleStats(c *gin.Context) { response, err := h.appService.GetArticleStats(c.Request.Context()) if err != nil { h.logger.Error("获取文章统计失败", zap.Error(err)) h.responseBuilder.InternalError(c, "获取文章统计失败") return } h.responseBuilder.Success(c, response, "获取统计成功") } // ==================== 分类相关方法 ==================== // ListCategories 获取分类列表 // @Summary 获取分类列表 // @Description 获取所有文章分类 // @Tags 文章分类-用户端 // @Accept json // @Produce json // @Success 200 {object} responses.CategoryListResponse "获取分类列表成功" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/article-categories [get] func (h *ArticleHandler) ListCategories(c *gin.Context) { response, err := h.appService.ListCategories(c.Request.Context()) if err != nil { h.logger.Error("获取分类列表失败", zap.Error(err)) h.responseBuilder.InternalError(c, "获取分类列表失败") return } h.responseBuilder.Success(c, response, "获取分类列表成功") } // GetCategoryByID 获取分类详情 // @Summary 获取分类详情 // @Description 根据ID获取分类详情 // @Tags 文章分类-用户端 // @Accept json // @Produce json // @Param id path string true "分类ID" // @Success 200 {object} responses.CategoryInfoResponse "获取分类详情成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 404 {object} map[string]interface{} "分类不存在" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @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不能为空") 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, "获取分类详情成功") } // CreateCategory 创建分类 // @Summary 创建分类 // @Description 创建新的文章分类 // @Tags 文章分类-管理端 // @Accept json // @Produce json // @Security Bearer // @Param request body commands.CreateCategoryCommand true "创建分类请求" // @Success 201 {object} map[string]interface{} "分类创建成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/admin/article-categories [post] func (h *ArticleHandler) CreateCategory(c *gin.Context) { var cmd commands.CreateCategoryCommand 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, "分类创建成功") } // UpdateCategory 更新分类 // @Summary 更新分类 // @Description 更新分类信息 // @Tags 文章分类-管理端 // @Accept json // @Produce json // @Security Bearer // @Param id path string true "分类ID" // @Param request body commands.UpdateCategoryCommand true "更新分类请求" // @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/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不能为空") return } 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, "分类更新成功") } // DeleteCategory 删除分类 // @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/article-categories/{id} [delete] func (h *ArticleHandler) DeleteCategory(c *gin.Context) { var cmd commands.DeleteCategoryCommand 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, "分类删除成功") } // ==================== 标签相关方法 ==================== // ListTags 获取标签列表 // @Summary 获取标签列表 // @Description 获取所有文章标签 // @Tags 文章标签-用户端 // @Accept json // @Produce json // @Success 200 {object} responses.TagListResponse "获取标签列表成功" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/article-tags [get] func (h *ArticleHandler) ListTags(c *gin.Context) { response, err := h.appService.ListTags(c.Request.Context()) if err != nil { h.logger.Error("获取标签列表失败", zap.Error(err)) h.responseBuilder.InternalError(c, "获取标签列表失败") return } h.responseBuilder.Success(c, response, "获取标签列表成功") } // GetTagByID 获取标签详情 // @Summary 获取标签详情 // @Description 根据ID获取标签详情 // @Tags 文章标签-用户端 // @Accept json // @Produce json // @Param id path string true "标签ID" // @Success 200 {object} responses.TagInfoResponse "获取标签详情成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 404 {object} map[string]interface{} "标签不存在" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @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不能为空") 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, "获取标签详情成功") } // CreateTag 创建标签 // @Summary 创建标签 // @Description 创建新的文章标签 // @Tags 文章标签-管理端 // @Accept json // @Produce json // @Security Bearer // @Param request body commands.CreateTagCommand true "创建标签请求" // @Success 201 {object} map[string]interface{} "标签创建成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/admin/article-tags [post] func (h *ArticleHandler) CreateTag(c *gin.Context) { var cmd commands.CreateTagCommand 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, "标签创建成功") } // UpdateTag 更新标签 // @Summary 更新标签 // @Description 更新标签信息 // @Tags 文章标签-管理端 // @Accept json // @Produce json // @Security Bearer // @Param id path string true "标签ID" // @Param request body commands.UpdateTagCommand true "更新标签请求" // @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/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不能为空") return } 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, "标签更新成功") } // DeleteTag 删除标签 // @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/article-tags/{id} [delete] func (h *ArticleHandler) DeleteTag(c *gin.Context) { var cmd commands.DeleteTagCommand 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, "标签删除成功") }