Browse Source

fix:对外接口服务关于报告模块得做调整(列表、详情、审批)

Roc 8 months ago
parent
commit
a7aa3c731b

+ 139 - 11
controllers/report.go

@@ -23,6 +23,7 @@ type ReportController struct {
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
 // @Param   Keyword   query   string  false       "搜索关键词"
 // @Param   State   query   int  false       "状态"
+// @Param   IsPublicPublish   query   int  false       "是否公开发布,1:是,2:否"
 // @Success 200 {object} models.ReportListResp
 // @router /list [get]
 func (this *ReportController) List() {
@@ -57,15 +58,21 @@ func (this *ReportController) List() {
 		condition += ` AND state = ? `
 		pars = append(pars, state)
 	}
-	admindId, _ := this.GetInt("AdminId")
-	if admindId > 0 {
+	isPublicPublish, _ := this.GetInt("IsPublicPublish")
+	if isPublicPublish > 0 {
+		condition += ` AND is_public_publish = ? `
+		pars = append(pars, isPublicPublish)
+	}
+
+	adminId, _ := this.GetInt("AdminId")
+	if adminId > 0 {
 		condition += ` AND admin_id = ? `
-		pars = append(pars, admindId)
+		pars = append(pars, adminId)
 	}
 	classifyId, _ := this.GetInt("ClassifyId")
 	if classifyId > 0 {
-		condition += ` AND (classify_id_first = ? OR classify_id_second = ?) `
-		pars = append(pars, classifyId, classifyId)
+		condition += ` AND (classify_id_first = ? OR classify_id_second = ? OR classify_id_third = ?) `
+		pars = append(pars, classifyId, classifyId, classifyId)
 	}
 
 	total, err := models.GetReportListCount(condition, pars, "")
@@ -82,8 +89,10 @@ func (this *ReportController) List() {
 	}
 
 	results := make([]*models.ReportItem, 0)
+
+	chapterList := make([]*models.ReportChapterItem, 0)
 	for _, v := range list {
-		t := models.FormatReport2Item(v)
+		t := models.FormatReport2Item(v, chapterList)
 		results = append(results, t)
 	}
 
@@ -129,7 +138,48 @@ func (this *ReportController) Detail() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-	resp := models.FormatReport2Item(item)
+
+	chapterList := make([]*models.ReportChapterItem, 0)
+	if item.HasChapter == 1 {
+		// 获取章节内容
+		tmpChapterList, err := models.GetChapterListByReportId(item.Id)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取晨/周报章节列表失败, Err: " + err.Error()
+			return
+		}
+
+		if len(tmpChapterList) > 0 {
+			// 章节类型的字段赋值
+			for _, item := range tmpChapterList {
+				chapterList = append(chapterList, models.FormatReportChapterItem(item))
+			}
+		}
+	}
+
+	if item.HeadResourceId > 0 {
+		headResource, err := models.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 := models.GetResourceItemById(item.EndResourceId)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+			return
+		}
+		item.EndImg = endResource.ImgUrl
+		item.EndStyle = endResource.Style
+	}
+
+	resp := models.FormatReport2Item(item, chapterList)
 
 	br.Data = resp
 	br.Ret = 200
@@ -176,7 +226,7 @@ func (this *ReportController) Approve() {
 		br.ErrMsg = fmt.Sprintf("报告状态有误, State: %d", item.State)
 		return
 	}
-	if item.Content == "" {
+	if item.HasChapter != 1 && item.Content == "" {
 		br.Msg = "报告内容为空,请检查内容"
 		return
 	}
@@ -254,7 +304,7 @@ func (this *ReportController) Approve() {
 // @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
 // @Param   HideDayWeek   query   int  false       "是否隐藏晨周报"
 // @Success 200 {object} models.Classify
-// @router /classify/list [get]
+// @router /classify/list/bak [get]
 func (this *ReportController) ListClassify() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
@@ -266,6 +316,8 @@ func (this *ReportController) ListClassify() {
 	keyWord := this.GetString("KeyWord")
 	//companyType := this.GetString("CompanyType")
 	companyType := ""
+	br.Msg = "该接口已启用,请调用V2接口"
+	return
 
 	var startSize int
 	if pageSize <= 0 {
@@ -362,7 +414,7 @@ func (this *ReportController) ListClassify() {
 			childrenMap[children[i].ParentId] = make([]*models.ClassifyItem, 0)
 		}
 		childrenMap[children[i].ParentId] = append(childrenMap[children[i].ParentId], &models.ClassifyItem{
-			Classify:       *children[i],
+			Classify: *children[i],
 			//ClassifyMenuId: relateMap[children[i].Id],
 		})
 	}
@@ -380,4 +432,80 @@ func (this *ReportController) ListClassify() {
 	br.Data = resp
 	br.Ret = 200
 	br.Msg = "获取成功"
-}
+}
+
+// ListClassifyV2
+// @Title 获取所有分类列表(2024-6-26 13:49:03)
+// @Description 获取所有分类列表(2024-6-26 13:49:03)
+// @Param   KeyWord   query   string  true       "检索关键词"
+// @Success 200 {object} models.ClassifyListV2Resp
+// @router /classify/list [get]
+func (this *ReportController) ListClassifyV2() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	resp := new(models.ClassifyListV2Resp)
+
+	keyWord := this.GetString("KeyWord")
+
+	list, err := models.GetClassifyListByKeywordV2(keyWord, -1)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	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.ClassifyListV2, 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 {
+		classifyIdList = append(classifyIdList, list[i].Id)
+	}
+	parentIdLen := len(classifyIdList)
+	if parentIdLen == 0 {
+		resp.List = list
+		br.Data = resp
+		br.Ret = 200
+		br.Msg = "获取成功"
+		return
+	}
+
+	// 先将分类列表排序
+	services.SortClassifyListBySortAndCreateTime(list)
+	// 接着转换结构
+	list = services.GetClassifyListTreeRecursive(list, 0)
+
+	resp.List = list
+	br.Data = resp
+	br.Ret = 200
+	br.Msg = "获取成功"
+}

+ 60 - 1
models/classify.go

@@ -39,6 +39,9 @@ type Classify struct {
 	RelateTel         int       `json:"-" description:"是否在电话会中可选: 0-否; 1-是"`
 	RelateVideo       int       `json:"-" description:"是否在路演视频中可选: 0-否; 1-是"`
 	IsMassSend        int       `json:"-" description:"1:群发,0:非群发"`
+	Enabled           int       `description:"是否可用,1可用,0禁用"`
+	Level             int       `description:"层级"`
+	HasChild          int       `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
 }
 
 type ClassifyListResp struct {
@@ -95,4 +98,60 @@ func GetClassifyChildByParentIds(parentId []int, keyWord string) (items []*Class
 	_, err = o.Raw(sql, pars...).QueryRows(&items)
 
 	return
-}
+}
+
+type ClassifyListV2Resp struct {
+	List []*ClassifyListV2
+}
+
+type ClassifyListV2 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-是"`
+	Enabled           int       `description:"是否可用,1可用,0禁用"`
+	Child             []*ClassifyListV2
+	ClassifyMenuId    int `description:"二级分类-子目录ID"`
+	//ClassifyMenuList      []*ClassifyMenu
+	//ChartPermissionIdList []int `description:"绑定的权限ID"`
+	Level    int `description:"层级"`
+	HasChild int `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
+}
+
+// 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 []*ClassifyListV2, 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
+}

+ 2 - 0
models/db.go

@@ -2,6 +2,7 @@ package models
 
 import (
 	"eta/eta_hub/models/data_manage"
+	"eta/eta_hub/models/report"
 	"eta/eta_hub/utils"
 	"time"
 
@@ -44,6 +45,7 @@ func init() {
 		new(data_manage.EdbSource),
 		new(data_manage.EdbClassify),
 		new(data_manage.EdbInfo),
+		new(report.ReportChapterPermissionMapping), // 报告章节的权限关系表
 	)
 
 	// 初始化指标来源

+ 15 - 0
models/permission.go

@@ -0,0 +1,15 @@
+package models
+
+import "github.com/beego/beego/v2/client/orm"
+
+type ChartPermissionMappingIdName struct {
+	PermissionId   int
+	PermissionName string
+}
+
+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.classify_id = ? "
+	_, err = o.Raw(sql, source, classifyId).QueryRows(&list)
+	return
+}

+ 95 - 4
models/report.go

@@ -69,6 +69,27 @@ type Report struct {
 	PublishTime        time.Time `description:"发布时间"`
 	CreateTime         time.Time `description:"创建时间"`
 	ModifyTime         time.Time `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:"报告时间创建时间"`
 }
 
 type ReportListResp struct {
@@ -430,6 +451,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:"报告期数"`
 }
@@ -458,9 +481,32 @@ type ReportItem struct {
 	ApproveTime        string `description:"审批时间"`
 	CreateTime         string `description:"创建时间"`
 	ModifyTime         string `description:"修改时间"`
-}
-
-func FormatReport2Item(origin *Report) (item *ReportItem) {
+	// eta1.8.3(研报改版)相关内容
+	//ContentStruct       string    `description:"内容组件"`
+	//LastModifyAdminId   int       `description:"最后更新人ID"`
+	//LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime string `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 string               `description:"报告时间创建时间"`
+	ChapterList      []*ReportChapterItem `description:"章节列表"`
+	HasChapter       int                  `description:"是否有章节 0-否 1-是"`
+}
+
+func FormatReport2Item(origin *Report, chapterList []*ReportChapterItem) (item *ReportItem) {
 	if origin == nil {
 		return
 	}
@@ -488,6 +534,23 @@ func FormatReport2Item(origin *Report) (item *ReportItem) {
 	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
 	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
 	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+
+	// eta1.8.3(研报改版)相关内容
+	item.HeadImg = origin.HeadImg
+	item.EndImg = origin.EndImg
+	item.HeadStyle = origin.HeadStyle
+	item.EndStyle = origin.EndStyle
+	item.CanvasColor = origin.CanvasColor
+	item.NeedSplice = origin.NeedSplice
+	item.ClassifyIdThird = origin.ClassifyIdThird
+	item.ClassifyNameThird = origin.ClassifyNameThird
+	item.ReportLayout = origin.ReportLayout
+	item.IsPublicPublish = origin.IsPublicPublish
+	item.ContentModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ContentModifyTime)
+	item.ReportCreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.ReportCreateTime)
+	item.ChapterList = chapterList
+	item.HasChapter = origin.HasChapter
+
 	return
 }
 
@@ -565,4 +628,32 @@ func GetClassifyListCount(keyWord, companyType string) (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	err = o.Raw(sqlCount, pars...).QueryRow(&count)
 	return
-}
+}
+
+// GetClassifyListByKeywordV2
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*ClassifyList
+// @return err error
+func GetClassifyListByKeywordV2(keyWord string, enabled int) (items []*ClassifyListV2, 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
+}

