system_create_article_logic.go 10.8 KB
package article

import (
	"context"
	"github.com/pkg/errors"
	"github.com/samber/lo"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/core"
	"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/message"
	"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"
	"html/template"
	"strconv"
	"strings"
	"unicode/utf8"

	"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 SystemCreateArticleLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	conn   transaction.Conn
}

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

func (l *SystemCreateArticleLogic) SystemCreateArticle(req *types.SystemArticleCreateRequest) (resp *types.SystemArticleCreateResponse, err error) {
	err = l.validate(req)
	if err != nil {
		return nil, xerr.NewErrMsg(err.Error())
	}
	// 检查发布人
	author, err := l.svcCtx.UserRepository.FindOne(l.ctx, l.conn, req.AuthorId)
	if err != nil {
		return nil, xerr.NewErrMsgErr("创建文章内容失败", err)
	}
	companyInfo, err := l.svcCtx.CompanyRepository.FindOne(l.ctx, l.conn, author.CompanyId)
	if err != nil {
		return nil, xerr.NewErrMsgErr("创建文章内容失败", err)
	}
	//文章作者
	articleAuthor := domain.UserSimple{
		Id:        author.Id,
		Name:      author.Name,
		Avatar:    author.Avatar,
		Position:  author.Position,
		Company:   companyInfo.Name,
		CompanyId: author.CompanyId,
	}
	//图片
	images, err := l.getImages(req)
	if err != nil {
		return nil, xerr.NewErrMsg(err.Error())
	}
	//视频
	videos, err := l.getVideos(req)
	if err != nil {
		return nil, xerr.NewErrMsg(err.Error())
	}
	userToken := contextdata.GetUserTokenFromCtx(l.ctx)
	// 获取当前用户信息
	userMe, err := l.svcCtx.ApiAuthService.MeInfo(l.ctx, authlib.RequestUserMeQuery{Token: req.AccessToken})
	if err != nil {
		return nil, xerr.NewErrMsgErr("获取当前用户信息失败", err)
	}
	article := &domain.Article{
		CompanyId:  companyInfo.Id,
		AuthorId:   req.AuthorId,
		Author:     articleAuthor,
		Title:      req.Title,
		Images:     images,
		Videos:     videos,
		WhoRead:    make([]int64, 0),
		WhoReview:  make([]int64, 0),
		Location:   domain.Location{},
		TargetUser: domain.ArticleTarget(req.TargetUser),
		Show:       domain.ArticleShowEnable,
		Tags:       make([]int64, 0),
		MatchUrl:   make(map[string]string),
		Source:     domain.ArticleSourceOperator,
		Operator: domain.Operator{
			Id:   userToken.UserId,
			Name: userMe.User.NickName,
		},
	}
	if len(req.MatchUrl) > 0 {
		article.MatchUrl = req.MatchUrl
	}
	//检查文章可被哪些人查看
	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
	}
	article.WhoRead = whoRead
	article.WhoReview = whoReview
	sections := NewArticleSectionFromParagraphs(req.Paragraphs, author.CompanyId, 0) //l.getSections(req, article)
	//设置内容摘要
	article.SetSummary(sections)
	//草稿ID
	if req.ArticleDraftId > 0 {
		articleDraft, err := l.svcCtx.ArticleDraftOperationRepository.FindOne(l.ctx, l.conn, req.ArticleDraftId)
		if err != nil {
			return nil, xerr.NewErrMsg("草稿不存在")
		}
		if articleDraft.CompanyId != userToken.CompanyId {
			return nil, xerr.NewErrMsg("您没有权限")
		}
	}
	//保存数据
	err = transaction.UseTrans(l.ctx, l.conn.DB(), func(ctx context.Context, conn transaction.Conn) error {
		_, err = l.svcCtx.ArticleRepository.Insert(ctx, conn, article)
		if err != nil {
			return xerr.NewErrMsgErr("创建文章失败", err)
		}
		for i := range sections {
			sections[i].ArticleId = article.Id
			_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, conn, sections[i])
			if err != nil {
				return xerr.NewErrMsgErr("创建文章内容失败", err)
			}
		}
		// 生成 备份数据
		var backupData domain.ArticleBackup
		backupData.MakeBackup(articleAuthor, article, sections, "原始版本")
		_, err = l.svcCtx.ArticleBackupRepository.Insert(l.ctx, conn, &backupData)
		if err != nil {
			return xerr.NewErrMsgErr("创建文章失败", err)
		}
		//删除草稿
		if req.ArticleDraftId > 0 {
			_, err = l.svcCtx.ArticleDraftOperationRepository.Delete(l.ctx, conn, &domain.ArticleDraftOperation{Id: req.ArticleDraftId})
			if err != nil {
				return xerr.NewErrMsg("删除草稿失败")
			}
		}
		//发送订阅消息
		err = message.NewMiniSubscribeLogic(l.ctx, l.svcCtx).FollowArticle(conn, article)
		if err != nil {
			return xerr.NewErrMsgErr("创建文章失败", err)
		}
		return nil
	}, true)
	if err != nil {
		return nil, xerr.NewErrMsgErr("创建文章失败", err)
	}
	resp = &types.SystemArticleCreateResponse{
		Id:         article.Id,
		Title:      article.Title,
		Content:    req.Content,
		AuthorId:   req.AuthorId,
		Images:     req.Images,
		Videos:     req.Videos,
		TargetUser: req.TargetUser,
		WhoRead:    whoRead,
		WhoReview:  whoReview,
	}
	return
}

