Эх сурвалжийг харах

Merge branch 'feature/eta_2.5.4_report_free_layout' of eta_server/eta_api into master

chenhan 3 өдөр өмнө
parent
commit
f4bb7791ef

+ 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,
 	}
 
 	// 获取当前编辑状态

+ 198 - 48
controllers/report_v2.go

@@ -18,6 +18,7 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -476,7 +477,6 @@ func (this *ReportController) Add() {
 	item.ReportVersion = req.ReportVersion
 	item.AdminId = sysUser.AdminId
 	item.AdminRealName = sysUser.RealName
-
 	item.ClassifyIdThird = req.ClassifyIdThird
 	item.ClassifyNameThird = classifyMap[req.ClassifyIdThird]
 
@@ -612,7 +612,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
@@ -667,6 +666,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)
@@ -685,7 +685,40 @@ func (this *ReportController) Detail() {
 			}
 		}
 
+		if item.ReportLayout == 3 {
+			var chapterMap = make(map[int]bool)
+			for _, chapter := range tmpChapterList {
+				chapterMap[chapter.ReportChapterId] = true
+			}
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(item.Id)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取自由布局内容页失败, Err: " + err.Error()
+				return
+			}
+			for _, page := range pages {
+				if chapterMap[page.ReportChapterId] {
+					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)
@@ -739,12 +772,18 @@ func (this *ReportController) Detail() {
 			v.Content = services.HandleReportContent(v.Content, "add", tokenMap)
 			v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "add", tokenMap)
 		}
-
+		if item.ReportLayout == 3 {
+			for _, page := range pageList {
+				page.Content = services.HandleReportContent(page.Content, "add", tokenMap)
+				page.ContentStruct = services.HandleReportContentStruct(page.ContentStruct, "add", tokenMap)
+			}
+		}
 	}
 
 	resp := &models.ReportDetailView{
-		ReportDetail: item,
-		ChapterList:  chapterList,
+		ReportDetail:           item,
+		ChapterList:            chapterList,
+		FreeLayoutContentPages: pageList,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -800,7 +839,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)
@@ -821,7 +865,7 @@ func (this *ReportController) SaveReportContent() {
 			content = this.GetString("Content")
 		}
 		content = services.HandleReportContent(content, "del", nil)
-		if content != "" {
+		if content != "" || reportInfo.ReportLayout == 3 {
 			e := utils.ContentXssCheck(content)
 			if e != nil {
 				br.Msg = "存在非法标签"
@@ -843,6 +887,7 @@ 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(content)
 			reportInfo.ContentSub = html.EscapeString(contentSub)
 			reportInfo.ContentStruct = html.EscapeString(req.ContentStruct)
@@ -853,15 +898,58 @@ 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"}
-			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()
+						pageContent := v.Content
+						pageContent = services.HandleReportContent(pageContent, "del", nil)
+						if pageContent != "" {
+							pageErr := utils.ContentXssCheck(pageContent)
+							if pageErr != nil {
+								br.Msg = "存在非法标签"
+								br.ErrMsg = "存在非法标签, Err: " + pageErr.Error()
+								return
+							}
+							var pageContentClean string
+							pageContentClean, pageErr = services.FilterReportContentBr(pageContent)
+							if pageErr != nil {
+								br.Msg = "内容去除前后空格失败"
+								br.ErrMsg = "内容去除前后空格失败, Err: " + pageErr.Error()
+								return
+							}
+							pageContent = pageContentClean
+							v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "del", nil)
+							v.Content = html.EscapeString(pageContent)
+							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)
@@ -1027,17 +1115,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 = "获取参数失败!"
@@ -1064,7 +1156,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)
 
@@ -1182,11 +1295,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()
@@ -1194,7 +1308,7 @@ func (this *ReportController) EditLayoutImg() {
 	}
 	if markStatus.Status == 1 {
 		br.Msg = markStatus.Msg
-		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		// br.Ret = 202 //202 服务器已接受请求,但尚未处理。
 		return
 	}
 
@@ -1466,10 +1580,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
+			}
 		}
 	}
 
