Browse Source

Merge branch 'dm' of http://8.136.199.33:3000/eta_gn_server/eta_api into dm

genlong 6 months ago
parent
commit
88d082bb93

+ 13 - 5
controllers/bi_dashboard.go

@@ -871,17 +871,25 @@ func (this *BIDaShboardController) Public() {
 	}
 
 
-	if opening {
-
-
-	}
-
 	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
 	if err != nil && !utils.IsErrNoRow(err) {
 		br.Msg = "获取数据异常!"
 		br.ErrMsg = "获取数据异常,Err:" + err.Error()
 		return
 	}
+
+	if opening {
+		_, err = biapprove.SubmitBiApprove(req.BiDashboardId, item.BiDashboardName, req.ClassifyId, this.SysUser.AdminId, this.SysUser.RealName)
+		if err != nil {
+			br.Msg = "提交审批失败"
+			br.ErrMsg = "提交审批失败, Err: " + err.Error()
+			return
+		}
+		item.State = 4
+	} else {
+		item.State = 6
+	}
+	
 	item.BiDashboardClassifyId = req.ClassifyId
 
 	err = bi_dashboard.EditDashboard(item)

+ 1 - 0
controllers/data_manage/excel/excel_info.go

@@ -1675,6 +1675,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 		SourcesFrom:   excelInfo.SourcesFrom,
 		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
+		ExcelInfoId:   excelInfo.ExcelInfoId,
 	}
 	br.Ret = 200
 	br.Success = true

+ 446 - 0
controllers/knowledge_classify.go

