system_update_article_logic.go 12.0 KB
package article

import (
	"context"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/core"
	"strconv"
	"unicode/utf8"

	"github.com/jinzhu/copier"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/message"

	"strings"

	"github.com/samber/lo"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/gateway/authlib"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/contextdata"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/tool/oss"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"

	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type SystemUpdateArticleLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	conn   transaction.Conn
}

func NewSystemUpdateArticleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SystemUpdateArticleLogic {
	return &SystemUpdateArticleLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
		conn:   svcCtx.DefaultDBConn(),
	}
}

func (l *SystemUpdateArticleLogic) SystemUpdateArticle(req *types.SystemArticleUpdateRequest) (resp *types.SystemArticleUpdateResponse, err error) {
	//var conn = l.svcCtx.DefaultDBConn()
	userToken := contextdata.GetUserTokenFromCtx(l.ctx)
	// 预处理参数
	if req.TargetUser == 0 {
		//TargetUser 设定为分发给所有人,清空 WhoRead
		req.WhoRead = make([]int64, 0)
	}
	// 检查文字数量
	if err = l.validateTextLimit(req); err != nil {
		return nil, err
	}
	// 获取当前用户信息
	userMe, err := l.svcCtx.ApiAuthService.MeInfo(l.ctx, authlib.RequestUserMeQuery{Token: req.AccessToken})
	if err != nil {
		return nil, xerr.NewErrMsgErr("获取当前用户信息失败", err)
	}
	operator := domain.UserSimple{
		Id:        userToken.UserId,
		Name:      userMe.User.NickName,
		Avatar:    userMe.User.Avatar,
		CompanyId: userToken.CompanyId,
		Company:   userMe.CurrentCompany.Name,
	}
	// 文章数据
	article, err := l.svcCtx.ArticleRepository.FindOne(l.ctx, l.conn, req.Id)
	if err != nil {
		return nil, xerr.NewErrMsgErr("帖子不存在", err)
	}
	//
	queryOption := domain.NewQueryOptions().WithFindOnly().MustWithKV("articleId", req.Id)
	_, sectionList, err := l.svcCtx.ArticleSectionRepository.Find(l.ctx, l.conn, queryOption)
	if err != nil {
		return nil, xerr.NewErrMsgErr("帖子不存在", err)
	}
	var oldBackup domain.ArticleBackup
	oldBackup.MakeBackup(operator, article, sectionList, "编辑")

	// 获取图片的尺寸大小
	images, err := l.getImages(req)
	if err != nil {
		return nil, err
	}
	//视频
	videos, err := l.getVideos(req)
	if err != nil {
		return nil, err
	}

	//检查文章可被哪些人查看
	whoRead, err := l.validateAndGetWhoRead(req, article)
	if err != nil {
		return nil, err
	}
	//检查文章可被哪些人评论
	whoReview := []int64{}
	if len(req.WhoReview) > 0 {
		whoReview = lo.Uniq(req.WhoReview)
	}
	//验证可评论
	if err = l.validateWhoReview(whoRead, whoReview, article); err != nil {
		return nil, err
	}
	//验证tag
	validTags := l.validateTags(req, article)
	if validTags != nil {
		return nil, validTags
	}
	article.Title = req.Title
	article.Version = article.Version + 1
	article.Images = images
	article.Videos = videos
	article.WhoRead = whoRead
	article.WhoReview = whoReview
	article.TargetUser = domain.ArticleTarget(req.TargetUser)
	article.Tags = req.Tags
	//文章内容
	articleSections := l.getSections(req, article)
	//设置内容概要
	article.SetSummary(articleSections)

	err = transaction.UseTrans(l.ctx, l.conn.DB(), func(ctx context.Context, c transaction.Conn) error {
		_, err = l.svcCtx.ArticleRepository.Update(l.ctx, c, article)
		if err != nil {
			return xerr.NewErrMsgErr("保存文章失败", err)
		}
		//文章内容
		updateSectionIds := []int64{}
		updateSection := []*domain.ArticleSection{}
		for _, item := range articleSections {
			if item.Id > 0 {
				section, err := l.svcCtx.ArticleSectionRepository.FindOne(ctx, c, item.Id)
				if err != nil {
					return xerr.NewErrMsgErr("获取文章段落内容失败", err)
				}
				section.Content = item.Content
				section.ArticleId = item.ArticleId
				section.Version = item.Version
				section.SortBy = item.SortBy
				section.Images = item.Images
				section.ParagraphType = item.ParagraphType
				section.ParagraphTemplate = item.ParagraphTemplate
				section.CompanyId = item.CompanyId
				_, err = l.svcCtx.ArticleSectionRepository.Update(ctx, c, section)
				if err != nil {
					return xerr.NewErrMsgErr("保存文章段落内容失败", err)
				}
			} else {
				_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, item)
				if err != nil {
					return xerr.NewErrMsgErr("保存文章段落内容失败", err)
				}
			}
			updateSectionIds = append(updateSectionIds, item.Id)
			updateSection = append(updateSection, item)
		}
		if len(updateSectionIds) > 0 {
			queryOption := domain.NewQueryOptions().WithKV("articleId", article.Id).WithKV("notIds", updateSectionIds)
			err = l.svcCtx.ArticleSectionRepository.DeleteBy(ctx, c, queryOption)
			if err != nil {
				return xerr.NewErrMsgErr("保存文章内容失败", err)
			}
		}
		var backup domain.ArticleBackup
		backup.MakeBackup(operator, article, updateSection, "编辑")

		if ok := backup.CheckChangeField(&oldBackup); ok {
			_, err = l.svcCtx.ArticleBackupRepository.Insert(ctx, c, &backup)
			if err != nil {
				return xerr.NewErrMsgErr("保存文章内容失败", err)
			}
		}
		//文章定性
		err = l.setTags(c, article)
		if err != nil {
			return err
		}
		return nil
	}, true)
	resp = &types.SystemArticleUpdateResponse{
		Id:           article.Id,
		Title:        article.Title,
		Images:       req.Images,
		CreatedAt:    article.CreatedAt,
		CountLove:    article.CountLove,
		CountComment: article.CountComment,
		Show:         int(article.Show),
		TargetUser:   int(article.TargetUser),
		Tags:         article.Tags,
	}
	_ = copier.Copy(&resp.Videos, videos)
	return
}