@@ -1568,10 +1700,28 @@ func (this *ReportController) SubmitApprove() {
 			}
 		}
 	} else {
-		if reportItem.Content == "" {
-			br.Msg = "报告内容为空,不可提交"
-			br.ErrMsg = "报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
-			return
+		if reportItem.ReportLayout != 3 {
+			if reportItem.Content == "" {
+				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
+			}
 		}
 	}
 
@@ -1827,10 +1977,10 @@ func (this *ReportCommonController) ShareTransform() {
 // @author: Roc
 // @datetime 2024-06-21 09:19:05
 func init() {
-	//fixApproveRecord()
-	//fixChapterPermission()
-	//fixReportEs()
-	//fixSmartReport()
+	// fixApproveRecord()
+	// fixChapterPermission()
+	// fixReportEs()
+	// fixSmartReport()
 }
 
 // 修复研报审批数据
@@ -1846,7 +1996,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
@@ -1896,7 +2046,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 {
@@ -2019,7 +2169,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,
@@ -2035,7 +2185,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,
@@ -2131,7 +2281,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

+ 58 - 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"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -91,6 +92,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) {
@@ -447,6 +449,7 @@ type ReportDetail struct {
 	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
 	RaiReportId         int       `description:"RAI报告ID"`
+	FreeLayoutConfig    string    `description:"'自由布局配置"`
 }
 
 func (m *ReportDetail) AfterFind(db *gorm.DB) (err error) {
@@ -615,7 +618,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列表"`
@@ -666,7 +669,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列表"`
 }
@@ -815,7 +818,6 @@ type SaveReportContent struct {
 	Content  string `description:"内容"`
 	ReportId int    `description:"报告id"`
 	NoChange int    `description:"内容是否未改变:1:内容未改变"`
-
 	// 以下是智能研报相关
 	ContentStruct  string `description:"内容组件"`
 	HeadImg        string `description:"报告头图地址"`
@@ -824,6 +826,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) {
@@ -953,9 +958,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
@@ -1700,3 +1706,49 @@ func GetAllPublishReportId() (items []int, err error) {
 	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)
+}

+ 266 - 0
models/report/report_free_layout.go

@@ -0,0 +1,266 @@
+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
+		err = tx.Model(&ReportFreeLayout{}).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{}).CreateInBatches(list, len(list)).Error
+		//.Clauses(clause.OnConflict{
+		//	Columns:   []clause.Column{{Name: "id"}},
+		//	DoUpdates: clause.AssignmentColumns([]string{"content", "content_struct", "page", "modify_time"}),
+		//})
+
+	}
+	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
+}
+
+// GetReportFreeLayoutListByReportId
+// @Description: 根据报告ID和章节ID获取所有的布局列表
+// @author: Roc
+// @datetime 2025-04-16 13:46:38
+// @param reportId int
+// @param chapterId int
+// @return list []*ReportFreeLayout
+// @return err error
+func GetReportFreeLayoutListByReportId(reportId, chapterId int) (list []*ReportFreeLayout, err error) {
+	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(&list).Error
+
+	return
+}
+
+// GetAllReportFreeLayoutListByReportId
+// @Description: 根据报告id获取所有的报告自由布局列表(含章节的)
+// @author: Roc
+// @datetime 2025-04-16 13:46:24
+// @param reportId int
+// @return list []*ReportFreeLayout
+// @return err error
+func GetAllReportFreeLayoutListByReportId(reportId int) (list []*ReportFreeLayout, err error) {
+	sql := `select * from report_free_layout  where report_id =? order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId).Find(&list).Error
+
+	return
+}

+ 19 - 11
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 {
@@ -504,9 +510,11 @@ func CountReportChapterByTypeId(typeId int) (count int, err error) {
 // AddReportChapter
 // @Description: 待添加的报告章节
 type AddReportChapter struct {
-	ReportChapter       *ReportChapter
-	GrantList           []*report.ReportChapterGrant
-	GrantPermissionList []*report.ReportChapterPermissionMapping
+	ReportChapter               *ReportChapter
+	GrantList                   []*report.ReportChapterGrant
+	GrantPermissionList         []*report.ReportChapterPermissionMapping
+	ReportChapterFreeLayoutList []*report.ReportFreeLayout
+	InheritReportChapterId      int `description:"继承的章节id"`
 }
 
 // EditReportChapterBaseInfoAndPermissionReq

+ 25 - 1
models/report_v2.go

@@ -17,7 +17,7 @@ import (
 // @param addReportChapterList []AddReportChapter
 // @return reportId int64
 // @return err error
-func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGrant, addReportChapterList []AddReportChapter) (reportId int64, err error) {
+func AddReportAndChapter(reportItem *Report, reportFreeLayoutList []*report.ReportFreeLayout, allGrantUserList []*report.ReportGrant, addReportChapterList []AddReportChapter) (reportId int64, err error) {
 	to := global.DbMap[utils.DbNameReport].Begin()
 	defer func() {
 		if err != nil {
@@ -34,6 +34,17 @@ func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGr
 	}
 	reportId = int64(reportItem.Id)
 
+	// 新增报告分页内容
+	if len(reportFreeLayoutList) > 0 {
+		for _, reportFreeLayout := range reportFreeLayoutList {
+			reportFreeLayout.ReportId = int(reportId)
+		}
+		err = to.CreateInBatches(reportFreeLayoutList, utils.MultiAddNum).Error
+		if err != nil {
+			return
+		}
+	}
+
 	// 新增报告授权
 	if len(allGrantUserList) > 0 {
 		for _, v := range allGrantUserList {
@@ -81,6 +92,19 @@ func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGr
 				}
 			}
 
+			// 新增报告章节分页内容
+			if len(addReportChapter.ReportChapterFreeLayoutList) > 0 {
+				reportChapterFreeLayoutList := addReportChapter.ReportChapterFreeLayoutList
+				for _, reportChapterFreeLayout := range reportChapterFreeLayoutList {
+					reportChapterFreeLayout.ReportId = int(reportId)
+					reportChapterFreeLayout.ReportChapterId = chapterItem.ReportChapterId
+				}
+				err = to.CreateInBatches(reportChapterFreeLayoutList, utils.MultiAddNum).Error
+				if err != nil {
+					return
+				}
+			}
+
 		}
 	}
 

+ 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
 }

+ 2 - 1
services/report_rai.go

@@ -290,7 +290,8 @@ func handleInsertRaiReport(articleResult models.ArticleResultApidate) (err error
 			// 新增报告及章节
 			var reportId int64
 			allGrantUserList := make([]*report.ReportGrant, 0)
-			reportId, err = models.AddReportAndChapter(item, allGrantUserList, []models.AddReportChapter{})
+			reportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+			reportId, err = models.AddReportAndChapter(item, reportFreeLayoutList, allGrantUserList, []models.AddReportChapter{})
 			if err != nil {
 				err = fmt.Errorf("新增报告及章节失败, Err: " + err.Error())
 				return

+ 117 - 16
services/report_v2.go

@@ -66,6 +66,8 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 
 	errMsg = "生成报告失败"
 
+	reportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+
 	// 报告继承
 	if inheritReportId > 0 {
 		inheritReport, tmpErr := models.GetReportByReportId(inheritReportId)
@@ -95,6 +97,7 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 			reportInfo.HeadImg = ``
 			reportInfo.EndImg = ``
 			reportInfo.EndResourceId = inheritReport.EndResourceId
+			reportInfo.FreeLayoutConfig= inheritReport.FreeLayoutConfig
 			if inheritReport.HeadResourceId > 0 {
 				reportInfo.HeadImg = inheritReport.HeadImg
 			}
@@ -102,6 +105,25 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 				reportInfo.EndImg = inheritReport.EndImg
 			}
 			reportInfo.InheritReportId = inheritReport.Id
+			pages, tmpErr := report.GetReportFreeLayoutListByReportId(inheritReport.Id, 0)
+			if tmpErr != nil {
+				errMsg = "获取自由布局内容页失败"
+				err = tmpErr
+				return
+			}
+			for _, v := range pages {
+				reportFreeLayoutList = append(reportFreeLayoutList, &report.ReportFreeLayout{
+					Id:              0,
+					ReportId:        0,
+					ReportChapterId: 0,
+					Page:            v.Page,
+					IsChapter:       v.IsChapter,
+					Content:         v.Content,
+					ContentStruct:   v.ContentStruct,
+					CreateTime:      time.Now(),
+					ModifyTime:      time.Now(),
+				})
+			}
 		}
 	}
 
@@ -110,7 +132,7 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 
 	// 新增报告及章节
 	var reportId int64
-	reportId, err = models.AddReportAndChapter(reportInfo, allGrantUserList, addChapterList)
+	reportId, err = models.AddReportAndChapter(reportInfo, reportFreeLayoutList, allGrantUserList, addChapterList)
 	if err != nil {
 		err = errors.New("新增报告及章节失败, Err: " + err.Error())
 		return
@@ -353,6 +375,24 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		return
 	}
 
+	// 每篇章节对应的分页内容页
+	chapterFreeLayoutListMap := make(map[int][]*report.ReportFreeLayout)
+	{
+		allReportFreeLayoutList, tmpErr := report.GetAllReportFreeLayoutListByReportId(inheritReportId)
+		if tmpErr != nil {
+			errMsg = "获取自由布局内容页失败"
+			err = tmpErr
+			return
+		}
+		for _, reportFreeLayout := range allReportFreeLayoutList {
+			chapterFreeLayoutList, ok := chapterFreeLayoutListMap[reportFreeLayout.ReportChapterId]
+			if !ok {
+				chapterFreeLayoutList = make([]*report.ReportFreeLayout, 0)
+			}
+			chapterFreeLayoutListMap[reportFreeLayout.ReportChapterId] = append(chapterFreeLayoutList, reportFreeLayout)
+		}
+	}
+
 	// 待添加的章节
 	chapterTypeList := make([]*models.ReportChapterType, 0)
 	// 待添加的章节类型id列表
@@ -496,20 +536,22 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		// 继承的报告章节内容
 		for i := 0; i < len(inheritReportChapters); i++ {
 			customChapter := inheritReportChapters[i]
+			// 继承的报告章节ID
+			inheritReportChapterId := customChapter.ReportChapterId
 
 			// 授权用户列表
-			tmpGrantList, ok := grantListMap[customChapter.ReportChapterId]
+			tmpGrantList, ok := grantListMap[inheritReportChapterId]
 			if !ok {
 				tmpGrantList = make([]*report.ReportChapterGrant, 0)
 			}
-			oldChapterIdGrantListMap[customChapter.ReportChapterId] = tmpGrantList
+			oldChapterIdGrantListMap[inheritReportChapterId] = tmpGrantList
 
 			// 关联品种列表
-			chapterPermissionList, ok := chapterPermissionListMap[customChapter.ReportChapterId]
+			chapterPermissionList, ok := chapterPermissionListMap[inheritReportChapterId]
 			if !ok {
 				chapterPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
 			}
-			oldChapterPermissionListMap[customChapter.ReportChapterId] = chapterPermissionList
+			oldChapterPermissionListMap[inheritReportChapterId] = chapterPermissionList
 
 			// 判断该章节是否是系统章节,如果是的话,那就是需要额外创建的
 			if customChapter.TypeId > 0 {
@@ -531,9 +573,10 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 			customChapter.ContentModifyTime = time.Now()
 
 			customAddChapter := models.AddReportChapter{
-				ReportChapter:       customChapter,
-				GrantList:           tmpGrantList,
-				GrantPermissionList: chapterPermissionList,
+				ReportChapter:          customChapter,
+				GrantList:              tmpGrantList,
+				GrantPermissionList:    chapterPermissionList,
+				InheritReportChapterId: inheritReportChapterId,
 			}
 			customAddChapterList = append(customAddChapterList, customAddChapter)
 		}
@@ -542,6 +585,9 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 	// 最大排序
 	var maxSort int
 	for _, typeItem := range chapterTypeList {
+		// 继承的章节ID
+		inheritReportChapterId := 0
+
 		v, ok := inheritChapterMap[typeItem.ReportChapterTypeId]
 
 		// 章节授权用户
@@ -552,6 +598,8 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		chapterItem := new(models.ReportChapter)
 
 		if ok && v != nil {
+			inheritReportChapterId = v.ReportChapterId
+
 			// 如果存在继承的章节,那么就从继承的章节内容中获取
 			chapterItem.AddType = 2
 			chapterItem.Title = v.Title
@@ -596,6 +644,8 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 					return
 				}
 				if chapterNewest != nil {
+					inheritReportChapterId = chapterNewest.ReportChapterId
+
 					chapterItem.AddType = 2
 					chapterItem.Title = chapterNewest.Title
 					chapterItem.ReportType = chapterNewest.ReportType
@@ -691,9 +741,10 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		}
 
 		addChapter := models.AddReportChapter{
-			ReportChapter:       chapterItem,
-			GrantList:           tmpGrantList,
-			GrantPermissionList: tmpChapterPermissionList,
+			ReportChapter:          chapterItem,
+			GrantList:              tmpGrantList,
+			GrantPermissionList:    tmpChapterPermissionList,
+			InheritReportChapterId: inheritReportChapterId,
 		}
 
 		chapterList = append(chapterList, addChapter)
@@ -706,6 +757,27 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		chapterList = append(chapterList, addChapterItem)
 	}
 
+	for k, chapterItem := range chapterList {
+		reportFreeLayoutList, ok := chapterFreeLayoutListMap[chapterItem.InheritReportChapterId]
+		if ok {
+			tmpReportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+			for _, v := range reportFreeLayoutList {
+				tmpReportFreeLayoutList = append(tmpReportFreeLayoutList, &report.ReportFreeLayout{
+					Id:              0,
+					ReportId:        0,
+					ReportChapterId: 0,
+					Page:            v.Page,
+					IsChapter:       v.IsChapter,
+					Content:         v.Content,
+					ContentStruct:   v.ContentStruct,
+					CreateTime:      time.Now(),
+					ModifyTime:      time.Now(),
+				})
+			}
+			chapterList[k].ReportChapterFreeLayoutList = tmpReportFreeLayoutList
+		}
+	}
+
 	//hasGrantUserMap := make(map[int]bool)
 	//for _, grantList := range typeGrantListMap {
 	//	for _, grant := range grantList {
@@ -766,7 +838,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
@@ -1196,10 +1276,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
+		}
 	}
 
 	// 根据审批开关及审批流判断当前报告状态
@@ -1645,6 +1743,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 != "" {

+ 116 - 108
services/smart_report.go

@@ -136,7 +136,7 @@ func SmartReportElasticUpsert(smartReportId int, state int) (err error) {
 	return
 }
 
-func ReportToPdf(width int, reportUrl, filePath string) (err error) {
+func ReportToPdf(width int, reportUrl, filePath string, top, bottom, left, right int) (err error) {
 	pyCode := `
 import asyncio
 from pyppeteer import launch
@@ -168,10 +168,10 @@ async def main():
         'path': "%s",
         'printBackground': True,
         'margin': {
-            'top': '20px',
-            'bottom': '20px',
-            'left': '20px',
-            'right': '20px'
+            'top': '%dpx',
+            'bottom': '%dpx',
+            'left': '%dpx',
+            'right': '%dpx'
         }
     })
     await browser.close()
