Merge remote-tracking branch 'origin/ai/0.4.0/tag' into test

# Conflicts:
#	docs/docs.go
#	docs/swagger.json
#	docs/swagger.yaml
#	internal/base/constant/constant.go
#	internal/base/reason/reason.go
#	internal/controller_backyard/siteinfo_controller.go
#	internal/router/answer_api_router.go
#	internal/schema/siteinfo_schema.go
#	internal/service/siteinfo/siteinfo_service.go
This commit is contained in:
LinkinStar 2022-11-15 10:54:01 +08:00
commit f17c28ae1d
17 changed files with 265 additions and 13 deletions

View File

@ -130,7 +130,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
revisionRepo := revision.NewRevisionRepo(dataData, uniqueIDRepo)
revisionService := revision_common.NewRevisionService(revisionRepo, userRepo)
followRepo := activity_common.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
tagService := tag2.NewTagService(tagRepo, revisionService, followRepo)
tagService := tag2.NewTagService(tagRepo, revisionService, followRepo, siteInfoCommonService)
tagController := controller.NewTagController(tagService, rankService)
followFollowRepo := activity.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
followService := follow.NewFollowService(followFollowRepo, followRepo, tagRepo)
@ -138,7 +138,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
collectionRepo := collection.NewCollectionRepo(dataData, uniqueIDRepo)
collectionGroupRepo := collection.NewCollectionGroupRepo(dataData)
tagRelRepo := tag.NewTagRelRepo(dataData)
tagCommonService := tagcommon.NewTagCommonService(tagRepo, tagRelRepo, revisionService)
tagCommonService := tagcommon.NewTagCommonService(tagRepo, tagRelRepo, revisionService, siteInfoCommonService)
collectionCommon := collectioncommon.NewCollectionCommon(collectionRepo)
answerCommon := answercommon.NewAnswerCommon(answerRepo)
metaRepo := meta.NewMetaRepo(dataData)

View File

@ -6422,4 +6422,4 @@
"in": "header"
}
}
}
}

View File

@ -74,6 +74,8 @@ backend:
tag:
not_found:
other: "Tag not found."
recommend_tag_not_found:
other: "Recommend Tag is not exist."
theme:
not_found:
other: "Theme not found."

View File

@ -57,3 +57,4 @@ const (
SiteTypeWrite = "write"
SiteTypeLegal = "legal"
)

View File

@ -44,4 +44,5 @@ const (
InstallConfigFailed = "error.install.create_config_failed"
SiteInfoNotFound = "error.site_info.not_found"
UploadFileSourceUnsupported = "error.upload.source_unsupported"
RecommendTagNotExist = "error.tag.recommend_tag_not_found"
)

View File

@ -36,7 +36,8 @@ func (tc *TagController) SearchTagLike(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
userinfo := middleware.GetUserInfoFromContext(ctx)
req.IsAdmin = userinfo.IsAdmin
resp, err := tc.tagService.SearchTagLike(ctx, req)
handler.HandleResponse(ctx, err, resp)
}

View File

@ -5,4 +5,5 @@ type UserCacheInfo struct {
UserID string `json:"user_id"`
UserStatus int `json:"user_status"`
EmailStatus int `json:"email_status"`
IsAdmin bool `json:"is_admin"`
}

View File

@ -21,6 +21,8 @@ type Tag struct {
FollowCount int `xorm:"not null default 0 INT(11) follow_count"`
QuestionCount int `xorm:"not null default 0 INT(11) question_count"`
Status int `xorm:"not null default 1 INT(11) status"`
Recommend bool `xorm:"not null default false BOOL recommend"`
Reserved bool `xorm:"not null default false BOOL reserved"`
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
}

View File