@@ -0,0 +1,446 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/knowledge"
+	knowledgeServ "eta_gn/eta_api/services/knowledge"
+)
+
+// 分类
+type KnowledgeClassifyController struct {
+	BaseAuthController
+}
+
+// @Title 新增分类接口
+// @Description 新增分类
+// @Param	request	body knowledge.ClassifyAddReq true "type json string"
+// @Success 200 新增成功
+// @router /classify/add [post]
+func (this *KnowledgeClassifyController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.ClassifyAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyName == "" {
+		br.Msg = "分类名称不可为空"
+		return
+	}
+
+	// 新增分类
+	err, errMsg, isSentEmail := knowledgeServ.AddKnowledgeClassify(req.ClassifyName, req.ParentId, req.ResourceType)
+	if err != nil {
+		br.Msg = "添加失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "添加失败,Err:" + err.Error()
+		br.IsSendEmail = isSentEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+}
+
+// CheckDeleteClassify
+// @Title 删除分类-检测接口
+// @Description 删除分类-信息检测,是否符合删除条件
+// @Param   ClassifyId   query   int  true       "分类ID"
+// @Success 200 {object} knowledge.CheckDeleteClassifyResp
+// @router /classify/checkDelete [get]
+func (this *KnowledgeClassifyController) CheckDeleteClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, err := this.GetInt("ClassifyId")
+	if classifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	classify, err := knowledgeObj.GetClassifyById(classifyId)
+	if err != nil {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		return
+	}
+	resp := new(knowledge.CheckDeleteClassifyResp)
+	if classify == nil {
+		br.Msg = "分类不存在"
+		resp.Code = 1
+		resp.Msg = "分类不存在"
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		return
+	}
+
+	//判断分类是否关联了报告
+	if classify.ParentId > 0 {
+		count, err := knowledgeObj.GetReportCountByClassifyId(classifyId)
+		if err != nil {
+			br.Msg = "获取信息失败"
+			br.ErrMsg = "获取信息失败,Err:" + err.Error()
+			return
+		}
+		if count > 0 {
+			resp.Code = 2
+			resp.Msg = "该分类有关联报告,不允许删除"
+			br.Data = resp
+			br.Ret = 200
+			br.Msg = "该分类有关联报告,不允许删除"
+			br.Success = true
+			return
+		}
+	} else {
+		subCount, err := knowledgeObj.GetClassifySubCountByClassifyId(classifyId)
+		if err != nil {
+			br.Msg = "获取信息失败"
+			br.ErrMsg = "获取信息失败,Err:" + err.Error()
+			return
+		}
+		if subCount > 0 {
+			resp.Code = 3
+			resp.Msg = "二级分类有关联报告,不允许删除"
+			br.Data = resp
+			br.Ret = 200
+			br.Msg = "二级分类有关联报告,不允许删除"
+			br.Success = true
+			return
+		}
+		subTotal, err := knowledgeObj.GetClassifySubCountByParentId(classifyId)
+		if err != nil {
+			br.Msg = "获取信息失败"
+			br.ErrMsg = "获取信息失败,Err:" + err.Error()
+			return
+		}
+		if subTotal > 0 {
+			resp.Code = 4
+			resp.Msg = "请先删除该分类下关联分类"
+			br.Data = resp
+			br.Ret = 200
+			br.Msg = "请先删除该分类下关联分类"
+			br.Success = true
+			return
+		}
+	}
+
+	resp.Code = 0
+	resp.Msg = "检测完成,可进行删除操作"
+	br.Ret = 200
+	br.Data = resp
+	br.Success = true
+	br.Msg = "检测成功"
+}
+
+// @Title 删除分类接口
+// @Description 删除分类
+// @Param	request	body knowledge.DeleteClassifyReq true "type json string"
+// @Success 200 Ret=200,删除成功
+// @router /classify/delete [post]
+func (this *KnowledgeClassifyController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.DeleteClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	//todo 是否需要删除接口
+	/*br.Msg = "报告分类不允许删除"
+	br.IsSendEmail = false
+	return*/
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	item, err := knowledgeObj.GetClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		return
+	}
+	if item == nil {
+		br.Msg = "分类不存在"
+		return
+	}
+	err = knowledgeObj.Delete(req.ClassifyId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 被删除是二级分类且关联电话会时, 同步FICC活动分类
+	//if item.ParentId > 0 && item.RelateTel == 1 {
+	//	go func() {
+	//		_ = yb.SyncClassifyAndFiccActivityType()
+	//	}()
+	//}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// @Title 修改分类接口
+// @Description 修改分类
+// @Param	request	body knowledge.EditClassifyReq true "type json string"
+// @Success 200 Ret=200,修改成功
+// @router /classify/edit [post]
+func (this *KnowledgeClassifyController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.EditClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "分类名称不可为空"
+		return
+	}
+
+	// 修改分类
+	err, errMsg, isSentEmail := knowledgeServ.EditKnowledgeClassify(req.ClassifyId, req.ParentId, req.ClassifyName)
+	if err != nil {
+		br.Msg = "修改失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		br.IsSendEmail = isSentEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "修改成功"
+}
+
+// ParentClassify
+// @Title 获取父级分类接口
+// @Description 获取父级分类
+// @Success 200 {object} knowledge.Classify
+// @router /classify/parent [get]
+func (this *KnowledgeClassifyController) ParentClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	items, err := knowledgeObj.GetAllClassify()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	classifyIds := make([]int, 0)
+	for i := range items {
+		classifyIds = append(classifyIds, items[i].ClassifyId)
+	}
+	classifyLen := len(classifyIds)
+	if classifyLen == 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	resp := make([]*knowledge.KnowledgeClassifyItem, 0)
+	for i := range items {
+		resp = append(resp, &knowledge.KnowledgeClassifyItem{
+			KnowledgeClassify: *items[i],
+		})
+	}
+
+	resp = knowledgeServ.GetClassifyTreeRecursive(resp, 0)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// @Title 根据id获取分类详情接口
+// @Description 根据id获取分类详情
+// @Param	request	body knowledge.FindByIdClassifyReq true "type json string"
+// @Success 200 {object} knowledge.Classify
+// @router /classify/findById [get]
+func (this *KnowledgeClassifyController) FindByIdClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.FindByIdClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	items, err := knowledgeObj.GetClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	br.Data = items
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// @Title 获取分类列表
+// @Description 获取分类列表
+// @Param   KeyWord   query   string  true       "检索关键词"
+// @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
+// @Param   HideDayWeek   query   int  false       "是否隐藏晨周报"
+// @Success 200 {object} knowledge.Classify
+// @router /classify/list [get]
+func (this *KnowledgeClassifyController) ListClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	keyWord := this.GetString("KeyWord")
+	reqEnabled, _ := this.GetInt("Enabled", -1)
+
+	enabled := -1
+	if reqEnabled == 1 {
+		enabled = reqEnabled
+	}
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	list, err := knowledgeObj.GetClassifyListByKeyword(keyWord, enabled)
+	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.ClassifyId] = true
+			if v.ParentId > 0 {
+				currParentClassifyIdList = append(currParentClassifyIdList, v.ParentId)
+			}
+		}
+
+		findList := list
+		list = make([]*knowledge.KnowledgeClassifyItem, 0)
+
+		tmpList, tmpErr := knowledgeServ.GetParentClassifyListByParentIdList(currParentClassifyIdList)
+		if tmpErr != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取上级分类信息失败,Err:" + tmpErr.Error()
+			return
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; !ok {
+				list = append(list, v)
+			}
+		}
+
+		list = append(list, findList...)
+	}
+
+	classifyIdList := make([]int, 0)
+	for i := range list {
+		classifyIdList = append(classifyIdList, list[i].ClassifyId)
+	}
+	parentIdLen := len(classifyIdList)
+	if parentIdLen == 0 {
+		resp := &knowledge.KnowledgeClassifyListResp{
+			List: list,
+		}
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	// 先将分类列表排序
+	knowledgeServ.SortClassifyListBySortAndCreateTime(list)
+	// 接着转换结构
+	list = knowledgeServ.GetClassifyListTreeRecursive(list, 0)
+
+	resp := new(knowledge.KnowledgeClassifyListResp)
+	resp.List = list
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Move
+// @Title 移动分类接口
+// @Description 移动分类
+// @Param	request	body knowledge.ClassifyMoveReq true "type json string"
+// @Success 200 新增成功
+// @router /classify/move [post]
+func (this *KnowledgeClassifyController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.ClassifyMoveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	e, msg := knowledgeServ.MoveKnowledgeClassify(req)
+	if e != nil {
+		br.Msg = msg
+		br.ErrMsg = "移动分类失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 875 - 0
controllers/knowledge_resource.go

@@ -0,0 +1,875 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/knowledge"
+	knowledgeServ "eta_gn/eta_api/services/knowledge"
+	"eta_gn/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"html"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 分类
+type KnowledgeResourceController struct {
+	BaseAuthController
+}
+
+// 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} knowledge.KnowledgeResourceListResp
+// @router /resource/list [get]
+func (this *KnowledgeResourceController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	classifyIds := this.GetString("ClassifyIds")
+	// 添加人
+	addUserIds, _ := this.GetInt("SysUserIds")
+	// 标签
+	tagIds := this.GetString("TagIds")
+
+	keyWord := this.GetString("Keyword")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (title LIKE ?) `
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
+	}
+	if classifyIds != "" {
+		//转成数组,并把类型转成int
+		classifyIdsArr := strings.Split(classifyIds, ",")
+		var classifyIdsInt []int
+		for _, v := range classifyIdsArr {
+			classifyId, _ := strconv.Atoi(v)
+			classifyIdsInt = append(classifyIdsInt, classifyId)
+		}
+		condition += ` AND classify_id in (?) `
+		pars = append(pars, classifyIdsInt)
+	}
+	if addUserIds > 0 {
+		// 转成数组,并把类型转成int
+		addUserIdsArr := strings.Split(strconv.Itoa(addUserIds), ",")
+		var addUserIdsInt []int
+		for _, v := range addUserIdsArr {
+			addUserId, _ := strconv.Atoi(v)
+			addUserIdsInt = append(addUserIdsInt, addUserId)
+		}
+		condition += ` AND admin_id in (?) `
+		pars = append(pars, addUserIdsInt)
+	}
+	if tagIds != "" {
+		//转成数组,并把类型转成int
+		tagIdsArr := strings.Split(tagIds, ",")
+		var tagIdsInt []int
+		for _, v := range tagIdsArr {
+			tagId, _ := strconv.Atoi(v)
+			tagIdsInt = append(tagIdsInt, tagId)
+		}
+		condition += ` AND tag_id in (?) `
+		pars = append(pars, tagIdsInt)
+	}
+
+	var err error
+	var total int
+	var list []*knowledge.KnowledgeResourceList
+	// 共享事件需要连表查询,所以需要单独写
+	total, err = knowledge.GetKnowledgeResourceListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	listTmp, err := knowledge.GetKnowledgeResourceList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	// 整理分类ID
+	classifyFullNameMap := make(map[int]string)
+	classifyIdsArr := make([]int, 0)
+	for _, v := range listTmp {
+		classifyIdsArr = append(classifyIdsArr, v.ClassifyId)
+	}
+	// 获取分类列表
+	classifyList, err := knowledge.GetFullClassifyListByIdList(classifyIdsArr)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range classifyList {
+		name := v.ClassifyName
+		if v.ParentName != "" {
+			name = v.ParentName + "/" + name
+		}
+		if v.RootName != "" {
+			name = v.RootName + "/" + name
+		}
+		classifyFullNameMap[v.ClassifyId] = name
+	}
+
+	// 整理标签ID
+	tagNameMap := make(map[int]string)
+	tagIdsArr := make([]int, 0)
+	for _, v := range listTmp {
+		tagIdsArr = append(tagIdsArr, v.TagId)
+	}
+	// 获取标签列表
+	tagObj := new(knowledge.KnowledgeTag)
+	tagList, err := tagObj.GetListByIdList(tagIdsArr)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+	}
+	for _, v := range tagList {
+		tagNameMap[v.TagId] = v.TagName
+	}
+
+	for _, v := range listTmp {
+		var startTime, endTime string
+		if v.StartTime != nil && !v.StartTime.IsZero() {
+			startTime = v.StartTime.In(time.Local).Format(utils.FormatDateTime)
+		}
+		if v.EndTime != nil && !v.EndTime.IsZero() {
+			endTime = v.EndTime.In(time.Local).Format(utils.FormatDateTime)
+		}
+		modifyTime := v.ModifyTime.In(time.Local).Format(utils.FormatDateTime)
+		createTime := v.CreateTime.In(time.Local).Format(utils.FormatDateTime)
+		tmp := &knowledge.KnowledgeResourceList{
+			KnowledgeResourceId: v.KnowledgeResourceId,
+			ResourceType:        v.ResourceType,
+			ClassifyId:          v.ClassifyId,
+			Title:               v.Title,
+			CreateTime:          createTime,
+			ModifyTime:          modifyTime,
+			State:               v.State,
+			ResourceCode:        v.ResourceCode,
+			AdminId:             v.AdminId,
+			AdminRealName:       v.AdminRealName,
+			SourceFrom:          v.SourceFrom,
+			TagId:               v.TagId,
+			StartTime:           startTime,
+			EndTime:             endTime,
+		}
+
+		// todo 编辑状态
+		/*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
+		}*/
+		classifyName, ok := classifyFullNameMap[tmp.ClassifyId]
+		if ok {
+			tmp.ClassifyFullName = classifyName
+		}
+		tagName, ok := tagNameMap[v.TagId]
+		if ok {
+			tmp.TagName = tagName
+		}
+		list = append(list, tmp)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(knowledge.KnowledgeResourceListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Add
+// @Title 新增事件接口
+// @Description 新增事件(区分事件类型)
+// @Param	request	body knowledge.AddReq true "type json string"
+// @Success 200 {object} knowledge.AddResp
+// @router /resource/add [post]
+func (this *KnowledgeResourceController) 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 knowledge.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.ClassifyId <= 0 {
+		br.Msg = "分类必填"
+		br.ErrMsg = "分类必填"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ResourceType == knowledge.KnowledgeResourceTypeEvent || req.ResourceType == knowledge.KnowledgeResourceTypePolicy {
+		if req.StartTime == "" {
+			br.Msg = "开始时间必填"
+			br.ErrMsg = "开始时间必填"
+			return
+		}
+	}
+	item, err, errMsg := knowledgeServ.AddResource(&req, sysUser)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(knowledge.AddResp)
+	resp.KnowledgeResourceId = item.KnowledgeResourceId
+	resp.ResourceCode = item.ResourceCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// Edit
+// @Title 编辑事件基础信息接口
+// @Description 编辑事件基础信息(不区分事件类型)
+// @Param	request	body knowledge.EditReq true "type json string"
+// @Success 200 {object} knowledge.EditResp
+// @router /resource/edit [post]
+func (this *KnowledgeResourceController) 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 knowledge.EditReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.KnowledgeResourceId <= 0 {
+		br.Msg = "请选择事件"
+		br.ErrMsg = "请选择事件"
+		return
+	}
+	if req.Title == `` {
+		br.Msg = "标题不能为空"
+		br.ErrMsg = "标题不能为空"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "分类必填"
+		br.ErrMsg = "分类必填"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.ResourceType == knowledge.KnowledgeResourceTypeEvent || req.ResourceType == knowledge.KnowledgeResourceTypePolicy {
+		if req.StartTime == "" {
+			br.Msg = "开始时间必填"
+			br.ErrMsg = "开始时间必填"
+			return
+		}
+	}
+
+	// todo 更新标记key
+	/*markStatus, err := services.UpdateReportEditMark(int(req.KnowledgeResourceId), 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
+	}*/
+	resourceObj := new(knowledge.KnowledgeResource)
+	resourceInfo, e := resourceObj.GetById(int(req.KnowledgeResourceId))
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "事件已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取事件失败, Err: " + e.Error()
+		return
+	}
+
+	// 编辑事件信息
+	err, errMsg := knowledgeServ.EditResource(resourceInfo, req, sysUser)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(knowledge.EditResp)
+	resp.KnowledgeResourceId = req.KnowledgeResourceId
+	resp.ResourceCode = resourceInfo.ResourceCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// Detail
+// @Title 获取事件详情接口
+// @Description 获取事件详情
+// @Param	request	body knowledge.KnowledgeResourceDetailReq true "type json string"
+// @Success 200 {object} knowledge.KnowledgeResource
+// @router /resource/detail [get]
+func (this *KnowledgeResourceController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	/*var req knowledge.KnowledgeResourceDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.KnowledgeResourceId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}*/
+	knowledgeResourceId, err := this.GetInt("KnowledgeResourceId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if knowledgeResourceId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	resourceObj := new(knowledge.KnowledgeResource)
+	item, err := resourceObj.GetById(knowledgeResourceId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			br.Msg = "事件已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	item.Content = html.UnescapeString(item.Content)
+
+	resp := &knowledge.KnowledgeResourceDetailView{
+		KnowledgeResource: item,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// SaveReportContent
+// @Title 保存草稿
+// @Description 保存草稿
+// @Param	request	body knowledge.SaveReportContent true "type json string"
+// @Success 200 {object} knowledge.KnowledgeResourceAuthorResp
+// @router /resource/saveContent [post]
+func (this *KnowledgeResourceController) 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 knowledge.SaveReportContent
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	knowledgeResourceId := req.KnowledgeResourceId
+	//noChangeFlag := req.NoChange
+
+	if knowledgeResourceId <= 0 {
+		resp := new(knowledge.SaveReportContentResp)
+		resp.KnowledgeResourceId = knowledgeResourceId
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "保存成功"
+		br.Data = resp
+		return
+	}
+
+	// 获取事件详情
+	/*resourceInfo, _ := knowledge.GetReportByknowledgeResourceId(req.KnowledgeResourceId)
+	if resourceInfo != nil && resourceInfo.State == 2 {
+		br.Msg = "该事件已发布,不允许编辑"
+		br.ErrMsg = "该事件已发布,不允许编辑"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 标记更新中
+	{
+		markStatus, err := services.UpdateReportEditMark(req.KnowledgeResourceId, 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)
+			}
+			resourceInfo.Content = html.EscapeString(content)
+			resourceInfo.ContentSub = html.EscapeString(contentSub)
+			resourceInfo.ContentStruct = html.EscapeString(req.ContentStruct)
+			resourceInfo.HeadImg = req.HeadImg
+			resourceInfo.EndImg = req.EndImg
+			resourceInfo.CanvasColor = req.CanvasColor
+			resourceInfo.HeadResourceId = req.HeadResourceId
+			resourceInfo.EndResourceId = req.EndResourceId
+			resourceInfo.ModifyTime = time.Now()
+			resourceInfo.ContentModifyTime = time.Now()
+			updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
+			err = resourceInfo.UpdateReport(updateCols)
+			if err != nil {
+				br.Msg = "保存失败"
+				br.ErrMsg = "保存失败,Err:" + err.Error()
+				return
+			}
+			go knowledge.AddReportSaveLog(knowledgeResourceId, this.SysUser.AdminId, resourceInfo.Content, resourceInfo.ContentSub, resourceInfo.ContentStruct, resourceInfo.CanvasColor, this.SysUser.AdminName, resourceInfo.HeadResourceId, resourceInfo.EndResourceId)
+		}
+	}
+	*/
+	resp := new(knowledge.SaveReportContentResp)
+	resp.KnowledgeResourceId = knowledgeResourceId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// BaseDetail
+// @Title 获取事件基础信息详情接口
+// @Description 获取事件基础信息详情接口
+// @Param	request	body knowledge.KnowledgeResourceDetailReq true "type json string"
+// @Success 200 {object} knowledge.KnowledgeResource
+// @router /resource/detail/base [get]
+func (this *KnowledgeResourceController) BaseDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	/*var req knowledge.KnowledgeResourceDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.KnowledgeResourceId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}*/
+	/*knowledgeResourceId, err := this.GetInt("KnowledgeResourceId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if knowledgeResourceId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	resourceInfo, err := knowledge.GetReportById(knowledgeResourceId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			br.Msg = "事件已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	// 基础信息就不获取章节信息了
+	chapterList := make([]*knowledge.KnowledgeResourceChapter, 0)
+
+	resourceInfo.Content = html.UnescapeString(resourceInfo.Content)
+	resourceInfo.ContentSub = html.UnescapeString(resourceInfo.ContentSub)
+
+	grandAdminList := make([]knowledge.KnowledgeResourceDetailViewAdmin, 0)
+	permissionList := make([]knowledge.KnowledgeResourceDetailViewPermission, 0)
+
+	// 处理事件授权用户列表
+	{
+		obj := report.ReportGrant{}
+		grantList, tmpErr := obj.GetGrantListById(knowledgeResourceId)
+		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, knowledge.KnowledgeResourceDetailViewAdmin{
+					AdminId:   v.AdminId,
+					AdminName: v.RealName,
+				})
+			}
+		}
+
+	}
+
+	// 处理章节id关联品种id列表
+	{
+		minClassifyId := resourceInfo.ClassifyIdThird
+		if minClassifyId <= 0 {
+			minClassifyId = resourceInfo.ClassifyIdSecond
+		}
+		if minClassifyId <= 0 {
+			minClassifyId = resourceInfo.ClassifyIdFirst
+		}
+		if minClassifyId <= 0 {
+			br.Msg = "分类异常"
+			br.ErrMsg = "分类异常"
+			return
+		}
+
+		// 获取分类关联的品种id
+		classifyPermissionList, tmpErr := knowledge.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 := knowledge.GetChartPermissionByIdList(permissionIdList)
+			if tmpErr != nil {
+				br.Msg = "获取章节id授权用户列表失败"
+				br.ErrMsg = "获取章节id授权用户列表失败, Err: " + tmpErr.Error()
+				return
+			}
+			for _, v := range adminList {
+				permissionList = append(permissionList, knowledge.KnowledgeResourceDetailViewPermission{
+					PermissionId:   v.ChartPermissionId,
+					PermissionName: v.PermissionName,
+				})
+			}
+		}
+
+	}
+
+	resp := &knowledge.KnowledgeResourceDetailView{
+		ReportDetail:   resourceInfo,
+		ChapterList:    chapterList,
+		GrandAdminList: grandAdminList,
+		PermissionList: permissionList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp*/
+}
+
+// Delete
+// @Title 删除事件接口
+// @Description 删除事件
+// @Param	request	body knowledge.DeleteReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /resource/delete [post]
+func (this *KnowledgeResourceController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.DeleteReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.KnowledgeResourceId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,事件id不可为空"
+		return
+	}
+	resourceObj := new(knowledge.KnowledgeResource)
+	if err = resourceObj.Delete(req.KnowledgeResourceId); err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除事件失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// ImportData
+// @Title Excel导入事件
+// @Description Excel导入事件
+// @Param   EntryFile   query   file  true       "文件"
+// @Success 200 Ret=200 录入成功
+// @router /resource/import_add [post]
+func (c *KnowledgeResourceController) ImportData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	resourceType, _ := c.GetInt("ResourceType")
+	file, _, err := c.GetFile("EntryFile")
+	if err != nil {
+		br.Msg = "获取文件失败"
+		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+		return
+	}
+	path := "./static/知识资源导入_" + strconv.Itoa(resourceType) + "_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	defer file.Close()
+	err = c.SaveToFile("EntryFile", path)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	if utils.RunMode == "debug" {
+		defer os.Remove(path)
+	}
+
+	successCount, failCount, err, errMsg := knowledgeServ.ImportResourceData(path, resourceType, sysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	resp := models.EdbdataImportResp{
+		SuccessCount: successCount,
+		FailCount:    failCount,
+	}
+	if failCount > 0 {
+		if successCount == 0 {
+			resp.Status = -1
+			resp.Msg = "导入失败"
+		} else {
+			resp.Status = 1
+			resp.Msg = "存在部分导入失败"
+		}
+	} else {
+		resp.Status = 0
+	}
+	br.Msg = "导入成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+}
+
+// ImportFailListDownload
+// @Title 下载失败列表
+// @Description 下载失败列表
+// @Success 200 {object} models.EdbdataClassifyResp
+// @router /resource/download_fail [get]
+func (this *KnowledgeResourceController) ImportFailListDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	resourceType, _ := this.GetInt("ResourceType")
+	failObj := new(knowledge.KnowledgeImportFail)
+	item, err := failObj.GetListBySysUserId(sysUser.AdminId, resourceType)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.Msg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	xlsxFile := xlsx.NewFile()
+	SheetName := "导入失败数据"
+	if this.Lang == utils.EnLangVersion {
+		SheetName = "Import Fail List"
+	}
+	sheet, err := xlsxFile.AddSheet(SheetName)
+	headRow := sheet.AddRow()
+	headRow.AddCell().SetValue("开始日期")
+	headRow.AddCell().SetValue("开始时间")
+	headRow.AddCell().SetValue("结束日期")
+	headRow.AddCell().SetValue("结束时间")
+	headRow.AddCell().SetValue("标题")
+	headRow.AddCell().SetValue("正文")
+	headRow.AddCell().SetValue("来源")
+	headRow.AddCell().SetValue("一级分类")
+	headRow.AddCell().SetValue("二级分类")
+	headRow.AddCell().SetValue("三级分类")
+	headRow.AddCell().SetValue("标签")
+	headRow.AddCell().SetValue("备注")
+
+	for _, v := range item {
+		row := sheet.AddRow()
+		row.AddCell().SetValue(v.StartDate)
+		row.AddCell().SetValue(v.StartTime)
+		row.AddCell().SetValue(v.EndDate)
+		row.AddCell().SetValue(v.EndTime)
+		row.AddCell().SetValue(v.Title)
+		row.AddCell().SetValue(v.Content)
+		row.AddCell().SetValue(v.SourceFrom)
+		row.AddCell().SetValue(v.ClassifyFirst)
+		row.AddCell().SetValue(v.ClassifySecond)
+		row.AddCell().SetValue(v.ClassifyThird)
+		row.AddCell().SetValue(v.Tag)
+		row.AddCell().SetValue(v.Remark)
+	}
+	fileName := time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	savePath := "./static/" + fileName
+	err = xlsxFile.Save(savePath)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	defer func() {
+		os.Remove(savePath)
+	}()
+	finalFileName := "失败列表.xlsx"
+
+	if this.Lang == utils.EnLangVersion {
+		finalFileName = "Failure List.xlsx"
+	}
+	this.Ctx.Output.Download(savePath, finalFileName)
+}

+ 220 - 0
controllers/knowledge_tag.go

@@ -0,0 +1,220 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/knowledge"
+	knowledgeServ "eta_gn/eta_api/services/knowledge"
+)
+
+// 标签
+type KnowledgeTagController struct {
+	BaseAuthController
+}
+
+// @Title 新增标签接口
+// @Description 新增标签
+// @Param	request	body knowledge.TagAddReq true "type json string"
+// @Success 200 新增成功
+// @router /tag/add [post]
+func (this *KnowledgeTagController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.TagAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.TagName == "" {
+		br.Msg = "标签名称不可为空"
+		return
+	}
+
+	// 新增标签
+	err, errMsg, isSentEmail := knowledgeServ.AddKnowledgeTag(req.TagName, req.ResourceType)
+	if err != nil {
+		br.Msg = "添加失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "添加失败,Err:" + err.Error()
+		br.IsSendEmail = isSentEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+}
+
+// @Title 删除标签接口
+// @Description 删除标签
+// @Param	request	body knowledge.DeleteTagReq true "type json string"
+// @Success 200 Ret=200,删除成功
+// @router /tag/delete [post]
+func (this *KnowledgeTagController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.DeleteTagReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.TagId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	//todo 是否需要删除接口
+	/*br.Msg = "报告标签不允许删除"
+	br.IsSendEmail = false
+	return*/
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	item, err := knowledgeObj.GetTagById(req.TagId)
+	if err != nil {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		return
+	}
+	if item == nil {
+		br.Msg = "标签不存在"
+		return
+	}
+	err = knowledgeObj.Delete(req.TagId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 被删除是二级标签且关联电话会时, 同步FICC活动标签
+	//if item.ParentId > 0 && item.RelateTel == 1 {
+	//	go func() {
+	//		_ = yb.SyncTagAndFiccActivityType()
+	//	}()
+	//}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// @Title 修改标签接口
+// @Description 修改标签
+// @Param	request	body knowledge.EditTagReq true "type json string"
+// @Success 200 Ret=200,修改成功
+// @router /tag/edit [post]
+func (this *KnowledgeTagController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.EditTagReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.TagId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	if req.TagName == "" {
+		br.Msg = "标签名称不可为空"
+		return
+	}
+
+	// 修改标签
+	err, errMsg, isSentEmail := knowledgeServ.EditKnowledgeTag(req.TagId, req.ParentId, req.TagName)
+	if err != nil {
+		br.Msg = "修改失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		br.IsSendEmail = isSentEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "修改成功"
+}
+
+// @Title 获取标签列表
+// @Description 获取标签列表
+// @Param   KeyWord   query   string  true       "检索关键词"
+// @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
+// @Param   HideDayWeek   query   int  false       "是否隐藏晨周报"
+// @Success 200 {object} knowledge.Tag
+// @router /tag/list [get]
+func (this *KnowledgeTagController) ListTag() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	keyWord := this.GetString("KeyWord")
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	list, err := knowledgeObj.GetTagListByKeyword(keyWord)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(knowledge.KnowledgeTagListResp)
+	resp.List = list
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Move
+// @Title 移动标签接口
+// @Description 移动标签
+// @Param	request	body knowledge.TagMoveReq true "type json string"
+// @Success 200 新增成功
+// @router /tag/move [post]
+func (this *KnowledgeTagController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req knowledge.TagMoveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.TagId <= 0 {
+		br.Msg = "请选择标签"
+		return
+	}
+	e, msg := knowledgeServ.MoveKnowledgeTag(req)
+	if e != nil {
+		br.Msg = msg
+		br.ErrMsg = "移动标签失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 1 - 1
models/bi_dashboard/bi_dashboard.go

@@ -14,7 +14,7 @@ type BiDashboard struct {
 	Sort                  int       `gorm:"column:sort" `                                    // 排序字段
 	CreateTime            time.Time `gorm:"column:create_time" `                             // 创建时间
 	ModifyTime            time.Time `gorm:"column:modify_time"`                              // 更新时间
-	State                 int       `gorm:"column:state"`                                    // 状态
+	State                 int       `gorm:"column:state"`                                    // 状态 1:未公开; 4-待审批;5-已驳回;6-已通过
 }
 
 // tableName

+ 8 - 0
models/bi_dashboard/bi_dashboard_classify.go

@@ -109,4 +109,12 @@ func GetBiDashboardClassifyMaxSort() (sort int, err error) {
 // del
 func DelBiDashboardClassify(id int) (err error) {
 	return global.DEFAULT_DmSQL.Where("bi_dashboard_classify_id = ?", id).Delete(&BiDashboardClassify{}).Error
+}
+
+type RespPublicGroupListItem struct {
+	GroupId       int64  `description:"目录id"`
+	GroupName     string `description:"目录名称"`
+	AdminId       int    `description:"目录创建者账号ID"`
+	DashboardList []*BiDashboard
+	Children      []*RespPublicGroupListItem
 }

+ 1 - 0
models/data_manage/excel/response/excel_info.go

@@ -31,6 +31,7 @@ type ExcelTableDetailResp struct {
 	SourcesFrom   string `description:"图表来源"`
 	ExcelSource   string `description:"表格来源str"`
 	ExcelSourceEn string `description:"表格来源(英文)"`
+	ExcelInfoId   int    `description:"表id"`
 }
 
 // ExcelTableDetailConfigResp

+ 329 - 0
models/knowledge/knowledge_classify.go

@@ -0,0 +1,329 @@
+package knowledge
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type KnowledgeClassify struct {
+	ClassifyId   int       `gorm:"primaryKey;column:classify_id"`
+	ClassifyName string    `gorm:"column:classify_name;default:'';type:varchar(125);not null"` // 注意:varchar的默认长度可能需要根据实际情况调整
+	Sort         int       `gorm:"column:sort;default:0;type:tinyint"`
+	ParentId     int       `gorm:"column:parent_id;default:0;type:int"`
+	CreateTime   time.Time `gorm:"column:create_time;default:CURRENT_TIMESTAMP"`
+	ModifyTime   time.Time `gorm:"column:modify_time;default:CURRENT_TIMESTAMP"`
+	Enabled      int       `gorm:"column:enabled;default:1;type:tinyint"`
+	Level        int       `gorm:"column:level;default:0;type:bigint"`
+	ResourceType int       `gorm:"column:resource_type;default:0;not null;type:tinyint"`
+	HasChild     int       `gorm:"column:has_child;default:0;type:int"`
+}
+
+func (k *KnowledgeClassify) TableName() string {
+	return "knowledge_classify"
+}
+
+func (k *KnowledgeClassify) GetPrimaryKey() string {
+	return "classify_id"
+}
+
+func (k *KnowledgeClassify) GetClassifyByName(classifyName string, parentId, resourceType int) (item *KnowledgeClassify, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE classify_name=? AND parent_id=? AND resource_type=?`, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, classifyName, parentId, resourceType).First(&item).Error
+	return
+}
+
+func (k *KnowledgeClassify) GetClassifyById(classifyId int) (item *KnowledgeClassify, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s=? `, k.TableName(), k.GetPrimaryKey())
+	err = global.DmSQL["rddp"].Raw(sql, classifyId).First(&item).Error
+	return
+}
+
+// 添加分类
+func (k *KnowledgeClassify) Add(item *KnowledgeClassify) (err error) {
+	err = global.DmSQL["rddp"].Create(item).Error
+	return
+}
+
+func (k *KnowledgeClassify) GetReportCountByClassifyId(classifyId int) (count int, err error) {
+	sql := `SELECT COUNT(1) as num FROM knowledge_resource WHERE classify_id=? `
+	err = global.DmSQL["rddp"].Raw(sql, classifyId).First(&count).Error
+	return
+}
+
+func (k *KnowledgeClassify) GetClassifySubCountByClassifyId(classifyId int) (count int, err error) {
+
+	sql := `SELECT COUNT(1) as num FROM knowledge_classify AS a
+        INNER JOIN knowledge_resource AS b ON a.classify_id=b.classify_id
+        WHERE a.parent_id=? `
+
+	err = global.DmSQL["rddp"].Raw(sql, classifyId).First(&count).Error
+	return
+}
+
+func (k *KnowledgeClassify) GetClassifySubCountByParentId(classifyId int) (count int, err error) {
+	sqlCount := `
+	SELECT COUNT(1) as num FROM %s AS a
+	WHERE a.parent_id=? `
+	sqlCount = fmt.Sprintf(sqlCount, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sqlCount, classifyId).Scan(&count).Error
+	return
+}
+
+// 删除分类
+func (k *KnowledgeClassify) Delete(classifyId int) (err error) {
+	sql := `DELETE FROM %s WHERE classify_id =? `
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Exec(sql, classifyId).Error
+	if err != nil {
+		return
+	}
+	return
+}
+
+type KnowledgeClassifyItem struct {
+	KnowledgeClassify
+	Child []*KnowledgeClassifyItem `gorm:"-"`
+}
+
+type KnowledgeClassifyListResp struct {
+	List []*KnowledgeClassifyItem
+}
+
+type ClassifyPermissionListResp struct {
+	List   []*KnowledgeClassifyItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type CheckDeleteClassifyReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+type CheckDeleteClassifyResp struct {
+	Code int    `description:"编码:0:检测成功,可进行删除,1:分类不存在,2:该分类有关联报告,不允许删除,3:二级分类有关联报告,不允许删除,4:该分类下有关联分类,是否确认全部删除"`
+	Msg  string `description:"描述信息"`
+}
+
+type ClassifyAddReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级分类id,没有父级分类传0"`
+	ResourceType int    `description:"分类类型:0事件库,1政策库,2观点库,3知识库"`
+}
+
+type DeleteClassifyReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+type EditClassifyReq struct {
+	ClassifyId int `description:"分类ID"`
+	ClassifyAddReq
+}
+
+type FindByIdClassifyReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+// EditClassifyPermissionReq 编辑分类权限请求
+type EditClassifyPermissionReq struct {
+	ClassifyId            int   `description:"分类ID"`
+	ChartPermissionIdList []int `description:"权限id数组"`
+}
+
+// GetAllClassify 获取所有分类
+func (k *KnowledgeClassify) GetAllClassify() (list []*KnowledgeClassify, err error) {
+	sql := ` SELECT * FROM %s `
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql).Find(&list).Error
+	return
+}
+
+// GetClassifyByKeyword 名称获取分类
+func (k *KnowledgeClassify) GetClassifyByKeyword(keyword string) (item *KnowledgeClassify, err error) {
+	sql := ` SELECT * FROM %s WHERE classify_name = ? LIMIT 1 `
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, keyword).Find(&item).Error
+	return
+}
+
+// UpdateClassify 更新分类
+func (k *KnowledgeClassify) Update(cols []string) (err error) {
+	err = global.DmSQL["rddp"].Select(cols).Updates(k).Error
+	return
+}
+
+// SimpleClassifyList 简版分类列表
+type SimpleClassifyList struct {
+	Id           int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID"`
+	Sort         int    `description:"排序"`
+	Child        []*SimpleClassifyList
+}
+
+// GetClassifyByCondition 获取分类列表
+func (k *KnowledgeClassify) GetClassifyByCondition(condition, orderRule string, pars []interface{}) (items []*SimpleClassifyList, err error) {
+
+	sql := `SELECT * FROM %s WHERE 1 = 1 `
+	if condition != `` {
+		sql += condition
+	}
+	order := `sort ASC, create_time ASC`
+	if orderRule != `` {
+		order = orderRule
+	}
+	sql += ` ORDER BY ` + order
+	sql = fmt.Sprintf(sql, k.TableName())
+
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// RelateTelSecClassifyWithPermissions 关联了电话会的二级分类及权限
+type RelateTelSecClassifyWithPermissions struct {
+	Id                 int    `description:"分类ID"`
+	ClassifyName       string `description:"分类名称"`
+	ChartPermissionIds string `description:"权限IDs"`
+}
+
+// UpdateClassifySortByParentId 根据父类id更新排序
+func (k *KnowledgeClassify) UpdateClassifySortByParentId(parentId, permissionId, nowSort int, updateSort string, resourceType int) (err error) {
+
+	sql := ` update %s set sort = ` + updateSort + ` WHERE parent_id=? AND resource_type=? AND sort > ? `
+	if permissionId > 0 {
+		sql += ` or ( classify_id > ` + fmt.Sprint(permissionId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Exec(sql, parentId, resourceType, nowSort).Error
+	return
+}
+
+// GetMaxSortByParentId 获取最大的排序值
+func (k *KnowledgeClassify) GetMaxSortByParentId(parentId int) (maxSort int, err error) {
+	sql := `SELECT COALESCE(MAX(sort),0) AS sort FROM %s WHERE parent_id = ? `
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentId).Scan(&maxSort).Error
+	return
+}
+
+// GetMaxSort 获取最大的排序值
+func (k *KnowledgeClassify) GetMaxSort() (maxSort int, err error) {
+	sql := `SELECT COALESCE(MAX(sort),0) AS sort FROM %s`
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql).Scan(&maxSort).Error
+	return
+}
+
+// GetFirstClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func (k *KnowledgeClassify) GetFirstClassifyByParentId(parentId int) (item *KnowledgeClassify, err error) {
+	sql := `SELECT * FROM %s WHERE parent_id = ? order by sort asc, classify_id asc limit 1`
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentId).First(&item).Error
+	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禁用"`
+}
+
+// GetCountClassifyChildByParentId
+// @Description: 获取父级分类下子分类数量
+// @author: Roc
+// @datetime 2024-06-17 10:58:46
+// @param parentId int
+// @return total int
+// @return err error
+func (k *KnowledgeClassify) GetCountClassifyChildByParentId(parentId int) (total int, err error) {
+
+	sql := fmt.Sprintf(`SELECT count(1) AS total FROM %s WHERE parent_id = ? `, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentId).Scan(&total).Error
+	return
+}
+
+// GetClassifyListByKeyword
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*KnowledgeClassifyList
+// @return err error
+func (k *KnowledgeClassify) GetClassifyListByKeyword(keyWord string, enabled int) (items []*KnowledgeClassifyItem, err error) {
+	sql := ``
+	pars := make([]interface{}, 0)
+
+	sql = `SELECT * FROM %s 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`
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// GetClassifyListByParentIdList
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*KnowledgeClassifyList
+// @return err error
+func (k *KnowledgeClassify) GetClassifyListByParentIdList(parentClassifyIdList []int) (items []*KnowledgeClassifyItem, err error) {
+	num := len(parentClassifyIdList)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT * FROM %s WHERE classify_id in (?) ORDER BY sort ASC, create_time ASC`
+
+	sql = fmt.Sprintf(sql, k.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentClassifyIdList).Find(&items).Error
+	return
+}
+
+type KnowledgeFullClassify struct {
+	KnowledgeClassify
+	ParentName string `gorm:"column:parent_name"`
+	RootName   string `gorm:"column:root_name"`
+}
+
+func GetFullClassifyListByIdList(classifyIdList []int) (items []*KnowledgeFullClassify, err error) {
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT kc1.*, kc2.classify_name AS parent_name, kc3.classify_name AS root_name  
+FROM knowledge_classify kc1  
+LEFT JOIN knowledge_classify kc2 ON kc1.parent_id = kc2.classify_id
+LEFT JOIN knowledge_classify kc3 ON kc2.parent_id = kc3.classify_id WHERE kc1.classify_id IN (?) `
+	err = global.DmSQL["rddp"].Raw(sql, classifyIdList).Find(&items).Error
+	return
+}
+
+func GetFullClassifyListByName(classifyNames []string) (items []*KnowledgeFullClassify, err error) {
+	num := len(classifyNames)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT kc1.*, kc2.classify_name AS parent_name, kc3.classify_name AS root_name  
+FROM knowledge_classify kc1  
+LEFT JOIN knowledge_classify kc2 ON kc1.parent_id = kc2.classify_id
+LEFT JOIN knowledge_classify kc3 ON kc2.parent_id = kc3.classify_id WHERE kc1.classify_name in (?)`
+	err = global.DmSQL["rddp"].Raw(sql, classifyNames).Find(&items).Error
+	return
+}

+ 52 - 0
models/knowledge/knowledge_import_fail.go

@@ -0,0 +1,52 @@
+package knowledge
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type KnowledgeImportFail struct {
+	Id             int       `gorm:"column:id;primaryKey;autoIncrement" description:"主键ID"`
+	ResourceType   int       `gorm:"column:resource_type;"`
+	Title          string    `gorm:"column:title;"`
+	Content        string    `gorm:"column:content"`
+	SourceFrom     string    `gorm:"column:source_from"`
+	Tag            int       `gorm:"column:tag"`
+	ClassifyFirst  string    `gorm:"column:classify_first;"`
+	ClassifySecond string    `gorm:"column:classify_second;"`
+	ClassifyThird  string    `gorm:"column:classify_third;"`
+	StartTime      string    `gorm:"column:start_time"`
+	EndTime        string    `gorm:"column:end_time"`
+	StartDate      string    `gorm:"column:start_date"`
+	EndDate        string    `gorm:"column:end_date"`
+	Remark         string    `gorm:"column:remark" description:"备注"`
+	SysUserId      string    `gorm:"column:sys_user_id" description:"系统用户ID"`
+	CreateTime     time.Time `gorm:"column:create_time" description:"创建时间"`
+}
+
+func (m *KnowledgeImportFail) TableName() string {
+	return "knowledge_import_fail"
+}
+
+func (m *KnowledgeImportFail) Add(item *KnowledgeImportFail) (err error) {
+	err = global.DmSQL["rddp"].Create(item).Error
+	return
+}
+
+func (m *KnowledgeImportFail) MultiAdd(items []*KnowledgeImportFail) (err error) {
+	err = global.DmSQL["rddp"].CreateInBatches(items, utils.MultiAddNum).Error
+	return err
+}
+
+func (m *KnowledgeImportFail) Delete(userId int) (err error) {
+	sql := fmt.Sprintf(`delete from %s where sys_user_id=?`, m.TableName())
+	err = global.DmSQL["rddp"].Exec(sql, userId).Error
+	return err
+}
+func (m *KnowledgeImportFail) GetListBySysUserId(sysUserId, resourceType int) (items []*KnowledgeImportFail, err error) {
+	sql := fmt.Sprintf(` SELECT * FROM %s WHERE sys_user_id=? `, m.TableName())
+	err = global.DmSQL["edb"].Raw(sql, sysUserId, resourceType).Find(&items).Error
+	return
+}

+ 400 - 0
models/knowledge/knowledge_resource.go

@@ -0,0 +1,400 @@
+package knowledge
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"gorm.io/gorm"
+	"strings"
+	"time"
+)
+
+const (
+	// 事件类型-事件库
+	KnowledgeResourceTypeEvent = 0
+	// 事件-政策库
+	KnowledgeResourceTypePolicy = 1
+	// 事件-观点库
+	KnowledgeResourceTypeOpinion = 2
+	// 事件类型-知识库
+	KnowledgeResourceTypeKnow = 3
+)
+
+type KnowledgeResource struct {
+	KnowledgeResourceId int        `gorm:"column:knowledge_resource_id;;primaryKey;autoIncrement"`
+	ResourceType        int        `gorm:"column:resource_type;"`
+	ClassifyId          int        `gorm:"column:classify_id"`
+	Title               string     `gorm:"column:title;"`
+	CreateTime          time.Time  `gorm:"column:create_time" description:"创建时间"`
+	ModifyTime          time.Time  `gorm:"column:modify_time;autoUpdateTime" description:"修改时间"`
+	State               int        `gorm:"column:state" description:"0:未发布;1:已发布;"`
+	Content             string     `gorm:"column:content"`
+	ResourceCode        string     `gorm:"column:resource_code"`
+	AdminId             int        `gorm:"column:admin_id" description:"创建者账号"`
+	AdminRealName       string     `gorm:"column:admin_real_name" description:"创建者姓名"`
+	SourceFrom          string     `gorm:"column:source_from"`
+	TagId               int        `gorm:"column:tag_id;default:0;NOT NULL"`
+	StartTime           *time.Time `gorm:"column:start_time"`
+	EndTime             *time.Time `gorm:"column:end_time"`
+	IsFile              int        `gorm:"column:is_file;default:0;NOT NULL"`
+	FileUrl             string     `gorm:"column:file_url"`
+}
+
+func (m *KnowledgeResource) TableName() string {
+	return "knowledge_resource"
+}
+
+// 查询列表
+func (m *KnowledgeResource) QueryList(condition string, pars []interface{}) (items []*KnowledgeResource, err error) {
+	return
+}
+
+type KnowledgeResourceList struct {
+	KnowledgeResourceId int    `gorm:"column:knowledge_resource_id;;primaryKey;autoIncrement"`
+	ResourceType        int    `gorm:"column:resource_type;"`
+	ClassifyId          int    `gorm:"column:classify_id"`
+	Title               string `gorm:"column:title;"`
+	CreateTime          string `gorm:"column:create_time" description:"创建时间"`
+	ModifyTime          string `gorm:"column:modify_time;autoUpdateTime" description:"修改时间"`
+	State               int    `gorm:"column:state" description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	ResourceCode        string `gorm:"column:resource_code"`
+	AdminId             int    `gorm:"column:admin_id" description:"创建者账号"`
+	AdminRealName       string `gorm:"column:admin_real_name" description:"创建者姓名"`
+	SourceFrom          string `gorm:"column:source_from"`
+	TagId               int    `gorm:"column:tag_id;default:0;NOT NULL"`
+	StartTime           string `gorm:"column:start_time"`
+	EndTime             string `gorm:"column:end_time"`
+	ClassifyFullName    string
+	TagName             string
+	// todo 是否需要记录最后更新人
+	/*DetailImgUrl        string    `gorm:"column:detail_img_url" description:"事件详情长图地址"`
+	DetailPdfUrl        string    `gorm:"column:detail_pdf_url" description:"事件详情PDF地址"`
+	LastModifyAdminId   int       `gorm:"column:last_modify_admin_id" description:"最后更新人ID"`
+	LastModifyAdminName string    `gorm:"column:last_modify_admin_name" description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `gorm:"column:content_modify_time" description:"内容更新时间"`*/
+}
+
+type KnowledgeResourceListResp struct {
+	List   []*KnowledgeResourceList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func GetKnowledgeResourceList(condition string, pars []interface{}, startSize, pageSize int) (items []*KnowledgeResource, err error) {
+
+	sql := `SELECT * FROM knowledge_resource WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY modify_time DESC LIMIT ?,?`
+	//_, err = o.Raw(sql, pars...).QueryRows(&items)
+	pars = append(pars, startSize)
+	pars = append(pars, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func GetKnowledgeResourceListCount(condition string, pars []interface{}) (count int, err error) {
+	//oRddp := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count  FROM knowledge_resource WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	//err = oRddp.Raw(sql, pars).QueryRow(&count)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *KnowledgeResource) Delete(knowledgeResourceIds int) (err error) {
+
+	sql := ` DELETE FROM knowledge_resource WHERE knowledge_resource_id =? `
+	//_, err = o.Raw(sql, knowledgeResourceIds).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, knowledgeResourceIds).Error
+	return
+}
+
+func (m *KnowledgeResource) GetById(knowledgeResourceId int) (item *KnowledgeResource, err error) {
+
+	sql := `SELECT * FROM knowledge_resource WHERE knowledge_resource_id =?`
+	//err = o.Raw(sql, knowledgeResourceId).QueryRow(&item)
+	err = global.DmSQL["rddp"].Raw(sql, knowledgeResourceId).First(&item).Error
+	return
+}
+
+// GetSimpleKnowledgeResourceByIds 根据事件ID查询事件基本信息
+func GetSimpleKnowledgeResourceByIds(knowledgeResourceIds []int) (list []*KnowledgeResource, err error) {
+	if len(knowledgeResourceIds) == 0 {
+		return
+	}
+
+	sql := `SELECT id, title, resource_code FROM knowledge_resource WHERE knowledge_resource_id IN (` + utils.GetOrmInReplace(len(knowledgeResourceIds)) + `)`
+	//_, err = o.Raw(sql, knowledgeResourceIds).QueryRows(&list)
+	err = global.DmSQL["rddp"].Raw(sql, knowledgeResourceIds).Find(&list).Error
+	return
+}
+
+type DeleteReq struct {
+	KnowledgeResourceId int `description:"事件id"`
+}
+
+type AddReq struct {
+	ResourceType int    `gorm:"column:resource_type;"`
+	ClassifyId   int    `gorm:"column:classify_id"`
+	Title        string `gorm:"column:title;"`
+	State        int    `gorm:"column:state" description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	Content      string `gorm:"column:content"`
+	SourceFrom   string `gorm:"column:source_from"`
+	TagId        int    `gorm:"column:tag_id;default:0;NOT NULL"`
+	StartTime    string `gorm:"column:start_time"`
+	EndTime      string `gorm:"column:end_time"`
+	FileUrl      string `gorm:"column:file_url"`
+}
+
+type AddResp struct {
+	KnowledgeResourceId int    `description:"事件id"`
+	ResourceCode        string `description:"事件code"`
+}
+
+func (m *KnowledgeResource) Add(item *KnowledgeResource) (err error) {
+	err = global.DmSQL["rddp"].Create(item).Error
+	return
+}
+
+func (m *KnowledgeResource) AddBatch(item []*KnowledgeResource) (err error) {
+	err = global.DmSQL["rddp"].CreateInBatches(item, 50).Error
+	return
+}
+
+type EditReq struct {
+	KnowledgeResourceId int64 `description:"事件id"`
+	AddReq
+}
+
+type EditResp struct {
+	KnowledgeResourceId int64  `description:"事件id"`
+	ResourceCode        string `description:"事件code"`
+}
+
+type ReportDetailReq struct {
+	KnowledgeResourceId int `description:"事件id"`
+}
+
+type ClassifyIdDetailReq struct {
+	ClassifyIdFirst  int `description:"事件一级分类id"`
+	ClassifyIdSecond int `description:"事件二级分类id"`
+}
+
+func GetReportDetailByClassifyId(classifyIdFirst, classifyIdSecond int) (item *KnowledgeResource, err error) {
+
+	sql := ` SELECT * FROM knowledge_resource WHERE 1=1 `
+	if classifyIdSecond > 0 {
+		sql = sql + ` AND classify_id_second=?   ORDER BY stage DESC LIMIT 1`
+		//err = o.Raw(sql, classifyIdSecond).QueryRow(&item)
+		err = global.DmSQL["rddp"].Raw(sql, classifyIdSecond).First(&item).Error
+	} else {
+		sql = sql + ` AND classify_id_first=?   ORDER BY stage DESC LIMIT 1`
+		//err = o.Raw(sql, classifyIdFirst).QueryRow(&item)
+		err = global.DmSQL["rddp"].Raw(sql, classifyIdFirst).First(&item).Error
+	}
+	return
+}
+
+type SaveReportContent struct {
+	Content             string `description:"内容"`
+	KnowledgeResourceId 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 AddReportSaveLog(knowledgeResourceId, adminId int, content, contentSub, contentStruct, canvasColor, adminName string, headResourceId, endResourceId int) (err error) {
+
+	sql := ` INSERT INTO  knowledge_resource_save_log(knowledge_resource_id, content,content_sub,content_struct,canvas_color,head_resource_id,end_resource_id,admin_id,admin_name) VALUES (?,?,?,?,?,?,?,?,?) `
+	//_, err = o.Raw(sql, knowledgeResourceId, content, contentSub, contentStruct, canvasColor, headResourceId, endResourceId, adminId, adminName).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, knowledgeResourceId, content, contentSub, contentStruct, canvasColor, headResourceId, endResourceId, adminId, adminName).Error
+	return
+}
+
+type SaveReportContentResp struct {
+	KnowledgeResourceId int `description:"事件id"`
+}
+
+func ModifyResourceCode(knowledgeResourceId int64, resourceCode string) (err error) {
+	sql := `UPDATE knowledge_resource SET resource_code=? WHERE knowledge_resource_id =? `
+	err = global.DmSQL["rddp"].Exec(sql, resourceCode, knowledgeResourceId).Error
+	return
+}
+
+// SaveDayWeekReportReq 新增晨报周报请求体
+type SaveDayWeekReportReq struct {
+	KnowledgeResourceId int    `description:"事件ID"`
+	Title               string `description:"标题"`
+	ReportType          string `description:"一级分类ID"`
+	Author              string `description:"作者"`
+	CreateTime          string `description:"创建时间"`
+}
+
+// UpdateReport 更新事件
+func (m *KnowledgeResource) Update(cols []string) (err error) {
+
+	//_, err = o.Update(reportInfo, cols...)
+	err = global.DmSQL["rddp"].Select(cols).Updates(m).Error
+	return
+}
+
+type ElasticReportDetail struct {
+	gorm.Model
+	KnowledgeResourceId int `gorm:"column:knowledge_resource_id;index" description:"事件ID"`
+
+	Title        string `gorm:"column:title" description:"标题"`
+	Abstract     string `gorm:"column:abstract" description:"摘要"`
+	BodyContent  string `gorm:"column:body_content" description:"内容"`
+	ClassifyId   int    `gorm:"column:classify_id" description:"最小单元的分类ID"`
+	ClassifyName string `gorm:"column:classify_name" description:"最小单元的分类名称"`
+}
+
+// UpdateReportSecondClassifyNameByClassifyId 更新事件分类名称字段
+func UpdateReportSecondClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+
+	sql := " UPDATE knowledge_resource SET classify_name_second = ? WHERE classify_id_second = ? "
+	//_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, classifyName, classifyId).Error
+	return
+}
+
+// UpdateReportFirstClassifyNameByClassifyId 更新事件分类一级名称字段
+func UpdateReportFirstClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+
+	sql := " UPDATE knowledge_resource SET classify_name_first = ? WHERE classify_id_first = ? "
+	//_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, classifyName, classifyId).Error
+	return
+}
+
+// UpdateReportThirdClassifyNameByClassifyId 更新事件的三级分类名称字段
+func UpdateReportThirdClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+
+	sql := " UPDATE knowledge_resource SET classify_name_third = ? WHERE classify_id_third = ? "
+	//_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, classifyName, classifyId).Error
+	return
+}
+
+// MarkEditReport 标记编辑英文研报的请求数据
+type MarkEditReport struct {
+	KnowledgeResourceId int `description:"研报id"`
+	ReportChapterId     int `description:"研报章节id"`
+	Status              int `description:"标记状态,1:编辑中,2:查询状态,3:编辑完成"`
+}
+
+type MarkReportResp struct {
+	Status int    `description:"状态:0:无人编辑, 1:当前有人在编辑"`
+	Msg    string `description:"提示信息"`
+	Editor string `description:"编辑者姓名"`
+}
+
+type MarkReportItem struct {
+	AdminId                 int    `description:"编辑者ID"`
+	Editor                  string `description:"编辑者姓名"`
+	ReportClassifyNameFirst string
+}
+
+// GetReportByCondition 获取事件
+func GetReportByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, isPage bool, startSize, pageSize int) (items []*KnowledgeResource, err error) {
+
+	fields := `*`
+	if len(fieldArr) > 0 {
+		fields = strings.Join(fieldArr, ",")
+	}
+	sql := `SELECT ` + fields + ` FROM knowledge_resource WHERE 1=1 `
+	sql += condition
+	order := ` ORDER BY modify_time DESC`
+	if orderRule != `` {
+		order = orderRule
+	}
+	sql += order
+	if isPage {
+		sql += ` LIMIT ?,?`
+		//_, err = o.Raw(sql, pars...).QueryRows(&items)
+		err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	} else {
+		//_, err = o.Raw(sql, pars).QueryRows(&items)
+		err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	}
+	return
+}
+
+// SetPrePublishReportById 设置定时发布
+func SetPrePublishReportById(knowledgeResourceId int, prePublishTime string, preMsgSend int) (err error) {
+
+	sql := `UPDATE knowledge_resource SET pre_publish_time=?, pre_msg_send=? WHERE knowledge_resource_id = ? and state = 1 `
+	//_, err = o.Raw(sql, prePublishTime, preMsgSend, knowledgeResourceId).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, prePublishTime, preMsgSend, knowledgeResourceId).Error
+	return
+}
+
+// UpdateReportsStateBySecondIds 批量更新二级分类事件状态
+func UpdateReportsStateBySecondIds(oldState, newState int, secondIds []int) (err error) {
+	if len(secondIds) <= 0 {
+		return
+	}
+
+	// (有审批流的)未发布->待提交
+	sql := fmt.Sprintf(`UPDATE knowledge_resource SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	//_, err = o.Raw(sql, newState, oldState, secondIds).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, newState, oldState, secondIds).Error
+	if err != nil {
+		return
+	}
+	// (无审批流的)待提交->未发布
+	sql = fmt.Sprintf(`UPDATE knowledge_resource SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second NOT IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	//_, err = o.Raw(sql, oldState, newState, secondIds).Exec()
+	err = global.DmSQL["rddp"].Exec(sql, oldState, newState, secondIds).Error
+	return
+}
+
+func (m *KnowledgeResource) AddMulti(items []*KnowledgeResource) (err error) {
+	err = global.DmSQL["rddp"].CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func GetReportFieldsByIds(ids []int, fields []string) (items []*KnowledgeResource, err error) {
+	if len(ids) == 0 {
+		return
+	}
+
+	field := " * "
+	if len(fields) > 0 {
+		field = fmt.Sprintf(" %s ", strings.Join(fields, ","))
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM knowledge_resource WHERE knowledge_resource_id IN (%s)`, field, utils.GetOrmInReplace(len(ids)))
+	//_, err = o.Raw(sql, ids).QueryRows(&items)
+	err = global.DmSQL["rddp"].Raw(sql, ids).Find(&items).Error
+	return
+}
+
+type KnowledgeResourceDetailView struct {
+	*KnowledgeResource
+}
+
+type ResourceImportData struct {
+	Title              string `description:"标题"`
+	StartDate          string `description:"开始日期"`
+	EndDate            string `description:"开始时间"`
+	StartTime          string `description:"结束日期"`
+	EndTime            string `description:"结束时间"`
+	Content            string `description:"正文"`
+	Tag                string `description:"标签"`
+	SourceFrom         string `description:"来源"`
+	ClassifyFirstName  string `description:"一级分类"`
+	ClassifySecondName string `description:"二级分类"`
+	ClassifyThirdName  string `description:"三级分类"`
+}

+ 247 - 0
models/knowledge/knowledge_tag.go

@@ -0,0 +1,247 @@
+package knowledge
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type KnowledgeTag struct {
+	TagId        int       `gorm:"primaryKey;autoIncrement;column:tag_id"`
+	TagName      string    `gorm:"column:tag_name;default:'';type:varchar(100);not null"`
+	TagType      int       `gorm:"column:tag_type;default:0;type:tinyint"`
+	Sort         int       `gorm:"column:sort;default:0;type:tinyint"`
+	ResourceType int       `gorm:"column:resource_type;default:0;not null;type:tinyint"`
+	CreateTime   time.Time `gorm:"column:create_time;default:CURRENT_TIMESTAMP"`
+	ModifyTime   time.Time `gorm:"column:modify_time;default:CURRENT_TIMESTAMP"`
+}
+
+func (m *KnowledgeTag) TableName() string {
+	return "knowledge_tag"
+}
+
+func (m *KnowledgeTag) GetPrimaryKey() string {
+	return "tag_id"
+}
+
+func (m *KnowledgeTag) GetTagByName(tagName string, resourceType int) (item *KnowledgeTag, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE tag_name=?  AND resource_type=?`, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, tagName, resourceType).First(&item).Error
+	return
+}
+
+func (m *KnowledgeTag) GetTagById(tagId int) (item *KnowledgeTag, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s=? `, m.TableName(), m.GetPrimaryKey())
+	err = global.DmSQL["rddp"].Raw(sql, tagId).First(&item).Error
+	return
+}
+
+// 添加分类
+func (m *KnowledgeTag) Add(item *KnowledgeTag) (err error) {
+	err = global.DmSQL["rddp"].Create(item).Error
+	return
+}
+
+func (m *KnowledgeTag) GetTagSubCountByTagId(tagId int) (count int, err error) {
+
+	sql := `SELECT COUNT(1) as num FROM %s AS a
+        INNER JOIN knowledge_resource AS b ON a.tag_id=b.tag_id
+        WHERE a.parent_id=? `
+	err = global.DmSQL["rddp"].Raw(sql, tagId).First(&count).Error
+	return
+}
+
+// 删除分类
+func (m *KnowledgeTag) Delete(tagId int) (err error) {
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE tag_id =? `, m.TableName())
+	err = global.DmSQL["rddp"].Exec(sql, tagId).Error
+	if err != nil {
+		return
+	}
+	return
+}
+
+type KnowledgeTagItem struct {
+	KnowledgeTag
+}
+
+type KnowledgeTagListResp struct {
+	List []*KnowledgeTagItem
+}
+
+type TagAddReq struct {
+	TagName      string `description:"分类名称"`
+	ParentId     int    `description:"父级分类id,没有父级分类传0"`
+	ResourceType int    `description:"分类类型:0事件库,1政策库,2观点库,3知识库"`
+}
+
+type DeleteTagReq struct {
+	TagId int `description:"分类ID"`
+}
+
+type EditTagReq struct {
+	TagId int `description:"分类ID"`
+	TagAddReq
+}
+
+type FindByIdTagReq struct {
+	TagId int `description:"分类ID"`
+}
+
+// EditTagPermissionReq 编辑分类权限请求
+type EditTagPermissionReq struct {
+	TagId                 int   `description:"分类ID"`
+	ChartPermissionIdList []int `description:"权限id数组"`
+}
+
+// GetAllTag 获取所有分类
+func (m *KnowledgeTag) GetAllTag() (list []*KnowledgeTag, err error) {
+	//o := orm.NewOrmUsingDB("rddp")
+	//sql := ` SELECT * FROM Tag `
+	//_, err = o.Raw(sql).QueryRows(&list)
+	sql := fmt.Sprintf(` SELECT * FROM %s `, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql).Find(&list).Error
+	return
+}
+
+// GetTagByKeyword 名称获取分类
+func (m *KnowledgeTag) GetTagByKeyword(keyword string) (item *KnowledgeTag, err error) {
+	sql := ` SELECT * FROM %s WHERE tag_name = ? LIMIT 1 `
+	err = global.DmSQL["rddp"].Raw(sql, keyword).Find(&item).Error
+	return
+}
+
+// UpdateTag 更新分类
+func (m *KnowledgeTag) Update(cols []string) (err error) {
+	err = global.DmSQL["rddp"].Select(cols).Updates(m).Error
+	return
+}
+
+// SimpleTagList 简版分类列表
+type SimpleTagList struct {
+	Id       int    `description:"分类ID"`
+	TagName  string `description:"分类名称"`
+	ParentId int    `description:"父级ID"`
+	Sort     int    `description:"排序"`
+	Child    []*SimpleTagList
+}
+
+// GetTagByCondition 获取分类列表
+func (m *KnowledgeTag) GetTagByCondition(condition, orderRule string, pars []interface{}) (items []*SimpleTagList, err error) {
+
+	sql := `SELECT * FROM %s WHERE 1 = 1 `
+	if condition != `` {
+		sql += condition
+	}
+	order := `sort ASC, create_time ASC`
+	if orderRule != `` {
+		order = orderRule
+	}
+	sql += ` ORDER BY ` + order
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// RelateTelSecTagWithPermissions 关联了电话会的二级分类及权限
+type RelateTelSecTagWithPermissions struct {
+	Id                 int    `description:"分类ID"`
+	TagName            string `description:"分类名称"`
+	ChartPermissionIds string `description:"权限IDs"`
+}
+
+// UpdateTagSortByParentId 根据父类id更新排序
+func (m *KnowledgeTag) UpdateTagSortByParentId(permissionId, nowSort int, updateSort string, resourceType int) (err error) {
+
+	sql := ` update %s set sort = ` + updateSort + ` WHERE resource_type=? AND sort > ? `
+	if permissionId > 0 {
+		sql += ` or ( tag_id > ` + fmt.Sprint(permissionId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Exec(sql, resourceType, nowSort).Error
+	return
+}
+
+// GetMaxSortByParentId 获取最大的排序值
+func (m *KnowledgeTag) GetMaxSortByParentId(parentId int) (maxSort int, err error) {
+	sql := `SELECT COALESCE(MAX(sort),0) AS sort FROM %s WHERE parent_id = ? `
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentId).Scan(&maxSort).Error
+	return
+}
+
+// GetMaxSort 获取最大的排序值
+func (m *KnowledgeTag) GetMaxSort() (maxSort int, err error) {
+	sql := `SELECT COALESCE(MAX(sort),0) AS sort FROM %s`
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql).Scan(&maxSort).Error
+	return
+}
+
+// GetFirstTagByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func (m *KnowledgeTag) GetFirstTag() (item *KnowledgeTag, err error) {
+	sql := `SELECT * FROM %s order by sort asc, tag_id asc limit 1`
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql).First(&item).Error
+	return
+}
+
+type TagMoveReq struct {
+	TagId     int `description:"分类ID"`
+	PrevTagId int `description:"上一个兄弟节点分类id"`
+	NextTagId int `description:"下一个兄弟节点分类id"`
+}
+
+type TagSetEnabledReq struct {
+	TagId   int `description:"分类ID"`
+	Enabled int `description:"是否可用,1可用,0禁用"`
+}
+
+// GetCountTagChildByParentId
+// @Description: 获取父级分类下子分类数量
+// @author: Roc
+// @datetime 2024-06-17 10:58:46
+// @param parentId int
+// @return total int
+// @return err error
+func (m *KnowledgeTag) GetCountTagChildByParentId(parentId int) (total int, err error) {
+	sql := fmt.Sprintf(`SELECT count(1) AS total FROM %s WHERE parent_id = ? `, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, parentId).Scan(&total).Error
+	return
+}
+
+// GetTagListByKeyword
+// @Description: 获取分类列表
+// @author: Roc
+// @datetime 2024-06-19 09:49:33
+// @param keyWord string
+// @param enabled int
+// @return items []*KnowledgeTagList
+// @return err error
+func (m *KnowledgeTag) GetTagListByKeyword(keyWord string) (items []*KnowledgeTagItem, err error) {
+	sql := ``
+	pars := make([]interface{}, 0)
+
+	sql = `SELECT * FROM %s WHERE 1=1 `
+
+	if keyWord != `` {
+		sql += ` AND tag_name LIKE ? `
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 1)
+	}
+	sql += ` ORDER BY sort ASC, create_time ASC`
+
+	sql = fmt.Sprintf(sql, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *KnowledgeTag) GetListByIdList(tagIdList []int) (items []*KnowledgeTag, err error) {
+	num := len(tagIdList)
+	if num <= 0 {
+		return
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE tag_id IN (?)`, m.TableName())
+	err = global.DmSQL["rddp"].Raw(sql, tagIdList).Find(&items).Error
+	return
+}

+ 0 - 375
models/report.go

@@ -292,37 +292,6 @@ func GetReportListCount(condition string, pars []interface{}) (count int, err er
 	return
 }
 
-func GetReportList(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-
-	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  `
-	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...).QueryRows(&items)
-	pars = append(pars, startSize)
-	pars = append(pars, pageSize)
-	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
-	return
-}
-
-// PublishReport 发布报告
-func PublishReport(reportIds []int) (err error) {
-	if len(reportIds) == 0 {
-		return
-	}
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE report SET state=2,publish_time=now(),modify_time=NOW() WHERE id IN (` + utils.GetOrmInReplace(len(reportIds)) + `)`
-	//_, err = o.Raw(sql).Exec()
-	err = global.DmSQL["rddp"].Exec(sql, reportIds).Error
-	return
-}
-
 // PublishCancelReport 取消发布报告
 func PublishCancelReport(reportId, state int, publishTimeNullFlag bool, lastModifyAdminId int, lastModifyAdminName string) (err error) {
 	//o := orm.NewOrmUsingDB("rddp")
@@ -409,14 +378,6 @@ func GetReportById(reportId int) (item *ReportDetail, err error) {
 	return
 }
 
-func GetReportByIds(reportIds string) (list []*ReportDetail, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT * FROM report WHERE id in ` + reportIds
-	//_, err = o.Raw(sql).QueryRows(&list)
-	err = global.DmSQL["rddp"].Raw(sql).Find(&list).Error
-	return
-}
-
 // GetSimpleReportByIds 根据报告ID查询报告基本信息
 func GetSimpleReportByIds(reportIds []int) (list []*Report, err error) {
 	if len(reportIds) == 0 {
@@ -466,45 +427,6 @@ func GetReportStage(classifyIdFirst, classifyIdSecond, classifyIdThird int) (cou
 	return
 }
 
-// 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 = " AND classify_id_first = ? "
-	}
-
-	//o := orm.NewOrmUsingDB("rddp")
-	//o.Raw(sql, yearStart, reportId, classifyId).QueryRow(&count)
-	err = global.DmSQL["rddp"].Raw(sql, yearStart, reportId, classifyId).Scan(&count).Error
-	return
-}
-
 type PublishReq struct {
 	ReportIds string `description:"报告id,多个用英文逗号隔开"`
 	ReportUrl string `description:"报告Url"`
@@ -755,14 +677,6 @@ type SaveReportContent struct {
 	EndResourceId  int    `description:"版尾资源ID"`
 }
 
-func EditReportContent(reportId int, content, contentSub string) (err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := ` UPDATE report SET content=?,content_sub=?,modify_time=NOW() WHERE id=? `
-	//_, err = o.Raw(sql, content, contentSub, reportId).Exec()
-	err = global.DmSQL["rddp"].Exec(sql, content, contentSub, reportId).Error
-	return
-}
-
 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,content_struct,canvas_color,head_resource_id,end_resource_id,admin_id,admin_name) VALUES (?,?,?,?,?,?,?,?,?) `
@@ -837,15 +751,6 @@ type SaveDayWeekReportReq struct {
 	CreateTime string `description:"创建时间"`
 }
 
-// GetDayWeekReportStage 获取晨报周报期数
-func GetDayWeekReportStage(classifyIdFirst int, yearStart time.Time) (count int, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := " SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first = ? AND create_time > ? "
-	//o.Raw(sql, classifyIdFirst, yearStart).QueryRow(&count)
-	err = global.DmSQL["rddp"].Raw(sql, classifyIdFirst, yearStart).Scan(&count).Error
-	return
-}
-
 // GetReportByReportId 主键获取报告
 func GetReportByReportId(reportId int) (item *Report, err error) {
 	//o := orm.NewOrmUsingDB("rddp")
@@ -855,15 +760,6 @@ func GetReportByReportId(reportId int) (item *Report, err error) {
 	return
 }
 
-// GetReportByOldReportId 根据老报告id主键获取报告
-func GetReportByOldReportId(reportId int) (item *Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT * FROM report WHERE old_report_id = ?`
-	//err = o.Raw(sql, reportId).QueryRow(&item)
-	err = global.DmSQL["rddp"].Raw(sql, reportId).First(&item).Error
-	return
-}
-
 // DeleteDayWeekReportAndChapter 删除晨周报及章节
 func DeleteDayWeekReportAndChapter(reportId int) (err error) {
 	//o := orm.NewOrmUsingDB("rddp")
@@ -925,27 +821,6 @@ type ReportDetailViewPermission struct {
 	PermissionName string
 }
 
-func GetUnPublishDayReport(startTime time.Time, endTime time.Time) (item *Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT
-				*
-			FROM
-				report AS a
-			WHERE
-				a.has_chapter = 1
-			AND a.chapter_type = "day"
-			AND a.state = 1
-			AND (
-				a.create_time BETWEEN ? AND ?
-			)
-			ORDER BY
-				a.create_time DESC
-			LIMIT 1 `
-	//err = o.Raw(sql, startTime, endTime).QueryRow(&item)
-	err = global.DmSQL["rddp"].Raw(sql, startTime, endTime).First(&item).Error
-	return
-}
-
 type ElasticReportDetail struct {
 	gorm.Model
 	ReportId           int    `gorm:"column:report_id;index" description:"报告ID"`
@@ -966,24 +841,6 @@ type ElasticReportDetail struct {
 	StageStr           string `gorm:"column:stage_str" description:"报告期数"`
 }
 
-// GetLastPublishedDayWeekReport 获取上一篇已发布的晨周报
-func GetLastPublishDayWeekReport(chapterType string) (item *Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT * FROM report WHERE has_chapter = 1 AND chapter_type = ? AND state = 2 ORDER BY publish_time DESC LIMIT 1 `
-	//err = o.Raw(sql, chapterType).QueryRow(&item)
-	err = global.DmSQL["rddp"].Raw(sql, chapterType).First(&item).Error
-	return
-}
-
-// GetNewReportExist
-func GetNewReportExist(oldReportId int) (item *Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT id FROM report WHERE old_report_id = ? LIMIT 1 `
-	//err = o.Raw(sql, oldReportId).QueryRow(&item)
-	err = global.DmSQL["rddp"].Raw(sql, oldReportId).First(&item).Error
-	return
-}
-
 // PublishReportAndChapter 发布报告及章节
 func PublishReportAndChapter(reportInfo *Report, isPublishReport bool, cols []string) (err error) {
 	//o := orm.NewOrmUsingDB("rddp")
@@ -1023,16 +880,6 @@ func PublishReportAndChapter(reportInfo *Report, isPublishReport bool, cols []st
 	return
 }
 
-func GetSyncEmptyVideoReport() (list []*Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT id FROM report WHERE old_report_id > 0 AND state = 2 AND chapter_type = "" AND (video_size = "" OR video_play_seconds = "")
-UNION ALL
-SELECT DISTINCT report_id FROM report_chapter WHERE publish_state = 2 AND (video_size = "" OR video_play_seconds = "") `
-	//_, err = o.Raw(sql).QueryRows(&list)
-	err = global.DmSQL["rddp"].Raw(sql).Find(&list).Error
-	return
-}
-
 // PublishReportById 发布报告
 func PublishReportById(reportId int, publishTime time.Time, lastModifyAdminId int, lastModifyAdminName string) (err error) {
 	//o := orm.NewOrmUsingDB("rddp")
@@ -1051,84 +898,6 @@ func ResetReportById(reportId, state int, lastModifyAdminId int, lastModifyAdmin
 	return
 }
 
-// GetCommentReportByReportId 查询有留言的报告列表
-func GetCommentReportByReportId(condition string, pars []interface{}, startSize, pageSize int) (list []*Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT
-	id,
-	create_time,
-	title,
-	classify_name_first,
-	classify_id_first,
-	classify_name_second,
-	classify_id_second,
-	state,
-IF
-	( publish_time, publish_time, create_time ) AS publish_time 
-FROM
-	report 
-WHERE
-	1=1
-  `
-	if condition != "" {
-		sql += condition
-	}
-	sql += ` ORDER BY  publish_time DESC , title ASC LIMIT ?,?`
-	//_, err = o.Raw(sql, pars...).QueryRows(&list)
-	pars = append(pars, startSize)
-	pars = append(pars, pageSize)
-	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&list).Error
-	return
-}
-
-// GetCommentReportByReportIdOrderBy 查询有留言的报告列表(指定排序)
-func GetCommentReportByReportIdOrderBy(condition string, pars []interface{}, startSize, pageSize int, orderBy string) (list []*Report, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT
-	id,
-	create_time,
-	title,
-	classify_name_first,
-	classify_id_first,
-	classify_name_second,
-	classify_id_second,
-	state,
-IF
-	( publish_time, publish_time, create_time ) AS publish_time 
-FROM
-	report 
-WHERE
-	1=1
-  `
-	if condition != "" {
-		sql += condition
-	}
-	if orderBy == `` {
-		sql += ` ORDER BY  publish_time DESC , title ASC `
-	} else {
-		sql += orderBy
-	}
-	sql += ` LIMIT ?,? `
-	//_, err = o.Raw(sql, pars...).QueryRows(&list)
-	pars = append(pars, startSize)
-	pars = append(pars, pageSize)
-	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&list).Error
-	return
-}
-
-// GetCommentReportTotalByReportId 查询有留言的报告列表总数
-func GetCommentReportTotalByReportId(condition string, pars []interface{}) (total int64, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT count(*)
-        FROM report WHERE 1=1`
-	if condition != "" {
-		sql += condition
-	}
-	//err = o.Raw(sql, pars).QueryRow(&total)
-	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&total).Error
-	return
-}
-
 // 点赞相关的报告列表
 type LikeReportItem struct {
 	gorm.Model
@@ -1144,116 +913,6 @@ type LikeReportItem struct {
 	Title                 string    `gorm:"column:title" description:"标题"`
 }
 
-// GetLikeReportByReportIdReportChapterId 获取有被点赞的报告列表
-func GetLikeReportByReportIdReportChapterId(reportIds string, chapterIds string, orderStr string, startSize, pageSize int) (list []*LikeReportItem, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `( SELECT
-id AS report_id,
-0 AS report_chapter_id,
-classify_id_first,
-classify_id_second,
-classify_name_first,
-classify_name_second,
-0 as report_chapter_type_id,
-"" as report_chapter_type_name,
-publish_time,
-title
-FROM
-	report
-WHERE
-	classify_name_first != "晨报" 
-	AND classify_name_first != "周报"
-	AND id in (` + reportIds + `)
-	)
-UNION
-	
-( SELECT
-report_id,
-report_chapter_id,
-classify_id_first,
-0 as classify_id_second,
-classify_name_first,
-null as classify_name_second,
-type_id as report_chapter_type_id,
-type_name as report_chapter_type_name,
-publish_time,
-title
-FROM
-	report_chapter
-WHERE
-	 report_chapter_id in (` + chapterIds + `)
-	)`
-	if orderStr != "" {
-		sql += ` ORDER BY FIELD(CONCAT(report_id, "-",report_chapter_id),` + orderStr + `)`
-	} else {
-		sql += ` ORDER BY  publish_time DESC, report_id Desc`
-	}
-	sql += ` LIMIT ?,?`
-	//_, err = o.Raw(sql, startSize, pageSize).QueryRows(&list)
-	err = global.DmSQL["rddp"].Raw(sql, startSize, pageSize).Find(&list).Error
-	return
-}
-
-// GetLikeReportTotalByReportIdReportChapterId 获取有被点赞的报告列表总数
-func GetLikeReportTotalByReportIdReportChapterId(reportIds string, chapterIds string) (total int64, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `select count(*) from (( SELECT
-id AS report_id,
-0 AS report_chapter_id,
-classify_id_first,
-classify_id_second,
-classify_name_first,
-classify_name_second,
-0 as report_chapter_type_id,
-publish_time,
-title
-FROM
-	report
-WHERE
-	classify_name_first != "晨报" 
-	AND classify_name_first != "周报"
-	AND id in (` + reportIds + `)
-	)
-UNION
-	
-( SELECT
-report_id,
-report_chapter_id,
-classify_id_first,
-0 as classify_id_second,
-classify_name_first,
-null as classify_name_second,
-type_id as report_chapter_type_id,
-publish_time,
-title
-FROM
-	report_chapter
-WHERE
-report_chapter_id in (` + chapterIds + `)
-	)) r`
-	//err = o.Raw(sql).QueryRow(&total)
-	err = global.DmSQL["rddp"].Raw(sql).Scan(&total).Error
-	return
-}
-
-// GetPageReportList 分页获取报告列表
-func GetPageReportList(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*ReportList, err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT * FROM report WHERE 1=1 `
-	sql += condition
-	sql += ` ORDER BY modify_time DESC`
-	totalSql := `SELECT COUNT(1) total FROM (` + sql + `) z `
-	//err = o.Raw(totalSql, pars).QueryRow(&total)
-	err = global.DmSQL["rddp"].Raw(totalSql, pars...).Scan(&total).Error
-	if err != nil {
-		return
-	}
-	sql += ` LIMIT ?,? `
-	//_, err = o.Raw(sql, pars...).QueryRows(&items)
-	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
-	return
-}
-
 // SunCodeReq 获取太阳码请求体
 type SunCodeReq struct {
 	CodePage  string `json:"CodePage" description:"太阳码page"`
@@ -1328,31 +987,6 @@ func UpdateReportThirdClassifyNameByClassifyId(classifyId int, classifyName stri
 	return
 }
 
-// UpdateReportSecondClassifyFirstNameByClassifyId 更新报告二级分类的一级分类名称和id
-func UpdateReportSecondClassifyFirstNameByClassifyId(classifyId, newClassifyId int, classifyName string) (err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := " UPDATE report SET classify_name_first = ?, classify_id_first = ? WHERE classify_id_second = ? "
-	//_, err = o.Raw(sql, classifyName, newClassifyId, classifyId).Exec()
-	err = global.DmSQL["rddp"].Exec(sql, classifyName, newClassifyId, classifyId).Error
-	return
-}
-
-// GetEmptyContentSubPPTReport 获取前两章为空的PPT报告
-func GetEmptyContentSubPPTReport() (list []*Report, err error) {
-	sql := `SELECT
-				r.id,
-				r.content,
-				r.content_sub
-			FROM
-				report AS r
-			JOIN ppt_v2 AS p ON r.id = p.report_id
-			WHERE
-				p.report_id > 0 AND r.content_sub = ""`
-	//_, err = orm.NewOrmUsingDB("rddp").Raw(sql).QueryRows(&list)
-	err = global.DmSQL["rddp"].Raw(sql).Find(&list).Error
-	return
-}
-
 // ModifyReportAuthor 更改报告作者
 func ModifyReportAuthor(condition string, pars []interface{}, authorName string) (count int, err error) {
 	//产品权限
@@ -1443,15 +1077,6 @@ func GetReportByCondition(condition string, pars []interface{}, fieldArr []strin
 	return
 }
 
-// ModifyReportMsgIsSendV2 更新报告消息状态
-func ModifyReportMsgIsSendV2(reportId int) (err error) {
-	//o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE report SET msg_is_send = 1, ths_msg_is_send = 1, msg_send_time = NOW() WHERE id = ? LIMIT 1`
-	//_, err = o.Raw(sql, reportId).Exec()
-	err = global.DmSQL["rddp"].Exec(sql, reportId).Error
-	return
-}
-
 // SetPrePublishReportById 设置定时发布
 func SetPrePublishReportById(reportId int, prePublishTime string, preMsgSend int) (err error) {
 	//o := orm.NewOrmUsingDB("rddp")

+ 198 - 0
routers/commentsRouter.go

@@ -8701,6 +8701,204 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "CheckDeleteClassify",
+            Router: `/classify/checkDelete`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/classify/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "FindByIdClassify",
+            Router: `/classify/findById`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "ListClassify",
+            Router: `/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "Move",
+            Router: `/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeClassifyController"],
+        beego.ControllerComments{
+            Method: "ParentClassify",
+            Router: `/classify/parent`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/resource/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/resource/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/resource/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "BaseDetail",
+            Router: `/resource/detail/base`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "ImportFailListDownload",
+            Router: `/resource/download_fail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/resource/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "ImportData",
+            Router: `/resource/import_add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/resource/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeResourceController"],
+        beego.ControllerComments{
+            Method: "SaveReportContent",
+            Router: `/resource/saveContent`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/tag/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/tag/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/tag/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"],
+        beego.ControllerComments{
+            Method: "ListTag",
+            Router: `/tag/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:KnowledgeTagController"],
+        beego.ControllerComments{
+            Method: "Move",
+            Router: `/tag/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:MeetingProbabilitiesController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:MeetingProbabilitiesController"],
         beego.ControllerComments{
             Method: "Detail",

+ 7 - 0
routers/router.go

@@ -394,6 +394,13 @@ func init() {
 				&range_analysis.RangeChartChartInfoController{},
 			),
 		),
+		web.NSNamespace("/knowledge",
+			web.NSInclude(
+				&controllers.KnowledgeClassifyController{},
+				&controllers.KnowledgeResourceController{},
+				&controllers.KnowledgeTagController{},
+			),
+		),
 		web.NSNamespace("/bi_dashborad",
 			web.NSInclude(
 				&controllers.BIDaShboardController{},

+ 2 - 2
services/bi_approve/bi_approve.go

@@ -1063,7 +1063,7 @@ func CheckBiCurrState(classifyId, operate int) (state int, err error) {
 }
 
 // SubmitBiApprove 提交审批
-func SubmitBiApprove(biId int, reportTitle string, classifyId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
+func SubmitBiApprove(biId int, biTitle string, classifyId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
 	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
 	confMap, e := models.GetBusinessConf()
 	if e != nil {
@@ -1120,7 +1120,7 @@ func SubmitBiApprove(biId int, reportTitle string, classifyId int, sysAdminId in
 	now := time.Now().Local()
 	newApprove := new(biapprove.BiApprove)
 	newApprove.BiId = biId
-	newApprove.BiTitle = reportTitle
+	newApprove.BiTitle = biTitle
 	newApprove.ClassifyId = classifyId
 	newApprove.State = BiApproveStateApproving
 	newApprove.FlowId = flowItem.BiApproveFlowId

+ 508 - 0
services/knowledge/classify.go

@@ -0,0 +1,508 @@
+package knowledge
+
+import (
+	"errors"
+	"eta_gn/eta_api/models/knowledge"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// MoveKnowledgeClassify 移动分类
+func MoveKnowledgeClassify(req knowledge.ClassifyMoveReq) (err error, errMsg string) {
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	classifyId := req.ClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		classifyInfo *knowledge.KnowledgeClassify
+		prevClassify *knowledge.KnowledgeClassify
+		nextClassify *knowledge.KnowledgeClassify
+
+		prevSort int
+		nextSort int
+	)
+
+	// 移动对象为分类, 判断权限
+	classifyInfo, err = knowledgeObj.GetClassifyById(classifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			errMsg = "分类不存在, 请刷新页面"
+			err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		errMsg = "移动失败"
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	} else if classifyInfo.ClassifyId == 0 {
+		errMsg = "分类不存在, 请刷新页面"
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+
+	parentClassifyId := classifyInfo.ParentId
+	if prevClassifyId > 0 {
+		prevClassify, err = knowledgeObj.GetClassifyById(prevClassifyId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				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 = knowledgeObj.GetClassifyById(nextClassifyId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				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 = moveKnowledgeClassify(classifyInfo, prevClassify, nextClassify, parentClassifyId, prevSort, nextSort, classifyInfo.ResourceType)
+	return
+}
+
+// moveKnowledgeClassify 移动分类
+func moveKnowledgeClassify(classifyInfo, prevClassify, nextClassify *knowledge.KnowledgeClassify, parentId, prevSort, nextSort, resourceType int) (err error, errMsg string) {
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	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 {
+					_ = knowledgeObj.UpdateClassifySortByParentId(parentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr, resourceType)
+				} else {
+					_ = knowledgeObj.UpdateClassifySortByParentId(parentId, 0, prevSort, updateSortStr, resourceType)
+				}
+
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextSort-prevSort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = knowledgeObj.UpdateClassifySortByParentId(parentId, prevClassify.ClassifyId, prevSort, updateSortStr, resourceType)
+					} else {
+						_ = knowledgeObj.UpdateClassifySortByParentId(parentId, 0, prevSort, updateSortStr, resourceType)
+					}
+
+				}
+			}
+		}
+
+		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 = knowledgeObj.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 := knowledgeObj.GetFirstClassifyByParentId(parentId)
+		if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstPermission != nil && firstPermission.ClassifyId != 0 && firstPermission.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = knowledgeObj.UpdateClassifySortByParentId(parentId, firstPermission.ClassifyId-1, 0, updateSortStr, resourceType)
+		}
+
+		classifyInfo.Sort = 0 //那就是排在第一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = classifyInfo.Update(updateCol)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("修改失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// AddKnowledgeClassify
+// @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 AddKnowledgeClassify(classifyName string, parentId, resourceType int) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `添加失败`
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	_, err = knowledgeObj.GetClassifyByName(classifyName, parentId, resourceType)
+	if err != nil && !utils.IsErrNoRow(err) {
+		errMsg = "获取分类信息失败"
+		return
+	}
+
+	if err == nil {
+		errMsg = "分类名称:" + classifyName + "已存在"
+		isSendEmail = false
+		err = errors.New(errMsg)
+		return
+	} else {
+		err = nil
+	}
+
+	level := 1
+	// 父级分类
+	var parentClassifyItem *knowledge.KnowledgeClassify
+	// 父级分类下的子分类数量
+	var childClassifyCount int
+
+	if parentId > 0 {
+		// 获取父级分类信息
+		parentClassifyItem, err = knowledgeObj.GetClassifyById(parentId)
+		if err != nil {
+			errMsg = "获取父级分类信息失败"
+			if utils.IsErrNoRow(err) {
+				errMsg = "父级分类不存在"
+			}
+			return
+		}
+		if parentClassifyItem.ResourceType != resourceType {
+			errMsg = "父级分类与当前分类类型不一致"
+			err = fmt.Errorf(errMsg)
+			return
+		}
+		level = parentClassifyItem.Level + 1
+
+		if level > 3 {
+			errMsg = "分类层级不可超过三级"
+			isSendEmail = false
+			return
+		}
+		// 获取父级分类下的子分类数量
+		childClassifyCount, err = knowledgeObj.GetCountClassifyChildByParentId(parentId)
+		if err != nil {
+			errMsg = "获取父级分类的子分类信息失败"
+			return
+		}
+	}
+
+	nowTime := time.Now().Local()
+	classify := new(knowledge.KnowledgeClassify)
+
+	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.Level = level
+	classify.ResourceType = resourceType
+
+	err = knowledgeObj.Add(classify)
+	if err != nil {
+		return
+	}
+	fmt.Println("classify.ClassifyId:")
+	fmt.Println(classify.ClassifyId)
+	// 如果父级分类不为空的话,那么就标记有子级分类,同时
+	if parentClassifyItem != nil {
+		parentClassifyItem.HasChild = 1
+		err = parentClassifyItem.Update([]string{"HasChild"})
+		if err != nil {
+			return
+		}
+
+		// 如果以前没有子级分类,那么就继承父级分类下的章节类型(创建新的章节与分类的关系)
+		if childClassifyCount <= 0 {
+			// todo 修改分类下的资源报告
+			moveResourceByAddClassify(parentClassifyItem, classify)
+		}
+	}
+	return
+}
+
+// 关于研报分类,因只允许报告或品种关联在最小分类下,所以当某个父分类(非三级分类)已关联报告或品种,需要在该父分类下增加子分类,则第一个子分类添加成功时,默认把该父分类关联的品种和报告转移至新创建的子分类(第一个子分类)下
+// moveReportByAddClassify
+// @Description: 报告和章节的转移
+// @author: Roc
+// @datetime 2024-06-17 16:29:56
+// @param parentClassifyInfo *knowledge.KnowledgeClassify
+// @param currClassifyInfo *knowledge.KnowledgeClassify
+// @return err error
+func moveResourceByAddClassify(parentClassifyInfo, currClassifyInfo *knowledge.KnowledgeClassify) (err error) {
+	/*defer func() {
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprint("历史报告更改分类失败,父级分类ID:", parentClassifyInfo.ClassifyId, ";当前分类ID:", currClassifyInfo.ClassifyId, ";错误信息:", 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.ClassifyId, currClassifyInfo.ClassifyName, parentClassifyInfo.ClassifyId)
+
+	// 获取当前分类下的所有章节类型
+	currReportChapterTypeList, err := knowledgeObj.GetAllReportChapterTypeListByClassifyId(currClassifyInfo.ClassifyId)
+	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 = knowledgeObj.ModifyKnowledgeClassifyAndReportChapterTypeByCondition(condition, pars, updateStr, chapterTypeIdMap, parentClassifyInfo.ClassifyId, currClassifyInfo.ClassifyId, currClassifyInfo.ClassifyName)
+	if err != nil {
+		return
+	}*/
+
+	return
+}
+
+func EditKnowledgeClassify(classifyId, parentId int, classifyName string) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `修改失败`
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	item, err := knowledgeObj.GetClassifyById(classifyId)
+	if err != nil {
+		errMsg = "获取分类信息失败"
+		if utils.IsErrNoRow(err) {
+			errMsg = "分类不存在, 或已被删除"
+			isSendEmail = false
+		}
+		return
+	}
+	if (parentId == 0 && item.ParentId > 0) || (parentId > 0 && item.ParentId == 0) {
+		err = errors.New("不允许修改分类层级")
+		errMsg = "不允许修改分类层级"
+		return
+	}
+	if parentId > 0 {
+		parentClassifyItem, e := knowledgeObj.GetClassifyById(parentId)
+		if e != nil {
+			errMsg = "获取父级分类失败"
+			err = errors.New("获取父级分类失败, Err: " + e.Error())
+			return
+		}
+		if parentClassifyItem.Level != item.Level {
+			errMsg = "不允许修改分类层级"
+			err = errors.New("不允许修改分类层级")
+			return
+		}
+	}
+	//originName := item.ClassifyName
+
+	// 重名校验
+	existName, e := knowledgeObj.GetClassifyByName(classifyName, parentId, item.ResourceType)
+	if e != nil && !utils.IsErrNoRow(e) {
+		errMsg = "分类名称已存在"
+		err = errors.New("获取重名分类失败, Err: " + err.Error())
+		return
+	}
+	if existName != nil && existName.ClassifyId > 0 && existName.ClassifyId != item.ClassifyId {
+		errMsg = "分类名称:" + classifyName + "已存在"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	item.ClassifyName = classifyName
+
+	// ETA1.8.3:不允许修改上级分类  2024-6-17 13:21:01
+	//
+	// todo 修改父级,修改level,只允许修改同级别
+	item.ParentId = parentId
+	item.ModifyTime = time.Now().Local()
+	cols := make([]string, 0)
+	cols = append(cols, "ParentId", "ClassifyName", "ModifyTime")
+	err = item.Update(cols)
+	if err != nil {
+		return
+	}
+	// todo 修改父级分类下相关的事件,找到当前分类的最小分类,把上一级的报告挪到最小分类上
+
+	return
+}
+
+// GetClassifyTreeRecursive 递归获取分类树形结构
+func GetClassifyTreeRecursive(list []*knowledge.KnowledgeClassifyItem, parentId int) []*knowledge.KnowledgeClassifyItem {
+	res := make([]*knowledge.KnowledgeClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Child = GetClassifyTreeRecursive(list, v.ClassifyId)
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// GetParentClassifyListByParentIdList
+// @Description: 递归获取父级分类信息,正常来讲只有三次
+// @author: Roc
+// @datetime 2024-06-19 13:23:33
+// @param parentClassifyIdList []int
+// @return list []*knowledge.KnowledgeClassifyItem
+// @return err error
+func GetParentClassifyListByParentIdList(parentClassifyIdList []int) (list []*knowledge.KnowledgeClassifyItem, err error) {
+	knowledgeObj := new(knowledge.KnowledgeClassify)
+	num := len(parentClassifyIdList)
+	if num <= 0 {
+		return
+	}
+	list, err = knowledgeObj.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 []*knowledge.KnowledgeClassifyItem
+// @param parentId int
+// @return []*knowledge.KnowledgeClassifyItem
+func GetClassifyListTreeRecursive(list []*knowledge.KnowledgeClassifyItem, parentId int) []*knowledge.KnowledgeClassifyItem {
+	res := make([]*knowledge.KnowledgeClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Child = GetClassifyListTreeRecursive(list, v.ClassifyId)
+			res = append(res, v)
+		}
+	}
+
+	// 前端的JP需要我这么返回
+	if len(res) <= 0 {
+		res = nil
+	}
+
+	return res
+}
+
+// BySortAndCreateTime 用来排序,先按Sort字段升序排序,若Sort相同,则按照CreateTime字段升序排序。
+type BySortAndCreateTime []*knowledge.KnowledgeClassifyItem
+
+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 []*knowledge.KnowledgeClassifyItem) {
+	sort.Sort(BySortAndCreateTime(classifyList))
+}

+ 846 - 0
services/knowledge/resource.go

@@ -0,0 +1,846 @@
+package knowledge
+
+import (
+	"errors"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/knowledge"
+	"eta_gn/eta_api/models/system"
+	"eta_gn/eta_api/services"
+	"eta_gn/eta_api/services/alarm_msg"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"html"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func AddResource(req *knowledge.AddReq, sysUser *system.Admin) (item *knowledge.KnowledgeResource, err error, errMsg string) {
+	var startTime time.Time
+	var endTime time.Time
+	if req.StartTime != "" {
+		startTime, err = time.ParseInLocation(utils.FormatDateTime, req.StartTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "开始时间格式不正确"
+			err = fmt.Errorf("开始时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	if req.EndTime != "" {
+		endTime, err = time.ParseInLocation(utils.FormatDateTime, req.EndTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "结束时间格式不正确"
+			err = fmt.Errorf("结束时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	isFile := 0
+	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			errMsg = "存在非法标签"
+			err = fmt.Errorf("存在非法标签, Err: " + e.Error())
+			return
+		}
+		content, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			errMsg = "内容去除前后空格失败"
+			err = fmt.Errorf("内容去除前后空格失败, 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)
+			return
+		}*/
+	} else {
+		if req.FileUrl == "" {
+			errMsg = "内容或文件不能为空"
+			err = fmt.Errorf("内容或文件不能为空")
+			return
+		}
+		isFile = 1
+	}
+
+	classifyObj := new(knowledge.KnowledgeClassify)
+	classifyItem, err := classifyObj.GetClassifyById(req.ClassifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			err = fmt.Errorf("分类不存在,Err:" + err.Error())
+			errMsg = "分类不存在"
+			return
+		}
+		errMsg = "获取分类失败"
+		err = fmt.Errorf("获取分类失败,Err:" + err.Error())
+		return
+	}
+	if req.ClassifyId != classifyItem.ClassifyId { // 如果传进来的分类id和数据库中查到的分类id不一致,说明传进来的分类id是错误的
+		errMsg = "错误分类"
+		err = fmt.Errorf("错误分类")
+		return
+	}
+
+	// todo 校验标签
+
+	item = new(knowledge.KnowledgeResource)
+	item.ClassifyId = req.ClassifyId
+	item.TagId = req.TagId
+	item.SourceFrom = req.SourceFrom
+	item.ResourceType = req.ResourceType
+	item.Title = req.Title
+	item.State = 1
+	item.Content = html.EscapeString(req.Content)
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	item.ResourceCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+	item.IsFile = isFile
+	item.FileUrl = req.FileUrl
+	//todo 内容前5行
+	//item.ContentSub = html.EscapeString(contentSub)
+	if !startTime.IsZero() {
+		item.StartTime = &startTime
+	} else {
+		item.StartTime = nil
+	}
+	if !endTime.IsZero() {
+		item.EndTime = &endTime
+	} else {
+		item.EndTime = nil
+	}
+	item.ModifyTime = time.Now()
+	item.AdminId = sysUser.AdminId
+	item.AdminRealName = sysUser.RealName
+	//item.LastModifyAdminId = sysUser.AdminId
+	//item.LastModifyAdminName = sysUser.RealName
+	item.ModifyTime = time.Now()
+	item.CreateTime = time.Now()
+
+	err = item.Add(item)
+	if err != nil {
+		errMsg = "保存失败"
+		err = fmt.Errorf("保存失败,Err:" + err.Error())
+		return
+	}
+	//todo 是否需要保存到es中
+	return
+}
+func EditResource(resourceInfo *knowledge.KnowledgeResource, req knowledge.EditReq, sysUser *system.Admin) (err error, errMsg string) {
+	errMsg = `保存失败`
+	var startTime time.Time
+	var endTime time.Time
+	if req.StartTime != "" {
+		startTime, err = time.ParseInLocation(utils.FormatDateTime, req.StartTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "开始时间格式不正确"
+			err = fmt.Errorf("开始时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	if req.EndTime != "" {
+		endTime, err = time.ParseInLocation(utils.FormatDateTime, req.EndTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "结束时间格式不正确"
+			err = fmt.Errorf("结束时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	if resourceInfo.IsFile == 0 {
+		if req.Content == "" {
+			errMsg = "内容不能为空"
+			err = fmt.Errorf("内容不能为空")
+			return
+		}
+	} else if resourceInfo.IsFile == 1 {
+		if req.FileUrl == "" {
+			errMsg = "文件地址不能为空"
+			err = fmt.Errorf("文件地址不能为空")
+			return
+		}
+	}
+	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			errMsg = "存在非法标签"
+			err = fmt.Errorf("存在非法标签, Err: " + e.Error())
+			return
+		}
+		content, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			errMsg = "内容去除前后空格失败"
+			err = fmt.Errorf("内容去除前后空格失败, 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)
+			return
+		}*/
+	}
+
+	classifyObj := new(knowledge.KnowledgeClassify)
+	classifyItem, err := classifyObj.GetClassifyById(req.ClassifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			err = fmt.Errorf("分类不存在,Err:" + err.Error())
+			errMsg = "分类不存在"
+			return
+		}
+		errMsg = "获取分类失败"
+		err = fmt.Errorf("获取分类失败,Err:" + err.Error())
+		return
+	}
+	if req.ClassifyId != classifyItem.ClassifyId { // 如果传进来的分类id和数据库中查到的分类id不一致,说明传进来的分类id是错误的
+		errMsg = "错误分类"
+		err = fmt.Errorf("错误分类")
+		return
+	}
+
+	resourceInfo.ClassifyId = req.ClassifyId
+	resourceInfo.SourceFrom = req.SourceFrom
+	resourceInfo.TagId = req.TagId
+	resourceInfo.Title = req.Title
+	if req.Content != "" {
+		resourceInfo.Content = html.EscapeString(req.Content)
+	}
+	if !startTime.IsZero() {
+		resourceInfo.StartTime = &startTime
+	}
+	if !endTime.IsZero() {
+		resourceInfo.EndTime = &endTime
+	}
+	resourceInfo.FileUrl = req.FileUrl
+	//resourceInfo.LastModifyAdminId = sysUser.AdminId
+	//resourceInfo.LastModifyAdminName = sysUser.RealName
+	resourceInfo.ModifyTime = time.Now()
+
+	updateCols := []string{"ClassifyId", "SourceFrom", "TagId", "Title", "Content", "StartTime", "EndTime", "ModifyTime", "FileUrl"}
+
+	// 修改报告的基本信息,以及报告的授权用户
+	err = resourceInfo.Update(updateCols)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// ImportResourceData
+// @Description: 数据导入
+// @author: Roc
+// @datetime 2024-08-01 11:27:21
+// @param path string
+// @param sysUser *system.Admin
+// @return successCount int
+// @return failCount int
+// @return err error
+// @return errMsg string
+func ImportResourceData(path string, resourceType int, sysUser *system.Admin) (successCount, failCount int, err error, errMsg string) {
+	// 错误信息
+	errMsgList := make([]string, 0)
+	// 操作记录
+	recordMap := make(map[string]string)
+	defer func() {
+		recordList := make([]*models.EdbinfoOpRecord, 0)
+		for tradeCode, remark := range recordMap {
+			recordList = append(recordList, &models.EdbinfoOpRecord{
+				TradeCode:  tradeCode,
+				Remark:     remark,
+				UserId:     sysUser.AdminId,
+				UserName:   sysUser.RealName,
+				CreateTime: time.Now(),
+			})
+		}
+		if len(recordList) > 0 {
+			go func() {
+				obj := models.EdbinfoOpRecord{}
+				_ = obj.MulCreate(recordList)
+			}()
+		}
+
+		// 错误信息记录
+		if len(errMsgList) > 0 {
+			utils.FileLog.Info("导入失败, errMsgList: %v", strings.Join(errMsgList, "\n"))
+		}
+	}()
+	errMsg = `导入失败`
+	xlFile, err := xlsx.OpenFile(path)
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+	if len(xlFile.Sheets) <= 0 {
+		errMsg = "导入模板异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	//导入失败数据
+	failDataList := make([]*knowledge.KnowledgeImportFail, 0)
+	var indexDataList []knowledge.ResourceImportData
+
+	// 模板校验,然后处理成标准化格式
+	for _, sheet := range xlFile.Sheets {
+		var tmpFailDataList []*knowledge.KnowledgeImportFail
+		var tmpIndexDataList []knowledge.ResourceImportData
+		rowList := sheet.Rows
+		if len(rowList) <= 0 {
+			errMsg = sheet.Name + "页异常"
+			err = errors.New(errMsg)
+			return
+		}
+
+		templateType := 1 // 模板类型
+		minCellNum := 6   // 模板最小列数
+		headerCell := rowList[0].Cells
+
+		// 确定模板
+		for _, v := range headerCell {
+			if v.String() == "导入模板2/Import Template 2" {
+				templateType = 2
+				minCellNum = 2
+				break
+			}
+		}
+
+		// 如果小于最少列数,则报错
+		if len(headerCell) < minCellNum {
+			errMsg = sheet.Name + "页模板异常"
+			err = errors.New(errMsg)
+			return
+		}
+		tmpIndexDataList, tmpFailDataList, err, errMsg = getDataByTemplateEvent(sheet, sysUser.AdminId, templateType)
+		if err != nil {
+			return
+		}
+		fmt.Println("tmpIndexDataList: ", tmpIndexDataList)
+		fmt.Println("tmpFailDataList: ", tmpFailDataList)
+		indexDataList = append(indexDataList, tmpIndexDataList...)
+		failDataList = append(failDataList, tmpFailDataList...)
+	}
+
+	if len(indexDataList) <= 0 {
+		if len(failDataList) > 0 {
+			errMsg = failDataList[0].Remark
+			err = errors.New(errMsg)
+		}
+		return
+	}
+
+	// 遍历获取所有分类
+	classifyNames := make([]string, 0)
+	for _, v := range indexDataList {
+		if v.ClassifyFirstName != "" {
+			classifyNames = append(classifyNames, v.ClassifyFirstName)
+		}
+		if v.ClassifySecondName != "" {
+			classifyNames = append(classifyNames, v.ClassifySecondName)
+		}
+		if v.ClassifyThirdName != "" {
+			classifyNames = append(classifyNames, v.ClassifyThirdName)
+		}
+	}
+	fmt.Println(classifyNames)
+	classifyList, err := knowledge.GetFullClassifyListByName(classifyNames)
+	if err != nil {
+		err = fmt.Errorf("获取分类数据失败 Err: %s", err.Error())
+		errMsg = "获取分类数据失败"
+		return
+	}
+	classifyNameMap := make(map[string]int)
+	for _, v := range classifyList {
+		name := fmt.Sprintf("%s/%s/%s", v.RootName, v.ParentName, v.ClassifyName)
+		classifyNameMap[name] = v.ClassifyId
+	}
+	// todo 获取所有tag
+	//tagNameMap := make(map[string]int)
+	//// TODO 成功数量超过20个指标,那就不导入了
+	/*	if len(targetMap) >= 100 {
+		failItem := new(knowledge.KnowledgeImportFail)
+		failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+		failItem.ClassifyName = classifyName
+		failItem.CreateDate = createDate
+		failItem.SecName = secName
+		failItem.Close = closeVal
+		failItem.Remark = "导入指标数量过多"
+		failItem.Frequency = frequency
+		failItem.Unit = unit
+		failDataList = append(failDataList, failItem)
+		//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+		continue
+	}*/
+
+	for _, v := range indexDataList {
+		// 判断分类是否存在
+		classifyName := fmt.Sprintf("%s/%s/%s", v.ClassifyFirstName, v.ClassifySecondName, v.ClassifyThirdName)
+		fmt.Println("classifyName: ", classifyName)
+		classifyId, ok := classifyNameMap[classifyName]
+		if !ok {
+			failItem := &knowledge.KnowledgeImportFail{
+				Title:          v.Title,
+				Content:        v.Content,
+				SourceFrom:     v.SourceFrom,
+				ClassifyFirst:  v.ClassifyFirstName,
+				ClassifySecond: v.ClassifySecondName,
+				ClassifyThird:  v.ClassifyThirdName,
+				ResourceType:   resourceType,
+				StartDate:      v.StartDate,
+				EndDate:        v.EndDate,
+				StartTime:      v.StartTime,
+				EndTime:        v.EndTime,
+				Remark:         "指标分类不存在",
+				SysUserId:      fmt.Sprint(sysUser.AdminId),
+				CreateTime:     time.Now(),
+			}
+			failDataList = append(failDataList, failItem)
+			continue
+
+		}
+
+		//todo 处理tag信息
+		/*tagId, ok := tagNameMap[v.Tag]
+		if !ok {
+			failItem := &knowledge.KnowledgeImportFail{
+				Title:          v.Title,
+				Content:        v.Content,
+				SourceFrom:     v.SourceFrom,
+				ClassifyFirst:  v.ClassifyFirstName,
+				ClassifySecond: v.ClassifySecondName,
+				ClassifyThird:  v.ClassifyThirdName,
+				ResourceType:   resourceType,
+				StartDate:      v.StartDate,
+				EndDate:        v.EndDate,
+				StartTime:      v.StartTime,
+				EndTime:        v.EndTime,
+				Remark:         "标签不存在",
+				SysUserId:      fmt.Sprint(sysUser.AdminId),
+		CreateTime:     time.Now(),
+			}
+			failDataList = append(failDataList, failItem)
+			continue
+		}*/
+
+		req := new(knowledge.AddReq)
+		req.Title = v.Title
+		req.Content = v.Content
+		req.SourceFrom = v.SourceFrom
+		req.ClassifyId = classifyId
+		//req.TagId = tagId
+		req.ResourceType = resourceType
+		if v.StartDate != "" {
+			req.StartTime = fmt.Sprintf("%s %s", v.StartDate, v.StartTime)
+		}
+		if v.EndDate != "" {
+			req.EndTime = fmt.Sprintf("%s %s", v.EndDate, v.EndTime)
+		}
+		tmpErr, msg := addExcelResource(req, sysUser)
+		if tmpErr != nil {
+			failItem := &knowledge.KnowledgeImportFail{
+				Title:          v.Title,
+				Content:        v.Content,
+				SourceFrom:     v.SourceFrom,
+				ClassifyFirst:  v.ClassifyFirstName,
+				ClassifySecond: v.ClassifySecondName,
+				ClassifyThird:  v.ClassifyThirdName,
+				ResourceType:   resourceType,
+				StartDate:      v.StartDate,
+				EndDate:        v.EndDate,
+				StartTime:      v.StartTime,
+				EndTime:        v.EndTime,
+				Remark:         "新增事件失败:" + msg,
+				SysUserId:      fmt.Sprint(sysUser.AdminId),
+				CreateTime:     time.Now(),
+			}
+			failDataList = append(failDataList, failItem)
+			continue
+		} else {
+			successCount++
+		}
+	}
+
+	// 失败数量
+	failCount = len(failDataList)
+	//fmt.Println("failDataList:", len(failDataList))
+	if failCount > 0 {
+		failObj := new(knowledge.KnowledgeImportFail)
+		//先删除导入失败记录
+		_ = failObj.Delete(sysUser.AdminId)
+
+		// 批量添加导入失败记录
+		err = failObj.MultiAdd(failDataList)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("导入数据 新增失败记录失败,Err:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增失败记录失败:Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+
+		{
+			//错误信息字符串切片,最后作为发送邮件通知使用
+			failContents := make([]string, 0)
+			for _, v := range failDataList {
+				failContents = append(failContents, fmt.Sprint(v.Title, "导入失败:", v.Remark))
+			}
+			utils.FileLog.Info("导入数据 存在部分数据导入失败:" + strings.Join(failContents, ";"))
+			//导入失败的话,最后统一邮件提醒就好啦,不需要每次都去提醒
+			go alarm_msg.SendAlarmMsg("导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), 3)
+		}
+	}
+
+	return
+}
+
+// getDataByTemplateEvent
+func getDataByTemplateEvent(sheet *xlsx.Sheet, sysUserId, resourceType int) (indexDataList []knowledge.ResourceImportData, failDataList []*knowledge.KnowledgeImportFail, err error, errMsg string) {
+	fmt.Println("sheet name: ", sheet.Name)
+	indexDataList = make([]knowledge.ResourceImportData, 0)
+	failDataList = make([]*knowledge.KnowledgeImportFail, 0)
+
+	//遍历行读取
+	maxRow := sheet.MaxRow
+	fmt.Println("maxRow:", maxRow)
+
+	// 表头信息
+	if maxRow <= 3 {
+		errMsg = "模板异常1"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 表头校验
+	{
+		headerRow := sheet.Row(1)
+		cells := headerRow.Cells
+		fmt.Println("cells:", len(cells))
+		if len(cells) < 11 {
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+		// 循环打印每个单元格
+		for k, v := range cells {
+			fmt.Println("第", k, "个单元格", v.Value)
+		}
+		templateFail := false
+		if cells[0].Value != "开始时间" {
+			templateFail = true
+		}
+		if cells[2].Value != "结束时间(非必填)" {
+			templateFail = true
+		}
+		if cells[4].Value != "标题" {
+			templateFail = true
+		}
+		if cells[5].Value != "正文" {
+			templateFail = true
+		}
+		if cells[6].Value != "来源" {
+			templateFail = true
+		}
+		if cells[7].Value != "一级分类" {
+			templateFail = true
+		}
+		if cells[8].Value != "二级分类" {
+			templateFail = true
+		}
+		if cells[9].Value != "三级分类" {
+			templateFail = true
+		}
+		if cells[10].Value != "标签" {
+			templateFail = true
+		}
+
+		headerRow2 := sheet.Row(2)
+		cells2 := headerRow2.Cells
+		for k, v := range cells2 {
+			fmt.Println("2第", k, "个单元格", v.Value)
+		}
+		if len(cells2) < 11 {
+			templateFail = true
+		} else {
+			if cells2[0].Value != "日期" {
+				templateFail = true
+			}
+			if cells2[1].Value != "时间" {
+				templateFail = true
+			}
+			if cells2[2].Value != "日期" {
+				templateFail = true
+			}
+			if cells2[3].Value != "时间" {
+				templateFail = true
+			}
+		}
+
+		if templateFail {
+			fmt.Println("头部信息校验失败")
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	for i := 3; i < maxRow; i++ {
+		row := sheet.Row(i)
+		cells := row.Cells
+		lenCell := len(cells)
+		for k, v := range cells {
+			fmt.Println("数据第行", i, "第", k, "个单元格", v.Value)
+		}
+		// 过滤空白行
+		if lenCell <= 0 {
+			continue
+		}
+		fmt.Println("lenCell:", lenCell)
+		if lenCell < 12 {
+			if cells[0].Value == `` {
+				continue
+			}
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+		startDate := strings.TrimSpace(cells[0].Value) //开始时间
+		startTime := strings.TrimSpace(cells[1].Value) //开始时间
+		endDate := strings.TrimSpace(cells[2].Value)   //结束时间
+		endTime := strings.TrimSpace(cells[3].Value)   //结束时间
+		startDateO := startDate
+		endDateO := endDate
+		startTimeO := startTime
+		endTimeO := endTime
+		title := strings.TrimSpace(cells[4].Value)         //标题
+		content := strings.TrimSpace(cells[5].Value)       //正文
+		sourceFrom := strings.TrimSpace(cells[6].Value)    //来源
+		classifyName1 := strings.TrimSpace(cells[7].Value) //一级分类
+		classifyName2 := strings.TrimSpace(cells[8].Value) //二级分类
+		classifyName3 := strings.TrimSpace(cells[9].Value) //三级分类
+		tag := strings.TrimSpace(cells[10].Value)          //标签
+
+		if title == "" || content == "" || sourceFrom == "" || classifyName1 == "" { //过滤空白行
+			continue
+		}
+
+		// 校验日期格式
+		if startDate != "" {
+			// 判断9:09:14日期格式是否正确
+			// 判断是否是数字
+			_, tmpErr := strconv.Atoi(startDate)
+			if tmpErr == nil {
+				startDate = utils.ExcelDateToDate(startDate).Format(utils.FormatDate)
+				fmt.Println(startDate)
+			} else {
+				startDate, tmpErr = getExcelDate(startDate)
+				if tmpErr != nil {
+					failDataList = append(failDataList, &knowledge.KnowledgeImportFail{
+						//Id:           0,
+						Title:          title,
+						Content:        content,
+						SourceFrom:     sourceFrom,
+						ClassifyFirst:  classifyName1,
+						ClassifySecond: classifyName2,
+						ClassifyThird:  classifyName3,
+						ResourceType:   resourceType,
+						StartDate:      startDateO,
+						EndDate:        endDateO,
+						StartTime:      startTimeO,
+						EndTime:        endTimeO,
+						Remark:         "日期格式异常",
+						SysUserId:      strconv.Itoa(sysUserId),
+						CreateTime:     time.Now(),
+					})
+					continue
+				}
+			}
+			if startTime != "" {
+				// 判断9:09:14日期格式是否正确
+				startTimeVal, tmpErr := strconv.ParseFloat(startTime, 64)
+				if tmpErr == nil {
+					startTime = utils.ExcelTimeToTime(startTimeVal).Format(utils.FormatTime)
+					fmt.Println(startTime)
+				} else {
+					_, tmpErr = time.Parse(utils.FormatDateTime, startDate+" "+startTime)
+					if tmpErr != nil {
+						failDataList = append(failDataList, &knowledge.KnowledgeImportFail{
+							//Id:           0,
+							Title:          title,
+							Content:        content,
+							SourceFrom:     sourceFrom,
+							ClassifyFirst:  classifyName1,
+							ClassifySecond: classifyName2,
+							ClassifyThird:  classifyName3,
+							ResourceType:   resourceType,
+							StartDate:      startDateO,
+							EndDate:        endDateO,
+							StartTime:      startTimeO,
+							EndTime:        endTimeO,
+							Remark:         "日期格式异常",
+							SysUserId:      strconv.Itoa(sysUserId),
+							CreateTime:     time.Now(),
+						})
+						continue
+					}
+				}
+			}
+		}
+		if endDate != "" {
+			_, tmpErr := strconv.Atoi(endDate)
+			if tmpErr == nil {
+				endDate = utils.ExcelDateToDate(endDate).Format(utils.FormatDate)
+			} else {
+				endDate, tmpErr = getExcelDate(endDate)
+				if tmpErr != nil {
+					failDataList = append(failDataList, &knowledge.KnowledgeImportFail{
+						//Id:           0,
+						Title:          title,
+						Content:        content,
+						SourceFrom:     sourceFrom,
+						ClassifyFirst:  classifyName1,
+						ClassifySecond: classifyName2,
+						ClassifyThird:  classifyName3,
+						ResourceType:   resourceType,
+						StartDate:      startDateO,
+						EndDate:        endDateO,
+						StartTime:      startTimeO,
+						EndTime:        endTimeO,
+						Remark:         "日期格式异常",
+						SysUserId:      strconv.Itoa(sysUserId),
+						CreateTime:     time.Now(),
+					})
+					continue
+				}
+			}
+			if endTime != "" {
+				// 判断9:09:14日期格式是否正确
+				endTimeVal, tmpErr := strconv.ParseFloat(endTime, 64)
+				if tmpErr == nil {
+					endTime = utils.ExcelTimeToTime(endTimeVal).Format(utils.FormatTime)
+					fmt.Println(endTime)
+				} else {
+					_, tmpErr = time.Parse(utils.FormatDateTime, endDate+" "+endTime)
+					if tmpErr != nil {
+						failDataList = append(failDataList, &knowledge.KnowledgeImportFail{
+							//Id:           0,
+							Title:          title,
+							Content:        content,
+							SourceFrom:     sourceFrom,
+							ClassifyFirst:  classifyName1,
+							ClassifySecond: classifyName2,
+							ClassifyThird:  classifyName3,
+							ResourceType:   resourceType,
+							StartDate:      startDate,
+							EndDate:        endDate,
+							StartTime:      startTimeO,
+							EndTime:        endTimeO,
+							Remark:         "日期格式异常",
+							SysUserId:      strconv.Itoa(sysUserId),
+							CreateTime:     time.Now(),
+						})
+						continue
+					}
+				}
+			}
+		}
+
+		resourceItem := knowledge.ResourceImportData{
+			Title:              title,
+			StartDate:          startDate,
+			EndDate:            endDate,
+			StartTime:          startTime,
+			EndTime:            endTime,
+			Content:            content,
+			Tag:                tag,
+			SourceFrom:         sourceFrom,
+			ClassifyFirstName:  classifyName1,
+			ClassifySecondName: classifyName2,
+			ClassifyThirdName:  classifyName3,
+		}
+		indexDataList = append(indexDataList, resourceItem)
+	}
+	return
+}
+
+func getExcelDate(createDate string) (newCreateDate string, err error) {
+	if strings.Contains(createDate, "-") {
+		//如果是带有 - 的普通日期格式文本
+		_, err = time.Parse("2006-1-2", createDate)
+		if err == nil {
+			newCreateDate = createDate
+		}
+	} else if strings.Contains(createDate, "/") {
+		//如果是带有 / 的普通日期格式文本
+		createDateTime, timeErr := time.Parse("2006/1/2", createDate)
+		if timeErr != nil {
+			err = timeErr
+		} else {
+			newCreateDate = createDateTime.Format("2006-01-02")
+		}
+	} else {
+		//可能是excel的日期格式
+		_, tmpErr := strconv.Atoi(createDate)
+		if tmpErr != nil {
+			err = tmpErr
+		} else {
+			newCreateDate = utils.ConvertToFormatDay(createDate) //录入日期
+		}
+	}
+
+	return
+}
+
+func addExcelResource(req *knowledge.AddReq, sysUser *system.Admin) (err error, errMsg string) {
+	var startTime time.Time
+	var endTime time.Time
+	if req.StartTime != "" {
+		startTime, err = time.ParseInLocation(utils.FormatDateTime, req.StartTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "开始时间格式不正确"
+			err = fmt.Errorf("开始时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	if req.EndTime != "" {
+		endTime, err = time.ParseInLocation(utils.FormatDateTime, req.EndTime, time.Local)
+		if err != nil { // 如果时间格式不正确,则返回错误信息
+			errMsg = "结束时间格式不正确"
+			err = fmt.Errorf("结束时间格式不正确,Err:" + err.Error())
+			return
+		}
+	}
+	item := new(knowledge.KnowledgeResource)
+	item.ClassifyId = req.ClassifyId
+	item.TagId = req.TagId
+	item.SourceFrom = req.SourceFrom
+	item.ResourceType = req.ResourceType
+	item.Title = req.Title
+	item.State = 1
+	item.Content = html.EscapeString(req.Content)
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	item.ResourceCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+	//todo 内容前5行
+	//item.ContentSub = html.EscapeString(contentSub)
+
+	if !startTime.IsZero() {
+		item.StartTime = &startTime
+	}
+	if !endTime.IsZero() {
+		item.EndTime = &endTime
+	}
+	item.ModifyTime = time.Now()
+	item.AdminId = sysUser.AdminId
+	item.AdminRealName = sysUser.RealName
+	//item.LastModifyAdminId = sysUser.AdminId
+	//item.LastModifyAdminName = sysUser.RealName
+	item.ModifyTime = time.Now()
+	item.CreateTime = time.Now()
+
+	err = item.Add(item)
+	if err != nil {
+		errMsg = "保存失败"
+		err = fmt.Errorf("保存失败,Err:" + err.Error())
+		return
+	}
+	//todo 是否需要保存到es中
+	return
+}

+ 248 - 0
services/knowledge/tag.go

@@ -0,0 +1,248 @@
+package knowledge
+
+import (
+	"errors"
+	"eta_gn/eta_api/models/knowledge"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// MoveKnowledgeTag 移动标签
+func MoveKnowledgeTag(req knowledge.TagMoveReq) (err error, errMsg string) {
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	TagId := req.TagId
+	prevTagId := req.PrevTagId
+	nextTagId := req.NextTagId
+
+	//如果有传入 上一个兄弟节点标签id
+	var (
+		TagInfo *knowledge.KnowledgeTag
+		prevTag *knowledge.KnowledgeTag
+		nextTag *knowledge.KnowledgeTag
+
+		prevSort int
+		nextSort int
+	)
+
+	// 移动对象为标签, 判断权限
+	TagInfo, err = knowledgeObj.GetTagById(TagId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			errMsg = "标签不存在, 请刷新页面"
+			err = fmt.Errorf("获取标签信息失败,Err:" + err.Error())
+			return
+		}
+		errMsg = "移动失败"
+		err = fmt.Errorf("获取标签信息失败,Err:" + err.Error())
+		return
+	} else if TagInfo.TagId == 0 {
+		errMsg = "标签不存在, 请刷新页面"
+		err = fmt.Errorf("获取标签信息失败,Err:" + err.Error())
+		return
+	}
+	if prevTagId > 0 {
+		prevTag, err = knowledgeObj.GetTagById(prevTagId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "上一个标签不存在, 请刷新页面"
+				err = fmt.Errorf("获取标签信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取上一个兄弟节点标签信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevTag.Sort
+	}
+
+	if nextTagId > 0 {
+		//下一个兄弟节点
+		nextTag, err = knowledgeObj.GetTagById(nextTagId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "下一个标签不存在, 请刷新页面"
+				err = fmt.Errorf("获取标签信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取下一个兄弟节点标签信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextTag.Sort
+	}
+
+	err, errMsg = moveKnowledgeTag(TagInfo, prevTag, nextTag, prevSort, nextSort, TagInfo.ResourceType)
+	return
+}
+
+// moveKnowledgeTag 移动标签
+func moveKnowledgeTag(TagInfo, prevTag, nextTag *knowledge.KnowledgeTag, prevSort, nextSort, resourceType int) (err error, errMsg string) {
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	updateCol := make([]string, 0)
+
+	if prevSort > 0 {
+		//如果是移动在两个兄弟节点之间
+		if nextSort > 0 {
+			//下一个兄弟节点
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevSort == nextSort || prevSort == TagInfo.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+
+				//变更标签
+				if prevTag != nil {
+					_ = knowledgeObj.UpdateTagSortByParentId(prevTag.TagId, prevTag.Sort, updateSortStr, resourceType)
+				} else {
+					_ = knowledgeObj.UpdateTagSortByParentId(0, prevSort, updateSortStr, resourceType)
+				}
+
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextSort-prevSort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+
+					//变更标签
+					if prevTag != nil {
+						_ = knowledgeObj.UpdateTagSortByParentId(prevTag.TagId, prevSort, updateSortStr, resourceType)
+					} else {
+						_ = knowledgeObj.UpdateTagSortByParentId(0, prevSort, updateSortStr, resourceType)
+					}
+
+				}
+			}
+		}
+
+		TagInfo.Sort = prevSort + 1
+		TagInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else {
+		// 拖动到父级标签的第一位
+		firstPermission, tmpErr := knowledgeObj.GetFirstTag()
+		if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取获取当前父级标签下的排序第一条的标签信息失败,Err:" + tmpErr.Error())
+			return
+		}
+
+		//如果该标签下存在其他标签,且第一个其他标签的排序等于0,那么需要调整排序
+		if firstPermission != nil && firstPermission.TagId != 0 && firstPermission.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = knowledgeObj.UpdateTagSortByParentId(firstPermission.TagId-1, 0, updateSortStr, resourceType)
+		}
+
+		TagInfo.Sort = 0 //那就是排在第一位
+		TagInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = TagInfo.Update(updateCol)
+		if err != nil {
+			errMsg = "移动失败"
+			err = fmt.Errorf("修改失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// AddKnowledgeTag
+// @Description: 添加报告标签
+// @author: Roc
+// @datetime 2024-06-17 11:01:21
+// @param TagName string
+// @param parentId int
+// @param chartPermissionIdList []int
+// @return err error
+// @return errMsg string
+// @return isSendEmail bool
+func AddKnowledgeTag(TagName string, resourceType int) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `添加失败`
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	_, err = knowledgeObj.GetTagByName(TagName, resourceType)
+	if err != nil && !utils.IsErrNoRow(err) {
+		errMsg = "获取标签信息失败"
+		return
+	}
+
+	if err == nil {
+		errMsg = "标签名称:" + TagName + "已存在"
+		isSendEmail = false
+		err = errors.New(errMsg)
+		return
+	} else {
+		err = nil
+	}
+
+	nowTime := time.Now().Local()
+	tag := new(knowledge.KnowledgeTag)
+
+	maxSort, err := tag.GetMaxSort()
+	if err != nil {
+		errMsg = "操作失败"
+		err = errors.New("查询品种排序失败, Err: " + err.Error())
+		return
+	}
+	tag.TagName = TagName
+	tag.CreateTime = nowTime
+	tag.ModifyTime = nowTime
+	tag.Sort = maxSort + 1
+	tag.ResourceType = resourceType
+
+	err = knowledgeObj.Add(tag)
+	if err != nil {
+		return
+	}
+	fmt.Println("tag.TagId:")
+	fmt.Println(tag.TagId)
+	return
+}
+
+func EditKnowledgeTag(TagId, parentId int, TagName string) (err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = `修改失败`
+	knowledgeObj := new(knowledge.KnowledgeTag)
+	item, err := knowledgeObj.GetTagById(TagId)
+	if err != nil {
+		errMsg = "获取标签信息失败"
+		if utils.IsErrNoRow(err) {
+			errMsg = "标签不存在, 或已被删除"
+			isSendEmail = false
+		}
+		return
+	}
+
+	//originName := item.TagName
+
+	// 重名校验
+	existName, e := knowledgeObj.GetTagByName(TagName, item.ResourceType)
+	if e != nil && !utils.IsErrNoRow(e) {
+		errMsg = "标签名称已存在"
+		err = errors.New("获取重名标签失败, Err: " + err.Error())
+		return
+	}
+	if existName != nil && existName.TagId > 0 && existName.TagId != item.TagId {
+		errMsg = "标签名称:" + TagName + "已存在"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	item.TagName = TagName
+
+	// ETA1.8.3:不允许修改上级标签  2024-6-17 13:21:01
+	//
+	// todo 修改父级,修改level,只允许修改同级别
+	item.ModifyTime = time.Now().Local()
+	cols := make([]string, 0)
+	cols = append(cols, "TagName", "ModifyTime")
+	err = item.Update(cols)
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 21 - 0
utils/common.go

@@ -2688,3 +2688,24 @@ func AppendPars(pars []interface{}, values ...interface{}) []interface{} {
 	}
 	return append(pars, values...)
 }
+
+func ExcelDateToDate(excelDate string) time.Time {
+	excelTime := time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC)
+	var days, _ = strconv.Atoi(excelDate)
+	return excelTime.Add(time.Second * time.Duration(days*86400))
+}
+
+// ExcelTimeToTime 将 Excel 中的时间浮点数转换为 Go 的 time.Time(只包含时间部分)
+func ExcelTimeToTime(excelTime float64) time.Time {
+	// Excel 中的时间是从 0(1900-01-00 00:00:00,注意 Excel 的日期基准问题)开始的浮点数天数
+	// 这里我们假设日期部分为 0(即只处理时间),并创建一个基准时间(可以是任意日期,因为我们只关心时间)
+	baseTime := time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC) // 注意:这里使用 1899-12-30 是因为 Excel 的日期系统从这一天开始(考虑到 1900 年错误)
+
+	// 计算小时、分钟和秒
+	hours := int(excelTime * 24)
+	minutes := int(math.Mod(excelTime*24*60, 60))
+	seconds := int(math.Mod(excelTime*24*60*60, 60))
+
+	// 创建一个只包含时间部分的 time.Time 对象
+	return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), hours, minutes, seconds, 0, time.UTC)
+}