@@ -187,7 +187,7 @@ finally:
     loop.close()
 `
 
-	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, width, filePath)
+	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, width+left+right, filePath, top, bottom, left, right)
 	utils.FileLog.Info("pdf pyCode: \n" + pyCode)
 	cmd := exec.Command(utils.CommandPython, "-c", pyCode)
 	output, e := cmd.CombinedOutput()
@@ -302,7 +302,10 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 	if reportUrl == `` {
 		return
 	}
-
+	report, err := models.GetReportById(reportId)
+	if err != nil {
+		return
+	}
 	// 先清空字段
 	if reportType == 1 {
 		err = models.UpdatePdfUrlReportById(reportId)
@@ -332,10 +335,14 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 		jpegPath := `./static/` + reportCode + "_1200.jpg"
 
 		width := 1200
+		top, bottom, left, right := 20, 20, 20, 20
+		if report.ReportLayout == 3 {
+			top, bottom, left, right = 0, 0, 0, 0
+		}
 		//if reportType == 3 {
 		//	width = 800
 		//}
-		err = ReportToPdf(width, reportUrl, pdfPath)
+		err = ReportToPdf(width, reportUrl, pdfPath, top, bottom, left, right)
 		if err != nil {
 			utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
 			go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
@@ -449,125 +456,126 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 			}
 		}
 	}()
+	if report.ReportLayout != 3 {
+		// 移动端
+		go func() {
+			pdfPathMobile := `./static/` + reportCode + "_600.pdf"
+			jpegPathMobile := `./static/` + reportCode + "_600.jpg"
+			top, bottom, left, right := 20, 20, 20, 20
+			width := 600
+			err = ReportToPdf(width, reportUrl, pdfPathMobile, top, bottom, left, right)
+			if err != nil {
+				utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
+				go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
+			}
 
-	// 移动端
-	go func() {
-		pdfPathMobile := `./static/` + reportCode + "_600.pdf"
-		jpegPathMobile := `./static/` + reportCode + "_600.jpg"
-
-		width := 600
-		err = ReportToPdf(width, reportUrl, pdfPathMobile)
-		if err != nil {
-			utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
-		}
-
-		file, err := os.Open(pdfPathMobile)
-		if err != nil {
-			utils.FileLog.Info("Open failed: , error: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("Open failed:"+err.Error(), 3)
-			return
-		}
-
-		ext := path.Ext(file.Name())
+			file, err := os.Open(pdfPathMobile)
+			if err != nil {
+				utils.FileLog.Info("Open failed: , error: \n" + err.Error())
+				go alarm_msg.SendAlarmMsg("Open failed:"+err.Error(), 3)
+				return
+			}
 
-		randStr := utils.GetRandStringNoSpecialChar(28)
-		fileName := randStr + ext
-		defer file.Close() //关闭上传文件
+			ext := path.Ext(file.Name())
 
-		resourceUrl := ``
-		ossClient := NewOssClient()
-		if ossClient == nil {
-			utils.FileLog.Info("初始化OSS服务失败")
-			return
-		}
-		resourceUrl, err = ossClient.UploadFile(fileName, pdfPathMobile, "")
-		if err != nil {
-			utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("文件上传失败:"+err.Error(), 3)
-			return
-		}
-		defer func() {
-			_ = os.Remove(pdfPathMobile)
-		}()
+			randStr := utils.GetRandStringNoSpecialChar(28)
+			fileName := randStr + ext
+			defer file.Close() //关闭上传文件
 
-		if reportType == 3 {
-			// 更新pdf url
-			ob := new(smart_report.SmartReport)
-			ob.SmartReportId = reportId
-			ob.DetailPdfUrlMobile = resourceUrl
-			if err = ob.Update([]string{"DetailPdfUrlMobile"}); err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+			resourceUrl := ``
+			ossClient := NewOssClient()
+			if ossClient == nil {
+				utils.FileLog.Info("初始化OSS服务失败")
 				return
 			}
-		} else if reportType == 2 {
-			err = models.ModifyEnglishReportPdfUrlMobile(reportId, resourceUrl)
+			resourceUrl, err = ossClient.UploadFile(fileName, pdfPathMobile, "")
 			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+				utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
+				go alarm_msg.SendAlarmMsg("文件上传失败:"+err.Error(), 3)
 				return
 			}
-		} else if reportType == 1 {
-			err = models.ModifyReportPdfUrlMobile(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
+			defer func() {
+				_ = os.Remove(pdfPathMobile)
+			}()
+
+			if reportType == 3 {
+				// 更新pdf url
+				ob := new(smart_report.SmartReport)
+				ob.SmartReportId = reportId
+				ob.DetailPdfUrlMobile = resourceUrl
+				if err = ob.Update([]string{"DetailPdfUrlMobile"}); err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
+			} else if reportType == 2 {
+				err = models.ModifyEnglishReportPdfUrlMobile(reportId, resourceUrl)
+				if err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
+			} else if reportType == 1 {
+				err = models.ModifyReportPdfUrlMobile(reportId, resourceUrl)
+				if err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
 			}
-		}
 
-		time.Sleep(1 * time.Minute)
-
-		err = ReportToJpeg(width, reportUrl, jpegPathMobile)
-		if err != nil {
-			utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
-		}
-		file, err = os.Open(jpegPathMobile)
-		if err != nil {
-			utils.FileLog.Info("open file failed: , error: \n" + err.Error())
-			return
-		}
+			time.Sleep(1 * time.Minute)
 
-		ext = path.Ext(file.Name())
+			err = ReportToJpeg(width, reportUrl, jpegPathMobile)
+			if err != nil {
+				utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
+			}
+			file, err = os.Open(jpegPathMobile)
+			if err != nil {
+				utils.FileLog.Info("open file failed: , error: \n" + err.Error())
+				return
+			}
 
-		randStr = utils.GetRandStringNoSpecialChar(28)
-		fileName = randStr + ext
-		defer file.Close() //关闭上传文件
+			ext = path.Ext(file.Name())
 
-		resourceUrl = ``
-		ossClient = NewOssClient()
-		if ossClient == nil {
-			utils.FileLog.Info("初始化OSS服务失败")
-			return
-		}
-		resourceUrl, err = ossClient.UploadFile(fileName, jpegPathMobile, "")
-		if err != nil {
-			utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
-			return
-		}
-		defer func() {
-			_ = os.Remove(jpegPathMobile)
-		}()
+			randStr = utils.GetRandStringNoSpecialChar(28)
+			fileName = randStr + ext
+			defer file.Close() //关闭上传文件
 
-		if reportType == 3 {
-			// 更新jpeg url
-			ob := new(smart_report.SmartReport)
-			ob.SmartReportId = reportId
-			ob.DetailImgUrlMobile = resourceUrl
-			if err = ob.Update([]string{"DetailImgUrlMobile"}); err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+			resourceUrl = ``
+			ossClient = NewOssClient()
+			if ossClient == nil {
+				utils.FileLog.Info("初始化OSS服务失败")
 				return
 			}
-		} else if reportType == 2 {
-			err = models.ModifyEnglishReportImgUrlMobile(reportId, resourceUrl)
+			resourceUrl, err = ossClient.UploadFile(fileName, jpegPathMobile, "")
 			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+				utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
 				return
 			}
-		} else if reportType == 1 {
-			err = models.ModifyReportImgUrlMobile(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
+			defer func() {
+				_ = os.Remove(jpegPathMobile)
+			}()
+
+			if reportType == 3 {
+				// 更新jpeg url
+				ob := new(smart_report.SmartReport)
+				ob.SmartReportId = reportId
+				ob.DetailImgUrlMobile = resourceUrl
+				if err = ob.Update([]string{"DetailImgUrlMobile"}); err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
+			} else if reportType == 2 {
+				err = models.ModifyEnglishReportImgUrlMobile(reportId, resourceUrl)
+				if err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
+			} else if reportType == 1 {
+				err = models.ModifyReportImgUrlMobile(reportId, resourceUrl)
+				if err != nil {
+					utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+					return
+				}
 			}
-		}
-	}()
+		}()
+	}
 }