Roc 8 mesiacov pred
rodič
commit
6230541b97
42 zmenil súbory, kde vykonal 9092 pridanie a 1448 odobranie
  1. 59 47
      controllers/classify.go
  2. 8 8
      controllers/english_report/report.go
  3. 435 491
      controllers/report.go
  4. 1 1
      controllers/report_approve/report_approve.go
  5. 59 36
      controllers/report_approve/report_approve_flow.go
  6. 1396 0
      controllers/report_chapter.go
  7. 50 63
      controllers/report_chapter_type.go
  8. 1902 0
      controllers/report_v2.go
  9. 13 36
      controllers/smart_report/smart_report.go
  10. 9 5
      go.mod
  11. 18 0
      go.sum
  12. 67 0
      models/chart_permission.go
  13. 240 92
      models/classify.go
  14. 46 0
      models/company/company_config.go
  15. 10 6
      models/db.go
  16. 3 1
      models/english_report.go
  17. 84 6
      models/permission.go
  18. 372 112
      models/report.go
  19. 110 0
      models/report/report_chapter_grant.go
  20. 152 0
      models/report/report_chapter_permission_mapping.go
  21. 126 0
      models/report/report_grant.go
  22. 13 4
      models/report_approve/report_approve.go
  23. 11 1
      models/report_approve/report_approve_flow.go
  24. 39 0
      models/report_approve/report_approve_record.go
  25. 322 42
      models/report_chapter.go
  26. 2 2
      models/report_chapter_ticker.go
  27. 71 24
      models/report_chapter_type.go
  28. 60 22
      models/report_chapter_type_permission.go
  29. 453 0
      models/report_v2.go
  30. 5 4
      models/wechat_send_msg.go
  31. 81 9
      routers/commentsRouter.go
  32. 10 8
      services/chart_permission_sync.go
  33. 843 0
      services/classify.go
  34. 136 374
      services/report.go
  35. 59 25
      services/report_approve.go
  36. 225 0
      services/report_chapter.go
  37. 130 0
      services/report_classify.go
  38. 1327 0
      services/report_v2.go
  39. 6 3
      services/smart_report.go
  40. 1 1
      services/video.go
  41. 37 25
      services/wechat_send_msg.go
  42. 101 0
      utils/common.go

+ 59 - 47
controllers/classify.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"eta/eta_mobile/models"
+	"eta/eta_mobile/services"
 	"eta/eta_mobile/utils"
 )
 
@@ -12,8 +13,6 @@ type ClassifyController struct {
 
 // @Title 获取分类列表
 // @Description 获取分类列表
-// @Param   PageSize   query   int  true       "每页数据条数"
-// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
 // @Param   KeyWord   query   string  true       "检索关键词"
 // @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
 // @Param   HideDayWeek   query   int  false       "是否隐藏晨周报"
@@ -27,31 +26,54 @@ func (this *ClassifyController) ListClassify() {
 	}()
 
 	keyWord := this.GetString("KeyWord")
-	companyType := this.GetString("CompanyType")
-	hideDayWeek, _ := this.GetInt("HideDayWeek")
-
 	reqEnabled, _ := this.GetInt("Enabled", -1)
-	// 商家不隐藏晨周报
-	if utils.BusinessCode != utils.BusinessCodeRelease {
-		hideDayWeek = 0
-	}
+
 	enabled := -1
 	if reqEnabled == 1 {
 		enabled = reqEnabled
 	}
 
-	list, err := models.GetClassifyList(keyWord, companyType, hideDayWeek, enabled)
+	list, err := models.GetClassifyListByKeyword(keyWord, enabled)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
 
-	parentIds := make([]int, 0)
+	if keyWord != `` {
+		idMap := make(map[int]bool)
+
+		currParentClassifyIdList := make([]int, 0)
+		for _, v := range list {
+			idMap[v.Id] = true
+			if v.ParentId > 0 {
+				currParentClassifyIdList = append(currParentClassifyIdList, v.ParentId)
+			}
+		}
+
+		findList := list
+		list = make([]*models.ClassifyList, 0)
+
+		tmpList, tmpErr := services.GetParentClassifyListByParentIdList(currParentClassifyIdList)
+		if tmpErr != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.Id]; !ok {
+				list = append(list, v)
+			}
+		}
+
+		list = append(list, findList...)
+	}
+
+	classifyIdList := make([]int, 0)
 	for i := range list {
-		parentIds = append(parentIds, list[i].Id)
+		classifyIdList = append(classifyIdList, list[i].Id)
 	}
-	parentIdLen := len(parentIds)
+	parentIdLen := len(classifyIdList)
 	if parentIdLen == 0 {
 		resp := &models.ClassifyListResp{
 			List: list,
@@ -63,12 +85,12 @@ func (this *ClassifyController) ListClassify() {
 		return
 	}
 
-	// 获取一级分类-子目录列表
+	// 获取子目录列表
 	menuListMap := make(map[int][]*models.ClassifyMenu, 0)
 	var menuCond string
 	var menuPars []interface{}
 	menuCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
-	menuPars = append(menuPars, parentIds)
+	menuPars = append(menuPars, classifyIdList)
 	parentMenus, e := models.GetClassifyMenuList(menuCond, menuPars)
 	if e != nil {
 		br.Msg = "获取失败"
@@ -82,26 +104,13 @@ func (this *ClassifyController) ListClassify() {
 		menuListMap[parentMenus[i].ClassifyId] = append(menuListMap[parentMenus[i].ClassifyId], parentMenus[i])
 	}
 
-	// 获取子分类
-	children, e := models.GetClassifyChildByParentIds(parentIds, keyWord, enabled)
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取子分类失败"
-		return
-	}
-	childrenIds := make([]int, 0)
-	for i := range children {
-		childrenIds = append(childrenIds, children[i].Id)
-	}
-	childrenIdsLen := len(childrenIds)
-
-	// 获取二级分类-子目录关联
+	// 分类与子目录关联
 	relateMap := make(map[int]int, 0)
-	if childrenIdsLen > 0 {
+	{
 		var relateCond string
 		var relatePars []interface{}
-		relateCond += ` AND classify_id IN (` + utils.GetOrmInReplace(childrenIdsLen) + `)`
-		relatePars = append(relatePars, childrenIds)
+		relateCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
+		relatePars = append(relatePars, classifyIdList)
 		relates, e := models.GetClassifyMenuRelationList(relateCond, relatePars)
 		if e != nil {
 			br.Msg = "获取失败"
@@ -113,26 +122,29 @@ func (this *ClassifyController) ListClassify() {
 		}
 	}
 
-	// 二级分类
-	childrenMap := make(map[int][]*models.ClassifyItem, 0)
-	for i := range children {
-
-		if childrenMap[children[i].ParentId] == nil {
-			childrenMap[children[i].ParentId] = make([]*models.ClassifyItem, 0)
-		}
-		tmp := &models.ClassifyItem{
-			Classify:       *children[i],
-			ClassifyMenuId: relateMap[children[i].Id],
+	// 查询分类绑定的权限
+	permissionList, _ := models.GetAllPermissionMapping()
+	classifyPermissionMap := make(map[int][]int)
+	if len(permissionList) > 0 {
+		for _, v := range permissionList {
+			classifyPermissionMap[v.ClassifyId] = append(classifyPermissionMap[v.ClassifyId], v.ChartPermissionId)
 		}
-		childrenMap[children[i].ParentId] = append(childrenMap[children[i].ParentId], tmp)
 	}
+	// 遍历分类并绑定子目录和权限
+	for i, v := range list {
+		list[i].ClassifyMenuList = menuListMap[v.Id]
 
-	// 一级分类
-	for i := range list {
-		list[i].ClassifyMenuList = menuListMap[list[i].Id]
-		list[i].Child = childrenMap[list[i].Id]
+		list[i].ClassifyMenuId = relateMap[v.Id]
+		if permissionIds, ok := classifyPermissionMap[v.Id]; ok {
+			list[i].ChartPermissionIdList = permissionIds
+		}
 	}
 
+	// 先将分类列表排序
+	services.SortClassifyListBySortAndCreateTime(list)
+	// 接着转换结构
+	list = services.GetClassifyListTreeRecursive(list, 0)
+
 	resp := new(models.ClassifyListResp)
 	resp.List = list
 	br.Data = resp

+ 8 - 8
controllers/english_report/report.go

@@ -87,7 +87,7 @@ func (this *EnglishReportController) Add() {
 	}
 
 	// 根据审批开关及审批流判断当前报告状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird, models.ReportOperateAdd)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -512,7 +512,7 @@ func (this *EnglishReportController) ListReport() {
 		fieldArr := []string{
 			"id", "add_type", "classify_id_first", "classify_name_first", "classify_id_second", "classify_name_second", "title", "abstract", "author",
 			"frequency", "create_time", "modify_time", "state", "publish_time", "pre_publish_time", "stage", "msg_is_send", "report_code", "pv", "share_url",
-			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name", "approve_time","detail_img_url","detail_pdf_url",
+			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name", "approve_time", "detail_img_url", "detail_pdf_url",
 		}
 		items, e := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize, fieldArr)
 		if e != nil {
@@ -736,7 +736,7 @@ func (this *EnglishReportController) PublishReport() {
 		}
 
 		// 根据审批开关及审批流判断当前报告状态
-		state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond, models.ReportOperatePublish)
+		state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond, 0, models.ReportOperatePublish)
 		if e != nil {
 			br.Msg = "操作失败"
 			br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -839,7 +839,7 @@ func (this *EnglishReportController) PrePublishReport() {
 	}
 
 	// 校验是否开启了审批流
-	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond)
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond, 0)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
@@ -898,7 +898,7 @@ func (this *EnglishReportController) PublishCancleReport() {
 	}
 
 	// 根据审批开关及审批流判断当前报告状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, models.ReportOperateCancelPublish)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, 0, models.ReportOperateCancelPublish)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -1355,7 +1355,7 @@ func (this *EnglishReportController) SubmitApprove() {
 	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateSubmitApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
@@ -1378,7 +1378,7 @@ func (this *EnglishReportController) SubmitApprove() {
 	}
 
 	// 提交审批
-	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, sysUser.AdminId, sysUser.RealName)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "提交审批失败, Err: " + e.Error()
@@ -1446,7 +1446,7 @@ func (this *EnglishReportController) CancelApprove() {
 	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateCancelApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 435 - 491
controllers/report.go


+ 1 - 1
controllers/report_approve/report_approve.go

@@ -43,7 +43,7 @@ func (this *ReportApproveController) CheckApproveOpen() {
 	}
 
 	// 校验是否开启了审批流
-	opening, e := services.CheckReportOpenApprove(req.ReportType, req.ClassifyFirstId, req.ClassifySecondId)
+	opening, e := services.CheckReportOpenApprove(req.ReportType, req.ClassifyFirstId, req.ClassifySecondId, req.ClassifyThirdId)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()

+ 59 - 36
controllers/report_approve/report_approve_flow.go

@@ -56,6 +56,8 @@ func (this *ReportApproveFlowController) List() {
 
 	var cond, orderRule string
 	var pars []interface{}
+	cond += fmt.Sprintf(` AND %s = ? `, report_approve.ReportApproveFlowCols.Enabled)
+	pars = append(pars, 1)
 	// 筛选项
 	{
 		keyword := strings.TrimSpace(params.Keyword)
@@ -68,6 +70,10 @@ func (this *ReportApproveFlowController) List() {
 			cond += fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifySecondId)
 			pars = append(pars, params.ReportType, params.ClassifySecondId)
 		}
+		if params.ClassifyThirdId > 0 {
+			cond += fmt.Sprintf(` AND %s = ? `, report_approve.ReportApproveFlowCols.ClassifyThirdId)
+			pars = append(pars, params.ReportType, params.ClassifyThirdId)
+		}
 		if params.SortRule > 0 {
 			orderMap := map[int]string{1: "ASC", 2: "DESC"}
 			orderRule = fmt.Sprintf("%s %s", report_approve.ReportApproveFlowCols.CreateTime, orderMap[params.SortRule])
@@ -137,7 +143,14 @@ func (this *ReportApproveFlowController) List() {
 		if v.ReportType == report_approve.FlowReportTypeEnglish {
 			t.ReportClassify = fmt.Sprintf("%s/%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], enClassifyIdName[enRootIdMap[v.ClassifySecondId]], enClassifyIdName[v.ClassifyFirstId], enClassifyIdName[v.ClassifySecondId])
 		} else {
-			t.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], cnClassifyIdName[v.ClassifyFirstId], cnClassifyIdName[v.ClassifySecondId])
+			reportClassify := fmt.Sprintf("%s/%s", report_approve.FlowReportTypeMap[v.ReportType], cnClassifyIdName[v.ClassifyFirstId])
+			if v.ClassifySecondId > 0 {
+				reportClassify = fmt.Sprintf("%s/%s", reportClassify, cnClassifyIdName[v.ClassifySecondId])
+			}
+			if v.ClassifyThirdId > 0 {
+				reportClassify = fmt.Sprintf("%s/%s", reportClassify, cnClassifyIdName[v.ClassifyThirdId])
+			}
+			t.ReportClassify = reportClassify
 		}
 		resp.List = append(resp.List, t)
 	}
@@ -221,9 +234,9 @@ func (this *ReportApproveFlowController) Add() {
 	// 审批流是否已存在
 	{
 		flowOb := new(report_approve.ReportApproveFlow)
-		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId)
 		existPars := make([]interface{}, 0)
-		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId)
+		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId, req.ClassifyThirdId)
 		exist, e := flowOb.GetItemByCondition(existCond, existPars, "")
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			br.Msg = "获取失败"
@@ -241,7 +254,9 @@ func (this *ReportApproveFlowController) Add() {
 	flowItem.ReportType = req.ReportType
 	flowItem.ClassifyFirstId = req.ClassifyFirstId
 	flowItem.ClassifySecondId = req.ClassifySecondId
+	flowItem.ClassifyThirdId = req.ClassifyThirdId
 	flowItem.CurrVersion = 1
+	flowItem.Enabled = 1
 	flowItem.CreateTime = time.Now().Local()
 	flowItem.ModifyTime = time.Now().Local()
 
@@ -273,7 +288,7 @@ func (this *ReportApproveFlowController) Add() {
 
 	// 更新审批对应的报告状态:未发布->待提交
 	go func() {
-		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, flowItem.ClassifyThirdId, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
 	}()
 
 	br.Data = detail
@@ -369,9 +384,9 @@ func (this *ReportApproveFlowController) Edit() {
 
 	// 审批流是否已存在
 	{
-		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s <> ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ReportApproveFlowId)
+		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?  AND %s = ? AND %s <> ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId, report_approve.ReportApproveFlowCols.ReportApproveFlowId)
 		existPars := make([]interface{}, 0)
-		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId, req.ReportApproveFlowId)
+		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId, req.ClassifyThirdId, req.ReportApproveFlowId)
 		exist, e := flowOb.GetItemByCondition(existCond, existPars, "")
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
@@ -403,15 +418,15 @@ func (this *ReportApproveFlowController) Edit() {
 	}
 
 	// 变更了报告分类时, 判断是否允许变更
-	if req.ReportType != flowItem.ReportType || req.ClassifyFirstId != flowItem.ClassifyFirstId || req.ClassifySecondId != flowItem.ClassifySecondId {
-		checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId)
+	if req.ReportType != flowItem.ReportType || req.ClassifyFirstId != flowItem.ClassifyFirstId || req.ClassifySecondId != flowItem.ClassifySecondId || req.ClassifyThirdId != flowItem.ClassifyThirdId {
+		checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, flowItem.ClassifyThirdId)
 		if e != nil {
 			br.Msg = "操作失败"
 			br.ErrMsg = "校验审批流是否可变更失败, Err: " + e.Error()
 			return
 		}
 		if !checkOk {
-			br.Msg = "当前有未走完流程的报告, 请走完流程后再做变更!"
+			br.Msg = "当前有未走完流程的报告, 请走完流程后再做变更"
 			return
 		}
 	}
@@ -420,6 +435,7 @@ func (this *ReportApproveFlowController) Edit() {
 	flowItem.ReportType = req.ReportType
 	flowItem.ClassifyFirstId = req.ClassifyFirstId
 	flowItem.ClassifySecondId = req.ClassifySecondId
+	flowItem.ClassifyThirdId = req.ClassifyThirdId
 	flowItem.CurrVersion += 1
 	flowItem.ModifyTime = time.Now().Local()
 
@@ -566,7 +582,7 @@ func (this *ReportApproveFlowController) Remove() {
 	}
 
 	// 校验是否允许删除
-	checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId)
+	checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, flowItem.ClassifyThirdId)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验审批流是否可变更失败, Err: " + e.Error()
@@ -586,7 +602,7 @@ func (this *ReportApproveFlowController) Remove() {
 
 	// 更新审批对应的报告状态:待提交->未发布
 	go func() {
-		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, flowItem.ClassifyThirdId, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
 	}()
 
 	br.Ret = 200
@@ -645,7 +661,7 @@ func (this *ReportApproveFlowController) ReportClassifyTree() {
 	}
 	hasFlowMap := make(map[string]bool)
 	for _, v := range flows {
-		k := fmt.Sprintf("%d-%d-%d", v.ReportType, v.ClassifyFirstId, v.ClassifySecondId)
+		k := fmt.Sprintf("%d-%d-%d-%d", v.ReportType, v.ClassifyFirstId, v.ClassifySecondId, v.ClassifyThirdId)
 		if k == flowKey {
 			// 当前审批流对应的分类标记为可选状态
 			continue
@@ -653,7 +669,7 @@ func (this *ReportApproveFlowController) ReportClassifyTree() {
 		hasFlowMap[k] = true
 	}
 
-	resp, cnTree, smartTree, enTree := make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0)
+	resp, cnTree, enTree := make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0)
 
 	var cnErr, enErr error
 	wg := sync.WaitGroup{}
@@ -672,17 +688,16 @@ func (this *ReportApproveFlowController) ReportClassifyTree() {
 		}
 		cnTree = services.GetReportClassifyTreeRecursive(classify, 0)
 		for _, v := range cnTree {
-			for _, v2 := range v.Children {
-				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeChinese, v.ClassifyId, v2.ClassifyId)
-				v2.HasFlow = hasFlowMap[k]
-			}
-		}
+			k1 := fmt.Sprintf("%d-%d-%d-%d", report_approve.FlowReportTypeChinese, v.ClassifyId, 0, 0)
+			v.HasFlow = hasFlowMap[k1]
 
-		smartTree = services.GetReportClassifyTreeRecursive(classify, 0)
-		for _, v := range smartTree {
 			for _, v2 := range v.Children {
-				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeSmart, v.ClassifyId, v2.ClassifyId)
-				v2.HasFlow = hasFlowMap[k]
+				k2 := fmt.Sprintf("%d-%d-%d-%d", report_approve.FlowReportTypeChinese, v.ClassifyId, v2.ClassifyId, 0)
+				v2.HasFlow = hasFlowMap[k2]
+				for _, v3 := range v2.Children {
+					k3 := fmt.Sprintf("%d-%d-%d-%d", report_approve.FlowReportTypeChinese, v.ClassifyId, v2.ClassifyId, v3.ClassifyId)
+					v3.HasFlow = hasFlowMap[k3]
+				}
 			}
 		}
 	}()
@@ -701,7 +716,7 @@ func (this *ReportApproveFlowController) ReportClassifyTree() {
 		enTree = services.GetReportClassifyTreeRecursive(classify, 0)
 		for _, v := range enTree {
 			for _, v2 := range v.Children {
-				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeEnglish, v.ClassifyId, v2.ClassifyId)
+				k := fmt.Sprintf("%d-%d-%d-%d", report_approve.FlowReportTypeEnglish, v.ClassifyId, v2.ClassifyId, 0)
 				v2.HasFlow = hasFlowMap[k]
 			}
 		}
@@ -720,19 +735,27 @@ func (this *ReportApproveFlowController) ReportClassifyTree() {
 		return
 	}
 
-	resp = append(resp, &report_approve.ReportClassifyTreeItem{
-		ClassifyId:   report_approve.FlowReportTypeChinese,
-		ClassifyName: "研报列表",
-		Children:     cnTree,
-	}, &report_approve.ReportClassifyTreeItem{
-		ClassifyId:   report_approve.FlowReportTypeEnglish,
-		ClassifyName: "英文研报",
-		Children:     enTree,
-	}, &report_approve.ReportClassifyTreeItem{
-		ClassifyId:   report_approve.FlowReportTypeSmart,
-		ClassifyName: "智能研报",
-		Children:     smartTree,
-	})
+	if this.Lang == utils.EnLangVersion {
+		resp = append(resp, &report_approve.ReportClassifyTreeItem{
+			ClassifyId:   report_approve.FlowReportTypeChinese,
+			ClassifyName: "Report list",
+			Children:     cnTree,
+		}, &report_approve.ReportClassifyTreeItem{
+			ClassifyId:   report_approve.FlowReportTypeEnglish,
+			ClassifyName: "English Report",
+			Children:     enTree,
+		})
+	} else {
+		resp = append(resp, &report_approve.ReportClassifyTreeItem{
+			ClassifyId:   report_approve.FlowReportTypeChinese,
+			ClassifyName: "研报",
+			Children:     cnTree,
+		}, &report_approve.ReportClassifyTreeItem{
+			ClassifyId:   report_approve.FlowReportTypeEnglish,
+			ClassifyName: "英文研报",
+			Children:     enTree,
+		})
+	}
 
 	br.Data = resp
 	br.Ret = 200

+ 1396 - 0
controllers/report_chapter.go

@@ -0,0 +1,1396 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report"
+	"eta/eta_mobile/services"
+	"eta/eta_mobile/services/data"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/kgiannakakis/mp3duration/src/mp3duration"
+	"html"
+	"os"
+	"path"
+	"strconv"
+	"time"
+)
+
+// AddChapter
+// @Title 新增晨周报章节内容
+// @Description 新增晨周报章节内容
+// @Param	request	body models.AddReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /chapter/add [post]
+func (this *ReportController) AddChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.AddReportChapterReq
+	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 = "报告ID有误"
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(req.ReportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许新增"
+		br.ErrMsg = "该报告已发布,不允许新增"
+		return
+	}
+
+	//newContent := req.Content
+	//// 更新章节及指标
+	//contentSub := ""
+	//if req.Content != "" {
+	//	e := utils.ContentXssCheck(req.Content)
+	//	if e != nil {
+	//		br.Msg = "存在非法标签"
+	//		br.ErrMsg = "存在非法标签, Err: " + e.Error()
+	//		return
+	//	}
+	//	contentClean, e := services.FilterReportContentBr(req.Content)
+	//	if e != nil {
+	//		br.Msg = "内容去除前后空格失败"
+	//		br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+	//		return
+	//	}
+	//	req.Content = contentClean
+	//
+	//	contentSub, err = services.GetReportContentSub(req.Content)
+	//	if err != nil {
+	//		br.Msg = "内容分段解析失败"
+	//		br.ErrMsg = "编辑报告章节-解析 ContentSub 失败, Err: " + err.Error()
+	//		return
+	//	}
+	//}
+	//if req.Content == "" {
+	//	req.Content = newContent
+	//}
+
+	// 最小单元的分类
+	var minClassifyId int
+	var minClassifyName string
+	if reportInfo.ClassifyIdThird > 0 {
+		minClassifyId = reportInfo.ClassifyIdThird
+		minClassifyName = reportInfo.ClassifyNameThird
+	} else if reportInfo.ClassifyIdSecond > 0 {
+		minClassifyId = reportInfo.ClassifyIdSecond
+		minClassifyName = reportInfo.ClassifyNameSecond
+	} else {
+		minClassifyId = reportInfo.ClassifyIdFirst
+		minClassifyName = reportInfo.ClassifyNameFirst
+	}
+
+	reportChapterInfo := new(models.ReportChapter)
+	reportChapterInfo.ReportId = reportInfo.Id
+	reportChapterInfo.ClassifyIdFirst = minClassifyId
+	reportChapterInfo.ClassifyNameFirst = minClassifyName
+
+	reportChapterInfo.Title = req.Title
+	reportChapterInfo.AddType = 1
+	reportChapterInfo.PublishState = 1
+	//reportChapterInfo.Author = req.Author
+	//reportChapterInfo.Content = html.EscapeString(req.Content)
+	//reportChapterInfo.ContentSub = html.EscapeString(contentSub)
+	reportChapterInfo.IsEdit = 1
+	//reportChapterInfo.CreateTime = req.CreateTime
+	reportChapterInfo.CreateTime = reportInfo.CreateTime
+	reportChapterInfo.VideoKind = 2
+	reportChapterInfo.Stage = reportInfo.Stage
+
+	reportChapterInfo.LastModifyAdminId = sysUser.AdminId
+	reportChapterInfo.LastModifyAdminName = sysUser.RealName
+	reportChapterInfo.ContentModifyTime = time.Now()
+	//reportChapterInfo.ContentStruct = html.EscapeString(req.ContentStruct)
+	//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 = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// EditChapterBaseInfoAndPermission
+// @Title 修改报告章节的基础信息、授权用户权限、品种权限
+// @Description 修改报告章节的基础信息、授权用户权限、品种权限
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /chapter/base_info/edit [post]
+func (this *ReportController) EditChapterBaseInfoAndPermission() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditReportChapterBaseInfoAndPermissionReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportChapterId := req.ReportChapterId
+	if reportChapterId <= 0 {
+		br.Msg = "报告章节ID有误"
+		return
+	}
+	// 获取章节详情
+	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "报告章节信息有误"
+		br.ErrMsg = "报告章节信息有误, Err: " + err.Error()
+		return
+	}
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(reportChapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 报告的最后编辑人
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+
+	err, errMsg := services.EditChapterBaseInfoAndPermission(reportInfo, reportChapterInfo, req.Title, req.PermissionIdList, req.AdminIdList)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// EditDayWeekChapter
+// @Title 编辑晨周报章节内容
+// @Description 编辑晨周报章节内容
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /editDayWeekChapter [post]
+func (this *ReportController) EditDayWeekChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportChapterId := req.ReportChapterId
+	if reportChapterId <= 0 {
+		br.Msg = "报告章节ID有误"
+		return
+	}
+	if req.Content == "" {
+		br.Msg = "请输入内容"
+		return
+	}
+
+	// 获取章节详情
+	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "报告章节信息有误"
+		br.ErrMsg = "报告章节信息有误, Err: " + err.Error()
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(reportChapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+
+	// 操作权限校验
+	{
+		// 如果不是创建人,那么就要去查看是否授权
+		if reportInfo.AdminId != sysUser.AdminId {
+			// 授权用户权限校验
+			chapterGrantObj := report.ReportChapterGrant{}
+			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
+			if tmpErr != nil {
+				if tmpErr.Error() == utils.ErrNoRow() {
+					br.Msg = "没有权限"
+					br.ErrMsg = "没有权限"
+					br.IsSendEmail = false
+					return
+				}
+				br.Msg = "获取章节id授权用户失败"
+				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
+				return
+			}
+		}
+
+		// 标记更新中
+		{
+			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
+			if err != nil {
+				br.Msg = err.Error()
+				return
+			}
+			if markStatus.Status == 1 {
+				br.Msg = markStatus.Msg
+				br.IsSendEmail = false
+				return
+			}
+		}
+	}
+
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+	// 报告的最后编辑人
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+
+	reqTickerList := req.TickerList
+	// 更新章节及指标
+	contentSub := ""
+	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
+		contentClean, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = contentClean
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			br.Msg = "内容分段解析失败"
+			br.ErrMsg = "编辑报告章节-解析 ContentSub 失败, Err: " + err.Error()
+			return
+		}
+	}
+
+	reportChapterInfo.Title = req.Title
+	//reportChapterInfo.AddType = req.AddType
+	reportChapterInfo.Author = req.Author
+	reportChapterInfo.Content = html.EscapeString(req.Content)
+	reportChapterInfo.ContentSub = html.EscapeString(contentSub)
+	reportChapterInfo.IsEdit = 1
+	reportChapterInfo.CreateTime = req.CreateTime
+
+	reportChapterInfo.LastModifyAdminId = sysUser.AdminId
+	reportChapterInfo.LastModifyAdminName = sysUser.RealName
+	reportChapterInfo.ContentModifyTime = time.Now()
+	reportChapterInfo.ContentStruct = html.EscapeString(req.ContentStruct)
+
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "Title", "AddType", "Author", "Content", "ContentSub", "IsEdit", "CreateTime")
+
+	updateCols = append(updateCols, "LastModifyAdminId", "LastModifyAdminName", "ContentModifyTime", "ContentStruct")
+
+	// 章节报告更新指标
+	tickerList := make([]*models.ReportChapterTicker, 0)
+	if len(reqTickerList) > 0 {
+		nowTime := time.Now()
+		for i := 0; i < len(reqTickerList); i++ {
+			tickerList = append(tickerList, &models.ReportChapterTicker{
+				ReportChapterId: reportChapterInfo.ReportChapterId,
+				Sort:            reqTickerList[i].Sort,
+				Ticker:          reqTickerList[i].Ticker,
+				CreateTime:      nowTime,
+				UpdateTime:      nowTime,
+			})
+		}
+	}
+	err = models.UpdateChapterAndTicker(reportInfo, reportChapterInfo, updateCols, tickerList)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+
+	// 标记更新中
+	{
+		markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+		if markStatus.Status == 1 {
+			br.Msg = markStatus.Msg
+			return
+		}
+	}
+
+	// 备份关键数据
+	chapters := make([]*models.ReportChapter, 0)
+	chapters = append(chapters, reportChapterInfo)
+	go services.SaveReportLogs(nil, chapters, sysUser.AdminId, sysUser.RealName)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// DelChapter
+// @Title 编辑晨周报章节内容
+// @Description 编辑晨周报章节内容
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /chapter/del [post]
+func (this *ReportController) DelChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.DelReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportChapterId := req.ReportChapterId
+	if reportChapterId <= 0 {
+		br.Msg = "报告章节ID有误"
+		return
+	}
+
+	// 获取章节详情
+	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "报告章节信息有误"
+		br.ErrMsg = "报告章节信息有误, Err: " + err.Error()
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(reportChapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+
+	// 操作权限校验
+	{
+		// 如果不是创建人,那么就要去查看是否授权
+		if reportInfo.AdminId != sysUser.AdminId {
+			// 授权用户权限校验
+			chapterGrantObj := report.ReportChapterGrant{}
+			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
+			if tmpErr != nil {
+				if tmpErr.Error() == utils.ErrNoRow() {
+					br.Msg = "没有权限"
+					br.ErrMsg = "没有权限"
+					br.IsSendEmail = false
+					return
+				}
+				br.Msg = "获取章节id授权用户失败"
+				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
+				return
+			}
+		}
+
+		// 标记更新中
+		{
+			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
+			if err != nil {
+				br.Msg = err.Error()
+				return
+			}
+			if markStatus.Status == 1 {
+				br.Msg = markStatus.Msg
+				br.IsSendEmail = false
+				return
+			}
+		}
+	}
+
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 删除章节
+	err, errMsg := services.DelChapter(reportInfo, reportChapterInfo, sysUser)
+	if err != nil {
+		br.Msg = "删除失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 备份关键数据
+	chapters := make([]*models.ReportChapter, 0)
+	chapters = append(chapters, reportChapterInfo)
+	go services.SaveReportLogs(nil, chapters, sysUser.AdminId, sysUser.RealName)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// GetReportChapterList
+// @Title 获取报告章节列表
+// @Description 获取报告章节列表
+// @Param   ReportId	query	string	true	"报告ID"
+// @Success 200 {object} company.CompanyListResp
+// @router /getReportChapterList [get]
+func (this *ReportController) GetReportChapterList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reqReportId := this.GetString("ReportId")
+	reportId, _ := strconv.Atoi(reqReportId)
+	if reportId <= 0 {
+		br.Msg = "报告ID有误"
+		return
+	}
+
+	// 获取报告信息
+	reportInfo, err := models.GetReportByReportId(reportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 权限校验
+	isAuth, err := services.CheckReportAuthByReportChapterInfo(sysUser.AdminId, reportInfo.AdminId, reportId)
+	if err != nil {
+		br.Msg = "获取报告权限失败"
+		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
+		return
+	}
+	if !isAuth {
+		br.Msg = "无操作权限"
+		br.ErrMsg = "无操作权限"
+		return
+	}
+
+	// 获取章节列表
+	chapterList, err := models.GetChapterListByReportId(reportId)
+	if err != nil {
+		br.Msg = "获取章节列表失败"
+		br.ErrMsg = "获取章节列表失败, Err: " + err.Error()
+		return
+	}
+	typeList, err := models.GetReportChapterTypeList()
+	if err != nil {
+		br.Msg = "获取章节类型列表失败"
+		br.ErrMsg = "获取章节类型列表失败, Err: " + err.Error()
+		return
+	}
+	typeIdImg := make(map[int]string)
+	for i := 0; i < len(typeList); i++ {
+		typeIdImg[typeList[i].ReportChapterTypeId] = typeList[i].EditImgUrl
+	}
+
+	resp := make([]models.ReportChapterResp, 0)
+	if len(chapterList) > 0 {
+		chapterIdList := make([]int, 0)
+		// 章节id授权用户列表map
+		chapterIdGrandListMap := make(map[int][]int)
+		// 章节id关联品种id列表map
+		chapterIdPermissionListMap := make(map[int][]int)
+
+		for _, v := range chapterList {
+			chapterIdList = append(chapterIdList, v.ReportChapterId)
+		}
+
+		// 处理章节id授权用户列表
+		{
+			chapterGrantObj := report.ReportChapterGrant{}
+			chapterGrantList, tmpErr := chapterGrantObj.GetGrantListByIdList(chapterIdList)
+			if tmpErr != nil {
+				br.Msg = "获取章节id授权用户列表失败"
+				br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+				return
+			}
+
+			for _, v := range chapterGrantList {
+				tmpChapterIdGrandList, ok := chapterIdGrandListMap[v.ReportChapterId]
+				if !ok {
+					tmpChapterIdGrandList = make([]int, 0)
+				}
+				chapterIdGrandListMap[v.ReportChapterId] = append(tmpChapterIdGrandList, v.AdminId)
+			}
+		}
+
+		// 处理章节id关联品种id列表
+		{
+			obj := report.ReportChapterPermissionMapping{}
+			chapterPermissionList, tmpErr := obj.GetPermissionListByIdList(chapterIdList)
+			if tmpErr != nil {
+				br.Msg = "获取章节id关联品种列表失败"
+				br.ErrMsg = "获取章节id关联品种列表失败, Err: " + tmpErr.Error()
+				return
+			}
+
+			for _, v := range chapterPermissionList {
+				tmpChapterIdPermissionList, ok := chapterIdPermissionListMap[v.ReportChapterId]
+				if !ok {
+					tmpChapterIdPermissionList = make([]int, 0)
+				}
+				chapterIdPermissionListMap[v.ReportChapterId] = append(tmpChapterIdPermissionList, v.ChartPermissionId)
+			}
+		}
+
+		// 章节类型的字段
+		for _, item := range chapterList {
+			// 授权的用户列表
+			tmpChapterIdGrandList, ok := chapterIdGrandListMap[item.ReportChapterId]
+			if !ok {
+				tmpChapterIdGrandList = make([]int, 0)
+			}
+			// 关联的品种列表
+			tmpChapterIdPermissionList, ok := chapterIdPermissionListMap[item.ReportChapterId]
+			if !ok {
+				tmpChapterIdPermissionList = make([]int, 0)
+			}
+
+			tmpChapterItem := models.ReportChapterResp{
+				ReportChapterId:  item.ReportChapterId,
+				ReportId:         item.ReportId,
+				ReportType:       item.ReportType,
+				TypeId:           item.TypeId,
+				TypeName:         item.TypeName,
+				TypeEditImg:      typeIdImg[item.TypeId],
+				Title:            item.Title,
+				IsEdit:           item.IsEdit,
+				Trend:            item.Trend,
+				Sort:             item.Sort,
+				PublishState:     item.PublishState,
+				VideoUrl:         item.VideoUrl,
+				VideoName:        item.VideoName,
+				VideoPlaySeconds: item.VideoPlaySeconds,
+				VideoSize:        item.VideoSize,
+				VideoKind:        item.VideoKind,
+				ModifyTime:       item.ModifyTime.Format(utils.FormatDate),
+				GrandAdminIdList: tmpChapterIdGrandList,
+				PermissionIdList: tmpChapterIdPermissionList,
+			}
+			markStatus, err := services.UpdateReportEditMark(item.ReportId, item.ReportChapterId, this.SysUser.AdminId, 2, this.SysUser.RealName, this.Lang)
+			if err != nil {
+				br.Msg = "查询标记状态失败"
+				br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+				return
+			}
+
+			if markStatus.Status == 0 {
+				tmpChapterItem.CanEdit = true
+			} else {
+				tmpChapterItem.Editor = markStatus.Editor
+			}
+
+			// 报告章节的操作权限
+			tmpChapterItem.IsAuth = services.CheckChapterAuthByAdminIdList(sysUser.AdminId, reportInfo.AdminId, tmpChapterIdGrandList)
+
+			resp = append(resp, tmpChapterItem)
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// GetDayWeekChapter
+// @Title 获取晨周报章节信息
+// @Description 获取晨周报章节信息
+// @Param	ReportChapterId  query  int  true  "报告章节ID"
+// @Success 200 Ret=200 保存成功
+// @router /getDayWeekChapter [get]
+func (this *ReportController) GetDayWeekChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportChapterId, _ := this.GetInt("ReportChapterId")
+	if reportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+
+	chapterItem, err := models.GetReportChapterItemById(reportChapterId)
+	if err != nil {
+		br.Msg = "获取章节信息失败"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportById(chapterItem.ReportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 权限校验
+	isAuth, err := services.CheckReportAuthByReportChapterInfo(sysUser.AdminId, reportInfo.AdminId, reportInfo.Id)
+	if err != nil {
+		br.Msg = "获取报告权限失败"
+		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
+		return
+	}
+	if !isAuth {
+		br.Msg = "无操作权限"
+		br.ErrMsg = "无操作权限"
+		return
+	}
+
+	chapterItem.Content = html.UnescapeString(chapterItem.Content)
+	chapterItem.ContentSub = html.UnescapeString(chapterItem.ContentSub)
+	chapterItem.ContentStruct = html.UnescapeString(chapterItem.ContentStruct)
+
+	// 授权用户列表map
+	chapterGrantIdList := make([]int, 0)
+	// 关联品种id列表map
+	chapterPermissionIdList := make([]int, 0)
+	// 处理章节id授权用户列表
+	{
+		chapterGrantObj := report.ReportChapterGrant{}
+		chapterGrantList, tmpErr := chapterGrantObj.GetGrantListById(chapterItem.ReportChapterId)
+		if tmpErr != nil {
+			br.Msg = "获取章节id授权用户列表失败"
+			br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+			return
+		}
+
+		for _, v := range chapterGrantList {
+			chapterGrantIdList = append(chapterGrantIdList, v.AdminId)
+		}
+	}
+
+	// 处理章节id关联品种id列表
+	{
+		obj := report.ReportChapterPermissionMapping{}
+		chapterPermissionList, tmpErr := obj.GetPermissionListById(chapterItem.ReportChapterId)
+		if tmpErr != nil {
+			br.Msg = "获取章节id关联品种列表失败"
+			br.ErrMsg = "获取章节id关联品种列表失败, Err: " + tmpErr.Error()
+			return
+		}
+
+		for _, v := range chapterPermissionList {
+			chapterPermissionIdList = append(chapterPermissionIdList, v.ChartPermissionId)
+		}
+	}
+
+	resp := models.ReportChapterItemResp{
+		ReportChapterItem: *chapterItem,
+		GrandAdminIdList:  chapterGrantIdList,
+		PermissionIdList:  chapterPermissionIdList,
+	}
+
+	// 获取当前编辑状态
+	{
+		markStatus, err := services.UpdateReportEditMark(chapterItem.ReportId, chapterItem.ReportChapterId, this.SysUser.AdminId, 2, this.SysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = "查询标记状态失败"
+			br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+			return
+		}
+		if markStatus.Status == 0 {
+			resp.CanEdit = true
+		} else {
+			resp.Editor = markStatus.Editor
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ChapterMove
+// @Title 移动章节类型
+// @Description 移动章节类型
+// @Param	request	body models.PermissionMoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /chapter/move [post]
+func (this *ReportController) ChapterMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.ReportChapterMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	if req.ReportChapterId == 0 {
+		br.Msg = "请选择要移动的章节"
+		return
+	}
+	e, msg := services.MoveReportChapter(&req)
+	if e != nil {
+		br.Msg = msg
+		br.ErrMsg = "移动品种失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// EditChapterTrendTag
+// @Title 编辑章节趋势标签
+// @Description 编辑章节趋势标签
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /editChapterTrendTag [post]
+func (this *ReportController) EditChapterTrendTag() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditChapterTrendTagReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "章节ID有误"
+		return
+	}
+	if req.Trend == "" {
+		br.Msg = "请输入标签"
+		return
+	}
+
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "获取章节信息失败"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+
+	// 操作权限校验
+	{
+		// 如果不是创建人,那么就要去查看是否授权
+		if reportInfo.AdminId != sysUser.AdminId {
+			// 授权用户权限校验
+			chapterGrantObj := report.ReportChapterGrant{}
+			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(chapterInfo.ReportChapterId, sysUser.AdminId)
+			if tmpErr != nil {
+				if tmpErr.Error() == utils.ErrNoRow() {
+					br.Msg = "没有权限"
+					br.ErrMsg = "没有权限"
+					br.IsSendEmail = false
+					return
+				}
+				br.Msg = "获取章节id授权用户失败"
+				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
+				return
+			}
+		}
+	}
+
+	// 更新章节标签
+	chapterInfo.Trend = req.Trend
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "Trend")
+	if err = chapterInfo.UpdateChapter(updateCols); err != nil {
+		br.Msg = "更新标签失败"
+		br.ErrMsg = "更新标签失败, Err: " + err.Error()
+		return
+	}
+
+	// 添加关键词
+	if err = models.AddTrendTagKeyWord(req.Trend); err != nil {
+		br.Msg = "添加标签关键词失败"
+		br.ErrMsg = "添加标签关键词失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// GetSunCode 获取太阳码
+// @Title 公共模块
+// @Description 获取分享海报
+// @Param	request	body models.SunCodeReq true "type json string"
+// @Success 200 {object} string "获取成功"
+// @failure 400 {string} string "获取失败"
+// @router /getSunCode [post]
+func (this *ReportController) GetSunCode() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.SunCodeReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	var sunCodeUrl string
+	//先查,查不到再去生成上传
+	item, err := models.GetYbPcSunCode(req.CodeScene, req.CodePage)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "查询太阳码失败!"
+		br.ErrMsg = "查询太阳码失败,Err:" + err.Error()
+		return
+	}
+	if item != nil {
+		sunCodeUrl = item.SuncodeUrl
+	}
+
+	if sunCodeUrl == "" {
+		sunCodeUrl, err = services.PcCreateAndUploadSunCode(req.CodeScene, req.CodePage)
+		if err != nil {
+			br.Msg = "生成太阳码失败!"
+			br.ErrMsg = "生成太阳码失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Data = sunCodeUrl
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// VoiceUpload
+// @Title 音频上传
+// @Description 音频上传接口
+// @Param   file   query   file  true       "文件"
+// @Param   ReportChapterId   query   int  true       "报告章节ID"
+// @Success Ret=200 上传成功
+// @router /chapter/voice/upload [post]
+func (this *ReportController) VoiceUpload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	f, h, err := this.GetFile("file")
+	if err != nil {
+		br.Msg = "获取资源信息失败"
+		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 报告章节id
+	reportChapterId, err := this.GetInt("ReportChapterId", 0)
+	if err != nil {
+		br.Msg = "报告章节ID异常"
+		br.ErrMsg = "报告章节ID异常,Err:" + err.Error()
+		return
+	}
+
+	// 报告章节信息
+	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "获取报告章节信息失败"
+		br.ErrMsg = "获取报告章节信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 报告信息
+	reportInfo, err := models.GetReportByReportId(reportChapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 权限校验
+	isAuth, err := services.CheckChapterAuthByReportChapterInfo(this.SysUser.AdminId, reportInfo.AdminId, reportChapterInfo)
+	if err != nil {
+		br.Msg = "获取报告权限失败"
+		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
+		return
+	}
+	if !isAuth {
+		br.Msg = "无操作权限"
+		br.ErrMsg = "无操作权限"
+		return
+	}
+
+	ext := path.Ext(h.Filename)
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fPath := uploadDir + "/" + fileName
+	defer f.Close() //关闭上传文件
+	err = this.SaveToFile("file", fPath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	//if utils.ObjectStorageClient == "minio" {
+	//	resourceUrl, err = services.UploadAudioToMinIo(fileName, fPath)
+	//	if err != nil {
+	//		br.Msg = "文件上传失败"
+	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+	//		return
+	//	}
+	//} else {
+	//	resourceUrl, err = services.UploadAudioAliyun(fileName, fPath)
+	//	if err != nil {
+	//		br.Msg = "文件上传失败"
+	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+	ossClient := services.NewOssClient()
+	if ossClient == nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "初始化OSS服务失败"
+		return
+	}
+	resourceUrl, err = ossClient.UploadFile(fileName, fPath, "")
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	defer func() {
+		os.Remove(fPath)
+	}()
+	item := new(models.Resource)
+	item.ResourceUrl = resourceUrl
+	item.ResourceType = 2
+	item.CreateTime = time.Now()
+	newId, err := models.AddResource(item)
+	if err != nil {
+		br.Msg = "资源上传失败"
+		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+		return
+	}
+
+	var playSeconds float64
+	playSeconds, err = mp3duration.Calculate(fPath)
+	if playSeconds <= 0 {
+		playSeconds, err = utils.GetVideoPlaySeconds(fPath)
+		if err != nil {
+			br.Msg = "获取音频时间失败"
+			br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	fileBody, err := os.ReadFile(fPath)
+	videoSize := len(fileBody)
+	sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024)
+	sizeStr := utils.SubFloatToFloatStr(sizeFloat, 2)
+
+	{
+		reportChapterCreateTime, err := time.Parse(utils.FormatDateTime, reportChapterInfo.CreateTime)
+		if err != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "上传失败,Err:" + err.Error()
+			return
+		}
+		createTimeStr := reportChapterCreateTime.Format("0102")
+		videoName := reportChapterInfo.Title + "(" + createTimeStr + ")"
+
+		reportChapterInfo.VideoUrl = resourceUrl
+		reportChapterInfo.VideoName = videoName
+		reportChapterInfo.VideoPlaySeconds = fmt.Sprint(playSeconds)
+		reportChapterInfo.VideoSize = sizeStr
+		reportChapterInfo.VideoKind = 1
+		reportChapterInfo.LastModifyAdminId = this.SysUser.AdminId
+		reportChapterInfo.LastModifyAdminName = this.SysUser.RealName
+		reportChapterInfo.ModifyTime = time.Now()
+		err = reportChapterInfo.UpdateChapter([]string{"VideoUrl", "VideoName", "VideoPlaySeconds", "VideoSize", "VideoKind", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime"})
+		if err != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "修改报告章节的音频信息失败,Err:" + err.Error()
+			return
+		}
+
+		// 修改报告的最近更新人信息
+		reportInfo.LastModifyAdminId = this.SysUser.AdminId
+		reportInfo.LastModifyAdminName = this.SysUser.RealName
+		reportInfo.ModifyTime = time.Now()
+		err = reportInfo.UpdateReport([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"})
+		if err != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "修改报告最后更新人信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	// 处理报告中的音频文件分贝
+	go services.HandleVideoDecibel(reportChapterInfo)
+
+	resp := new(models.ResourceResp)
+	resp.Id = newId
+	resp.ResourceUrl = resourceUrl
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	return
+}
+
+// PublishDayWeekReportChapter
+// @Title 发布章节
+// @Description 发布章节
+// @Param	request	body models.PublishReportChapterReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /publishDayWeekReportChapter [post]
+func (this *ReportController) PublishDayWeekReportChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.PublishReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+
+	// 获取报告详情
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "章节信息有误"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	reportInfo, err := models.GetReportByReportId(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "查询报告有误"
+		br.ErrMsg = "查询报告信息失败, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 图表刷新状态
+	refreshResult := data.CheckBatchChartRefreshResult("report", chapterInfo.ReportId, chapterInfo.ReportChapterId)
+	if !refreshResult {
+		br.Msg = "图表刷新未完成,请稍后操作"
+		br.ErrMsg = "图表刷新未完成,请稍后操作"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 如果是系统章节,那么需要校验下是否已经停更
+	if chapterInfo.TypeId > 0 {
+		// 获取规则配置
+		reportChapterTypeRule, err := models.GetReportChapterTypeById(chapterInfo.TypeId)
+		if err != nil {
+			br.Msg = "获取配置信息异常"
+			br.ErrMsg = "获取配置信息异常, Err: " + err.Error()
+			return
+		}
+		if reportChapterTypeRule.Enabled == 0 {
+			br.Msg = "该章节已永久停更"
+			br.ErrMsg = "该章节已永久停更 "
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	var publishTime time.Time
+	if reportInfo.MsgIsSend == 1 && reportInfo.PublishTime.IsZero() { //如果报告曾经发布过,并且已经发送过模版消息,则章节的发布时间为报告的发布时间
+		publishTime = reportInfo.PublishTime
+	} else {
+		publishTime = time.Now()
+	}
+
+	// 更新报告的最后编辑人
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+	err = reportInfo.UpdateReport([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"})
+	if err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "报告最后编辑人保存失败, Err: " + err.Error()
+		return
+	}
+
+	// 更新章节信息
+	chapterInfo.IsEdit = 1
+	chapterInfo.PublishState = 2
+	chapterInfo.PublishTime = publishTime
+	chapterInfo.LastModifyAdminId = this.SysUser.AdminId
+	chapterInfo.LastModifyAdminName = this.SysUser.RealName
+	chapterInfo.ModifyTime = time.Now()
+
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "IsEdit", "PublishState", "PublishTime", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime")
+	err = chapterInfo.UpdateChapter(updateCols)
+	if err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+
+	// 修改报告的最近更新人信息
+	go func() {
+		reportInfo.LastModifyAdminId = this.SysUser.AdminId
+		reportInfo.LastModifyAdminName = this.SysUser.RealName
+		reportInfo.ModifyTime = time.Now()
+		err = reportInfo.UpdateReport([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"})
+		if err != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "修改报告最后更新人信息失败,Err:" + err.Error()
+			return
+		}
+	}()
+
+	// 更新章节ES
+	{
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+	}
+
+	// 同时发布报告
+	//if req.PublishReport == 1 {
+	//	if _, e := services.PublishDayWeekReport(chapterInfo.ReportId); e != nil {
+	//		br.Msg = "章节发布成功,报告发布失败,请手动发布报告"
+	//		br.ErrMsg = "发布晨/周报失败, Err: " + e.Error()
+	//		return
+	//	}
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// GetUnPublishReportChapterList
+// @Title 获取报告中未发布的章节数
+// @Description 获取报告中未发布的章节数
+// @Param	ReportId  query  int  true  "报告ID"
+// @Success 200 Ret=200 获取成功
+// @router /chapter/un_publish/list [get]
+func (this *ReportController) GetUnPublishReportChapterList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportId, _ := this.GetInt("ReportId")
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.IsSendEmail = false
+		return
+	}
+	reportInfo, err := models.GetReportById(reportId)
+	if err != nil {
+		br.Msg = "报告有误"
+		br.ErrMsg = "报告有误, Err: " + err.Error()
+		return
+	}
+
+	if reportInfo.HasChapter == 0 {
+		br.Msg = "报告未包含章节"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 获取未发布的章节列表
+	unPublishedList, err := models.GetUnPublishedChapterList(reportInfo.Id)
+	if err != nil {
+		br.Msg = "获取未发布的章节数失败"
+		br.ErrMsg = "获取未发布的章节数失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = unPublishedList
+}

+ 50 - 63
controllers/report_chapter_type.go

@@ -16,7 +16,7 @@ type ReportChapterTypeController struct {
 // List
 // @Title 报告章节列表
 // @Description 报告章节列表
-// @Param   ReportType  query  string  true  "报告类型: day-晨报; week-周报"
+// @Param   ClassifyId  query  int  true  "所属分类id"
 // @Success 200 {object} models.ReportChapterTypePageListResp
 // @router /chapter_type/list [get]
 func (this *ReportChapterTypeController) List() {
@@ -33,22 +33,27 @@ func (this *ReportChapterTypeController) List() {
 		br.Ret = 408
 		return
 	}
-	reportType := this.GetString("ReportType")
-	typeArr := []string{utils.REPORT_TYPE_DAY, utils.REPORT_TYPE_WEEK}
-	if !utils.InArrayByStr(typeArr, reportType) {
-		br.Msg = "请选择报告类型"
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	if classifyId <= 0 {
+		br.Msg = "请选择分类"
 		return
 	}
-	cond := ` AND research_type = ?`
+
+	cond := ` AND report_classify_id = ?`
 	pars := make([]interface{}, 0)
-	pars = append(pars, reportType)
+	pars = append(pars, classifyId)
 	list, e := models.GetReportChapterTypePageList(cond, pars)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取报告章节列表失败, Err: " + e.Error()
 		return
 	}
-	mappingList, e := models.GetChapterTypePermissionByResearchType(reportType)
+	chapterTypeIdList := make([]int, 0)
+	for _, v := range list {
+		chapterTypeIdList = append(chapterTypeIdList, v.ReportChapterTypeId)
+	}
+
+	mappingList, e := models.GetChapterTypePermissionByChapterTypeIdList(chapterTypeIdList)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取章节类型权限列表失败, Err: " + e.Error()
@@ -116,15 +121,16 @@ func (this *ReportChapterTypeController) Add() {
 		br.Msg = "请输入章节名称"
 		return
 	}
-	typeArr := []string{utils.REPORT_TYPE_DAY, utils.REPORT_TYPE_WEEK}
-	if !utils.InArrayByStr(typeArr, req.ResearchType) {
-		br.Msg = "请选择报告类型"
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
 		return
 	}
+
 	// 重名校验
-	cond := ` AND report_chapter_type_name = ? AND research_type = ?`
+	cond := ` AND report_chapter_type_name = ? AND report_classify_id = ?`
 	pars := make([]interface{}, 0)
-	pars = append(pars, req.ReportChapterTypeName, req.ResearchType)
+	pars = append(pars, req.ReportChapterTypeName, req.ClassifyId)
 	exists, e := models.GetReportChapterTypeByCondition(cond, pars)
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		br.Msg = "操作失败"
@@ -149,7 +155,9 @@ func (this *ReportChapterTypeController) Add() {
 	item.Enabled = 1
 	item.CreatedTime = nowTime
 	item.LastUpdatedTime = nowTime
-	item.ResearchType = req.ResearchType
+	//item.ResearchType = req.ResearchType
+	item.ResearchType = "chapter"
+	item.ReportClassifyId = req.ClassifyId
 	item.IsSet = 0
 	item.ReportChapterTypeKey = req.ReportChapterTypeName
 	item.TickerTitle = req.ReportChapterTypeName
@@ -160,7 +168,6 @@ func (this *ReportChapterTypeController) Add() {
 		return
 	}
 
-	// todo 更新章节权限
 	cond = ` and product_id=1`
 	pars = make([]interface{}, 0)
 	permissionList, e := services.GetChartPermissionList(cond, pars)
@@ -174,29 +181,20 @@ func (this *ReportChapterTypeController) Add() {
 		permissionIdName[permissionList[i].ChartPermissionId] = permissionList[i].PermissionName
 	}
 
-	researchType := item.ResearchType
-	newPermissions := make([]*models.ReportChapterTypePermission, 0)       // 报告章节权限表(新)
-	newWeekPermissions := make([]*models.ChartPermissionChapterMapping, 0) // 报告章节权限表(老)
+	newPermissions := make([]*models.ReportChapterTypePermission, 0) // 报告章节权限表(新)
 	for i := range req.ChartPermissionIdList {
 		newPermissions = append(newPermissions, &models.ReportChapterTypePermission{
 			ReportChapterTypeId:   item.ReportChapterTypeId,
 			ReportChapterTypeName: item.ReportChapterTypeName,
 			ChartPermissionId:     req.ChartPermissionIdList[i],
 			PermissionName:        permissionIdName[req.ChartPermissionIdList[i]],
-			ResearchType:          researchType,
+			ResearchType:          item.ResearchType,
 			CreatedTime:           nowTime,
 		})
-		if researchType == utils.REPORT_TYPE_WEEK {
-			newWeekPermissions = append(newWeekPermissions, &models.ChartPermissionChapterMapping{
-				ChartPermissionId:   req.ChartPermissionIdList[i],
-				ReportChapterTypeId: item.ReportChapterTypeId,
-				ResearchType:        researchType,
-			})
-		}
 	}
 
 	// 设置权限
-	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, researchType, newPermissions, newWeekPermissions)
+	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, newPermissions)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "设置章节类型权限失败, Err: " + e.Error()
@@ -208,10 +206,9 @@ func (this *ReportChapterTypeController) Add() {
 		_ = utils.Rc.Delete(key)
 	}
 
-	//todo 同步更新crm章节权限和章节类型
 	go func() {
 		var syncReq services.ChapterTypeSyncReq
-		syncReq.ResearchType = researchType
+		syncReq.ResearchType = item.ResearchType
 		syncReq.ReportChapterTypeId = item.ReportChapterTypeId
 		_, _ = services.ReportChapterTypeSync(&syncReq)
 	}()
@@ -254,15 +251,16 @@ func (this *ReportChapterTypeController) Edit() {
 		br.Msg = "请输入章节名称"
 		return
 	}
-	typeArr := []string{utils.REPORT_TYPE_DAY, utils.REPORT_TYPE_WEEK}
-	if !utils.InArrayByStr(typeArr, req.ResearchType) {
-		br.Msg = "请选择报告类型"
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
 		return
 	}
+
 	// 重名校验
-	cond := ` AND report_chapter_type_name = ? AND research_type = ?`
+	cond := ` AND report_chapter_type_name = ? AND report_classify_id = ?`
 	pars := make([]interface{}, 0)
-	pars = append(pars, req.ReportChapterTypeName, req.ResearchType)
+	pars = append(pars, req.ReportChapterTypeName, req.ClassifyId)
 	exists, e := models.GetReportChapterTypeByCondition(cond, pars)
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		br.Msg = "操作失败"
@@ -281,8 +279,15 @@ func (this *ReportChapterTypeController) Edit() {
 		return
 	}
 	originName := item.ReportChapterTypeName
-	//更新章节权限
-	// todo 更新章节权限
+	if item.ReportChapterTypeName != req.ReportChapterTypeName {
+		item.ReportChapterTypeName = req.ReportChapterTypeName
+		err := item.Update([]string{"ReportChapterTypeName"})
+		if err != nil {
+			br.Msg = "修改失败"
+			br.ErrMsg = "修改章节类型失败, Err: " + err.Error()
+			return
+		}
+	}
 	cond = ` and product_id=1`
 	pars = make([]interface{}, 0)
 	permissionList, e := services.GetChartPermissionList(cond, pars)
@@ -297,29 +302,20 @@ func (this *ReportChapterTypeController) Edit() {
 		permissionIdName[permissionList[i].ChartPermissionId] = permissionList[i].PermissionName
 	}
 
-	researchType := item.ResearchType
-	newPermissions := make([]*models.ReportChapterTypePermission, 0)       // 报告章节权限表(新)
-	newWeekPermissions := make([]*models.ChartPermissionChapterMapping, 0) // 报告章节权限表(老)
+	newPermissions := make([]*models.ReportChapterTypePermission, 0) // 报告章节权限表(新)
 	for i := range req.ChartPermissionIdList {
 		newPermissions = append(newPermissions, &models.ReportChapterTypePermission{
 			ReportChapterTypeId:   item.ReportChapterTypeId,
 			ReportChapterTypeName: item.ReportChapterTypeName,
 			ChartPermissionId:     req.ChartPermissionIdList[i],
 			PermissionName:        permissionIdName[req.ChartPermissionIdList[i]],
-			ResearchType:          researchType,
+			ResearchType:          item.ResearchType,
 			CreatedTime:           nowTime,
 		})
-		if researchType == utils.REPORT_TYPE_WEEK {
-			newWeekPermissions = append(newWeekPermissions, &models.ChartPermissionChapterMapping{
-				ChartPermissionId:   req.ChartPermissionIdList[i],
-				ReportChapterTypeId: item.ReportChapterTypeId,
-				ResearchType:        researchType,
-			})
-		}
 	}
 
 	// 设置权限
-	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, researchType, newPermissions, newWeekPermissions)
+	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, newPermissions)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "设置章节类型权限失败, Err: " + e.Error()
@@ -340,7 +336,7 @@ func (this *ReportChapterTypeController) Edit() {
 
 	go func() {
 		var syncReq services.ChapterTypeSyncReq
-		syncReq.ResearchType = researchType
+		syncReq.ResearchType = item.ResearchType
 		syncReq.ReportChapterTypeId = item.ReportChapterTypeId
 		_, _ = services.ReportChapterTypeSync(&syncReq)
 	}()
@@ -474,30 +470,21 @@ func (this *ReportChapterTypeController) AuthSetting() {
 		permissionIdName[permissionList[i].ChartPermissionId] = permissionList[i].PermissionName
 	}
 
-	researchType := item.ResearchType
 	nowTime := time.Now().Local()
-	newPermissions := make([]*models.ReportChapterTypePermission, 0)       // 报告章节权限表(新)
-	newWeekPermissions := make([]*models.ChartPermissionChapterMapping, 0) // 报告章节权限表(老)
+	newPermissions := make([]*models.ReportChapterTypePermission, 0) // 报告章节权限表(新)
 	for i := range req.ChartPermissionIdList {
 		newPermissions = append(newPermissions, &models.ReportChapterTypePermission{
 			ReportChapterTypeId:   item.ReportChapterTypeId,
 			ReportChapterTypeName: item.ReportChapterTypeName,
 			ChartPermissionId:     req.ChartPermissionIdList[i],
 			PermissionName:        permissionIdName[req.ChartPermissionIdList[i]],
-			ResearchType:          researchType,
-			CreatedTime:           nowTime,
+			//ResearchType:          researchType,
+			CreatedTime: nowTime,
 		})
-		if researchType == utils.REPORT_TYPE_WEEK {
-			newWeekPermissions = append(newWeekPermissions, &models.ChartPermissionChapterMapping{
-				ChartPermissionId:   req.ChartPermissionIdList[i],
-				ReportChapterTypeId: item.ReportChapterTypeId,
-				ResearchType:        researchType,
-			})
-		}
 	}
 
 	// 设置权限
-	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, researchType, newPermissions, newWeekPermissions)
+	e = models.SetReportChapterTypePermission(item.ReportChapterTypeId, newPermissions)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "设置章节类型权限失败, Err: " + e.Error()
@@ -546,7 +533,7 @@ func (this *ReportChapterTypeController) PermissionList() {
 		br.Msg = "章节不存在或已被删除"
 		return
 	}
-	list, e := models.GetChapterTypePermissionByTypeIdAndResearchType(typeId, item.ResearchType)
+	list, e := models.GetChapterTypePermissionByReportChapterTypeId(typeId)
 	if e != nil {
 		br.Msg = "章节不存在或已被删除"
 		br.ErrMsg = "获取章节类型权限列表失败, Err: " + e.Error()

+ 1902 - 0
controllers/report_v2.go

@@ -0,0 +1,1902 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/models/smart_report"
+	"eta/eta_mobile/models/system"
+	"eta/eta_mobile/services"
+	"eta/eta_mobile/services/alarm_msg"
+	"eta/eta_mobile/services/data"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ListReport
+// @Title 获取报告列表接口
+// @Description 获取报告列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间);approve_time(审批时间)"
+// @Param   StartDate   query   string  true       "开始时间"
+// @Param   EndDate   query   string  true       "结束时间"
+// @Param   Frequency   query   string  true       "频度"
+// @Param   ClassifyIdFirst   query   int  true       "一级分类id"
+// @Param   ClassifyIdSecond   query   int  true       "二级分类id"
+// @Param   ClassifyIdThird   query   int  true       "三级分类id"
+// @Param   State   query   int  true       "状态"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   PublishSort   query   string  true       "desc:降序,asc 升序(预留)"
+// @Param   FilterReportType   query   string  true       "筛选报告类型,1:公共研报,2:共享研报,3:我的研报"
+// @Success 200 {object} models.ReportListResp
+// @router /list [get]
+func (this *ReportController) ListReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	timeType := this.GetString("TimeType")
+	startDate := this.GetString("StartDate")
+	endDate := this.GetString("EndDate")
+	frequency := this.GetString("Frequency")
+	classifyIdFirst, _ := this.GetInt("ClassifyIdFirst", 0)
+	classifyIdSecond, _ := this.GetInt("ClassifyIdSecond", 0)
+	classifyIdThird, _ := this.GetInt("ClassifyIdThird", 0)
+	state, _ := this.GetInt("State")
+	keyWord := this.GetString("KeyWord")
+	msgIsSend, _ := this.GetInt("MsgIsSend")
+	filterReportType, _ := this.GetInt("FilterReportType", 1)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	if timeType == "" {
+		timeType = "publish_time"
+	}
+	if timeType != "publish_time" && timeType != "modify_time" && timeType != "approve_time" {
+		br.Msg = "请选择正确的时间"
+		br.ErrMsg = "请选择正确的时间"
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (a.title LIKE ? OR a.admin_real_name LIKE ? ) `
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
+	}
+	if startDate != "" {
+		condition += ` AND a.` + timeType + ` >= ? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		condition += ` AND a.` + timeType + ` <= ? `
+		pars = append(pars, endDate)
+	}
+	if frequency != "" {
+		condition += ` AND a.frequency = ? `
+		pars = append(pars, frequency)
+	}
+
+	if classifyIdFirst > 0 {
+		condition += ` AND a.classify_id_first = ? `
+		pars = append(pars, classifyIdFirst)
+	}
+	if classifyIdSecond > 0 {
+		condition += ` AND a.classify_id_second = ? `
+		pars = append(pars, classifyIdSecond)
+	}
+	if classifyIdThird > 0 {
+		condition += ` AND a.classify_id_third = ? `
+		pars = append(pars, classifyIdSecond)
+	}
+
+	if state > 0 {
+		condition += ` AND a.state = ? `
+		pars = append(pars, state)
+	}
+	// 消息是否已推送 1-未推送; 2-已推送
+	if msgIsSend == 1 {
+		condition += ` AND (a.msg_is_send = 0 OR a.ths_msg_is_send = 0) `
+	}
+	if msgIsSend == 2 {
+		condition += ` AND a.msg_is_send = 1 AND a.ths_msg_is_send = 1 `
+	}
+
+	var err error
+	var total int
+	var list []*models.ReportList
+
+	switch filterReportType {
+	// 筛选报告类型,1:公共研报,2:共享研报,3:我的研报
+	case 1:
+		condition += ` AND a.is_public_publish = ? `
+		pars = append(pars, 1)
+		condition += `  AND a.state in (2,6) `
+
+	case 3:
+		condition += ` AND a.admin_id = ? `
+		pars = append(pars, this.SysUser.AdminId)
+	case 2:
+		condition += ` AND (a.admin_id = ? or b.admin_id = ?) `
+		pars = append(pars, this.SysUser.AdminId, this.SysUser.AdminId)
+	}
+
+	// 共享报告需要连表查询,所以需要单独写
+	if filterReportType == 2 {
+		total, err = models.GetReportListCountByGrant(condition, pars)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		list, err = models.GetReportListByGrant(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		total, err = models.GetReportListCountV1(condition, pars)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		list, err = models.GetReportListV1(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	listLen := len(list)
+	if listLen > 0 {
+		pvMap := make(map[int]int)
+		uvMap := make(map[int]int)
+		reportIdArr := make([]string, 0)
+		syncReportIdArr := make([]string, 0)      // 同步过来的报告IDs
+		oldAndNewReportIdMap := make(map[int]int) // 旧报告和新报告的id对应关系
+		for i := 0; i < listLen; i++ {
+			reportIdArr = append(reportIdArr, strconv.Itoa(list[i].Id))
+			if list[i].OldReportId > 0 && list[i].ReportLayout == 1 {
+				syncReportIdArr = append(syncReportIdArr, strconv.Itoa(list[i].OldReportId))
+				oldAndNewReportIdMap[list[i].OldReportId] = list[i].Id
+			}
+			pvMap[list[i].Id] = list[i].Pv
+			uvMap[list[i].Id] = list[i].Uv
+		}
+
+		// 当下报告的pv,uv
+		if len(reportIdArr) > 0 {
+			pvList, e := models.GetReportPvUvByReportIdList(reportIdArr)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取同步报告对应的PV、UV失败, Err: " + e.Error()
+				return
+			}
+			for _, v := range pvList {
+				pv := pvMap[v.ReportId]
+				uv := uvMap[v.ReportId]
+				pvMap[v.ReportId] = v.PvTotal + pv
+				uvMap[v.ReportId] = v.UvTotal + uv
+			}
+		}
+
+		//reportIds := strings.Join(reportIdArr, ",")
+		//syncReportIds := strings.Join(syncReportIdArr, ",")
+
+		// 查询同步过来的报告对应的老报告PV+UV
+		if len(syncReportIdArr) > 0 {
+			puvList, e := models.GetPUVByResearchReportIds(syncReportIdArr)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取同步报告对应的PV、UV失败, Err: " + e.Error()
+				return
+			}
+			puvLen := len(puvList)
+			for i := 0; i < puvLen; i++ {
+				newReportId, ok := oldAndNewReportIdMap[puvList[i].ResearchReportId]
+				if ok {
+					pv := pvMap[newReportId]
+					uv := uvMap[newReportId]
+					pvMap[newReportId] = puvList[i].Pv + pv
+					uvMap[newReportId] = puvList[i].Uv + uv
+				}
+			}
+		}
+		// 晨周报音频列表
+		videoList, err := models.GetReportChapterVideoListByReportIds(reportIdArr)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取报告音频文件失败,Err:" + err.Error()
+			return
+		}
+		for i := 0; i < listLen; i++ {
+			list[i].Content = html.UnescapeString(list[i].Content)
+			list[i].ContentSub = html.UnescapeString(list[i].ContentSub)
+			// 除周报外其余报告均可推送客群
+			list[i].NeedThsMsg = 1
+			//if list[i].HasChapter == 1 && list[i].ChapterType == utils.REPORT_TYPE_WEEK {
+			//	list[i].NeedThsMsg = 0
+			//}
+			chapterList := make([]*models.ReportChapterVideoList, 0)
+			for ii := 0; ii < len(videoList); ii++ {
+				if list[i].Id == videoList[ii].ReportId {
+					chapterList = append(chapterList, videoList[ii])
+				}
+			}
+			list[i].ChapterVideoList = chapterList
+			list[i].Pv += pvMap[list[i].Id]
+			list[i].Uv += uvMap[list[i].Id]
+		}
+	}
+
+	for _, item := range list {
+		/*key := fmt.Sprint(`crm:report:edit:`, item.Id)
+		opUserId, _ := utils.Rc.RedisInt(key)
+		//如果当前没有人操作,获取当前操作人是本人,那么编辑按钮可用
+		if opUserId <= 0 || (opUserId == this.SysUser.AdminId) || item.ClassifyNameFirst == "周报" || item.ClassifyNameFirst == "晨报" {
+			item.CanEdit = true
+		} else {
+			adminInfo, errAdmin := system.GetSysUserById(opUserId)
+			if errAdmin != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + errAdmin.Error()
+				return
+			}
+			item.Editor = adminInfo.RealName
+		}*/
+		if item.HasChapter == 1 {
+			item.CanEdit = true
+			continue
+		}
+		markStatus, err := services.UpdateReportEditMark(item.Id, 0, this.SysUser.AdminId, 2, this.SysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = "查询标记状态失败"
+			br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+			return
+		}
+		if markStatus.Status == 0 {
+			item.CanEdit = true
+		} else {
+			item.Editor = markStatus.Editor
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.ReportListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Add
+// @Title 新增报告接口
+// @Description 新增报告(不区分报告类型)
+// @Param	request	body models.AddReq true "type json string"
+// @Success 200 {object} models.AddResp
+// @router /add [post]
+func (this *ReportController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.AddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.Title == `` {
+		br.Msg = "标题不能为空"
+		br.ErrMsg = "标题不能为空"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ClassifyIdFirst <= 0 {
+		br.Msg = "分类必填"
+		br.ErrMsg = "分类必填"
+		br.IsSendEmail = false
+		return
+	}
+
+	var contentSub string
+	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
+		content, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = content
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}
+
+	// 报告期数
+	maxStage, err := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird)
+	if err != nil {
+		br.Msg = "期数获取失败!"
+		br.ErrMsg = "期数获取失败,Err:" + err.Error()
+		return
+	}
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird, models.ReportOperateAdd)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 协作方式,1:个人,2:多人协作。默认:1
+	if req.CollaborateType == 0 {
+		req.CollaborateType = 1
+	}
+	// 报告布局,1:常规布局,2:智能布局。默认:1
+	if req.ReportLayout == 0 {
+		req.ReportLayout = 1
+	}
+	// 是否公开发布,1:是,2:否
+	if req.IsPublicPublish == 0 {
+		req.IsPublicPublish = 1
+	}
+
+	classifyItemList, err := models.GetClassifyListByIdList([]int{req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird})
+	classifyMap := make(map[int]string)
+	for _, v := range classifyItemList {
+		classifyMap[v.Id] = v.ClassifyName
+	}
+
+	item := new(models.Report)
+	item.AddType = req.AddType
+	item.ReportVersion = 2
+	item.ClassifyIdFirst = req.ClassifyIdFirst
+	item.ClassifyNameFirst = classifyMap[req.ClassifyIdFirst]
+	item.ClassifyIdSecond = req.ClassifyIdSecond
+	item.ClassifyNameSecond = classifyMap[req.ClassifyIdSecond]
+	item.Title = req.Title
+	item.Abstract = req.Abstract
+	item.Author = req.Author
+	item.Frequency = req.Frequency
+	item.State = state
+	item.Content = html.EscapeString(req.Content)
+	item.Stage = maxStage + 1
+	item.ContentSub = html.EscapeString(contentSub)
+	item.CreateTime = req.CreateTime
+	item.ModifyTime = time.Now()
+	item.ReportVersion = req.ReportVersion
+	item.AdminId = sysUser.AdminId
+	item.AdminRealName = sysUser.RealName
+
+	item.ClassifyIdThird = req.ClassifyIdThird
+	item.ClassifyNameThird = classifyMap[req.ClassifyIdThird]
+
+	// 产品要求,如果是多人协作,那么就是章节类型的报告
+	if req.CollaborateType == 2 {
+		item.HasChapter = 1
+		item.ChapterType = ""
+	}
+	item.LastModifyAdminId = sysUser.AdminId
+	item.LastModifyAdminName = sysUser.RealName
+	item.ContentModifyTime = time.Now()
+	item.NeedSplice = 1
+	item.ContentStruct = html.EscapeString(req.ContentStruct)
+	item.HeadImg = req.HeadImg
+	item.EndImg = req.EndImg
+	item.CanvasColor = req.CanvasColor
+	item.HeadResourceId = req.HeadResourceId
+	item.EndResourceId = req.EndResourceId
+	item.CollaborateType = req.CollaborateType
+	item.ReportLayout = req.ReportLayout
+	item.IsPublicPublish = req.IsPublicPublish
+	item.ReportCreateTime = time.Now()
+
+	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	recordItem := &models.ReportStateRecord{
+		ReportId:   item.Id,
+		ReportType: 1,
+		State:      1,
+		AdminId:    this.SysUser.AdminId,
+		AdminName:  this.SysUser.AdminName,
+		CreateTime: time.Now(),
+	}
+	go func() {
+		_, _ = models.AddReportStateRecord(recordItem)
+	}()
+
+	resp := new(models.AddResp)
+	resp.ReportId = int64(item.Id)
+	resp.ReportCode = item.ReportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// Edit
+// @Title 编辑报告基础信息接口
+// @Description 编辑报告基础信息(不区分报告类型)
+// @Param	request	body models.EditReq true "type json string"
+// @Success 200 {object} models.EditResp
+// @router /edit [post]
+func (this *ReportController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	//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()
+		return
+	}
+	if markStatus.Status == 1 {
+		br.Msg = markStatus.Msg
+		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		return
+	}
+
+	reportInfo, e := models.GetReportByReportId(int(req.ReportId))
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	if reportInfo.State == models.ReportStatePublished || reportInfo.State == models.ReportStatePass {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 编辑报告信息
+	err, errMsg := services.EditReport(reportInfo, req, sysUser)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	reportCode := utils.MD5(strconv.Itoa(int(req.ReportId)))
+	resp := new(models.EditResp)
+	resp.ReportId = req.ReportId
+	resp.ReportCode = reportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// Detail
+// @Title 获取报告详情接口
+// @Description 获取报告详情
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /detail [get]
+func (this *ReportController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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
+	}*/
+	reportId, err := this.GetInt("ReportId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if reportId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	item, err := models.GetReportById(reportId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	chapterList := make([]*models.ReportChapter, 0)
+	if item.HasChapter == 1 {
+		// 获取章节内容
+		tmpChapterList, err := models.GetPublishedChapterListByReportId(item.Id)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取晨/周报章节列表失败, Err: " + err.Error()
+			return
+		}
+
+		if len(tmpChapterList) > 0 {
+			// 章节类型的字段赋值
+			for _, item := range tmpChapterList {
+				item.Content = html.UnescapeString(item.Content)
+				item.ContentSub = html.UnescapeString(item.ContentSub)
+				chapterList = append(chapterList, item)
+			}
+		}
+
+		//item.Abstract = item.Title
+	}
+	item.Content = html.UnescapeString(item.Content)
+	item.ContentSub = html.UnescapeString(item.ContentSub)
+	item.ContentStruct = html.UnescapeString(item.ContentStruct)
+
+	if item.HeadResourceId > 0 {
+		headResource, err := smart_report.GetResourceItemById(item.HeadResourceId)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+			return
+		}
+		item.HeadImg = headResource.ImgUrl
+		item.HeadStyle = headResource.Style
+	}
+
+	if item.EndResourceId > 0 {
+		endResource, err := smart_report.GetResourceItemById(item.EndResourceId)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+			return
+		}
+		item.EndImg = endResource.ImgUrl
+		item.EndStyle = endResource.Style
+	}
+
+	resp := &models.ReportDetailView{
+		ReportDetail: item,
+		ChapterList:  chapterList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// SaveReportContent
+// @Title 保存草稿
+// @Description 保存草稿
+// @Param	request	body models.SaveReportContent true "type json string"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /saveReportContent [post]
+func (this *ReportController) SaveReportContent() {
+	br := new(models.BaseResponse).Init()
+	br.IsSendEmail = false
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.SaveReportContent
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	noChangeFlag := req.NoChange
+
+	if reportId <= 0 {
+		resp := new(models.SaveReportContentResp)
+		resp.ReportId = reportId
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "保存成功"
+		br.Data = resp
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, _ := models.GetReportByReportId(req.ReportId)
+	if reportInfo != nil && reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 标记更新中
+	{
+		markStatus, err := services.UpdateReportEditMark(req.ReportId, 0, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+		if markStatus.Status == 1 {
+			br.Msg = markStatus.Msg
+			return
+		}
+	}
+
+	// 内容有过修改的话,那么逻辑处理
+	if noChangeFlag != 1 {
+		content := req.Content
+		if content == "" {
+			content = this.GetString("Content")
+		}
+		if content != "" {
+			e := utils.ContentXssCheck(req.Content)
+			if e != nil {
+				br.Msg = "存在非法标签"
+				br.ErrMsg = "存在非法标签, Err: " + e.Error()
+				return
+			}
+			contentClean, e := services.FilterReportContentBr(req.Content)
+			if e != nil {
+				br.Msg = "内容去除前后空格失败"
+				br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+				return
+			}
+			content = contentClean
+
+			contentSub, err := services.GetReportContentSub(content)
+			if err != nil {
+				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)
+			reportInfo.HeadImg = req.HeadImg
+			reportInfo.EndImg = req.EndImg
+			reportInfo.CanvasColor = req.CanvasColor
+			reportInfo.HeadResourceId = req.HeadResourceId
+			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
+			}
+			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)
+	resp.ReportId = reportId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// AuthorizedListReport
+// @Title 获取有权限的报告列表接口
+// @Description 获取有权限的报告列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   ClassifyIdFirst   query   int  true       "一级分类id"
+// @Param   ClassifyIdSecond   query   int  true       "二级分类id"
+// @Param   ClassifyIdThird   query   int  true       "三级分类id"
+// @Success 200 {object} models.ReportListResp
+// @router /list/authorized [get]
+func (this *ReportController) AuthorizedListReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+	classifyIdFirst, _ := this.GetInt("ClassifyIdFirst", 0)
+	classifyIdSecond, _ := this.GetInt("ClassifyIdSecond", 0)
+	classifyIdThird, _ := this.GetInt("ClassifyIdThird", 0)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+	var list []*models.ReportList
+
+	// 没有输入信息,那就不展示
+	if keyword == `` && classifyIdFirst <= 0 {
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp := new(models.ReportListResp)
+		resp.Paging = page
+		resp.List = list
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 当前用户有权限的报告id列表
+	grantReportIdList := make([]int, 0)
+	{
+		obj := report.ReportGrant{}
+		grantList, err := obj.GetGrantListByAdminId(this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取授权报告id失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range grantList {
+			grantReportIdList = append(grantReportIdList, v.ReportId)
+		}
+	}
+
+	var condition string
+	var pars []interface{}
+
+	if classifyIdFirst > 0 {
+		condition += ` AND a.classify_id_first = ? `
+		pars = append(pars, classifyIdFirst)
+	}
+	if classifyIdSecond > 0 {
+		condition += ` AND a.classify_id_second = ? `
+		pars = append(pars, classifyIdSecond)
+	}
+	if classifyIdThird > 0 {
+		condition += ` AND a.classify_id_third = ? `
+		pars = append(pars, classifyIdSecond)
+	}
+
+	if keyword != `` {
+		condition += ` AND a.title LIKE ? `
+		pars = utils.GetLikeKeywordPars(pars, keyword, 1)
+	}
+
+	var err error
+	var total int
+
+	orCondition := `AND ( (a.is_public_publish = ? AND a.state in (2,6)) or a.admin_id = ? `
+	pars = append(pars, 1, this.SysUser.AdminId)
+
+	// 当前用户有权限的报告id列表
+	num := len(grantReportIdList)
+	if num > 0 {
+		orCondition += ` OR a.id in (` + utils.GetOrmInReplace(num) + `)`
+		pars = append(pars, grantReportIdList)
+	}
+	orCondition += ` ) `
+
+	condition += orCondition
+
+	total, err = models.GetReportListCountByAuthorized(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	list, err = models.GetReportListByAuthorized(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	//for _, item := range list {
+	//	if item.HasChapter == 1 {
+	//		item.CanEdit = true
+	//	} else {
+	//		markStatus, err := services.UpdateReportEditMark(item.Id, this.SysUser.AdminId, 2, this.SysUser.RealName, this.Lang)
+	//		if err != nil {
+	//			br.Msg = "查询标记状态失败"
+	//			br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+	//			return
+	//		}
+	//		if markStatus.Status == 0 {
+	//			item.CanEdit = true
+	//		} else {
+	//			item.Editor = markStatus.Editor
+	//		}
+	//	}
+	//}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.ReportListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// BaseDetail
+// @Title 获取报告基础信息详情接口
+// @Description 获取报告基础信息详情接口
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /detail/base [get]
+func (this *ReportController) BaseDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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
+	}*/
+	reportId, err := this.GetInt("ReportId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if reportId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	reportInfo, err := models.GetReportById(reportId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	// 基础信息就不获取章节信息了
+	chapterList := make([]*models.ReportChapter, 0)
+
+	reportInfo.Content = html.UnescapeString(reportInfo.Content)
+	reportInfo.ContentSub = html.UnescapeString(reportInfo.ContentSub)
+
+	grandAdminList := make([]models.ReportDetailViewAdmin, 0)
+	permissionList := make([]models.ReportDetailViewPermission, 0)
+
+	// 处理报告授权用户列表
+	{
+		obj := report.ReportGrant{}
+		grantList, tmpErr := obj.GetGrantListById(reportId)
+		if tmpErr != nil {
+			br.Msg = "获取章节id授权用户列表失败"
+			br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+			return
+		}
+
+		if len(grantList) > 0 {
+			grandAdminIdList := make([]int, 0)
+			for _, v := range grantList {
+				grandAdminIdList = append(grandAdminIdList, v.AdminId)
+			}
+			adminList, tmpErr := system.GetAdminListByIdList(grandAdminIdList)
+			if tmpErr != nil {
+				br.Msg = "获取章节id授权用户列表失败"
+				br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+				return
+			}
+			for _, v := range adminList {
+				grandAdminList = append(grandAdminList, models.ReportDetailViewAdmin{
+					AdminId:   v.AdminId,
+					AdminName: v.RealName,
+				})
+			}
+		}
+
+	}
+
+	// 处理章节id关联品种id列表
+	{
+		minClassifyId := reportInfo.ClassifyIdThird
+		if minClassifyId <= 0 {
+			minClassifyId = reportInfo.ClassifyIdSecond
+		}
+		if minClassifyId <= 0 {
+			minClassifyId = reportInfo.ClassifyIdFirst
+		}
+		if minClassifyId <= 0 {
+			br.Msg = "分类异常"
+			br.ErrMsg = "分类异常"
+			return
+		}
+
+		// 获取分类关联的品种id
+		classifyPermissionList, tmpErr := models.GetPermission(minClassifyId)
+		if tmpErr != nil {
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取失败,Err:" + tmpErr.Error()
+			return
+		}
+
+		if len(classifyPermissionList) > 0 {
+			permissionIdList := make([]int, 0)
+			for _, v := range classifyPermissionList {
+				permissionIdList = append(permissionIdList, v.ChartPermissionId)
+			}
+			adminList, tmpErr := models.GetChartPermissionByIdList(permissionIdList)
+			if tmpErr != nil {
+				br.Msg = "获取章节id授权用户列表失败"
+				br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+				return
+			}
+			for _, v := range adminList {
+				permissionList = append(permissionList, models.ReportDetailViewPermission{
+					PermissionId:   v.ChartPermissionId,
+					PermissionName: v.PermissionName,
+				})
+			}
+		}
+
+	}
+
+	resp := &models.ReportDetailView{
+		ReportDetail:   reportInfo,
+		ChapterList:    chapterList,
+		GrandAdminList: grandAdminList,
+		PermissionList: permissionList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EditLayoutImg
+// @Title 版图设置接口
+// @Description 版图设置接口
+// @Param	request	body models.EditLayoutImgReq true "type json string"
+// @Success 200 {object} models.EditResp
+// @router /layout_img/edit [post]
+func (this *ReportController) EditLayoutImg() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditLayoutImgReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	//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()
+		return
+	}
+	if markStatus.Status == 1 {
+		br.Msg = markStatus.Msg
+		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		return
+	}
+
+	reportInfo, e := models.GetReportByReportId(int(req.ReportId))
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	if reportInfo.State == models.ReportStatePublished || reportInfo.State == models.ReportStatePass {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+
+	// 编辑报告信息
+	err, errMsg := services.EditReportLayoutImg(reportInfo, req, sysUser)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	reportCode := utils.MD5(strconv.Itoa(int(req.ReportId)))
+	resp := new(models.EditResp)
+	resp.ReportId = req.ReportId
+	resp.ReportCode = reportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// PublishReport
+// @Title 发布报告接口
+// @Description 发布报告
+// @Param	request	body models.PublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /publish [post]
+func (this *ReportController) PublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportIds := req.ReportIds
+	if reportIds == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+
+	// 这里实际上不会批量发布了...
+	reportArr := strings.Split(reportIds, ",")
+	tips := ""
+	for _, v := range reportArr {
+		vint, err := strconv.Atoi(v)
+		if err != nil {
+			br.Msg = "参数错误"
+			br.ErrMsg = "参数错误,Err:" + err.Error()
+			return
+		}
+
+		// 报告的图表刷新状态校验
+		refreshResult := data.CheckBatchChartRefreshResult("report", vint, 0)
+		if !refreshResult {
+			br.Msg = "图表刷新未完成,请稍后操作"
+			br.ErrMsg = "图表刷新未完成,请稍后操作"
+			br.IsSendEmail = false
+			return
+		}
+
+		// 报告发布
+		tmpTips, err, errMsg := services.PublishReport(vint, req.ReportUrl, this.SysUser)
+		if err != nil {
+			br.Msg = errMsg
+			br.ErrMsg = "报告发布失败,Err:" + err.Error()
+			return
+		}
+		tips = tmpTips
+	}
+	// 发布晨周报部分章节未发布的提示
+	if tips != "" {
+		br.Data = tips
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "发布成功"
+}
+
+// PublishCancelReport
+// @Title 取消发布报告接口
+// @Description 取消发布报告
+// @Param	request	body models.PublishCancelReq true "type json string"
+// @Success 200 Ret=200 取消发布成功
+// @router /publish/cancle [post]
+func (this *ReportController) PublishCancelReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PublishCancelReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportIds <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	publishTimeNullFlag := true
+	reportInfo, err := models.GetReportById(req.ReportIds)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if reportInfo.MsgIsSend == 1 {
+		publishTimeNullFlag = false
+	}
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, reportInfo.ClassifyIdThird, models.ReportOperateCancelPublish)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
+	err = models.PublishCancelReport(req.ReportIds, state, publishTimeNullFlag, this.SysUser.AdminId, this.SysUser.RealName)
+	if err != nil {
+		br.Msg = "取消发布失败"
+		br.ErrMsg = "取消发布失败,Err:" + err.Error()
+		return
+	}
+	// 更新ES禁用
+	{
+		go services.UpdateReportEs(req.ReportIds, 1)
+	}
+
+	//// 获取审批流设置
+	//confKey := "approval_flow"
+	//confTmp, e := company.GetConfigDetailByCode(confKey)
+	//if e != nil {
+	//	br.Msg = "获取审批流配置失败"
+	//	br.ErrMsg = "获取审批流配置失败, Err: " + e.Error()
+	//	return
+	//}
+	//if confTmp.ConfigValue == "1" || confTmp.ConfigValue == "2" || confTmp.ConfigValue == "3" {
+	//	br.Msg = "撤销成功"
+	//} else {
+	//	br.Msg = "取消发布成功"
+	//}
+
+	recordItem := &models.ReportStateRecord{
+		ReportId:   req.ReportIds,
+		ReportType: 1,
+		State:      state,
+		AdminId:    this.SysUser.AdminId,
+		AdminName:  this.SysUser.AdminName,
+		CreateTime: time.Now(),
+	}
+	go func() {
+		_, _ = models.AddReportStateRecord(recordItem)
+	}()
+	br.Ret = 200
+	br.Success = true
+}
+
+// PrePublishReport
+// @Title 设置定时发布接口
+// @Description 设置定时发布接口
+// @Param	request	body models.PrePublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /pre_publish [post]
+func (this *ReportController) PrePublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PrePublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId == 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	if req.PrePublishTime == "" {
+		br.Msg = "发布时间不能为空"
+		return
+	}
+	if req.PreMsgSend != 0 && req.PreMsgSend != 1 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "是否发送模版消息标识错误"
+		return
+	}
+	prePublishTime, err := time.ParseInLocation(utils.FormatDateTime, req.PrePublishTime, time.Local)
+	if err != nil {
+		br.Msg = "发布时间格式错误"
+		br.ErrMsg = "发布时间格式错误,Err:" + err.Error()
+		return
+	}
+	if prePublishTime.Before(time.Now()) {
+		br.Msg = "发布时间不允许选择过去时间"
+		return
+	}
+	if prePublishTime.Before(time.Now().Add(2 * time.Minute)) {
+		br.Msg = "发布时间距离当前时间太近了"
+		return
+	}
+
+	reportDetail, err := models.GetReportById(reportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if reportDetail == nil {
+		br.Msg = "报告不存在"
+		return
+	}
+
+	// 如果是章节类型的报告,那么需要确认所有章节已发布
+	if reportDetail.HasChapter == 1 {
+		chapterList, err := models.GetChapterListByReportId(reportId)
+		if err != nil {
+			return
+		}
+		for _, chapter := range chapterList {
+			if chapter.PublishState == 1 {
+				br.Msg = "还存在未发布的章节"
+				br.ErrMsg = "还存在未发布的章节"
+				return
+			}
+		}
+	} else {
+		if reportDetail.Content == "" {
+			br.Msg = "报告内容为空,不可设置定时发布"
+			br.ErrMsg = "报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
+			return
+		}
+	}
+
+	if reportDetail.State == 2 {
+		br.Msg = "报告已发布,不可设置定时发布"
+		return
+	}
+
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeChinese, reportDetail.ClassifyIdFirst, reportDetail.ClassifyIdSecond, reportDetail.ClassifyIdThird)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+	if opening {
+		br.Msg = "报告已开启审批流, 不可设置定时发布"
+		return
+	}
+
+	var tmpErr error
+	if tmpErr = models.SetPrePublishReportById(reportDetail.Id, req.PrePublishTime, req.PreMsgSend); tmpErr != nil {
+		br.Msg = "设置定时发布失败"
+		br.ErrMsg = "设置定时发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(reportDetail.Id)
+		return
+	}
+
+	// 生成报告pdf和长图
+	if req.ReportUrl != "" {
+		go services.Report2pdfAndJpeg(req.ReportUrl, reportDetail.Id, 1)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "定时发布成功"
+}
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *ReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 如果是章节类型的报告,那么需要确认所有章节已发布
+	if reportItem.HasChapter == 1 {
+		chapterList, err := models.GetChapterListByReportId(reportId)
+		if err != nil {
+			return
+		}
+		for _, chapter := range chapterList {
+			if chapter.PublishState == 1 {
+				br.Msg = "还存在未发布的章节"
+				br.ErrMsg = "还存在未发布的章节"
+				return
+			}
+		}
+	} else {
+		if reportItem.Content == "" {
+			br.Msg = "报告内容为空,不可提交"
+			br.ErrMsg = "报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
+			return
+		}
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, reportItem.ClassifyIdThird, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, reportItem.ClassifyIdThird, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *ReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, reportItem.ClassifyIdThird, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	//if reportItem.ApproveId <= 0 {
+	//	br.Msg = "报告审批不存在"
+	//	br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+	//	return
+	//}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// init
+// @Description: 修复历史报告数据
+// @author: Roc
+// @datetime 2024-06-21 09:19:05
+func init() {
+	//fixApproveRecord()
+	//fixChapterPermission()
+	//fixReportEs()
+	//fixSmartReport()
+}
+
+// 修复研报审批数据
+func fixApproveRecord() {
+	recordOb := new(report_approve.ReportApproveRecord)
+
+	recordCond := fmt.Sprintf(` AND %s = ? AND %s in (?,?)`, report_approve.ReportApproveRecordCols.NodeType, report_approve.ReportApproveRecordCols.State)
+	recordPars := make([]interface{}, 0)
+	recordPars = append(recordPars, 0, report_approve.ReportApproveStatePass, report_approve.ReportApproveStateRefuse)
+	list, e := recordOb.GetItemsByCondition(recordCond, recordPars, []string{}, "")
+	if e != nil {
+		fmt.Println("查找审批记录失败,Err:", e.Error())
+		return
+	}
+	for _, recordItem := range list {
+		//fmt.Println(recordItem)
+		recordItem.NodeState = report_approve.ReportApproveStatePass
+		recordItem.NodeApproveUserId = recordItem.ApproveUserId
+		recordItem.NodeApproveUserName = recordItem.ApproveUserName
+		recordItem.NodeApproveTime = recordItem.ApproveTime
+
+		// 如果不是或签,那么只需要修复自己就好了
+		if recordItem.ApproveType != report_approve.NodeApproveTypeAny {
+			recordCols := []string{"State", "ApproveTime", "ModifyTime", "NodeState", "NodeApproveUserId", "NodeApproveUserName", "NodeApproveTime"}
+			if e = recordItem.Update(recordCols); e != nil {
+				fmt.Println("更新审批记录状态失败,Err:", e.Error())
+			}
+			continue
+		}
+		// 或签
+		// 需要将该审批的同一个节点的记录标记为已审批
+		if e := recordItem.UpdateNodeState(recordItem.ReportApproveId, recordItem.NodeId, recordItem.NodeState, recordItem.NodeApproveUserId, recordItem.NodeApproveUserName, recordItem.NodeApproveTime); e != nil {
+			fmt.Println("更新同一节点的其他审批记录状态失败,Err:", e.Error())
+		}
+	}
+
+	fmt.Println("审批数据修复完成")
+}
+
+// fixChapterPermission
+// @Description: 修复章节关联的品种权限
+// @author: Roc
+// @datetime 2024-06-20 18:08:34
+func fixChapterPermission() {
+	allChapterTypePermissionList, err := models.GetAllChapterTypePermission()
+	if err != nil {
+		fmt.Println("获取所有章节类型ID获取章节类型权限列表失败,Err:", err.Error())
+		return
+	}
+
+	currChapterTypePermissionIdListMap := make(map[int][]int)
+
+	hasPermissionMap := make(map[string]bool)
+	for _, v := range allChapterTypePermissionList {
+		tmpChapterTypePermissionList, ok := currChapterTypePermissionIdListMap[v.ReportChapterTypeId]
+		if !ok {
+			tmpChapterTypePermissionList = make([]int, 0)
+		}
+		key := fmt.Sprint(v.ReportChapterTypeId, "-", v.ChartPermissionId)
+		if _, has := hasPermissionMap[key]; !has {
+			hasPermissionMap[key] = true
+			currChapterTypePermissionIdListMap[v.ReportChapterTypeId] = append(tmpChapterTypePermissionList, v.ChartPermissionId)
+		}
+	}
+
+	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}
+	allReportChapterList, err := models.GetAllReportChapter()
+	if err != nil {
+		fmt.Println("获取所有章节失败,Err:", err.Error())
+		return
+	}
+
+	addList := make([]*report.ReportChapterPermissionMapping, 0)
+	for _, v := range allReportChapterList {
+		// 如果是上面的章节id,那么就过滤掉,因为已经入库了
+		if utils.InArrayByInt(notIdList, v.ReportChapterId) {
+			continue
+		}
+		permissionIdList, ok := currChapterTypePermissionIdListMap[v.TypeId]
+		if !ok {
+			continue
+		}
+
+		for _, permissionId := range permissionIdList {
+			addList = append(addList, &report.ReportChapterPermissionMapping{
+				ReportChapterPermissionMappingId: 0,
+				ReportChapterId:                  v.ReportChapterId,
+				ChartPermissionId:                permissionId,
+				CreateTime:                       v.ModifyTime,
+			})
+		}
+
+	}
+
+	obj := report.ReportChapterPermissionMapping{}
+	err = obj.MultiAdd(addList)
+	if err != nil {
+		fmt.Println("批量添加报章节的品种权限失败,Err:", err.Error())
+	}
+
+	return
+}
+
+// fixReportEs
+// @Description: 修复报告es数据
+// @author: Roc
+// @datetime 2024-06-20 18:08:34
+func fixReportEs() {
+
+	//reportInfo, err := models.GetReportByReportId(3941)
+	//if err != nil {
+	//	fmt.Println("查询信息失败,", err)
+	//	return
+	//}
+	//content := utils.TrimHtml(html.UnescapeString(reportInfo.Content))
+	//fmt.Println(content)
+	//
+	//fmt.Println("=========================")
+	//
+	//chapterInfo, err := models.GetReportChapterInfoById(9637)
+	//if err != nil {
+	//	fmt.Println("查询信息失败2,", err)
+	//	return
+	//}
+	//
+	//content = utils.TrimHtml(html.UnescapeString(chapterInfo.Content))
+	//fmt.Println(content)
+	//
+	//services.UpdateReportChapterEs(9637)
+	//return
+
+	var condition string
+	var pars []interface{}
+
+	condition += " AND state in (2,6) "
+	list, err := models.GetReportListV1(condition, pars, 0, 100000)
+	if err != nil {
+		fmt.Println("查询信息失败,", err)
+		return
+	}
+
+	num := len(list)
+	fmt.Println(num, "条待修复报告es数据")
+
+	for k, v := range list {
+		fmt.Println("剩余", num-k, "条")
+		services.UpdateReportEs(v.Id, 2)
+	}
+
+	fmt.Println("报告ES数据修复完成")
+
+	return
+}
+
+// fixSmartReport
+// @Description: 修复智能研报的数据
+// @author: Roc
+// @datetime 2024-06-27 16:54:41
+func fixSmartReport() {
+	fmt.Println("修复智能研报开始")
+	// 先判断是否已经修复过数据,如果修复过,那么就不用修复了
+	{
+		condition := ` AND report_layout=2 and old_report_id>0 `
+		list, err := models.GetReportByCondition(condition, []interface{}{}, []string{}, " order by id asc ", false, 0, 0)
+		if err != nil {
+			fmt.Println("获取已修复的报告列表失败, Err:" + err.Error())
+			return
+		}
+		if len(list) > 0 {
+			fmt.Println("智能研报已经修复过数据,不需要再次修复")
+			return
+		}
+	}
+
+	var condition string
+	var pars []interface{}
+	reportOB := new(smart_report.SmartReport)
+	list, e := reportOB.GetItemsByCondition(condition, pars, []string{}, " smart_report_id asc ")
+	if e != nil {
+		fmt.Println("获取智能报告列表失败, Err:" + e.Error())
+		return
+	}
+
+	addList := make([]*models.Report, 0)
+	for _, v := range list {
+		fmt.Println(v)
+		addList = append(addList, &models.Report{
+			//Id:                  0,
+			AddType:            1,
+			ClassifyIdFirst:    v.ClassifyIdFirst,
+			ClassifyNameFirst:  v.ClassifyNameFirst,
+			ClassifyIdSecond:   v.ClassifyIdSecond,
+			ClassifyNameSecond: v.ClassifyNameSecond,
+			Title:              v.Title,
+			Abstract:           v.Abstract,
+			Author:             v.Author,
+			Frequency:          v.Frequency,
+			CreateTime:         v.CreateTime.Format(utils.FormatDateTime),
+			ModifyTime:         v.ModifyTime,
+			State:              v.State,
+			PublishTime:        v.PublishTime,
+			Stage:              v.Stage,
+			MsgIsSend:          v.MsgIsSend,
+			//ThsMsgIsSend:        v.Tha,
+			Content:             v.Content,
+			VideoUrl:            v.VideoUrl,
+			VideoName:           v.VideoName,
+			VideoPlaySeconds:    fmt.Sprint(v.VideoPlaySeconds),
+			VideoSize:           v.VideoSize,
+			ContentSub:          v.ContentSub,
+			ReportCode:          fmt.Sprint(v.SmartReportId),
+			ReportVersion:       1,
+			HasChapter:          0,
+			ChapterType:         "",
+			OldReportId:         v.SmartReportId,
+			MsgSendTime:         v.MsgSendTime,
+			AdminId:             v.AdminId,
+			AdminRealName:       v.AdminRealName,
+			ApproveTime:         v.ApproveTime,
+			ApproveId:           v.ApproveId,
+			DetailImgUrl:        v.DetailImgUrl,
+			DetailPdfUrl:        v.DetailPdfUrl,
+			ContentStruct:       v.ContentStruct,
+			LastModifyAdminId:   v.LastModifyAdminId,
+			LastModifyAdminName: v.LastModifyAdminName,
+			ContentModifyTime:   v.ContentModifyTime,
+			Pv:                  v.Pv,
+			Uv:                  v.Uv,
+			HeadImg:             v.HeadImg,
+			EndImg:              v.EndImg,
+			CanvasColor:         v.CanvasColor,
+			NeedSplice:          v.NeedSplice,
+			HeadResourceId:      v.HeadResourceId,
+			EndResourceId:       v.EndResourceId,
+			ClassifyIdThird:     0,
+			ClassifyNameThird:   "",
+			CollaborateType:     1,
+			ReportLayout:        2,
+			IsPublicPublish:     1,
+			ReportCreateTime:    v.CreateTime,
+			InheritReportId:     0,
+		})
+	}
+
+	if len(addList) > 0 {
+		err := models.InsertMultiReport(addList)
+		if err != nil {
+			fmt.Println("新增智能研报失败")
+			return
+		}
+
+		reportMap := make(map[int]*models.Report)
+		// 找出已经修复过的新的研报
+		{
+			reportList, tmpErr := models.GetReportByCondition(` AND report_layout=2 and old_report_id>0 `, []interface{}{}, []string{}, " order by id asc ", false, 0, 0)
+			if tmpErr != nil {
+				fmt.Println("获取已修复的报告列表失败, Err:" + tmpErr.Error())
+				return
+			}
+			for _, v := range reportList {
+				v.ReportCode = utils.MD5(strconv.Itoa(v.Id))
+				v.Update([]string{"ReportCode"})
+				reportMap[v.OldReportId] = v
+			}
+		}
+
+		// 找出智能研报的审批单并处理
+		{
+
+			// 智能研报走审批单的
+			reportApproveObj := new(report_approve.ReportApprove)
+
+			approveList, tmpErr := reportApproveObj.GetItemsByCondition(` AND report_type=3 `, []interface{}{}, []string{}, " report_approve_id asc ")
+			if tmpErr != nil {
+				fmt.Println("获取已修复的报告列表失败, Err:" + tmpErr.Error())
+				return
+			}
+
+			for _, v := range approveList {
+				reportInfo, ok := reportMap[v.ReportId]
+				if ok {
+					v.ReportId = reportInfo.Id
+					v.ReportType = 1
+					err = v.Update([]string{"ReportType", "ReportId"})
+					if err != nil {
+						fmt.Println(v.ReportApproveId, "数据修复失败,其对应的报告ID是:", v.ReportId, ";新的报告ID是:", reportInfo.Id)
+					}
+				} else {
+					fmt.Println(v.ReportApproveId, "找不到对应的报告,其对应的报告ID是:", v.ReportId)
+				}
+			}
+		}
+	}
+
+	fmt.Println("修复智能研报完成")
+}

+ 13 - 36
controllers/smart_report/smart_report.go

@@ -79,7 +79,7 @@ func (this *SmartReportController) Add() {
 	stageMax += 1
 
 	// 根据审批开关及审批流判断当前报告状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, req.ClassifyIdFirst, req.ClassifyIdSecond, 0, models.ReportOperateAdd)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -247,7 +247,7 @@ func (this *SmartReportController) Edit() {
 		contentModify = true
 	}
 	cols := []string{"ClassifyIdFirst", "ClassifyNameFirst", "ClassifyIdSecond", "ClassifyNameSecond", "Title", "Abstract", "Author",
-		"Frequency", "Content", "ContentSub", "ContentStruct", "ModifyTime", "HeadImg", "EndImg", "CanvasColor","HeadResourceId", "EndResourceId"}
+		"Frequency", "Content", "ContentSub", "ContentStruct", "ModifyTime", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId"}
 	item.ClassifyIdFirst = req.ClassifyIdFirst
 	item.ClassifyNameFirst = req.ClassifyNameFirst
 	item.ClassifyIdSecond = req.ClassifyIdSecond
@@ -277,27 +277,6 @@ func (this *SmartReportController) Edit() {
 		return
 	}
 	resp := smart_report.FormatSmartReport2Item(item)
-	if resp.HeadResourceId > 0 {
-		headResource, err := smart_report.GetResourceItemById(resp.HeadResourceId)
-		if err != nil {
-			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版头失败, Err: " + e.Error()
-			return
-		}
-		resp.HeadImg = headResource.ImgUrl
-		resp.HeadStyle = headResource.Style
-	}
-
-	if resp.EndResourceId > 0 {
-		endResource, err := smart_report.GetResourceItemById(resp.EndResourceId)
-		if err != nil {
-			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版头失败, Err: " + e.Error()
-			return
-		}
-		resp.EndImg = endResource.ImgUrl
-		resp.EndStyle = endResource.Style
-	}
 
 	br.Ret = 200
 	br.Success = true
@@ -414,7 +393,7 @@ func (this *SmartReportController) Detail() {
 		headResource, err := smart_report.GetResourceItemById(resp.HeadResourceId)
 		if err != nil {
 			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版头失败, Err: " + e.Error()
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
 			return
 		}
 		resp.HeadImg = headResource.ImgUrl
@@ -425,7 +404,7 @@ func (this *SmartReportController) Detail() {
 		endResource, err := smart_report.GetResourceItemById(resp.EndResourceId)
 		if err != nil {
 			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版头失败, Err: " + e.Error()
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
 			return
 		}
 		resp.EndImg = endResource.ImgUrl
@@ -505,7 +484,7 @@ func (this *SmartReportController) Publish() {
 	}
 
 	// 根据审批开关及审批流判断当前报告状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond, operate)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond, 0, operate)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -662,7 +641,7 @@ func (this *SmartReportController) PrePublish() {
 	}
 
 	// 校验是否开启了审批流
-	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond)
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond, 0)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
@@ -838,7 +817,7 @@ func (this *SmartReportController) SaveContent() {
 	for _, ad := range admins {
 		adminIdName[ad.AdminId] = ad.RealName
 	}
-	editing, e := services.UpdateSmartReportEditing(req.SmartReportId, 1, sysUser.AdminId, sysUser.RealName, adminIdName)
+	editing, e := services.UpdateSmartReportEditing(req.SmartReportId, 1, sysUser.AdminId, sysUser.RealName, adminIdName, this.Lang)
 	if e != nil {
 		br.Msg = e.Error()
 		return
@@ -872,7 +851,7 @@ func (this *SmartReportController) SaveContent() {
 		item.CanvasColor = req.CanvasColor
 		item.HeadResourceId = req.HeadResourceId
 		item.EndResourceId = req.EndResourceId
-		cols := []string{"Content", "ContentSub", "ContentStruct", "ContentModifyTime", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime", "HeadImg", "EndImg", "CanvasColor","HeadResourceId", "EndResourceId"}
+		cols := []string{"Content", "ContentSub", "ContentStruct", "ContentModifyTime", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId"}
 		if e = item.Update(cols); e != nil {
 			br.Msg = "操作失败"
 			br.ErrMsg = "更新报告内容失败"
@@ -949,7 +928,7 @@ func (this *SmartReportController) MarkEditStatus() {
 		adminIdName[ad.AdminId] = ad.RealName
 	}
 
-	data, e := services.UpdateSmartReportEditing(req.SmartReportId, req.Status, sysUser.AdminId, sysUser.RealName, adminIdName)
+	data, e := services.UpdateSmartReportEditing(req.SmartReportId, req.Status, sysUser.AdminId, sysUser.RealName, adminIdName, this.Lang)
 	if e != nil {
 		br.Msg = e.Error()
 		return
@@ -1112,7 +1091,7 @@ func (this *SmartReportController) List() {
 
 	for _, v := range list {
 		item := smart_report.FormatSmartReport2Item(v)
-		mark, e := services.UpdateSmartReportEditing(v.SmartReportId, 2, sysUser.AdminId, sysUser.RealName, adminIdName)
+		mark, e := services.UpdateSmartReportEditing(v.SmartReportId, 2, sysUser.AdminId, sysUser.RealName, adminIdName, this.Lang)
 		if e != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "查询编辑中标记失败, Err:" + e.Error()
@@ -1425,7 +1404,7 @@ func (this *SmartReportController) SubmitApprove() {
 	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateSubmitApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
@@ -1448,7 +1427,7 @@ func (this *SmartReportController) SubmitApprove() {
 	}
 
 	// 提交审批
-	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeSmart, reportItem.SmartReportId, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeSmart, reportItem.SmartReportId, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, sysUser.AdminId, sysUser.RealName)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "提交审批失败, Err: " + e.Error()
@@ -1516,7 +1495,7 @@ func (this *SmartReportController) CancelApprove() {
 	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateCancelApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
@@ -1564,5 +1543,3 @@ func (this *SmartReportController) CancelApprove() {
 	br.Success = true
 	br.Msg = "操作成功"
 }
-
-

+ 9 - 5
go.mod

@@ -53,6 +53,7 @@ require (
 	github.com/aliyun/credentials-go v1.3.1 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
 	github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 // indirect
+	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
@@ -73,6 +74,8 @@ require (
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/gorilla/css v1.0.1 // indirect
+	github.com/h2non/filetype v1.1.3 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
@@ -82,6 +85,7 @@ require (
 	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+	github.com/microcosm-cc/bluemonday v1.0.27 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/minio/sha256-simd v1.0.1 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -111,12 +115,12 @@ require (
 	github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
 	github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
-	golang.org/x/crypto v0.19.0 // indirect
+	golang.org/x/crypto v0.24.0 // indirect
 	golang.org/x/image v0.14.0 // indirect
-	golang.org/x/net v0.21.0 // indirect
-	golang.org/x/sync v0.2.0 // indirect
-	golang.org/x/sys v0.17.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/net v0.26.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

+ 18 - 0
go.sum

@@ -68,6 +68,8 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoU
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
 github.com/aws/aws-sdk-go v1.51.2 h1:Ruwgz5aqIXin5Yfcgc+PCzoqW5tEGb9aDL/JWDsre7k=
 github.com/aws/aws-sdk-go v1.51.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
 github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=
 github.com/beego/beego/v2 v2.1.0 h1:Lk0FtQGvDQCx5V5yEu4XwDsIgt+QOlNjt5emUa3/ZmA=
@@ -222,9 +224,13 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
+github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
+github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -287,6 +293,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
+github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
 github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
@@ -495,6 +503,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
 golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -545,6 +555,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
 golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -560,6 +572,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
 golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -595,6 +609,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -616,6 +632,8 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 67 - 0
models/chart_permission.go

@@ -7,6 +7,10 @@ import (
 	"time"
 )
 
+const (
+	FiccProductId = 1
+)
+
 // ChartPermission 报告权限表
 type ChartPermission struct {
 	ChartPermissionId     int       `orm:"column(chart_permission_id);pk" description:"问题ID" json:"chart_permission_id"`
@@ -210,3 +214,66 @@ func (c *ChartPermission) GetFirstChartPermissionByParentId(parentId int) (item
 	err = o.Raw(sql, parentId).QueryRow(&item)
 	return
 }
+
+// GetChartPermissionById 主键获取品种
+func GetChartPermissionById(permissionId int) (item *ChartPermission, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM chart_permission WHERE chart_permission_id = ?`
+	err = o.Raw(sql, permissionId).QueryRow(&item)
+	return
+}
+
+// GetSecondaryChartPermissions 获取二级权限列表
+func GetSecondaryChartPermissions() (list []*ChartPermission, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM chart_permission WHERE product_id = ? AND parent_id > 0 AND enabled = 1 ORDER BY parent_id ASC, sort ASC, created_time ASC`
+	_, err = o.Raw(sql, FiccProductId).QueryRows(&list)
+	return
+}
+
+type SimpleChartPermission struct {
+	ChartPermissionId   int                      `description:"品种ID"`
+	ChartPermissionName string                   `description:"品种名称"`
+	Sort                int                      `description:"排序"`
+	Children            []*SimpleChartPermission `description:"子分类"`
+}
+
+func FormatChartPermission2Simple(origin *ChartPermission) (item *SimpleChartPermission) {
+	if origin == nil {
+		return
+	}
+	item = new(SimpleChartPermission)
+	item.ChartPermissionId = origin.ChartPermissionId
+	item.ChartPermissionName = origin.PermissionName
+	item.Sort = origin.Sort
+
+	return
+}
+
+// GetChartPermissionsByProductId 获取权限列表
+func GetChartPermissionsByProductId() (list []*ChartPermission, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM chart_permission WHERE product_id = ? AND enabled = 1 ORDER BY parent_id ASC, sort ASC, created_time ASC`
+	_, err = o.Raw(sql, FiccProductId).QueryRows(&list)
+
+	return
+}
+
+// GetChartPermissionByIdList
+// @Description: 根据品种ID列表获取权限列表
+// @author: Roc
+// @datetime 2024-06-07 10:32:29
+// @param permissionIdList []int
+// @return items []*ChartPermission
+// @return err error
+func GetChartPermissionByIdList(permissionIdList []int) (items []*ChartPermission, err error) {
+	num := len(permissionIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM chart_permission WHERE chart_permission_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, permissionIdList).QueryRows(&items)
+
+	return
+}

+ 240 - 92
models/classify.go

@@ -40,38 +40,42 @@ type Classify struct {
 	RelateTel         int       `description:"是否在电话会中可选: 0-否; 1-是"`
 	RelateVideo       int       `description:"是否在路演视频中可选: 0-否; 1-是"`
 	IsMassSend        int       `description:"1:群发,0:非群发"`
+	Enabled           int       `description:"是否可用,1可用,0禁用"`
+	Level             int       `description:"层级"`
+	HasChild          int       `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
 }
 
 type ClassifyAddReq struct {
-	ClassifyName      string                 `description:"分类名称"`
-	ParentId          int                    `description:"父级分类id,没有父级分类传0"`
-	Abstract          string                 `description:"栏目简介"`
-	Descript          string                 `description:"分享描述"`
-	ReportAuthor      string                 `description:"栏目作者"`
-	AuthorDescript    string                 `description:"作者简介"`
-	ColumnImgUrl      string                 `description:"栏目配图"`
-	ReportImgUrl      string                 `description:"报告配图"`
-	HeadImgUrl        string                 `description:"头部banner"`
-	AvatarImgUrl      string                 `description:"头像"`
-	HomeImgUrl        string                 `description:"首页配图"`
-	ClassifyLabel     string                 `description:"分类标签"`
-	ShowType          int                    `description:"展示类型:1-列表 2-专栏"`
-	HasTeleconference int                    `description:"是否有电话会:0-否 1-是"`
-	VipTitle          string                 `description:"研究员头衔"`
-	Sort              int                    `description:"后台排序"`
-	IsShow            int                    `description:"是否在小程序显示:1-显示 0-隐藏"`
-	YbFiccSort        int                    `description:"小程序FICC页排序"`
-	YbFiccIcon        string                 `description:"小程序FICC页icon"`
-	YbFiccPcIcon      string                 `description:"小程序PC端FICC页背景图"`
-	YbIconUrl         string                 `description:"小程序已购页icon"`
-	YbBgUrl           string                 `description:"小程序已购详情背景图"`
-	YbListImg         string                 `description:"小程序研报列表封面图"`
-	YbShareBgImg      string                 `description:"小程序研报详情分享背景图"`
-	YbRightBanner     string                 `description:"Pc端详情页,右侧,报告合集背景图"`
-	MenuList          []*ClassifyMenuSaveReq `description:"子目录列表"`
-	ClassifyMenuId    int                    `description:"二级分类-子目录ID"`
-	RelateTel         int                    `description:"是否在电话会中可选: 0-否; 1-是"`
-	RelateVideo       int                    `description:"是否在路演视频中可选: 0-否; 1-是"`
+	ClassifyName          string `description:"分类名称"`
+	ParentId              int    `description:"父级分类id,没有父级分类传0"`
+	ChartPermissionIdList []int  `description:"权限id数组"`
+	/*Abstract              string                 `description:"栏目简介"`
+	Descript              string                 `description:"分享描述"`
+	ReportAuthor          string                 `description:"栏目作者"`
+	AuthorDescript        string                 `description:"作者简介"`
+	ColumnImgUrl          string                 `description:"栏目配图"`
+	ReportImgUrl          string                 `description:"报告配图"`
+	HeadImgUrl            string                 `description:"头部banner"`
+	AvatarImgUrl          string                 `description:"头像"`
+	HomeImgUrl            string                 `description:"首页配图"`
+	ClassifyLabel         string                 `description:"分类标签"`
+	ShowType              int                    `description:"展示类型:1-列表 2-专栏"`
+	HasTeleconference     int                    `description:"是否有电话会:0-否 1-是"`
+	VipTitle              string                 `description:"研究员头衔"`
+	Sort                  int                    `description:"后台排序"`
+	IsShow                int                    `description:"是否在小程序显示:1-显示 0-隐藏"`
+	YbFiccSort            int                    `description:"小程序FICC页排序"`
+	YbFiccIcon            string                 `description:"小程序FICC页icon"`
+	YbFiccPcIcon          string                 `description:"小程序PC端FICC页背景图"`
+	YbIconUrl             string                 `description:"小程序已购页icon"`
+	YbBgUrl               string                 `description:"小程序已购详情背景图"`
+	YbListImg             string                 `description:"小程序研报列表封面图"`
+	YbShareBgImg          string                 `description:"小程序研报详情分享背景图"`
+	YbRightBanner         string                 `description:"Pc端详情页,右侧,报告合集背景图"`
+	MenuList              []*ClassifyMenuSaveReq `description:"子目录列表"`
+	ClassifyMenuId        int                    `description:"二级分类-子目录ID"`
+	RelateTel             int                    `description:"是否在电话会中可选: 0-否; 1-是"`
+	RelateVideo           int                    `description:"是否在路演视频中可选: 0-否; 1-是"`*/
 }
 
 func GetClassifyByName(classifyName string, parentId int) (item *Classify, err error) {
@@ -141,13 +145,20 @@ func DeleteClassify(classifyId int) (err error) {
 // 修改分类
 func EditClassify(req *EditClassifyReq) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE classify SET classify_name = ?,abstract=?, parent_id= ?,descript=?,report_author=?,author_descript=?,column_img_url=?,head_img_url=?,avatar_img_url=?,report_img_url=?,home_img_url=?,classify_label=?,show_type=?,has_teleconference=?,vip_title=?,modify_time= NOW() WHERE id = ? `
-	_, err = o.Raw(sql, req.ClassifyName, req.Abstract, req.ParentId, req.Descript, req.ReportAuthor, req.AuthorDescript, req.ColumnImgUrl, req.HeadImgUrl, req.AvatarImgUrl, req.ReportImgUrl, req.HomeImgUrl, req.ClassifyLabel, req.ShowType, req.HasTeleconference, req.VipTitle, req.ClassifyId).Exec()
+	//sql := `UPDATE classify SET classify_name = ?,abstract=?, parent_id= ?,descript=?,report_author=?,author_descript=?,column_img_url=?,head_img_url=?,avatar_img_url=?,report_img_url=?,home_img_url=?,classify_label=?,show_type=?,has_teleconference=?,vip_title=?,modify_time= NOW() WHERE id = ? `
+	//_, err = o.Raw(sql, req.ClassifyName, req.Abstract, req.ParentId, req.Descript, req.ReportAuthor, req.AuthorDescript, req.ColumnImgUrl, req.HeadImgUrl, req.AvatarImgUrl, req.ReportImgUrl, req.HomeImgUrl, req.ClassifyLabel, req.ShowType, req.HasTeleconference, req.VipTitle, req.ClassifyId).Exec()
+	sql := `UPDATE classify SET classify_name = ?,parent_id= ?,modify_time= NOW() WHERE id = ? `
+	_, err = o.Raw(sql, req.ClassifyName, req.ParentId, req.ClassifyId).Exec()
+
 	return
 }
 
-//获取父级分类
-
+// ParentClassify
+// @Description: 获取父级分类
+// @author: Roc
+// @datetime 2024-06-18 15:03:49
+// @return items []*Classify
+// @return err error
 func ParentClassify() (items []*Classify, err error) {
 	sql := `SELECT * FROM classify WHERE parent_id=0 order by id desc `
 	o := orm.NewOrmUsingDB("rddp")
@@ -164,41 +175,47 @@ func FindByIdClassify(classifyId int) (item *Classify, err error) {
 }
 
 type ClassifyList struct {
-	Id                int       `orm:"column(id);pk"`
-	ClassifyName      string    `description:"分类名称"`
-	Sort              int       `description:"排序"`
-	ParentId          int       `description:"父级分类id"`
-	CreateTime        time.Time `description:"创建时间"`
-	ModifyTime        time.Time `description:"修改时间"`
-	Abstract          string    `description:"简介"`
-	Descript          string    `description:"描述"`
-	ClassifyLabel     string    `description:"分类标签"`
-	ShowType          int       `description:"展示类型:1-列表 2-专栏"`
-	HasTeleconference int       `description:"是否有电话会:0-否 1-是"`
-	IsShow            int       `description:"是否在小程序显示:1-显示 0-隐藏"`
-	YbFiccSort        int       `description:"小程序FICC页排序"`
-	YbFiccIcon        string    `description:"小程序FICC页icon"`
-	YbFiccPcIcon      string    `description:"小程序PC端FICC页背景图"`
-	YbIconUrl         string    `description:"小程序已购页icon"`
-	YbBgUrl           string    `description:"小程序已购详情背景图"`
-	YbListImg         string    `description:"小程序研报列表封面图"`
-	YbShareBgImg      string    `description:"小程序研报详情分享背景图"`
-	YbRightBanner     string    `description:"Pc端详情页,右侧,报告合集背景图"`
-	RelateTel         int       `description:"是否在电话会中可选: 0-否; 1-是"`
-	RelateVideo       int       `description:"是否在路演视频中可选: 0-否; 1-是"`
-	Child             []*ClassifyItem
-	ClassifyMenuList  []*ClassifyMenu
+	Id                    int       `orm:"column(id);pk"`
+	ClassifyName          string    `description:"分类名称"`
+	Sort                  int       `description:"排序"`
+	ParentId              int       `description:"父级分类id"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+	Abstract              string    `description:"简介"`
+	Descript              string    `description:"描述"`
+	ClassifyLabel         string    `description:"分类标签"`
+	ShowType              int       `description:"展示类型:1-列表 2-专栏"`
+	HasTeleconference     int       `description:"是否有电话会:0-否 1-是"`
+	IsShow                int       `description:"是否在小程序显示:1-显示 0-隐藏"`
+	YbFiccSort            int       `description:"小程序FICC页排序"`
+	YbFiccIcon            string    `description:"小程序FICC页icon"`
+	YbFiccPcIcon          string    `description:"小程序PC端FICC页背景图"`
+	YbIconUrl             string    `description:"小程序已购页icon"`
+	YbBgUrl               string    `description:"小程序已购详情背景图"`
+	YbListImg             string    `description:"小程序研报列表封面图"`
+	YbShareBgImg          string    `description:"小程序研报详情分享背景图"`
+	YbRightBanner         string    `description:"Pc端详情页,右侧,报告合集背景图"`
+	RelateTel             int       `description:"是否在电话会中可选: 0-否; 1-是"`
+	RelateVideo           int       `description:"是否在路演视频中可选: 0-否; 1-是"`
+	Enabled               int       `description:"是否可用,1可用,0禁用"`
+	Child                 []*ClassifyList
+	ClassifyMenuId        int `description:"二级分类-子目录ID"`
+	ClassifyMenuList      []*ClassifyMenu
+	ChartPermissionIdList []int `description:"绑定的权限ID"`
+	Level                 int   `description:"层级"`
+	HasChild              int   `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
 }
 
 type ClassifyItem struct {
 	Classify
-	ClassifyMenuId   int `description:"二级分类-子目录ID"`
-	ClassifyMenuList []*ClassifyMenu
+	ClassifyMenuId        int `description:"二级分类-子目录ID"`
+	ClassifyMenuList      []*ClassifyMenu
+	ChartPermissionIdList []int `description:"绑定的权限ID"`
+	Child                 []*ClassifyItem
 }
 
 type ClassifyListResp struct {
-	List   []*ClassifyList
-	Paging *paging.PagingItem `description:"分页数据"`
+	List []*ClassifyList
 }
 
 type ClassifyPermissionListResp struct {
@@ -207,14 +224,9 @@ type ClassifyPermissionListResp struct {
 }
 
 // 获取分类列表
-func GetClassifyList(keyWord, companyType string, hideDayWeek, enabled int) (items []*ClassifyList, err error) {
+func GetClassifyList(keyWord string, enabled int) (items []*ClassifyList, err error) {
 	sql := ``
 	companyTypeSqlStr := ``
-	if companyType == "ficc" {
-		companyTypeSqlStr = " AND id != 40 AND parent_id != 40 "
-	} else if companyType == "权益" {
-		companyTypeSqlStr = " AND (id = 40 or parent_id = 40)  "
-	}
 	if enabled == 1 {
 		companyTypeSqlStr += ` AND enabled = 1 `
 	}
@@ -232,9 +244,6 @@ func GetClassifyList(keyWord, companyType string, hideDayWeek, enabled int) (ite
 		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
 	} else {
 		sql = `SELECT * FROM classify WHERE parent_id=0 ` + companyTypeSqlStr
-		if hideDayWeek == 1 {
-			sql += ` AND classify_name <> '晨报' AND classify_name <> '周报' `
-		}
 
 		sql += ` ORDER BY sort ASC, create_time ASC`
 	}
@@ -254,16 +263,19 @@ func GetClassifyListCount(keyWord, companyType string, hideDayWeek int) (count i
 	} else if companyType == "权益" {
 		companyTypeSqlStr = " AND (id = 40 or parent_id = 40)  "
 	}
+
+	pars := make([]interface{}, 0)
+
 	if keyWord != "" {
 		sqlCount = `SELECT  COUNT(1) AS count FROM (
                SELECT * FROM classify
-               WHERE parent_id=0 ` + companyTypeSqlStr + `  AND classify_name LIKE '%` + keyWord + `%'
+               WHERE parent_id=0 ` + companyTypeSqlStr + `  AND classify_name LIKE ?
                UNION
                SELECT * FROM classify
                WHERE id IN(SELECT parent_id FROM classify
-               WHERE parent_id>0 ` + companyTypeSqlStr + `  AND classify_name LIKE '%` + keyWord + `%')
+               WHERE parent_id>0 ` + companyTypeSqlStr + `  AND classify_name LIKE ? )
                )AS t `
-
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
 	} else {
 		sqlCount = `SELECT COUNT(1) AS count FROM classify WHERE parent_id=0 ` + companyTypeSqlStr
 		if hideDayWeek == 1 {
@@ -271,7 +283,7 @@ func GetClassifyListCount(keyWord, companyType string, hideDayWeek int) (count i
 		}
 	}
 	o := orm.NewOrmUsingDB("rddp")
-	err = o.Raw(sqlCount).QueryRow(&count)
+	err = o.Raw(sqlCount, pars...).QueryRow(&count)
 	return
 }
 
@@ -300,12 +312,16 @@ type FindByIdClassifyReq struct {
 func GetClassifyChild(parentId int, keyWord string) (items []*Classify, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := ``
+	pars := make([]interface{}, 0)
 	if keyWord != "" {
-		sql = `SELECT * FROM classify WHERE parent_id=? AND classify_name LIKE '%` + keyWord + `%' ORDER BY create_time ASC `
+		sql = `SELECT * FROM classify WHERE classify_name LIKE ? AND parent_id=? ORDER BY create_time ASC `
+		pars = append(pars, utils.GetLikeKeyword(keyWord))
 	} else {
 		sql = `SELECT * FROM classify WHERE parent_id=? ORDER BY create_time ASC `
 	}
-	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	pars = append(pars, parentId)
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+
 	return
 }
 
@@ -404,23 +420,155 @@ type RelateTelSecClassifyWithPermissions struct {
 	ChartPermissionIds string `description:"权限IDs"`
 }
 
-// GetRelateTelSecClassifyAndChartPermissions 获取关联了电话会的二级分类及权限
-func GetRelateTelSecClassifyAndChartPermissions() (items []*RelateTelSecClassifyWithPermissions, err error) {
-	dbName := `weekly_report`
-	if utils.RunMode == "debug" {
-		dbName = `test_v2_weekly_report`
+// UpdateClassifySortByParentId 根据父类id更新排序
+func UpdateClassifySortByParentId(parentId, permissionId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` update classify set sort = ` + updateSort + ` WHERE parent_id=? AND sort > ? `
+	if permissionId > 0 {
+		sql += ` or ( id > ` + fmt.Sprint(permissionId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
 	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetMaxSortByParentId 获取最大的排序值
+func (classifyInfo *Classify) GetMaxSortByParentId(parentId int) (maxSort int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT
-				a.id,
-				a.classify_name,
-				GROUP_CONCAT(b.chart_permission_id) AS chart_permission_ids
-			FROM classify AS a
-			LEFT JOIN %s.chart_permission_search_key_word_mapping AS b ON b.from = 'rddp' AND a.classify_name = b.key_word
-			WHERE
-				a.parent_id > 0 AND a.relate_tel = 1
-			GROUP BY a.id`
-	sql = fmt.Sprintf(sql, dbName)
-	_, err = o.Raw(sql).QueryRows(&items)
+	sql := `SELECT max(sort) AS sort FROM classify WHERE parent_id = ? `
+	err = o.Raw(sql, parentId).QueryRow(&maxSort)
+	return
+}
+
+// GetMaxSort 获取最大的排序值
+func (classifyInfo *Classify) GetMaxSort() (maxSort int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT max(sort) AS sort FROM classify`
+	err = o.Raw(sql).QueryRow(&maxSort)
+	return
+}
+
+// GetFirstClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func (classifyInfo *Classify) GetFirstClassifyByParentId(parentId int) (item *Classify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM classify WHERE parent_id = ? order by sort asc, id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+type ClassifyMoveReq struct {
+	ClassifyId     int `description:"分类ID"`
+	PrevClassifyId int `description:"上一个兄弟节点分类id"`
+	NextClassifyId int `description:"下一个兄弟节点分类id"`
+}
+
+type ClassifySetEnabledReq struct {
+	ClassifyId int `description:"分类ID"`
+	Enabled    int `description:"是否可用,1可用,0禁用"`
+}
+
+func (classifyInfo *Classify) SetEnabled(id, enabled int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := ` UPDATE classify SET enabled =?  WHERE id = ?`
+	_, err = to.Raw(sql, enabled, id).Exec()
+	if err != nil {
+		return
+	}
+	sql = ` UPDATE classify SET enabled =?  WHERE parent_id = ?`
+	_, err = to.Raw(sql, enabled, id).Exec()
+	if err != nil {
+		return
+	}
+	return
+}
+
+// GetCountClassifyChildByParentId
+// @Description: 获取父级分类下子分类数量
+// @author: Roc
+// @datetime 2024-06-17 10:58:46
+// @param parentId int
+// @return total int
+// @return err error
+func GetCountClassifyChildByParentId(parentId int) (total int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT count(1) AS total FROM classify WHERE parent_id = ? `
+	err = o.Raw(sql, parentId).QueryRow(&total)
+
+	return
+}
+
+// GetClassifyListByKeyword
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*ClassifyList
+// @return err error
+func GetClassifyListByKeyword(keyWord string, enabled int) (items []*ClassifyList, err error) {
+	sql := ``
+	pars := make([]interface{}, 0)
+
+	sql = `SELECT * FROM classify WHERE 1=1 `
+	if enabled == 1 {
+		sql += ` AND enabled = 1 `
+	}
+
+	if keyWord != `` {
+		sql += ` AND classify_name LIKE ? `
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 1)
+	}
+	sql += ` ORDER BY sort ASC, create_time ASC`
+
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+// GetClassifyListByParentIdList
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*ClassifyList
+// @return err error
+func GetClassifyListByParentIdList(parentClassifyIdList []int) (items []*ClassifyList, err error) {
+	num := len(parentClassifyIdList)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT * FROM classify WHERE id in (` + utils.GetOrmInReplace(num) + `) ORDER BY sort ASC, create_time ASC`
+
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Raw(sql, parentClassifyIdList).QueryRows(&items)
+	return
+}
+
+// GetClassifyListByIdList
+// @Description: 根据指标ID列表,获取分类列表
+// @author: Roc
+// @datetime 2024-06-27 15:23:57
+// @param classifyIdList []int
+// @return items []*Classify
+// @return err error
+func GetClassifyListByIdList(classifyIdList []int) (items []*Classify, err error) {
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT * FROM classify WHERE id IN (` + utils.GetOrmInReplace(num) + `) `
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Raw(sql, classifyIdList).QueryRows(&items)
 	return
 }

+ 46 - 0
models/company/company_config.go

@@ -1,6 +1,9 @@
 package company
 
 import (
+	"encoding/json"
+	"errors"
+	"eta/eta_mobile/utils"
 	"github.com/beego/beego/v2/client/orm"
 )
 
@@ -33,3 +36,46 @@ func GetConfigDetailByCode(configCode string) (item CrmConfig, err error) {
 	err = o.Raw(sql, configCode).QueryRow(&item)
 	return
 }
+
+// ConfigClassifyId
+// @Description: 后台配置的报告id
+type ConfigClassifyId struct {
+	Debug   int `json:"debug"`
+	Release int `json:"release"`
+}
+
+// GetReportClassifyIdByConfigKey
+// @Description: 获取关联的报告id
+// @author: Roc
+// @datetime 2024-06-18 14:10:27
+// @param configKey string
+// @return classifyId int
+// @return err error
+func GetReportClassifyIdByConfigKey(configKey string) (classifyId int, err error) {
+	// 别问为啥要从配置里拿=_=!
+	conf, e := GetConfigDetailByCode(configKey)
+	if e != nil {
+		err = errors.New("获取配置的id失败, Err: " + e.Error())
+		return
+	}
+	if conf.ConfigValue == "" {
+		err = errors.New("ID配置有误")
+		return
+	}
+	type TwoWeekIdConf struct {
+		Debug   []int
+		Release []int
+	}
+	classifyIdConf := new(ConfigClassifyId)
+	if e = json.Unmarshal([]byte(conf.ConfigValue), &classifyIdConf); e != nil {
+		err = errors.New("解析ID配置失败, Err: " + e.Error())
+		return
+	}
+	if utils.RunMode == "debug" {
+		classifyId = classifyIdConf.Debug
+	} else {
+		classifyId = classifyIdConf.Release
+	}
+
+	return
+}

+ 10 - 6
models/db.go

@@ -12,6 +12,7 @@ import (
 	"eta/eta_mobile/models/data_stat"
 	"eta/eta_mobile/models/eta_trial"
 	"eta/eta_mobile/models/ppt_english"
+	"eta/eta_mobile/models/report"
 	"eta/eta_mobile/models/report_approve"
 	"eta/eta_mobile/models/sandbox"
 	"eta/eta_mobile/models/semantic_analysis"
@@ -244,12 +245,15 @@ func initReport() {
 		new(ChartPermission),                     // 权限表
 		new(YbPcSuncode),
 		new(YbSuncodePars),
-		new(ReportAuthor),                  //报告作者
-		new(ClassifyMenu),                  // 报告分类-子目录表
-		new(ClassifyMenuRelation),          // 报告分类-子目录关联表
-		new(ChartPermissionChapterMapping), // 权限mapping表
-		new(ReportChapterType),             // 报告章节类型表
-		new(ReportStateRecord),             // 研报状态修改记录表
+		new(ReportAuthor),                          //报告作者
+		new(ClassifyMenu),                          // 报告分类-子目录表
+		new(ClassifyMenuRelation),                  // 报告分类-子目录关联表
+		new(ChartPermissionChapterMapping),         // 权限mapping表
+		new(ReportChapterType),                     // 报告章节类型表
+		new(ReportStateRecord),                     // 研报状态修改记录表
+		new(report.ReportGrant),                    // 报告授权用户表
+		new(report.ReportChapterGrant),             // 报告章节授权用户表
+		new(report.ReportChapterPermissionMapping), // 报告章节的权限关系表
 	)
 }
 

+ 3 - 1
models/english_report.go

@@ -91,6 +91,8 @@ type AddEnglishReportReq struct {
 	ClassifyNameFirst  string `description:"一级分类名称"`
 	ClassifyIdSecond   int    `description:"二级分类id"`
 	ClassifyNameSecond string `description:"二级分类名称"`
+	ClassifyIdThird    int    `description:"三级分类id"`
+	ClassifyNameThird  string `description:"三级分类名称"`
 	Title              string `description:"标题"`
 	Abstract           string `description:"摘要"`
 	Author             string `description:"作者"`
@@ -936,4 +938,4 @@ func UpdatePdfUrlEnglishReportById(reportId int) (err error) {
 	sql := `UPDATE english_report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE id = ? `
 	_, err = o.Raw(sql, reportId).Exec()
 	return
-}
+}

+ 84 - 6
models/permission.go

@@ -10,12 +10,58 @@ type ChartPermissionSearchKeyWordMapping struct {
 	From               string `description:"类型标识" json:"-"`
 	TacticType         string `description:"策略表type字段值" json:"-"`
 	TeleconferenceSort int    `description:"电话会类型排序" json:"-"`
+	ClassifyId         int    `description:"分类ID"`
 }
 
-func GetPermission(classifyNameSecond string) (items []*ChartPermissionSearchKeyWordMapping, err error) {
+func GetPermission(classifyId int) (items []*ChartPermissionSearchKeyWordMapping, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT * FROM chart_permission_search_key_word_mapping AS a WHERE a.from='rddp' AND a.key_word=? `
-	_, err = o.Raw(sql, classifyNameSecond).QueryRows(&items)
+	sql := `SELECT * FROM chart_permission_search_key_word_mapping AS a WHERE a.from='rddp' AND a.classify_id = ? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+func GetAllPermissionMapping() (items []*ChartPermissionSearchKeyWordMapping, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM chart_permission_search_key_word_mapping AS a WHERE a.from='rddp'`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// EditChartPermissionSearchKeyWordMappingMulti 修改报告报告权限(先删除原有的权限,再添加新的权限)
+func EditChartPermissionSearchKeyWordMappingMulti(keyword string, permissionIdList []int, classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := "DELETE FROM chart_permission_search_key_word_mapping WHERE `from` = 'rddp' AND classify_id = ?"
+	_, err = to.Raw(sql, classifyId).Exec()
+	if err != nil {
+		return
+	}
+
+	if len(permissionIdList) > 0 {
+		chartPermissionSearchKeyWordMappingList := make([]*ChartPermissionSearchKeyWordMapping, 0)
+		for _, permissionId := range permissionIdList {
+			tmpChartPermissionSearchKeyWordMapping := &ChartPermissionSearchKeyWordMapping{
+				ChartPermissionId:  permissionId,
+				KeyWord:            keyword,
+				From:               "rddp",
+				TacticType:         "",
+				TeleconferenceSort: 0,
+				ClassifyId:         classifyId,
+			}
+			chartPermissionSearchKeyWordMappingList = append(chartPermissionSearchKeyWordMappingList, tmpChartPermissionSearchKeyWordMapping)
+		}
+		_, err = to.InsertMulti(len(chartPermissionSearchKeyWordMappingList), chartPermissionSearchKeyWordMappingList)
+	}
 	return
 }
 
@@ -39,10 +85,42 @@ type ChartPermissionMappingIdName struct {
 	PermissionName string
 }
 
-func GetChartPermissionNameFromMappingByKeyword(keyword string, source string) (list []*ChartPermissionMappingIdName, err error) {
+func GetChartPermissionNameFromMappingByKeyword(source string, classifyId int) (list []*ChartPermissionMappingIdName, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := " SELECT b.chart_permission_id AS permission_id,b.permission_name FROM chart_permission_search_key_word_mapping AS a INNER JOIN chart_permission AS b ON a.chart_permission_id = b.chart_permission_id WHERE a.`from` = ? AND a.key_word = ? "
-	_, err = o.Raw(sql, source, keyword).QueryRows(&list)
+	sql := " SELECT b.chart_permission_id AS permission_id,b.permission_name FROM chart_permission_search_key_word_mapping AS a INNER JOIN chart_permission AS b ON a.chart_permission_id = b.chart_permission_id WHERE a.`from` = ? AND a.classify_id = ? "
+	_, err = o.Raw(sql, source, classifyId).QueryRows(&list)
+	return
+}
 
+// UpdateChartPermissionNameFromMappingByKeyword 根据关键词及来源更新新关键词
+func UpdateChartPermissionNameFromMappingByKeyword(newKeyword string, classifyId int, source string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE chart_permission_search_key_word_mapping SET key_word = ? WHERE classify_id = ? AND `from` = ? "
+	_, err = o.Raw(sql, newKeyword, classifyId, source).Exec()
+	return
+}
+
+// ChartPermissionSearchKeyWordMappingAndPermissionName
+// @Description: 分类关联品种
+type ChartPermissionSearchKeyWordMappingAndPermissionName struct {
+	ChartPermissionId   int    `description:"权限id"`
+	ChartPermissionName string `description:"权限名称"`
+	PermissionName      string `description:"权限名称"`
+	KeyWord             string `description:"二级分类名称"`
+	ClassifyId          int    `description:"分类ID"`
+}
+
+// GetPermissionByClassifyId
+// @Description: 根据分类id获取关联的报告权限
+// @author: Roc
+// @datetime 2024-06-19 14:56:44
+// @param classifyId int
+// @return items []*ChartPermissionSearchKeyWordMappingAndPermissionName
+// @return err error
+func GetPermissionByClassifyId(classifyId int) (items []*ChartPermissionSearchKeyWordMappingAndPermissionName, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT a.chart_permission_name,a.permission_name,b.chart_permission_id,b.key_word,b.classify_id FROM chart_permission AS a 
+ join chart_permission_search_key_word_mapping AS b ON a.chart_permission_id=b.chart_permission_id WHERE b.from='rddp' AND b.classify_id = ? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
 	return
 }

+ 372 - 112
models/report.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"errors"
 	"eta/eta_mobile/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
@@ -63,6 +64,28 @@ type Report struct {
 	AdminRealName      string    `description:"创建者姓名"`
 	ApproveTime        time.Time `description:"审批时间"`
 	ApproveId          int       `description:"审批ID"`
+	DetailImgUrl       string    `description:"报告详情长图地址"`
+	DetailPdfUrl       string    `description:"报告详情PDF地址"`
+
+	ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	Pv                  int       `description:"pv"`
+	Uv                  int       `description:"uv"`
+	HeadImg             string    `description:"报告头图地址"`
+	EndImg              string    `description:"报告尾图地址"`
+	CanvasColor         string    `description:"画布颜色"`
+	NeedSplice          int       `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId      int       `description:"版头资源ID"`
+	EndResourceId       int       `description:"版尾资源ID"`
+	ClassifyIdThird     int       `description:"三级分类id"`
+	ClassifyNameThird   string    `description:"三级分类名称"`
+	CollaborateType     int8      `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
+	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+	InheritReportId     int       `description:"待继承的报告ID"`
 }
 
 type ReportList struct {
@@ -106,6 +129,24 @@ type ReportList struct {
 	ApproveTime        string                    `description:"审批时间"`
 	DetailImgUrl       string                    `description:"报告详情长图地址"`
 	DetailPdfUrl       string                    `description:"报告详情PDF地址"`
+
+	CollaborateType     int8      `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
+	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+	ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	HeadImg             string    `description:"报告头图地址"`
+	EndImg              string    `description:"报告尾图地址"`
+	CanvasColor         string    `description:"画布颜色"`
+	NeedSplice          int       `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId      int       `description:"版头资源ID"`
+	EndResourceId       int       `description:"版尾资源ID"`
+	ClassifyIdThird     int       `description:"三级分类id"`
+	ClassifyNameThird   string    `description:"三级分类名称"`
+	InheritReportId     int       `description:"待继承的报告ID"`
 }
 
 type ReportListResp struct {
@@ -113,17 +154,114 @@ type ReportListResp struct {
 	Paging *paging.PagingItem `description:"分页数据"`
 }
 
-func GetReportListCount(condition string, pars []interface{}, companyType string) (count int, err error) {
-	//产品权限
-	companyTypeSqlStr := ``
-	if companyType == "ficc" {
-		companyTypeSqlStr = " AND classify_id_first != 40 "
-	} else if companyType == "权益" {
-		companyTypeSqlStr = " AND classify_id_first = 40 "
+// GetReportListCountV1
+// @Description: 获取普通报告列表的报告数量
+// @author: Roc
+// @datetime 2024-05-30 15:14:43
+// @param condition string
+// @param pars []interface{}
+// @return count int
+// @return err error
+func GetReportListCountV1(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count  FROM report as a WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetReportListV1
+// @Description: 获取普通报告列表的数据
+// @author: Roc
+// @datetime 2024-05-30 15:14:25
+// @param condition string
+// @param pars []interface{}
+// @param startSize int
+// @param pageSize int
+// @return items []*ReportList
+// @return err error
+func GetReportListV1(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+
+	sql := `SELECT * FROM report as a WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type ReportPvUv struct {
+	ReportId int
+	PvTotal  int
+	UvTotal  int
+}
+
+func GetReportPvUvByReportIdList(reportIdList []string) (items []ReportPvUv, err error) {
+	num := len(reportIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+
+	sql := `SELECT report_id, COUNT(1) as pv_total,COUNT(DISTINCT user_id) as uv_total  FROM report_view_record  WHERE report_id in (` + utils.GetOrmInReplace(num) + `) GROUP BY report_id`
+	_, err = o.Raw(sql, reportIdList).QueryRows(&items)
+	return
+}
+
+// GetReportListCountByGrant
+// @Description: 获取共享报告列表的报告数量
+// @author: Roc
+// @datetime 2024-05-30 15:14:01
+// @param condition string
+// @param pars []interface{}
+// @return count int
+// @return err error
+func GetReportListCountByGrant(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT a.id  FROM report as a 
+    JOIN report_grant b on a.id=b.report_id 
+ WHERE 1=1  `
+	if condition != "" {
+		sql += condition
 	}
+	sql += " GROUP BY a.id "
 
+	sql = `SELECT COUNT(1) AS count  FROM (` + sql + `) d`
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetReportListByGrant
+// @Description: 获取共享报告列表的数据
+// @author: Roc
+// @datetime 2024-05-30 15:15:07
+// @param condition string
+// @param pars []interface{}
+// @param startSize int
+// @param pageSize int
+// @return items []*ReportList
+// @return err error
+func GetReportListByGrant(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+
+	sql := `SELECT * FROM report as a JOIN report_grant b on a.id = b.report_id  WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += ` GROUP BY a.id ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetReportListCount(condition string, pars []interface{}) (count int, err error) {
 	oRddp := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT COUNT(1) AS count  FROM report WHERE 1=1 ` + companyTypeSqlStr
+	sql := `SELECT COUNT(1) AS count  FROM report WHERE 1=1 `
 	if condition != "" {
 		sql += condition
 	}
@@ -131,20 +269,13 @@ func GetReportListCount(condition string, pars []interface{}, companyType string
 	return
 }
 
-func GetReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int) (items []*ReportList, err error) {
+func GetReportList(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	//产品权限
-	companyTypeSqlStr := ``
-	if companyType == "ficc" {
-		companyTypeSqlStr = " AND classify_id_first != 40 "
-	} else if companyType == "权益" {
-		companyTypeSqlStr = " AND classify_id_first = 40 "
-	}
 
 	sql := `SELECT *,
         (SELECT COUNT(1) FROM report_view_record AS rvr WHERE rvr.report_id=report.id) AS pv,
         (SELECT COUNT(DISTINCT user_id) FROM report_view_record AS rvr WHERE rvr.report_id=report.id) AS uv
-        FROM report WHERE 1=1  ` + companyTypeSqlStr
+        FROM report WHERE 1=1  `
 	if condition != "" {
 		sql += condition
 	}
@@ -166,28 +297,15 @@ func PublishReport(reportIds []int) (err error) {
 }
 
 // PublishCancelReport 取消发布报告
-func PublishCancelReport(reportId, state int, publishTimeNullFlag bool) (err error) {
+func PublishCancelReport(reportId, state int, publishTimeNullFlag bool, lastModifyAdminId int, lastModifyAdminName string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	var sql string
 	if publishTimeNullFlag {
-		sql = ` UPDATE report SET state=?, publish_time=null, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
+		sql = ` UPDATE report SET state=?, publish_time=null, pre_publish_time=null, pre_msg_send=0,last_modify_admin_id=?,last_modify_admin_name=?,modify_time = NOW() WHERE id =?`
 	} else {
-		sql = ` UPDATE report SET state=?, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
+		sql = ` UPDATE report SET state=?, pre_publish_time=null, pre_msg_send=0,last_modify_admin_id=?,last_modify_admin_name=?,modify_time = NOW() WHERE id =?`
 	}
-	_, err = o.Raw(sql, state, reportId).Exec()
-	return
-}
-
-// 取消发布报告
-func PublishCancleReport(reportIds int, publishTimeNullFlag bool) (err error) {
-	o := orm.NewOrmUsingDB("rddp")
-	var sql string
-	if publishTimeNullFlag {
-		sql = ` UPDATE report SET state=1, publish_time=null, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
-	} else {
-		sql = ` UPDATE report SET state=1, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
-	}
-	_, err = o.Raw(sql, reportIds).Exec()
+	_, err = o.Raw(sql, state, reportId, lastModifyAdminId, lastModifyAdminName).Exec()
 	return
 }
 
@@ -226,7 +344,31 @@ type ReportDetail struct {
 	ThsMsgIsSend       int    `description:"客户群消息是否已发送,0:否,1:是"`
 	HasChapter         int    `description:"是否有章节 0-否 1-是"`
 	ChapterType        string `description:"章节类型 day-晨报 week-周报"`
-	ReportCode         string `description:"报告code"`
+	AdminId            int    `description:"创建者账号"`
+	AdminRealName      string `description:"创建者姓名"`
+	ReportCode         string `description:"报告唯一编码"`
+
+	// eta1.8.3(研报改版)相关内容
+	ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	Pv                  int       `description:"pv"`
+	Uv                  int       `description:"uv"`
+	HeadImg             string    `description:"报告头图地址"`
+	EndImg              string    `description:"报告尾图地址"`
+	HeadStyle           string    `description:"版头样式"`
+	EndStyle            string    `description:"版尾样式"`
+	CanvasColor         string    `description:"画布颜色"`
+	NeedSplice          int       `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId      int       `description:"版头资源ID"`
+	EndResourceId       int       `description:"版尾资源ID"`
+	ClassifyIdThird     int       `description:"三级分类id"`
+	ClassifyNameThird   string    `description:"三级分类名称"`
+	CollaborateType     int8      `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
+	ReportCreateTime    time.Time `description:"报告时间创建时间"`
 }
 
 func GetReportById(reportId int) (item *ReportDetail, err error) {
@@ -244,39 +386,89 @@ func GetReportByIds(reportIds string) (list []*ReportDetail, err error) {
 }
 
 // GetSimpleReportByIds 根据报告ID查询报告基本信息
-func GetSimpleReportByIds(reportIds []int) (list []*ReportDetail, err error) {
+func GetSimpleReportByIds(reportIds []int) (list []*Report, err error) {
 	if len(reportIds) == 0 {
 		return
 	}
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT id, title FROM report WHERE id IN (` + utils.GetOrmInReplace(len(reportIds)) + `)`
+	sql := `SELECT id, title, report_code FROM report WHERE id IN (` + utils.GetOrmInReplace(len(reportIds)) + `)`
 	_, err = o.Raw(sql, reportIds).QueryRows(&list)
 	return
 }
 
-func GetReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
+// GetReportStage
+// @Description: 获取报告的最大期数(每一年的最大期数)
+// @author: Roc
+// @datetime 2024-06-03 17:44:14
+// @param classifyIdFirst int
+// @param classifyIdSecond int
+// @param classifyIdThird int
+// @return count int
+// @return err error
+func GetReportStage(classifyIdFirst, classifyIdSecond, classifyIdThird int) (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ``
-	if classifyIdSecond > 0 {
-		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_second=? "
-		o.Raw(sql, classifyIdSecond).QueryRow(&count)
+	classifyId := classifyIdThird
+	if classifyId <= 0 {
+		classifyId = classifyIdSecond
+	}
+	if classifyId <= 0 {
+		classifyId = classifyIdFirst
+	}
+	if classifyId <= 0 {
+		err = errors.New("错误的分类id")
+		return
+	}
+	yearStart := time.Date(time.Now().Local().Year(), 1, 1, 0, 0, 0, 0, time.Local)
+
+	sql := `SELECT MAX(stage) AS max_stage FROM report WHERE create_time > ? `
+	if classifyIdThird > 0 {
+		sql += " AND classify_id_third = ? "
+	} else if classifyIdSecond > 0 {
+		sql += " AND classify_id_second = ? "
 	} else {
-		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first=? "
-		o.Raw(sql, classifyIdFirst).QueryRow(&count)
+		sql = " AND classify_id_first = ? "
 	}
+	o.Raw(sql, yearStart, classifyId).QueryRow(&count)
+
 	return
 }
 
-func GetReportStageEdit(classifyIdFirst, classifyIdSecond, reportId int) (count int, err error) {
-	o := orm.NewOrmUsingDB("rddp")
-	sql := ``
-	if classifyIdSecond > 0 {
-		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_second=? AND id<>? "
-		o.Raw(sql, classifyIdSecond, reportId).QueryRow(&count)
+// GetReportStageEdit
+// @Description: 获取报告的最大期数(每一年的最大期数)
+// @author: Roc
+// @datetime 2024-06-04 13:50:34
+// @param classifyIdFirst int
+// @param classifyIdSecond int
+// @param classifyIdThird int
+// @param reportId int
+// @return count int
+// @return err error
+func GetReportStageEdit(classifyIdFirst, classifyIdSecond, classifyIdThird, reportId int) (count int, err error) {
+	classifyId := classifyIdThird
+	if classifyId <= 0 {
+		classifyId = classifyIdSecond
+	}
+	if classifyId <= 0 {
+		classifyId = classifyIdFirst
+	}
+	if classifyId <= 0 {
+		err = errors.New("错误的分类id")
+		return
+	}
+	yearStart := time.Date(time.Now().Local().Year(), 1, 1, 0, 0, 0, 0, time.Local)
+
+	sql := `SELECT MAX(stage) AS max_stage FROM report WHERE create_time > ? AND id<>?  `
+	if classifyIdThird > 0 {
+		sql += " AND classify_id_third = ? "
+	} else if classifyIdSecond > 0 {
+		sql += " AND classify_id_second = ? "
 	} else {
-		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first=? AND id<>? "
-		o.Raw(sql, classifyIdFirst, reportId).QueryRow(&count)
+		sql = " AND classify_id_first = ? "
 	}
+
+	o := orm.NewOrmUsingDB("rddp")
+	o.Raw(sql, yearStart, reportId, classifyId).QueryRow(&count)
+
 	return
 }
 
@@ -299,6 +491,8 @@ type AddReq struct {
 	ClassifyNameFirst  string `description:"一级分类名称"`
 	ClassifyIdSecond   int    `description:"二级分类id"`
 	ClassifyNameSecond string `description:"二级分类名称"`
+	ClassifyIdThird    int    `description:"三级分类id"`
+	ClassifyNameThird  string `description:"三级分类名称"`
 	Title              string `description:"标题"`
 	Abstract           string `description:"摘要"`
 	Author             string `description:"作者"`
@@ -307,6 +501,18 @@ type AddReq struct {
 	Content            string `description:"内容"`
 	CreateTime         string `description:"创建时间"`
 	ReportVersion      int    `description:"1:旧版,2:新版"`
+	ContentStruct      string `description:"内容组件"`
+	HeadImg            string `description:"报告头图地址"`
+	EndImg             string `description:"报告尾图地址"`
+	CanvasColor        string `description:"画布颜色"`
+	NeedSplice         int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId     int    `description:"版头资源ID"`
+	EndResourceId      int    `description:"版尾资源ID"`
+	CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish    int8   `description:"是否公开发布,1:是,2:否"`
+	InheritReportId    int    `description:"待继承的报告ID"`
+	GrantAdminIdList   []int  `description:"授权用户id列表"`
 }
 
 type PrePublishReq struct {
@@ -333,6 +539,8 @@ type EditReq struct {
 	ClassifyNameFirst  string `description:"一级分类名称"`
 	ClassifyIdSecond   int    `description:"二级分类id"`
 	ClassifyNameSecond string `description:"二级分类名称"`
+	ClassifyIdThird    int    `description:"三级分类id"`
+	ClassifyNameThird  string `description:"三级分类名称"`
 	Title              string `description:"标题"`
 	Abstract           string `description:"摘要"`
 	Author             string `description:"作者"`
@@ -340,6 +548,17 @@ type EditReq struct {
 	State              int    `description:"状态:1:未发布,2:已发布"`
 	Content            string `description:"内容"`
 	CreateTime         string `description:"创建时间"`
+	ContentStruct      string `description:"内容组件"`
+	HeadImg            string `description:"报告头图地址"`
+	EndImg             string `description:"报告尾图地址"`
+	CanvasColor        string `description:"画布颜色"`
+	NeedSplice         int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId     int    `description:"版头资源ID"`
+	EndResourceId      int    `description:"版尾资源ID"`
+	//CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	//ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish  int8  `description:"是否公开发布,1:是,2:否"`
+	GrantAdminIdList []int `description:"授权用户id列表"`
 }
 
 type EditResp struct {
@@ -371,6 +590,12 @@ func EditReport(item *Report, reportId int64) (err error) {
 	return
 }
 
+func (m *Report) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
 type ReportDetailReq struct {
 	ReportId int `description:"报告id"`
 }
@@ -452,6 +677,15 @@ type SaveReportContent struct {
 	Content  string `description:"内容"`
 	ReportId int    `description:"报告id"`
 	NoChange int    `description:"内容是否未改变:1:内容未改变"`
+
+	// 以下是智能研报相关
+	ContentStruct  string `description:"内容组件"`
+	HeadImg        string `description:"报告头图地址"`
+	EndImg         string `description:"报告尾图地址"`
+	CanvasColor    string `description:"画布颜色"`
+	NeedSplice     int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId int    `description:"版头资源ID"`
+	EndResourceId  int    `description:"版尾资源ID"`
 }
 
 func EditReportContent(reportId int, content, contentSub string) (err error) {
@@ -461,16 +695,16 @@ func EditReportContent(reportId int, content, contentSub string) (err error) {
 	return
 }
 
-func AddReportSaveLog(reportId, adminId int, content, contentSub, adminName string) (err error) {
+func AddReportSaveLog(reportId, adminId int, content, contentSub, contentStruct, canvasColor, adminName string, headResourceId, endResourceId int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` INSERT INTO report_save_log(report_id, content,content_sub,admin_id,admin_name) VALUES (?,?,?,?,?) `
-	_, err = o.Raw(sql, reportId, content, contentSub, adminId, adminName).Exec()
+	sql := ` INSERT INTO report_save_log(report_id, content,content_sub,content_struct,canvas_color,head_resource_id,end_resource_id,admin_id,admin_name) VALUES (?,?,?,?,?) `
+	_, err = o.Raw(sql, reportId, content, contentSub, contentStruct, canvasColor, headResourceId, endResourceId, adminId, adminName).Exec()
 	return
 }
 
 func MultiAddReportChaptersSaveLog(items []*ReportChapter, adminId int, adminRealName string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	p, err := o.Raw(`INSERT INTO report_save_log(report_id, report_chapter_id, content, content_sub, admin_id, admin_name) VALUES (?,?,?,?,?,?)`).Prepare()
+	p, err := o.Raw(`INSERT INTO report_save_log(report_id, report_chapter_id, content, content_sub,content_struct,admin_id, admin_name) VALUES (?,?,?,?,?,?,?)`).Prepare()
 	if err != nil {
 		return
 	}
@@ -478,7 +712,7 @@ func MultiAddReportChaptersSaveLog(items []*ReportChapter, adminId int, adminRea
 		_ = p.Close()
 	}()
 	for _, v := range items {
-		_, err = p.Exec(v.ReportId, v.ReportChapterId, v.Content, v.ContentSub, adminId, adminRealName)
+		_, err = p.Exec(v.ReportId, v.ReportChapterId, v.Content, v.ContentSub, v.ContentStruct, adminId, adminRealName)
 		if err != nil {
 			return
 		}
@@ -532,38 +766,6 @@ func GetDayWeekReportStage(classifyIdFirst int, yearStart time.Time) (count int,
 	return
 }
 
-// AddReportAndChapter 新增报告及章节
-func AddReportAndChapter(reportItem *Report, chapterItemList []*ReportChapter) (reportId int64, err error) {
-	o := orm.NewOrmUsingDB("rddp")
-	to, err := o.Begin()
-	if err != nil {
-		return
-	}
-	defer func() {
-		if err != nil {
-			_ = to.Rollback()
-		} else {
-			_ = to.Commit()
-		}
-	}()
-
-	if reportId, err = to.Insert(reportItem); err != nil {
-		return
-	}
-	if len(chapterItemList) > 0 {
-		for _, chapterItem := range chapterItemList {
-			chapterItem.ReportId = int(reportId)
-			cpId, tmpErr := to.Insert(chapterItem)
-			if tmpErr != nil {
-				return
-			}
-			chapterItem.ReportChapterId = int(cpId)
-		}
-	}
-
-	return
-}
-
 // GetReportByReportId 主键获取报告
 func GetReportByReportId(reportId int) (item *Report, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -615,10 +817,27 @@ func (reportInfo *Report) UpdateReport(cols []string) (err error) {
 	return
 }
 
-// 晨周报详情
+// ReportDetailView
+// @Description: 晨周报详情
 type ReportDetailView struct {
 	*ReportDetail
-	ChapterList []*ReportChapter
+	ChapterList    []*ReportChapter
+	GrandAdminList []ReportDetailViewAdmin
+	PermissionList []ReportDetailViewPermission
+}
+
+// ReportDetailViewAdmin
+// @Description: 报告里面的授权人
+type ReportDetailViewAdmin struct {
+	AdminId   int
+	AdminName string
+}
+
+// ReportDetailViewPermission
+// @Description: 报告分类关联的品种权限
+type ReportDetailViewPermission struct {
+	PermissionId   int
+	PermissionName string
 }
 
 func GetUnPublishDayReport(startTime time.Time, endTime time.Time) (item *Report, err error) {
@@ -654,6 +873,8 @@ type ElasticReportDetail struct {
 	ClassifyNameFirst  string `description:"一级分类名称"`
 	ClassifyIdSecond   int    `description:"二级分类ID"`
 	ClassifyNameSecond string `description:"二级分类名称"`
+	ClassifyId         int    `description:"最小单元的分类ID"`
+	ClassifyName       string `description:"最小单元的分类名称"`
 	Categories         string `description:"关联的品种名称(包括品种别名)"`
 	StageStr           string `description:"报告期数"`
 }
@@ -677,7 +898,7 @@ func GetNewReportExist(oldReportId int) (item *Report, err error) {
 }
 
 // PublishReportAndChapter 发布报告及章节
-func PublishReportAndChapter(reportInfo *Report, publishIds, unPublishIds []int, isPublishReport bool, cols []string) (err error) {
+func PublishReportAndChapter(reportInfo *Report, isPublishReport bool, cols []string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	to, err := o.Begin()
 	if err != nil {
@@ -692,19 +913,27 @@ func PublishReportAndChapter(reportInfo *Report, publishIds, unPublishIds []int,
 	}()
 	// 更新报告
 	if isPublishReport {
-		if _, err = to.Update(reportInfo, cols...); err != nil {
+		_, err = to.Update(reportInfo, cols...)
+		if err != nil {
 			return
 		}
 	}
+
+	// 发布该报告的所有章节
+	sql := ` UPDATE report_chapter SET publish_state = 2, publish_time = ? WHERE report_id = ?  `
+	_, err = to.Raw(sql, reportInfo.PublishTime, reportInfo.Id).Exec()
+
 	// 发布章节
-	if len(publishIds) > 0 {
-		sql := ` UPDATE report_chapter SET publish_state = 2, publish_time = ? WHERE report_id = ? AND report_chapter_id IN (` + utils.GetOrmInReplace(len(publishIds)) + `) `
-		_, err = to.Raw(sql, reportInfo.PublishTime, reportInfo.Id, publishIds).Exec()
-	}
-	if len(unPublishIds) > 0 {
-		sql := ` UPDATE report_chapter SET publish_state = 1, publish_time = NULL, is_edit = 0 WHERE report_id = ? AND report_chapter_id IN (` + utils.GetOrmInReplace(len(unPublishIds)) + `) `
-		_, err = to.Raw(sql, reportInfo.Id, unPublishIds).Exec()
-	}
+	//if len(publishIds) > 0 {
+	//	sql := ` UPDATE report_chapter SET publish_state = 2, publish_time = ? WHERE report_id = ? AND report_chapter_id IN (` + utils.GetOrmInReplace(len(publishIds)) + `) `
+	//	_, err = to.Raw(sql, reportInfo.PublishTime, reportInfo.Id, publishIds).Exec()
+	//}
+
+	//if len(unPublishIds) > 0 {
+	//	sql := ` UPDATE report_chapter SET publish_state = 1, publish_time = NULL, is_edit = 0 WHERE report_id = ? AND report_chapter_id IN (` + utils.GetOrmInReplace(len(unPublishIds)) + `) `
+	//	_, err = to.Raw(sql, reportInfo.Id, unPublishIds).Exec()
+	//}
+
 	return
 }
 
@@ -718,18 +947,18 @@ SELECT DISTINCT report_id FROM report_chapter WHERE publish_state = 2 AND (video
 }
 
 // PublishReportById 发布报告
-func PublishReportById(reportId int, publishTime time.Time) (err error) {
+func PublishReportById(reportId int, publishTime time.Time, lastModifyAdminId int, lastModifyAdminName string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE report SET state = 2, publish_time = ?, pre_publish_time=null, pre_msg_send=0, modify_time = NOW() WHERE id = ? `
-	_, err = o.Raw(sql, publishTime, reportId).Exec()
+	sql := `UPDATE report SET state = 2, publish_time = ?, pre_publish_time=null, pre_msg_send=0, modify_time = NOW(),last_modify_admin_id=?,last_modify_admin_name=? WHERE id = ? `
+	_, err = o.Raw(sql, publishTime, lastModifyAdminId, lastModifyAdminName, reportId).Exec()
 	return
 }
 
 // ResetReportById 重置报告状态
-func ResetReportById(reportId, state int) (err error) {
+func ResetReportById(reportId, state int, lastModifyAdminId int, lastModifyAdminName string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE report SET state = ?, pre_publish_time = null, pre_msg_send = 0, modify_time = NOW() WHERE id = ?`
-	_, err = o.Raw(sql, state, reportId).Exec()
+	sql := `UPDATE report SET state = ?, pre_publish_time = null, pre_msg_send = 0, modify_time = NOW(),last_modify_admin_id=?,last_modify_admin_name=? WHERE id = ?`
+	_, err = o.Raw(sql, state, lastModifyAdminId, lastModifyAdminName, reportId).Exec()
 	return
 }
 
@@ -984,6 +1213,14 @@ func UpdateReportFirstClassifyNameByClassifyId(classifyId int, classifyName stri
 	return
 }
 
+// UpdateReportThirdClassifyNameByClassifyId 更新报告的三级分类名称字段
+func UpdateReportThirdClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE report SET classify_name_third = ? WHERE classify_id_third = ? "
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
 // UpdateReportSecondClassifyFirstNameByClassifyId 更新报告二级分类的一级分类名称和id
 func UpdateReportSecondClassifyFirstNameByClassifyId(classifyId, newClassifyId int, classifyName string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -1047,8 +1284,9 @@ func UpdateReportChapterPublishTime(reportId int, videoNameDate string) (err err
 
 // MarkEditReport 标记编辑英文研报的请求数据
 type MarkEditReport struct {
-	ReportId int `description:"研报id"`
-	Status   int `description:"标记状态,1:编辑中,2:编辑完成"`
+	ReportId        int `description:"研报id"`
+	ReportChapterId int `description:"研报章节id"`
+	Status          int `description:"标记状态,1:编辑中,2:查询状态,3:编辑完成"`
 }
 
 type MarkReportResp struct {
@@ -1128,7 +1366,7 @@ func GetReportStateCount(state int) (count int, err error) {
 }
 
 // UpdateReportsStateByCond 批量更新报告状态
-func UpdateReportsStateByCond(classifyFirstId, classifySecondId, oldState, newState int) (err error) {
+func UpdateReportsStateByCond(classifyFirstId, classifySecondId, classifyThirdId, oldState, newState int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	cond := ``
 	if classifyFirstId > 0 {
@@ -1137,6 +1375,9 @@ func UpdateReportsStateByCond(classifyFirstId, classifySecondId, oldState, newSt
 	if classifySecondId > 0 {
 		cond += fmt.Sprintf(` AND classify_id_second = %d`, classifySecondId)
 	}
+	if classifyThirdId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_third = %d`, classifyThirdId)
+	}
 	sql := fmt.Sprintf(`UPDATE report SET state = ?, pre_publish_time = NULL WHERE state = ? %s`, cond)
 	_, err = o.Raw(sql, newState, oldState).Exec()
 	return
@@ -1160,6 +1401,13 @@ func UpdateReportsStateBySecondIds(oldState, newState int, secondIds []int) (err
 	return
 }
 
+// GetReportPdfUrlReq 获取报告pdf地址请求体
+type GetReportPdfUrlReq struct {
+	ReportUrl  string `description:"报告Url"`
+	ReportCode string `description:"报告Code"`
+	Type       int    `description:"类型 1-pdf 2-图片"`
+}
+
 func ModifyReportPdfUrl(reportId int, detailPdfUrl string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE report SET detail_pdf_url=? WHERE id=? `
@@ -1174,11 +1422,23 @@ func ModifyReportImgUrl(reportId int, detailImgUrl string) (err error) {
 	return
 }
 
-
 // UpdatePdfUrlReportById 清空pdf相关字段
 func UpdatePdfUrlReportById(reportId int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE id = ? `
 	_, err = o.Raw(sql, reportId).Exec()
 	return
-}
+}
+
+// InsertMultiReport
+// @Description: 批量新增报告
+// @author: Roc
+// @datetime 2024-06-27 15:55:25
+// @param items []*Report
+// @return err error
+func InsertMultiReport(items []*Report) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(500, items)
+
+	return
+}

+ 110 - 0
models/report/report_chapter_grant.go

@@ -0,0 +1,110 @@
+package report
+
+import (
+	"eta/eta_mobile/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ReportChapterGrant
+// @Description: 报告章节授权用户表
+type ReportChapterGrant struct {
+	GrantId         int       `orm:"column(grant_id)"` // 授权id
+	ReportChapterId int       `description:"报告章节id"`
+	AdminId         int       `description:"授权的用户id"`
+	CreateTime      time.Time `description:"授权时间"`
+}
+
+// MultiAddReportChapterGrantGrant
+// @Description: 批量添加报告授权用户
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:36:02
+// @param reportChapterId int
+// @param list []*ReportChapterGrant
+// @return err error
+func (m ReportChapterGrant) MultiAddReportChapterGrantGrant(reportChapterId int, list []*ReportChapterGrant) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := "DELETE from report_chapter_grant where report_chapter_id=?"
+	_, err = to.Raw(sql, reportChapterId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增授权记录
+	if len(list) > 0 {
+		_, tmpErr := to.InsertMulti(500, list)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}
+
+// GetGrantListById
+// @Description: 根据id获取授权列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportChapterId int
+// @return list []*ReportChapterGrant
+// @return err error
+func (m ReportChapterGrant) GetGrantListById(reportChapterId int) (list []*ReportChapterGrant, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_chapter_grant WHERE report_chapter_id=? `
+	_, err = o.Raw(sql, reportChapterId).QueryRows(&list)
+
+	return
+}
+
+// GetGrantListByIdList
+// @Description: 根据id列表获取授权列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportChapterIdList []int
+// @return list []*ReportChapterGrant
+// @return err error
+func (m ReportChapterGrant) GetGrantListByIdList(reportChapterIdList []int) (list []*ReportChapterGrant, err error) {
+	num := len(reportChapterIdList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_chapter_grant WHERE report_chapter_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, reportChapterIdList).QueryRows(&list)
+
+	return
+}
+
+// GetGrantByIdAndAdmin
+// @Description: 根据reportId和操作人获取报告章节权限配置
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:49:59
+// @param reportChapterId int
+// @param sysUserId int
+// @return item *ReportGrant
+// @return err error
+func (m ReportChapterGrant) GetGrantByIdAndAdmin(reportChapterId, sysUserId int) (item *ReportGrant, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_chapter_grant WHERE report_chapter_id = ? AND admin_id = ? `
+	err = o.Raw(sql, reportChapterId, sysUserId).QueryRow(&item)
+
+	return
+}

+ 152 - 0
models/report/report_chapter_permission_mapping.go

@@ -0,0 +1,152 @@
+package report
+
+import (
+	"eta/eta_mobile/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ReportChapterPermissionMapping
+// @Description: 报告章节的权限关系表
+type ReportChapterPermissionMapping struct {
+	ReportChapterPermissionMappingId int `orm:"column(report_chapter_permission_mapping_id)"`
+	ReportChapterId                  int `description:"报告章节的id"` // 报告章节的id
+	ChartPermissionId                int `description:"权限id"`    // 权限id
+	CreateTime                       time.Time
+}
+
+// MultiAddReportChapterPermissionMappingPermission
+// @Description: 批量添加报告品种权限用户
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:36:02
+// @param reportChapterId int
+// @param list []*ReportChapterPermissionMapping
+// @return err error
+func (m ReportChapterPermissionMapping) MultiAddReportChapterPermissionMappingPermission(reportChapterId int, list []*ReportChapterPermissionMapping) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := "DELETE from report_chapter_permission_mapping where report_chapter_id=?"
+	_, err = to.Raw(sql, reportChapterId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增品种权限记录
+	if len(list) > 0 {
+		_, tmpErr := to.InsertMulti(500, list)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}
+
+// GetPermissionListById
+// @Description: 根据id获取品种权限列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportChapterId int
+// @return list []*ReportChapterPermissionMapping
+// @return err error
+func (m ReportChapterPermissionMapping) GetPermissionListById(reportChapterId int) (list []*ReportChapterPermissionMapping, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_chapter_permission_mapping WHERE report_chapter_id=? `
+	_, err = o.Raw(sql, reportChapterId).QueryRows(&list)
+
+	return
+}
+
+// ReportChapterPermissionItem
+// @Description: 报告章节的权限关系表(带有品种名称)
+type ReportChapterPermissionItem struct {
+	ReportChapterPermissionMappingId int    `orm:"column(report_chapter_permission_mapping_id)"`
+	ReportChapterId                  int    `description:"报告章节的id"`
+	ChartPermissionId                int    `description:"权限id"`
+	ChartPermissionName              string `description:"品种名称"`
+	CreateTime                       time.Time
+}
+
+// GetPermissionItemListById
+// @Description: 根据id获取品种权限列表(带有品种名称)
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportChapterId int
+// @return list []*ReportChapterPermissionMapping
+// @return err error
+func (m ReportChapterPermissionMapping) GetPermissionItemListById(reportChapterId int) (list []*ReportChapterPermissionItem, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT a.*,b.chart_permission_name FROM report_chapter_permission_mapping AS a 
+         JOIN chart_permission AS b on a.chart_permission_id=b.chart_permission_id WHERE report_chapter_id=? `
+	_, err = o.Raw(sql, reportChapterId).QueryRows(&list)
+
+	return
+}
+
+// GetPermissionListByIdList
+// @Description: 根据id列表获取品种权限列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportChapterIdList []int
+// @return list []*ReportChapterPermissionMapping
+// @return err error
+func (m ReportChapterPermissionMapping) GetPermissionListByIdList(reportChapterIdList []int) (list []*ReportChapterPermissionMapping, err error) {
+	num := len(reportChapterIdList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_chapter_permission_mapping WHERE report_chapter_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, reportChapterIdList).QueryRows(&list)
+
+	return
+}
+
+// MultiAdd
+// @Description: 批量添加
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-20 18:04:33
+// @param list []*ReportChapterPermissionMapping
+// @return err error
+func (m ReportChapterPermissionMapping) MultiAdd(list []*ReportChapterPermissionMapping) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 新增品种权限记录
+	if len(list) > 0 {
+		_, err = to.InsertMulti(500, list)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 126 - 0
models/report/report_grant.go

@@ -0,0 +1,126 @@
+package report
+
+import (
+	"eta/eta_mobile/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ReportGrant
+// @Description: 报告授权用户表
+type ReportGrant struct {
+	GrantId    int       `orm:"column(grant_id)"` // 授权id
+	ReportId   int       `description:"报告id"`
+	AdminId    int       `description:"授权的用户id"`
+	CreateTime time.Time `description:"授权时间"`
+}
+
+// MultiAddReportGrantGrant
+// @Description: 批量添加报告授权用户
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:36:02
+// @param reportId int
+// @param list []*ReportGrant
+// @return err error
+func (m ReportGrant) MultiAddReportGrantGrant(reportId int, list []*ReportGrant) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := "DELETE from report_grant where report_id=?"
+	_, err = to.Raw(sql, reportId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增授权记录
+	if len(list) > 0 {
+		_, tmpErr := to.InsertMulti(500, list)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}
+
+// GetGrantListById
+// @Description: 根据id获取授权列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportId int
+// @return list []*ReportGrant
+// @return err error
+func (m ReportGrant) GetGrantListById(reportId int) (list []*ReportGrant, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_grant WHERE report_id=? `
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+// GetGrantListByIdList
+// @Description: 根据id列表获取授权列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param reportIdList []int
+// @return list []*ReportGrant
+// @return err error
+func (m ReportGrant) GetGrantListByIdList(reportIdList []int) (list []*ReportGrant, err error) {
+	num := len(reportIdList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_grant WHERE report_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, reportIdList).QueryRows(&list)
+
+	return
+}
+
+// GetGrantByIdAndAdmin
+// @Description: 根据reportId和操作人获取报告权限配置
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:49:59
+// @param reportId int
+// @param sysUserId int
+// @return item *ReportGrant
+// @return err error
+func (m ReportGrant) GetGrantByIdAndAdmin(reportId, sysUserId int) (item *ReportGrant, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_grant WHERE report_id = ? AND admin_id = ? `
+	err = o.Raw(sql, reportId, sysUserId).QueryRow(&item)
+
+	return
+}
+
+// GetGrantListByAdminId
+// @Description: 根据id获取授权列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-04 15:33:58
+// @param adminId int
+// @return list []*ReportGrant
+// @return err error
+func (m ReportGrant) GetGrantListByAdminId(adminId int) (list []*ReportGrant, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_grant WHERE admin_id=? `
+	_, err = o.Raw(sql, adminId).QueryRows(&list)
+
+	return
+}

+ 13 - 4
models/report_approve/report_approve.go

@@ -17,6 +17,7 @@ type ReportApprove struct {
 	ReportTitle      string    `description:"报告标题"`
 	ClassifyFirstId  int       `description:"一级分类ID"`
 	ClassifySecondId int       `description:"二级分类ID"`
+	ClassifyThirdId  int       `description:"三级分类ID"`
 	State            int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
 	FlowId           int       `description:"审批流ID"`
 	FlowVersion      int       `description:"审批流版本"`
@@ -37,6 +38,7 @@ var ReportApproveCols = struct {
 	ReportTitle      string `description:"报告标题"`
 	ClassifyFirstId  string `description:"一级分类ID"`
 	ClassifySecondId string `description:"二级分类ID"`
+	ClassifyThirdId  string `description:"三级分类ID"`
 	State            string
 	FlowId           string
 	FlowVersion      string
@@ -55,6 +57,7 @@ var ReportApproveCols = struct {
 	ReportTitle:      "report_title",
 	ClassifyFirstId:  "classify_first_id",
 	ClassifySecondId: "classify_second_id",
+	ClassifyThirdId:  "classify_third_id",
 	State:            "state",
 	FlowId:           "flow_id",
 	FlowVersion:      "flow_version",
@@ -258,6 +261,7 @@ type ReportApproveItemOrm struct {
 	ReportTitle           string    `description:"报告标题"`
 	ClassifyFirstId       int       `description:"一级分类ID"`
 	ClassifySecondId      int       `description:"二级分类ID"`
+	ClassifyThirdId       int       `description:"二级分类ID"`
 	State                 int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
 	RecordState           int       `description:"审批记录状态:1-待审批;2-已通过;3-已驳回"`
 	FlowId                int       `description:"审批流ID"`
@@ -271,6 +275,8 @@ type ReportApproveItemOrm struct {
 	HandleTime            time.Time `description:"处理时间"`
 	CreateTime            time.Time `description:"创建时间"`
 	ModifyTime            time.Time `description:"修改时间"`
+	NodeState             int       `description:"当前节点审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回" json:"-"`
+	NodeApproveTime       time.Time `description:"当前节点审批时间" json:"-"`
 }
 
 // GetApprovingReportApproveCount 获取待处理的审批分页列表总数
@@ -278,7 +284,7 @@ func GetApprovingReportApproveCount(cond string, pars []interface{}) (count int,
 	o := orm.NewOrmUsingDB("rddp")
 	base := fmt.Sprintf(`SELECT a.report_approve_record_id
 		FROM report_approve_record AS a
-		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id AND a.node_id = b.curr_node_id
 		WHERE 1 = 1 %s`, cond)
 	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
 	err = o.Raw(sql, pars).QueryRow(&count)
@@ -294,7 +300,7 @@ func GetApprovingReportApprovePageList(cond string, pars []interface{}, orderRul
 	}
 	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, b.*
 		FROM report_approve_record AS a
-		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id AND a.node_id = b.curr_node_id
 		WHERE 1 = 1 %s %s
 		LIMIT ?,?`, cond, order)
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
@@ -320,7 +326,7 @@ func GetApprovedReportApprovePageList(cond string, pars []interface{}, orderRule
 	if orderRule != "" {
 		order = ` ORDER BY ` + orderRule
 	}
-	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, a.approve_time AS handle_time, b.*
+	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state,a.node_state,a.node_approve_time, a.approve_time AS handle_time, b.*
 		FROM report_approve_record AS a
 		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
 		WHERE 1 = 1 %s %s
@@ -378,6 +384,7 @@ type ReportApproveDetailReport struct {
 	ReportType     int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ReportId       int    `description:"报告ID"`
 	ReportTitle    string `description:"报告标题"`
+	ReportCode     string `description:"报告code"`
 	ReportClassify string `description:"报告分类"`
 	//ClassifyFirstId  int    `description:"一级分类ID"`
 	//ClassifySecondId int    `description:"二级分类ID"`
@@ -426,7 +433,8 @@ func (m *ReportApprove) CreateApproveAndRecord(approveItem *ReportApprove, recor
 
 // ReportApprovePassReq 审批通过请求体
 type ReportApprovePassReq struct {
-	ReportApproveId int `description:"审批ID"`
+	ReportApproveId int    `description:"审批ID"`
+	ReportUrl       string `description:"报告URL"`
 }
 
 // ReportApproveRefuseReq 审批驳回请求体
@@ -445,4 +453,5 @@ type ReportApproveCheckApproveOpenReq struct {
 	ReportType       int `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ClassifyFirstId  int `description:"一级分类ID"`
 	ClassifySecondId int `description:"二级分类ID"`
+	ClassifyThirdId  int `description:"三级分类ID"`
 }

+ 11 - 1
models/report_approve/report_approve_flow.go

@@ -16,9 +16,11 @@ type ReportApproveFlow struct {
 	ReportType          int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ClassifyFirstId     int       `description:"一级分类ID"`
 	ClassifySecondId    int       `description:"二级分类ID"`
+	ClassifyThirdId     int       `description:"三级分类ID"`
 	CurrVersion         int       `description:"当前版本号"`
 	CreateTime          time.Time `description:"创建时间"`
 	ModifyTime          time.Time `description:"修改时间"`
+	Enabled             int       `description:"1:有效,0:禁用"`
 }
 
 var ReportApproveFlowCols = struct {
@@ -27,18 +29,22 @@ var ReportApproveFlowCols = struct {
 	ReportType          string
 	ClassifyFirstId     string
 	ClassifySecondId    string
+	ClassifyThirdId     string
 	CurrVersion         string
 	CreateTime          string
 	ModifyTime          string
+	Enabled             string
 }{
 	ReportApproveFlowId: "report_approve_flow_id",
 	FlowName:            "flow_name",
 	ReportType:          "report_type",
 	ClassifyFirstId:     "classify_first_id",
 	ClassifySecondId:    "classify_second_id",
+	ClassifyThirdId:     "classify_third_id",
 	CurrVersion:         "curr_version",
 	CreateTime:          "create_time",
 	ModifyTime:          "modify_time",
+	Enabled:             "enabled",
 }
 
 func (m *ReportApproveFlow) TableName() string {
@@ -153,6 +159,7 @@ type ReportApproveFlowItem struct {
 	ReportType          int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ClassifyFirstId     int    `description:"一级分类ID"`
 	ClassifySecondId    int    `description:"二级分类ID"`
+	ClassifyThirdId     int    `description:"三级分类ID"`
 	ReportClassify      string `description:"报告类型: XX研报/一级分类/二级分类"`
 	CreateTime          string `description:"创建时间"`
 	ModifyTime          string `description:"修改时间"`
@@ -169,6 +176,7 @@ func FormatReportApproveFlow2Item(origin *ReportApproveFlow) (item *ReportApprov
 	item.ReportType = origin.ReportType
 	item.ClassifyFirstId = origin.ClassifyFirstId
 	item.ClassifySecondId = origin.ClassifySecondId
+	item.ClassifyThirdId = origin.ClassifyThirdId
 	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
 	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
 	return
@@ -180,6 +188,7 @@ type ReportApproveFlowAddReq struct {
 	ReportType       int                        `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ClassifyFirstId  int                        `description:"一级分类ID"`
 	ClassifySecondId int                        `description:"二级分类ID"`
+	ClassifyThirdId  int                        `description:"三级分类ID"`
 	Nodes            []ReportApproveNodeSaveReq `description:"审批节点信息"`
 }
 
@@ -304,7 +313,7 @@ func (m *ReportApproveFlow) UpdateFlowAndNodes(flowItem *ReportApproveFlow, node
 	}()
 
 	// 更新审批流
-	updateCols := []string{"FlowName", "ReportType", "ClassifyFirstId", "ClassifySecondId", "CurrVersion", "ModifyTime"}
+	updateCols := []string{"FlowName", "ReportType", "ClassifyFirstId", "ClassifySecondId", "ClassifyThirdId", "CurrVersion", "ModifyTime"}
 	if e = flowItem.Update(updateCols); e != nil {
 		err = fmt.Errorf("update flow err: %s", e.Error())
 		return
@@ -376,6 +385,7 @@ type ReportApproveFlowListReq struct {
 	ReportType       int    `form:"ReportType" description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
 	ClassifyFirstId  int    `form:"ClassifyFirstId" description:"一级分类ID"`
 	ClassifySecondId int    `form:"ClassifySecondId" description:"二级分类ID"`
+	ClassifyThirdId  int    `form:"ClassifyThirdId" description:"三级级分类ID"`
 	Keyword          string `form:"Keyword" description:"关键词"`
 	SortRule         int    `form:"SortRule" description:"排序方式: 1-正序; 2-倒序(默认)"`
 }

+ 39 - 0
models/report_approve/report_approve_record.go

@@ -25,6 +25,10 @@ type ReportApproveRecord struct {
 	ApproveTime           time.Time `description:"审批时间"`
 	CreateTime            time.Time `description:"创建时间"`
 	ModifyTime            time.Time `description:"修改时间"`
+	NodeState             int       `description:"当前节点审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeApproveUserId     int       `description:"当前节点审批人ID"`
+	NodeApproveUserName   string    `description:"当前节点审批人姓名"`
+	NodeApproveTime       time.Time `description:"当前节点审批时间"`
 }
 
 var ReportApproveRecordCols = struct {
@@ -43,6 +47,10 @@ var ReportApproveRecordCols = struct {
 	ApproveTime           string
 	CreateTime            string
 	ModifyTime            string
+	NodeState             string `description:"当前节点审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeApproveUserId     string `description:"当前节点审批人ID"`
+	NodeApproveUserName   string `description:"当前节点审批人姓名"`
+	NodeApproveTime       string `description:"当前节点审批时间"`
 }{
 	ReportApproveRecordId: "report_approve_record_id",
 	ReportApproveId:       "report_approve_id",
@@ -59,6 +67,10 @@ var ReportApproveRecordCols = struct {
 	ApproveTime:           "approve_time",
 	CreateTime:            "create_time",
 	ModifyTime:            "modify_time",
+	NodeState:             "node_state",
+	NodeApproveUserId:     "node_approve_user_id",
+	NodeApproveUserName:   "node_approve_user_name",
+	NodeApproveTime:       "node_approve_time",
 }
 
 func (m *ReportApproveRecord) TableName() string {
@@ -209,6 +221,33 @@ func FormatReportApproveRecord2Item(origin *ReportApproveRecord) (item *ReportAp
 	return
 }
 
+// UpdateNodeState
+// @Description: 将该审批的同一个节点的记录标记为已审批
+// @author: Roc
+// @receiver m
+// @datetime 2024-06-11 13:57:20
+// @param reportApproveId int
+// @param nodeId int
+// @param nodeState int
+// @param nodeApproveUserId int
+// @param nodeApproveUserName string
+// @param nodeApproveTime time.Time
+// @return err error
+func (m *ReportApproveRecord) UpdateNodeState(reportApproveId, nodeId, nodeState, nodeApproveUserId int, nodeApproveUserName string, nodeApproveTime time.Time) (err error) {
+	pars := make([]interface{}, 0)
+	pars = append(pars, nodeState, nodeApproveUserId, nodeApproveUserName, nodeApproveTime)
+
+	// 更新条件
+	whereParas := []interface{}{reportApproveId, nodeId}
+	pars = append(pars, whereParas)
+
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`UPDATE %s SET node_state=?,node_approve_user_id=?,node_approve_user_name=?,node_approve_time=? WHERE report_approve_id = ? AND node_id = ?`, m.TableName())
+	_, err = o.Raw(sql, pars).Exec()
+
+	return
+}
+
 // ReportApproveDetailNodeUserRecord 审批详情-节点用户审批记录
 type ReportApproveDetailNodeUserRecord struct {
 	ReportApproveRecordId int    `description:"审批记录ID"`

+ 322 - 42
models/report_chapter.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_mobile/models/report"
 	"eta/eta_mobile/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
@@ -8,33 +9,90 @@ import (
 
 // ReportChapter 报告章节
 type ReportChapter struct {
-	ReportChapterId   int       `orm:"column(report_chapter_id);pk" description:"报告章节ID"`
-	ReportId          int       `description:"报告ID"`
-	ReportType        string    `description:"报告类型 day-晨报 week-周报"`
-	ClassifyIdFirst   int       `description:"一级分类id"`
-	ClassifyNameFirst string    `description:"一级分类名称"`
-	TypeId            int       `description:"品种ID"`
-	TypeName          string    `description:"品种名称"`
-	Title             string    `description:"标题"`
-	Abstract          string    `description:"摘要"`
-	AddType           int       `description:"新增方式:1:新增报告,2:继承报告"`
-	Author            string    `description:"作者"`
-	Content           string    `description:"内容"`
-	ContentSub        string    `description:"内容前两个章节"`
-	Stage             int       `description:"期数"`
-	Trend             string    `description:"趋势观点"`
-	Sort              int       `description:"排序: 数值越小越靠前"`
-	IsEdit            int       `description:"是否已编辑 0-待编辑 1-已编辑"`
-	PublishState      int       `description:"发布状态 1-待发布,2-已发布"`
-	PublishTime       time.Time `description:"发布时间"`
-	VideoUrl          string    `description:"音频文件URL"`
-	VideoName         string    `description:"音频文件名称"`
-	VideoPlaySeconds  string    `description:"音频播放时长"`
-	VideoSize         string    `description:"音频文件大小,单位M"`
-	VideoKind         int       `description:"音频生成方式:1,手动上传,2:自动生成"`
-	CreateTime        string    `description:"创建时间"`
-	ModifyTime        time.Time `description:"修改时间"`
-	OriginalVideoUrl  string    `description:"原始音频文件URL"`
+	ReportChapterId     int       `orm:"column(report_chapter_id);pk" description:"报告章节ID"`
+	ReportId            int       `description:"报告ID"`
+	ReportType          string    `description:"报告类型 day-晨报 week-周报"`
+	ClassifyIdFirst     int       `description:"一级分类id"`
+	ClassifyNameFirst   string    `description:"一级分类名称"`
+	TypeId              int       `description:"品种ID"`
+	TypeName            string    `description:"品种名称"`
+	Title               string    `description:"标题"`
+	Abstract            string    `description:"摘要"`
+	AddType             int       `description:"新增方式:1:新增报告,2:继承报告"`
+	Author              string    `description:"作者"`
+	Content             string    `description:"内容"`
+	ContentSub          string    `description:"内容前两个章节"`
+	Stage               int       `description:"期数"`
+	Trend               string    `description:"趋势观点"`
+	Sort                int       `description:"排序: 数值越小越靠前"`
+	IsEdit              int       `description:"是否已编辑 0-待编辑 1-已编辑"`
+	PublishState        int       `description:"发布状态 1-待发布,2-已发布"`
+	PublishTime         time.Time `description:"发布时间"`
+	VideoUrl            string    `description:"音频文件URL"`
+	VideoName           string    `description:"音频文件名称"`
+	VideoPlaySeconds    string    `description:"音频播放时长"`
+	VideoSize           string    `description:"音频文件大小,单位M"`
+	VideoKind           int       `description:"音频生成方式:1,手动上传,2:自动生成"`
+	CreateTime          string    `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	OriginalVideoUrl    string    `description:"原始音频文件URL"`
+	ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+}
+
+// ReportChapterItem 报告章节详情
+type ReportChapterItem struct {
+	ReportChapterId     int    `orm:"column(report_chapter_id);pk" description:"报告章节ID"`
+	ReportId            int    `description:"报告ID"`
+	ReportType          string `description:"报告类型 day-晨报 week-周报"`
+	ClassifyIdFirst     int    `description:"一级分类id"`
+	ClassifyNameFirst   string `description:"一级分类名称"`
+	TypeId              int    `description:"品种ID"`
+	TypeName            string `description:"品种名称"`
+	Title               string `description:"标题"`
+	Abstract            string `description:"摘要"`
+	AddType             int    `description:"新增方式:1:新增报告,2:继承报告"`
+	Author              string `description:"作者"`
+	Content             string `description:"内容"`
+	ContentSub          string `description:"内容前两个章节"`
+	Stage               int    `description:"期数"`
+	Trend               string `description:"趋势观点"`
+	Sort                int    `description:"排序: 数值越小越靠前"`
+	IsEdit              int    `description:"是否已编辑 0-待编辑 1-已编辑"`
+	PublishState        int    `description:"发布状态 1-待发布,2-已发布"`
+	PublishTime         string `description:"发布时间"`
+	VideoUrl            string `description:"音频文件URL"`
+	VideoName           string `description:"音频文件名称"`
+	VideoPlaySeconds    string `description:"音频播放时长"`
+	VideoSize           string `description:"音频文件大小,单位M"`
+	VideoKind           int    `description:"音频生成方式:1,手动上传,2:自动生成"`
+	CreateTime          string `description:"创建时间"`
+	ModifyTime          string `description:"修改时间"`
+	OriginalVideoUrl    string `description:"原始音频文件URL"`
+	ContentStruct       string `description:"内容组件"`
+	LastModifyAdminId   int    `description:"最后更新人ID"`
+	LastModifyAdminName string `description:"最后更新人姓名"`
+	ContentModifyTime   string `description:"内容更新时间"`
+	ReportLayout        int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportCreateTime    string `description:"报告时间创建时间"`
+}
+
+// ReportChapterItemResp
+// @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:"版尾样式"`
 }
 
 type ReportChapterResp struct {
@@ -62,6 +120,11 @@ type ReportChapterResp struct {
 	PublishTime      string `description:"发布时间"`
 	CreateTime       string `description:"创建时间"`
 	ModifyTime       string `description:"修改时间"`
+	GrandAdminIdList []int  `description:"授权的用户id列表"`
+	PermissionIdList []int  `description:"关联的品种id列表"`
+	CanEdit          bool   `description:"是否可编辑"`
+	Editor           string `description:"编辑人"`
+	IsAuth           bool   `description:"是否有权限"`
 }
 
 // GetChapterListByReportId 根据ReportId获取章节列表
@@ -82,6 +145,15 @@ func GetPublishedChapterListByReportId(reportId int) (list []*ReportChapter, err
 	return
 }
 
+// AddReportChapterReq
+// @Description: 新增报告章节请求体
+type AddReportChapterReq struct {
+	ReportId         int    `description:"报告ID"`
+	Title            string `description:"标题"`
+	PermissionIdList []int  `description:"报告关联的品种权限"`
+	AdminIdList      []int  `description:"授权的编辑人id列表"`
+}
+
 // EditReportChapterReq 编辑报告章节请求体
 type EditReportChapterReq struct {
 	ReportChapterId  int            `description:"报告章节ID"`
@@ -95,6 +167,14 @@ type EditReportChapterReq struct {
 	VideoName        string         `description:"音频文件名称"`
 	VideoPlaySeconds string         `description:"音频播放时长"`
 	VideoSize        string         `description:"音频文件大小,单位M"`
+
+	// 以下是智能研报相关
+	ContentStruct  string `description:"内容组件"`
+	HeadImg        string `description:"报告头图地址"`
+	EndImg         string `description:"报告尾图地址"`
+	CanvasColor    string `description:"画布颜色"`
+	HeadResourceId int    `description:"版头资源ID"`
+	EndResourceId  int    `description:"版尾资源ID"`
 }
 
 type EditTickList struct {
@@ -112,6 +192,21 @@ func GetReportChapterInfoById(reportChapterId int) (item *ReportChapter, err err
 	return
 }
 
+// GetReportChapterItemById
+// @Description: 根据主键获取报告章节(时间格式为字符串的数据)
+// @author: Roc
+// @datetime 2024-06-27 14:10:29
+// @param reportChapterId int
+// @return item *ReportChapterItem
+// @return err error
+func GetReportChapterItemById(reportChapterId int) (item *ReportChapterItem, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_chapter_id = ? `
+	err = o.Raw(sql, reportChapterId).QueryRow(&item)
+
+	return
+}
+
 // GetLastPublishedReportChapter 获取上一篇已发表的晨周报章节
 func GetLastPublishedReportChapter(typeId int, reportType string) (item *ReportChapter, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -121,10 +216,27 @@ func GetLastPublishedReportChapter(typeId int, reportType string) (item *ReportC
 	return
 }
 
+// Add
+// @Description: 新增章节报告
+// @author: Roc
+// @receiver chapterInfo
+// @datetime 2024-06-04 15:14:41
+// @return err error
+func (chapterChapterInfo *ReportChapter) Add() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err := o.Insert(chapterChapterInfo)
+	if err != nil {
+		return
+	}
+	chapterChapterInfo.ReportChapterId = int(lastId)
+
+	return
+}
+
 // UpdateChapter 更新报表章节
-func (chapterInfo *ReportChapter) UpdateChapter(cols []string) (err error) {
+func (chapterChapterInfo *ReportChapter) UpdateChapter(cols []string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	_, err = o.Update(chapterInfo, cols...)
+	_, err = o.Update(chapterChapterInfo, cols...)
 
 	return
 }
@@ -136,7 +248,11 @@ type EditChapterTrendTagReq struct {
 }
 
 // UpdateChapterAndTicker 更新章节及ticker
-func UpdateChapterAndTicker(chapterInfo *ReportChapter, updateCols []string, tickerList []*ReportChapterTicker) (err error) {
+func UpdateChapterAndTicker(reportInfo *Report, chapterInfo *ReportChapter, updateCols []string, tickerList []*ReportChapterTicker) (err error) {
+	// 更新报告的最近编辑人信息
+	if err = reportInfo.UpdateReport([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"}); err != nil {
+		return
+	}
 	// 更新章节
 	if err = chapterInfo.UpdateChapter(updateCols); err != nil {
 		return
@@ -234,18 +350,8 @@ func GetReportChapterVideoListByChapterIds(chapterIds []int) (list []*ReportChap
 
 // PublishReportChapterReq 发布报告章节请求体
 type PublishReportChapterReq struct {
-	ReportChapterId  int            `description:"报告章节ID"`
-	Title            string         `description:"标题"`
-	AddType          int            `description:"新增方式:1:新增报告,2:继承报告"`
-	Author           string         `description:"作者"`
-	Content          string         `description:"内容"`
-	TickerList       []EditTickList `description:"指标信息"`
-	CreateTime       string         `description:"发布时间"`
-	PublishReport    int            `description:"是否同时发布报告"`
-	VideoUrl         string         `description:"音频文件URL"`
-	VideoName        string         `description:"音频文件名称"`
-	VideoPlaySeconds string         `description:"音频播放时长"`
-	VideoSize        string         `description:"音频文件大小,单位M"`
+	ReportChapterId int `description:"报告章节ID"`
+	PublishReport   int `description:"是否同时发布报告"`
 }
 
 // CountPublishedChapterNum 获取报告已发布的章节数
@@ -257,6 +363,36 @@ func CountPublishedChapterNum(reportId int) (count int, err error) {
 	return
 }
 
+// CountUnPublishedChapterNum
+// @Description: 获取报告未发布的章节数
+// @author: Roc
+// @datetime 2024-06-14 15:59:23
+// @param reportId int
+// @return count int
+// @return err error
+func CountUnPublishedChapterNum(reportId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT COUNT(1) AS ct FROM report_chapter WHERE report_id = ? AND publish_state = 1 `
+	err = o.Raw(sql, reportId).QueryRow(&count)
+
+	return
+}
+
+// GetUnPublishedChapterList
+// @Description: 获取报告未发布的章节列表
+// @author: Roc
+// @datetime 2024-06-14 15:59:23
+// @param reportId int
+// @return list []*ReportChapter
+// @return err error
+func GetUnPublishedChapterList(reportId int) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT report_chapter_id,report_id,title FROM report_chapter WHERE report_id = ? AND publish_state = 1 ORDER BY sort ASC`
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
 // GetChapterListByChapterIds 根据ReportId获取章节列表
 func GetChapterListByChapterIds(chapterIds []int) (list []*ReportChapter, err error) {
 	if len(chapterIds) == 0 {
@@ -305,3 +441,147 @@ func CountReportChapterByTypeId(typeId int) (count int, err error) {
 	err = o.Raw(sql, typeId).QueryRow(&count)
 	return
 }
+
+// AddReportChapter
+// @Description: 待添加的报告章节
+type AddReportChapter struct {
+	ReportChapter       *ReportChapter
+	GrantList           []*report.ReportChapterGrant
+	GrantPermissionList []*report.ReportChapterPermissionMapping
+}
+
+// EditReportChapterBaseInfoAndPermissionReq
+// @Description: 编辑报告章节的基础信息请求
+type EditReportChapterBaseInfoAndPermissionReq struct {
+	ReportChapterId  int    `description:"报告章节ID"`
+	Title            string `description:"标题"`
+	PermissionIdList []int  `description:"报告关联的品种权限"`
+	AdminIdList      []int  `description:"授权的编辑人id列表"`
+}
+
+// GetReportChapterIdList
+// @Description: 获取报告的所有章节id列表
+// @author: Roc
+// @datetime 2024-06-05 11:09:40
+// @param reportId int
+// @return list []int
+// @return err error
+func GetReportChapterIdList(reportId int) (list []int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT report_chapter_id FROM report_chapter
+			WHERE report_id = ? 
+			ORDER BY report_chapter_id ASC `
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+// ReportChapterMoveReq
+// @Description:  报告章节移动请求
+type ReportChapterMoveReq struct {
+	ReportChapterId int `description:"报告章节id"`
+	//	ParentChartPermissionId int `description:"父级品种id"`
+	PrevReportChapterId int `description:"上一个兄弟节点报告章节id"`
+	NextReportChapterId int `description:"下一个兄弟节点报告章节id"`
+}
+
+// GetReportChapterById
+// @Description: 获取具体章节
+// @author: Roc
+// @receiver r
+// @datetime 2024-06-06 09:32:40
+// @param reportChapterId int
+// @return item *ReportChapter
+// @return err error
+func (chapterChapterInfo *ReportChapter) GetReportChapterById(reportChapterId int) (item *ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_chapter_id = ?`
+	err = o.Raw(sql, reportChapterId).QueryRow(&item)
+	return
+}
+
+// UpdateReportChapterSortByReportId
+// @Description: 根据父类id更新排序
+// @author: Roc
+// @receiver chapterChapterInfo
+// @datetime 2024-06-06 09:39:28
+// @param reportId int
+// @param reportChapterId int
+// @param nowSort int
+// @param updateSort string
+// @return err error
+func (chapterChapterInfo *ReportChapter) UpdateReportChapterSortByReportId(reportId, reportChapterId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	pars := make([]interface{}, 0)
+	sql := ` update report_chapter set sort = ` + updateSort + ` WHERE report_id = ? AND sort > ?`
+	pars = append(pars, reportId, nowSort)
+
+	if reportChapterId > 0 {
+		sql += ` or ( report_chapter_id > ?  and sort = ? )`
+		pars = append(pars, reportChapterId, nowSort)
+	}
+
+	_, err = o.Raw(sql, pars).Exec()
+
+	return
+}
+
+// GetFirstReportChapterByReportId
+// @Description: 获取当前报告下,且排序数相同 的排序第一条的数据
+// @author: Roc
+// @receiver chapterChapterInfo
+// @datetime 2024-06-06 09:45:32
+// @param reportId int
+// @return item *ReportChapter
+// @return err error
+func (chapterChapterInfo *ReportChapter) GetFirstReportChapterByReportId(reportId int) (item *ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE 1 = 1 AND report_id = ? ORDER BY sort ASC, report_chapter_id ASC LIMIT 1`
+	err = o.Raw(sql, reportId).QueryRow(&item)
+	return
+}
+
+// GetMaxSortByReportId
+// @Description: 获取最大的排序值
+// @author: Roc
+// @receiver chapterChapterInfo
+// @datetime 2024-06-06 09:44:13
+// @param reportId int
+// @return maxSort int
+// @return err error
+func (chapterChapterInfo *ReportChapter) GetMaxSortByReportId(reportId int) (maxSort int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT max(sort) AS sort FROM report_chapter WHERE report_id = ?`
+	err = o.Raw(sql, reportId).QueryRow(&maxSort)
+
+	return
+}
+
+// Update
+// @Description: 数据变更
+// @author: Roc
+// @receiver chapterChapterInfo
+// @datetime 2024-06-06 09:47:46
+// @param cols []string
+// @return err error
+func (chapterChapterInfo *ReportChapter) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(chapterChapterInfo, cols...)
+
+	return
+}
+
+// DelReportChapterReq
+// @Description: 删除报告章节请求体
+type DelReportChapterReq struct {
+	ReportChapterId int `description:"报告章节ID"`
+}
+
+// GetAllReportChapter 获取所有的报告章节
+func GetAllReportChapter() (items []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter ORDER BY report_chapter_id asc `
+	_, err = o.Raw(sql).QueryRows(&items)
+
+	return
+}

+ 2 - 2
models/report_chapter_ticker.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_mobile/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
 )
@@ -61,8 +62,7 @@ func GetDailyBaseColumnList(keyword string, typeId int) (list []*DailyBaseColumn
 	sql := ` SELECT * FROM daily_base_column WHERE 1 = 1 `
 	pars := make([]interface{}, 0)
 	if keyword != "" {
-		keyword = "%" + keyword + "%"
-		pars = append(pars, keyword)
+		pars = append(pars, utils.GetLikeKeyword(keyword))
 		sql += ` AND base_column_name like ? `
 	}
 	pars = append(pars, typeId)

+ 71 - 24
models/report_chapter_type.go

@@ -8,28 +8,30 @@ import (
 )
 
 type ReportChapterType struct {
-	ReportChapterTypeId    int       `orm:"column(report_chapter_type_id);pk" description:"报告章节类型id"`
-	ReportChapterTypeKey   string    `description:"章节key"`
-	ReportChapterTypeThumb string    `description:"H5展示的图片"`
-	BannerUrl              string    `description:"banner显示图片"`
-	ReportChapterTypeName  string    `description:"报告章节类型名称"`
-	Sort                   int       `description:"排序字段"`
-	Enabled                int       `description:"启禁用状态"`
-	CreatedTime            time.Time `description:"创建时间"`
-	LastUpdatedTime        time.Time `description:"更新时间"`
-	ResearchType           string    `description:"研报类型"`
-	SelectedImage          string    `description:"选中时的图片"`
-	UnselectedImage        string    `description:"没选中时的图片"`
-	PcSelectedImage        string    `description:"PC-选中的图片"`
-	PcUnselectedImage      string    `description:"PC-未选中的图片"`
-	EditImgUrl             string    `description:"管理后台编辑时选用的图"`
-	TickerTitle            string    `description:"指标列的标题"`
-	IsShow                 int       `description:"是否显示(研报小程序端根据此字段判断)"`
-	PauseStartTime         string    `description:"暂停开始日期"`
-	PauseEndTime           string    `description:"暂停结束日期"`
-	IsSet                  int       `description:"是否设置:0为设置,1已设置"`
-	YbIconUrl              string    `description:"研报小程序icon"`
-	YbBottomIcon           string    `description:"研报小程序详情底部icon"`
+	ReportChapterTypeId        int       `orm:"column(report_chapter_type_id);pk" description:"报告章节类型id"`
+	ReportChapterTypeKey       string    `description:"章节key"`
+	ReportChapterTypeThumb     string    `description:"H5展示的图片"`
+	BannerUrl                  string    `description:"banner显示图片"`
+	ReportChapterTypeName      string    `description:"报告章节类型名称"`
+	Sort                       int       `description:"排序字段"`
+	Enabled                    int       `description:"启禁用状态"`
+	CreatedTime                time.Time `description:"创建时间"`
+	LastUpdatedTime            time.Time `description:"更新时间"`
+	ResearchType               string    `description:"研报类型"`
+	SelectedImage              string    `description:"选中时的图片"`
+	UnselectedImage            string    `description:"没选中时的图片"`
+	PcSelectedImage            string    `description:"PC-选中的图片"`
+	PcUnselectedImage          string    `description:"PC-未选中的图片"`
+	EditImgUrl                 string    `description:"管理后台编辑时选用的图"`
+	TickerTitle                string    `description:"指标列的标题"`
+	IsShow                     int       `description:"是否显示(研报小程序端根据此字段判断)"`
+	PauseStartTime             string    `description:"暂停开始日期"`
+	PauseEndTime               string    `description:"暂停结束日期"`
+	IsSet                      int       `description:"是否设置:0为设置,1已设置"`
+	YbIconUrl                  string    `description:"研报小程序icon"`
+	YbBottomIcon               string    `description:"研报小程序详情底部icon"`
+	ReportClassifyId           int       `description:"所属分类id"`
+	InheritReportChapterTypeId int       `description:"继承的报告章节类型id"`
 }
 
 func (r *ReportChapterType) Create() (err error) {
@@ -42,6 +44,23 @@ func (r *ReportChapterType) Create() (err error) {
 	return
 }
 
+// MultiCreate
+// @Description: 批量添加报告章节类型
+// @author: Roc
+// @receiver r
+// @datetime 2024-06-17 14:36:09
+// @param addList []*ReportChapterType
+// @return err error
+func (r *ReportChapterType) MultiCreate(addList []*ReportChapterType) (err error) {
+	if len(addList) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(500, addList)
+
+	return
+}
+
 func (r *ReportChapterType) Update(cols []string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	_, err = o.Update(r, cols...)
@@ -341,7 +360,7 @@ type ReportChapterTypeListItem struct {
 // ReportChapterTypeAddReq 新增章节类型请求体
 type ReportChapterTypeAddReq struct {
 	ReportChapterTypeName string `description:"报告章节类型名称"`
-	ResearchType          string `description:"研报类型"`
+	ClassifyId            int    `description:"所属报告分类id"`
 	ChartPermissionIdList []int  `description:"权限id数组"`
 	/*SelectedImage         string `description:"选中时的icon"`
 	UnselectedImage       string `description:"未选中时的icon"`
@@ -354,7 +373,7 @@ type ReportChapterTypeAddReq struct {
 type ReportChapterTypeEditReq struct {
 	ReportChapterTypeId   int    `description:"报告章节类型id"`
 	ReportChapterTypeName string `description:"报告章节类型名称"`
-	ResearchType          string `description:"研报类型"`
+	ClassifyId            int    `description:"所属报告分类id"`
 	ChartPermissionIdList []int  `description:"权限id数组"`
 	/*SelectedImage         string `description:"选中时的icon"`
 	UnselectedImage       string `description:"未选中时的icon"`
@@ -447,3 +466,31 @@ func (r *ReportChapterType) SetEnabled(id, enabled int) (err error) {
 	_, err = o.Raw(sql, enabled, id).Exec()
 	return
 }
+
+// GetReportChapterTypeListByClassifyId
+// @Description: 通过报告类型获取章节类型列表(已启用的)
+// @author: Roc
+// @datetime 2024-06-03 16:00:12
+// @param classifyId int
+// @return list []*ReportChapterType
+// @return err error
+func GetReportChapterTypeListByClassifyId(classifyId int) (list []*ReportChapterType, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter_type WHERE report_classify_id = ? AND enabled = 1`
+	_, err = o.Raw(sql, classifyId).QueryRows(&list)
+	return
+}
+
+// GetAllReportChapterTypeListByClassifyId
+// @Description: 通过报告类型获取所有的章节类型列表
+// @author: Roc
+// @datetime 2024-06-03 16:00:12
+// @param classifyId int
+// @return list []*ReportChapterType
+// @return err error
+func GetAllReportChapterTypeListByClassifyId(classifyId int) (list []*ReportChapterType, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter_type WHERE report_classify_id = ? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&list)
+	return
+}

+ 60 - 22
models/report_chapter_type_permission.go

@@ -16,16 +16,22 @@ type ReportChapterTypePermission struct {
 	CreatedTime           time.Time `description:"创建时间"`
 }
 
-// GetChapterTypePermissionByTypeIdAndResearchType 根据章节类型ID及研报类型获取章节类型权限列表
-func GetChapterTypePermissionByTypeIdAndResearchType(typeId int, researchType string) (list []*ReportChapterTypePermission, err error) {
+// GetChapterTypePermissionByReportChapterTypeId
+// @Description: 根据章节类型ID获取章节类型权限列表
+// @author: Roc
+// @datetime 2024-06-03 15:42:47
+// @param typeId int
+// @return list []*ReportChapterTypePermission
+// @return err error
+func GetChapterTypePermissionByReportChapterTypeId(typeId int) (list []*ReportChapterTypePermission, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT * FROM report_chapter_type_permission WHERE report_chapter_type_id = ? AND research_type = ? ORDER BY chart_permission_id ASC `
-	_, err = o.Raw(sql, typeId, researchType).QueryRows(&list)
+	sql := ` SELECT * FROM report_chapter_type_permission WHERE report_chapter_type_id = ?  ORDER BY chart_permission_id ASC `
+	_, err = o.Raw(sql, typeId).QueryRows(&list)
 	return
 }
 
 // SetReportChapterTypePermission 设置报告章节类型权限
-func SetReportChapterTypePermission(chapterTypeId int, researchType string, newPermissions []*ReportChapterTypePermission, newWeekPermissions []*ChartPermissionChapterMapping) (err error) {
+func SetReportChapterTypePermission(chapterTypeId int, newPermissions []*ReportChapterTypePermission) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	to, err := o.Begin()
 	if err != nil {
@@ -40,8 +46,8 @@ func SetReportChapterTypePermission(chapterTypeId int, researchType string, newP
 	}()
 
 	// 删除原章节类型权限
-	sql := `DELETE FROM report_chapter_type_permission WHERE report_chapter_type_id = ? AND research_type = ?`
-	_, err = to.Raw(sql, chapterTypeId, researchType).Exec()
+	sql := `DELETE FROM report_chapter_type_permission WHERE report_chapter_type_id = ?`
+	_, err = to.Raw(sql, chapterTypeId).Exec()
 	if err != nil {
 		return
 	}
@@ -55,22 +61,23 @@ func SetReportChapterTypePermission(chapterTypeId int, researchType string, newP
 	}
 
 	// 周报章节调整chart_permission_chapter_mapping表
-	if researchType == utils.REPORT_TYPE_WEEK {
-		// 删除原权限
-		sql = `DELETE FROM chart_permission_chapter_mapping WHERE report_chapter_type_id = ? AND research_type = ?`
-		_, err = to.Raw(sql, chapterTypeId, researchType).Exec()
-		if err != nil {
-			return
-		}
+	//if researchType == utils.REPORT_TYPE_WEEK {
+	//	// 删除原权限
+	//	sql = `DELETE FROM chart_permission_chapter_mapping WHERE report_chapter_type_id = ? AND research_type = ?`
+	//	_, err = to.Raw(sql, chapterTypeId, researchType).Exec()
+	//	if err != nil {
+	//		return
+	//	}
+	//
+	//	// 新增权限
+	//	if len(newWeekPermissions) > 0 {
+	//		_, err = to.InsertMulti(len(newWeekPermissions), newWeekPermissions)
+	//		if err != nil {
+	//			return
+	//		}
+	//	}
+	//}
 
-		// 新增权限
-		if len(newWeekPermissions) > 0 {
-			_, err = to.InsertMulti(len(newWeekPermissions), newWeekPermissions)
-			if err != nil {
-				return
-			}
-		}
-	}
 	return
 }
 
@@ -81,3 +88,34 @@ func GetChapterTypePermissionByResearchType(researchType string) (list []*Report
 	_, err = o.Raw(sql, researchType).QueryRows(&list)
 	return
 }
+
+// GetChapterTypePermissionByChapterTypeIdList
+// @Description: 根据章节类型ID列表获取章节类型权限列表
+// @author: Roc
+// @datetime 2024-06-03 14:10:17
+// @param chapterTypeIdList []int
+// @return list []*ReportChapterTypePermission
+// @return err error
+func GetChapterTypePermissionByChapterTypeIdList(chapterTypeIdList []int) (list []*ReportChapterTypePermission, err error) {
+	num := len(chapterTypeIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter_type_permission WHERE report_chapter_type_id in (` + utils.GetOrmInReplace(num) + `) ORDER BY chart_permission_id ASC `
+	_, err = o.Raw(sql, chapterTypeIdList).QueryRows(&list)
+	return
+}
+
+// GetAllChapterTypePermission
+// @Description: 获取所有章节类型ID获取章节类型权限列表
+// @author: Roc
+// @datetime 2024-06-03 15:42:47
+// @return list []*ReportChapterTypePermission
+// @return err error
+func GetAllChapterTypePermission() (list []*ReportChapterTypePermission, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter_type_permission ORDER BY chart_permission_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}

+ 453 - 0
models/report_v2.go

@@ -0,0 +1,453 @@
+package models
+
+import (
+	"errors"
+	"eta/eta_mobile/models/report"
+	"eta/eta_mobile/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// AddReportAndChapter
+// @Description: 新增报告及章节
+// @author: Roc
+// @datetime 2024-06-06 17:08:34
+// @param reportItem *Report
+// @param allGrantUserList []*report.ReportGrant
+// @param addReportChapterList []AddReportChapter
+// @return reportId int64
+// @return err error
+func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGrant, addReportChapterList []AddReportChapter) (reportId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 新增报告
+	reportId, err = to.Insert(reportItem)
+	if err != nil {
+		return
+	}
+	reportItem.Id = int(reportId)
+
+	// 新增报告授权
+	if len(allGrantUserList) > 0 {
+		for _, v := range allGrantUserList {
+			v.ReportId = reportItem.Id
+		}
+		_, err = to.InsertMulti(500, allGrantUserList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 新增报告章节
+	if len(addReportChapterList) > 0 {
+		for _, addReportChapter := range addReportChapterList {
+			// 新增章节
+			chapterItem := addReportChapter.ReportChapter
+			chapterItem.ReportId = int(reportId)
+			cpId, tmpErr := to.Insert(chapterItem)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			chapterItem.ReportChapterId = int(cpId)
+
+			// 新增章节授权
+			if len(addReportChapter.GrantList) > 0 {
+				grantList := addReportChapter.GrantList
+				for _, v := range grantList {
+					v.ReportChapterId = chapterItem.ReportChapterId
+				}
+				_, err = to.InsertMulti(500, grantList)
+				if err != nil {
+					return
+				}
+			}
+
+			// 新增报告章节关联的品种
+			if len(addReportChapter.GrantPermissionList) > 0 {
+				permissionList := addReportChapter.GrantPermissionList
+				for _, v := range permissionList {
+					v.ReportChapterId = chapterItem.ReportChapterId
+				}
+				_, err = to.InsertMulti(500, permissionList)
+				if err != nil {
+					return
+				}
+			}
+
+		}
+	}
+
+	return
+}
+
+// EditReportAndPermission
+// @Description: 修改报告的基础信息、授权用户权限
+// @author: Roc
+// @datetime 2024-06-06 17:11:12
+// @param reportInfo *Report
+// @param updateCols []string
+// @param addReportGrantList []*report.ReportGrant
+// @param delReportGrantIdList []int
+// @return err error
+func EditReportAndPermission(reportInfo *Report, updateCols []string, addReportGrantList []*report.ReportGrant, delReportGrantIdList []int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 变更报告章节信息
+	if len(updateCols) > 0 {
+		_, err = to.Update(reportInfo, updateCols...)
+		if err != nil {
+			return
+		}
+	}
+
+	// 新增报告授权用户
+	if len(addReportGrantList) > 0 {
+		_, err = to.InsertMulti(500, addReportGrantList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告授权用户
+	delNum := len(delReportGrantIdList)
+	if delNum > 0 {
+		sql := `DELETE FROM report_grant WHERE grant_id IN (` + utils.GetOrmInReplace(delNum) + `)`
+		_, err = to.Raw(sql, delReportGrantIdList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// AddChapterBaseInfoAndPermission
+// @Description: 新增报告章节的基础信息、授权用户权限
+// @author: Roc
+// @datetime 2024-06-11 15:33:50
+// @param reportChapterInfo *ReportChapter
+// @param addReportChapterGrantList []*report.ReportChapterGrant
+// @param addChapterPermissionMap []*report.ReportChapterPermissionMapping
+// @return err error
+func AddChapterBaseInfoAndPermission(reportChapterInfo *ReportChapter, addReportChapterGrantList []*report.ReportChapterGrant, addChapterPermissionMap []*report.ReportChapterPermissionMapping) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	lastId, err := to.Insert(reportChapterInfo)
+	if err != nil {
+		return
+	}
+	reportChapterInfo.ReportChapterId = int(lastId)
+
+	// 新增报告章节授权用户
+	if len(addReportChapterGrantList) > 0 {
+		for k, _ := range addReportChapterGrantList {
+			addReportChapterGrantList[k].ReportChapterId = reportChapterInfo.ReportChapterId
+		}
+		_, err = to.InsertMulti(500, addReportChapterGrantList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 新增报告章节关联的品种配置
+	if len(addChapterPermissionMap) > 0 {
+		for k, _ := range addChapterPermissionMap {
+			addChapterPermissionMap[k].ReportChapterId = reportChapterInfo.ReportChapterId
+		}
+
+		_, err = to.InsertMulti(500, addChapterPermissionMap)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// EditChapterBaseInfoAndPermission
+// @Description: 修改报告章节的基础信息、授权用户权限、品种权限
+// @author: Roc
+// @datetime 2024-06-05 11:45:04
+// @param reportChapterInfo *ReportChapter
+// @param updateCols []string
+// @param addReportChapterGrantList []report.ReportChapterGrant
+// @param addChapterPermissionMap []*report.ReportChapterPermissionMapping
+// @param delReportChapterGrantIdList []int
+// @param delChapterPermissionMappingIdList []int
+// @return err error
+func EditChapterBaseInfoAndPermission(reportInfo *Report, reportChapterInfo *ReportChapter, updateCols []string, addReportChapterGrantList []*report.ReportChapterGrant, addChapterPermissionMap []*report.ReportChapterPermissionMapping, delReportChapterGrantIdList, delChapterPermissionMappingIdList []int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 变更报告的最后编辑人信息
+	{
+		_, err = to.Update(reportInfo, "LastModifyAdminId", "LastModifyAdminName", "ModifyTime")
+		if err != nil {
+			return
+		}
+	}
+
+	// 变更报告章节信息
+	if len(updateCols) > 0 {
+		_, err = to.Update(reportChapterInfo, updateCols...)
+		if err != nil {
+			return
+		}
+	}
+
+	// 新增报告章节授权用户
+	if len(addReportChapterGrantList) > 0 {
+		_, err = to.InsertMulti(500, addReportChapterGrantList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告章节授权用户
+	delNum := len(delReportChapterGrantIdList)
+	if delNum > 0 {
+		sql := `DELETE FROM report_chapter_grant WHERE grant_id IN (` + utils.GetOrmInReplace(delNum) + `)`
+		_, err = to.Raw(sql, delReportChapterGrantIdList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	// 新增报告章节的品种配置
+	if len(addChapterPermissionMap) > 0 {
+		_, err = to.InsertMulti(500, addChapterPermissionMap)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告章节的品种配置
+	delNum = len(delChapterPermissionMappingIdList)
+	if delNum > 0 {
+		sql := `DELETE FROM report_chapter_permission_mapping WHERE report_chapter_permission_mapping_id IN (` + utils.GetOrmInReplace(delNum) + `)`
+		_, err = to.Raw(sql, delChapterPermissionMappingIdList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// DelChapterAndPermission
+// @Description: 删除报告章节、授权用户权限、品种权限
+// @author: Roc
+// @datetime 2024-06-06 17:25:47
+// @param reportInfo *Report
+// @param updateReportCols []string
+// @param reportChapterInfo *ReportChapter
+// @return err error
+func DelChapterAndPermission(reportInfo *Report, updateReportCols []string, reportChapterInfo *ReportChapter) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 变更报告信息
+	if len(updateReportCols) > 0 {
+		_, err = to.Update(reportInfo, updateReportCols...)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告对应章节
+	{
+		_, err = to.Delete(reportChapterInfo)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告章节的授权用户权限
+	{
+		sql := `DELETE FROM report_chapter_grant WHERE report_chapter_id = ? `
+		_, err = to.Raw(sql, reportChapterInfo.ReportChapterId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除报告章节的品种配置
+	{
+		sql := `DELETE FROM report_chapter_permission_mapping WHERE report_chapter_id = ? `
+		_, err = to.Raw(sql, reportChapterInfo.ReportChapterId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// GetReportListCountByAuthorized
+// @Description: 获取有权限的报告列表的报告数量
+// @author: Roc
+// @datetime 2024-05-30 15:14:01
+// @param condition string
+// @param pars []interface{}
+// @return count int
+// @return err error
+func GetReportListCountByAuthorized(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count  FROM report as a 
+ WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetReportListByAuthorized
+// @Description: 获取有权限的报告列表的数据
+// @author: Roc
+// @datetime 2024-05-30 15:15:07
+// @param condition string
+// @param pars []interface{}
+// @param startSize int
+// @param pageSize int
+// @return items []*ReportList
+// @return err error
+func GetReportListByAuthorized(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+
+	sql := `SELECT id,classify_id_first,classify_name_first,classify_id_second,classify_name_second,classify_id_third,classify_name_third,title,stage,create_time,author,report_layout,collaborate_type,is_public_publish FROM report as a WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += ` GROUP BY a.id ORDER BY  report_create_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ModifyReportClassifyAndReportChapterTypeByCondition
+// @Description:
+// @author: Roc
+// @datetime 2024-06-17 16:12:44
+// @param condition string
+// @param pars []interface{}
+// @param updateStr string
+// @param chapterTypeIdMap map[int]int 当前的章节类型ID ---> 继承的章节类型ID
+// @param oldClassifyId int
+// @param currClassifyId int
+// @param currClassifyName string
+// @return err error
+func ModifyReportClassifyAndReportChapterTypeByCondition(condition string, pars []interface{}, updateStr string, chapterTypeIdMap map[int]int, oldClassifyId, currClassifyId int, currClassifyName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if condition == `` {
+		err = errors.New("condition不能为空")
+		return
+	}
+	// 修改报告的所属分类
+	sql := `UPDATE report as a SET ` + updateStr + ` WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		return
+	}
+
+	// 修改历史报告中的章节分类归属
+	sql = `UPDATE report_chapter set classify_id_first=?,classify_name_first=? where classify_id_first = ?`
+	_, err = to.Raw(sql, currClassifyId, currClassifyName, oldClassifyId).Exec()
+	if err != nil {
+		return
+	}
+
+	for currTypeId, oldTypeId := range chapterTypeIdMap {
+		// 没有章节类型的不处理
+		if oldTypeId == 0 {
+			continue
+		}
+		tmpSql := `UPDATE report_chapter set type_id=? where type_id = ?`
+		_, err = to.Raw(tmpSql, currTypeId, oldTypeId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// EditLayoutImgReq
+// @Description: 版图设置请求
+type EditLayoutImgReq struct {
+	ReportId       int64  `description:"报告id"`
+	HeadImg        string `description:"报告头图地址"`
+	EndImg         string `description:"报告尾图地址"`
+	CanvasColor    string `description:"画布颜色"`
+	HeadResourceId int    `description:"版头资源ID"`
+	EndResourceId  int    `description:"版尾资源ID"`
+}

+ 5 - 4
models/wechat_send_msg.go

@@ -15,7 +15,7 @@ func GetOpenIdArr() (items []string, err error) {
 	return
 }
 
-func GetOpenIdArrByClassifyNameSecond(classifyNameSecond string) (items []string, err error) {
+func GetOpenIdArrByClassifyId(classifyId int) (items []string, err error) {
 	sql := ` SELECT DISTINCT ur.open_id FROM wx_user AS wu 
 			INNER JOIN company AS c ON c.company_id = wu.company_id 
 			INNER JOIN company_product AS d ON c.company_id=d.company_id
@@ -23,10 +23,11 @@ func GetOpenIdArrByClassifyNameSecond(classifyNameSecond string) (items []string
 			INNER JOIN company_report_permission AS e ON d.company_id=e.company_id
 			INNER JOIN chart_permission AS f ON e.chart_permission_id=f.chart_permission_id
 			INNER JOIN chart_permission_search_key_word_mapping AS g ON f.chart_permission_id=g.chart_permission_id
-			WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续')
+			WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续') AND  e.status IN('正式','试用','永续') 
 			AND g.from='rddp'
-			AND g.key_word=?
+			AND g.classify_id=?
 			ORDER BY FIELD(c.company_id, 16) DESC, ur.user_record_id ASC  `
-	_, err = orm.NewOrmUsingDB("weekly").Raw(sql, classifyNameSecond).QueryRows(&items)
+	o := orm.NewOrmUsingDB("weekly")
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
 	return
 }

+ 81 - 9
routers/commentsRouter.go

@@ -5040,8 +5040,8 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "AddDayWeekReport",
-            Router: `/addDayWeekReport`,
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5049,8 +5049,8 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "CancelApprove",
-            Router: `/approve/cancel`,
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5058,8 +5058,17 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "SubmitApprove",
-            Router: `/approve/submit`,
+            Method: "Author",
+            Router: `/author`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "AddChapter",
+            Router: `/chapter/add`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5067,13 +5076,49 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "Author",
-            Router: `/author`,
+            Method: "EditChapterBaseInfoAndPermission",
+            Router: `/chapter/base_info/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "DelChapter",
+            Router: `/chapter/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ChapterMove",
+            Router: `/chapter/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetUnPublishReportChapterList",
+            Router: `/chapter/un_publish/list`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "VoiceUpload",
+            Router: `/chapter/voice/upload`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "ClassifyIdDetail",
@@ -5101,6 +5146,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "BaseDetail",
+            Router: `/detail/base`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "Edit",
@@ -5236,6 +5290,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "EditLayoutImg",
+            Router: `/layout_img/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "ListReport",
@@ -5245,6 +5308,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "AuthorizedListReport",
+            Router: `/list/authorized`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "MarkEditStatus",
@@ -5274,7 +5346,7 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "PublishCancleReport",
+            Method: "PublishCancelReport",
             Router: `/publish/cancle`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),

+ 10 - 8
services/chart_permission_sync.go

@@ -96,11 +96,12 @@ func ChartFiccPermissionSync() (err error, errMsg string) {
 }
 
 type EditClassifyPermissionReq struct {
-	Keyword string
+	Keyword    string
+	ClassifyId int
 }
 
 // EditClassifyChartPermissionSync 设置报告分类权限
-func EditClassifyChartPermissionSync(keyword string) (err error) {
+func EditClassifyChartPermissionSync(keyword string, classifyId int) (err error) {
 	defer func() {
 		if err != nil {
 			utils.FileLog.Info("同步设置报告分类权限失败, Err: " + err.Error())
@@ -110,7 +111,7 @@ func EditClassifyChartPermissionSync(keyword string) (err error) {
 	if utils.CrmEtaServerUrl == "" {
 		return
 	}
-	req := &EditClassifyPermissionReq{Keyword: keyword}
+	req := &EditClassifyPermissionReq{Keyword: keyword, ClassifyId: classifyId}
 	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/crm/chart_permission/classify/sync")
 	b, err := crmEtaPost(url, req)
 	if err != nil {
@@ -134,10 +135,11 @@ func EditClassifyChartPermissionSync(keyword string) (err error) {
 type EditReportPermissionSyncReq struct {
 	ReportId           int64  `description:"报告id"`
 	ClassifyNameSecond string `description:"二级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类ID"`
 }
 
 // EditReportPermissionSync 设置报告权限
-func EditReportPermissionSync(reportId int64, classifyNameSecond string) (err error) {
+func EditReportPermissionSync(reportId int64, classifyIdSecond int) (err error) {
 	defer func() {
 		if err != nil {
 			utils.FileLog.Info("同步设置报告权限失败, Err: " + err.Error())
@@ -147,7 +149,7 @@ func EditReportPermissionSync(reportId int64, classifyNameSecond string) (err er
 	if utils.CrmEtaServerUrl == "" {
 		return
 	}
-	req := &EditReportPermissionSyncReq{ReportId: reportId, ClassifyNameSecond: classifyNameSecond}
+	req := &EditReportPermissionSyncReq{ReportId: reportId, ClassifyIdSecond: classifyIdSecond}
 	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/crm/chart_permission/report/sync")
 	b, err := crmEtaPost(url, req)
 	if err != nil {
@@ -170,11 +172,11 @@ func EditReportPermissionSync(reportId int64, classifyNameSecond string) (err er
 
 type EditKeywordPermissionSyncReq struct {
 	NewKeyword string
-	Keyword    string
+	ClassifyId int
 }
 
 // EditKeywordPermissionSync 设置报告权限分类名称
-func EditKeywordPermissionSync(newKeyword, keyword string) (err error) {
+func EditKeywordPermissionSync(newKeyword string, classifyId int) (err error) {
 	defer func() {
 		if err != nil {
 			utils.FileLog.Info("同步设置报告权限分类名称失败, Err: " + err.Error())
@@ -184,7 +186,7 @@ func EditKeywordPermissionSync(newKeyword, keyword string) (err error) {
 	if utils.CrmEtaServerUrl == "" {
 		return
 	}
-	req := &EditKeywordPermissionSyncReq{NewKeyword: newKeyword, Keyword: keyword}
+	req := &EditKeywordPermissionSyncReq{NewKeyword: newKeyword, ClassifyId: classifyId}
 	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/crm/chart_permission/keyword/sync")
 	b, err := crmEtaPost(url, req)
 	if err != nil {

+ 843 - 0
services/classify.go

@@ -0,0 +1,843 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// MoveReportClassify 移动分类
+func MoveReportClassify(req models.ClassifyMoveReq) (err error, errMsg string) {
+	classifyId := req.ClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		classifyInfo *models.Classify
+		prevClassify *models.Classify
+		nextClassify *models.Classify
+
+		prevSort int
+		nextSort int
+	)
+
+	// 移动对象为分类, 判断权限
+	classifyInfo, err = models.GetClassifyById(classifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "分类不存在, 请刷新页面"
+			err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		errMsg = "移动失败"
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	} else if classifyInfo.Id == 0 {
+		errMsg = "分类不存在, 请刷新页面"
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+
+	parentClassifyId := classifyInfo.ParentId
+	if prevClassifyId > 0 {
+		prevClassify, err = models.GetClassifyById(prevClassifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "上一个分类不存在, 请刷新页面"
+				err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		if prevClassify.ParentId != parentClassifyId {
+			errMsg = "禁止拖动到其他节点"
+			err = fmt.Errorf(errMsg)
+			return
+		}
+		prevSort = prevClassify.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = models.GetClassifyById(nextClassifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "下一个分类不存在, 请刷新页面"
+				err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		if nextClassify.ParentId != parentClassifyId {
+			errMsg = "禁止拖动到其他节点"
+			err = fmt.Errorf(errMsg)
+			return
+		}
+		nextSort = nextClassify.Sort
+	}
+
+	err, errMsg = moveReportClassify(classifyInfo, prevClassify, nextClassify, parentClassifyId, prevSort, nextSort)
+	return
+}
+
+// moveReportClassify 移动分类
+func moveReportClassify(classifyInfo, prevClassify, nextClassify *models.Classify, parentId, prevSort, nextSort int) (err error, errMsg string) {
+	ob := new(models.Classify)
+	updateCol := make([]string, 0)
+
+	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	if classifyInfo.ParentId != parentId {
+		errMsg = "移动失败"
+		err = fmt.Errorf("不支持目录层级变更")
+		return
+	}
+
+	if prevSort > 0 {
+		//如果是移动在两个兄弟节点之间
+		if nextSort > 0 {
+			//下一个兄弟节点
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevSort == nextSort || prevSort == classifyInfo.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+
+				//变更分类
+				if prevClassify != nil {
+					_ = models.UpdateClassifySortByParentId(parentId, prevClassify.Id, prevClassify.Sort, updateSortStr)
+				} else {
+					_ = models.UpdateClassifySortByParentId(parentId, 0, prevSort, updateSortStr)
+				}
+
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextSort-prevSort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = models.UpdateClassifySortByParentId(parentId, prevClassify.Id, prevSort, updateSortStr)
+					} else {
+						_ = models.UpdateClassifySortByParentId(parentId, 0, prevSort, updateSortStr)
+					}
+
+				}
+			}
+		}
+
+		classifyInfo.Sort = prevSort + 1
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else if prevClassify == nil && nextClassify == nil && parentId > 0 {
+		//处理只拖动到目录里,默认放到目录底部的情况
+		var maxSort int
+		maxSort, err = ob.GetMaxSortByParentId(parentId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("查询组内排序信息失败,Err:" + err.Error())
+			return
+		}
+		classifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else {
+		// 拖动到父级分类的第一位
+		firstPermission, tmpErr := ob.GetFirstClassifyByParentId(parentId)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstPermission != nil && firstPermission.Id != 0 && firstPermission.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = models.UpdateClassifySortByParentId(parentId, firstPermission.Id-1, 0, updateSortStr)
+		}
+
+		classifyInfo.Sort = 0 //那就是排在第一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = classifyInfo.UpdateClassify(updateCol)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("修改失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// AddReportClassify
+// @Description: 添加报告分类
+// @author: Roc
+// @datetime 2024-06-17 11:01:21
+// @param classifyName string
+// @param parentId int
+// @param chartPermissionIdList []int
+// @return err error
+// @return errMsg string
+// @return isSendEmail bool
+func AddReportClassify(classifyName string, parentId int, chartPermissionIdList []int) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `添加失败`
+	item, err := models.GetClassifyByName(classifyName, parentId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取分类信息失败"
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "分类名称:" + classifyName + "已存在"
+			isSendEmail = false
+		}
+		return
+	}
+	if item != nil {
+		errMsg = "分类名称:" + classifyName + "已存在"
+		isSendEmail = false
+		err = errors.New(errMsg)
+		return
+	}
+
+	level := 1
+
+	// 父级分类
+	var parentClassifyItem *models.Classify
+	// 父级分类下的子分类数量
+	var childClassifyCount int
+
+	if parentId > 0 {
+		// 获取父级分类信息
+		parentClassifyItem, err = models.GetClassifyById(parentId)
+		if err != nil {
+			errMsg = "获取父级分类信息失败"
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "父级分类不存在"
+			}
+			return
+		}
+		level = parentClassifyItem.Level + 1
+
+		if level > 3 {
+			errMsg = "分类层级不可超过三级"
+			isSendEmail = false
+			return
+		}
+
+		// 判断是否分类存在待操作的审批单
+		err, errMsg = checkClassifyApprove(parentClassifyItem)
+		if err != nil {
+			return
+		}
+
+		// 获取父级分类下的子分类数量
+		childClassifyCount, err = models.GetCountClassifyChildByParentId(parentId)
+		if err != nil {
+			errMsg = "获取父级分类的子分类信息失败"
+			return
+		}
+
+	}
+
+	nowTime := time.Now().Local()
+	classify := new(models.Classify)
+
+	maxSort, err := classify.GetMaxSort()
+	if err != nil {
+		errMsg = "操作失败"
+		err = errors.New("查询品种排序失败, Err: " + err.Error())
+		return
+	}
+	classify.ClassifyName = classifyName
+	classify.ParentId = parentId
+	classify.CreateTime = nowTime
+	classify.ModifyTime = nowTime
+	classify.Sort = maxSort + 1
+	classify.Enabled = 1
+	classify.ShowType = 1 //默认列表格式
+	classify.IsShow = 1
+	classify.Level = level
+	/*classify.Abstract = req.Abstract
+	classify.Descript = req.Descript
+	classify.Abstract = req.Abstract
+	classify.Descript = req.Descript
+	classify.ReportAuthor = req.ReportAuthor
+	classify.AuthorDescript = req.AuthorDescript
+	classify.ColumnImgUrl = req.ColumnImgUrl
+	classify.ReportImgUrl = req.ReportImgUrl
+	classify.HeadImgUrl = req.HeadImgUrl
+	classify.AvatarImgUrl = req.AvatarImgUrl
+	classify.HomeImgUrl = req.HomeImgUrl
+	classify.ClassifyLabel = req.ClassifyLabel
+	classify.ShowType = req.ShowType
+	classify.HasTeleconference = req.HasTeleconference
+	classify.VipTitle = req.VipTitle
+
+	classify.IsShow = req.IsShow
+	classify.YbFiccSort = req.YbFiccSort
+	classify.YbFiccIcon = req.YbFiccIcon
+	classify.YbFiccPcIcon = req.YbFiccPcIcon
+	classify.YbIconUrl = req.YbIconUrl
+	classify.YbBgUrl = req.YbBgUrl
+	classify.YbListImg = req.YbListImg
+	classify.YbShareBgImg = req.YbShareBgImg
+	classify.YbRightBanner = req.YbRightBanner
+	classify.RelateTel = req.RelateTel
+	classify.RelateVideo = req.RelateVideo
+	if req.ParentId > 0 {
+		parentClassify := new(models.Classify)
+		if parentClassify, err = models.GetClassifyById(req.ParentId); err != nil {
+			br.Msg = "获取父级分类信息失败"
+			br.ErrMsg = "获取父级分类信息失败, Err:" + err.Error()
+			return
+		}
+		updateParent := false
+		updateCols := make([]string, 0)
+		updateCols = append(updateCols, "HasTeleconference")
+		if req.HasTeleconference == 1 {
+			// 二级分类包含电话会,则一级分类也默认包含电话会
+			if parentClassify.HasTeleconference == 0 {
+				parentClassify.HasTeleconference = 1
+				updateParent = true
+			}
+		} else {
+			// 二级分类均无电话会,则一级分类也无电话会
+			if parentClassify.HasTeleconference == 1 {
+				child, err := models.GetClassifyChild(parentClassify.Id, "")
+				if err != nil {
+					br.Msg = "获取子分类失败"
+					br.ErrMsg = "获取子分类失败,Err:" + err.Error()
+					return
+				}
+				// 存在同一级分类下的二级分类有电话会则不变动
+				hasTel := false
+				for i := 0; i < len(child); i++ {
+					if child[i].HasTeleconference == 1 {
+						hasTel = true
+						break
+					}
+				}
+				if !hasTel {
+					parentClassify.HasTeleconference = 0
+					updateParent = true
+				}
+			}
+		}
+		if updateParent {
+			if err = parentClassify.UpdateClassify(updateCols); err != nil {
+				br.Msg = "更新父级分类失败"
+				br.ErrMsg = "更新父级分类失败, Err:" + err.Error()
+				return
+			}
+		}
+	}*/
+	err = models.AddClassify(classify)
+	if err != nil {
+		return
+	}
+
+	//获取报告分类权限列表
+	err = models.EditChartPermissionSearchKeyWordMappingMulti(classifyName, chartPermissionIdList, classify.Id)
+	if err != nil {
+		errMsg = "修改分类权限失败"
+		return
+	}
+
+	// 修改CRM权限
+	go func() {
+		_ = EditClassifyChartPermissionSync(classifyName, classify.Id)
+	}()
+
+	// 如果父级分类不为空的话,那么就标记有子级分类,同时
+	if parentClassifyItem != nil {
+		parentClassifyItem.HasChild = 1
+		parentClassifyItem.UpdateClassify([]string{"HasChild"})
+
+		// 如果以前没有子级分类,那么就继承父级分类下的章节类型(创建新的章节与分类的关系)
+		if childClassifyCount <= 0 {
+			// 继承父级分类下的章节类型(创建新的章节与分类的关系)
+			tmpErr := inheritReportChapterType(parentId, classify.Id)
+			if tmpErr != nil {
+				return
+			}
+
+			// 继承父级分类审批流
+			go inheritReportApproveFlow(parentClassifyItem, classify)
+
+			moveReportByAddClassify(parentClassifyItem, classify)
+		}
+	}
+
+	return
+}
+
+// checkClassifyApprove
+// @Description: 判断分类是否存在待操作的审批单
+// @author: Roc
+// @datetime 2024-06-27 13:19:15
+// @param currClassify *models.Classify
+// @return err error
+// @return errMsg string
+func checkClassifyApprove(currClassify *models.Classify) (err error, errMsg string) {
+	errMsg = `判断是否有审批流关联失败`
+	var firstClassifyId, secondClassifyId int
+	if currClassify.ParentId > 0 {
+		parentClassifyItem, tmpErr := models.GetClassifyById(currClassify.ParentId)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsg = "获取父级分类信息失败"
+			if tmpErr.Error() == utils.ErrNoRow() {
+				errMsg = "父级分类不存在"
+			}
+			return
+		}
+		firstClassifyId = parentClassifyItem.Id
+		secondClassifyId = currClassify.Id
+	} else {
+		firstClassifyId = currClassify.Id
+	}
+	// 校验审批流是否关联了进行中的审批
+	{
+		flowOb := new(report_approve.ReportApproveFlow)
+
+		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId)
+		existPars := make([]interface{}, 0)
+		existPars = append(existPars, report_approve.FlowReportTypeChinese, firstClassifyId, secondClassifyId, 0)
+		flowItem, e := flowOb.GetItemByCondition(existCond, existPars, "")
+		if e != nil {
+			// 父级分类如果没有审批流,那么就正常进行就好了
+			if e.Error() != utils.ErrNoRow() {
+				err = errors.New("获取审批流是否已存在失败, Err: " + e.Error())
+				return
+			}
+			err = nil
+			return
+		}
+		if flowItem == nil {
+			return
+		}
+
+		approvingOb := new(report_approve.ReportApprove)
+		approvingCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveCols.FlowId, report_approve.ReportApproveCols.FlowVersion, report_approve.ReportApproveCols.State)
+		approvingPars := make([]interface{}, 0)
+		approvingPars = append(approvingPars, flowItem.ReportApproveFlowId, flowItem.CurrVersion, report_approve.ReportApproveStateApproving)
+		count, e := approvingOb.GetCountByCondition(approvingCond, approvingPars)
+		if e != nil {
+			err = errors.New("获取审批流关联进行中的审批数失败. Err: " + e.Error())
+			return
+		}
+		if count > 0 {
+			errMsg = "当前有未走完流程的报告,请走完流程后再做变更"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	return
+}
+
+// 关于研报分类,因只允许报告或品种关联在最小分类下,所以当某个父分类(非三级分类)已关联报告或品种,需要在该父分类下增加子分类,则第一个子分类添加成功时,默认把该父分类关联的品种和报告转移至新创建的子分类(第一个子分类)下
+// moveReportByAddClassify
+// @Description: 报告和章节的转移
+// @author: Roc
+// @datetime 2024-06-17 16:29:56
+// @param parentClassifyInfo *models.Classify
+// @param currClassifyInfo *models.Classify
+// @return err error
+func moveReportByAddClassify(parentClassifyInfo, currClassifyInfo *models.Classify) (err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprint("历史报告更改分类失败,父级分类ID:", parentClassifyInfo.Id, ";当前分类ID:", currClassifyInfo.Id, ";错误信息:", err.Error()))
+		}
+	}()
+	if currClassifyInfo.Level > 3 {
+		err = errors.New("父级分类不支持三级分类以上")
+		return
+	}
+
+	// 报告的分类归属调整,转为下一级的分类
+
+	var condition, updateStr string
+	pars := make([]interface{}, 0)
+	switch currClassifyInfo.Level {
+	case 3: // 当前分类是3级分类
+		updateStr += ` classify_id_third = ?,classify_name_third = ?`
+		condition += ` AND classify_id_second = ? `
+	case 2: // 当前分类是2级分类
+		updateStr += ` classify_id_second = ?,classify_name_second = ?`
+		condition += ` AND classify_id_first = ? `
+	default:
+		err = errors.New("错误的分类层级")
+		return
+	}
+
+	pars = append(pars, currClassifyInfo.Id, currClassifyInfo.ClassifyName, parentClassifyInfo.Id)
+
+	// 获取当前分类下的所有章节类型
+	currReportChapterTypeList, err := models.GetAllReportChapterTypeListByClassifyId(currClassifyInfo.Id)
+	if err != nil {
+		return
+	}
+	// 当前的章节类型ID ---> 继承的章节类型ID
+	chapterTypeIdMap := make(map[int]int)
+	for _, v := range currReportChapterTypeList {
+		chapterTypeIdMap[v.ReportChapterTypeId] = v.InheritReportChapterTypeId
+	}
+
+	// 报告转移后,历史章节报告中的type_id也要修复成最新的type_id
+	err = models.ModifyReportClassifyAndReportChapterTypeByCondition(condition, pars, updateStr, chapterTypeIdMap, parentClassifyInfo.Id, currClassifyInfo.Id, currClassifyInfo.ClassifyName)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// inheritReportChapterType
+// @Description: 继承父级分类下的章节类型
+// @author: Roc
+// @datetime 2024-06-17 14:41:04
+// @param parentClassifyId int
+// @param currClassifyId int
+// @return err error
+func inheritReportChapterType(parentClassifyId, currClassifyId int) (err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprint("继承父级分类下的章节类型失败,父级分类ID:", parentClassifyId, ";当前分类ID:", currClassifyId, ";错误信息:", err.Error()))
+		}
+	}()
+	parentReportChapterTypeList, err := models.GetAllReportChapterTypeListByClassifyId(parentClassifyId)
+	if err != nil {
+		return
+	}
+
+	// 如果没有章节类型,那么就直接返回
+	if len(parentReportChapterTypeList) <= 0 {
+		return
+	}
+
+	addList := make([]*models.ReportChapterType, 0)
+	for _, v := range parentReportChapterTypeList {
+		addList = append(addList, &models.ReportChapterType{
+			//ReportChapterTypeId:        0,
+			ReportChapterTypeKey:       v.ReportChapterTypeKey,
+			ReportChapterTypeThumb:     v.ReportChapterTypeThumb,
+			BannerUrl:                  v.BannerUrl,
+			ReportChapterTypeName:      v.ReportChapterTypeName,
+			Sort:                       v.Sort,
+			Enabled:                    v.Enabled,
+			CreatedTime:                time.Now(),
+			LastUpdatedTime:            time.Now(),
+			ResearchType:               v.ResearchType,
+			SelectedImage:              v.SelectedImage,
+			UnselectedImage:            v.UnselectedImage,
+			PcSelectedImage:            v.PcSelectedImage,
+			PcUnselectedImage:          v.PcUnselectedImage,
+			EditImgUrl:                 v.EditImgUrl,
+			TickerTitle:                v.TickerTitle,
+			IsShow:                     v.IsShow,
+			PauseStartTime:             v.PauseStartTime,
+			PauseEndTime:               v.PauseEndTime,
+			IsSet:                      v.IsSet,
+			YbIconUrl:                  v.YbIconUrl,
+			YbBottomIcon:               v.YbBottomIcon,
+			ReportClassifyId:           currClassifyId,
+			InheritReportChapterTypeId: v.ReportChapterTypeId,
+		})
+	}
+
+	obj := models.ReportChapterType{}
+	err = obj.MultiCreate(addList)
+
+	return
+}
+
+// inheritReportApproveFlow
+// @Description: 继承父级分类下的审批流
+// @author: Roc
+// @datetime 2024-06-17 14:41:04
+// @param parentClassifyId int
+// @param currClassifyId int
+// @return err error
+func inheritReportApproveFlow(parentClassifyItem, currClassifyItem *models.Classify) (err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprint("继承父级分类下的审批流失败,父级分类ID:", parentClassifyItem.Id, ";当前分类ID:", currClassifyItem.Id, ";错误信息:", err.Error()))
+		}
+	}()
+
+	var firstClassify, secondClassify, thirdClassify *models.Classify
+	if parentClassifyItem.ParentId > 0 {
+		// 获取父级分类信息
+		firstClassify, err = models.GetClassifyById(parentClassifyItem.ParentId)
+		if err != nil {
+			return
+		}
+
+		secondClassify = parentClassifyItem
+		thirdClassify = currClassifyItem
+	} else {
+		firstClassify = parentClassifyItem
+		secondClassify = currClassifyItem
+	}
+
+	flowObj := report_approve.ReportApproveFlow{}
+
+	// 获取父级的审批流
+	existCond := fmt.Sprintf(` AND %s = ? AND %s = ? `, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId)
+	existPars := make([]interface{}, 0)
+	existPars = append(existPars, report_approve.FlowReportTypeChinese, firstClassify.Id)
+
+	// 如果这是第三级,那么说明只需要查找第二级的审批配置就好了
+	if thirdClassify != nil {
+		existCond = fmt.Sprintf(`%s AND %s = ?`, existCond, report_approve.ReportApproveFlowCols.ClassifySecondId)
+		existPars = append(existPars, secondClassify.Id)
+	}
+	//if thirdClassify != nil {
+	//	existCond = fmt.Sprintf(`%s AND %s = ?`, existCond, report_approve.ReportApproveFlowCols.ClassifyThirdId)
+	//	existPars = append(existPars, thirdClassify.Id)
+	//}
+
+	parentFlow, err := flowObj.GetItemByCondition(existCond, existPars, "")
+	if err != nil {
+		// 如果没有配置审批流,那么就直接返回
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		}
+		return
+	}
+
+	// 获取父级的审批节点
+	nodeObj := report_approve.ReportApproveNode{}
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, parentFlow.ReportApproveFlowId, parentFlow.CurrVersion)
+	parentNodeList, err := nodeObj.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if err != nil {
+		return
+	}
+
+	// 新审批流
+	currFlow := &report_approve.ReportApproveFlow{
+		ReportApproveFlowId: 0,
+		FlowName:            currClassifyItem.ClassifyName,
+		ReportType:          parentFlow.ReportType,
+		ClassifyFirstId:     firstClassify.Id,
+		ClassifySecondId:    secondClassify.Id,
+		//ClassifyThirdId:     0,
+		CurrVersion: 1,
+		Enabled:     1,
+		CreateTime:  time.Now().Local(),
+		ModifyTime:  time.Now().Local(),
+	}
+	if thirdClassify != nil {
+		currFlow.ClassifyThirdId = thirdClassify.Id
+	}
+
+	// 新审批流的节点
+	nodeItems := make([]*report_approve.ReportApproveNode, 0)
+	for _, v := range parentNodeList {
+		n := &report_approve.ReportApproveNode{
+			//ReportApproveNodeId: 0,
+			//ReportApproveFlowId: 0,
+			PrevNodeId:  0,
+			NextNodeId:  0,
+			NodeType:    v.NodeType,
+			ApproveType: v.ApproveType,
+			Users:       v.Users,
+			CurrVersion: 1,
+			CreateTime:  time.Now().Local(),
+		}
+		nodeItems = append(nodeItems, n)
+	}
+
+	// 新增审批流和节点
+	err = flowObj.CreateFlowAndNodes(currFlow, nodeItems)
+	if err != nil {
+		return
+	}
+
+	parentFlow.Enabled = 0
+	err = parentFlow.Update([]string{"Enabled"})
+
+	return
+}
+
+// EditReportClassify
+// @Description: 编辑报告分类
+// @author: Roc
+// @datetime 2024-06-17 13:31:33
+// @param classifyId int
+// @param classifyName string
+// @param chartPermissionIdList []int
+// @return err error
+// @return errMsg string
+// @return isSendEmail bool
+func EditReportClassify(classifyId int, classifyName string, chartPermissionIdList []int) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `修改失败`
+
+	item, err := models.GetClassifyById(classifyId)
+	if err != nil {
+		errMsg = "获取分类信息失败"
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "分类不存在, 或已被删除"
+			isSendEmail = false
+		}
+		return
+	}
+	originName := item.ClassifyName
+
+	// 重名校验
+	existName, e := models.GetClassifyByName(classifyName, item.ParentId)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		errMsg = "获取信息失败"
+		err = errors.New("获取重名分类失败, Err: " + err.Error())
+		return
+	}
+	if existName != nil && existName.Id != item.Id {
+		errMsg = "分类名称:" + classifyName + "已存在"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	item.ClassifyName = classifyName
+
+	// ETA1.8.3:不允许修改上级分类  2024-6-17 13:21:01
+	//item.ParentId = req.ParentId
+	item.ModifyTime = time.Now().Local()
+	cols := make([]string, 0)
+	cols = append(cols, "ClassifyName", "ModifyTime")
+	err = item.UpdateClassify(cols)
+	if err != nil {
+		return
+	}
+
+	err = models.EditChartPermissionSearchKeyWordMappingMulti(item.ClassifyName, chartPermissionIdList, item.Id)
+	if err != nil {
+		errMsg = "修改分类权限失败"
+		return
+	}
+
+	// 修改CRM权限
+	go func() {
+		_ = EditClassifyChartPermissionSync(item.ClassifyName, item.Id)
+	}()
+
+	// TODO 修改分类的关联品种时,历史报告中关联的品种怎么处理?
+	// 更新报告分类名称/父级分类后
+	go func() {
+		_ = AfterUpdateClassifyNameOrParent(item.Id, item.ParentId, item.ParentId, originName, item.ClassifyName, item.Level)
+	}()
+
+	return
+}
+
+// GetClassifyTreeRecursive 递归获取分类树形结构
+func GetClassifyTreeRecursive(list []*models.ClassifyItem, parentId int) []*models.ClassifyItem {
+	res := make([]*models.ClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Child = GetClassifyTreeRecursive(list, v.Id)
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// GetParentClassifyListByParentIdList
+// @Description: 递归获取父级分类信息,正常来讲只有三次
+// @author: Roc
+// @datetime 2024-06-19 13:23:33
+// @param parentClassifyIdList []int
+// @return list []*models.ClassifyList
+// @return err error
+func GetParentClassifyListByParentIdList(parentClassifyIdList []int) (list []*models.ClassifyList, err error) {
+	num := len(parentClassifyIdList)
+	if num <= 0 {
+		return
+	}
+	list, err = models.GetClassifyListByParentIdList(parentClassifyIdList)
+	if err != nil {
+		return
+	}
+
+	// 是否还有上级
+	{
+		currParentClassifyIdList := make([]int, 0)
+		for _, v := range list {
+			if v.ParentId > 0 {
+				currParentClassifyIdList = append(currParentClassifyIdList, v.ParentId)
+			}
+		}
+
+		if len(currParentClassifyIdList) > 0 {
+			tmpList, tmpErr := GetParentClassifyListByParentIdList(currParentClassifyIdList)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			list = append(tmpList, list...)
+		}
+	}
+
+	return
+}
+
+// GetClassifyListTreeRecursive
+// @Description: 递归获取分类树形结构
+// @author: Roc
+// @datetime 2024-06-19 13:23:28
+// @param list []*models.ClassifyList
+// @param parentId int
+// @return []*models.ClassifyList
+func GetClassifyListTreeRecursive(list []*models.ClassifyList, parentId int) []*models.ClassifyList {
+	res := make([]*models.ClassifyList, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Child = GetClassifyListTreeRecursive(list, v.Id)
+			res = append(res, v)
+		}
+	}
+
+	// 前端的JP需要我这么返回
+	if len(res) <= 0 {
+		res = nil
+	}
+
+	return res
+}
+
+// BySortAndCreateTime 用来排序,先按Sort字段升序排序,若Sort相同,则按照CreateTime字段升序排序。
+type BySortAndCreateTime []*models.ClassifyList
+
+func (a BySortAndCreateTime) Len() int {
+	return len(a)
+}
+
+func (a BySortAndCreateTime) Swap(i, j int) {
+	a[i], a[j] = a[j], a[i]
+}
+
+func (a BySortAndCreateTime) Less(i, j int) bool {
+	if a[i].Sort == a[j].Sort {
+		return a[i].CreateTime.Before(a[j].CreateTime)
+	}
+	return a[i].Sort < a[j].Sort
+}
+
+// SortClassifyListBySortAndCreateTime sorts the ClassifyList slice by Sort and then CreateTime in ascending order.
+func SortClassifyListBySortAndCreateTime(classifyList []*models.ClassifyList) {
+	sort.Sort(BySortAndCreateTime(classifyList))
+}

+ 136 - 374
services/report.go

@@ -5,13 +5,13 @@ import (
 	"errors"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/models/company"
+	"eta/eta_mobile/models/report"
 	"eta/eta_mobile/models/system"
 	"eta/eta_mobile/services/alarm_msg"
 	"eta/eta_mobile/services/public_api"
 	"eta/eta_mobile/utils"
 	"fmt"
 	"github.com/PuerkitoBio/goquery"
-	"github.com/rdlucklib/rdluck_tools/http"
 	"html"
 	"os"
 	"regexp"
@@ -45,246 +45,17 @@ func GetReportContentSub(content string) (contentSub string, err error) {
 	return
 }
 
-type ZgParam struct {
-}
-
-// 找钢网
-func ZhaoGangSend(report *models.ReportDetail) (err error) {
-	defer func() {
-		if err != nil {
-			go alarm_msg.SendAlarmMsg("发送报告至找刚网失败,Err"+err.Error(), 3)
-			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送报告至找刚网失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
-		}
-	}()
-	reportIdStr := strconv.Itoa(report.Id)
-	articleId := utils.MD5(reportIdStr)
-
-	var reportType int
-	if report.ClassifyNameSecond == "知白守黑日评" {
-		reportType = 1
-	} else {
-		reportType = 2
-	}
-	contentHtml := html.UnescapeString(report.Content)
-
-	doc, err := goquery.NewDocumentFromReader(strings.NewReader(contentHtml))
-	if err != nil {
-		fmt.Println("Create Doc Err:" + err.Error())
-		err = errors.New("Create Doc Err:" + err.Error())
-		return
-	}
-	doc.Find("p").Each(func(i int, p *goquery.Selection) {
-		phtml, err := p.Html()
-		if err != nil {
-			fmt.Println("Err:" + err.Error())
-			return
-		}
-		if phtml == "<br/>" {
-			if i == 0 {
-				p.Remove()
-			} else {
-				p.ReplaceWithHtml("<br/>")
-			}
-		} else {
-			p.SetAttr("style", "line-height:30px")
-		}
-	})
-	doc.Find("img").Each(func(i int, img *goquery.Selection) {
-		img.SetAttr("style", "width:100%;")
-	})
-	contentHtml, _ = doc.Find("body").Html()
-
-	createDate, _ := time.Parse(utils.FormatDateTime, report.CreateTime)
-	createDay := createDate.Format("0102")
-	title := "【第 " + strconv.Itoa(report.Stage) + "期|FICC" + "】 " + report.Title + "(" + createDay + ")"
-	contentSummary := report.Abstract
-
-	postUrl := `http://appserver.index.zhaogang.com/double.index.appserver.service/api/v1/wechat/article/hongze/push`
-	signStr := "zhaogang_data_vip" + "articleId" + articleId + "type" + strconv.Itoa(reportType) + "title" + title + "contentSummary" + contentSummary + "zhaogang_data_vip"
-	fmt.Println("signStr:", signStr)
-	sign := utils.MD5(signStr)
-	disclaimers := `<p>1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。</p >         <p>2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。</p >         <p>3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。</p >         <p>4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。</p >`
-	param := make(map[string]interface{})
-	dataMap := make(map[string]interface{})
-	contentMap := make(map[string]interface{})
-
-	dataMap["articleId"] = articleId
-	dataMap["type"] = reportType
-	dataMap["title"] = title
-	dataMap["contentSummary"] = contentSummary
-
-	contentMap["contentHtml"] = contentHtml
-	contentMap["articleAuthor"] = report.Author
-	//contentMap["publishedTime"] = report.PublishTime.Format(utils.FormatDateTime)
-	contentMap["publishedTime"] = report.PublishTime
-	contentMap["audioName"] = report.VideoName
-	contentMap["audioUrl"] = report.VideoUrl
-
-	videoPlaySeconds := report.VideoPlaySeconds
-	f, _ := strconv.ParseFloat(videoPlaySeconds, 64)
-	contentMap["audioLength"] = f * 1000
-	contentMap["disclaimers"] = disclaimers
-
-	param["cardData"] = dataMap
-	param["contentData"] = contentMap
-	param["sign"] = strings.ToUpper(sign)
-
-	paramJson, err := json.Marshal(param)
-	if err != nil {
-		fmt.Println("param json.Marshal Err:" + err.Error())
-		err = errors.New("param json.Marshal Err:" + err.Error())
-		return
-	}
-
-	utils.FileLog.Info("ZhaoGangSend parms:%s", string(paramJson))
-	result, err := http.Post(postUrl, string(paramJson), "application/json")
-	if err != nil {
-		fmt.Println("post err:" + err.Error())
-		err = errors.New("post Err:" + err.Error())
-		return
-	}
-	utils.FileLog.Info("ZhaoGangSend Result:%s", string(result))
-	//返回数据校验
-	mapResult := make(map[string]interface{})
-	err = json.Unmarshal(result, &mapResult)
-	if err != nil {
-		fmt.Println("找钢网返回数据转json失败: err:", err.Error(), ";返回数据:", string(result))
-		err = errors.New(fmt.Sprint("找钢网返回数据转json失败: err:", err.Error(), ";返回数据:", string(result)))
-		return
-	}
-	if resultCode, ok := mapResult["code"]; ok {
-		tmpResultCode := resultCode.(float64)
-		if tmpResultCode != 200 {
-			fmt.Println("找钢网返回数据异常,code返回参异常;返回数据:", string(result))
-			err = errors.New(fmt.Sprint("找钢网返回数据异常,code返回参异常;返回数据:", string(result)))
-			return
-		}
-	} else {
-		fmt.Println("找钢网返回数据异常,缺少code返回参;返回数据:", string(result))
-		err = errors.New(fmt.Sprint("找钢网返回数据异常,缺少code返回参;返回数据:", string(result)))
-		return
-	}
-
-	utils.FileLog.Info("%s", string(result))
-	return
-}
-
-/*func init() {
-	fmt.Println("start")
-	vint := 845
-	report, err := models.GetReportById(vint)
-	if err != nil {
-		fmt.Println("GetReportById Err", err.Error())
-		return
-	}
-	ZhaoGangSend(report)
-	fmt.Println("end")
-	return
-}*/
-
-// PublishDayWeekReport 发布晨周报
-func PublishDayWeekReport(reportId int) (tips string, err error) {
-	report, err := models.GetReportByReportId(reportId)
-	if err != nil {
-		return
-	}
-	if report.State == 2 {
-		return
-	}
-	chapters, err := models.GetChapterListByReportId(reportId)
-	if err != nil {
-		return
-	}
-	chapterLen := len(chapters)
-	if chapterLen <= 0 {
-		err = errors.New("报告章节为空,不可发布")
-		return
-	}
-	reportType := chapters[0].ReportType
-	// 校验章节
-	publishReport, tips, publishIdArr, unPublishIdArr, err := checkDayWeekChapterWrite(chapters, reportType)
-	if err != nil {
-		return
-	}
-	publishLen := len(publishIdArr)
-	if publishLen <= 0 {
-		err = errors.New("报告章节均不可发布")
-		return
-	}
-
-	// 需发布整期
-	updateCols := make([]string, 0)
-	if publishReport {
-		updateCols = append(updateCols, "Title", "State", "ModifyTime")
-
-		// 发布后标题调整
-		title := report.Title
-		title = strings.ReplaceAll(title, "【弘则FICC晨报】", "")
-		title = strings.ReplaceAll(title, "【弘则FICC周报】", "")
-		if title == "" {
-			// 取第一个需发布章节的标题
-			firstId := publishIdArr[0]
-			firstTitle := ""
-			for i := 0; i < chapterLen; i++ {
-				if chapters[i].ReportChapterId == firstId {
-					firstTitle = chapters[i].Title
-					break
-				}
-			}
-			title = firstTitle
-		}
-		report.Title = title
-		report.State = 2
-
-		// 研报后台4.4 只在没有发布过时更新发布时间,其余均按模版消息发送时间当作发布时间
-		if report.MsgIsSend == 0 || report.PublishTime.IsZero() {
-			report.PublishTime = time.Now().Local()
-			updateCols = append(updateCols, "PublishTime")
-
-		}
-		report.ModifyTime = time.Now().Local()
-	}
-	publishIdStr := utils.IntArr2joinString(publishIdArr, ",")
-	//unPublishIdStr := utils.IntArr2joinString(unPublishIdArr, ",")
-
-	if e := models.PublishReportAndChapter(report, publishIdArr, unPublishIdArr, publishReport, updateCols); e != nil {
-		err = errors.New("发布报告及章节失败")
-		return
-	}
-	// 生成章节音频
-	go func() {
-		_ = UpdateChaptersVideo(publishIdStr)
-	}()
-	// 更新报告ES
-	go func() {
-		_ = UpdateReportEs(report.Id, 2)
-	}()
-
-	// 发布时备份内容
-	go SaveReportLogs(report, chapters, report.AdminId, report.AdminRealName)
-	return
-}
-
 // UpdateChaptersVideo 更新章节音频
-func UpdateChaptersVideo(chapterIds string) (err error) {
+func UpdateChaptersVideo(ids []int) (err error) {
 	defer func() {
 		if err != nil {
-			utils.FileLog.Error("UpdateChaptersVideo, chapterIds:%s, Err:%s", chapterIds, err.Error())
-			go alarm_msg.SendAlarmMsg("更新章节音频失败, 章节ID: "+chapterIds+", Err: "+err.Error(), 3)
+			utils.FileLog.Error("UpdateChaptersVideo, chapterIds:%v, Err:%s", ids, err.Error())
+			go alarm_msg.SendAlarmMsg(fmt.Sprintf("更新章节音频失败, 章节ID: %v; Err: "+err.Error(), ids), 3)
 		}
 	}()
-	if chapterIds == "" {
+	if len(ids) <= 0 {
 		return
 	}
-	ids := make([]int, 0)
-	chapterIdArr := strings.Split(chapterIds, ",")
-	for _, v := range chapterIdArr {
-		id, e := strconv.Atoi(v)
-		if e != nil {
-			return
-		}
-		ids = append(ids, id)
-	}
 
 	chapterList, err := models.GetChapterListByChapterIds(ids)
 	if err != nil {
@@ -317,41 +88,41 @@ func UpdateChaptersVideo(chapterIds string) (err error) {
 }
 
 // PublishTodayDayReport 发布今日晨报
-func PublishTodayDayReport() (err error) {
-	nowTime := time.Now()
-	startTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
-	endTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 23, 59, 59, 0, time.Local)
-	todayReport, err := models.GetUnPublishDayReport(startTime, endTime)
-	if err != nil {
-		if err.Error() == utils.ErrNoRow() { //如果是找不到待发送的晨报,那么需要将err置空
-			err = nil
-		}
-		return
-	}
-	if todayReport != nil {
-		if _, tmpErr := PublishDayWeekReport(todayReport.Id); tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		// 定时发布的晨报自动推送客群
-		reportDetail, tmpErr := models.GetReportById(todayReport.Id)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		// 推送模板消息
-		if tmpErr = SendMiniProgramReportWxMsg(todayReport.Id); tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		if tmpErr = models.ModifyReportThsMsgIsSend(reportDetail); tmpErr != nil {
-			err = tmpErr
-			return
-		}
-	}
-
-	return
-}
+//func PublishTodayDayReport() (err error) {
+//	nowTime := time.Now()
+//	startTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
+//	endTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 23, 59, 59, 0, time.Local)
+//	todayReport, err := models.GetUnPublishDayReport(startTime, endTime)
+//	if err != nil {
+//		if err.Error() == utils.ErrNoRow() { //如果是找不到待发送的晨报,那么需要将err置空
+//			err = nil
+//		}
+//		return
+//	}
+//	if todayReport != nil {
+//		if _, tmpErr, _ := PublishChapterReport(todayReport, "", nil); tmpErr != nil {
+//			err = tmpErr
+//			return
+//		}
+//		// 定时发布的晨报自动推送客群
+//		reportDetail, tmpErr := models.GetReportById(todayReport.Id)
+//		if tmpErr != nil {
+//			err = tmpErr
+//			return
+//		}
+//		// 推送模板消息
+//		if tmpErr = SendMiniProgramReportWxMsg(todayReport.Id); tmpErr != nil {
+//			err = tmpErr
+//			return
+//		}
+//		if tmpErr = models.ModifyReportThsMsgIsSend(reportDetail); tmpErr != nil {
+//			err = tmpErr
+//			return
+//		}
+//	}
+//
+//	return
+//}
 
 // UpdateReportEs 更新报告/章节Es
 func UpdateReportEs(reportId int, publishState int) (err error) {
@@ -370,46 +141,21 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 			return
 		}
 		if len(chapterList) > 0 {
-			for i := 0; i < len(chapterList); i++ {
-				// 章节对应的品种
-				permissionList, tmpErr := models.GetChapterTypePermissionByTypeIdAndResearchType(chapterList[i].TypeId, chapterList[i].ReportType)
-				if tmpErr != nil {
-					return
-				}
-				categoryArr := make([]string, 0)
-				if len(permissionList) > 0 {
-					for ii := 0; ii < len(permissionList); ii++ {
-						categoryArr = append(categoryArr, permissionList[ii].PermissionName)
-					}
-				}
-				aliasArr, _ := addCategoryAliasToArr(categoryArr)
-				chapterCategories := strings.Join(aliasArr, ",")
-
-				esChapter := &models.ElasticReportDetail{
-					ReportId:           chapterList[i].ReportId,
-					ReportChapterId:    chapterList[i].ReportChapterId,
-					Title:              chapterList[i].Title,
-					Abstract:           chapterList[i].Abstract,
-					BodyContent:        utils.TrimHtml(html.UnescapeString(chapterList[i].Content)),
-					PublishTime:        chapterList[i].PublishTime.Format(utils.FormatDateTime),
-					PublishState:       chapterList[i].PublishState,
-					Author:             chapterList[i].Author,
-					ClassifyIdFirst:    chapterList[i].ClassifyIdFirst,
-					ClassifyNameFirst:  chapterList[i].ClassifyNameFirst,
-					ClassifyIdSecond:   0,
-					ClassifyNameSecond: "",
-					Categories:         chapterCategories,
-					StageStr:           strconv.Itoa(chapterList[i].Stage),
-				}
-				chapterDocId := fmt.Sprintf("%d-%d", reportInfo.Id, chapterList[i].ReportChapterId)
-				if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
+			// 更新章节的es数据
+			for _, chapterInfo := range chapterList {
+				err = updateReportChapterEsByChapter(chapterInfo)
+				if err != nil {
 					return
 				}
 			}
 		}
 	} else {
-		//if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
-		permissionList, tmpErr := models.GetChartPermissionNameFromMappingByKeyword(reportInfo.ClassifyNameSecond, "rddp")
+		// 获取最小分类的id
+		minClassifyId, _, tmpErr := getMinClassify(reportInfo)
+		if tmpErr != nil {
+			return
+		}
+		permissionList, tmpErr := models.GetChartPermissionNameFromMappingByKeyword("rddp", minClassifyId)
 		if tmpErr != nil {
 			return
 		}
@@ -422,6 +168,12 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 		//}
 	}
 
+	// 最小单位的分类id
+	minClassifyId, minClassifyName, err := getMinClassify(reportInfo)
+	if err != nil {
+		return
+	}
+
 	// 新增报告ES
 	esReport := &models.ElasticReportDetail{
 		ReportId:           reportInfo.Id,
@@ -436,6 +188,8 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 		ClassifyNameFirst:  reportInfo.ClassifyNameFirst,
 		ClassifyIdSecond:   reportInfo.ClassifyIdSecond,
 		ClassifyNameSecond: reportInfo.ClassifyNameSecond,
+		ClassifyId:         minClassifyId,
+		ClassifyName:       minClassifyName,
 		Categories:         categories,
 		StageStr:           strconv.Itoa(reportInfo.Stage),
 	}
@@ -481,7 +235,12 @@ func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error)
 	return
 }
 
-// UpdateReportChapterEs 更新报告章节ES
+// UpdateReportChapterEs
+// @Description: 通过章节id更新报告章节ES
+// @author: Roc
+// @datetime 2024-06-20 13:16:22
+// @param reportChapterId int
+// @return err error
 func UpdateReportChapterEs(reportChapterId int) (err error) {
 	if reportChapterId <= 0 {
 		return
@@ -490,26 +249,44 @@ func UpdateReportChapterEs(reportChapterId int) (err error) {
 	if err != nil {
 		return
 	}
+
+	err = updateReportChapterEsByChapter(chapterInfo)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// updateReportChapterEsByChapter
+// @Description: 通过章节详情更新报告章节ES
+// @author: Roc
+// @datetime 2024-06-20 13:16:11
+// @param chapterInfo *models.ReportChapter
+// @return err error
+func updateReportChapterEsByChapter(chapterInfo *models.ReportChapter) (err error) {
 	// 章节对应的品种
-	permissionList, tmpErr := models.GetChapterTypePermissionByTypeIdAndResearchType(chapterInfo.TypeId, chapterInfo.ReportType)
+	obj := report.ReportChapterPermissionMapping{}
+	permissionList, tmpErr := obj.GetPermissionItemListById(chapterInfo.ReportChapterId)
 	if tmpErr != nil {
 		return
 	}
 	categoryArr := make([]string, 0)
 	if len(permissionList) > 0 {
 		for ii := 0; ii < len(permissionList); ii++ {
-			categoryArr = append(categoryArr, permissionList[ii].PermissionName)
+			categoryArr = append(categoryArr, permissionList[ii].ChartPermissionName)
 		}
 	}
 	aliasArr, _ := addCategoryAliasToArr(categoryArr)
 	categories := strings.Join(aliasArr, ",")
 	// 新增/编辑ES
+
 	esChapter := &models.ElasticReportDetail{
 		ReportId:           chapterInfo.ReportId,
 		ReportChapterId:    chapterInfo.ReportChapterId,
 		Title:              chapterInfo.Title,
 		Abstract:           chapterInfo.Abstract,
-		BodyContent:        utils.TrimHtml(html.EscapeString(chapterInfo.Content)),
+		BodyContent:        utils.TrimHtml(html.UnescapeString(chapterInfo.Content)),
 		PublishTime:        chapterInfo.PublishTime.Format(utils.FormatDateTime),
 		PublishState:       chapterInfo.PublishState,
 		Author:             chapterInfo.Author,
@@ -517,6 +294,8 @@ func UpdateReportChapterEs(reportChapterId int) (err error) {
 		ClassifyNameFirst:  chapterInfo.ClassifyNameFirst,
 		ClassifyIdSecond:   0,
 		ClassifyNameSecond: "",
+		ClassifyId:         chapterInfo.ClassifyIdFirst,
+		ClassifyName:       chapterInfo.ClassifyNameFirst,
 		Categories:         categories,
 		StageStr:           strconv.Itoa(chapterInfo.Stage),
 	}
@@ -559,28 +338,6 @@ func DeleteReportAndChapter(reportId int) (err error) {
 	return
 }
 
-// UpdatePublishedReportToEs 更新已发布的报告ES
-func UpdatePublishedReportToEs() (err error) {
-	// 获取所有已发布的报告
-	var condition string
-	var pars []interface{}
-	condition = ` AND state IN (2, 6) `
-	reportList, err := models.GetReportList(condition, pars, "ficc", 1, 5000)
-	count := 0
-	failCount := 0
-	for i := 0; i < len(reportList); i++ {
-		if err = UpdateReportEs(reportList[i].Id, 2); err != nil {
-			fmt.Printf("更新失败, report_id: %d, Err: %s\n", reportList[i].Id, err.Error())
-			failCount += 1
-		} else {
-			count += 1
-		}
-	}
-	fmt.Printf("报告总数:%d, 更新成功数: %d, 更新失败数: %d", len(reportList), count, failCount)
-
-	return
-}
-
 // 替换报告内容中的base64图片
 func replaceReportBase64ToImg(content string) (newContent string, err error) {
 	if content == "" {
@@ -700,13 +457,12 @@ func UpdateReportVideo(reportId int) (err error) {
 			err = tmpErr
 			return
 		}
-		chapterIdArr := make([]string, 0)
+		chapterIdArr := make([]int, 0)
 		for i := 0; i < len(chapterList); i++ {
-			chapterIdArr = append(chapterIdArr, strconv.Itoa(chapterList[i].ReportChapterId))
+			chapterIdArr = append(chapterIdArr, chapterList[i].ReportChapterId)
 		}
-		chapterIds := strings.Join(chapterIdArr, ",")
 		//go UpdateChaptersVideo(chapterIds)
-		err = UpdateChaptersVideo(chapterIds)
+		err = UpdateChaptersVideo(chapterIdArr)
 	} else {
 		// 更新报告音频
 		if reportInfo.VideoUrl != "" {
@@ -937,6 +693,12 @@ func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) {
 func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId int64, reportCode, errMsg string, err error) {
 	contentSub := ""
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			errMsg = "存在非法标签"
+			err = errors.New("存在非法标签, Err: " + e.Error())
+			return
+		}
 		contentClean, e := FilterReportContentBr(req.Content)
 		if e != nil {
 			errMsg = "内容去除前后空格失败"
@@ -951,7 +713,7 @@ func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId in
 		}
 		contentSub = sub
 	}
-	maxStage, e := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond)
+	maxStage, e := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird)
 	if e != nil {
 		errMsg = "期数获取失败!"
 		err = errors.New("期数获取失败,Err:" + e.Error())
@@ -977,36 +739,31 @@ func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId in
 	item.ReportVersion = req.ReportVersion
 	item.AdminId = adminInfo.AdminId
 	item.AdminRealName = adminInfo.RealName
-	newReportId, e = models.AddReport(item)
-	if e != nil {
-		errMsg = "保存失败"
-		err = errors.New("保存失败,Err:" + e.Error())
-		return
-	}
 
-	// 处理权限
-	//if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
-	go func() {
-		permissionItems, e := models.GetPermission(req.ClassifyNameSecond)
-		if e != nil {
-			alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
-		}
-		for _, v := range permissionItems {
-			e = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
-			if e != nil {
-				alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
-			}
-		}
-		// 同步crm权限
-		_ = EditReportPermissionSync(newReportId, req.ClassifyNameSecond)
-	}()
-	//}
+	item.ClassifyIdThird = req.ClassifyIdThird
+	item.ClassifyNameThird = req.ClassifyNameThird
+	// 产品要求,如果是多人协作,那么就是章节类型的报告
+	if req.CollaborateType == 2 {
+		item.HasChapter = 1
+		item.ChapterType = ""
+	}
+	item.LastModifyAdminId = adminInfo.AdminId
+	item.LastModifyAdminName = adminInfo.RealName
+	item.ContentModifyTime = time.Now()
+	item.NeedSplice = 1
+	item.ContentStruct = html.EscapeString(req.ContentStruct)
+	item.HeadImg = req.HeadImg
+	item.EndImg = req.EndImg
+	item.CanvasColor = req.CanvasColor
+	item.HeadResourceId = req.HeadResourceId
+	item.EndResourceId = req.EndResourceId
+	item.CollaborateType = req.CollaborateType
+	item.ReportLayout = req.ReportLayout
+	item.IsPublicPublish = req.IsPublicPublish
+	item.ReportCreateTime = time.Now()
+
+	err, errMsg = AddReportAndChapter(item, 0, req.GrantAdminIdList)
 
-	reportCode = utils.MD5(strconv.Itoa(int(newReportId)))
-	//修改唯一编码
-	{
-		go models.ModifyReportCode(newReportId, reportCode)
-	}
 	return
 }
 
@@ -1190,10 +947,14 @@ func GetReportContentSubWithoutIframe(content string) (contentSub string, err er
 }
 
 // UpdateReportEditMark 更新研报当前更新状态
-// status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
-func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName string) (ret models.MarkReportResp, err error) {
+// status 枚举值 1:编辑中, 2:只做查询,3:完成编辑
+func UpdateReportEditMark(reportId, reportChapterId, nowUserId, status int, nowUserName, lang string) (ret models.MarkReportResp, err error) {
 	//更新标记key
 	key := fmt.Sprint(`crm:report:edit:`, reportId)
+	//  章节id不为0则加上章节id
+	if reportChapterId > 0 {
+		key = fmt.Sprint(key, ":", reportChapterId)
+	}
 	ret.Status = 0
 	ret.Msg = "无人编辑"
 
@@ -1222,9 +983,6 @@ func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName string) (
 		classifyNameFirst = reportInfo.ClassifyNameFirst
 	}
 
-	if classifyNameFirst == "晨报" || classifyNameFirst == "周报" {
-		return
-	}
 	if opUserId > 0 && opUserId != nowUserId {
 		editor := opUser.Editor
 		if editor == "" {
@@ -1238,7 +996,11 @@ func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName string) (
 		}
 
 		ret.Status = 1
-		ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+		if lang == utils.EnLangVersion {
+			ret.Msg = fmt.Sprintf("%s is currently editing the report", editor)
+		} else {
+			ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+		}
 		ret.Editor = editor
 		return
 	}
@@ -1250,12 +1012,12 @@ func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName string) (
 			return
 		}
 		if opUserId > 0 {
-			utils.Rc.Do("SETEX", key, int64(180), string(bt)) //3分钟缓存
+			utils.Rc.Do("SETEX", key, int64(60), string(bt)) //3分钟缓存
 		} else {
-			utils.Rc.SetNX(key, string(bt), time.Second*60*3) //3分钟缓存
+			utils.Rc.SetNX(key, string(bt), time.Second*60*1) //3分钟缓存
 		}
-	} else if status == 0 {
-		//清除编辑缓存
+	} else if status == 3 {
+		//完成编辑,开始清除编辑缓存
 		_ = utils.Rc.Delete(key)
 	}
 	return
@@ -1282,7 +1044,7 @@ func SaveReportLogs(item *models.Report, chapters []*models.ReportChapter, admin
 	}()
 
 	if item != nil {
-		e := models.AddReportSaveLog(item.Id, item.AdminId, item.Content, item.ContentSub, item.AdminRealName)
+		e := models.AddReportSaveLog(item.Id, item.AdminId, item.Content, item.ContentSub, item.ContentStruct, item.CanvasColor, item.AdminRealName, item.HeadResourceId, item.EndResourceId)
 		if e != nil {
 			err = fmt.Errorf("AddReportSaveLog: %s", e.Error())
 			return

+ 59 - 25
services/report_approve.go

@@ -28,14 +28,16 @@ func GetReportClassifyTreeRecursive(list []*models.Classify, parentId int) []*re
 }
 
 // CheckReportApproveFlowChange 校验是否可变更分类
-func CheckReportApproveFlowChange(reportType, classifyFirstId, classifySecondId int) (ok bool, err error) {
+func CheckReportApproveFlowChange(reportType, classifyFirstId, classifySecondId, classifyThirdId int) (ok bool, err error) {
 	var count int
 	cond := ` AND classify_id_first = ? AND classify_id_second = ? AND state = ?`
 	pars := make([]interface{}, 0)
 	pars = append(pars, classifyFirstId, classifySecondId, models.ReportStateWaitApprove)
 	switch reportType {
 	case report_approve.FlowReportTypeChinese:
-		ct, e := models.GetReportListCount(cond, pars, "")
+		cond += ` AND classify_id_third = ?  `
+		pars = append(pars, classifyThirdId)
+		ct, e := models.GetReportListCount(cond, pars)
 		if e != nil {
 			err = fmt.Errorf("GetReportListCount err: %s", e.Error())
 			return
@@ -67,7 +69,7 @@ func CheckReportApproveFlowChange(reportType, classifyFirstId, classifySecondId
 }
 
 // CheckReportOpenApprove 校验报告是否开启了审批流
-func CheckReportOpenApprove(reportType, firstId, secondId int) (opening bool, err error) {
+func CheckReportOpenApprove(reportType, firstId, secondId, thirdId int) (opening bool, err error) {
 	// 获取审批配置
 	confMap, e := models.GetBusinessConf()
 	if e != nil {
@@ -79,9 +81,9 @@ func CheckReportOpenApprove(reportType, firstId, secondId int) (opening bool, er
 
 	// 查询对应分类是否有审批流
 	flowOb := new(report_approve.ReportApproveFlow)
-	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId)
 	flowPars := make([]interface{}, 0)
-	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowPars = append(flowPars, reportType, firstId, secondId, thirdId)
 	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
@@ -97,7 +99,7 @@ func CheckReportOpenApprove(reportType, firstId, secondId int) (opening bool, er
 }
 
 // CheckReportCurrState 校验报告当前应有的状态
-func CheckReportCurrState(reportType, firstId, secondId, operate int) (state int, err error) {
+func CheckReportCurrState(reportType, firstId, secondId, thirdId, operate int) (state int, err error) {
 	// 获取审批配置
 	confMap, e := models.GetBusinessConf()
 	if e != nil {
@@ -109,9 +111,9 @@ func CheckReportCurrState(reportType, firstId, secondId, operate int) (state int
 
 	// 查询对应分类是否有审批流
 	flowOb := new(report_approve.ReportApproveFlow)
-	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId)
 	flowPars := make([]interface{}, 0)
-	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowPars = append(flowPars, reportType, firstId, secondId, thirdId)
 	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
@@ -148,7 +150,7 @@ func CheckReportCurrState(reportType, firstId, secondId, operate int) (state int
 }
 
 // SubmitReportApprove 提交审批
-func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId, secondId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
+func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId, secondId, thirdId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
 	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
 	confMap, e := models.GetBusinessConf()
 	if e != nil {
@@ -171,9 +173,9 @@ func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId,
 
 	// 查询审批流
 	flowOb := new(report_approve.ReportApproveFlow)
-	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ClassifyThirdId)
 	flowPars := make([]interface{}, 0)
-	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowPars = append(flowPars, reportType, firstId, secondId, thirdId)
 	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
 	if e != nil {
 		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
@@ -216,6 +218,7 @@ func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId,
 	newApprove.ReportTitle = reportTitle
 	newApprove.ClassifyFirstId = firstId
 	newApprove.ClassifySecondId = secondId
+	newApprove.ClassifyThirdId = thirdId
 	newApprove.State = report_approve.ReportApproveStateApproving
 	newApprove.FlowId = flowItem.ReportApproveFlowId
 	newApprove.FlowVersion = flowItem.CurrVersion
@@ -407,7 +410,7 @@ func updateReportApproveState(reportType, reportId, approveId, state int) (err e
 }
 
 // PassReportApprove 通过审批
-func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, sysAdminId int) (tips string, err error) {
+func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, sysAdminId int, reportUrl string) (tips string, err error) {
 	if approveItem == nil {
 		err = fmt.Errorf("审批信息有误")
 		return
@@ -457,7 +460,12 @@ func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *re
 	recordItem.State = report_approve.ReportApproveStatePass
 	recordItem.ApproveTime = now
 	recordItem.ModifyTime = now
-	recordCols := []string{"State", "ApproveTime", "ModifyTime"}
+	recordItem.NodeState = report_approve.ReportApproveStatePass
+	recordItem.NodeApproveUserId = recordItem.ApproveUserId
+	recordItem.NodeApproveUserName = recordItem.ApproveUserName
+	recordItem.NodeApproveTime = now
+
+	recordCols := []string{"State", "ApproveTime", "ModifyTime", "NodeState", "NodeApproveUserId", "NodeApproveUserName", "NodeApproveTime"}
 	lastApprove := false
 
 	// 依次审批
@@ -504,6 +512,7 @@ func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *re
 			newRecord.ApproveUserSort = nextUser.Sort
 			newRecord.CreateTime = now
 			newRecord.ModifyTime = now
+			newRecord.NodeState = report_approve.ReportApproveStateApproving
 			if e = newRecord.Create(); e != nil {
 				err = fmt.Errorf("生成审批记录失败, Err: %s", e.Error())
 				return
@@ -602,6 +611,13 @@ func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *re
 			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
 			return
 		}
+
+		// 将该审批的同一个节点的记录标记为已审批
+		if e = recordItem.UpdateNodeState(recordItem.ReportApproveId, recordItem.NodeId, recordItem.NodeState, recordItem.NodeApproveUserId, recordItem.NodeApproveUserName, recordItem.NodeApproveTime); e != nil {
+			err = fmt.Errorf("更新同一节点的其他审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+
 		if currNode.NextNodeId == 0 {
 			lastApprove = true
 		}
@@ -660,6 +676,9 @@ func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *re
 				return
 			}
 		}()
+
+		// 生成报告pdf和长图
+		go Report2pdfAndJpeg(reportUrl, approveItem.ReportId, approveItem.ReportType)
 	}
 	return
 }
@@ -681,12 +700,24 @@ func RefuseReportApprove(approveItem *report_approve.ReportApprove, recordItem *
 	recordItem.ApproveRemark = approveRemark
 	recordItem.ApproveTime = now
 	recordItem.ModifyTime = now
-	recordCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime"}
+
+	recordItem.NodeState = report_approve.ReportApproveStatePass
+	recordItem.NodeApproveUserId = recordItem.ApproveUserId
+	recordItem.NodeApproveUserName = recordItem.ApproveUserName
+	recordItem.NodeApproveTime = now
+
+	recordCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime", "NodeState", "NodeApproveUserId", "NodeApproveUserName", "NodeApproveTime"}
 	if e := recordItem.Update(recordCols); e != nil {
 		err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
 		return
 	}
 
+	// 将该审批的同一个节点的记录标记为已审批
+	if e := recordItem.UpdateNodeState(recordItem.ReportApproveId, recordItem.NodeId, recordItem.NodeState, recordItem.NodeApproveUserId, recordItem.NodeApproveUserName, recordItem.NodeApproveTime); e != nil {
+		err = fmt.Errorf("更新同一节点的其他审批记录状态失败, Err: %s", e.Error())
+		return
+	}
+
 	// 驳回-更新审批, 报告状态, 推送消息
 	approveItem.State = report_approve.ReportApproveStateRefuse
 	approveItem.ApproveRemark = approveRemark
@@ -697,6 +728,7 @@ func RefuseReportApprove(approveItem *report_approve.ReportApprove, recordItem *
 		err = fmt.Errorf("更新审批状态失败, Err: %s", e.Error())
 		return
 	}
+
 	if e := updateReportApproveState(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, models.ReportStateRefused); e != nil {
 		err = fmt.Errorf("更新报告状态失败, Err: %s", e.Error())
 		return
@@ -756,6 +788,7 @@ func BuildNextNodeRecordAndMsg(approveNodeItem *report_approve.ReportApproveNode
 		r.ApproveUserSort = u.Sort
 		r.CreateTime = now
 		r.ModifyTime = now
+		r.NodeState = report_approve.ReportApproveStateApproving // 当前节点审批状态
 		newRecords = append(newRecords, r)
 		// 依次审批仅生成一条记录
 		if approveNode.ApproveType == report_approve.NodeApproveTypeRoll {
@@ -798,7 +831,7 @@ func BuildNextNodeRecordAndMsg(approveNodeItem *report_approve.ReportApproveNode
 func AfterReportApprovePass(reportType, reportId int) (err error) {
 	// 中文研报
 	if reportType == report_approve.FlowReportTypeChinese {
-		report, e := models.GetReportById(reportId)
+		reportInfo, e := models.GetReportByReportId(reportId)
 		if e != nil {
 			if e.Error() == utils.ErrNoRow() {
 				return
@@ -806,8 +839,9 @@ func AfterReportApprovePass(reportType, reportId int) (err error) {
 			err = fmt.Errorf("获取研报信息失败, Err: %s", e.Error())
 			return
 		}
-		_ = CreateVideo(report)
-		_ = UpdateReportEs(report.Id, models.ReportStatePublished)
+		_ = CreateVideo(reportInfo)
+		_ = UpdateReportEs(reportInfo.Id, models.ReportStatePublished)
+
 		return
 	}
 
@@ -830,10 +864,10 @@ func AfterReportApprovePass(reportType, reportId int) (err error) {
 		}
 
 		// 写入队列
-		var queue smart_report.Report2ImgQueueReq
-		queue.ReportType = 2
-		queue.ReportCode = item.ReportCode
-		_ = utils.Rc.LPush(utils.CACHE_CREATE_REPORT_IMGPDF_QUEUE, queue)
+		//var queue smart_report.Report2ImgQueueReq
+		//queue.ReportType = 2
+		//queue.ReportCode = item.ReportCode
+		//_ = utils.Rc.LPush(utils.CACHE_CREATE_REPORT_IMGPDF_QUEUE, queue)
 
 		// 生成音频
 		if item.VideoUrl == "" {
@@ -882,7 +916,7 @@ func CheckCloseReportApproveConf() (yes bool, err error) {
 }
 
 // FlowOperateResetReportState 审批流变化-重置报告的初始状态
-func FlowOperateResetReportState(reportType, classifyFirstId, classifySecondId, oldState, State int) (err error) {
+func FlowOperateResetReportState(reportType, classifyFirstId, classifySecondId, classifyThirdId, oldState, State int) (err error) {
 	defer func() {
 		if err != nil {
 			tips := fmt.Sprintf("审批流变化-重置报告初始状态失败, ErrMsg: %s", err.Error())
@@ -893,7 +927,7 @@ func FlowOperateResetReportState(reportType, classifyFirstId, classifySecondId,
 
 	// 中文研报
 	if reportType == report_approve.FlowReportTypeChinese {
-		e := models.UpdateReportsStateByCond(classifyFirstId, classifySecondId, oldState, State)
+		e := models.UpdateReportsStateByCond(classifyFirstId, classifySecondId, classifyThirdId, oldState, State)
 		if e != nil {
 			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
 		}
@@ -932,7 +966,7 @@ func ConfigChangeResetReportState(changeType string) (err error) {
 
 	// 关闭审批-待提交->未发布
 	if changeType == "" {
-		e := models.UpdateReportsStateByCond(0, 0, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+		e := models.UpdateReportsStateByCond(0, 0, 0, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
 		if e != nil {
 			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
 		}
@@ -995,7 +1029,7 @@ func ConfigChangeResetReportState(changeType string) (err error) {
 
 	// 开启第三方审批->未发布->待提交
 	if changeType == models.BusinessConfReportApproveTypeOther {
-		e := models.UpdateReportsStateByCond(0, 0, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+		e := models.UpdateReportsStateByCond(0, 0, 0, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
 		if e != nil {
 			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
 		}

+ 225 - 0
services/report_chapter.go

@@ -0,0 +1,225 @@
+package services
+
+import (
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"time"
+)
+
+// MoveReportChapter
+// @Description: 移动报告章节
+// @author: Roc
+// @datetime 2024-06-06 09:48:52
+// @param req *models.ReportChapterMoveReq
+// @return err error
+// @return errMsg string
+func MoveReportChapter(req *models.ReportChapterMoveReq) (err error, errMsg string) {
+	ob := new(models.ReportChapter)
+	reportChapterId := req.ReportChapterId
+	prevReportChapterId := req.PrevReportChapterId
+	nextReportChapterId := req.NextReportChapterId
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		reportChapter     *models.ReportChapter
+		prevReportChapter *models.ReportChapter
+		nextReportChapter *models.ReportChapter
+
+		prevSort int
+		nextSort int
+	)
+
+	// 移动对象为分类, 判断权限
+	reportChapter, err = ob.GetReportChapterById(reportChapterId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "当前报告章节不存在"
+			err = fmt.Errorf("获取报告章节信息失败,Err:" + err.Error())
+			return
+		}
+		errMsg = "移动失败"
+		err = fmt.Errorf("获取章节信息失败,Err:" + err.Error())
+		return
+	} else if reportChapter.ReportChapterId == 0 {
+		errMsg = "当前报告章节不存在"
+		err = fmt.Errorf("获取报告章节信息失败,Err:" + err.Error())
+		return
+	}
+
+	if prevReportChapterId > 0 {
+		prevReportChapter, err = ob.GetReportChapterById(prevReportChapterId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevReportChapter.Sort
+	}
+
+	if nextReportChapterId > 0 {
+		//下一个兄弟节点
+		nextReportChapter, err = ob.GetReportChapterById(nextReportChapterId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextReportChapter.Sort
+	}
+
+	err, errMsg = moveReportChapter(reportChapter, prevReportChapter, nextReportChapter, reportChapter.ReportId, prevSort, nextSort)
+	return
+}
+
+// moveReportChapter
+// @Description: 移动章节分类
+// @author: Roc
+// @datetime 2024-06-06 09:48:46
+// @param reportChapter *models.ReportChapter
+// @param prevReportChapter *models.ReportChapter
+// @param nextReportChapter *models.ReportChapter
+// @param reportId int
+// @param prevSort int
+// @param nextSort int
+// @return err error
+// @return errMsg string
+func moveReportChapter(reportChapter, prevReportChapter, nextReportChapter *models.ReportChapter, reportId int, prevSort, nextSort int) (err error, errMsg string) {
+	ob := new(models.ReportChapter)
+	updateCol := make([]string, 0)
+
+	if prevSort > 0 {
+		//如果是移动在两个兄弟节点之间
+		if nextSort > 0 {
+			//下一个兄弟节点
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevSort == nextSort || prevSort == reportChapter.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+
+				//变更分类
+				if prevReportChapter != nil {
+					_ = ob.UpdateReportChapterSortByReportId(reportId, prevReportChapter.ReportChapterId, prevReportChapter.Sort, updateSortStr)
+				} else {
+					_ = ob.UpdateReportChapterSortByReportId(reportId, 0, prevSort, updateSortStr)
+				}
+
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextSort-prevSort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+
+					//变更分类
+					if prevReportChapter != nil {
+						_ = ob.UpdateReportChapterSortByReportId(reportId, prevReportChapter.ReportChapterId, prevSort, updateSortStr)
+					} else {
+						_ = ob.UpdateReportChapterSortByReportId(reportId, 0, prevSort, updateSortStr)
+					}
+
+				}
+			}
+		}
+
+		reportChapter.Sort = prevSort + 1
+		reportChapter.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else if prevReportChapter == nil && nextReportChapter == nil {
+		//处理只拖动到目录里,默认放到目录底部的情况
+		var maxSort int
+		maxSort, err = ob.GetMaxSortByReportId(reportId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("查询组内排序信息失败,Err:" + err.Error())
+			return
+		}
+		reportChapter.Sort = maxSort + 1 //那就是排在组内最后一位
+		reportChapter.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else {
+		// 拖动到父级分类的第一位
+		firstReportChapter, tmpErr := ob.GetFirstReportChapterByReportId(reportId)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取当前报告下,且排序数相同 的排序第一条的数据失败,Err:" + tmpErr.Error())
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstReportChapter != nil && firstReportChapter.ReportChapterId != 0 && firstReportChapter.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = ob.UpdateReportChapterSortByReportId(reportId, firstReportChapter.ReportChapterId-1, 0, updateSortStr)
+		}
+
+		reportChapter.Sort = 0 //那就是排在第一位
+		reportChapter.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = reportChapter.UpdateChapter(updateCol)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("修改失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// CheckChapterAuthByAdminIdList
+// @Description: 根据管理员id列表,判断当前用户是否有章节权限
+// @author: Roc
+// @datetime 2024-06-13 11:03:10
+// @param adminId int
+// @param createAdminId int
+// @param grantAdminIdList []int
+// @return isAuth bool
+func CheckChapterAuthByAdminIdList(adminId, createAdminId int, grantAdminIdList []int) (isAuth bool) {
+	// 如果是自己创建的报告,那么就有权限
+	if adminId == createAdminId {
+		isAuth = true
+		return
+	}
+	// 如果是授权用户,那么就有权限
+	if utils.IsCheckInList(grantAdminIdList, adminId) {
+		isAuth = true
+		return
+	}
+
+	return
+}
+
+// CheckChapterAuthByReportChapterInfo
+// @Description: 根据报告章节信息,判断当前用户是否有章节权限
+// @author: Roc
+// @datetime 2024-06-13 11:10:46
+// @param adminId int
+// @param createAdminId int
+// @param reportChapterInfo *models.ReportChapter
+// @return isAuth bool
+// @return err error
+func CheckChapterAuthByReportChapterInfo(adminId, createAdminId int, reportChapterInfo *models.ReportChapter) (isAuth bool, err error) {
+	// 如果是自己创建的报告,那么就有权限
+	if adminId == createAdminId {
+		isAuth = true
+		return
+	}
+
+	chapterGrantObj := report.ReportChapterGrant{}
+	chapterGrantList, err := chapterGrantObj.GetGrantListById(reportChapterInfo.ReportChapterId)
+	if err != nil {
+		return
+	}
+
+	for _, v := range chapterGrantList {
+		if v.AdminId == adminId {
+			isAuth = true
+			return
+		}
+	}
+
+	return
+}

+ 130 - 0
services/report_classify.go

@@ -0,0 +1,130 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/services/alarm_msg"
+	"fmt"
+)
+
+// UpdateParentClassifyHasTel 更新父级分类是否含有电话字段
+func UpdateParentClassifyHasTel(classifyId, parentId, hasTeleconference int) (err error) {
+	if classifyId <= 0 || parentId <= 0 {
+		return
+	}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg("编辑分类后-修改父级分类电话会信息失败, ErrMsg: "+err.Error(), 3)
+		}
+	}()
+
+	parentClassify, e := models.GetClassifyById(parentId)
+	if e != nil {
+		err = fmt.Errorf("获取父级分类信息失败, Err: %s", e.Error())
+		return
+	}
+	updateParent := false
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "HasTeleconference")
+	if hasTeleconference == 1 {
+		// 二级分类包含电话会,则一级分类也默认包含电话会
+		if parentClassify.HasTeleconference == 0 {
+			parentClassify.HasTeleconference = 1
+			updateParent = true
+		}
+	} else {
+		// 二级分类均无电话会,则一级分类也无电话会
+		if parentClassify.HasTeleconference == 1 {
+			child, e := models.GetClassifyChild(parentClassify.Id, "")
+			if e != nil {
+				err = fmt.Errorf("获取子分类失败, Err: %s", e.Error())
+				return
+			}
+			// 存在同一级分类下的二级分类有电话会则不变动
+			hasTel := false
+			for i := 0; i < len(child); i++ {
+				if child[i].HasTeleconference == 1 && child[i].Id != classifyId {
+					hasTel = true
+					break
+				}
+			}
+			if !hasTel {
+				parentClassify.HasTeleconference = 0
+				updateParent = true
+			}
+		}
+	}
+	if updateParent {
+		if e = parentClassify.UpdateClassify(updateCols); e != nil {
+			err = fmt.Errorf("更新父级分类失败, Err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// AfterUpdateClassifyNameOrParent 更新报告分类名称/父级分类后的操作
+// 当二级分类名称做了修改, 更新chart_permission_search_key_word_mapping对应的key_word
+// 以及report表中的classify_name_second, 不然报告的权限会有BUG
+func AfterUpdateClassifyNameOrParent(classifyId, parentId, originParentId int, originName, classifyName string, classifyLevel int) (err error) {
+	if classifyId == 0 {
+		return
+	}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg("更改分类名称后-同步更新报告表字段及权限表关键词失败, ErrMsg: "+err.Error(), 3)
+		}
+	}()
+
+	// 修改名称
+	// ETA1.8.3 现在任何一级都能挂报告,所以只需要名称变更就要去更新数据,不需要判断是否属于子分类(2024-6-18 10:37:17)
+	if originName != classifyName {
+		switch classifyLevel {
+		case 1: // 一级分类
+			if e := models.UpdateReportFirstClassifyNameByClassifyId(classifyId, classifyName); e != nil {
+				err = fmt.Errorf("更新报告表一级分类名称失败, Err: %s", e.Error())
+				return
+			}
+		case 2: // 二级分类
+			// 更新报告表分类字段
+			if e := models.UpdateReportSecondClassifyNameByClassifyId(classifyId, classifyName); e != nil {
+				err = fmt.Errorf("更新报告表二级分类名称失败, Err: %s", e.Error())
+				return
+			}
+		case 3: // 三级分类
+			// 更新报告表分类字段
+			if e := models.UpdateReportThirdClassifyNameByClassifyId(classifyId, classifyName); e != nil {
+				err = fmt.Errorf("更新报告表三级分类名称失败, Err: %s", e.Error())
+				return
+			}
+		default:
+			err = errors.New(fmt.Sprint("错误的分类级别,ClassifyId:", classifyId, ";层级:", classifyLevel))
+			return
+		}
+
+		// 更新关键词
+		if e := models.UpdateChartPermissionNameFromMappingByKeyword(classifyName, classifyId, "rddp"); e != nil {
+			err = fmt.Errorf("更新二级分类关键词失败, Err: %s", e.Error())
+			return
+		}
+		// 同步crm_master
+		_ = EditKeywordPermissionSync(classifyName, classifyId)
+	}
+
+	//// 二级分类-修改了父级分类
+	//// ETA1.8.3已经不允许修改父级分类了(2024-6-18 10:37:17)
+	//if originParentId > 0 && parentId > 0 && originParentId != parentId {
+	//	parentClassify, e := models.GetClassifyById(parentId)
+	//	if e != nil {
+	//		err = fmt.Errorf("获取父级分类信息失败, Err: %s", e.Error())
+	//		return
+	//	}
+	//	// 更新报告表一级分类名称和ID
+	//	if e = models.UpdateReportSecondClassifyFirstNameByClassifyId(classifyId, parentClassify.Id, parentClassify.ClassifyName); e != nil {
+	//		err = fmt.Errorf("更新报告表一级分类名称和ID, Err: %s", e.Error())
+	//		return
+	//	}
+	//}
+
+	return
+}

+ 1327 - 0
services/report_v2.go

@@ -0,0 +1,1327 @@
+package services
+
+import (
+	"archive/zip"
+	"errors"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/models/system"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/file"
+	"github.com/rdlucklib/rdluck_tools/http"
+	"os"
+	"path"
+	"strconv"
+	"time"
+)
+
+// AddReportAndChapter
+// @Description: 新增报告(包含章节)
+// @author: Roc
+// @datetime 2024-06-04 11:23:20
+// @param reportInfo *models.Report
+// @param inheritReportId int
+// @return err error
+// @return errMsg string
+func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAdminIdList []int) (err error, errMsg string) {
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, reportInfo.ClassifyIdThird, models.ReportOperateAdd)
+	if e != nil {
+		//errMsg = "操作失败"
+		err = errors.New("校验报告当前状态失败, Err: " + e.Error())
+		return
+	}
+	// 报告状态
+	reportInfo.State = state
+
+	// 获取最小分类id
+	minClassifyId := reportInfo.ClassifyIdThird
+	if minClassifyId <= 0 {
+		minClassifyId = reportInfo.ClassifyIdSecond
+	}
+	if minClassifyId <= 0 {
+		minClassifyId = reportInfo.ClassifyIdFirst
+	}
+	if minClassifyId <= 0 {
+		errMsg = "分类异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	errMsg = "生成报告失败"
+
+	// 报告继承
+	if inheritReportId > 0 {
+		inheritReport, tmpErr := models.GetReportByReportId(inheritReportId)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			errMsg = "获取待继承的报告失败"
+			err = tmpErr
+			return
+		}
+		if inheritReport != nil {
+			// 判断当前的报告分类与继承的报告分类是否一致
+			if inheritReport.ClassifyIdFirst != reportInfo.ClassifyIdFirst || inheritReport.ClassifyIdSecond != reportInfo.ClassifyIdSecond || inheritReport.ClassifyIdThird != reportInfo.ClassifyIdThird {
+				errMsg = "分类异常,与继承的报告分类不一致"
+				err = errors.New(errMsg)
+				return
+			}
+
+			reportInfo.ChapterType = inheritReport.ChapterType
+			reportInfo.Content = inheritReport.Content
+			reportInfo.ContentSub = inheritReport.ContentSub
+			reportInfo.ContentStruct = inheritReport.ContentStruct
+			reportInfo.HeadImg = inheritReport.HeadImg
+			reportInfo.EndImg = inheritReport.EndImg
+			reportInfo.CanvasColor = inheritReport.CanvasColor
+			reportInfo.NeedSplice = inheritReport.NeedSplice
+			reportInfo.HeadResourceId = inheritReport.HeadResourceId
+			reportInfo.EndResourceId = inheritReport.EndResourceId
+			reportInfo.InheritReportId = inheritReport.Id
+		}
+	}
+
+	// 获取待生成的报告章节
+	addChapterList, allGrantUserList, err, errMsg := getAddChapter(reportInfo, minClassifyId, inheritReportId, grantAdminIdList)
+
+	// 新增报告及章节
+	var reportId int64
+	reportId, err = models.AddReportAndChapter(reportInfo, allGrantUserList, addChapterList)
+	if err != nil {
+		err = errors.New("新增报告及章节失败, Err: " + err.Error())
+		return
+	}
+	reportCode := utils.MD5(strconv.Itoa(reportInfo.Id))
+	reportInfo.ReportCode = reportCode
+
+	// 修改唯一编码
+	{
+		go models.ModifyReportCode(reportId, reportCode)
+	}
+
+	// 报告权限处理
+	go handleReportPermission(reportId, minClassifyId)
+
+	return
+}
+
+// EditReport
+// @Description: 修改报告的基础信息、授权用户权限
+// @author: Roc
+// @datetime 2024-06-06 17:16:58
+// @param reportInfo *models.Report
+// @param req models.EditReq
+// @param sysUser *system.Admin
+// @return err error
+// @return errMsg string
+func EditReport(reportInfo *models.Report, req models.EditReq, sysUser *system.Admin) (err error, errMsg string) {
+	errMsg = `保存失败`
+	//var stage int
+	//if reportInfo.ClassifyNameFirst != req.ClassifyNameFirst || reportInfo.ClassifyNameSecond != req.ClassifyNameSecond {
+	//	// 报告期数
+	//	maxStage, _ := models.GetReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird, int(req.ReportId))
+	//	maxStage = maxStage + 1
+	//	stage = maxStage
+	//} else {
+	//	stage = reportInfo.Stage
+	//}
+
+	//if req.State != reportInfo.State {
+	//	recordItem := &models.ReportStateRecord{
+	//		ReportId:   int(req.ReportId),
+	//		ReportType: 1,
+	//		State:      req.State,
+	//		AdminId:    this.SysUser.AdminId,
+	//		AdminName:  this.SysUser.AdminName,
+	//		CreateTime: time.Now(),
+	//	}
+	//	go func() {
+	//		_, _ = models.AddReportStateRecord(recordItem)
+	//	}()
+	//}
+
+	//item := new(models.Report)
+	//reportInfo.ClassifyIdFirst = req.ClassifyIdFirst
+	//reportInfo.ClassifyNameFirst = req.ClassifyNameFirst
+	//reportInfo.ClassifyIdSecond = req.ClassifyIdSecond
+	//reportInfo.ClassifyNameSecond = req.ClassifyNameSecond
+	//reportInfo.ClassifyIdThird = req.ClassifyIdThird
+	//reportInfo.ClassifyNameThird = req.ClassifyNameThird
+	reportInfo.Title = req.Title
+	reportInfo.Abstract = req.Abstract
+	reportInfo.Author = req.Author
+	reportInfo.Frequency = req.Frequency
+	//reportInfo.State = reportInfo.State // 编辑不变更状态
+	//reportInfo.Stage = stage // 编辑不变更期数
+	//reportInfo.Content = html.EscapeString(req.Content)	// 编辑不变更具体内容
+	//reportInfo.ContentSub = html.EscapeString(contentSub)	// 编辑不变更具体内容
+	reportInfo.CreateTime = req.CreateTime
+	//reportInfo.CollaborateType = req.CollaborateType
+	//reportInfo.ReportLayout = req.ReportLayout
+	if req.IsPublicPublish <= 0 {
+		req.IsPublicPublish = 1
+	}
+	reportInfo.IsPublicPublish = req.IsPublicPublish
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+
+	state, e := CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, reportInfo.ClassifyIdThird, models.ReportOperateEdit)
+	if e != nil {
+		//errMsg = "操作失败"
+		err = errors.New("校验报告当前状态失败, Err: " + e.Error())
+		return
+	}
+	// 报告状态
+	reportInfo.State = state
+
+	//updateCols := []string{"ClassifyIdFirst", "ClassifyNameFirst", "ClassifyIdSecond", "ClassifyNameSecond", "ClassifyIdThird", "ClassifyNameThird", "Title", "Abstract", "Author", "Frequency", "Stage", "CreateTime", "IsPublicPublish", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime"}
+	updateCols := []string{"Title", "Abstract", "Author", "Frequency", "CreateTime", "IsPublicPublish", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime", "State"}
+
+	if req.HeadResourceId > 0 {
+		reportInfo.HeadResourceId = req.HeadResourceId
+		updateCols = append(updateCols, "HeadResourceId")
+	}
+	if req.EndResourceId > 0 {
+		reportInfo.EndResourceId = req.EndResourceId
+		updateCols = append(updateCols, "EndResourceId")
+	}
+
+	// 需要添加的报告授权数据
+	addReportAdminList := make([]*report.ReportGrant, 0)
+	// 待移除的报告授权数据id
+	delReportGrantIdList := make([]int, 0)
+
+	// 处理当前报告需要新增/移除的授权信息
+	{
+		reportGrantObj := report.ReportGrant{}
+		// 获取当前报告已经授权的用户信息
+		reportGrantList, tmpErr := reportGrantObj.GetGrantListById(reportInfo.Id)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		// 当前报告已经授权的用户信息
+		currReportAdminMap := make(map[int]*report.ReportGrant)
+		// 需要删除的报告授权数据
+		delReportAdminMap := make(map[int]*report.ReportGrant)
+		for _, v := range reportGrantList {
+			currReportAdminMap[v.AdminId] = v
+			delReportAdminMap[v.AdminId] = v
+		}
+
+		// 先看需要新增哪些用户
+		for _, tmpAdminId := range req.GrantAdminIdList {
+			_, ok := currReportAdminMap[tmpAdminId]
+			// 如果章节中需要新增的用户 已经在 报告授权用户里面,那么就忽略,可以不用新增了
+			if ok {
+				delete(delReportAdminMap, tmpAdminId)
+				continue
+			}
+
+			// 如果不存在,那么就新增授权
+			addReportAdminList = append(addReportAdminList, &report.ReportGrant{
+				//GrantId:         0,
+				ReportId:   reportInfo.Id,
+				AdminId:    tmpAdminId,
+				CreateTime: time.Now(),
+			})
+		}
+
+		// 查出需要移除的授权id
+		for _, v := range delReportAdminMap {
+			delReportGrantIdList = append(delReportGrantIdList, v.GrantId)
+		}
+	}
+
+	// 修改报告的基本信息,以及报告的授权用户
+	err = models.EditReportAndPermission(reportInfo, updateCols, addReportAdminList, delReportGrantIdList)
+	if err != nil {
+		return
+	}
+
+	// 报告权限处理
+	{
+		minClassifyId, _, tmpErr := getMinClassify(reportInfo)
+		if tmpErr != nil {
+			return
+		}
+		go handleReportPermission(int64(reportInfo.Id), minClassifyId)
+	}
+
+	return
+}
+
+// getAddChapter
+// @Description: 获取待新增的报告章节列表
+// @author: Roc
+// @datetime 2024-06-04 13:10:58
+// @param reportInfo *models.Report
+// @param minClassifyId int
+// @param inheritReportId int
+// @param grantAdminIdList []int
+// @return chapterList []*models.ReportChapter
+// @return err error
+// @return errMsg string
+func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int, grantAdminIdList []int) (chapterList []models.AddReportChapter, allGrantUserList []*report.ReportGrant, err error, errMsg string) {
+	// 待生成的报告章节内容
+	chapterList = make([]models.AddReportChapter, 0)
+
+	// 所有的授权用户
+	allGrantUserList = make([]*report.ReportGrant, 0)
+	// 报告授权的用户ID
+	needAdminIdMap := make(map[int]bool)
+	for _, adminId := range grantAdminIdList {
+		needAdminIdMap[adminId] = true
+		allGrantUserList = append(allGrantUserList, &report.ReportGrant{
+			//GrantId:      0,
+			//ReportId:     0,
+			AdminId:    adminId,
+			CreateTime: time.Now(),
+		})
+	}
+	if reportInfo.HasChapter != 1 {
+		return
+	}
+
+	// 最小单元的分类
+	var minClassifyName string
+	if reportInfo.ClassifyIdThird == minClassifyId {
+		minClassifyName = reportInfo.ClassifyNameThird
+	} else if reportInfo.ClassifyIdSecond == minClassifyId {
+		minClassifyName = reportInfo.ClassifyNameSecond
+	} else {
+		minClassifyName = reportInfo.ClassifyNameFirst
+	}
+
+	errMsg = "生成报告章节失败"
+	// 章节类型列表
+	allTypeList, err := models.GetReportChapterTypeListByClassifyId(minClassifyId)
+	if err != nil {
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 待添加的章节
+	chapterTypeList := make([]*models.ReportChapterType, 0)
+	// 待添加的章节类型id列表
+	chapterTypeIdList := make([]int, 0)
+
+	nowTime := time.Now().Local()
+	for _, chapterType := range allTypeList {
+		// 如果被永久暂停更新了
+		if chapterType.Enabled == 0 { //该章节已被永久禁用,那么就不允许继承或者新增该章节
+			continue
+		}
+
+		if chapterType.PauseStartTime != "" && chapterType.PauseEndTime != "" && chapterType.PauseStartTime != utils.EmptyDateStr && chapterType.PauseEndTime != utils.EmptyDateStr {
+			startTime, timeErr := time.ParseInLocation(utils.FormatDate, chapterType.PauseStartTime, time.Local)
+			if timeErr != nil {
+				utils.FileLog.Error("更新规则时间转换失败4001, Err: " + timeErr.Error())
+				continue
+			}
+			endTime, timeErr := time.ParseInLocation(utils.FormatDate, chapterType.PauseEndTime, time.Local)
+			if timeErr != nil {
+				utils.FileLog.Error("更新规则时间转换失败4002, Err: " + timeErr.Error())
+				continue
+			}
+			// 暂停更新
+			if nowTime.After(startTime) && nowTime.Before(endTime.AddDate(0, 0, 1)) {
+				continue
+			}
+		}
+
+		// 正常的章节类型状态,那么应该自动创建该章节
+		chapterTypeList = append(chapterTypeList, chapterType)
+		chapterTypeIdList = append(chapterTypeIdList, chapterType.ReportChapterTypeId)
+	}
+
+	// 待继承的章节类id的授权用户
+	oldChapterIdGrantListMap := make(map[int][]*report.ReportChapterGrant)
+	// 待继承的章节类id关联的品种列表
+	oldChapterPermissionListMap := make(map[int][]*report.ReportChapterPermissionMapping)
+	// 自定义章节列表
+	customAddChapterList := make([]models.AddReportChapter, 0)
+	// 报告继承
+	inheritChapterMap := make(map[int]*models.ReportChapter)
+
+	// 当前分类下配置的章节类型id所关联的品种列表(默认配置,不是从继承报告里面获取的,如果有继承章节,那么需要从继承报告里面获取)
+	currChapterTypePermissionListMap := make(map[int][]*report.ReportChapterPermissionMapping)
+
+	if len(chapterTypeIdList) > 0 {
+		mappingList, e := models.GetChapterTypePermissionByChapterTypeIdList(chapterTypeIdList)
+		if e != nil {
+			err = fmt.Errorf("获取章节类型权限列表失败, Err: " + e.Error())
+			return
+		}
+		hasPermissionMap := make(map[string]bool)
+		for _, v := range mappingList {
+			tmpChapterTypePermissionList, ok := currChapterTypePermissionListMap[v.ReportChapterTypeId]
+			if !ok {
+				tmpChapterTypePermissionList = make([]*report.ReportChapterPermissionMapping, 0)
+			}
+			key := fmt.Sprint(v.ReportChapterTypeId, "-", v.ChartPermissionId)
+			if _, has := hasPermissionMap[key]; !has {
+				hasPermissionMap[key] = true
+				currChapterTypePermissionListMap[v.ReportChapterTypeId] = append(tmpChapterTypePermissionList, &report.ReportChapterPermissionMapping{
+					ReportChapterPermissionMappingId: 0,
+					ReportChapterId:                  0,
+					ChartPermissionId:                v.ChartPermissionId,
+					CreateTime:                       time.Now(),
+				})
+			}
+		}
+	}
+
+	if inheritReportId > 0 {
+		// 继承待继承的报告章节内容
+		inheritReportChapters, tmpErr := models.GetChapterListByReportId(inheritReportId)
+		if tmpErr != nil {
+			errMsg = "获取待继承的报告章节失败"
+			err = tmpErr
+			return
+		}
+		reportChaptersIdList := make([]int, 0)
+		for _, v := range inheritReportChapters {
+			reportChaptersIdList = append(reportChaptersIdList, v.ReportChapterId)
+		}
+
+		// 继承的报告章节用户map
+		grantListMap := make(map[int][]*report.ReportChapterGrant)
+
+		// 继承的报告章节的关联品种map
+		chapterPermissionListMap := make(map[int][]*report.ReportChapterPermissionMapping)
+
+		// 授权数据列表
+		if len(reportChaptersIdList) > 0 {
+			// 授权用户数据
+			obj := report.ReportChapterGrant{}
+			grantList, tmpErr := obj.GetGrantListByIdList(reportChaptersIdList)
+			if tmpErr != nil {
+				errMsg = "获取待继承的报告章节的授权用户列表失败"
+				err = tmpErr
+				return
+			}
+
+			for _, v := range grantList {
+				// 如果不在报告授权的用户ID里面,那么该章节就不继承该授权用户
+				if _, ok := needAdminIdMap[v.AdminId]; !ok {
+					continue
+				}
+
+				currReportChapterId := v.ReportChapterId
+				tmpGrantList, ok := grantListMap[currReportChapterId]
+				if !ok {
+					tmpGrantList = make([]*report.ReportChapterGrant, 0)
+				}
+				v.ReportChapterId = 0
+				v.GrantId = 0
+				tmpGrantList = append(tmpGrantList, v)
+				grantListMap[currReportChapterId] = tmpGrantList
+			}
+
+			// 授权关联品种数据
+			permissionObj := report.ReportChapterPermissionMapping{}
+			permissionList, tmpErr := permissionObj.GetPermissionListByIdList(reportChaptersIdList)
+			if tmpErr != nil {
+				errMsg = "获取待继承的报告章节的授权用户列表失败"
+				err = tmpErr
+				return
+			}
+
+			for _, v := range permissionList {
+				currReportChapterId := v.ReportChapterId
+				tmpPermissionList, ok := chapterPermissionListMap[currReportChapterId]
+				if !ok {
+					tmpPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
+				}
+				v.ReportChapterId = 0
+				v.ReportChapterPermissionMappingId = 0
+				tmpPermissionList = append(tmpPermissionList, v)
+				chapterPermissionListMap[currReportChapterId] = tmpPermissionList
+			}
+		}
+
+		// 继承的报告章节内容
+		for i := 0; i < len(inheritReportChapters); i++ {
+			customChapter := inheritReportChapters[i]
+
+			// 授权用户列表
+			tmpGrantList, ok := grantListMap[customChapter.ReportChapterId]
+			if !ok {
+				tmpGrantList = make([]*report.ReportChapterGrant, 0)
+			}
+			oldChapterIdGrantListMap[customChapter.ReportChapterId] = tmpGrantList
+
+			// 关联品种列表
+			chapterPermissionList, ok := chapterPermissionListMap[customChapter.ReportChapterId]
+			if !ok {
+				chapterPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
+			}
+			oldChapterPermissionListMap[customChapter.ReportChapterId] = chapterPermissionList
+
+			// 判断该章节是否是系统章节,如果是的话,那就是需要额外创建的
+			if customChapter.TypeId > 0 {
+				inheritChapterMap[customChapter.TypeId] = customChapter
+				continue
+			}
+
+			// 自定义的报告内容
+			customChapter.ReportId = reportInfo.Id
+			customChapter.ReportChapterId = 0
+			customChapter.ClassifyIdFirst = minClassifyId
+			customChapter.ClassifyNameFirst = minClassifyName
+			customChapter.Stage = reportInfo.Stage
+			customChapter.PublishState = 1
+			customChapter.CreateTime = reportInfo.CreateTime
+			customChapter.ModifyTime = time.Now()
+			customChapter.LastModifyAdminId = reportInfo.LastModifyAdminId
+			customChapter.LastModifyAdminName = reportInfo.LastModifyAdminName
+			customChapter.ContentModifyTime = time.Now()
+
+			customAddChapter := models.AddReportChapter{
+				ReportChapter:       customChapter,
+				GrantList:           tmpGrantList,
+				GrantPermissionList: chapterPermissionList,
+			}
+			customAddChapterList = append(customAddChapterList, customAddChapter)
+		}
+	}
+
+	// 最大排序
+	var maxSort int
+	for _, typeItem := range chapterTypeList {
+		v, ok := inheritChapterMap[typeItem.ReportChapterTypeId]
+
+		// 章节授权用户
+		var tmpGrantList []*report.ReportChapterGrant
+		// 章节关联品种
+		var tmpChapterPermissionList []*report.ReportChapterPermissionMapping
+
+		chapterItem := new(models.ReportChapter)
+
+		if ok && v != nil {
+			// 如果存在继承的章节,那么就从继承的章节内容中获取
+			chapterItem.AddType = 2
+			chapterItem.Title = v.Title
+			chapterItem.ReportType = v.ReportType
+			chapterItem.ClassifyIdFirst = minClassifyId
+			chapterItem.ClassifyNameFirst = minClassifyName
+			chapterItem.TypeId = typeItem.ReportChapterTypeId
+			chapterItem.TypeName = typeItem.ReportChapterTypeName
+			chapterItem.Content = v.Content
+			chapterItem.ContentSub = v.ContentSub
+			chapterItem.Stage = reportInfo.Stage
+			chapterItem.PublishState = 1
+			chapterItem.Sort = typeItem.Sort
+			chapterItem.CreateTime = reportInfo.CreateTime
+			chapterItem.ModifyTime = time.Now()
+
+			chapterItem.LastModifyAdminId = reportInfo.LastModifyAdminId
+			chapterItem.LastModifyAdminName = reportInfo.LastModifyAdminName
+			chapterItem.ContentModifyTime = time.Now()
+			chapterItem.ContentStruct = v.ContentStruct
+			chapterItem.ReportLayout = v.ReportLayout
+			chapterItem.ReportCreateTime = time.Now()
+
+			// 继承历史章节中的授权用户列表
+			tmpGrantList, ok = oldChapterIdGrantListMap[v.ReportChapterId]
+			if !ok {
+				tmpGrantList = make([]*report.ReportChapterGrant, 0)
+			}
+			// 继承历史章节中的关联品种列表
+			tmpChapterPermissionList, ok = oldChapterPermissionListMap[v.ReportChapterId]
+			if !ok {
+				tmpChapterPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
+			}
+		} else {
+			chapterItem.AddType = 1
+			chapterItem.Title = typeItem.ReportChapterTypeName
+			chapterItem.ReportType = typeItem.ResearchType
+			chapterItem.ClassifyIdFirst = minClassifyId
+			chapterItem.ClassifyNameFirst = minClassifyName
+			chapterItem.TypeId = typeItem.ReportChapterTypeId
+			chapterItem.TypeName = typeItem.ReportChapterTypeName
+			chapterItem.Stage = reportInfo.Stage
+			chapterItem.PublishState = 1
+			chapterItem.Sort = typeItem.Sort
+			chapterItem.CreateTime = reportInfo.CreateTime
+			chapterItem.ModifyTime = time.Now()
+
+			chapterItem.LastModifyAdminId = reportInfo.LastModifyAdminId
+			chapterItem.LastModifyAdminName = reportInfo.LastModifyAdminName
+			chapterItem.ContentModifyTime = time.Now()
+			//chapterItem.ContentStruct = v.ContentStruct
+			chapterItem.ReportLayout = reportInfo.ReportLayout
+			chapterItem.ReportCreateTime = time.Now()
+
+			// 默认配置:从当前分类下配置的章节类型id所关联的品种列表
+			tmpChapterPermissionList, ok = currChapterTypePermissionListMap[typeItem.ReportChapterTypeId]
+			if !ok {
+				tmpChapterPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
+			}
+
+		}
+
+		if typeItem.Sort > maxSort {
+			maxSort = typeItem.Sort
+		}
+
+		addChapter := models.AddReportChapter{
+			ReportChapter:       chapterItem,
+			GrantList:           tmpGrantList,
+			GrantPermissionList: tmpChapterPermissionList,
+		}
+
+		chapterList = append(chapterList, addChapter)
+	}
+
+	// 将自定义的章节内容添加到待生成的章节内容中
+	for _, addChapterItem := range customAddChapterList {
+		maxSort++
+		addChapterItem.ReportChapter.Sort = maxSort
+		chapterList = append(chapterList, addChapterItem)
+	}
+
+	//hasGrantUserMap := make(map[int]bool)
+	//for _, grantList := range typeGrantListMap {
+	//	for _, grant := range grantList {
+	//		if _, ok := hasGrantUserMap[grant.AdminId]; !ok {
+	//			allGrantUserList = append(allGrantUserList, &report.ReportGrant{
+	//				//GrantId:      0,
+	//				//ReportId:     0,
+	//				AdminId:    grant.AdminId,
+	//				CreateTime: time.Now(),
+	//			})
+	//		}
+	//	}
+	//}
+
+	return
+}
+
+// AddChapterBaseInfoAndPermission
+// @Description:  添加章节基本信息及权限
+// @author: Roc
+// @datetime 2024-06-11 15:35:23
+// @param reportInfo *models.Report
+// @param reportChapterInfo *models.ReportChapter
+// @param permissionIdList []int
+// @param adminIdList []int
+// @return err error
+// @return errMsg string
+func AddChapterBaseInfoAndPermission(reportInfo *models.Report, reportChapterInfo *models.ReportChapter, permissionIdList []int, adminIdList []int) (err error, errMsg string) {
+	errMsg = "新增失败"
+
+	if reportInfo.State == 2 {
+		errMsg = "该报告已发布,不允许编辑"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 需要添加的报告章节授权数据
+	addChapterAdminList := make([]*report.ReportChapterGrant, 0)
+	for _, adminId := range adminIdList {
+		//新增授权
+		addChapterAdminList = append(addChapterAdminList, &report.ReportChapterGrant{
+			//GrantId:         0,
+			//ReportChapterId: reportChapterInfo.ReportChapterId,
+			AdminId:    adminId,
+			CreateTime: time.Now(),
+		})
+	}
+
+	// 需要添加的报告章节品种权限数据
+	addChapterPermissionList := make([]*report.ReportChapterPermissionMapping, 0)
+	// 品种权限
+	for _, permissionId := range permissionIdList {
+		// 如果不存在,那么就新增品种权限配置
+		addChapterPermissionList = append(addChapterPermissionList, &report.ReportChapterPermissionMapping{
+			//ReportChapterPermissionMappingId:         0,
+			//ReportChapterId:   reportChapterInfo.ReportChapterId,
+			ChartPermissionId: permissionId,
+			CreateTime:        time.Now(),
+		})
+	}
+
+	err = models.AddChapterBaseInfoAndPermission(reportChapterInfo, addChapterAdminList, addChapterPermissionList)
+
+	return
+}
+
+// EditChapterBaseInfoAndPermission
+// @Description: 修改报告章节的基础信息、授权用户权限、品种权限
+// @author: Roc
+// @datetime 2024-06-05 11:49:11
+// @param reportInfo *models.Report
+// @param reportChapterInfo *models.ReportChapter
+// @param title string
+// @param permissionIdList []int
+// @param adminIdList []int
+// @return err error
+// @return errMsg string
+func EditChapterBaseInfoAndPermission(reportInfo *models.Report, reportChapterInfo *models.ReportChapter, title string, permissionIdList []int, adminIdList []int) (err error, errMsg string) {
+	errMsg = "修改失败"
+
+	if reportInfo.State == 2 {
+		errMsg = "该报告已发布,不允许编辑"
+		err = errors.New(errMsg)
+		return
+	}
+
+	updateCols := make([]string, 0)
+	// 如果标题内容,那么就修改
+	if title != `` {
+		reportChapterInfo.Title = title
+		reportChapterInfo.ModifyTime = time.Now()
+		updateCols = append(updateCols, "Title", "ModifyTime")
+		reportChapterInfo.UpdateChapter(updateCols)
+	}
+
+	reportGrantObj := report.ReportGrant{}
+	chapterGrantObj := report.ReportChapterGrant{}
+	chapterPermissionObj := report.ReportChapterPermissionMapping{}
+
+	// 报告授权的用户map
+	reportGrantAdminIdMap := make(map[int]bool)
+	// 获取报告授权的用户信息
+	{
+		// 获取当前报告已经授权的用户信息
+		reportGrantList, tmpErr := reportGrantObj.GetGrantListById(reportInfo.Id)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		for _, v := range reportGrantList {
+			reportGrantAdminIdMap[v.AdminId] = true
+		}
+	}
+
+	// 需要添加的报告章节授权数据
+	addChapterAdminList := make([]*report.ReportChapterGrant, 0)
+	// 待移除的报告章节授权数据id
+	delReportChapterGrantIdList := make([]int, 0)
+
+	// 处理当前报告章节需要新增/移除的授权信息
+	{
+		// 获取当前章节已经授权的用户信息
+		chapterGrantList, tmpErr := chapterGrantObj.GetGrantListById(reportChapterInfo.ReportChapterId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		// 当前章节已经授权的用户信息
+		currChapterAdminMap := make(map[int]*report.ReportChapterGrant)
+		// 需要删除的报告章节授权数据
+		delChapterAdminMap := make(map[int]*report.ReportChapterGrant)
+		for _, v := range chapterGrantList {
+			currChapterAdminMap[v.AdminId] = v
+			delChapterAdminMap[v.AdminId] = v
+		}
+
+		for _, adminId := range adminIdList {
+			// 如果该用户 不在 报告授权的用户map 里面,说明这个用户是要移除的
+			if _, ok := reportGrantAdminIdMap[adminId]; !ok {
+				continue
+			}
+			_, ok := currChapterAdminMap[adminId]
+			// 如果存在,那么从 “需要删除的报告章节授权数据” 的map中移除
+			if ok {
+				delete(delChapterAdminMap, adminId)
+				continue
+			}
+			// 如果不存在,那么就新增授权
+			addChapterAdminList = append(addChapterAdminList, &report.ReportChapterGrant{
+				//GrantId:         0,
+				ReportChapterId: reportChapterInfo.ReportChapterId,
+				AdminId:         adminId,
+				CreateTime:      time.Now(),
+			})
+		}
+
+		// 查出需要移除的授权id
+		for _, v := range delChapterAdminMap {
+			delReportChapterGrantIdList = append(delReportChapterGrantIdList, v.GrantId)
+		}
+	}
+
+	// 其他章节授权的用户
+	otherChapterAdminMap := make(map[int]bool)
+	{
+		// 获取报告所有的章节id
+		reportChapterIdList, tmpErr := models.GetReportChapterIdList(reportInfo.Id)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		if len(reportChapterIdList) > 0 {
+			list, tmpErr := chapterGrantObj.GetGrantListByIdList(reportChapterIdList)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			for _, v := range list {
+				// 如果是当前章节,因为涉及到重新授权,所以得过滤
+				if v.ReportChapterId == reportChapterInfo.ReportChapterId {
+					continue
+				}
+				otherChapterAdminMap[v.AdminId] = true
+			}
+		}
+	}
+
+	// 需要添加的报告章节品种权限数据
+	addChapterPermissionList := make([]*report.ReportChapterPermissionMapping, 0)
+	// 待移除的报告章节品种权限数据id
+	delChapterPermissionMappingIdList := make([]int, 0)
+
+	// 处理当前报告章节需要新增/移除的品种权限信息
+	{
+		// 获取当前章节已经配置的品种权限信息
+		chapterPermissionList, tmpErr := chapterPermissionObj.GetPermissionListById(reportChapterInfo.ReportChapterId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		// 当前章节已经配置的品种权限信息
+		currChapterPermissionMap := make(map[int]*report.ReportChapterPermissionMapping)
+		// 需要删除的报告章节品种权限配置
+		delChapterPermissionMap := make(map[int]*report.ReportChapterPermissionMapping)
+		for _, v := range chapterPermissionList {
+			currChapterPermissionMap[v.ChartPermissionId] = v
+			delChapterPermissionMap[v.ChartPermissionId] = v
+		}
+
+		for _, permissionId := range permissionIdList {
+			_, ok := currChapterPermissionMap[permissionId]
+			// 如果存在,那么从 “需要删除的报告章节品种权限配置” 的map中移除
+			if ok {
+				delete(delChapterPermissionMap, permissionId)
+				continue
+			}
+			// 如果不存在,那么就新增品种权限配置
+			addChapterPermissionList = append(addChapterPermissionList, &report.ReportChapterPermissionMapping{
+				//ReportChapterPermissionMappingId:         0,
+				ReportChapterId:   reportChapterInfo.ReportChapterId,
+				ChartPermissionId: permissionId,
+				CreateTime:        time.Now(),
+			})
+		}
+
+		// 查出需要移除的品种权限配置
+		for _, v := range delChapterPermissionMap {
+			delChapterPermissionMappingIdList = append(delChapterPermissionMappingIdList, v.ReportChapterPermissionMappingId)
+		}
+	}
+
+	err = models.EditChapterBaseInfoAndPermission(reportInfo, reportChapterInfo, updateCols, addChapterAdminList, addChapterPermissionList, delReportChapterGrantIdList, delChapterPermissionMappingIdList)
+
+	return
+}
+
+// DelChapter
+// @Description: 删除报告章节、授权用户权限、品种权限
+// @author: Roc
+// @datetime 2024-06-06 17:28:37
+// @param reportInfo *models.Report
+// @param reportChapterInfo *models.ReportChapter
+// @param sysUser *system.Admin
+// @return err error
+// @return errMsg string
+func DelChapter(reportInfo *models.Report, reportChapterInfo *models.ReportChapter, sysUser *system.Admin) (err error, errMsg string) {
+	errMsg = "删除失败"
+	if reportInfo.State == 2 {
+		errMsg = "该报告已发布,不允许删除"
+		err = errors.New(errMsg)
+		return
+	}
+
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+	updateReportCols := []string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"}
+	err = models.DelChapterAndPermission(reportInfo, updateReportCols, reportChapterInfo)
+
+	return
+}
+
+// DownloadVoice
+// @Description:  下载报告音频文件
+// @author: Roc
+// @datetime 2024-06-13 15:36:46
+// @param reportInfo *models.ReportDetail
+// @return savePath string
+// @return fileName string
+// @return err error
+// @return errMsg string
+func DownloadVoice(reportInfo *models.ReportDetail) (savePath, fileName string, err error, errMsg string) {
+	errMsg = `下载失败`
+	// 如果报告有音频,那么下载音频
+	if reportInfo.VideoUrl != `` {
+		savePath = time.Now().Format(utils.FormatDateTimeUnSpace) + utils.GetRandString(5) + ".mp3"
+		fileName = reportInfo.VideoName + ".mp3"
+		fileBody, tmpErr := http.Get(reportInfo.VideoUrl)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsg = "音频下载失败"
+			return
+		}
+		err = file.SaveFile(fileBody, savePath)
+		if err != nil {
+			errMsg = "音频保存失败"
+			return
+		}
+	}
+
+	// 如果是章节报告,那么就下载压缩包
+	if reportInfo.HasChapter == 1 {
+		videoList, tmpErr := models.GetReportChapterVideoList(reportInfo.Id)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsg = "获取音频列表失败"
+			return
+		}
+
+		if len(videoList) > 0 {
+			// 创建zip
+			savePath = time.Now().Format(utils.FormatDateTimeUnSpace) + utils.GetRandString(5) + ".zip"
+			fileName = reportInfo.VideoName + ".zip"
+
+			zipFile, tmpErr := os.Create(savePath)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			zipWriter := zip.NewWriter(zipFile)
+			// 生成zip过程中报错关闭
+			defer func() {
+				if err != nil {
+					zipWriter.Close()
+					zipFile.Close()
+				}
+			}()
+
+			// 获取音频,写入zip
+			for i := 0; i < len(videoList); i++ {
+				item := videoList[i]
+				if item.VideoName == "" || item.VideoUrl == "" {
+					continue
+				}
+				// 音频文件后缀
+				ext := path.Ext(item.VideoUrl)
+				ioWriter, tmpErr := zipWriter.Create(fmt.Sprintf("%s%s", item.VideoName, ext))
+				if tmpErr != nil {
+					err = tmpErr
+					if os.IsPermission(err) {
+						fmt.Println("权限不足: ", err)
+						return
+					}
+					return
+				}
+
+				var content []byte
+				content, err = http.Get(item.VideoUrl)
+				if err != nil {
+					content = []byte("")
+				}
+
+				ioWriter.Write(content)
+			}
+			// 生成zip后关闭,否则下载文件会损坏
+			zipWriter.Close()
+			zipFile.Close()
+		}
+	}
+
+	return
+}
+
+// CheckReportAuthByAdminIdList
+// @Description: 根据管理员id列表,判断当前用户是否有报告权限
+// @author: Roc
+// @datetime 2024-06-13 11:03:10
+// @param adminId int
+// @param createAdminId int
+// @param grantAdminIdList []int
+// @return isAuth bool
+func CheckReportAuthByAdminIdList(adminId, createAdminId int, grantAdminIdList []int) (isAuth bool) {
+	// 如果是自己创建的报告,那么就有权限
+	if adminId == createAdminId {
+		isAuth = true
+		return
+	}
+	// 如果是授权用户,那么就有权限
+	if utils.IsCheckInList(grantAdminIdList, adminId) {
+		isAuth = true
+		return
+	}
+
+	return
+}
+
+// CheckReportAuthByReportChapterInfo
+// @Description: 根据报告ID,判断当前用户是否有报告权限
+// @author: Roc
+// @datetime 2024-06-13 16:21:28
+// @param adminId int
+// @param reportInfoId int
+// @return isAuth bool
+// @return err error
+func CheckReportAuthByReportChapterInfo(adminId, createAdminId int, reportInfoId int) (isAuth bool, err error) {
+	// 如果是自己创建的报告,那么就有权限
+	if adminId == createAdminId {
+		isAuth = true
+		return
+	}
+
+	obj := report.ReportGrant{}
+	chapterGrantList, err := obj.GetGrantListById(reportInfoId)
+	if err != nil {
+		return
+	}
+
+	for _, v := range chapterGrantList {
+		if v.AdminId == adminId {
+			isAuth = true
+			return
+		}
+	}
+
+	return
+}
+
+// EditReportLayoutImg
+// @Description: 修改报告的版图信息
+// @author: Roc
+// @datetime 2024-06-18 11:35:00
+// @param reportInfo *models.Report
+// @param req models.EditLayoutImgReq
+// @param sysUser *system.Admin
+// @return err error
+// @return errMsg string
+func EditReportLayoutImg(reportInfo *models.Report, req models.EditLayoutImgReq, sysUser *system.Admin) (err error, errMsg string) {
+	errMsg = `保存失败`
+
+	reportInfo.HeadResourceId = req.HeadResourceId
+	reportInfo.HeadImg = req.HeadImg
+	reportInfo.EndResourceId = req.EndResourceId
+	reportInfo.EndImg = req.EndImg
+	reportInfo.CanvasColor = req.CanvasColor
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+
+	updateCols := []string{"HeadResourceId", "HeadImg", "EndResourceId", "EndImg", "CanvasColor", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime"}
+
+	err = reportInfo.UpdateReport(updateCols)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// PublishReport
+// @Description: 报告发布
+// @author: Roc
+// @datetime 2024-06-20 09:44:13
+// @param reportId int
+// @param reportUrl string
+// @param sysUser *system.Admin
+// @return tips string
+// @return err error
+// @return errMsg string
+func PublishReport(reportId int, reportUrl string, sysUser *system.Admin) (tips string, err error, errMsg string) {
+	errMsg = `报告发布失败`
+	reportInfo, err := models.GetReportByReportId(reportId)
+	if err != nil {
+		errMsg = "获取报告信息失败"
+		return
+	}
+	if reportInfo == nil {
+		errMsg = "获取报告信息失败"
+		err = errors.New(errMsg)
+		return
+	}
+
+	var publishTime time.Time
+	if reportInfo.MsgIsSend == 1 && reportInfo.PublishTime.IsZero() { //如果报告曾经发布过,并且已经发送过模版消息,则章节的发布时间为报告的发布时间
+		publishTime = reportInfo.PublishTime
+	} else {
+		publishTime = time.Now()
+	}
+	var tmpErr error
+
+	// 章节类型的报告(原来的晨周报)
+	if reportInfo.HasChapter == 1 {
+		tips, tmpErr, errMsg = PublishChapterReport(reportInfo, reportUrl, sysUser)
+		if tmpErr != nil {
+			err = errors.New("晨周报发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(reportId))
+		}
+		return
+	}
+
+	// 普通报告
+	if reportInfo.Content == "" {
+		errMsg = `报告内容为空,不可发布`
+		err = errors.New("报告内容为空,不需要生成,report_id:" + strconv.Itoa(reportId))
+		return
+	}
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, reportInfo.ClassifyIdThird, models.ReportOperatePublish)
+	if e != nil {
+		errMsg = "操作失败"
+		err = errors.New("校验报告当前状态失败, Err: " + e.Error())
+		return
+	}
+
+	if state == models.ReportStatePublished {
+		// 发布报告
+		if tmpErr = models.PublishReportById(reportId, publishTime, sysUser.AdminId, sysUser.RealName); tmpErr != nil {
+			err = errors.New("报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(reportId))
+			return
+		}
+		go func() {
+			// 生成音频
+			if reportInfo.VideoUrl == "" {
+				_ = CreateVideo(reportInfo)
+			}
+			// 更新报告Es
+			_ = UpdateReportEs(reportId, 2)
+		}()
+	} else {
+		// 从无审批切换为有审批, 状态重置
+		if e = models.ResetReportById(reportId, state, sysUser.AdminId, sysUser.RealName); e != nil {
+			errMsg = "操作失败"
+			err = fmt.Errorf("重置报告状态失败, Err: %s, ReportId: %d", e.Error(), reportId)
+			return
+		}
+	}
+
+	recordItem := &models.ReportStateRecord{
+		ReportId:   reportId,
+		ReportType: 1,
+		State:      state,
+		AdminId:    sysUser.AdminId,
+		AdminName:  sysUser.AdminName,
+		CreateTime: time.Now(),
+	}
+	go func() {
+		_, _ = models.AddReportStateRecord(recordItem)
+	}()
+
+	// 生成报告pdf和长图
+	if reportUrl != "" {
+		go Report2pdfAndJpeg(reportUrl, reportId, 1)
+	}
+
+	// 报告权限处理
+	{
+		minClassifyId, _, tmpErr := getMinClassify(reportInfo)
+		if tmpErr != nil {
+			return
+		}
+		go handleReportPermission(int64(reportInfo.Id), minClassifyId)
+	}
+
+	return
+}
+
+// PublishChapterReport
+// @Description: 发布章节类型的报告
+// @author: Roc
+// @datetime 2024-06-28 10:38:50
+// @param reportInfo *models.Report
+// @param reportUrl string
+// @return tips string
+// @return err error
+// @return errMsg string
+func PublishChapterReport(reportInfo *models.Report, reportUrl string, sysUser *system.Admin) (tips string, err error, errMsg string) {
+	reportId := reportInfo.Id
+	if reportInfo.State == 2 {
+		return
+	}
+
+	// 获取所有章节列表
+	chapters, err := models.GetChapterListByReportId(reportId)
+	if err != nil {
+		return
+	}
+	chapterLen := len(chapters)
+	if chapterLen <= 0 {
+		tips = "报告章节为空,不可发布"
+		errMsg = tips
+		err = errors.New(tips)
+		return
+	}
+
+	reportChapterIdList := make([]int, 0)
+	// 获取报告中的所有章节列表
+	chapterList, err := models.GetChapterListByReportId(reportId)
+	if err != nil {
+		return
+	}
+	for _, chapter := range chapterList {
+		if chapter.PublishState == 1 {
+			tips = "还存在未发布的章节"
+			errMsg = tips
+			err = errors.New(tips)
+			return
+		}
+		reportChapterIdList = append(reportChapterIdList, chapter.ReportChapterId)
+	}
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, reportInfo.ClassifyIdThird, models.ReportOperatePublish)
+	if e != nil {
+		//errMsg = "操作失败"
+		err = errors.New("校验报告当前状态失败, Err: " + e.Error())
+		return
+	}
+
+	// 如果状态不是已发布,那么就重置状态
+	if state != models.ReportStatePublished {
+		// 从无审批切换为有审批, 状态重置
+		if e = models.ResetReportById(reportId, state, sysUser.AdminId, sysUser.RealName); e != nil {
+			//errMsg = "操作失败"
+			err = fmt.Errorf("重置报告状态失败, Err: %s, ReportId: %d", e.Error(), reportId)
+			return
+		}
+		return
+	}
+
+	// 需发布整期
+	updateCols := make([]string, 0)
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	updateCols = append(updateCols, "LastModifyAdminId", "LastModifyAdminName", "Title", "State", "ModifyTime")
+
+	// 发布后标题调整
+	//title := reportInfo.Title
+	//title = strings.ReplaceAll(title, "【弘则FICC晨报】", "")
+	//title = strings.ReplaceAll(title, "【弘则FICC周报】", "")
+	//if title == "" {
+	//	// 取第一个需发布章节的标题
+	//	firstId := publishIdArr[0]
+	//	firstTitle := ""
+	//	for i := 0; i < chapterLen; i++ {
+	//		if chapters[i].ReportChapterId == firstId {
+	//			firstTitle = chapters[i].Title
+	//			break
+	//		}
+	//	}
+	//	title = firstTitle
+	//}
+	//report.Title = title
+	reportInfo.State = state
+
+	// 研报后台4.4 只在没有发布过时更新发布时间,其余均按模版消息发送时间当作发布时间
+	if reportInfo.MsgIsSend == 0 || reportInfo.PublishTime.IsZero() {
+		reportInfo.PublishTime = time.Now().Local()
+		updateCols = append(updateCols, "PublishTime")
+
+	}
+	reportInfo.ModifyTime = time.Now().Local()
+
+	if e := models.PublishReportAndChapter(reportInfo, true, updateCols); e != nil {
+		err = errors.New("发布报告及章节失败")
+		return
+	}
+	// 生成章节音频
+	if len(reportChapterIdList) > 0 {
+		go func() {
+			_ = UpdateChaptersVideo(reportChapterIdList)
+		}()
+	}
+	// 更新报告ES
+	go func() {
+		_ = UpdateReportEs(reportInfo.Id, 2)
+	}()
+
+	// 发布时备份内容
+	go SaveReportLogs(reportInfo, chapters, reportInfo.AdminId, reportInfo.AdminRealName)
+
+	// 生成报告pdf和长图
+	if reportUrl != "" {
+		go Report2pdfAndJpeg(reportUrl, reportId, 1)
+	}
+
+	return
+}
+
+// getMinClassify
+// @Description: 获取最小分类ID
+// @author: Roc
+// @datetime 2024-06-20 09:23:19
+// @param reportInfo *models.Report
+// @return minClassifyId int
+// @return minClassifyName string
+// @return err error
+func getMinClassify(reportInfo *models.Report) (minClassifyId int, minClassifyName string, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("获取最小分类ID失败,报告ID:%d,Err:%s", reportInfo.Id, err.Error())
+		}
+	}()
+	minClassifyId = reportInfo.ClassifyIdThird
+	minClassifyName = reportInfo.ClassifyNameThird
+	if minClassifyId <= 0 {
+		minClassifyId = reportInfo.ClassifyIdSecond
+		minClassifyName = reportInfo.ClassifyNameSecond
+	}
+	if minClassifyId <= 0 {
+		minClassifyId = reportInfo.ClassifyIdFirst
+		minClassifyName = reportInfo.ClassifyNameFirst
+	}
+	if minClassifyId <= 0 {
+		err = errors.New("分类异常")
+	}
+
+	return
+}
+
+// handleReportPermission
+// @Description: 报告权限处理
+// @author: Roc
+// @datetime 2024-06-19 18:00:51
+// @param reportId int64
+// @param minClassifyId int
+func handleReportPermission(reportId int64, minClassifyId int) {
+	// TODO 报告权限处理
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("报告权限处理失败,报告ID:%d,分类ID:%d,Err:%s", reportId, minClassifyId, err.Error())
+			//alarm_msg.SendAlarmMsg("修改删除报告权限失败,Err:"+err.Error(), 3)
+		}
+	}()
+
+	err = models.RemoveChartPermissionChapterMapping(reportId)
+	if err != nil {
+		err = errors.New("修改删除报告权限失败,Err:" + err.Error())
+		return
+	}
+	permissionItems, err := models.GetPermission(minClassifyId)
+	if err != nil {
+		err = errors.New("获取权限失败,Err:" + err.Error())
+		return
+	}
+	for _, v := range permissionItems {
+		err = models.AddChartPermissionChapterMapping(v.ChartPermissionId, reportId)
+		if err != nil {
+			err = errors.New("新增权限失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	// 同步crm权限
+	_ = EditReportPermissionSync(reportId, minClassifyId)
+
+	return
+}

+ 6 - 3
services/smart_report.go

@@ -47,7 +47,7 @@ func SmartReportBuildVideoAndUpdate(item *smart_report.SmartReport) {
 
 // UpdateSmartReportEditing 更新研报当前更新状态
 // status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
-func UpdateSmartReportEditing(reportId, status, thisUserId int, thisUserName string, adminIdName map[int]string) (ret models.MarkReportResp, err error) {
+func UpdateSmartReportEditing(reportId, status, thisUserId int, thisUserName string, adminIdName map[int]string, lang string) (ret models.MarkReportResp, err error) {
 	key := fmt.Sprint(utils.CACHE_SMART_REPORT_EDITING, reportId)
 	ret.Status = 0
 	ret.Msg = "无人编辑"
@@ -71,7 +71,11 @@ func UpdateSmartReportEditing(reportId, status, thisUserId int, thisUserName str
 			editor = adminIdName[opUserId]
 		}
 		ret.Status = 1
-		ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+		if lang == utils.EnLangVersion {
+			ret.Msg = fmt.Sprintf("%s is currently editing the report", editor)
+		} else {
+			ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+		}
 		ret.Editor = editor
 		return
 	}
@@ -132,7 +136,6 @@ func SmartReportElasticUpsert(smartReportId int, state int) (err error) {
 	return
 }
 
-
 func ReportToPdf(reportUrl, filePath string) (err error) {
 	pyCode := `
 import asyncio

+ 1 - 1
services/video.go

@@ -21,7 +21,7 @@ import (
 	"unicode"
 )
 
-func CreateVideo(report *models.ReportDetail) (err error) {
+func CreateVideo(report *models.Report) (err error) {
 	defer func() {
 		if err != nil {
 			utils.FileLog.Error("CreateVideo Err:%s", err.Error())

+ 37 - 25
services/wechat_send_msg.go

@@ -4,10 +4,11 @@ import (
 	"encoding/json"
 	"errors"
 	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/company"
 	"eta/eta_mobile/services/alarm_msg"
 	"eta/eta_mobile/utils"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strconv"
 	"strings"
@@ -15,10 +16,6 @@ import (
 
 // SendMiniProgramReportWxMsg 推送报告微信模板消息-小程序链接
 func SendMiniProgramReportWxMsg(reportId int) (err error) {
-	if utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeSandbox {
-		return
-	}
-
 	var msg string
 	reportIdStr := strconv.Itoa(reportId)
 	defer func() {
@@ -31,7 +28,7 @@ func SendMiniProgramReportWxMsg(reportId int) (err error) {
 	}()
 	utils.FileLog.Info("%s", "services SendMsg")
 
-	report, err := models.GetReportById(reportId)
+	report, err := models.GetReportByReportId(reportId)
 	if err != nil {
 		msg = "GetReportInfo Err:" + err.Error()
 		return
@@ -54,30 +51,34 @@ func SendMiniProgramReportWxMsg(reportId int) (err error) {
 	//}
 
 	var openIdArr []string
-	if report.ClassifyIdSecond <= 0 {
+
+	// 如果是弘则,且报告分类是晨报,那么就所有人推送
+	if (utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox || utils.BusinessCode == utils.BusinessCodeDebug) && report.ClassifyNameFirst == "晨报" {
+		// 如果是章节,那就推送所有用户
 		openIdArr, err = models.GetOpenIdArr()
 		if err != nil {
 			msg = "get GetOpenIdArr err:" + err.Error()
 			return
 		}
 	} else {
-		classify, err := models.GetClassifyById(report.ClassifyIdSecond)
+		minClassifyId, _, err := getMinClassify(report)
+		if err != nil {
+			msg = "获取报告的最小分类失败 err:" + err.Error()
+			return err
+		}
+
+		// 判断分类是否存在
+		_, err = models.GetClassifyById(minClassifyId)
 		if err != nil {
 			msg = "获取报告分类失败 err:" + err.Error()
 			return err
 		}
-		if classify.IsMassSend == 1 {
-			openIdArr, err = models.GetOpenIdArr()
-			if err != nil {
-				msg = "get GetOpenIdArr err:" + err.Error()
-				return err
-			}
-		} else {
-			openIdArr, err = models.GetOpenIdArrByClassifyNameSecond(report.ClassifyNameSecond)
-			if err != nil {
-				msg = "GetOpenIdArrByClassifyNameSecond err:" + err.Error()
-				return err
-			}
+
+		// 获取该分类关联的openid列表
+		openIdArr, err = models.GetOpenIdArrByClassifyId(minClassifyId)
+		if err != nil {
+			msg = "GetOpenIdArrByClassifyNameSecond err:" + err.Error()
+			return err
 		}
 	}
 
@@ -95,7 +96,7 @@ func SendMiniProgramReportWxMsg(reportId int) (err error) {
 	first := fmt.Sprintf("Hi,最新一期%s已上线,欢迎查看", report.ClassifyNameFirst)
 	keyword1 := title
 	keyword2 := report.Title
-	keyword3 := report.PublishTime
+	keyword3 := report.PublishTime.Format(utils.FormatDateTime)
 	keyword4 := report.Abstract
 
 	//sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
@@ -109,7 +110,14 @@ func SendMiniProgramReportWxMsg(reportId int) (err error) {
 	//sendMap["data"] = sendData
 
 	var wxAppPath string
-	if report.ChapterType == utils.REPORT_TYPE_WEEK {
+	weekClassifyId, err := company.GetReportClassifyIdByConfigKey("report_week_classify_id")
+	if err != nil {
+		msg = "获取周报ID配置失败:" + err.Error()
+		return err
+	}
+
+	// 周报走这个逻辑,我也不清楚为什么,原先就是这样(2024-6-20 16:46:07)
+	if report.ClassifyIdFirst == weekClassifyId {
 		wxAppPath = fmt.Sprintf("pages-report/chapterList?reportId=%s", reportIdStr)
 	} else {
 		wxAppPath = fmt.Sprintf("pages-report/reportDetail?reportId=%s", reportIdStr)
@@ -175,14 +183,18 @@ type SendWxTemplate struct {
 	OpenIdArr      []string `description:"消息接收者openid"`
 }
 
-// 推送模板消息
+// SendTemplateMsg 推送模板消息
 func SendTemplateMsg(sendInfo *SendWxTemplate) (err error) {
+	if utils.SendWxTemplateMsgUrl == `` {
+		// 找不到推送服务
+		return
+	}
 	postData, err := json.Marshal(sendInfo)
 	if err != nil {
 		alarm_msg.SendAlarmMsg("SendTemplateMsg json.Marshal Err:"+err.Error(), 1)
 		return err
 	}
-	body := ioutil.NopCloser(strings.NewReader(string(postData)))
+	body := io.NopCloser(strings.NewReader(string(postData)))
 	client := &http.Client{}
 	req, err := http.NewRequest("POST", utils.SendWxTemplateMsgUrl, body)
 	if err != nil {
@@ -198,7 +210,7 @@ func SendTemplateMsg(sendInfo *SendWxTemplate) (err error) {
 		return err
 	}
 	defer resp.Body.Close()
-	b, err := ioutil.ReadAll(resp.Body)
+	b, err := io.ReadAll(resp.Body)
 	if err != nil {
 		return err
 	}

+ 101 - 0
utils/common.go

@@ -12,7 +12,9 @@ import (
 	"errors"
 	"fmt"
 	"github.com/PuerkitoBio/goquery"
+	"github.com/microcosm-cc/bluemonday"
 	"github.com/shopspring/decimal"
+	xhtml "golang.org/x/net/html"
 	"html"
 	"image"
 	"image/png"
@@ -21,6 +23,7 @@ import (
 	"math/rand"
 	"net"
 	"net/http"
+	"net/url"
 	"os"
 	"os/exec"
 	"path"
@@ -2282,3 +2285,101 @@ func DateConvMysqlConvMongo(dateCon string) string {
 	}
 	return cond
 }
+
+// 检查src属性是否以http或data:image开头
+func isValidSrc(src string) bool {
+	// 使用Parse函数解析URL
+	parsedURL, err := url.Parse(src)
+	if err != nil {
+		validSchemes := regexp.MustCompile(`^data:image\/.*;base64,.*$`)
+		return validSchemes.MatchString(src)
+	}
+	if parsedURL.Host == "" || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") {
+		validSchemes := regexp.MustCompile(`^data:image\/.*;base64,.*$`)
+		return validSchemes.MatchString(src)
+	}
+	return true
+}
+
+// ContentXssCheck 校验文本中的JS代码
+func ContentXssCheck(content string) (err error) {
+	// 解析HTML内容
+	node, err := xhtml.Parse(strings.NewReader(content))
+	if err != nil {
+		err = fmt.Errorf(" html.Parse Err: %v", err)
+		return
+	}
+
+	// 遍历解析后的节点树,查找特定标签
+	var visit func(n *xhtml.Node) error
+	visit = func(n *xhtml.Node) error {
+		if n.Type == xhtml.ElementNode {
+			lowerData := strings.ToLower(n.Data)
+			switch lowerData {
+			case "script", "javascript":
+				err = fmt.Errorf(" script is forbidden")
+				return err
+			default:
+				for _, attr := range n.Attr { //判断事件
+					lowerKey := strings.ToLower(attr.Key)
+					lowerVal := strings.ToLower(attr.Val)
+					if lowerKey == "src" || lowerKey == "dynsrc" || lowerKey == "background" || lowerKey == "lowsrc" {
+						if lowerVal != `` && !isValidSrc(lowerVal) {
+							err = fmt.Errorf("invalid src attribute value: %s", attr.Val)
+							return err
+						}
+					}
+					if strings.HasPrefix(lowerKey, "on") {
+						err = fmt.Errorf("the event is forbidden: %s:%s", attr.Key, attr.Val)
+						return err
+					}
+					if lowerKey == "style" {
+						if strings.Contains(lowerVal, "javascript:") || strings.Contains(lowerVal, "script:") {
+							err = fmt.Errorf("invalid style attribute value: %s", attr.Val)
+							return err
+						}
+					}
+				}
+				/*	case "src":
+					// 如果<src>是某个标签的属性,你可能需要递归检查其父节点
+					// 这里简单起见,我们假设<src>不是有效的HTML标签,并忽略它
+					// 在实际中,你可能需要更复杂的逻辑来处理这种情况
+					fmt.Println("Warning: Unexpected 'src' tag found.")*/
+			}
+		}
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			if err = visit(c); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	// 检查HTML文档中的事件
+	if err = visit(node); err != nil {
+		return
+	}
+	return
+}
+
+func ContentXssFilter(content string) (cleanContent string) {
+	p := customXssPolicy()
+	// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
+	cleanContent = p.Sanitize(
+		content,
+	)
+	return
+}
+
+func customXssPolicy() (p *bluemonday.Policy) {
+	p = bluemonday.UGCPolicy()
+
+	// iframe
+	p.AllowElements("iframe")
+	p.AllowAttrs("width").OnElements("iframe")
+	p.AllowAttrs("height").OnElements("iframe")
+	p.AllowAttrs("src").OnElements("iframe")
+	p.AllowAttrs("frameborder").Matching(bluemonday.Number).OnElements("iframe")
+	p.AllowAttrs("allow").Matching(regexp.MustCompile(`[a-z; -]*`)).OnElements("iframe")
+	p.AllowAttrs("allowfullscreen").OnElements("iframe")
+	return
+}

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov