Browse Source

合并冲突

kobe6258 1 day ago
parent
commit
4e49356e0b

+ 104 - 17
controllers/report_chapter.go

@@ -13,6 +13,7 @@ import (
 	"path"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -159,7 +160,6 @@ func (this *ReportController) AddChapter() {
 	//reportChapterInfo.CanvasColor = req.CanvasColor
 	//reportChapterInfo.HeadResourceId = req.HeadResourceId
 	//reportChapterInfo.EndResourceId = req.EndResourceId
-
 	err, errMsg := services.AddChapterBaseInfoAndPermission(reportInfo, reportChapterInfo, req.PermissionIdList, req.AdminIdList)
 	if err != nil {
 		br.Msg = "保存失败"
@@ -169,7 +169,9 @@ func (this *ReportController) AddChapter() {
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 {
+		br.Data = reportInfo.FreeLayoutConfig
+	}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -311,10 +313,6 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.Msg = "报告章节ID有误"
 		return
 	}
-	if req.Content == "" {
-		br.Msg = "请输入内容"
-		return
-	}
 
 	// 获取章节详情
 	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
@@ -331,7 +329,14 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.ErrMsg = "报告信息有误, Err: " + err.Error()
 		return
 	}
-
+	if req.Content == "" && reportInfo.ReportLayout != 3 {
+		br.Msg = "请输入内容"
+		return
+	}
+	if reportInfo.ReportLayout == 3 && req.FreeLayoutConfig == "" {
+		br.Msg = "请输入自由布局配置"
+		return
+	}
 	// 操作权限校验
 	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, reportChapterInfo, true, this.Lang)
 	if !hasAuth {
@@ -431,11 +436,57 @@ func (this *ReportController) EditDayWeekChapter() {
 			})
 		}
 	}
-	err = models.UpdateChapterAndTicker(reportInfo, reportChapterInfo, updateCols, tickerList)
-	if err != nil {
-		br.Msg = "保存失败"
-		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
-		return
+	if reportInfo.ReportLayout == 3 {
+		//对自由布局的数据做一个处理
+		//自由布局更新每页的数据
+		ormList := report.ToOrmViewList(req.FreeLayoutContentPages, true, reportInfo.Id, reportChapterId)
+		var wg sync.WaitGroup
+		wg.Add(len(ormList))
+		for _, v := range ormList {
+			go func(v *report.ReportFreeLayout) {
+				defer wg.Done()
+				content := v.Content
+				if content != "" {
+					// 处理关联excel的表格id
+					content = services.HandleReportContentTable(reportInfo.Id, content)
+					content = services.HandleReportContent(content, "del", nil)
+					e := utils.ContentXssCheck(content)
+					if e != nil {
+						br.Msg = "存在非法标签"
+						br.ErrMsg = "存在非法标签, Err: " + e.Error()
+						return
+					}
+					contentClean, e := services.FilterReportContentBr(content)
+					if e != nil {
+						br.Msg = "内容去除前后空格失败"
+						br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+						return
+					}
+					content = contentClean
+					if v.ContentStruct != `` {
+						v.ContentStruct = services.HandleReportContentStructTable(reportChapterInfo.ReportId, v.ContentStruct)
+						v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "del", nil)
+					}
+					v.Content = html.EscapeString(content)
+					v.ContentStruct = html.EscapeString(v.ContentStruct)
+				}
+			}(v)
+		}
+		wg.Wait()
+		reportInfo.FreeLayoutConfig = req.FreeLayoutConfig
+		err = models.UpdateChapterFreeLayoutContentPage(reportInfo, reportChapterInfo, updateCols, tickerList, ormList)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		err = models.UpdateChapterAndTicker(reportInfo, reportChapterInfo, updateCols, tickerList)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+			return
+		}
 	}
 
 	// 标记更新中
@@ -537,7 +588,9 @@ func (this *ReportController) DelChapter() {
 		br.ErrMsg = "删除失败,Err:" + err.Error()
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 {
+		go report.DeleteChapters(reportInfo.Id, reportChapterInfo.ReportChapterId)
+	}
 	// 备份关键数据
 	chapters := make([]*models.ReportChapter, 0)
 	chapters = append(chapters, reportChapterInfo)
@@ -778,13 +831,38 @@ func (this *ReportController) GetDayWeekChapter() {
 		br.ErrMsg = "无操作权限"
 		return
 	}
+	var pageNum int
+	var pages []*report.ContentPage
+	if reportInfo.ReportLayout == 3 {
+		pages, err = report.GetSingleFreeLayoutChapterPagesByReportId(reportInfo.Id, reportChapterId)
+		if err != nil {
+			br.Msg = "获取自由布局页面列表"
+			br.ErrMsg = "获取自由布局页面列表,Err:" + err.Error()
+			return
+		}
+		if len(pages) == 0 {
+			//获取当前章节前置章节总页数
+			pageNum, err = report.GetPrevFreeLayoutChaptersPagesByChapterId(reportInfo.Id, reportChapterId)
+			if err != nil {
+				br.Msg = "获取自由布局前置章节总页数"
+				br.ErrMsg = "获取自由布局前置章节总页数,Err:" + err.Error()
+				return
+			}
 
+		} else {
+			for _, page := range pages {
+				page.Content = html.UnescapeString(page.Content)
+				page.ContentStruct = html.UnescapeString(page.ContentStruct)
+				page.Content = services.HandleReportContentTable(page.ReportId, page.Content)
+				page.ContentStruct = services.HandleReportContentStructTable(page.ReportId, page.ContentStruct)
+			}
+		}
+	}
 	chapterItem.Content = html.UnescapeString(chapterItem.Content)
 	chapterItem.ContentSub = html.UnescapeString(chapterItem.ContentSub)
 	chapterItem.ContentStruct = html.UnescapeString(chapterItem.ContentStruct)
 	chapterItem.Content = services.HandleReportContentTable(chapterItem.ReportId, chapterItem.Content)
 	chapterItem.ContentStruct = services.HandleReportContentStructTable(chapterItem.ReportId, chapterItem.ContentStruct)
-
 	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
 	if err != nil {
 		br.Msg = "获取失败"
@@ -796,6 +874,12 @@ func (this *ReportController) GetDayWeekChapter() {
 		tokenMap := make(map[string]string)
 		chapterItem.Content = services.HandleReportContent(chapterItem.Content, "add", tokenMap)
 		chapterItem.ContentStruct = services.HandleReportContentStruct(chapterItem.ContentStruct, "add", tokenMap)
+		if reportInfo.ReportLayout == 3 {
+			for _, page := range pages {
+				page.Content = services.HandleReportContent(page.Content, "add", tokenMap)
+				page.ContentStruct = services.HandleReportContentStruct(page.ContentStruct, "add", tokenMap)
+			}
+		}
 	}
 
 	// 授权用户列表map
@@ -833,9 +917,12 @@ func (this *ReportController) GetDayWeekChapter() {
 	}
 
 	resp := models.ReportChapterItemResp{
-		ReportChapterItem: *chapterItem,
-		GrandAdminIdList:  chapterGrantIdList,
-		PermissionIdList:  chapterPermissionIdList,
+		FreeLayoutContentPages: pages,
+		FreeLayoutConfig:       reportInfo.FreeLayoutConfig,
+		PreviousPagesNum:       pageNum,
+		ReportChapterItem:      *chapterItem,
+		GrandAdminIdList:       chapterGrantIdList,
+		PermissionIdList:       chapterPermissionIdList,
 	}
 
 	// 获取当前编辑状态

+ 192 - 45
controllers/report_v2.go

@@ -18,6 +18,7 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -664,7 +665,6 @@ func (this *ReportController) Edit() {
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
-
 	reportCode := utils.MD5(strconv.Itoa(int(req.ReportId)))
 	resp := new(models.EditResp)
 	resp.ReportId = req.ReportId
@@ -719,6 +719,7 @@ func (this *ReportController) Detail() {
 		return
 	}
 	chapterList := make([]*models.ReportChapter, 0)
+	pageList := make([]*report.ContentPage, 0)
 	if item.HasChapter == 1 {
 		// 获取章节内容
 		tmpChapterList, err := models.GetPublishedChapterListByReportId(item.Id)
@@ -737,7 +738,34 @@ func (this *ReportController) Detail() {
 			}
 		}
 
+		if item.ReportLayout == 3 {
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(item.Id)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取自由布局内容页失败, Err: " + err.Error()
+				return
+			}
+			for _, page := range pages {
+				page.Content = html.UnescapeString(page.Content)
+				page.ContentStruct = html.UnescapeString(page.ContentStruct)
+				pageList = append(pageList, page)
+			}
+		}
 		//item.Abstract = item.Title
+	} else {
+		if item.ReportLayout == 3 {
+			pages, err := report.GetFreeLayoutPagesByReportId(item.Id)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取自由布局内容页失败, Err: " + err.Error()
+				return
+			}
+			for _, page := range pages {
+				page.Content = html.UnescapeString(page.Content)
+				page.ContentStruct = html.UnescapeString(page.ContentStruct)
+				pageList = append(pageList, page)
+			}
+		}
 	}
 	item.Content = html.UnescapeString(item.Content)
 	item.ContentSub = html.UnescapeString(item.ContentSub)