// validateTextLimit 验证输入文本长度
func (l *SystemUpdateArticleLogic) validateTextLimit(req *types.SystemArticleUpdateRequest) error {
	wordNum := 0
	for i := range req.Section {
		num := utf8.RuneCountInString(req.Section[i].Content)
		if num > 1000 {
			return xerr.NewErrMsg("内容最多只能输入1000字")
		}
		wordNum += num
	}
	if wordNum > 1000 {
		//return xerr.NewErrMsg("内容最多只能输入1000字")
	}
	return nil
}

// validateAndGetWhoRead 验证并获取可阅读
func (l *SystemUpdateArticleLogic) validateAndGetWhoRead(req *types.SystemArticleUpdateRequest, article *domain.Article) ([]int64, error) {
	whoRead := make([]int64, 0)
	if len(req.WhoRead) > 0 {
		whoRead = lo.Uniq(req.WhoRead)
		var u *domain.User
		var err error
		for _, val := range whoRead {
			u, err = l.svcCtx.UserRepository.FindOne(l.ctx, l.conn, val)
			if err != nil {
				return whoRead, xerr.NewErrMsgErr("文章可查看人设置错误", err)
			}
			if u.CompanyId != article.CompanyId {
				return whoRead, xerr.NewErrMsg("文章可查看人设置错误")
			}
		}
	}
	return whoRead, nil
}

// validateWhoReview 验证可评论
func (l *SystemUpdateArticleLogic) validateWhoReview(whoRead []int64, whoReview []int64, article *domain.Article) error {
	//有指定可查看人的情况
	if len(whoRead) > 0 {
		if len(whoReview) > 0 {
			// 检查 whoRead 是否 完全包含 whoReview
			ok := lo.Every(whoRead, whoReview)
			if !ok {
				return xerr.NewErrMsg("文章可评论人设置错误")
			}
		}
		if len(whoReview) == 0 {
			//有指定可查看人 ,但未指定可评论人
			return xerr.NewErrMsg("文章可评论人设置错误")
		}
	}
	//没有指定可查看人的情况
	if len(whoRead) == 0 {
		if len(whoReview) > 0 {
			// 未指定可查看人(全员可看),有指定可评论人,
			var u *domain.User
			var err error
			for _, val := range whoReview {
				u, err = l.svcCtx.UserRepository.FindOne(l.ctx, l.conn, val)
				if err != nil {
					return xerr.NewErrMsgErr("文章可评论人设置错误", err)
				}
				if u.CompanyId != article.CompanyId {
					return xerr.NewErrMsg("文章可评论人设置错误")
				}
			}
		}
	}
	return nil
}