@ -71,12 +71,54 @@ func (tr *tagRepo) GetTagBySlugName(ctx context.Context, slugName string) (tagIn
}
// GetTagListByName get tag list all like name
func (tr *tagRepo) GetTagListByName(ctx context.Context, name string, limit int) (tagList []*entity.Tag, err error) {
func (tr *tagRepo) GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error) {
tagList = make([]*entity.Tag, 0)
session := tr.data.DB.Where("slug_name LIKE ?", name+"%")
cond := &entity.Tag{}
session := tr.data.DB.Where("")
if name != "" {
session.Where("slug_name LIKE ?", name+"%")
} else {
cond.Recommend = true
}
session.Where(builder.Eq{"status": entity.TagStatusAvailable})
session.Limit(limit).Asc("slug_name")
err = session.Find(&tagList)
if !hasReserved {
cond.Recommend = false
session.UseBool("recommend", "reserved")
} else {
session.UseBool("recommend")
}
err = session.Find(&tagList, cond)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
func (tr *tagRepo) GetRecommendTagList(ctx context.Context) (tagList []*entity.Tag, err error) {
tagList = make([]*entity.Tag, 0)
cond := &entity.Tag{}
session := tr.data.DB.Where("")
cond.Recommend = true
session.Where(builder.Eq{"status": entity.TagStatusAvailable})
session.Asc("slug_name")
session.UseBool("recommend")
err = session.Find(&tagList, cond)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
func (tr *tagRepo) GetReservedTagList(ctx context.Context) (tagList []*entity.Tag, err error) {
tagList = make([]*entity.Tag, 0)
cond := &entity.Tag{}
session := tr.data.DB.Where("")
cond.Reserved = true
session.Where(builder.Eq{"status": entity.TagStatusAvailable})
session.Asc("slug_name")
session.UseBool("reserved")
err = session.Find(&tagList, cond)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -137,6 +179,24 @@ func (tr *tagRepo) UpdateTagSynonym(ctx context.Context, tagSlugNameList []strin
return
}
func (tr *tagRepo) UpdateTagsAttribute(ctx context.Context, tags []string, attribute string, value bool) (err error) {
bean := &entity.Tag{}
switch attribute {
case "recommend":
bean.Recommend = value
case "reserved":
bean.Reserved = value
default:
return
}
session := tr.data.DB.In("slug_name", tags).Cols(attribute).UseBool(attribute)
_, err = session.Update(bean)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetTagByID get tag one
func (tr *tagRepo) GetTagByID(ctx context.Context, tagID string) (
tag *entity.Tag, exist bool, err error,

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/data"

View File

@ -29,6 +29,7 @@ type TagResp struct {
DisplayName string `json:"display_name"`
// if main tag slug name is not empty, this tag is synonymous with the main tag
MainTagSlugName string `json:"main_tag_slug_name"`
Recommend bool `json:"recommend"`
}
type SearchResp struct {

View File

@ -11,7 +11,8 @@ import (
// SearchTagLikeReq get tag list all request
type SearchTagLikeReq struct {
// tag
Tag string `validate:"required,gt=0,lte=35" form:"tag"`
Tag string `validate:"omitempty" form:"tag"`
IsAdmin bool `json:"-"`
}
// GetTagInfoReq get tag info request
@ -218,3 +219,9 @@ type GetFollowingTagsResp struct {
// if main tag slug name is not empty, this tag is synonymous with the main tag
MainTagSlugName string `json:"main_tag_slug_name"`
}
type SearchTagLikeResp struct {
SlugName string `json:"slug_name"`
Recommend bool `json:"recommend"`
Reserved bool `json:"reserved"`
}

View File

@ -43,6 +43,7 @@ func (as *AuthService) GetUserCacheInfo(ctx context.Context, accessToken string)
log.Infof("user status updated: %+v", cacheInfo)
userCacheInfo.UserStatus = cacheInfo.UserStatus
userCacheInfo.EmailStatus = cacheInfo.EmailStatus
userCacheInfo.IsAdmin = cacheInfo.IsAdmin
// update current user cache info
err := as.authRepo.SetUserCacheInfo(ctx, accessToken, userCacheInfo)
if err != nil {

View File

@ -105,6 +105,16 @@ func (qs *QuestionService) CloseMsgList(ctx context.Context, lang i18n.Language)
// AddQuestion add question
func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.QuestionAdd) (questionInfo *schema.QuestionInfo, err error) {
recommendExist, err := qs.tagCommon.ExistRecommend(ctx, req.Tags)
if err != nil {
return
}
if !recommendExist {
err = fmt.Errorf("recommend is not exist")
err = errors.BadRequest(reason.RecommendTagNotExist).WithError(err).WithStack()
return
}
questionInfo = &schema.QuestionInfo{}
question := &entity.Question{}
now := time.Now()

View File

@ -3,7 +3,9 @@ package tag
import (
"context"
"encoding/json"
"github.com/answerdev/answer/internal/service/revision_common"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/answerdev/answer/pkg/htmltext"
"github.com/answerdev/answer/internal/base/pager"
@ -24,28 +26,35 @@ type TagService struct {
tagRepo tagcommon.TagRepo
revisionService *revision_common.RevisionService
followCommon activity_common.FollowRepo
siteInfoService *siteinfo_common.SiteInfoCommonService
}
// NewTagService new tag service
func NewTagService(
tagRepo tagcommon.TagRepo,
revisionService *revision_common.RevisionService,
followCommon activity_common.FollowRepo) *TagService {
followCommon activity_common.FollowRepo,
siteInfoService *siteinfo_common.SiteInfoCommonService) *TagService {
return &TagService{
tagRepo: tagRepo,
revisionService: revisionService,
followCommon: followCommon,
siteInfoService: siteInfoService,
}
}
// SearchTagLike get tag list all
func (ts *TagService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []string, err error) {
tags, err := ts.tagRepo.GetTagListByName(ctx, req.Tag, 5)
func (ts *TagService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []schema.SearchTagLikeResp, err error) {
tags, err := ts.tagRepo.GetTagListByName(ctx, req.Tag, 5, req.IsAdmin)
if err != nil {
return
}
for _, tag := range tags {
resp = append(resp, tag.SlugName)
item := schema.SearchTagLikeResp{}
item.SlugName = tag.SlugName
item.Recommend = tag.Recommend
item.Reserved = tag.Reserved
resp = append(resp, item)
}
return resp, nil
}

View File

@ -6,6 +6,7 @@ import (
"strings"
"github.com/answerdev/answer/internal/service/revision_common"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
@ -16,7 +17,7 @@ type TagRepo interface {
AddTagList(ctx context.Context, tagList []*entity.Tag) (err error)
GetTagListByIDs(ctx context.Context, ids []string) (tagList []*entity.Tag, err error)
GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error)
GetTagListByName(ctx context.Context, name string, limit int) (tagList []*entity.Tag, err error)
GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error)
GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error)
RemoveTag(ctx context.Context, tagID string) (err error)
UpdateTag(ctx context.Context, tag *entity.Tag) (err error)
@ -25,6 +26,9 @@ type TagRepo interface {
GetTagByID(ctx context.Context, tagID string) (tag *entity.Tag, exist bool, err error)
GetTagList(ctx context.Context, tag *entity.Tag) (tagList []*entity.Tag, err error)
GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (tagList []*entity.Tag, total int64, err error)
GetRecommendTagList(ctx context.Context) (tagList []*entity.Tag, err error)
GetReservedTagList(ctx context.Context) (tagList []*entity.Tag, err error)
UpdateTagsAttribute(ctx context.Context, tags []string, attribute string, value bool) (err error)
}
type TagRelRepo interface {
@ -42,19 +46,86 @@ type TagCommonService struct {
revisionService *revision_common.RevisionService
tagRepo TagRepo
tagRelRepo TagRelRepo
siteInfoService *siteinfo_common.SiteInfoCommonService
}
// NewTagCommonService new tag service
func NewTagCommonService(tagRepo TagRepo, tagRelRepo TagRelRepo,
revisionService *revision_common.RevisionService,
siteInfoService *siteinfo_common.SiteInfoCommonService,
) *TagCommonService {
return &TagCommonService{
tagRepo: tagRepo,
tagRelRepo: tagRelRepo,
revisionService: revisionService,
siteInfoService: siteInfoService,
}
}
func (ts *TagCommonService) GetSiteWriteRecommendTag(ctx context.Context) (tags []string, err error) {
tags = make([]string, 0)
list, err := ts.tagRepo.GetRecommendTagList(ctx)
for _, item := range list {
tags = append(tags, item.SlugName)
}
return tags, nil
}
func (ts *TagCommonService) SetSiteWriteRecommendTag(ctx context.Context, tags []string, required bool, userID string) (err error) {
err = ts.UpdateTag(ctx, tags, userID)
if err != nil {
return err
}
err = ts.SetTagsAttribute(ctx, tags, "recommend", true)
if err != nil {
return err
}
return nil
}
func (ts *TagCommonService) GetSiteWriteReservedTag(ctx context.Context) (tags []string, err error) {
tags = make([]string, 0)
list, err := ts.tagRepo.GetReservedTagList(ctx)
for _, item := range list {
tags = append(tags, item.SlugName)
}
return tags, nil
}
func (ts *TagCommonService) SetSiteWriteReservedTag(ctx context.Context, tags []string, userID string) (err error) {
err = ts.UpdateTag(ctx, tags, userID)
if err != nil {
return err
}
err = ts.SetTagsAttribute(ctx, tags, "reserved", true)
if err != nil {
return err
}
return nil
}
// SetTagsAttribute
func (ts *TagCommonService) SetTagsAttribute(ctx context.Context, tags []string, attribute string, value bool) (err error) {
var tagslist []string
switch attribute {
case "recommend":
tagslist, err = ts.GetSiteWriteRecommendTag(ctx)
case "reserved":
tagslist, err = ts.GetSiteWriteReservedTag(ctx)
default:
return
}
err = ts.tagRepo.UpdateTagsAttribute(ctx, tagslist, attribute, false)
if err != nil {
return err
}
err = ts.tagRepo.UpdateTagsAttribute(ctx, tags, attribute, value)
if err != nil {
return err
}
return nil
}
// GetTagListByName
func (ts *TagCommonService) GetTagListByName(ctx context.Context, tagName string) (tagInfo *entity.Tag, exist bool, err error) {
tagName = strings.ToLower(tagName)
@ -68,6 +139,23 @@ func (ts *TagCommonService) GetTagListByNames(ctx context.Context, tagNames []st
return ts.tagRepo.GetTagListByNames(ctx, tagNames)
}
func (ts *TagCommonService) ExistRecommend(ctx context.Context, tags []*schema.TagItem) (bool, error) {
tagNames := make([]string, 0)
for _, item := range tags {
tagNames = append(tagNames, item.SlugName)
}
list, err := ts.GetTagListByNames(ctx, tagNames)
if err != nil {
return false, err
}
for _, item := range list {
if item.Recommend {
return true, nil
}
}
return false, nil
}
//
// GetObjectTag get object tag
@ -90,6 +178,7 @@ func (ts *TagCommonService) GetObjectTag(ctx context.Context, objectId string) (
SlugName: tagInfo.SlugName,
DisplayName: tagInfo.DisplayName,
MainTagSlugName: tagInfo.MainTagSlugName,
Recommend: tagInfo.Recommend,
})
}
return objTags, nil
@ -130,6 +219,69 @@ func (ts *TagCommonService) BatchGetObjectTag(ctx context.Context, objectIds []s
return objectIDTagMap, nil
}
func (ts *TagCommonService) UpdateTag(ctx context.Context, tags []string, userID string) (err error) {
if len(tags) == 0 {
return nil
}
thisTagNameList := make([]string, 0)
thisTagIDList := make([]string, 0)
for _, t := range tags {
t = strings.ToLower(t)
thisTagNameList = append(thisTagNameList, t)
}
// find tags name
tagListInDb, err := ts.tagRepo.GetTagListByNames(ctx, thisTagNameList)
if err != nil {
return err
}
tagInDbMapping := make(map[string]*entity.Tag)
for _, tag := range tagListInDb {
tagInDbMapping[tag.SlugName] = tag
thisTagIDList = append(thisTagIDList, tag.ID)
}
addTagList := make([]*entity.Tag, 0)
for _, tag := range tags {
_, ok := tagInDbMapping[tag]
if ok {
continue
}
item := &entity.Tag{}
item.SlugName = tag
item.DisplayName = tag
item.OriginalText = ""
item.ParsedText = ""
item.Status = entity.TagStatusAvailable
addTagList = append(addTagList, item)
}
if len(addTagList) > 0 {
err = ts.tagRepo.AddTagList(ctx, addTagList)
if err != nil {
return err
}
for _, tag := range addTagList {
thisTagIDList = append(thisTagIDList, tag.ID)
revisionDTO := &schema.AddRevisionDTO{
UserID: userID,
ObjectID: tag.ID,
Title: tag.SlugName,
}
tagInfoJson, _ := json.Marshal(tag)
revisionDTO.Content = string(tagInfoJson)
err = ts.revisionService.AddRevision(ctx, revisionDTO, true)
if err != nil {
return err
}
}
}
return nil
}
// ObjectChangeTag change object tag list
func (ts *TagCommonService) ObjectChangeTag(ctx context.Context, objectTagData *schema.TagChange) (err error) {
if len(objectTagData.Tags) == 0 {

View File

@ -112,6 +112,7 @@ func (us *UserService) EmailLogin(ctx context.Context, req *schema.UserEmailLogi
UserID: userInfo.ID,
EmailStatus: userInfo.MailStatus,
UserStatus: userInfo.Status,
IsAdmin: userInfo.IsAdmin,
}
resp.AccessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)
if err != nil {
@ -322,6 +323,7 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo
UserID: userInfo.ID,
EmailStatus: userInfo.MailStatus,
UserStatus: userInfo.Status,
IsAdmin: userInfo.IsAdmin,
}
resp.AccessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)
if err != nil {
@ -408,6 +410,7 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
UserID: userInfo.ID,
EmailStatus: userInfo.MailStatus,
UserStatus: userInfo.Status,
IsAdmin: userInfo.IsAdmin,
}
resp.AccessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)
if err != nil {