+ 152 - 0
models/report/report_chapter_permission_mapping.go

@@ -0,0 +1,152 @@
+package report
+
+import (
+	"eta/eta_hub/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
+}

+ 139 - 0
models/report_chapter.go

@@ -0,0 +1,139 @@
+package models
+
+import (
+	"eta/eta_hub/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// 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"`
+	ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+}
+
+// GetChapterListByReportId 根据ReportId获取章节列表
+func GetChapterListByReportId(reportId int) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_id = ?  ORDER BY sort ASC`
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+type ReportChapterItem struct {
+	ReportChapterId   int    `orm:"column(report_chapter_id);pk" description:"报告章节ID"`
+	ReportId          int    `description:"报告ID"`
+	ReportType        string `json:"-" 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 `json:"-" 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 `json:"-" description:"原始音频文件URL"`
+	//ContentStruct       string    `description:"内容组件"`
+	LastModifyAdminId   int    `json:"-" description:"最后更新人ID"`
+	LastModifyAdminName string `json:"-" description:"最后更新人姓名"`
+	ContentModifyTime   string `description:"内容更新时间"`
+	ReportLayout        int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportCreateTime    string `description:"报告时间创建时间"`
+}
+
+func FormatReportChapterItem(origin *ReportChapter) (item *ReportChapterItem) {
+	if origin == nil {
+		return
+	}
+	item = &ReportChapterItem{
+		ReportChapterId:   origin.ReportChapterId,
+		ReportId:          origin.ReportId,
+		ReportType:        origin.ReportType,
+		ClassifyIdFirst:   origin.ClassifyIdFirst,
+		ClassifyNameFirst: origin.ClassifyNameFirst,
+		TypeId:            origin.TypeId,
+		TypeName:          origin.TypeName,
+		Title:             origin.Title,
+		Abstract:          origin.Abstract,
+		AddType:           origin.AddType,
+		Author:            origin.Author,
+		Content:           origin.Content,
+		ContentSub:        origin.ContentSub,
+		Stage:             origin.Stage,
+		Trend:             origin.Trend,
+		Sort:              origin.Sort,
+		IsEdit:            origin.IsEdit,
+		PublishState:      origin.PublishState,
+		PublishTime:       utils.TimeTransferString(utils.FormatDateTime, origin.PublishTime),
+		VideoUrl:          origin.VideoUrl,
+		VideoName:         origin.VideoName,
+		VideoPlaySeconds:  origin.VideoPlaySeconds,
+		VideoSize:         origin.VideoSize,
+		VideoKind:         origin.VideoKind,
+		CreateTime:        origin.CreateTime,
+		OriginalVideoUrl:  origin.OriginalVideoUrl,
+		//LastModifyAdminId:   origin.LastModifyAdminId,
+		//LastModifyAdminName: origin.LastModifyAdminName,
+		ContentModifyTime: utils.TimeTransferString(utils.FormatDateTime, origin.ContentModifyTime),
+		ReportLayout:      origin.ReportLayout,
+		ReportCreateTime:  utils.TimeTransferString(utils.FormatDateTime, origin.ReportCreateTime),
+	}
+
+	return
+}
+
+// GetPublishedChapterListByReportId 根据ReportId获取已发布章节列表
+func GetPublishedChapterListByReportId(reportId int) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_id = ? AND publish_state = 2 ORDER BY sort ASC`
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}

+ 118 - 0
models/smart_resource.go

@@ -0,0 +1,118 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+type SmartReportResource struct {
+	ResourceId int       `orm:"column(resource_id);pk" description:"智能研报资源ID"`
+	ImgUrl     string    // 图片链接
+	Style      string    // 版图样式
+	ImgName    string    // 图片名称
+	Type       int       // 类型 1-版头 2-版尾
+	CreateTime time.Time // 创建时间
+}
+
+func (m *SmartReportResource) TableName() string {
+	return "smart_report_resource"
+}
+
+func (m *SmartReportResource) PrimaryId() string {
+	return "resource_id"
+}
+
+func (m *SmartReportResource) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ResourceId = int(id)
+	return
+}
+
+func (m *SmartReportResource) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *SmartReportResource) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ResourceId).Exec()
+	return
+}
+
+type SmartReportResourceItem struct {
+	ResourceId int    `orm:"column(resource_id);pk" description:"智能研报资源ID"`
+	ImgUrl     string // 图片链接
+	ImgName    string // 图片名称
+	Style      string // 版图样式
+	Type       int    // 类型 1-版头 2-版尾
+	CreateTime string // 创建时间
+}
+
+// SmartReportResourceListResp 智能研报资源库
+type SmartReportResourceListResp struct {
+	List   []*SmartReportResourceItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func (m *SmartReportResource) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *SmartReportResource) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, startSize, pageSize int) (items []*SmartReportResourceItem, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ` ORDER BY create_time DESC`
+
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// SmartReportResourceEditReq 智能研报资源编辑请求体
+type SmartReportResourceEditReq struct {
+	ResourceId int    `description:"资源ID"`
+	ImgName    string `description:"图片名称"`
+	Style      string `description:"版图样式"`
+}
+
+func (m *SmartReportResource) GetItemById(id int) (item *SmartReportResource, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// SmartReportResourceRemoveReq 删除智能研报资源请求体
+type SmartReportResourceRemoveReq struct {
+	ResourceIds string `description:"资源IDs"`
+}
+
+// SmartReportResourceAddReq 新增智能研报资源请求体
+type SmartReportResourceAddReq struct {
+	Type    int    `description:"类型 1-版头 2-版尾"`
+	ImgUrl  string `description:"图片链接"`
+	ImgName string `description:"图片名称"`
+	Style   string `description:"版图样式"`
+}
+
+func GetResourceItemById(id int) (item *SmartReportResource, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM smart_report_resource WHERE resource_id = ? LIMIT 1`)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}

