777 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			777 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| //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
 | ||
| 
 | ||
| 	// 绑定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, "获取文章详情成功")
 | ||
| }
 | ||
| 
 | ||
| // 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
 | ||
| 
 | ||
| 	// 先绑定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, "文章更新成功")
 | ||
| }
 | ||
| 
 | ||
| // 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 设置文章的定时发布时间,支持格式:YYYY-MM-DD HH:mm:ss
 | ||
| // @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
 | ||
| 
 | ||
| 	// 先绑定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 将已发布文章归档
 | ||
| // @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
 | ||
| 
 | ||
| 	// 先绑定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, "设置推荐状态成功")
 | ||
| }
 | ||
| 
 | ||
| // 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, "获取统计成功")
 | ||
| }
 | ||
| 
 | ||
| // UpdateSchedulePublishArticle 修改定时发布时间
 | ||
| // @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}/update-schedule-publish [post]
 | ||
| func (h *ArticleHandler) UpdateSchedulePublishArticle(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.UpdateSchedulePublishArticle(c.Request.Context(), &cmd); err != nil {
 | ||
| 		h.logger.Error("修改定时发布时间失败", zap.Error(err))
 | ||
| 		h.responseBuilder.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.responseBuilder.Success(c, nil, "修改定时发布时间成功")
 | ||
| }
 | ||
| 
 | ||
| // ==================== 分类相关方法 ====================
 | ||
| 
 | ||
| // 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
 | ||
| 
 | ||
| 	// 绑定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, "获取分类详情成功")
 | ||
| }
 | ||
| 
 | ||
| // 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
 | ||
| 
 | ||
| 	// 先绑定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, "分类更新成功")
 | ||
| }
 | ||
| 
 | ||
| // 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
 | ||
| 
 | ||
| 	// 绑定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, "获取标签详情成功")
 | ||
| }
 | ||
| 
 | ||
| // 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
 | ||
| 
 | ||
| 	// 先绑定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, "标签更新成功")
 | ||
| }
 | ||
| 
 | ||
| // 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, "标签删除成功")
 | ||
| }
 |