repo_description: A Q&A platform software for teams at any scales. Whether it's a community forum, help center, or knowledge management platform, you can always count on Apache Answer.
repo_topics: community forum go golang hacktoberfest q-and-a question react typescript
code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  package controller
  
  import (
  	"github.com/apache/answer/internal/base/handler"
  	"github.com/apache/answer/internal/base/middleware"
  	"github.com/apache/answer/internal/base/pager"
  	"github.com/apache/answer/internal/base/reason"
  	"github.com/apache/answer/internal/schema"
  	"github.com/apache/answer/internal/service/permission"
  	"github.com/apache/answer/internal/service/rank"
  	"github.com/apache/answer/internal/service/tag"
  	"github.com/apache/answer/internal/service/tag_common"
  	"github.com/gin-gonic/gin"
  	"github.com/segmentfault/pacman/errors"
  )
  
  // TagController tag controller
  type TagController struct {
  	tagService       *tag.TagService
  	tagCommonService *tag_common.TagCommonService
  	rankService      *rank.RankService
  }
  
  // NewTagController new controller
  func NewTagController(
  	tagService *tag.TagService,
  	tagCommonService *tag_common.TagCommonService,
  	rankService *rank.RankService,
  ) *TagController {
  	return &TagController{tagService: tagService, tagCommonService: tagCommonService, rankService: rankService}
  }
  
  // SearchTagLike get tag list
  // @Summary get tag list
  // @Description get tag list
  // @Tags Tag
  // @Produce json
  // @Security ApiKeyAuth
  // @Param tag query string false "tag"
  // @Success 200 {object} handler.RespBody{data=[]schema.GetTagBasicResp}
  // @Router /answer/api/v1/question/tags [get]
  func (tc *TagController) SearchTagLike(ctx *gin.Context) {
  	req := &schema.SearchTagLikeReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  	resp, err := tc.tagCommonService.SearchTagLike(ctx, req)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // GetTagsBySlugName get tags list
  // @Summary get tags list
  // @Description get tags list by slug name
  // @Tags Tag
  // @Produce json
  // @Param tags query []string false "string collection" collectionFormat(csv)
  // @Success 200 {object} handler.RespBody{data=[]schema.GetTagBasicResp}
  // @Router /answer/api/v1/tags [get]
  func (tc *TagController) GetTagsBySlugName(ctx *gin.Context) {
  	req := &schema.SearchTagsBySlugName{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	resp, err := tc.tagService.GetTagsBySlugName(ctx, req)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // RemoveTag delete tag
  // @Summary delete tag
  // @Description delete tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.RemoveTagReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag [delete]
  func (tc *TagController) RemoveTag(ctx *gin.Context) {
  	req := &schema.RemoveTagReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	can, err := tc.rankService.CheckOperationPermission(ctx, req.UserID, permission.TagDelete, "")
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if !can {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  	err = tc.tagService.RemoveTag(ctx, req)
  	handler.HandleResponse(ctx, err, nil)
  }
  
  // AddTag add tag
  // @Summary add tag
  // @Description add tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.AddTagReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag [post]
  func (tc *TagController) AddTag(ctx *gin.Context) {
  	req := &schema.AddTagReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
  		permission.TagAdd,
  	})
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if !canList[0] {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  
  	resp, err := tc.tagCommonService.AddTag(ctx, req)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // UpdateTag update tag
  // @Summary update tag
  // @Description update tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.UpdateTagReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag [put]
  func (tc *TagController) UpdateTag(ctx *gin.Context) {
  	req := &schema.UpdateTagReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
  		permission.TagEdit,
  		permission.TagEditWithoutReview,
  	})
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if !canList[0] {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  	req.NoNeedReview = canList[1]
  
  	err = tc.tagService.UpdateTag(ctx, req)
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  	} else {
  		handler.HandleResponse(ctx, err, &schema.UpdateTagResp{WaitForReview: !req.NoNeedReview})
  	}
  }
  
  // RecoverTag recover delete tag
  // @Summary recover delete tag
  // @Description recover delete tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.RecoverTagReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag/recover [post]
  func (tc *TagController) RecoverTag(ctx *gin.Context) {
  	req := &schema.RecoverTagReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  
  	canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
  		permission.TagUnDelete,
  	})
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if !canList[0] {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  
  	err = tc.tagService.RecoverTag(ctx, req)
  	handler.HandleResponse(ctx, err, nil)
  }
  
  // GetTagInfo get tag one
  // @Summary get tag one
  // @Description get tag one
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param tag_id query string true "tag id"
  // @Param tag_name query string true "tag name"
  // @Success 200 {object} handler.RespBody{data=schema.GetTagResp}
  // @Router /answer/api/v1/tag [get]
  func (tc *TagController) GetTagInfo(ctx *gin.Context) {
  	req := &schema.GetTagInfoReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
  		permission.TagEdit,
  		permission.TagDelete,
  		permission.TagUnDelete,
  	})
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	req.CanEdit = canList[0]
  	req.CanDelete = canList[1]
  	req.CanRecover = canList[2]
  	req.CanMerge = middleware.GetUserIsAdminModerator(ctx)
  
  	resp, err := tc.tagService.GetTagInfo(ctx, req)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // GetTagWithPage get tag page
  // @Summary get tag page
  // @Description get tag page
  // @Tags Tag
  // @Produce json
  // @Param page query int false "page size"
  // @Param page_size query int false "page size"
  // @Param slug_name query string false "slug_name"
  // @Param query_cond query string false "query condition" Enums(popular, name, newest)
  // @Success 200 {object} handler.RespBody{data=pager.PageModel{list=[]schema.GetTagPageResp}}
  // @Router /answer/api/v1/tags/page [get]
  func (tc *TagController) GetTagWithPage(ctx *gin.Context) {
  	req := &schema.GetTagWithPageReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  
  	resp, err := tc.tagService.GetTagWithPage(ctx, req)
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if pager.ValPageOutOfRange(resp.Count, req.Page, req.PageSize) {
  		handler.HandleResponse(ctx, errors.NotFound(reason.RequestFormatError), nil)
  		return
  	}
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // GetFollowingTags get following tag list
  // @Summary get following tag list
  // @Description get following tag list
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Produce json
  // @Success 200 {object} handler.RespBody{data=[]schema.GetFollowingTagsResp}
  // @Router /answer/api/v1/tags/following [get]
  func (tc *TagController) GetFollowingTags(ctx *gin.Context) {
  	userID := middleware.GetLoginUserIDFromContext(ctx)
  	resp, err := tc.tagService.GetFollowingTags(ctx, userID)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // GetTagSynonyms get tag synonyms
  // @Summary get tag synonyms
  // @Description get tag synonyms
  // @Tags Tag
  // @Produce json
  // @Param tag_id query int true "tag id"
  // @Success 200 {object} handler.RespBody{data=schema.GetTagSynonymsResp}
  // @Router /answer/api/v1/tag/synonyms [get]
  func (tc *TagController) GetTagSynonyms(ctx *gin.Context) {
  	req := &schema.GetTagSynonymsReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	can, err := tc.rankService.CheckOperationPermission(ctx, req.UserID, permission.TagSynonym, "")
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	req.CanEdit = can
  
  	resp, err := tc.tagService.GetTagSynonyms(ctx, req)
  	handler.HandleResponse(ctx, err, resp)
  }
  
  // UpdateTagSynonym update tag
  // @Summary update tag
  // @Description update tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.UpdateTagSynonymReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag/synonym [put]
  func (tc *TagController) UpdateTagSynonym(ctx *gin.Context) {
  	req := &schema.UpdateTagSynonymReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	can, err := tc.rankService.CheckOperationPermission(ctx, req.UserID, permission.TagSynonym, "")
  	if err != nil {
  		handler.HandleResponse(ctx, err, nil)
  		return
  	}
  	if !can {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  
  	err = tc.tagService.UpdateTagSynonym(ctx, req)
  	handler.HandleResponse(ctx, err, nil)
  }
  
  // MergeTag merge tag
  // @Summary merge tag
  // @Description merge tag
  // @Security ApiKeyAuth
  // @Tags Tag
  // @Accept json
  // @Produce json
  // @Param data body schema.AddTagReq true "tag"
  // @Success 200 {object} handler.RespBody
  // @Router /answer/api/v1/tag/merge [post]
  func (tc *TagController) MergeTag(ctx *gin.Context) {
  	req := &schema.MergeTagReq{}
  	if handler.BindAndCheck(ctx, req) {
  		return
  	}
  
  	isAdminModerator := middleware.GetUserIsAdminModerator(ctx)
  	if !isAdminModerator {
  		handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
  		return
  	}
  
  	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
  	err := tc.tagService.MergeTag(ctx, req)
  
  	handler.HandleResponse(ctx, err, nil)
  }
  