// validateTags 验证标签
func (l *SystemUpdateArticleLogic) validateTags(req *types.SystemArticleUpdateRequest, article *domain.Article) error {
	if len(req.Tags) > 0 {
		req.Tags = lo.Uniq(req.Tags)
		for _, value := range req.Tags {
			t, err := l.svcCtx.ArticleTagRepository.FindOne(l.ctx, l.conn, value)
			if err != nil {
				return xerr.NewErrMsgErr("文章标签设置错误", err)
			}
			if t.CompanyId != article.CompanyId {
				return xerr.NewErrMsgErr("文章标签设置错误", err)
			}
		}
	}
	return nil
}

// getImages 获取图片信息
func (l *SystemUpdateArticleLogic) getImages(req *types.SystemArticleUpdateRequest) ([]domain.Image, error) {
	// 获取图片的尺寸大小
	images := make([]domain.Image, 0)
	for _, val := range req.Images {
		fInfo, err := oss.GetImageInfo(val)
		if err != nil {
			return images, xerr.NewErrMsg("获取图片失败")
		}
		w, _ := strconv.Atoi(fInfo.ImageWidth.Value)
		h, _ := strconv.Atoi(fInfo.ImageHeight.Value)
		images = append(images, domain.Image{
			Url:    val,
			Width:  w,
			Height: h,
		})
	}
	return images, nil
}

// getVideo 获取视频信息
func (l *SystemUpdateArticleLogic) getVideos(req *types.SystemArticleUpdateRequest) ([]domain.Video, error) {
	videos := make([]domain.Video, 0)
	if len(req.Videos) > 0 {
		for _, item := range req.Videos {
			cover, w, h, err := oss.GetVideoCover(item.Url)
			if err != nil {
				return videos, xerr.NewErrMsg("获取视频信息失败")
			}
			videos = append(videos, domain.Video{
				Url:    item.Url,
				Cover:  cover,
				Width:  w,
				Height: h,
			})
		}
	}
	return videos, nil
}

func (l *SystemUpdateArticleLogic) getSections(req *types.SystemArticleUpdateRequest, article *domain.Article) []*domain.ArticleSection {
	articleSections := make([]*domain.ArticleSection, 0)
	sortBy := 1
	lo.ForEach(req.Section, func(item types.ArticleSection, index int) {
		strList := strings.Split(item.Content, "\n")
		for key, value := range strList {
			if value == "" && item.ParagraphType == domain.ParagraphTypeNone {
				continue
			}
			section := domain.ArticleSection{
				CompanyId:         article.CompanyId,
				Version:           article.Version,
				ArticleId:         article.Id,
				Content:           value,
				SortBy:            sortBy,
				Images:            item.Images,
				ParagraphType:     item.ParagraphType,
				ParagraphTemplate: core.NewDomainParagraph(item.ParagraphTemplate),
			}
			if key == 0 {
				section.Id = item.Id
			}
			articleSections = append(articleSections, &section)
			sortBy++
		}
	})
	return articleSections
}

// setTags 设置定性标签
func (l *SystemUpdateArticleLogic) setTags(conn transaction.Conn, article *domain.Article) error {
	_, oldTags, err := l.svcCtx.ArticleAndTagRepository.Find(l.ctx, conn, domain.NewQueryOptions().WithFindOnly().WithKV("articleId", article.Id))
	if err != nil {
		return xerr.NewErrMsgErr("检查文章的标签失败", err)
	}
	//删除旧标签
	for _, v := range oldTags {
		_, err = l.svcCtx.ArticleAndTagRepository.Delete(l.ctx, conn, v)
		if err != nil {
			return xerr.NewErrMsg(err.Error())
		}
	}
	if len(article.Tags) > 0 {
		for _, tagId := range article.Tags {
			articleTag := &domain.ArticleAndTag{
				CompanyId: article.CompanyId,
				ArticleId: article.Id,
				TagId:     tagId,
			}
			_, err = l.svcCtx.ArticleAndTagRepository.Insert(l.ctx, conn, articleTag)
			if err != nil {
				return xerr.NewErrMsg(err.Error())
			}
		}
		//发送定性通知
		messageLogic := message.NewMiniSystemLogic(l.ctx, l.svcCtx)
		err = messageLogic.ArticleDefined(conn, article.CompanyId, article.AuthorId, article.Title)
		if err != nil {
			return xerr.NewErrMsg(err.Error())
		}
	}
	return nil
}