// validate 验证
func (l *SystemCreateArticleLogic) validate(req *types.SystemArticleCreateRequest) error {
	//文章标题
	if utf8.RuneCountInString(req.Title) > 64 {
		return errors.New("标题最多只能输入64字")
	}
	//文章内容
	if utf8.RuneCountInString(req.Content) > 1000 {
		return errors.New("内容最多只能输入1000字")
	}
	//图片
	if len(req.Images) > 9 {
		return errors.New("图片数量最多9张")
	}
	return nil
}

// getImages 获取图片信息
func (l *SystemCreateArticleLogic) getImages(req *types.SystemArticleCreateRequest) ([]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 *SystemCreateArticleLogic) getVideos(req *types.SystemArticleCreateRequest) ([]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
}

// validateAndGetWhoRead 验证并获取可阅读
func (l *SystemCreateArticleLogic) validateAndGetWhoRead(req *types.SystemArticleCreateRequest, 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 *SystemCreateArticleLogic) 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
}

// getSections 文章内容
func (l *SystemCreateArticleLogic) getSections(req *types.SystemArticleCreateRequest, article *domain.Article) []*domain.ArticleSection {
	articleSections := make([]*domain.ArticleSection, 0)
	sortBy := 1
	lo.ForEach(req.Paragraphs, func(paragraph types.Paragraph, index int) {
		//去除空
		strList := []string{paragraph.Text}
		// 无模板样式的
		if paragraph.Type == 0 {
			strList = strings.Split(paragraph.Text, "\n")
		}
		for i2 := range strList {
			newStr := template.HTMLEscapeString(strList[i2])
			section := domain.ArticleSection{
				CompanyId: article.CompanyId,
				Version:   article.Version,
				ArticleId: article.Id,
				Content:   newStr,
				SortBy:    sortBy,

				Images:            paragraph.Images,
				ParagraphType:     paragraph.Type,
				ParagraphTemplate: core.NewDomainParagraph(paragraph),
			}
			articleSections = append(articleSections, &section)
			sortBy++
		}
	})
	return articleSections
}

func NewArticleSectionFromParagraphs(paragraphs []types.Paragraph, companyId int64, articleId int64) []*domain.ArticleSection {
	articleSections := make([]*domain.ArticleSection, 0)
	sortBy := 1
	lo.ForEach(paragraphs, func(paragraph types.Paragraph, index int) {
		//去除空
		strList := []string{paragraph.Text}
		// 无模板样式的
		if paragraph.Type == 0 {
			strList = strings.Split(paragraph.Text, "\n")
		}
		for i2 := range strList {
			newStr := template.HTMLEscapeString(strList[i2])
			section := domain.ArticleSection{
				CompanyId: companyId,
				ArticleId: articleId,
				Content:   newStr,
				SortBy:    sortBy,

				Images:            paragraph.Images,
				ParagraphType:     paragraph.Type,
				ParagraphTemplate: core.NewDomainParagraph(paragraph),
			}
			section.Images = paragraph.Images
			articleSections = append(articleSections, &section)
			sortBy++
		}
	})
	return articleSections
}