作者 tangxvhui

调整 文章编辑历史的保存

... ... @@ -168,12 +168,14 @@ type (
List []MiniArticleBackupItem `json:"list"`
}
MiniArticleBackupItem {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Images []string `json:"images"`
CreatedAt int64 `json:"createdAt"`
Location Location `json:"location"`
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Images []string `json:"images"`
Videos []Video `json:"videos"`
ChangeField []string `json:"changeField"`
CreatedAt int64 `json:"createdAt"`
Location Location `json:"location"`
}
)
// 标记人员浏览了那个文章
... ...
... ... @@ -51,17 +51,29 @@ func (l *MiniArticleBackupSearchLogic) MiniArticleBackupSearch(req *types.MiniAr
for _, val2 := range backupList[i].Images {
images = append(images, val2.Url)
}
videos := []types.Video{}
for _, val2 := range backupList[i].Videos {
videos = append(videos, types.Video{
Url: val2.Url,
Cover: val2.Cover,
Width: val2.Width,
Height: val2.Height,
})
}
resp.List[i] = types.MiniArticleBackupItem{
Id: backupList[i].Id,
Title: backupList[i].Title,
Content: content.String(),
Images: images,
Videos: videos,
CreatedAt: backupList[i].CreatedAt,
Location: types.Location{
Longitude: backupList[i].Location.Longitude,
Latitude: backupList[i].Location.Latitude,
Descript: backupList[i].Location.Descript,
},
ChangeField: backupList[i].ChangeField,
}
}
return resp, nil
... ...
... ... @@ -127,7 +127,7 @@ func (l *MiniCreateArticleLogic) MiniCreateArticle(req *types.MiniArticleCreateR
}
//切分文章分段
sectionList := []domain.ArticleSection{}
sectionList := []*domain.ArticleSection{}
newStr := ""
for i := range req.Section {
strList := strings.Split(req.Section[i], "\n")
... ... @@ -141,7 +141,7 @@ func (l *MiniCreateArticleLogic) MiniCreateArticle(req *types.MiniArticleCreateR
SortBy: len(sectionList),
TotalComment: 0,
}
sectionList = append(sectionList, newSection)
sectionList = append(sectionList, &newSection)
}
}
... ... @@ -185,11 +185,20 @@ func (l *MiniCreateArticleLogic) MiniCreateArticle(req *types.MiniArticleCreateR
for i := range sectionList {
sectionList[i].ArticleId = newArticle.Id
_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, &sectionList[i])
_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, sectionList[i])
if err != nil {
return xerr.NewErrMsgErr("创建文章内容失败", err)
}
}
// 生成 备份数据
backupData := newArticle.MakeBackup(articleAuthor, sectionList)
backupData.Action = "原始版本"
_, err = l.svcCtx.ArticleBackupRepository.Insert(l.ctx, conn, backupData)
if err != nil {
return xerr.NewErrMsgErr("创建文章失败", err)
}
return nil
}, true)
if err != nil {
... ...
... ... @@ -32,7 +32,7 @@ func NewSystemArticleRestoreLogic(ctx context.Context, svcCtx *svc.ServiceContex
func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticleRestoreRequest) (resp *types.SystemArticleRestoreResponse, err error) {
var conn = l.svcCtx.DefaultDBConn()
backup, err := l.svcCtx.ArticleBackupRepository.FindOne(l.ctx, conn, req.Id)
oldBackup, err := l.svcCtx.ArticleBackupRepository.FindOne(l.ctx, conn, req.Id)
if err != nil {
return nil, xerr.NewErrMsgErr("获取编辑历史记录失败", err)
}
... ... @@ -42,7 +42,7 @@ func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticl
if err != nil {
return nil, xerr.NewErrMsgErr("获取当前用户信息失败", err)
}
article, err := l.svcCtx.ArticleRepository.FindOne(l.ctx, conn, backup.ArticleId)
article, err := l.svcCtx.ArticleRepository.FindOne(l.ctx, conn, oldBackup.ArticleId)
if err != nil {
return nil, xerr.NewErrMsgErr("获取文章失败", err)
}
... ... @@ -50,6 +50,7 @@ func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticl
if err != nil {
return nil, xerr.NewErrMsgErr("获取文章段落内容失败", err)
}
// 备份数据
newBackUp := article.MakeBackup(domain.UserSimple{
Id: userToken.UserId,
... ... @@ -59,15 +60,16 @@ func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticl
Company: userMe.CurrentCompany.Name,
}, sectionList)
newBackUp.Action = "恢复"
_ = newBackUp.CheckChangeField(oldBackup)
article.Version = article.Version + 1
article.Images = backup.Images
article.Videos = backup.Videos
article.Title = backup.Title
article.MatchUrl = backup.MatchUrl
articleSections := make([]domain.ArticleSection, 0)
lo.ForEach(backup.Section, func(item domain.ArticleSection, index int) {
articleSections = append(articleSections, domain.ArticleSection{
article.Images = oldBackup.Images
article.Videos = oldBackup.Videos
article.Title = oldBackup.Title
article.MatchUrl = oldBackup.MatchUrl
articleSections := make([]*domain.ArticleSection, 0)
lo.ForEach(oldBackup.Section, func(item domain.ArticleSection, index int) {
articleSections = append(articleSections, &domain.ArticleSection{
Id: item.Id,
CompanyId: item.CompanyId,
Version: article.Version,
... ... @@ -98,7 +100,7 @@ func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticl
return xerr.NewErrMsgErr("恢复文章版本失败", err)
}
} else {
_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, &item)
_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, item)
if err != nil {
return xerr.NewErrMsgErr("恢复文章版本失败", err)
}
... ... @@ -106,7 +108,8 @@ func (l *SystemArticleRestoreLogic) SystemArticleRestore(req *types.SystemArticl
updateSectionIds = append(updateSectionIds, item.Id)
}
if len(updateSectionIds) > 0 {
err = l.svcCtx.ArticleSectionRepository.DeleteBy(ctx, c, domain.NewQueryOptions().WithKV("articleId", article.Id).WithKV("notIds", updateSectionIds))
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)
}
... ...
... ... @@ -57,25 +57,25 @@ func (l *SystemUpdateArticleLogic) SystemUpdateArticle(req *types.SystemArticleU
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)
}
_, sectionList, err := l.svcCtx.ArticleSectionRepository.Find(l.ctx, l.conn, map[string]interface{}{"articleId": article.Id})
//
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)
return nil, xerr.NewErrMsgErr("帖子不存在", err)
}
//备份数据
backup := article.MakeBackup(domain.UserSimple{
Id: userToken.UserId,
Name: userMe.User.NickName,
Avatar: userMe.User.Avatar,
CompanyId: userToken.CompanyId,
Company: userMe.CurrentCompany.Name,
}, sectionList)
backup.Action = "编辑"
oldBackup := article.MakeBackup(operator, sectionList)
// 获取图片的尺寸大小
images, err := l.getImages(req)
if err != nil {
... ... @@ -126,6 +126,7 @@ func (l *SystemUpdateArticleLogic) SystemUpdateArticle(req *types.SystemArticleU
}
//文章内容
updateSectionIds := []int64{}
updateSection := []*domain.ArticleSection{}
for _, item := range articleSections {
if item.Id > 0 {
section, err := l.svcCtx.ArticleSectionRepository.FindOne(ctx, c, item.Id)
... ... @@ -142,22 +143,29 @@ func (l *SystemUpdateArticleLogic) SystemUpdateArticle(req *types.SystemArticleU
return xerr.NewErrMsgErr("保存文章段落内容失败", err)
}
} else {
_, err = l.svcCtx.ArticleSectionRepository.Insert(ctx, c, &item)
_, 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 {
err = l.svcCtx.ArticleSectionRepository.DeleteBy(ctx, c, domain.NewQueryOptions().WithKV("articleId", article.Id).WithKV("notIds", updateSectionIds))
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)
}
}
_, err = l.svcCtx.ArticleBackupRepository.Insert(ctx, c, backup)
if err != nil {
return xerr.NewErrMsgErr("保存文章内容失败", err)
backup := article.MakeBackup(operator, updateSection)
backup.Action = "编辑"
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)
... ... @@ -307,8 +315,8 @@ func (l *SystemUpdateArticleLogic) getVideos(req *types.SystemArticleUpdateReque
return videos, nil
}
func (l *SystemUpdateArticleLogic) getSections(req *types.SystemArticleUpdateRequest, article *domain.Article) []domain.ArticleSection {
articleSections := make([]domain.ArticleSection, 0)
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")
... ... @@ -326,7 +334,7 @@ func (l *SystemUpdateArticleLogic) getSections(req *types.SystemArticleUpdateReq
if key == 0 {
section.Id = item.Id
}
articleSections = append(articleSections, section)
articleSections = append(articleSections, &section)
sortBy++
}
})
... ...
... ... @@ -1028,12 +1028,14 @@ type MiniArticleBackupSearchResponse struct {
}
type MiniArticleBackupItem struct {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Images []string `json:"images"`
CreatedAt int64 `json:"createdAt"`
Location Location `json:"location"`
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Images []string `json:"images"`
Videos []Video `json:"videos"`
ChangeField []string `json:"changeField"`
CreatedAt int64 `json:"createdAt"`
Location Location `json:"location"`
}
type MiniArticleMarkUserReadRequest struct {
... ...
... ... @@ -10,26 +10,27 @@ import (
)
type ArticleBackup struct {
Id int64 `gorm:"primaryKey"` // 唯一标识
CompanyId int64
CreatedAt int64
UpdatedAt int64
DeletedAt int64
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
Version int
Operator domain.UserSimple `gorm:"type:jsonb;serializer:json"` // 操作人
ArticleId int64
Title string // 标题
Section []domain.ArticleSection `gorm:"type:jsonb;serializer:json"` // 分段内容
Images []domain.Image `gorm:"type:jsonb;serializer:json"` // 图片
Videos []domain.Video `gorm:"type:jsonb;serializer:json"` // 视频
Action string // 操作
WhoRead []int64 `gorm:"type:jsonb;serializer:json"` // 谁可以看
WhoReview []int64 `gorm:"type:jsonb;serializer:json"` // 评论人
Tags []int64 `gorm:"type:jsonb;serializer:json"` // 标签
Location domain.Location `gorm:"type:jsonb;serializer:json"` // 坐标
TargetUser int // 分发方式 0 分发给所有人 1 分发给指定的人
MatchUrl map[string]string `gorm:"type:jsonb;serializer:json"` // 匹配文章内容中的url文本
Id int64 `gorm:"primaryKey"` // 唯一标识
CompanyId int64
CreatedAt int64
UpdatedAt int64
DeletedAt int64
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
Version int
Operator domain.UserSimple `gorm:"type:jsonb;serializer:json"` // 操作人
ArticleId int64
Title string // 标题
Section []domain.ArticleSection `gorm:"type:jsonb;serializer:json"` // 分段内容
Images []domain.Image `gorm:"type:jsonb;serializer:json"` // 图片
Videos []domain.Video `gorm:"type:jsonb;serializer:json"` // 视频
ChangeField []string `gorm:"type:jsonb;serializer:json"` //
Action string // 操作
WhoRead []int64 `gorm:"type:jsonb;serializer:json"` // 谁可以看
WhoReview []int64 `gorm:"type:jsonb;serializer:json"` // 评论人
Tags []int64 `gorm:"type:jsonb;serializer:json"` // 标签
Location domain.Location `gorm:"type:jsonb;serializer:json"` // 坐标
TargetUser int // 分发方式 0 分发给所有人 1 分发给指定的人
MatchUrl map[string]string `gorm:"type:jsonb;serializer:json"` // 匹配文章内容中的url文本
}
func (m *ArticleBackup) TableName() string {
... ...
... ... @@ -157,25 +157,26 @@ func (repository *ArticleBackupRepository) Find(ctx context.Context, conn transa
func (repository *ArticleBackupRepository) ModelToDomainModel(from *models.ArticleBackup) (*domain.ArticleBackup, error) {
to := &domain.ArticleBackup{
Id: from.Id,
CompanyId: from.CompanyId,
CreatedAt: from.CreatedAt,
UpdatedAt: from.CreatedAt,
DeletedAt: from.DeletedAt,
Version: from.Version,
Operator: from.Operator,
ArticleId: from.ArticleId,
Title: from.Title,
Section: from.Section,
Images: from.Images,
Action: from.Action,
TargetUser: domain.ArticleTarget(from.TargetUser),
Location: from.Location,
WhoRead: from.WhoRead,
WhoReview: from.WhoReview,
Tags: from.Tags,
MatchUrl: from.MatchUrl,
Videos: from.Videos,
Id: from.Id,
CompanyId: from.CompanyId,
CreatedAt: from.CreatedAt,
UpdatedAt: from.CreatedAt,
DeletedAt: from.DeletedAt,
Version: from.Version,
Operator: from.Operator,
ArticleId: from.ArticleId,
Title: from.Title,
Section: from.Section,
Images: from.Images,
Action: from.Action,
TargetUser: domain.ArticleTarget(from.TargetUser),
Location: from.Location,
WhoRead: from.WhoRead,
WhoReview: from.WhoReview,
Tags: from.Tags,
MatchUrl: from.MatchUrl,
ChangeField: from.ChangeField,
Videos: from.Videos,
}
// err := copier.Copy(to, from)
return to, nil
... ... @@ -183,26 +184,27 @@ func (repository *ArticleBackupRepository) ModelToDomainModel(from *models.Artic
func (repository *ArticleBackupRepository) DomainModelToModel(from *domain.ArticleBackup) (*models.ArticleBackup, error) {
to := &models.ArticleBackup{
Id: from.Id,
CompanyId: from.CompanyId,
CreatedAt: from.CreatedAt,
UpdatedAt: from.CreatedAt,
DeletedAt: from.DeletedAt,
IsDel: 0,
Version: from.Version,
Operator: from.Operator,
ArticleId: from.ArticleId,
Title: from.Title,
Section: from.Section,
Images: from.Images,
Action: from.Action,
WhoRead: from.WhoRead,
WhoReview: from.WhoReview,
Tags: from.Tags,
Location: from.Location,
TargetUser: int(from.TargetUser),
MatchUrl: from.MatchUrl,
Videos: from.Videos,
Id: from.Id,
CompanyId: from.CompanyId,
CreatedAt: from.CreatedAt,
UpdatedAt: from.CreatedAt,
DeletedAt: from.DeletedAt,
IsDel: 0,
Version: from.Version,
Operator: from.Operator,
ArticleId: from.ArticleId,
Title: from.Title,
Section: from.Section,
Images: from.Images,
Action: from.Action,
WhoRead: from.WhoRead,
WhoReview: from.WhoReview,
Tags: from.Tags,
Location: from.Location,
TargetUser: int(from.TargetUser),
MatchUrl: from.MatchUrl,
Videos: from.Videos,
ChangeField: from.ChangeField,
}
// err := copier.Copy(to, from)
return to, nil
... ...
... ... @@ -130,7 +130,7 @@ func (m *Article) MakeBackup(operator UserSimple, section []*ArticleSection) *Ar
return &b
}
func (m *Article) SetSummary(sectionList []ArticleSection) {
func (m *Article) SetSummary(sectionList []*ArticleSection) {
if len(sectionList) == 0 {
return
}
... ...
package domain
import (
"bytes"
"context"
"encoding/json"
"fmt"
"sort"
"gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
)
// 编辑文章后保存的历史记录
type ArticleBackup struct {
Id int64 `json:"id"`
CompanyId int64 `json:"companyId"`
CreatedAt int64 `json:"createdAt,omitempty"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
DeletedAt int64 `json:"deletedAt,omitempty"`
Version int `json:"version,omitempty"`
Operator UserSimple `json:"operator"` // 操作人
ArticleId int64 `json:"articleId"` //
Title string `json:"title"` // 标题
Section []ArticleSection `json:"section"` // 分段内容
Images []Image `json:"images"` // 图片
Videos []Video `json:"videos"` // 视频
Action string `json:"action"` // 操作
TargetUser ArticleTarget `json:"targetUser"` // 分发方式 0 分发给所有人 1 分发给指定的人
Location Location `json:"location"` // 定位坐标
WhoRead []int64 `json:"whoRead"` // 谁可以看
WhoReview []int64 `json:"whoReview"` // 评论人
Tags []int64 `json:"tags"` // 标签
MatchUrl map[string]string `json:"matchUrl"` // 匹配文章内容中的url文本
Id int64 `json:"id"`
CompanyId int64 `json:"companyId"`
CreatedAt int64 `json:"createdAt,omitempty"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
DeletedAt int64 `json:"deletedAt,omitempty"`
Version int `json:"version,omitempty"`
Operator UserSimple `json:"operator"` // 操作人
ArticleId int64 `json:"articleId"` //
Title string `json:"title"` // 标题
Section []ArticleSection `json:"section"` // 分段内容
Images []Image `json:"images"` // 图片
Videos []Video `json:"videos"` // 视频
Action string `json:"action"` // 操作
// 新的备份内容相对与旧的文章信息哪些内容发生了变更,可选值[WhoRead] 修改了分发对象, [WhoReview] 修改了评论范围,[Section] 修改了内容
ChangeField []string `json:"changeField"`
TargetUser ArticleTarget `json:"targetUser"` // 分发方式 0 分发给所有人 1 分发给指定的人
Location Location `json:"location"` // 定位坐标
WhoRead []int64 `json:"whoRead"` // 谁可以看
WhoReview []int64 `json:"whoReview"` // 评论人
Tags []int64 `json:"tags"` // 标签
MatchUrl map[string]string `json:"matchUrl"` // 匹配文章内容中的url文本
}
type ArticleBackupRepository interface {
... ... @@ -37,3 +43,98 @@ type ArticleBackupRepository interface {
FindOne(ctx context.Context, conn transaction.Conn, id int64) (*ArticleBackup, error)
Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*ArticleBackup, error)
}
// 检查新的备份内容相对与旧的文章信息哪些内容发生了变更
// 记录ArticleBackup中数据发生变更的的字段名
func (bk *ArticleBackup) CheckChangeField(oldBackup *ArticleBackup) bool {
bk.ChangeField = make([]string, 0)
//比较 WhoRead
{
whoReadChanged := false
sort.Slice(bk.WhoRead, func(i, j int) bool {
return bk.WhoRead[i] < bk.WhoRead[j]
})
sort.Slice(oldBackup.WhoRead, func(i, j int) bool {
return oldBackup.WhoRead[i] < oldBackup.WhoRead[j]
})
if len(bk.WhoRead) != len(oldBackup.WhoRead) {
whoReadChanged = true
} else {
for i := range bk.WhoRead {
if bk.WhoRead[i] != oldBackup.WhoRead[i] {
whoReadChanged = true
}
}
}
if whoReadChanged {
bk.ChangeField = append(bk.ChangeField, "WhoRead")
}
}
//比较 whoReview
{
whoReviewChanged := false
sort.Slice(bk.WhoReview, func(i, j int) bool {
return bk.WhoReview[i] < bk.WhoReview[j]
})
sort.Slice(oldBackup.WhoReview, func(i, j int) bool {
return oldBackup.WhoReview[i] < oldBackup.WhoReview[j]
})
if len(bk.WhoReview) != len(oldBackup.WhoReview) {
whoReviewChanged = true
} else {
for i := range bk.WhoReview {
if bk.WhoReview[i] != oldBackup.WhoReview[i] {
whoReviewChanged = true
}
}
}
if whoReviewChanged {
bk.ChangeField = append(bk.ChangeField, "WhoReview")
}
}
//比较段落内容+图片+视频 是否发生变更
{
sectionChanged := false
newSectionData := map[string]string{
"title": bk.Title,
}
oldSectionData := map[string]string{
"title": oldBackup.Title,
}
for _, val := range bk.Section {
mkey := fmt.Sprintf("section-%d", val.SortBy)
newSectionData[mkey] = val.Content
}
for _, val := range bk.Images {
newSectionData[val.Url] = ""
}
for _, val := range bk.Videos {
newSectionData[val.Url] = ""
}
for _, val := range oldBackup.Section {
mKey := fmt.Sprintf("section-%d", val.SortBy)
oldSectionData[mKey] = val.Content
}
for _, val := range oldBackup.Images {
oldSectionData[val.Url] = ""
}
for _, val := range oldBackup.Videos {
oldSectionData[val.Url] = ""
}
newSectionJson, _ := json.Marshal(newSectionData)
oldSectionJson, _ := json.Marshal(oldSectionData)
if ok := bytes.Equal(newSectionJson, oldSectionJson); !ok {
sectionChanged = true
}
if sectionChanged {
bk.ChangeField = append(bk.ChangeField, "Section")
}
}
return len(bk.ChangeField) > 0
}
... ...