@@ -846,8 +874,9 @@ func (this *ReportController) Detail() {
 	}
 
 	resp := &models.ReportDetailView{
-		ReportDetail: item,
-		ChapterList:  chapterList,
+		ReportDetail:           item,
+		ChapterList:            chapterList,
+		FreeLayoutContentPages: pageList,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -904,7 +933,12 @@ func (this *ReportController) SaveReportContent() {
 		br.IsSendEmail = false
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 && req.FreeLayoutConfig == "" {
+		br.Msg = "自由布局配置为空"
+		br.ErrMsg = "自由布局配置为空"
+		br.IsSendEmail = false
+		return
+	}
 	// 标记更新中
 	{
 		markStatus, err := services.UpdateReportEditMark(req.ReportId, 0, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
@@ -933,11 +967,12 @@ func (this *ReportController) SaveReportContent() {
 
 		content = services.HandleReportContent(content, "del", nil)
 
+
 		fmt.Println("line 839")
 		fmt.Println(content)
 		fmt.Println("line 841")
 
-		if content != "" {
+		if content != "" || reportInfo.ReportLayout == 3 {
 			e := utils.ContentXssCheck(content)
 			if e != nil {
 				br.Msg = "存在非法标签"
@@ -959,7 +994,11 @@ func (this *ReportController) SaveReportContent() {
 				go alarm_msg.SendAlarmMsg("解析 ContentSub 失败,Err:"+err.Error(), 3)
 				//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
 			}
+
 			//reportInfo.Content = html.EscapeString(req.Content) //html.EscapeString(content)
+
+
+
 			reportInfo.Content = html.EscapeString(content)
 			reportInfo.ContentSub = html.EscapeString(contentSub)
 			reportInfo.ContentStruct = html.EscapeString(req.ContentStruct)
@@ -970,17 +1009,65 @@ func (this *ReportController) SaveReportContent() {
 			reportInfo.EndResourceId = req.EndResourceId
 			reportInfo.ModifyTime = time.Now()
 			reportInfo.ContentModifyTime = time.Now()
-			updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
+		//	updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
 
-			fmt.Println(reportInfo.ContentStruct)
-			err = reportInfo.UpdateReport(updateCols)
-			if err != nil {
-				br.Msg = "保存失败"
-				br.ErrMsg = "保存失败,Err:" + err.Error()
-				return
+			//fmt.Println(reportInfo.ContentStruct)
+			//err = reportInfo.UpdateReport(updateCols)
+			//if err != nil {
+			//	br.Msg = "保存失败"
+			//	br.ErrMsg = "保存失败,Err:" + err.Error()
+			//	return
+			if reportInfo.ReportLayout == 3 {
+				reportInfo.FreeLayoutConfig = req.FreeLayoutConfig
+				//自由布局更新每页的数据
+				ormList := report.ToOrmViewList(req.FreeLayoutContentPages, false, reportInfo.Id, 0)
+				var wg sync.WaitGroup
+				wg.Add(len(ormList))
+				for _, v := range ormList {
+					go func(v *report.ReportFreeLayout) {
+						defer wg.Done()
+						content = v.Content
+						content = services.HandleReportContent(content, "del", nil)
+						if content != "" {
+							e = utils.ContentXssCheck(content)
+							if e != nil {
+								br.Msg = "存在非法标签"
+								br.ErrMsg = "存在非法标签, Err: " + e.Error()
+								return
+							}
+							contentClean, e := services.FilterReportContentBr(content)
+							if e != nil {
+								br.Msg = "内容去除前后空格失败"
+								br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+								return
+							}
+							content = contentClean
+							v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "del", nil)
+							v.Content = html.EscapeString(content)
+							v.ContentStruct = html.EscapeString(v.ContentStruct)
+						}
+					}(v)
+				}
+				wg.Wait()
+				err = models.InsertOrUpdateReportFreeLayoutContentPage(reportInfo, ormList)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "保存失败,Err:" + err.Error()
+					return
+				}
+			} else {
+				updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
+				err = reportInfo.UpdateReport(updateCols)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "保存失败,Err:" + err.Error()
+					return
+				}
 			}
+
 			go models.AddReportSaveLog(reportId, this.SysUser.AdminId, reportInfo.Content, reportInfo.ContentSub, reportInfo.ContentStruct, reportInfo.CanvasColor, this.SysUser.AdminName, reportInfo.HeadResourceId, reportInfo.EndResourceId)
 		}
+
 	}
 
 	resp := new(models.SaveReportContentResp)
@@ -1002,7 +1089,7 @@ func (this *ReportController) SaveReportContent() {
 // @Param   ClassifyIdThird   query   int  true       "三级分类id"
 // @Param   State   query   string  true       "报告状态,多状态用英文,隔开"
 // @Success 200 {object} models.ReportListResp
-// @router /list/authorized [get]		
+// @router /list/authorized [get]
 func (this *ReportController) AuthorizedListReport() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
@@ -1172,17 +1259,21 @@ func (this *ReportController) BaseDetail() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-	/*var req models.ReportDetailReq
-	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
-	if err != nil {
-		br.Msg = "参数解析异常!"
-		br.ErrMsg = "参数解析失败,Err:" + err.Error()
-		return
-	}
-	if req.ReportId <= 0 {
-		br.Msg = "参数错误"
-		return
-	}*/
+	/*
+	   var req models.ReportDetailReq
+	   err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+
+	   	if err != nil {
+	   		br.Msg = "参数解析异常!"
+	   		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+	   		return
+	   	}
+
+	   	if req.ReportId <= 0 {
+	   		br.Msg = "参数错误"
+	   		return
+	   	}
+	*/
 	reportId, err := this.GetInt("ReportId")
 	if err != nil {
 		br.Msg = "获取参数失败!"
@@ -1209,7 +1300,28 @@ func (this *ReportController) BaseDetail() {
 
 	reportInfo.Content = html.UnescapeString(reportInfo.Content)
 	reportInfo.ContentSub = html.UnescapeString(reportInfo.ContentSub)
-
+	if reportInfo.ReportLayout == 3 {
+		if reportInfo.HeadResourceId > 0 {
+			headResource, err := smart_report.GetResourceItemById(reportInfo.HeadResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.HeadImg = headResource.ImgUrl
+			reportInfo.HeadStyle = headResource.Style
+		}
+		if reportInfo.EndResourceId > 0 {
+			headResource, err := smart_report.GetResourceItemById(reportInfo.EndResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版尾失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.EndImg = headResource.ImgUrl
+			reportInfo.EndStyle = headResource.Style
+		}
+	}
 	grandAdminList := make([]models.ReportDetailViewAdmin, 0)
 	permissionList := make([]models.ReportDetailViewPermission, 0)
 
@@ -1327,11 +1439,12 @@ func (this *ReportController) EditLayoutImg() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-	//if req.Content == "" {
-	//	br.Msg = "报告内容不能为空"
-	//	return
-	//}
-	//更新标记key
+	//	if req.Content == "" {
+	//		br.Msg = "报告内容不能为空"
+	//		return
+	//	}
+	//
+	// 更新标记key
 	markStatus, err := services.UpdateReportEditMark(int(req.ReportId), 0, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
 	if err != nil {
 		br.Msg = err.Error()
@@ -1339,7 +1452,7 @@ func (this *ReportController) EditLayoutImg() {
 	}
 	if markStatus.Status == 1 {
 		br.Msg = markStatus.Msg
-		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		// br.Ret = 202 //202 服务器已接受请求,但尚未处理。
 		return
 	}
 
@@ -1611,10 +1724,28 @@ func (this *ReportController) PrePublishReport() {
 			}
 		}
 	} else {
-		if reportDetail.Content == "" {
-			br.Msg = "报告内容为空,不可设置定时发布"
-			br.ErrMsg = "报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
-			return
+		if reportDetail.ReportLayout != 3 {
+			if reportDetail.Content == "" {
+				br.Msg = "报告内容为空,不可设置定时发布"
+				br.ErrMsg = "报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
+				return
+			}
+		} else {
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(reportDetail.Id)
+			if err != nil {
+				br.Msg = "获取自由布局报告失败,不可设置定时发布"
+				br.ErrMsg = "获取自由布局报告失败,不可设置定时发布,Err:" + err.Error()
+				return
+			}
+			var content string
+			for _, page := range pages {
+				content += page.Content
+			}
+			if content == "" {
+				br.Msg = "自由布局报告内容为空,不可设置定时发布"
+				br.ErrMsg = "自由布局报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
+				return
+			}
 		}
 	}
 
@@ -1713,10 +1844,26 @@ func (this *ReportController) SubmitApprove() {
 			}
 		}
 	} else {
-		if reportItem.Content == "" {
+		if reportItem.Content == "" && reportItem.ReportLayout != 3 {
 			br.Msg = "报告内容为空,不可提交"
 			br.ErrMsg = "报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
 			return
+		} else {
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(reportItem.Id)
+			if err != nil {
+				br.Msg = "获取自由布局报告失败,不可提交"
+				br.ErrMsg = "获取自由布局报告失败,不可提交,Err:" + err.Error()
+				return
+			}
+			var content string
+			for _, page := range pages {
+				content += page.Content
+			}
+			if content == "" {
+				br.Msg = "自由布局报告内容为空,不可提交"
+				br.ErrMsg = "自由布局报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
+				return
+			}
 		}
 	}
 
@@ -1972,10 +2119,10 @@ func (this *ReportCommonController) ShareTransform() {
 // @author: Roc
 // @datetime 2024-06-21 09:19:05
 func init() {
-	//fixApproveRecord()
-	//fixChapterPermission()
-	//fixReportEs()
-	//fixSmartReport()
+	// fixApproveRecord()
+	// fixChapterPermission()
+	// fixReportEs()
+	// fixSmartReport()
 }
 
 // 修复研报审批数据
@@ -1991,7 +2138,7 @@ func fixApproveRecord() {
 		return
 	}
 	for _, recordItem := range list {
-		//fmt.Println(recordItem)
+		// fmt.Println(recordItem)
 		recordItem.NodeState = recordItem.State
 		recordItem.NodeApproveUserId = recordItem.ApproveUserId
 		recordItem.NodeApproveUserName = recordItem.ApproveUserName
@@ -2041,7 +2188,7 @@ func fixChapterPermission() {
 		}
 	}
 
-	//notIdList := []int{9675, 9675, 9740, 9749, 9768, 9773, 9791, 9792, 9793, 9850, 9851, 9852, 9852, 9852, 9853, 9854, 9856, 9857, 9857, 9858, 9859, 9860, 9861, 9862, 9862, 9863, 9866}
+	// notIdList := []int{9675, 9675, 9740, 9749, 9768, 9773, 9791, 9792, 9793, 9850, 9851, 9852, 9852, 9852, 9853, 9854, 9856, 9857, 9857, 9858, 9859, 9860, 9861, 9862, 9862, 9863, 9866}
 	notIdList := []int{}
 	allReportChapterList, err := models.GetAllReportChapter()
 	if err != nil {
@@ -2164,7 +2311,7 @@ func fixSmartReport() {
 	for _, v := range list {
 		fmt.Println(v)
 		addList = append(addList, &models.Report{
-			//Id:                  0,
+			// Id:                  0,
 			AddType:            1,
 			ClassifyIdFirst:    v.ClassifyIdFirst,
 			ClassifyNameFirst:  v.ClassifyNameFirst,
@@ -2180,7 +2327,7 @@ func fixSmartReport() {
 			PublishTime:        v.PublishTime,
 			Stage:              v.Stage,
 			MsgIsSend:          v.MsgIsSend,
-			//ThsMsgIsSend:        v.Tha,
+			// ThsMsgIsSend:        v.Tha,
 			Content:             v.Content,
 			VideoUrl:            v.VideoUrl,
 			VideoName:           v.VideoName,
@@ -2276,7 +2423,7 @@ func fixSmartReport() {
 func initPdf() {
 	inFile := "anNNgk3Bbi4LRULwcJgNOPrREYh5.pdf"
 	f2, err := services.GeneralWaterMarkPdf(inFile, "颜鹏 - 18170239278")
-	//f2, err := services.GeneralWaterMarkPdf(inFile, "上周美国馏分油库存累库95万桶,馏分油表需环比下降(-25.6万桶/日)。本期馏分油产量继续抬升,在供增需减的环比变动下库存持续累库。馏分油供应的增加我们认为可能和进口的油种有关,今年以来美国进口的中重质原油占比不断走高,尤其是5")
+	// f2, err := services.GeneralWaterMarkPdf(inFile, "上周美国馏分油库存累库95万桶,馏分油表需环比下降(-25.6万桶/日)。本期馏分油产量继续抬升,在供增需减的环比变动下库存持续累库。馏分油供应的增加我们认为可能和进口的油种有关,今年以来美国进口的中重质原油占比不断走高,尤其是5")
 	if err != nil {
 		fmt.Println("生成失败,ERR:", err)
 		return

+ 59 - 6
models/report.go

@@ -4,6 +4,7 @@ import (
 	sql2 "database/sql"
 	"errors"
 	"eta/eta_api/global"
+	"eta/eta_api/models/report"
 	"eta/eta_api/utils"
 	"fmt"
 	"strings"
@@ -92,6 +93,7 @@ type Report struct {
 	InheritReportId     int       `description:"待继承的报告ID"`
 	VoiceGenerateType   int       `description:"音频生成方式,0:系统生成,1:人工上传"`
 	RaiReportId         int       `description:"RAI报告ID"`
+	FreeLayoutConfig    string    `description:"'自由布局配置"`
 }
 
 func (m *Report) AfterFind(db *gorm.DB) (err error) {
@@ -450,6 +452,7 @@ type ReportDetail struct {
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
 	RaiReportId         int       `description:"RAI报告ID"`
 	ClassifyEnabled     bool      `description:"分类是否禁用"`
+	FreeLayoutConfig    string    `description:"'自由布局配置"`
 }
 
 func (m *ReportDetail) AfterFind(db *gorm.DB) (err error) {
@@ -618,7 +621,7 @@ type AddReq struct {
 	HeadResourceId     int    `description:"版头资源ID"`
 	EndResourceId      int    `description:"版尾资源ID"`
 	CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
-	ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局,3:自由布局。默认:1"`
 	IsPublicPublish    int8   `description:"是否公开发布,1:是,2:否"`
 	InheritReportId    int    `description:"待继承的报告ID"`
 	GrantAdminIdList   []int  `description:"授权用户id列表"`
@@ -669,7 +672,7 @@ type EditReq struct {
 	HeadResourceId     int    `description:"版头资源ID"`
 	EndResourceId      int    `description:"版尾资源ID"`
 	//CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
-	//ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	//ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局,3:自由布局。默认:1"`
 	IsPublicPublish  int8  `description:"是否公开发布,1:是,2:否"`
 	GrantAdminIdList []int `description:"授权用户id列表"`
 }
@@ -818,7 +821,6 @@ type SaveReportContent struct {
 	Content  string `description:"内容"`
 	ReportId int    `description:"报告id"`
 	NoChange int    `description:"内容是否未改变:1:内容未改变"`
-
 	// 以下是智能研报相关
 	ContentStruct  string `description:"内容组件"`
 	HeadImg        string `description:"报告头图地址"`
@@ -827,6 +829,9 @@ type SaveReportContent struct {
 	NeedSplice     int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+	//自由布局相关
+	FreeLayoutContentPages []report.ContentPage `description:"自由布局页面数据"`
+	FreeLayoutConfig       string               `description:"自由布局配置"`
 }
 
 //func EditReportContent(reportId int, content, contentSub string) (err error) {
@@ -956,9 +961,10 @@ func (reportInfo *Report) UpdateReport(cols []string) (err error) {
 // @Description: 晨周报详情
 type ReportDetailView struct {
 	*ReportDetail
-	ChapterList    []*ReportChapter
-	GrandAdminList []ReportDetailViewAdmin
-	PermissionList []ReportDetailViewPermission
+	ChapterList            []*ReportChapter
+	GrandAdminList         []ReportDetailViewAdmin
+	PermissionList         []ReportDetailViewPermission
+	FreeLayoutContentPages []*report.ContentPage
 }
 
 // ReportDetailViewAdmin
@@ -1698,9 +1704,56 @@ type ReportShartUrlResp struct {
 	UrlToken string `description:"分享链接token"`
 }
 
+
 func GetAllPublishReportId() (items []int, err error) {
 	o := global.DbMap[utils.DbNameReport]
 	sql := `SELECT  a.id FROM report as a WHERE 1=1 AND state in (2,6) `
 	err = o.Raw(sql).Find(&items).Error
 	return
 }
+func InsertOrUpdateReportFreeLayoutContentPage(reportInfo *Report, ormList []*report.ReportFreeLayout) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+	reportUpdateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime","FreeLayoutConfig"}
+	err = tx.Model(&reportInfo).Select(reportUpdateCols).Updates(reportInfo).Error
+	return report.BatchInsertOrUpdatePages(tx, ormList, false, reportInfo.Id, 0)
+}
+func UpdateChapterFreeLayoutContentPage(reportInfo *Report, chapterInfo *ReportChapter, updateCols []string, tickerList []*ReportChapterTicker, ormList []*report.ReportFreeLayout) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	if err = tx.Model(&reportInfo).Select([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime","FreeLayoutConfig"}).Updates(reportInfo).Error; err != nil {
+		return
+	}
+	// 更新章节
+	if err = tx.Model(&chapterInfo).Select(updateCols).Updates(chapterInfo).Error; err != nil {
+		return
+	}
+	sql := ` DELETE FROM report_chapter_ticker WHERE report_chapter_id = ? `
+	// 清空并新增章节ticker
+	if err = tx.Exec(sql, chapterInfo.ReportChapterId).Error; err != nil {
+		return
+	}
+	tickerLen := len(tickerList)
+	if tickerLen > 0 {
+		err = tx.CreateInBatches(tickerList, len(tickerList)).Error
+		if err != nil {
+			return
+		}
+	}
+	return report.BatchInsertOrUpdatePages(tx, ormList, true, reportInfo.Id, chapterInfo.ReportChapterId)
+
+}

+ 234 - 0
models/report/report_free_layout.go

@@ -0,0 +1,234 @@
+package report
+
+import (
+	sql2 "database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"time"
+)
+
+type ReportFreeLayout struct {
+	Id              int       `gorm:"primaryKey;autoIncrement;column:id"` // 主键
+	ReportId        int       `gorm:"column:report_id"`                   // 研报Id
+	ReportChapterId int       `gorm:"column:report_chapter_id"`           // 章节Id
+	Page            int       `gorm:"column:page"`                        // 页码
+	IsChapter       int       `gorm:"column:is_chapter"`                  // 是否多章节
+	Content         string    `gorm:"column:content;size:255"`            // 内容
+	ContentStruct   string    `gorm:"column:content_struct;size:255"`     // 内容
+	CreateTime      time.Time `gorm:"column:create_time"`                 // 创建时间
+	ModifyTime      time.Time `gorm:"column:modify_time"`                 // 修改时间
+}
+type PagePositionEnum string
+
+const (
+	Left   PagePositionEnum = "left"
+	Right  PagePositionEnum = "right"
+	Center PagePositionEnum = "center"
+)
+
+type ContentPage struct {
+	Id              int    `json:"Id"`
+	Page            int    `json:"Page"`
+	Content         string `json:"Content"`
+	ContentStruct   string `json:"ContentStruct"`
+	ReportId        int    `json:"ChapterId"`
+	ReportChapterId int    `json:"ReportChapterId"`
+}
+
+func (cp *ContentPage) ToView(isChapter bool, ReportId int, ReportChapterId int) *ReportFreeLayout {
+	if isChapter {
+		return &ReportFreeLayout{
+			ReportId:        ReportId,
+			ReportChapterId: ReportChapterId,
+			Page:            cp.Page,
+			IsChapter:       1,
+			Content:         cp.Content,
+			ContentStruct:   cp.ContentStruct,
+			CreateTime:      time.Now(),
+		}
+	} else {
+		return &ReportFreeLayout{
+			ReportId:        ReportId,
+			ReportChapterId: ReportChapterId,
+			Page:            cp.Page,
+			IsChapter:       0,
+			Content:         cp.Content,
+			ContentStruct:   cp.ContentStruct,
+			CreateTime:      time.Now(),
+		}
+	}
+}
+func (cp *ReportFreeLayout) ToPageView() *ContentPage {
+	return &ContentPage{
+		Page:            cp.Page,
+		Content:         cp.Content,
+		ContentStruct:   cp.ContentStruct,
+		ReportId:        cp.ReportId,
+		ReportChapterId: cp.ReportChapterId,
+	}
+}
+func ToOrmViewList(srcList []ContentPage, isChapter bool, ReportId int, ReportChapterId int) (list []*ReportFreeLayout) {
+	for _, v := range srcList {
+		list = append(list, v.ToView(isChapter, ReportId, ReportChapterId))
+	}
+	return
+}
+
+func ToPageViewList(srcList []*ReportFreeLayout) (list []*ContentPage) {
+	for _, v := range srcList {
+		list = append(list, v.ToPageView())
+	}
+	return
+}
+
+// TableName 设置表名
+func (*ReportFreeLayout) TableName() string {
+	return "report_free_layout"
+}
+
+func SortPage(reportId int, tx *gorm.DB) (err error) {
+	if tx == nil {
+		tx = global.DbMap[utils.DbNameReport].Begin()
+		defer func() {
+			if err != nil {
+				_ = tx.Rollback()
+				return
+			}
+			_ = tx.Commit()
+		}()
+	}
+	sql := `select * from report_free_layout where report_id = ?  and is_chapter=1   order by page asc`
+	var ormList []*ReportFreeLayout
+	err = tx.Raw(sql, reportId).Find(&ormList).Error
+	if err != nil {
+		return
+	}
+	if len(ormList) == 0 {
+		return
+	}
+	chapterPages := make(map[int][]*ReportFreeLayout)
+	for _, v := range ormList {
+		chapterPages[v.ReportChapterId] = append(chapterPages[v.ReportChapterId], v)
+	}
+
+	chapterSql := `select report_chapter_id from report_chapter where report_id =? order by sort asc`
+	var chapterIds []int
+	err = tx.Raw(chapterSql, reportId).Scan(&chapterIds).Error
+	if err != nil {
+		return
+	}
+	initPage := 1
+	for _, chapter := range chapterIds {
+		chapterList := chapterPages[chapter]
+		for _, v := range chapterList {
+			v.Page = initPage
+			initPage++
+		}
+
+	}
+	var updateList []*ReportFreeLayout
+	for _, chapterList := range chapterPages {
+		updateList = append(updateList, chapterList...)
+	}
+	err = tx.Model(&ReportFreeLayout{}).Clauses(clause.OnConflict{
+		Columns:   []clause.Column{{Name: "Id"}},
+		DoUpdates: clause.AssignmentColumns([]string{"Page"}),
+	}).CreateInBatches(updateList, len(updateList)).Error
+	return
+}
+
+func DeleteChapters(reportId int, chapterId int) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+	err = tx.Exec("delete from report_free_layout where   report_id = ?  and report_chapter_id=? and is_chapter=1", reportId, chapterId).Error
+	if err != nil {
+		return
+	}
+	err = SortPage(reportId, tx)
+	return
+}
+func BatchInsertOrUpdatePages(tx *gorm.DB, list []*ReportFreeLayout, isChapter bool, reportId, chapterId int) (err error) {
+	if isChapter {
+		err = tx.Exec("delete from report_free_layout where   report_id = ?  and report_chapter_id=? and is_chapter=1", reportId, chapterId).Error
+		if err != nil {
+			return
+		}
+		err = tx.Model(&ReportFreeLayout{}).Clauses(clause.OnConflict{
+			Columns:   []clause.Column{{Name: "id"}},
+			DoUpdates: clause.AssignmentColumns([]string{"content", "content_struct", "modify_time"}),
+		}).CreateInBatches(list, len(list)).Error
+		if err != nil {
+			return
+		}
+		err = SortPage(reportId, tx)
+		return
+	} else {
+		err = tx.Exec("delete from  report_free_layout where report_id = ? and  is_chapter=0", reportId).Error
+		if err != nil {
+			return
+		}
+		err = tx.Model(&ReportFreeLayout{}).Clauses(clause.OnConflict{
+			Columns:   []clause.Column{{Name: "id"}},
+			DoUpdates: clause.AssignmentColumns([]string{"content", "content_struct", "page", "modify_time"}),
+		}).CreateInBatches(list, len(list)).Error
+	}
+	return
+}
+func GetPrevFreeLayoutChaptersPagesByChapterId(reportId int, chapterId int) (pageNum int, err error) {
+	var pageNumNullable sql2.NullInt64
+	sql := `SELECT count(*) 
+FROM report_free_layout rfl
+JOIN report_chapter rc ON rc.report_id = rfl.report_id and rc.report_chapter_id=rfl.report_chapter_id
+WHERE rfl.report_id = ?
+  AND rc.sort < (
+    SELECT sort
+    FROM report_chapter
+    WHERE report_id = ? and report_chapter_id=?
+  )`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId, reportId, chapterId).Scan(&pageNumNullable).Error
+	if err != nil {
+		return
+	}
+	if pageNumNullable.Valid {
+		pageNum = int(pageNumNullable.Int64)
+	}
+	return
+}
+func GetFreeLayoutChapterPagesByReportId(reportId int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	sql := `select rfl.*,rc.sort from report_free_layout rfl LEFT JOIN report_chapter rc on rc.report_id=rfl.report_id and rc.report_chapter_id=rfl.report_chapter_id where rfl.report_id =? order by rc.sort,rfl.page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId).Find(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	list = ToPageViewList(ormList)
+	return
+}
+func GetSingleFreeLayoutChapterPagesByReportId(reportId, chapterId int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	sql := `select * from report_free_layout where report_id =? and report_chapter_id=? order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId, chapterId).Find(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	list = ToPageViewList(ormList)
+	return
+}
+func GetFreeLayoutPagesByReportId(id int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	sql := `select * from report_free_layout  where report_id =? and is_chapter=0 order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, id).Find(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	list = ToPageViewList(ormList)
+	return
+}

+ 14 - 8
models/report_chapter.go

@@ -134,14 +134,17 @@ func (m *ReportChapterItem) ConvDateTimeStr() {
 // @Description: 章节详情(带有一些额外的数据)
 type ReportChapterItemResp struct {
 	ReportChapterItem
-	GrandAdminIdList []int  `description:"授权的用户id列表"`
-	PermissionIdList []int  `description:"关联的品种id列表"`
-	CanEdit          bool   `description:"是否可编辑"`
-	Editor           string `description:"编辑人"`
-	HeadImg          string `description:"报告头图地址"`
-	EndImg           string `description:"报告尾图地址"`
-	HeadStyle        string `description:"版头样式"`
-	EndStyle         string `description:"版尾样式"`
+	FreeLayoutContentPages []*report.ContentPage
+	FreeLayoutConfig       string
+	PreviousPagesNum       int
+	GrandAdminIdList       []int  `description:"授权的用户id列表"`
+	PermissionIdList       []int  `description:"关联的品种id列表"`
+	CanEdit                bool   `description:"是否可编辑"`
+	Editor                 string `description:"编辑人"`
+	HeadImg                string `description:"报告头图地址"`
+	EndImg                 string `description:"报告尾图地址"`
+	HeadStyle              string `description:"版头样式"`
+	EndStyle               string `description:"版尾样式"`
 }
 
 type ReportChapterResp struct {
@@ -222,6 +225,9 @@ type EditReportChapterReq struct {
 	CanvasColor    string `description:"画布颜色"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+	//自由布局研报相关
+	FreeLayoutContentPages []report.ContentPage `description:"自由布局内容"`
+	FreeLayoutConfig       string               `description:"'自由布局配置"`
 }
 
 type EditTickList struct {

+ 3 - 0
services/report_chapter.go

@@ -166,6 +166,9 @@ func moveReportChapter(reportChapter, prevReportChapter, nextReportChapter *mode
 			err = fmt.Errorf("修改失败,Err:" + err.Error())
 			return
 		}
+		go func() {
+			_ = report.SortPage(reportChapter.ReportId, nil)
+		}()
 	}
 	return
 }

+ 34 - 5
services/report_v2.go

@@ -766,7 +766,15 @@ func AddChapterBaseInfoAndPermission(reportInfo *models.Report, reportChapterInf
 			CreateTime:        time.Now(),
 		})
 	}
-
+	if reportInfo.ReportLayout == 3 {
+		//增加默认排序
+		maxSort, sortErr := reportChapterInfo.GetMaxSortByReportId(reportInfo.Id)
+		if sortErr != nil {
+			err = fmt.Errorf("获取报告章节最大排序失败, ReportId: %d, Err: %v", reportInfo.Id, sortErr)
+			return
+		}
+		reportChapterInfo.Sort = maxSort + 1
+	}
 	err = models.AddChapterBaseInfoAndPermission(reportChapterInfo, addChapterAdminList, addChapterPermissionList)
 
 	return
@@ -1197,10 +1205,28 @@ func PublishReport(reportId int, reportUrl string, sysUser *system.Admin) (tips
 	}
 
 	// 普通报告
-	if reportInfo.Content == "" {
-		errMsg = `报告内容为空,不可发布`
-		err = errors.New("报告内容为空,不需要生成,report_id:" + strconv.Itoa(reportId))
-		return
+	if reportInfo.ReportLayout != 3 {
+		if reportInfo.Content == "" {
+			errMsg = `报告内容为空,不可发布`
+			err = errors.New("报告内容为空,不需要生成,report_id:" + strconv.Itoa(reportId))
+			return
+		}
+	} else {
+		pages, pageErr := report.GetFreeLayoutChapterPagesByReportId(reportInfo.Id)
+		if pageErr != nil {
+			errMsg = "获取自由布局报告失败,不可发布"
+			err = errors.New("获取自由布局报告失败,不可发布,Err:" + pageErr.Error())
+			return
+		}
+		var content string
+		for _, page := range pages {
+			content += page.Content
+		}
+		if content == "" {
+			errMsg = "自由布局报告内容为空,不可设置定时发布"
+			err = errors.New("自由布局报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportInfo.Id))
+			return
+		}
 	}
 
 	// 根据审批开关及审批流判断当前报告状态
@@ -1646,6 +1672,9 @@ func GetGeneralPdfUrl(reportId int, reportCode, classifyFirstName string, report
 	case 2:
 		// 智能布局
 		pdfUrl = fmt.Sprintf("%s/reportshare_smart_pdf?code=%s", reportUrl, reportCode)
+	case 3:
+		// 智能布局
+		pdfUrl = fmt.Sprintf("%s/reportshare_free_pdf?code=%s", reportUrl, reportCode)
 	}
 
 	if pdfUrl != "" {