+ 10 - 1
routers/commentsRouter.go

@@ -234,13 +234,22 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "ListClassify",
+            Method: "ListClassifyV2",
             Router: `/classify/list`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ListClassify",
+            Router: `/classify/list/bak`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:ReportController"],
         beego.ControllerComments{
             Method: "Detail",

+ 276 - 0
services/report.go

@@ -1,11 +1,15 @@
 package services
 
 import (
+	"errors"
 	"eta/eta_hub/models"
+	"eta/eta_hub/models/report"
 	"eta/eta_hub/utils"
 	"fmt"
 	"html"
+	"sort"
 	"strconv"
+	"strings"
 )
 
 // UpdateReportEs 更新报告/章节Es
@@ -18,7 +22,41 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 		return
 	}
 	categories := ""
+	if reportInfo.HasChapter == 1 {
+		// 晨周报
+		chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
+		if tmpErr != nil {
+			return
+		}
+		if len(chapterList) > 0 {
+			// 更新章节的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("rddp", reportInfo.ClassifyIdSecond)
+		if tmpErr != nil {
+			return
+		}
+		categoryArr := make([]string, 0)
+		for i := 0; i < len(permissionList); i++ {
+			categoryArr = append(categoryArr, permissionList[i].PermissionName)
+		}
+		aliasArr, _ := addCategoryAliasToArr(categoryArr)
+		categories = strings.Join(aliasArr, ",")
+		//}
+	}
 
+	// 最小单位的分类id
+	minClassifyId, minClassifyName, err := getMinClassify(reportInfo)
+	if err != nil {
+		return
+	}
 
 	// 新增报告ES
 	esReport := &models.ElasticReportDetail{
@@ -34,6 +72,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),
 	}
@@ -44,3 +84,239 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 
 	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) {
+	// 章节对应的品种
+	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].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.UnescapeString(chapterInfo.Content)),
+		PublishTime:        chapterInfo.PublishTime.Format(utils.FormatDateTime),
+		PublishState:       chapterInfo.PublishState,
+		Author:             chapterInfo.Author,
+		ClassifyIdFirst:    chapterInfo.ClassifyIdFirst,
+		ClassifyNameFirst:  chapterInfo.ClassifyNameFirst,
+		ClassifyIdSecond:   0,
+		ClassifyNameSecond: "",
+		ClassifyId:         chapterInfo.ClassifyIdFirst,
+		ClassifyName:       chapterInfo.ClassifyNameFirst,
+		Categories:         categories,
+		StageStr:           strconv.Itoa(chapterInfo.Stage),
+	}
+	chapterDocId := fmt.Sprintf("%d-%d", chapterInfo.ReportId, chapterInfo.ReportChapterId)
+	if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
+		return
+	}
+
+	return
+}
+
+// addCategoryAliasToArr 品种别名
+func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error) {
+	aliasArr = categoryArr
+	if len(categoryArr) > 0 {
+		for i := 0; i < len(categoryArr); i++ {
+			if strings.Contains(categoryArr[i], "沥青") {
+				aliasArr = append(aliasArr, "BU")
+			}
+			if strings.Contains(categoryArr[i], "MEG") {
+				aliasArr = append(aliasArr, "EG", "乙二醇")
+			}
+			if strings.Contains(categoryArr[i], "聚酯") {
+				aliasArr = append(aliasArr, "长丝", "短纤", "瓶片")
+			}
+			if strings.Contains(categoryArr[i], "纯苯+苯乙烯") {
+				aliasArr = append(aliasArr, "EB")
+			}
+			if strings.Contains(categoryArr[i], "聚乙烯") {
+				aliasArr = append(aliasArr, "PP", "PE")
+			}
+			if strings.Contains(categoryArr[i], "玻璃纯碱") {
+				aliasArr = append(aliasArr, "玻璃", "纯碱", "FG", "SA")
+			}
+			if strings.Contains(categoryArr[i], "甲醇") {
+				aliasArr = append(aliasArr, "甲醇", "MA")
+			}
+			if strings.Contains(categoryArr[i], "橡胶") {
+				aliasArr = append(aliasArr, "橡胶", "RU")
+			}
+		}
+	}
+	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
+}
+
+// UpdateReportEs 更新报告/章节Es
+//func UpdateReportEs(reportId int, publishState int) (err error) {
+//	if reportId <= 0 {
+//		return
+//	}
+//	reportInfo, err := models.GetReportByReportId(reportId)
+//	if err != nil {
+//		return
+//	}
+//	categories := ""
+//
+//	// 新增报告ES
+//	esReport := &models.ElasticReportDetail{
+//		ReportId:           reportInfo.Id,
+//		ReportChapterId:    0,
+//		Title:              reportInfo.Title,
+//		Abstract:           reportInfo.Abstract,
+//		BodyContent:        utils.TrimHtml(html.UnescapeString(reportInfo.Content)),
+//		PublishTime:        reportInfo.PublishTime.Format(utils.FormatDateTime),
+//		PublishState:       publishState,
+//		Author:             reportInfo.Author,
+//		ClassifyIdFirst:    reportInfo.ClassifyIdFirst,
+//		ClassifyNameFirst:  reportInfo.ClassifyNameFirst,
+//		ClassifyIdSecond:   reportInfo.ClassifyIdSecond,
+//		ClassifyNameSecond: reportInfo.ClassifyNameSecond,
+//		Categories:         categories,
+//		StageStr:           strconv.Itoa(reportInfo.Stage),
+//	}
+//	docId := fmt.Sprintf("%d-%d", reportInfo.Id, 0)
+//	if err = EsAddOrEditReport(utils.EsReportIndexName, docId, esReport); err != nil {
+//		return
+//	}
+//
+//	return
+//}
+
+// 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.ClassifyListV2, 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.ClassifyListV2
+func GetClassifyListTreeRecursive(list []*models.ClassifyListV2, parentId int) []*models.ClassifyListV2 {
+	res := make([]*models.ClassifyListV2, 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.ClassifyListV2
+
+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.ClassifyListV2) {
+	sort.Sort(BySortAndCreateTime(classifyList))
+}