Browse Source

Merge remote-tracking branch 'origin/master' into eta/2.4.5

# Conflicts:
#	models/business_conf.go
Roc 1 month ago
parent
commit
f551bf0302
42 changed files with 2333 additions and 144 deletions
  1. 83 3
      controllers/data_manage/chart_info.go
  2. 6 2
      controllers/data_manage/correlation/correlation_chart_info.go
  3. 6 2
      controllers/data_manage/cross_variety/chart_info.go
  4. 9 0
      controllers/data_manage/edb_info.go
  5. 229 0
      controllers/data_manage/excel/excel_info.go
  6. 6 2
      controllers/data_manage/future_good/future_good_chart_info.go
  7. 6 2
      controllers/data_manage/line_equation/line_chart_info.go
  8. 6 2
      controllers/data_manage/line_feature/chart_info.go
  9. 6 2
      controllers/data_manage/range_analysis/chart_info.go
  10. 233 0
      controllers/eta_forum/eta_forum.go
  11. 14 12
      controllers/report_chapter.go
  12. 1 2
      controllers/resource.go
  13. 34 1
      controllers/user_login.go
  14. 15 11
      controllers/voice.go
  15. 54 34
      models/business_conf.go
  16. 14 3
      models/data_manage/chart_info.go
  17. 33 0
      models/data_manage/data_manage_permission/excel.go
  18. 1 0
      models/data_manage/edb_info.go
  19. 26 0
      models/data_manage/excel/excel_info.go
  20. 36 29
      models/data_manage/excel/response/excel_info.go
  21. 1 0
      models/data_manage/my_chart.go
  22. 3 0
      models/db.go
  23. 18 8
      models/report.go
  24. 6 0
      models/system/sys_group.go
  25. 54 9
      routers/commentsRouter.go
  26. 6 0
      routers/router.go
  27. 592 0
      services/data/area_graph/processor_business_logic.go
  28. 27 0
      services/data/area_graph/processor_factory.go
  29. 11 1
      services/data/chart_info.go
  30. 1 1
      services/data/chart_info_elastic.go
  31. 6 1
      services/data/chart_info_excel_balance.go
  32. 11 4
      services/data/excel/excel_info.go
  33. 7 5
      services/document_manage_service/document_manage_service.go
  34. 198 8
      services/elastic/elastic.go
  35. 217 0
      services/eta_forum/chart_collect.go
  36. 88 0
      services/eta_forum/eta_forum_hub_lib.go
  37. 66 0
      services/excel_info.go
  38. 33 0
      services/system.go
  39. 19 0
      utils/common.go
  40. 15 0
      utils/config.go
  41. 1 0
      utils/constants.go
  42. 135 0
      utils/date_util.go

+ 83 - 3
controllers/data_manage/chart_info.go

@@ -2,6 +2,7 @@ package data_manage
 
 import (
 	"encoding/json"
+	"errors"
 	"eta/eta_mobile/controllers"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/models/data_manage"
@@ -9,6 +10,7 @@ import (
 	"eta/eta_mobile/models/system"
 	"eta/eta_mobile/services"
 	"eta/eta_mobile/services/data"
+	"eta/eta_mobile/services/data/area_graph"
 	"eta/eta_mobile/services/data/data_manage_permission"
 	"eta/eta_mobile/services/data/excel"
 	etaTrialService "eta/eta_mobile/services/eta_trial"
@@ -1218,6 +1220,16 @@ func (this *ChartInfoController) ChartInfoDetail() {
 			}
 		}
 
+		// 面积图 面积堆积 数据处理
+		if chartInfo.ChartType == utils.CHART_TYPE_AREA {
+			err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+			if err != nil {
+				br.Msg = "面积图处理失败"
+				br.ErrMsg = errMsg
+				return
+			}
+		}
+
 		// 图表的指标来源
 		sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
 		chartInfo.ChartSource = strings.Join(sourceNameList, ",")
@@ -1249,6 +1261,60 @@ func (this *ChartInfoController) ChartInfoDetail() {
 	br.Data = resp
 }
 
+func fillAreaGraphData(extraConfigStr string, edbDataList []*data_manage.ChartEdbInfoMapping) (err error, errMsg string) {
+
+	var tmpConfig data_manage.AreaExtraConf
+	if extraConfigStr != `` {
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpConfig)
+		if err != nil {
+			errMsg = "面积图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		if tmpConfig.StandardEdbInfoId <= 0 {
+			utils.FileLog.Info(`面积图未开启面积堆积`)
+			return
+		}
+	}
+	if tmpConfig.IsHeap == 1 {
+		standardIndexMap := make(map[string]*data_manage.EdbDataList)
+		var startDate, endDate string
+		for _, v := range edbDataList {
+			// 判断是否为基准指标
+			if v.EdbInfoId == tmpConfig.StandardEdbInfoId {
+				if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+					startDate = dataList[0].DataTime
+					endDate = dataList[len(dataList)-1].DataTime
+					for _, dataObject := range dataList {
+						standardIndexMap[dataObject.DataTime] = dataObject
+					}
+				}
+				break
+			}
+		}
+		strategy, err := area_graph.CreateStrategy(tmpConfig.NullDealWay)
+		if err != nil {
+			return err, "创建空值处理器失败"
+		}
+		err = strategy.Deal(tmpConfig, edbDataList, standardIndexMap, startDate, endDate)
+		if err != nil {
+			return err, err.Error()
+		}
+
+		// 时间戳处理
+		for _, mapping := range edbDataList {
+			if dataList, ok := mapping.DataList.([]*data_manage.EdbDataList); ok {
+				for _, dataInfo := range dataList {
+					toFormatTime := utils.StringToFormatTime(dataInfo.DataTime, utils.FormatDate)
+					dataInfo.DataTimestamp = toFormatTime.UnixMilli()
+				}
+			}
+		}
+	}
+
+	return nil, ""
+}
+
 // PreviewChartInfoDetail
 // @Title 获取图表详情(预览)
 // @Description 获取图表详情接口(预览)
@@ -2153,7 +2219,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 		showSysId = sysUser.AdminId
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2262,7 +2328,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
@@ -2272,7 +2338,10 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 			if currClassify, ok := chartClassifyMap[v.ChartClassifyId]; ok {
 				tmp.HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(v.IsJoinPermission, currClassify.IsJoinPermission, v.ChartInfoId, v.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
 			}
-
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}
@@ -2738,6 +2807,17 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartClassifyParent)
 	}
 	chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartViewClassify)
+
+	// 面积图 面积堆积 数据处理
+	if chartInfo.ChartType == utils.CHART_TYPE_AREA {
+		err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "面积图面积堆积数据处理失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	resp.EdbInfoList = edbList
 	//判断是否需要展示英文标识
 	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, chartInfo.Source, chartInfo.ChartType)

+ 6 - 2
controllers/data_manage/correlation/correlation_chart_info.go

@@ -1795,7 +1795,7 @@ func (this *CorrelationChartInfoController) SearchByEs() {
 		sourceList = append(sourceList, source)
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1848,13 +1848,17 @@ func (this *CorrelationChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 6 - 2
controllers/data_manage/cross_variety/chart_info.go

@@ -1433,7 +1433,7 @@ func (c *ChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_CROSS_HEDGING}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1486,13 +1486,17 @@ func (c *ChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 9 - 0
controllers/data_manage/edb_info.go

@@ -3416,6 +3416,15 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 	for i := 0; i < edbInfoListLen; i++ {
 		edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
 		classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
+		// 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
+		if keyWord == "" {
+			if this.Lang == utils.ZhLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbName
+			}
+			if this.Lang == utils.EnLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
+			}
+		}
 	}
 
 	// 当前列表中的分类map

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

@@ -6,6 +6,7 @@ import (
 	"eta/eta_mobile/controllers"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/models/data_manage"
+	excelPermissionModel "eta/eta_mobile/models/data_manage/data_manage_permission"
 	excel3 "eta/eta_mobile/models/data_manage/excel"
 	"eta/eta_mobile/models/data_manage/excel/request"
 	"eta/eta_mobile/models/data_manage/excel/response"
@@ -13,6 +14,7 @@ import (
 	"eta/eta_mobile/services/data"
 	"eta/eta_mobile/services/data/data_manage_permission"
 	excel2 "eta/eta_mobile/services/data/excel"
+	"eta/eta_mobile/services/elastic"
 	"eta/eta_mobile/services/excel"
 	"eta/eta_mobile/utils"
 	"fmt"
@@ -2756,3 +2758,230 @@ func (c *ExcelInfoController) GetEdbSource() {
 	br.Ret = 200
 	br.Success = true
 }
+
+// SearchByEs
+// @Title ES搜索
+// @Description ES搜索
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   Source   query   int  true       "格来源,1:excel插件的表格,2:自定义表格,3:混合表格,默认:1"
+// @Param   IsShowMe   query   bool  false       "是否只看我的,true、false"
+// @Param   IsShare   query   bool  false       "是否只看我的,true、false"
+// @Success 200 {object} response.ExcelListResp
+// @router /excel_info/search_by_es [get]
+func (c *ExcelInfoController) SearchByEs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	source, _ := c.GetInt("Source")
+	if source <= 0 {
+		source = utils.EXCEL_DEFAULT
+	}
+	isShowMe, _ := c.GetBool("IsShowMe")
+	isShare, _ := c.GetBool("IsShare")
+	keyword := c.GetString("KeyWord")
+	if keyword == `` {
+		keyword = c.GetString("Keyword")
+	}
+
+	var total, startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize15
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	// 平衡表的查询条件
+	var condBalance string
+	var parsBalance []interface{}
+	if source == utils.BALANCE_TABLE {
+		condBalance += ` AND source = ? AND parent_id = 0 AND balance_type = 0` // 只显示动态表的一级表(不显示子表和静态表)
+		parsBalance = append(parsBalance, source)
+	}
+
+	// 可见性过滤
+	var queryIds, exceptIds []int
+	{
+		unauthorized, e := excelPermissionModel.GetExcelInfoDataNoPermissionByUserId(sysUser.AdminId, source)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取无权限表格失败, %v", e)
+			return
+		}
+		if len(unauthorized) > 0 {
+			for _, v := range unauthorized {
+				id, _ := strconv.Atoi(v.DataId)
+				if id == 0 {
+					continue
+				}
+				exceptIds = append(exceptIds, id)
+			}
+		}
+	}
+
+	// 自定义分析表
+	var queryAdminId int
+	if source == utils.CUSTOM_ANALYSIS_TABLE {
+		// 自定义分析共享表格
+		if isShare {
+			var kw string
+			if keyword != "" {
+				kw = fmt.Sprint("%", kw, "%")
+			}
+			// 查询我分享的/分享给我的表格
+			excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, sysUser.AdminId, kw)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+				return
+			}
+			var excelIds []int
+			for _, v := range excels {
+				id := int(v.ExcelInfoId)
+				if !utils.InArrayByInt(excelIds, id) {
+					excelIds = append(excelIds, id)
+					continue
+				}
+			}
+			if len(excelIds) == 0 {
+				list := make([]*excel3.SearchExcelInfo, 0)
+				resp := response.SearchExcelListResp{
+					Paging: page,
+					List:   list,
+				}
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+			queryIds = excelIds
+		} else {
+			// 非共享只看我的
+			isShowMe = true
+		}
+	}
+
+	// 获取所有有权限的指标和分类
+	permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserExcelAndClassifyPermissionList(c.SysUser.AdminId, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+		return
+	}
+	hasCheck := make(map[int]bool)
+	if isShowMe {
+		// 平衡表查询有权限的表格IDs
+		if source == utils.BALANCE_TABLE {
+			excelIds, e := services.GetBalanceExcelIdsByAdminId(sysUser.AdminId, condBalance, parsBalance, permissionEdbIdList, permissionClassifyIdList)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取平衡表有权限的表格IDs失败, %v", e)
+				return
+			}
+			if len(excelIds) == 0 {
+				list := make([]*excel3.SearchExcelInfo, 0)
+				resp := response.SearchExcelListResp{
+					Paging: page,
+					List:   list,
+				}
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+			queryIds = excelIds
+		} else {
+			queryAdminId = sysUser.AdminId
+		}
+	}
+
+	// es搜索表格
+	t, list, e := elastic.SearchExcelInfoData(utils.EsExcelIndexName, keyword, source, queryAdminId, queryIds, exceptIds, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("搜索表格失败, %v", e)
+		return
+	}
+	total = int(t)
+
+	if len(list) > 0 {
+		classifyIdList := make([]int, 0)
+		for _, v := range list {
+			classifyIdList = append(classifyIdList, v.ExcelClassifyId)
+
+		}
+		classifyMap := make(map[int]*excel3.ExcelClassify)
+
+		// 分类信息
+		{
+			classifyList, err := excel3.GetClassifyByIdList(classifyIdList)
+			if err != nil {
+				br.Msg = "获取表格列表信息失败"
+				br.ErrMsg = "获取表格分类列表数据失败,Err:" + err.Error()
+				return
+			}
+			for _, v := range classifyList {
+				classifyMap[v.ExcelClassifyId] = v
+			}
+		}
+
+		for k, v := range list {
+			// 数据权限
+			if authCheck, ok1 := hasCheck[v.ExcelInfoId]; ok1 {
+				v.HaveOperaAuth = authCheck
+			} else {
+				if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+					v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+				}
+			}
+			if v.Source == utils.BALANCE_TABLE {
+				// 处理按钮权限和编辑状态
+				markStatus, err := services.UpdateExcelEditMark(v.ExcelInfoId, sysUser.AdminId, 2, sysUser.RealName)
+				if err != nil {
+					br.Msg = "查询标记状态失败"
+					br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+					return
+				}
+				if markStatus.Status == 0 {
+					list[k].CanEdit = true
+				} else {
+					list[k].Editor = markStatus.Editor
+				}
+
+				// excel表格按钮权限
+				list[k].Button = excel2.GetBalanceExcelInfoOpButton(sysUser.AdminId, v.SysUserId, v.HaveOperaAuth, v.ExcelInfoId)
+			}
+		}
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.SearchExcelListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 6 - 2
controllers/data_manage/future_good/future_good_chart_info.go

@@ -2352,7 +2352,7 @@ func (this *FutureGoodChartInfoController) ChartInfoSearchByEs() {
 		showSysId = sysUser.AdminId
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2405,13 +2405,17 @@ func (this *FutureGoodChartInfoController) ChartInfoSearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 6 - 2
controllers/data_manage/line_equation/line_chart_info.go

@@ -1586,7 +1586,7 @@ func (this *LineEquationChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_LINE_EQUATION}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1639,13 +1639,17 @@ func (this *LineEquationChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 6 - 2
controllers/data_manage/line_feature/chart_info.go

@@ -2717,7 +2717,7 @@ func (this *LineFeaturesChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2770,13 +2770,17 @@ func (this *LineFeaturesChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 6 - 2
controllers/data_manage/range_analysis/chart_info.go

@@ -1365,7 +1365,7 @@ func (this *RangeChartChartInfoController) SearchByEs() {
 	sourceList := make([]int, 0)
 	sourceList = append(sourceList, utils.CHART_SOURCE_RANGE_ANALYSIS)
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1418,13 +1418,17 @@ func (this *RangeChartChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 233 - 0
controllers/eta_forum/eta_forum.go

@@ -0,0 +1,233 @@
+package eta_forum
+
+import (
+	"eta/eta_mobile/controllers"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/data_manage"
+	"eta/eta_mobile/services/eta_forum"
+	"eta/eta_mobile/utils"
+)
+
+type EtaForumController struct {
+	controllers.BaseAuthController
+}
+
+// UserChartList
+// @Title 查询用户在eta社区中有权限查看的图表列表
+// @Description 查询用户在eta社区中有权限查看的图表列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /chart_list [get]
+func (this *EtaForumController) UserChartList() {
+	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
+	}
+	keyword := this.GetString("Keyword")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	resp, err, errMsg := eta_forum.GetUserChartList(businessCode, userMobile, telAreaCode, keyword, currentIndex, pageSize)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// UserCollectChartClassifyList
+// @Title 查询社区中用户收藏的分类列表
+// @Description 查询社区中用户收藏的分类列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /collect/chart_classify [get]
+func (this *EtaForumController) UserCollectChartClassifyList() {
+	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
+	}
+
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	resp, err, errMsg := eta_forum.GetUserCollectChartClassifyList(businessCode, userMobile, telAreaCode)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// UserCollectChartList
+// @Title 查询社区中用户收藏的图表列表
+// @Description 查询社区中用户收藏的图表列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /collect/chart [get]
+func (this *EtaForumController) UserCollectChartList() {
+	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
+	}
+
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	collectClassifyIds := this.GetString("CollectClassifyIds")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+
+	resp, err, errMsg := eta_forum.GetUserCollectChartList(businessCode, userMobile, telAreaCode, keyword, collectClassifyIds, currentIndex, pageSize)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// CommonChartInfoDetailFromUniqueCode
+// @Title 根据编码获取图表详情
+// @Description 根据编码获取图表详情接口
+// @Param   UniqueCode   query   int  true       "图表唯一编码,如果是管理后台访问,传固定字符串:7c69b590249049942070ae9dcd5bf6dc"
+// @Param   IsCache   query   bool  true       "是否走缓存,默认false"
+// @Success 200 {object} data_manage.ChartInfoDetailFromUniqueCodeResp
+// @router /chart/from_unique_code [get]
+func (this *EtaForumController) CommonChartInfoDetailFromUniqueCode() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	uniqueCode := this.GetString("UniqueCode")
+	if uniqueCode == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,uniqueCode is empty"
+		return
+	}
+
+	//是否走缓存
+	isCache, _ := this.GetBool("IsCache")
+	resp := new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+	status := true
+	forumResp, err, _ := eta_forum.GeChartFromUniqueCode(uniqueCode, isCache)
+	if err != nil {
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = nil
+		resp.Status = false
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	chartInfo := forumResp.ChartInfo
+	if chartInfo == nil {
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = nil
+		resp.Status = false
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	resp.ChartInfo = chartInfo
+	resp.Status = status
+	resp.DataResp = forumResp.DataResp
+	resp.EdbInfoList = forumResp.EdbInfoList
+	resp.XDataList = forumResp.XDataList
+	resp.YDataList = forumResp.YDataList
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 14 - 12
controllers/report_chapter.go

@@ -8,8 +8,6 @@ import (
 	"eta/eta_mobile/services"
 	"eta/eta_mobile/services/data"
 	"eta/eta_mobile/utils"
-	"fmt"
-	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"html"
 	"os"
 	"path"
@@ -1162,15 +1160,19 @@ func (this *ReportController) VoiceUpload() {
 		return
 	}
 
-	var playSeconds float64
-	playSeconds, err = mp3duration.Calculate(fPath)
-	if playSeconds <= 0 {
-		playSeconds, err = utils.GetVideoPlaySeconds(fPath)
-		if err != nil {
-			br.Msg = "获取音频时间失败"
-			br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
-			return
-		}
+	//var playSeconds float64
+	//playSeconds, err = mp3duration.Calculate(fPath)
+	//if playSeconds <= 0 {
+	//	playSeconds, err = utils.GetVideoPlaySeconds(fPath)
+	//	if err != nil {
+	//		br.Msg = "获取音频时间失败"
+	//		br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+	playSecondsStr, err := utils.GetDuration(fPath) //mp3duration.Calculate(fPath)
+	if err != nil {
+		utils.FileLog.Info("获取音频时间失败,Err:" + err.Error())
 	}
 
 	fileBody, err := os.ReadFile(fPath)
@@ -1190,7 +1192,7 @@ func (this *ReportController) VoiceUpload() {
 
 		reportChapterInfo.VideoUrl = resourceUrl
 		reportChapterInfo.VideoName = videoName
-		reportChapterInfo.VideoPlaySeconds = fmt.Sprint(playSeconds)
+		reportChapterInfo.VideoPlaySeconds = playSecondsStr
 		reportChapterInfo.VideoSize = sizeStr
 		reportChapterInfo.VideoKind = 1
 		reportChapterInfo.LastModifyAdminId = this.SysUser.AdminId

+ 1 - 2
controllers/resource.go

@@ -846,7 +846,7 @@ func (this *ResourceController) UploadV2() {
 // @Description 获取STSToken
 // @Success 200 获取成功
 // @router /oss/get_sts_token [get]
-func (this *ResourceController) OssSTSToken() {
+func (this *ResourceAuthController) OssSTSToken() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		this.Data["json"] = br
@@ -927,7 +927,6 @@ func (this *ResourceController) WechatWarning() {
 	br.Success = true
 }
 
-
 // FileDownload
 // @Title 文件下载
 // @Description 文件下载

+ 34 - 1
controllers/user_login.go

@@ -12,6 +12,7 @@ import (
 	"github.com/mojocn/base64Captcha"
 	"image/color"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -490,6 +491,33 @@ func (this *UserLoginController) Login() {
 		}
 		sysUser = emailUser
 	}
+	var wg sync.WaitGroup
+	var FullGroupName string
+	nameChan := make(chan string, 1)
+	//需要对用户的分组展示优化为多级
+	if sysUser.GroupId > 0 {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			groupList, e := system.GetGroupByDepartmentId(sysUser.DepartmentId)
+			if e != nil {
+				nameChan <- ""
+				return
+			}
+			var root = new(system.GroupNode)
+			services.BuildGroupTree(root, groupList, 10, 0)
+			var find bool
+			var fullGroupName string
+			if root != nil {
+				find, fullGroupName = services.GetGroupName(root.Child, sysUser.GroupId)
+			}
+			if find {
+				nameChan <- fullGroupName
+			} else {
+				nameChan <- ""
+			}
+		}()
+	}
 
 	// 登录成功(无论哪种方式登录), 清除异常标记
 	abnormalCache := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, sysUser.AdminName)
@@ -587,7 +615,12 @@ func (this *UserLoginController) Login() {
 		record.CreateTime = time.Now()
 		_ = system.AddSysUserLoginRecord(record)
 	}()
-
+	wg.Wait()
+	close(nameChan)
+	FullGroupName = <-nameChan
+	if FullGroupName != "" {
+		resp.GroupName = FullGroupName
+	}
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true

+ 15 - 11
controllers/voice.go

@@ -6,7 +6,6 @@ import (
 	"eta/eta_mobile/services"
 	"eta/eta_mobile/utils"
 	"fmt"
-	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"github.com/rdlucklib/rdluck_tools/http"
 	"os"
 	"path"
@@ -116,15 +115,20 @@ func (this *VoiceController) Upload() {
 		return
 	}
 
-	var playSeconds float64
-	playSeconds, err = mp3duration.Calculate(fpath)
-	if playSeconds <= 0 {
-		playSeconds, err = utils.GetVideoPlaySeconds(fpath)
-		if err != nil {
-			br.Msg = "获取音频时间失败"
-			br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
-			return
-		}
+	//var playSeconds float64
+	//playSeconds, err = mp3duration.Calculate(fpath)
+	//if playSeconds <= 0 {
+	//	playSeconds, err = utils.GetVideoPlaySeconds(fpath)
+	//	if err != nil {
+	//		br.Msg = "获取音频时间失败"
+	//		br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+
+	playSecondsStr, err := utils.GetDuration(fpath) //mp3duration.Calculate(fPath)
+	if err != nil {
+		utils.FileLog.Info("获取音频时间失败,Err:" + err.Error())
 	}
 
 	fileBody, err := os.ReadFile(fpath)
@@ -146,7 +150,7 @@ func (this *VoiceController) Upload() {
 
 		reportInfo.VideoUrl = resourceUrl
 		reportInfo.VideoName = videoName
-		reportInfo.VideoPlaySeconds = fmt.Sprint(playSeconds)
+		reportInfo.VideoPlaySeconds = playSecondsStr
 		reportInfo.VideoSize = sizeStr
 		reportInfo.LastModifyAdminId = this.SysUser.AdminId
 		reportInfo.LastModifyAdminName = this.SysUser.RealName

+ 54 - 34
models/business_conf.go

@@ -9,45 +9,50 @@ import (
 	"time"
 )
 
-const (
-	BusinessConfUseXf                     = "UseXf"
-	BusinessConfXfAppid                   = "XfAppid"
-	BusinessConfXfApiKey                  = "XfApiKey"
-	BusinessConfXfApiSecret               = "XfApiSecret"
-	BusinessConfXfVcn                     = "XfVcn"
-	BusinessConfEnPptCoverImgs            = "EnPptCoverImgs"
-	BusinessConfIsReportApprove           = "IsReportApprove"
-	BusinessConfReportApproveType         = "ReportApproveType"
-	BusinessConfCompanyName               = "CompanyName"
-	BusinessConfCompanyWatermark          = "CompanyWatermark"
-	BusinessConfWatermarkChart            = "WatermarkChart"
-	BusinessConfLoginSmsTpId              = "LoginSmsTpId"
-	BusinessConfLoginSmsGjTpId            = "LoginSmsGjTpId"
-	BusinessConfSmsJhgnAppKey             = "SmsJhgnAppKey"
-	BusinessConfSmsJhgjAppKey             = "SmsJhgjAppKey"
-	BusinessConfLdapHost                  = "LdapHost"
-	BusinessConfLdapBase                  = "LdapBase"
-	BusinessConfLdapPort                  = "LdapPort"
-	BusinessConfEmailClient               = "EmailClient"
-	BusinessConfEmailServerHost           = "EmailServerHost"
-	BusinessConfEmailServerPort           = "EmailServerPort"
-	BusinessConfEmailSender               = "EmailSender"
-	BusinessConfEmailSenderUserName       = "EmailSenderUserName"
-	BusinessConfEmailSenderPassword       = "EmailSenderPassword"
-	BusinessConfSmsClient                 = "SmsClient"
-	BusinessConfNanHuaSmsAppKey           = "NanHuaSmsAppKey"
-	BusinessConfNanHuaSmsAppSecret        = "NanHuaSmsAppSecret"
-	BusinessConfNanHuaSmsApiHost          = "NanHuaSmsApiHost"
-	BusinessConfLoginSmsTplContent        = "LoginSmsTplContent"
-	BusinessConfLoginEmailTemplateSubject = "LoginEmailTemplateSubject"
-	BusinessConfLoginEmailTemplateContent = "LoginEmailTemplateContent"
-	BusinessConfLdapBindUserSuffix        = "LdapBindUserSuffix"
-	BusinessConfLdapUserFilter            = "LdapUserFilter"
+var (
+	BusinessConfMap map[string]string
+)
 
+const (
+	BusinessConfUseXf                        = "UseXf"
+	BusinessConfXfAppid                      = "XfAppid"
+	BusinessConfXfApiKey                     = "XfApiKey"
+	BusinessConfXfApiSecret                  = "XfApiSecret"
+	BusinessConfXfVcn                        = "XfVcn"
+	BusinessConfEnPptCoverImgs               = "EnPptCoverImgs"
+	BusinessConfIsReportApprove              = "IsReportApprove"
+	BusinessConfReportApproveType            = "ReportApproveType"
+	BusinessConfCompanyName                  = "CompanyName"
+	BusinessConfCompanyWatermark             = "CompanyWatermark"
+	BusinessConfWatermarkChart               = "WatermarkChart"
+	BusinessConfLoginSmsTpId                 = "LoginSmsTpId"
+	BusinessConfLoginSmsGjTpId               = "LoginSmsGjTpId"
+	BusinessConfSmsJhgnAppKey                = "SmsJhgnAppKey"
+	BusinessConfSmsJhgjAppKey                = "SmsJhgjAppKey"
+	BusinessConfLdapHost                     = "LdapHost"
+	BusinessConfLdapBase                     = "LdapBase"
+	BusinessConfLdapPort                     = "LdapPort"
+	BusinessConfEmailClient                  = "EmailClient"
+	BusinessConfEmailServerHost              = "EmailServerHost"
+	BusinessConfEmailServerPort              = "EmailServerPort"
+	BusinessConfEmailSender                  = "EmailSender"
+	BusinessConfEmailSenderUserName          = "EmailSenderUserName"
+	BusinessConfEmailSenderPassword          = "EmailSenderPassword"
+	BusinessConfSmsClient                    = "SmsClient"
+	BusinessConfNanHuaSmsAppKey              = "NanHuaSmsAppKey"
+	BusinessConfNanHuaSmsAppSecret           = "NanHuaSmsAppSecret"
+	BusinessConfNanHuaSmsApiHost             = "NanHuaSmsApiHost"
+	BusinessConfLoginSmsTplContent           = "LoginSmsTplContent"
+	BusinessConfLoginEmailTemplateSubject    = "LoginEmailTemplateSubject"
+	BusinessConfLoginEmailTemplateContent    = "LoginEmailTemplateContent"
+	BusinessConfLdapBindUserSuffix           = "LdapBindUserSuffix"
+	BusinessConfLdapUserFilter               = "LdapUserFilter"
 	BusinessConfTencentApiSecretId           = "TencentApiSecretId"           // 腾讯云API-密钥对
 	BusinessConfTencentApiSecretKey          = "TencentApiSecretKey"          // 腾讯云API-密钥对
 	BusinessConfTencentApiRecTaskCallbackUrl = "TencentApiRecTaskCallbackUrl" // 腾讯云API-语音识别回调地址
 	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
+	BusinessConfEsIndexNameExcel             = "EsIndexNameExcel"             // ES索引名称-表格
+	BusinessConfEsIndexNameDataSource        = "EsIndexNameDataSource"                 // 聚合国际短信变量
 	BusinessConfIsOpenChartExpired           = "IsOpenChartExpired"           // 图表是否鉴权
 )
 
@@ -237,3 +242,18 @@ func InitUseMongoConf() {
 		utils.UseMongo = true
 	}
 }
+
+func InitBusinessConf() {
+	var e error
+	BusinessConfMap, e = GetBusinessConf()
+	if e != nil {
+		return
+	}
+	// ES索引名称
+	if BusinessConfMap[BusinessConfEsIndexNameExcel] != "" {
+		utils.EsExcelIndexName = BusinessConfMap[BusinessConfEsIndexNameExcel]
+	}
+	if BusinessConfMap[BusinessConfEsIndexNameDataSource] != "" {
+		utils.EsDataSourceIndexName = BusinessConfMap[BusinessConfEsIndexNameDataSource]
+	}
+}

+ 14 - 3
models/data_manage/chart_info.go

@@ -61,8 +61,17 @@ type ChartInfo struct {
 
 type ChartInfoMore struct {
 	ChartInfo
-	IsEnChart     bool `description:"是否展示英文标识"`
-	HaveOperaAuth bool `description:"是否有数据权限,默认:false"`
+	IsEnChart     bool   `description:"是否展示英文标识"`
+	HaveOperaAuth bool   `description:"是否有数据权限,默认:false"`
+	SearchText    string `description:"搜索结果(含高亮)"`
+}
+
+// AreaExtraConf 面积图配置
+type AreaExtraConf struct {
+	IsHeap            int `description:"是否堆积 1-堆积 2-不堆积"`
+	HeapWay           int `description:"堆积方式 1-普通 2-百分比"`
+	StandardEdbInfoId int `description:"基准指标id"`
+	NullDealWay       int `description:"空值处理方式,1-插值填充 2-前值填充 3-后值填充 4-等于0 5-删除日期"`
 }
 
 func AddChartInfo(item *ChartInfo) (lastId int64, err error) {
@@ -187,6 +196,7 @@ type ChartSaveItem struct {
 	ChartColor        string  `description:"颜色"`
 	PredictChartColor string  `description:"预测数据的颜色"`
 	ChartWidth        float64 `description:"线条大小"`
+	ChartScale        float64 `description:"参考刻度线"`
 	Source            int     `description:"1:ETA图库;2:商品价格曲线"`
 	EdbAliasName      string  `description:"中文别名"`
 	IsConvert         int     `description:"是否数据转换 0不转 1转"`
@@ -643,6 +653,7 @@ type ChartEdbInfoMapping struct {
 	ChartColor          string  `description:"颜色"`
 	PredictChartColor   string  `description:"预测数据的颜色"`
 	ChartWidth          float64 `description:"线条大小"`
+	ChartScale          float64 `description:"参考刻度线"`
 	ChartType           int     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
 	LatestDate          string  `description:"数据最新日期"`
 	LatestValue         float64 `description:"数据最新值"`
@@ -1502,7 +1513,7 @@ func ChartInfoSearchByKeyWord(keyword string, showSysId int) (searchList []*Char
 }
 
 // ChartInfoSearchByEmptyKeyWord 没有关键字的时候获取默认100条数据
-func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (total int64, searchList []*ChartInfo, err error) {
+func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (total int64, searchList []*ChartInfoMore, err error) {
 	num := len(sourceList)
 	o := orm.NewOrmUsingDB("data")
 

+ 33 - 0
models/data_manage/data_manage_permission/excel.go

@@ -971,3 +971,36 @@ func GetExcelInfoDataPermissionClassifyNoAuthRecordListByUserId(userId, excelSou
 
 	return
 }
+
+// ExcelInfoPermissionAdminAuth 含创建人的表格权限
+type ExcelInfoPermissionAdminAuth struct {
+	ExcelInfoPermission
+	ExcelName    string `json:"excel_name"`     // 表格名称
+	UniqueCode   string `json:"unique_code"`    // 唯一编码
+	CreateUserId int    `json:"create_user_id"` // 创建人ID
+}
+
+// GetAdminAuthExcelInfoPermission 获取用户有权限的表格
+func GetAdminAuthExcelInfoPermission(source, adminId int, keywords string) (items []*ExcelInfoPermissionAdminAuth, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT a.*, b.sys_user_id AS create_user_id, b.excel_name, b.unique_code FROM excel_info_permission AS a
+		JOIN excel_info AS b ON a.excel_info_id = b.excel_info_id
+		WHERE a.source = ? AND (b.sys_user_id = ? OR a.sys_user_id = ?)`
+	var pars []interface{}
+	pars = append(pars, source, adminId, adminId)
+	if keywords != "" {
+		sql += ` AND b.excel_name LIKE ?`
+		pars = append(pars, keywords)
+	}
+	sql += ` ORDER BY a.create_time ASC`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetExcelInfoDataNoPermissionByUserId 获取用户所有无权限表格
+func GetExcelInfoDataNoPermissionByUserId(userId, source int) (items []*DataPermissionNoAuthRecord, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT excel_info_permission_no_auth_record_id as data_permission_no_auth_record_id,op_unique_code,source as sub_source,excel_info_id as data_id,excel_name as data_name,sys_user_id,create_time FROM excel_info_permission_no_auth_record WHERE sys_user_id = ? AND source = ? ORDER BY excel_info_permission_no_auth_record_id desc`
+	_, err = o.Raw(sql, userId, source).QueryRows(&items)
+	return
+}

+ 1 - 0
models/data_manage/edb_info.go

@@ -402,6 +402,7 @@ type EdbInfoList struct {
 	NoUpdate         int8                    `description:"是否停止更新,0:继续更新;1:停止更新"`
 	IsJoinPermission int                     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth    bool                    `description:"是否有数据权限,默认:false"`
+	SearchText       string                  `description:"搜索结果(含高亮)"`
 }
 
 type EdbDataInsertConfigItem struct {

+ 26 - 0
models/data_manage/excel/excel_info.go

@@ -32,6 +32,7 @@ type ExcelInfo struct {
 	RelExcelInfoId     int       `description:"平衡表里静态表关联的动态表excel id"`
 	VersionName        string    `description:"静态表版本名称"`
 	SourcesFrom        string    `description:"图表来源"`
+	ExtraConfig        string    `description:"额外配置"`
 }
 
 // Update 更新 excel表格基础信息
@@ -719,3 +720,28 @@ func GetExcelRuleMappingByExcelInfoId(id int) (items []*ExcelInfoRuleMappingView
 	_, err = o.Raw(sql, id).QueryRows(&items)
 	return
 }
+
+// SearchExcelInfo 表格搜索
+type SearchExcelInfo struct {
+	ExcelInfo
+	SearchText    string                `description:"搜索结果(含高亮)"`
+	HaveOperaAuth bool                  `description:"是否有数据权限"`
+	Button        ExcelInfoDetailButton `description:"操作权限"`
+	CanEdit       bool                  `description:"是否可编辑"`
+	Editor        string                `description:"编辑人"`
+}
+
+// ExcelCommonExtraConfig 表格额外配置
+type ExcelCommonExtraConfig struct {
+	TableFreeze ExcelInfoFreeze `description:"表格冻结"`
+}
+
+// ExcelInfoFreeze 表格冻结
+type ExcelInfoFreeze struct {
+	FreezeFirstRow bool `description:"冻结首行"`
+	FreezeFirstCol bool `description:"冻结首列"`
+	FreezeStartRow int  `description:"冻结开始行"`
+	FreezeEndRow   int  `description:"冻结结束行"`
+	FreezeStartCol int  `description:"冻结开始列"`
+	FreezeEndCol   int  `description:"冻结结束列"`
+}

+ 36 - 29
models/data_manage/excel/response/excel_info.go

@@ -61,35 +61,36 @@ type TableDetailResp struct {
 
 // ExcelInfoDetail excel表格详情(前端使用)
 type ExcelInfoDetail struct {
-	ExcelInfoId        int                          `orm:"column(excel_info_id);pk"`
-	Source             int                          `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
-	ExcelType          int                          `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName          string                       `description:"表格名称"`
-	UniqueCode         string                       `description:"表格唯一编码"`
-	ExcelClassifyId    int                          `description:"表格分类id"`
-	SysUserId          int                          `description:"操作人id"`
-	SysUserRealName    string                       `description:"操作人真实姓名"`
-	Content            string                       `description:"表格内容"`
-	ExcelImage         string                       `description:"表格图片"`
-	FileUrl            string                       `description:"表格下载地址"`
-	Sort               int                          `description:"排序字段,数字越小越排前面"`
-	IsDelete           int                          `description:"是否删除,0:未删除,1:已删除"`
-	ModifyTime         time.Time                    `description:"最近修改日期"`
-	CreateTime         time.Time                    `description:"创建日期"`
-	TableData          interface{}                  `description:"表格内容"`
-	Button             excel2.ExcelInfoDetailButton `description:"操作权限"`
-	CanEdit            bool                         `description:"是否可编辑"`
-	Editor             string                       `description:"编辑人"`
-	IsJoinPermission   int                          `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	HaveOperaAuth      bool                         `description:"是否有数据权限"`
-	ParentId           int                          `description:"表格的父级id"`
-	BalanceType        int                          `description:"平衡表类型:0 动态表,1静态表"`
-	UpdateUserId       int                          `description:"更新人id"`
-	UpdateUserRealName string                       `description:"更新人真实姓名"`
-	RelExcelInfoId     int                          `description:"平衡表里静态表关联的动态表excel id"`
-	SourcesFrom        string                       `description:"图表来源"`
-	ExcelSource        string                       `description:"表格来源str"`
-	ExcelSourceEn      string                       `description:"表格来源(英文)"`
+	ExcelInfoId        int                           `orm:"column(excel_info_id);pk"`
+	Source             int                           `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	ExcelType          int                           `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName          string                        `description:"表格名称"`
+	UniqueCode         string                        `description:"表格唯一编码"`
+	ExcelClassifyId    int                           `description:"表格分类id"`
+	SysUserId          int                           `description:"操作人id"`
+	SysUserRealName    string                        `description:"操作人真实姓名"`
+	Content            string                        `description:"表格内容"`
+	ExcelImage         string                        `description:"表格图片"`
+	FileUrl            string                        `description:"表格下载地址"`
+	Sort               int                           `description:"排序字段,数字越小越排前面"`
+	IsDelete           int                           `description:"是否删除,0:未删除,1:已删除"`
+	ModifyTime         time.Time                     `description:"最近修改日期"`
+	CreateTime         time.Time                     `description:"创建日期"`
+	TableData          interface{}                   `description:"表格内容"`
+	Button             excel2.ExcelInfoDetailButton  `description:"操作权限"`
+	CanEdit            bool                          `description:"是否可编辑"`
+	Editor             string                        `description:"编辑人"`
+	IsJoinPermission   int                           `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth      bool                          `description:"是否有数据权限"`
+	ParentId           int                           `description:"表格的父级id"`
+	BalanceType        int                           `description:"平衡表类型:0 动态表,1静态表"`
+	UpdateUserId       int                           `description:"更新人id"`
+	UpdateUserRealName string                        `description:"更新人真实姓名"`
+	RelExcelInfoId     int                           `description:"平衡表里静态表关联的动态表excel id"`
+	SourcesFrom        string                        `description:"图表来源"`
+	ExcelSource        string                        `description:"表格来源str"`
+	ExcelSourceEn      string                        `description:"表格来源(英文)"`
+	ExtraConfig        excel2.ExcelCommonExtraConfig `description:"表格额外配置"`
 }
 
 // ExcelInfoDetailButton 操作按钮
@@ -118,3 +119,9 @@ type BalanceTableVersionListItem struct {
 type BalanceTableVersionListResp struct {
 	List []*BalanceTableVersionListItem
 }
+
+// SearchExcelListResp 搜索表格列表返回数据
+type SearchExcelListResp struct {
+	Paging *paging.PagingItem
+	List   []*excel2.SearchExcelInfo
+}

+ 1 - 0
models/data_manage/my_chart.go

@@ -329,6 +329,7 @@ type MyChartList struct {
 	Source              int    `description:"1:ETA图库;2:商品价格曲线"`
 	IsJoinPermission    int    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth       bool   `description:"是否有数据权限,默认:false"`
+	SearchText          string `description:"搜索结果(含高亮)"`
 }
 
 type MyChartListResp struct {

+ 3 - 0
models/db.go

@@ -632,4 +632,7 @@ func afterInitTable() {
 
 	// 初始化是否启用mongo配置
 	InitUseMongoConf()
+
+	// 初始化商家基本配置
+	InitBusinessConf()
 }

+ 18 - 8
models/report.go

@@ -1686,7 +1686,7 @@ func GetReportListByCollectList(classifyIdFirst, classifyIdSecond, classifyIdThi
 	return items, err
 }
 
-func GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList []string, keyword, author string, state int) (count int, err error) {
+func GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList []string, keyword, author string, isPublic int, stateArr []int) (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	var params []interface{}
 
@@ -1731,9 +1731,14 @@ func GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyId
 		params = append(params, author)
 	}
 
-	if state > 0 {
-		sql += " AND b.state = ? "
-		params = append(params, state)
+	if isPublic > 0 {
+		sql += " AND b.is_public_publish = ? "
+		params = append(params, isPublic)
+	}
+
+	if len(stateArr) > 0 {
+		sql += fmt.Sprintf(" AND b.state IN (%s)", utils.GetOrmInReplace(len(stateArr)))
+		params = append(params, stateArr)
 	}
 
 	// 分类id判断
@@ -1772,7 +1777,7 @@ func GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyId
 	return count, err
 }
 
-func GetReportListByCollectListV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList []string, keyword, orderField, orderType string, startSize, pageSize int, author string, state int) (items []*ReportList, err error) {
+func GetReportListByCollectListV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList []string, keyword, orderField, orderType string, startSize, pageSize int, author string, isPublic int, stateArr []int) (items []*ReportList, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	var params []interface{}
 
@@ -1799,9 +1804,14 @@ b.abstract,b.admin_id,b.admin_real_name,b.last_modify_admin_id,b.last_modify_adm
 		params = append(params, author)
 	}
 
-	if state > 0 {
-		sql += " AND b.state = ? "
-		params = append(params, state)
+	if isPublic > 0 {
+		sql += " AND b.is_public_publish = ? "
+		params = append(params, isPublic)
+	}
+
+	if len(stateArr) > 0 {
+		sql += fmt.Sprintf(" AND b.state IN (%s)", utils.GetOrmInReplace(len(stateArr)))
+		params = append(params, stateArr)
 	}
 
 	// 分类id判断

+ 6 - 0
models/system/sys_group.go

@@ -192,6 +192,12 @@ func MultiUpdateGroupSort(items []*GroupSort) (err error) {
 	return
 }
 
+type GroupNode struct {
+	GroupId   int    `json:"groupId"`
+	GroupName string `json:"groupName"`
+	Child     []*GroupNode
+}
+
 func GetGroupByDepartmentId(departmentId int) (list []*SysFullGroup, err error) {
 	sql := `SELECT
 				s.*, g.group_name AS parent_group_name,

+ 54 - 9
routers/commentsRouter.go

@@ -907,6 +907,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "SearchByEs",
+            Router: `/excel_info/search_by_es`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "BatchRefresh",
@@ -3922,6 +3931,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "CommonChartInfoDetailFromUniqueCode",
+            Router: `/chart/from_unique_code`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserChartList",
+            Router: `/chart_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserCollectChartList",
+            Router: `/collect/chart`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserCollectChartClassifyList",
+            Router: `/collect/chart_classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/eta_trial:EtaTrialController"],
         beego.ControllerComments{
             Method: "QuestionnaireCommit",
@@ -5947,19 +5992,19 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"],
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceAuthController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceAuthController"],
         beego.ControllerComments{
-            Method: "Upload",
-            Router: `/image/upload`,
-            AllowHTTPMethods: []string{"post"},
+            Method: "OssSTSToken",
+            Router: `/oss/get_sts_token`,
+            AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"],
         beego.ControllerComments{
-            Method: "UploadV2",
-            Router: `/image/uploadV2`,
+            Method: "Upload",
+            Router: `/image/upload`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5967,9 +6012,9 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"],
         beego.ControllerComments{
-            Method: "OssSTSToken",
-            Router: `/oss/get_sts_token`,
-            AllowHTTPMethods: []string{"get"},
+            Method: "UploadV2",
+            Router: `/image/uploadV2`,
+            AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})

+ 6 - 0
routers/router.go

@@ -23,6 +23,7 @@ import (
 	"eta/eta_mobile/controllers/data_stat"
 	"eta/eta_mobile/controllers/document_manage"
 	"eta/eta_mobile/controllers/english_report"
+	"eta/eta_mobile/controllers/eta_forum"
 	"eta/eta_mobile/controllers/eta_trial"
 	"eta/eta_mobile/controllers/material"
 	"eta/eta_mobile/controllers/report_approve"
@@ -327,6 +328,11 @@ func init() {
 				&material.MaterialController{},
 			),
 		),
+		web.NSNamespace("/eta_forum",
+			web.NSInclude(
+				&eta_forum.EtaForumController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 592 - 0
services/data/area_graph/processor_business_logic.go

@@ -0,0 +1,592 @@
+package area_graph
+
+import (
+	"errors"
+	"eta/eta_mobile/models/data_manage"
+	"eta/eta_mobile/utils"
+	"github.com/shopspring/decimal"
+	"math"
+	"sort"
+	"time"
+)
+
+type InterpolateStrategy struct{}
+
+// Deal 空值填充:插值法填充
+func (i *InterpolateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 插值法补充数据
+				var startEdbInfoData *data_manage.EdbDataList
+				for index := 0; index < len(dataList); index++ {
+					// 获取当前数据和下一个数据
+					currentIndexData := dataList[index]
+					//afterIndexData := dataList[index+1]
+
+					if startEdbInfoData == nil {
+						startEdbInfoData = currentIndexData
+						continue
+					}
+
+					// 获取两条数据之间相差的天数
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
+					currDataTime, _ := time.ParseInLocation(utils.FormatDate, currentIndexData.DataTime, time.Local)
+					betweenHour := int(currDataTime.Sub(startDataTime).Hours())
+					betweenDay := betweenHour / 24
+
+					// 如果相差一天,那么过滤
+					if betweenDay <= 1 {
+						startEdbInfoData = currentIndexData
+						continue
+					}
+
+					// 生成线性方程式
+					var a, b float64
+					{
+						coordinateData := make([]utils.Coordinate, 0)
+						tmpCoordinate1 := utils.Coordinate{
+							X: 1,
+							Y: startEdbInfoData.Value,
+						}
+						coordinateData = append(coordinateData, tmpCoordinate1)
+						tmpCoordinate2 := utils.Coordinate{
+							X: float64(betweenDay) + 1,
+							Y: currentIndexData.Value,
+						}
+						coordinateData = append(coordinateData, tmpCoordinate2)
+
+						a, b = utils.GetLinearResult(coordinateData)
+						if math.IsNaN(a) || math.IsNaN(b) {
+							err = errors.New("线性方程公式生成失败")
+							return
+						}
+					}
+
+					// 插值补充数据
+					for i := 1; i < betweenDay; i++ {
+						tmpDataTime := startDataTime.AddDate(0, 0, i)
+						aDecimal := decimal.NewFromFloat(a)
+						xDecimal := decimal.NewFromInt(int64(i) + 1)
+						bDecimal := decimal.NewFromFloat(b)
+
+						val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
+						nextDay := tmpDataTime.Format(utils.FormatDate)
+
+						replenishIndexData := data_manage.EdbDataList{
+							EdbDataId:     currentIndexData.EdbDataId,
+							DataTime:      nextDay,
+							DataTimestamp: tmpDataTime.UnixMilli(),
+							Value:         val,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+					}
+					startEdbInfoData = currentIndexData
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					if _, ok := standardIndexMap[dataObject.DataTime]; ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type FillWithPreviousStrategy struct{}
+
+// Deal 空值填充:前值填充
+func (f *FillWithPreviousStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(beforeIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         beforeIndexData.Value,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						beforeIndexData = &replenishIndexData
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type FillWithNextStrategy struct{}
+
+// Deal 空值填充:后值填充
+func (f *FillWithNextStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for !startDataTime.After(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				// 将切片数据倒序
+				reverseSlice(dataList)
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(afterIndexData.DataTime, beforeIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(afterIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         beforeIndexData.Value,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						afterIndexData = &replenishIndexData
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type SetToZeroStrategy struct{}
+
+// Deal 空值填充:设为0
+func (s *SetToZeroStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(beforeIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         0,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						beforeIndexData = &replenishIndexData
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type DeleteDateStrategy struct{}
+
+// Deal 删除日期
+func (d *DeleteDateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) error {
+	// 取所有指标的时间交集
+	// 创建一个 map 来保存每个时间点的出现次数
+	timeMap := make(map[string]int)
+	for _, v := range edbDataList {
+		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+			// 遍历所有的 dataList,为每个 DataTime 增加一个计数
+			for _, dataObject := range dataList {
+				timeMap[dataObject.DataTime]++
+			}
+		}
+	}
+
+	for _, v := range edbDataList {
+		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+			// 遍历所有的 dataList,保留所有时间点在所有指标中都存在的数据
+			var resultDataList []*data_manage.EdbDataList
+			for _, dataObject := range dataList {
+				if timeMap[dataObject.DataTime] == len(edbDataList) {
+					// 如果该时间点在所有指标中都存在,加入到结果列表
+					resultDataList = append(resultDataList, dataObject)
+				}
+			}
+
+			// 将符合条件的数据重新赋值回 v.DataList
+			v.DataList = resultDataList
+		}
+	}
+	return nil
+}
+
+// 将列表颠倒
+func reverseSlice(dataList []*data_manage.EdbDataList) {
+	// 使用双指针法,前后两个指针向中间逼近
+	for i, j := 0, len(dataList)-1; i < j; i, j = i+1, j-1 {
+		// 交换位置
+		dataList[i], dataList[j] = dataList[j], dataList[i]
+	}
+}

+ 27 - 0
services/data/area_graph/processor_factory.go

@@ -0,0 +1,27 @@
+package area_graph
+
+import (
+	"eta/eta_mobile/models/data_manage"
+	"fmt"
+)
+
+type NullDealStrategy interface {
+	Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error)
+}
+
+func CreateStrategy(dealWay int) (NullDealStrategy, error) {
+	switch dealWay {
+	case 1:
+		return &InterpolateStrategy{}, nil
+	case 2:
+		return &FillWithPreviousStrategy{}, nil
+	case 3:
+		return &FillWithNextStrategy{}, nil
+	case 4:
+		return &SetToZeroStrategy{}, nil
+	case 5:
+		return &DeleteDateStrategy{}, nil
+	default:
+		return nil, fmt.Errorf("未知的空值处理类型: %d", dealWay)
+	}
+}

+ 11 - 1
services/data/chart_info.go

@@ -653,6 +653,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 			item.ChartStyle = ""
 			item.ChartColor = ""
 			item.ChartWidth = 0
+			item.ChartScale = 0
 			item.MaxData = v.MaxValue
 			item.MinData = v.MinValue
 		} else {
@@ -666,6 +667,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 			item.ChartStyle = v.ChartStyle
 			item.ChartColor = v.ChartColor
 			item.ChartWidth = v.ChartWidth
+			item.ChartScale = v.ChartScale
 			item.IsOrder = v.IsOrder
 			item.MaxData = v.MaxData
 			item.MinData = v.MinData
@@ -873,6 +875,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 				return
 			}
 
+			nowYear := time.Now().Year()
 			newDataList := make([]*data_manage.EdbDataList, 0)
 			for _, v := range dataList {
 				dataTime, e := time.Parse(utils.FormatDate, v.DataTime)
@@ -880,6 +883,10 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 					err = errors.New("季节性图处理右轴指标数据转换日期失败,Err:" + e.Error())
 					return
 				}
+				dataTimeT, _ := time.Parse(utils.FormatDate, v.DataTime)
+				year := dataTimeT.Year()
+				newItemDate := dataTimeT.AddDate(nowYear-year, 0, 0)
+				v.DataTimestamp = newItemDate.UnixNano() / 1e6
 				if dataTime.Equal(rightAxisDate) || dataTime.After(rightAxisDate) {
 					newDataList = append(newDataList, v)
 				}
@@ -2405,6 +2412,7 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 					ChartColor:        "",
 					PredictChartColor: "",
 					ChartWidth:        0,
+					ChartScale:        0,
 					Source:            utils.CHART_SOURCE_DEFAULT,
 				})
 			}
@@ -2857,6 +2865,8 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 		if chartItem.ChartType == 2 && chartItem.DateType <= 0 {
 			dateType = 3
 		}
+	} else {
+		calendar = req.Calendar
 	}
 	sort.Ints(edbInfoIdArr)
 	var edbInfoIdArrStr []string
@@ -4237,7 +4247,7 @@ func SeasonChartData(dataList []*data_manage.ChartEdbInfoMapping, seasonExtraCon
 			valueMap := make(map[time.Time]float64)
 
 			samePeriodStandardDeviationList := make([]*data_manage.MaxMinLimitsData, 0)
-			for i := len(quarterDataList) - 1; i > len(quarterDataList)-seasonConfig.SamePeriodAverage.Year-1 && i > 0; i-- {
+			for i := len(quarterDataList) - 1; i > len(quarterDataList)-seasonConfig.SamePeriodStandardDeviation.Year-1 && i > 0; i-- {
 				// 插值成日度
 				dataTimeList, _, err = HandleDataByLinearRegressionToListV2(quarterDataList[i].DataList, handleDataMap)
 				if err != nil {

+ 1 - 1
services/data/chart_info_elastic.go

@@ -125,7 +125,7 @@ func EsDeleteChartInfo(chartInfoId int) {
 }
 
 // EsSearchChartInfo 搜索图表信息
-func EsSearchChartInfo(keyword string, showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (list []*data_manage.ChartInfo, total int64, err error) {
+func EsSearchChartInfo(keyword string, showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (list []*data_manage.ChartInfoMore, total int64, err error) {
 	list, total, err = elastic.SearchChartInfoData(utils.CHART_INDEX_NAME, keyword, showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
 	return
 }

+ 6 - 1
services/data/chart_info_excel_balance.go

@@ -452,13 +452,14 @@ func GetBalanceExcelEdbDataMapList(chartInfoId, chartType int, calendar, startDa
 			var rightAxisDate time.Time
 			if jumpYear == 1 {
 				latestDate = latestDate.AddDate(-1, 0, 0)
-				latestDateStr := fmt.Sprintf("%d-%s", latestDate.Year(),xStartDate)
+				latestDateStr := fmt.Sprintf("%d-%s", latestDate.Year(), xStartDate)
 				rightAxisDate, err = time.Parse(utils.FormatDate, latestDateStr)
 				if err != nil {
 					return
 				}
 			}
 
+			nowYear := time.Now().Year()
 			newDataList := make([]*data_manage.EdbDataList, 0)
 			for _, v := range dataList {
 				dataTime, e := time.Parse(utils.FormatDate, v.DataTime)
@@ -466,6 +467,10 @@ func GetBalanceExcelEdbDataMapList(chartInfoId, chartType int, calendar, startDa
 					err = errors.New("季节性图处理右轴指标数据转换日期失败,Err:" + e.Error())
 					return
 				}
+				dataTimeT, _ := time.Parse(utils.FormatDate, v.DataTime)
+				year := dataTimeT.Year()
+				newItemDate := dataTimeT.AddDate(nowYear-year, 0, 0)
+				v.DataTimestamp = newItemDate.UnixNano() / 1e6
 				if dataTime.Equal(rightAxisDate) || dataTime.After(rightAxisDate) {
 					newDataList = append(newDataList, v)
 				}

+ 11 - 4
services/data/excel/excel_info.go

@@ -103,6 +103,14 @@ func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang stri
 		SourcesFrom:     excelInfo.SourcesFrom,
 	}
 
+	// 额外配置(表格冻结行列等)
+	if excelInfo.ExtraConfig != "" {
+		if e := json.Unmarshal([]byte(excelInfo.ExtraConfig), &excelDetail.ExtraConfig); e != nil {
+			err = fmt.Errorf("额外配置解析失败, %v", e)
+			return
+		}
+	}
+
 	// 无权限,不需要返回数据
 	if !haveOperaAuth {
 		return
@@ -161,7 +169,7 @@ func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang stri
 		excelDetail.ExcelSource = strings.Join(sourceNameList, ",")
 		excelDetail.ExcelSourceEn = strings.Join(sourceNameEnList, ",")
 		excelDetail.TableData = result
-	case utils.MIXED_TABLE: // 混合表格
+	case utils.MIXED_TABLE, utils.BALANCE_TABLE: // 混合表格/平衡表
 		var result request.MixedTableReq
 		err = json.Unmarshal([]byte(excelDetail.Content), &result)
 		if err != nil {
@@ -1456,9 +1464,8 @@ func GetExcelEdbBatchRefreshKey(source string, reportId, chapterId int) string {
 	return fmt.Sprint("batch_refresh_excel_edb:", source, ":", reportId, ":", chapterId)
 }
 
-
 // GetEdbSourceByEdbInfoIdList 获取关联指标的来源
-func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNameEnList []string,err error) {
+func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNameEnList []string, err error) {
 	sourceNameList = make([]string, 0)
 	sourceNameEnList = make([]string, 0)
 	sourceMap := make(map[int]string)
@@ -1507,4 +1514,4 @@ func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNam
 		sourceNameEnList = append(sourceNameEnList, conf[models.BusinessConfCompanyName])
 	}
 	return
-}
+}

+ 7 - 5
services/document_manage_service/document_manage_service.go

@@ -501,11 +501,13 @@ func RuiSiReportListV2(classifyIdList, chartPermissionIdList []string, keyword,
 		}
 	}
 	// 作者为 全球市场战略研究中心 PCI Research
-	author := "战研中心 PCIR"
-	// 已发布的报告
-	state := 2
+	var author string
+	//author := "战研中心 PCIR"
+	// 已发布/已审批的公开发布的报告
+	isPublic := 1
+	stateArr := []int{2, 6}
 
-	count, err := models.GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList, keyword, author, state)
+	count, err := models.GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList, keyword, author, isPublic, stateArr)
 	if err != nil {
 		return nil, err
 	}
@@ -517,7 +519,7 @@ func RuiSiReportListV2(classifyIdList, chartPermissionIdList []string, keyword,
 		return &reportPage, nil
 	}
 
-	reportList, err := models.GetReportListByCollectListV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList, keyword, orderField, orderType, startSize, pageSize, author, state)
+	reportList, err := models.GetReportListByCollectListV2(classifyIdFirst, classifyIdSecond, classifyIdThird, chartPermissionIdList, keyword, orderField, orderType, startSize, pageSize, author, isPublic, stateArr)
 	if err != nil {
 		return nil, err
 	}

+ 198 - 8
services/elastic/elastic.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"errors"
 	"eta/eta_mobile/models/data_manage"
+	"eta/eta_mobile/models/data_manage/excel"
 	"eta/eta_mobile/utils"
 	"fmt"
 	"github.com/olivere/elastic/v7"
@@ -827,6 +828,14 @@ func searchEdbInfoData(indexName string, mustMap, mustNotMap []interface{}, shou
 
 	queryMap["from"] = from
 	queryMap["size"] = size
+	queryMap["highlight"] = map[string]interface{}{
+		"fields": map[string]interface{}{
+			"EdbName": map[string]interface{}{},
+		},
+		"pre_tags":  "<span style=\"color:#0052D9\">",
+		"post_tags": "</span>",
+	}
+
 	jsonBytes, _ := json.Marshal(queryMap)
 	fmt.Println(string(jsonBytes))
 
@@ -884,7 +893,11 @@ func searchEdbInfoData(indexName string, mustMap, mustNotMap []interface{}, shou
 					edbInfoItem.EdbCode = v.Highlight["EdbCode"][0]
 				}
 				if len(v.Highlight["EdbName"]) > 0 {
-					edbInfoItem.EdbCode = v.Highlight["EdbName"][0]
+					// 搜索结果高亮用新字段,原EdbName直接高亮展示上会有点影响
+					edbInfoItem.SearchText = v.Highlight["EdbName"][0]
+					//edbInfoItem.EdbName = v.Highlight["EdbName"][0]
+				} else {
+					edbInfoItem.SearchText = edbInfoItem.EdbName
 				}
 				list = append(list, edbInfoItem)
 				searchMap[v.Id] = v.Id
@@ -1224,8 +1237,8 @@ func EsDeleteDataV2(indexName, docId string) (err error) {
 }
 
 // SearchChartInfoData 查询es中的图表数据
-func SearchChartInfoData(indexName, keywordStr string, showSysId int, sourceList []int, noPermissionChartIdList []int, from, size int) (list []*data_manage.ChartInfo, total int64, err error) {
-	list = make([]*data_manage.ChartInfo, 0)
+func SearchChartInfoData(indexName, keywordStr string, showSysId int, sourceList []int, noPermissionChartIdList []int, from, size int) (list []*data_manage.ChartInfoMore, total int64, err error) {
+	list = make([]*data_manage.ChartInfoMore, 0)
 	defer func() {
 		if err != nil {
 			fmt.Println("EsAddOrEditData Err:", err.Error())
@@ -1362,6 +1375,14 @@ func SearchChartInfoData(indexName, keywordStr string, showSysId int, sourceList
 	// 分页查询
 	queryMap["from"] = from
 	queryMap["size"] = size
+	queryMap["highlight"] = map[string]interface{}{
+		"fields": map[string]interface{}{
+			keywordNameKey: map[string]interface{}{},
+		},
+		"pre_tags":  "<span style=\"color:#0052D9\">",
+		"post_tags": "</span>",
+	}
+
 	jsonBytes, _ := json.Marshal(queryMap)
 	fmt.Println(string(jsonBytes))
 
@@ -1393,15 +1414,18 @@ func SearchChartInfoData(indexName, keywordStr string, showSysId int, sourceList
 					fmt.Println("movieJson err:", err)
 					return
 				}
-				chartInfoItem := new(data_manage.ChartInfo)
+				chartInfoItem := new(data_manage.ChartInfoMore)
 				tmpErr = json.Unmarshal(itemJson, &chartInfoItem)
 				if err != nil {
 					fmt.Println("json.Unmarshal chartInfoJson err:", err)
 					err = tmpErr
 					return
 				}
-				if len(v.Highlight["ChartName"]) > 0 {
-					chartInfoItem.ChartName = v.Highlight["ChartName"][0]
+				if len(v.Highlight[keywordNameKey]) > 0 {
+					//chartInfoItem.ChartName = v.Highlight["ChartName"][0]
+					chartInfoItem.SearchText = v.Highlight[keywordNameKey][0]
+				} else {
+					chartInfoItem.SearchText = chartInfoItem.ChartName
 				}
 				list = append(list, chartInfoItem)
 				searchMap[v.Id] = v.Id
@@ -1574,6 +1598,14 @@ func SearchMyChartInfoData(indexName, keywordStr string, adminId int, noPermissi
 	// 分页查询
 	queryMap["from"] = from
 	queryMap["size"] = size
+	queryMap["highlight"] = map[string]interface{}{
+		"fields": map[string]interface{}{
+			keywordNameKey: map[string]interface{}{},
+		},
+		"pre_tags":  "<span style=\"color:#0052D9\">",
+		"post_tags": "</span>",
+	}
+
 	jsonBytes, _ := json.Marshal(queryMap)
 	fmt.Println(string(jsonBytes))
 
@@ -1612,8 +1644,11 @@ func SearchMyChartInfoData(indexName, keywordStr string, adminId int, noPermissi
 					err = tmpErr
 					return
 				}
-				if len(v.Highlight["ChartName"]) > 0 {
-					chartInfoItem.ChartName = v.Highlight["ChartName"][0]
+				if len(v.Highlight[keywordNameKey]) > 0 {
+					//chartInfoItem.ChartName = v.Highlight["ChartName"][0]
+					chartInfoItem.SearchText = v.Highlight[keywordNameKey][0]
+				} else {
+					chartInfoItem.SearchText = chartInfoItem.ChartName
 				}
 				list = append(list, chartInfoItem)
 				searchMap[v.Id] = v.Id
@@ -1850,3 +1885,158 @@ func SearchEdbInfoDataByAdminId(indexName, keywordStr string, from, size, filter
 
 	return searchEdbInfoData(indexName, mustMap, mustNotMap, shouldMap, from, size)
 }
+
+// SearchExcelInfoData 查询es中的表格
+func SearchExcelInfoData(indexName, keyword string, source, adminId int, queryIds, exceptIds []int, from, size int) (total int64, list []*excel.SearchExcelInfo, err error) {
+	list = make([]*excel.SearchExcelInfo, 0)
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("SearchExcelInfoData err: %v", err)
+			utils.FileLog.Info(tips)
+		}
+	}()
+	client := utils.EsClient
+
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+	mustMap = append(mustMap, map[string]interface{}{
+		"term": map[string]interface{}{
+			"IsDelete": 0,
+		},
+	})
+
+	// 表格名称
+	shouldMap := make(map[string]interface{}, 0)
+	if keyword != "" {
+		shouldMap["should"] = []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"ExcelName": keyword,
+				},
+			},
+		}
+
+		//shouldMap := map[string]interface{}{
+		//	"should": []interface{}{
+		//		map[string]interface{}{
+		//			"match": map[string]interface{}{
+		//				"ExcelName": keyword,
+		//			},
+		//		},
+		//	},
+		//}
+	}
+
+	// 表格来源
+	if source > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Source": source,
+			},
+		})
+	}
+
+	// 创建人
+	if adminId > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"SysUserId": adminId,
+			},
+		})
+	}
+
+	// 查询和排除的表格IDs
+	if len(queryIds) > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"terms": map[string]interface{}{
+				"ExcelInfoId": queryIds,
+			},
+		})
+	}
+	if len(exceptIds) > 0 {
+		mustNotMap = append(mustNotMap, map[string]interface{}{
+			"terms": map[string]interface{}{
+				"ExcelInfoId": exceptIds,
+			},
+		})
+	}
+
+	// 关键字匹配
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+	queryMap := map[string]interface{}{
+		"query": map[string]interface{}{
+			"bool": map[string]interface{}{
+				"must":     mustMap,
+				"must_not": mustNotMap,
+				//"should":   shouldMap,
+			},
+		},
+	}
+
+	//jsonBytes, _ := json.Marshal(queryMap)
+	//fmt.Println(string(jsonBytes))
+
+	// 根据条件数量统计
+	requestTotalHits := client.Count(indexName).BodyJson(queryMap)
+	t, e := requestTotalHits.Do(context.Background())
+	if e != nil {
+		err = fmt.Errorf("total hits err: %v", e)
+		return
+	}
+	total = t
+
+	// 表格名称高亮,分页
+	highlightKeyName := "ExcelName"
+	queryMap["highlight"] = map[string]interface{}{
+		"fields": map[string]interface{}{
+			highlightKeyName: map[string]interface{}{},
+		},
+		"pre_tags":  "<span style=\"color:#0052D9\">",
+		"post_tags": "</span>",
+	}
+	queryMap["from"] = from
+	queryMap["size"] = size
+
+	//jsonBytes, _ := json.Marshal(queryMap)
+	//fmt.Println(string(jsonBytes))
+
+	request := client.Search(indexName).Source(queryMap)
+	searchResp, e := request.Do(context.Background())
+	if e != nil {
+		err = fmt.Errorf("search do err: %v", e)
+		return
+	}
+	//fmt.Println(searchResp)
+	if searchResp.Status != 0 {
+		return
+	}
+	if searchResp.Hits == nil {
+		return
+	}
+	searchMap := make(map[string]string)
+	for _, v := range searchResp.Hits.Hits {
+		if _, ok := searchMap[v.Id]; ok {
+			continue
+		}
+		j, e := v.Source.MarshalJSON()
+		if e != nil {
+			err = fmt.Errorf("hits json err: %v", e)
+			return
+		}
+		item := new(excel.SearchExcelInfo)
+		if e = json.Unmarshal(j, &item); e != nil {
+			err = fmt.Errorf("hits json unmarshal err: %v", e)
+			return
+		}
+		if len(v.Highlight[highlightKeyName]) > 0 {
+			item.SearchText = v.Highlight[highlightKeyName][0]
+		} else {
+			item.SearchText = item.ExcelName
+		}
+		list = append(list, item)
+		searchMap[v.Id] = v.Id
+	}
+	return
+}

+ 217 - 0
services/eta_forum/chart_collect.go

@@ -0,0 +1,217 @@
+package eta_forum
+
+import (
+	"eta/eta_mobile/models/data_manage"
+	"eta/eta_mobile/services/alarm_msg"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type UserChartListReq struct {
+	Keyword         string `description:"搜索关键词"`
+	PageSize        int
+	CurrentIndex    int
+	UserMobile      string `description:"用户手机号"`
+	BusinessCode    string `description:"商户号"`
+	UserTelAreaCode string `description:"手机号区号"`
+}
+
+type UserChartListResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        *UserChartListRespItem
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+
+type UserChartListRespItem struct {
+	ChartInfoList []*data_manage.ChartInfo
+	Paging        *paging.PagingItem `description:"分页数据"`
+}
+
+type UserCollectChartClassifyListResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        *UserCollectChartClassifyListItem
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+
+type UserCollectChartClassifyListItem struct {
+	List     []*ChartCollectClassifyItem
+	Language string `description:"指标的展示语言,CN:中文,EN:英文"`
+}
+
+// ChartCollectClassifyItem 我的图表分类信息
+type ChartCollectClassifyItem struct {
+	CollectClassifyId int    `description:"分类ID"`
+	ClassifyName      string `description:"分类名称"`
+	UserId            int    `description:"创建人id"`
+	ChartNum          int    `description:"分类下的图表数量"`
+}
+
+type UserCollectChartListResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        *UserCollectChartListRespItem
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+type UserCollectChartListRespItem struct {
+	Paging *paging.PagingItem
+	List   []*ChartCollectView
+}
+
+type ChartCollectView struct {
+	ChartCollectId    int
+	ChartInfoId       int       `description:"图表id"`
+	UserId            int       `description:"用户id"`
+	CreateTime        time.Time `description:"创建时间"`
+	CollectTime       time.Time `description:"收藏时间"`
+	CollectClassifyId int
+	ChartName         string `description:"来源名称"`
+	ChartNameEn       string `description:"英文图表名称"`
+	ChartImage        string `description:"图表图片"`
+	UniqueCode        string `description:"图表唯一编码"`
+}
+
+type ChartFromUniqueCodeResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        *ChartFromUniqueCodeRespItem
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+
+type ChartFromUniqueCodeRespItem struct {
+	ChartInfo   *data_manage.ChartInfoView
+	EdbInfoList []*data_manage.ChartEdbInfoMapping
+	XEdbIdValue []int               `description:"柱方图的x轴数据,指标id"`
+	YDataList   []data_manage.YData `description:"柱方图的y轴数据"`
+	XDataList   []data_manage.XData `description:"商品价格曲线的X轴数据"`
+	//BarChartInfo BarChartInfoReq `description:"柱方图的配置"`
+	//CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
+	DataResp  interface{} `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
+	WaterMark string      `description:"水印"`
+}
+
+// GetUserChartList 查询社区中对用户可见的图表列表
+func GetUserChartList(businessCode, userMobile, telAreaCode, keyword string, currentIndex, pageSize int) (resp UserChartListRespItem, err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("查询社区中对用户可见的图表列表 GetUserChartList:Err:%v,ErrMsg:%s", err, errMsg)
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	urlQuery := fmt.Sprintf("BusinessCode=%s&UserMobile=%s&UserTelAreaCode=%s&Keyword=%s&CurrentIndex=%d&PageSize=%d", businessCode, userMobile, telAreaCode, keyword, currentIndex, pageSize)
+	result, err := GetUserChartListLib(urlQuery)
+	if err != nil {
+		errMsg = "查询失败"
+		err = fmt.Errorf("查询失败,Err:" + err.Error())
+		return
+	}
+	if result.Ret != 200 {
+		errMsg = "查询失败"
+		err = fmt.Errorf(result.Msg + result.ErrMsg)
+		return
+	}
+	resp.Paging = result.Data.Paging
+	resp.ChartInfoList = result.Data.ChartInfoList
+
+	return
+}
+
+// GetUserCollectChartClassifyList 查询社区中用户收藏的分类列表
+func GetUserCollectChartClassifyList(businessCode, userMobile, telAreaCode string) (resp UserCollectChartClassifyListItem, err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("查询社区中用户收藏的分类列表 GetUserCollectChartClassifyList:Err:%v,ErrMsg:%s", err, errMsg)
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	urlQuery := fmt.Sprintf("BusinessCode=%s&UserMobile=%s&UserTelAreaCode=%s", businessCode, userMobile, telAreaCode)
+	result, err := getUserCollectChartClassifyLib(urlQuery)
+	if err != nil {
+		errMsg = "查询失败"
+		err = fmt.Errorf("查询失败,Err:" + err.Error())
+		return
+	}
+	if result.Ret != 200 {
+		errMsg = "查询失败"
+		err = fmt.Errorf(result.Msg + result.ErrMsg)
+		return
+	}
+	resp.List = result.Data.List
+	resp.Language = result.Data.Language
+
+	return
+}
+
+// GetUserCollectChartList 查询社区中对用户收藏的图表列表
+func GetUserCollectChartList(businessCode, userMobile, telAreaCode, keyword, collectClassifyIds string, currentIndex, pageSize int) (resp UserCollectChartListRespItem, err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("查询社区中对用户收藏的图表列表 GetUserCollectChartList:Err:%v,ErrMsg:%s", err, errMsg)
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	urlQuery := fmt.Sprintf("BusinessCode=%s&UserMobile=%s&UserTelAreaCode=%s&Keyword=%s&CollectClassifyIds=%s&CurrentIndex=%d&PageSize=%d", businessCode, userMobile, telAreaCode, keyword, collectClassifyIds, currentIndex, pageSize)
+	result, err := getUserCollectChartListLib(urlQuery)
+	if err != nil {
+		errMsg = "查询失败"
+		err = fmt.Errorf("查询失败,Err:" + err.Error())
+		return
+	}
+	if result.Ret != 200 {
+		errMsg = "查询失败"
+		err = fmt.Errorf(result.Msg + result.ErrMsg)
+		return
+	}
+	resp.Paging = result.Data.Paging
+	resp.List = result.Data.List
+
+	return
+}
+
+// GeChartFromUniqueCode 社区中根据唯一编码查询图表
+func GeChartFromUniqueCode(uniqueCode string, isCache bool) (resp ChartFromUniqueCodeRespItem, err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("查询社区中对用户可见的图表列表 GeChartFromUniqueCode:Err:%v,ErrMsg:%s", err, errMsg)
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	urlQuery := fmt.Sprintf("UniqueCode=%s&isCache=%v", uniqueCode, isCache)
+	result, err := getChartFromUniqueCodeLib(urlQuery)
+	if err != nil {
+		errMsg = "查询失败"
+		err = fmt.Errorf("查询失败,Err:" + err.Error())
+		return
+	}
+	if result.Ret != 200 {
+		errMsg = "查询失败"
+		err = fmt.Errorf(result.Msg + result.ErrMsg)
+		return
+	}
+	resp = *result.Data
+
+	return
+}

+ 88 - 0
services/eta_forum/eta_forum_hub_lib.go

@@ -0,0 +1,88 @@
+package eta_forum
+
+import (
+	"encoding/json"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+)
+
+// GetUserChartListLib 查询有权限的图表列表
+func GetUserChartListLib(req string) (resp *UserChartListResp, err error) {
+	_, resultByte, err := get(req, "/v1/chart/user/chart_list")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// getUserCollectChartClassifyLib 查询收藏的分类列表
+func getUserCollectChartClassifyLib(req string) (resp *UserCollectChartClassifyListResp, err error) {
+	_, resultByte, err := get(req, "/v1/chart_collect/classify/list")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// getUserCollectChartListLib 查询收藏的图表列表
+func getUserCollectChartListLib(req string) (resp *UserCollectChartListResp, err error) {
+	_, resultByte, err := get(req, "/v1/chart_collect/chart/list")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// getChartFromUniqueCodeLib 根据唯一编码查询图表
+func getChartFromUniqueCodeLib(req string) (resp ChartFromUniqueCodeResp, err error) {
+	_, resultByte, err := get(req, "/v1/chart/common/from_unique_code")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// get
+func get(paramStr string, urlStr string) (resp *models.BaseResponse, result []byte, err error) {
+	if utils.ETA_FORUM_HUB_URL == "" {
+		err = fmt.Errorf("ETA社区桥接服务地址为空")
+		return
+	}
+	urlStr = urlStr + "?" + paramStr
+	getUrl := utils.ETA_FORUM_HUB_URL + urlStr
+	result, err = HttpGet(getUrl)
+	if err != nil {
+		err = fmt.Errorf("调用ETA社区桥接服务接口失败 error:%s", err.Error())
+		return
+	}
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func HttpGet(url string) ([]byte, error) {
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("authorization", utils.MD5(utils.ETA_FORUM_HUB_NAME_EN+utils.ETA_FORUM_HUB_MD5_KEY))
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	utils.FileLog.Debug("HttpPost:" + string(b))
+	return b, err
+}

+ 66 - 0
services/excel_info.go

@@ -3,7 +3,9 @@ package services
 import (
 	"encoding/json"
 	"eta/eta_mobile/models"
+	excel3 "eta/eta_mobile/models/data_manage/excel"
 	"eta/eta_mobile/models/system"
+	"eta/eta_mobile/services/data/data_manage_permission"
 	"eta/eta_mobile/utils"
 	"fmt"
 	"time"
@@ -60,3 +62,67 @@ func UpdateExcelEditMark(excelInfoId, nowUserId, status int, nowUserName string)
 	}
 	return
 }
+
+// GetBalanceExcelIdsByAdminId 获取用户有权限的平衡表excelIds
+func GetBalanceExcelIdsByAdminId(adminId int, condition string, pars []interface{}, permissionEdbIdList, permissionClassifyIdList []int) (authIds []int, err error) {
+	//找到当前协作人相关的表格ID
+	obj := new(excel3.ExcelWorker)
+	existList, err := obj.GetBySysUserId(adminId)
+	if err != nil {
+		//br.Msg = "获取表格协作人失败!"
+		//br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
+		return
+	}
+	var excelIds []int
+	newCondition := condition
+	newPars := pars
+	if len(existList) > 0 {
+		for _, v := range existList {
+			excelIds = append(excelIds, v.ExcelInfoId)
+		}
+		newCondition += fmt.Sprintf(` AND  ( excel_info_id IN (%s)  or sys_user_id = ?)`, utils.GetOrmInReplace(len(excelIds)))
+		newPars = append(newPars, excelIds, adminId)
+	} else {
+		newCondition += ` AND  sys_user_id = ? `
+		newPars = append(newPars, adminId)
+	}
+
+	//获取表格信息
+	tmpList, e := excel3.GetNoContentExcelListByConditionNoPage(newCondition, newPars)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		//br.Success = true
+		//br.Msg = "获取表格信息失败"
+		//br.ErrMsg = "获取表格信息失败,Err:" + e.Error()
+		return
+	}
+	classifyIdListTmp := make([]int, 0)
+	for _, v := range tmpList {
+		classifyIdListTmp = append(classifyIdListTmp, v.ExcelClassifyId)
+	}
+	classifyMap := make(map[int]*excel3.ExcelClassify)
+
+	// 分类信息
+	if len(classifyIdListTmp) > 0 {
+		classifyListTmp, e := excel3.GetClassifyByIdList(classifyIdListTmp)
+		if e != nil {
+			//br.Msg = "获取表格分类信息失败"
+			//br.ErrMsg = "获取表格分类列表数据失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range classifyListTmp {
+			classifyMap[v.ExcelClassifyId] = v
+		}
+	}
+	excelIds = make([]int, 0)
+	for _, v := range tmpList {
+		// 数据权限
+		if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+			v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+			if v.HaveOperaAuth {
+				excelIds = append(excelIds, v.ExcelInfoId)
+			}
+		}
+	}
+	authIds = excelIds
+	return
+}

+ 33 - 0
services/system.go

@@ -3,6 +3,7 @@ package services
 import (
 	"eta/eta_mobile/models/company"
 	"eta/eta_mobile/models/system"
+	"fmt"
 )
 
 // CheckAdminIsSameBigGroup 判断是否两个系统用户是否同一个大组内
@@ -54,3 +55,35 @@ func CheckAdminIsSameBigGroup(adminInfo1, adminInfo2 *system.Admin) (isSame bool
 	}
 	return
 }
+
+func BuildGroupTree(rootNode *system.GroupNode, groupList []*system.SysFullGroup, depth, current int) {
+	if current >= depth {
+		return
+	}
+	for _, group := range groupList {
+		if group.ParentId == rootNode.GroupId {
+			childNode := &system.GroupNode{
+				GroupId:   group.GroupId,
+				GroupName: group.GroupName,
+			}
+			BuildGroupTree(childNode, groupList, depth, current+1)
+			rootNode.Child = append(rootNode.Child, childNode)
+		}
+	}
+}
+
+func GetGroupName(nodes []*system.GroupNode, groupId int) (find bool, groupName string) {
+	for _, child := range nodes {
+		if child.GroupId == groupId {
+			return true, child.GroupName
+		}
+		if len(child.Child) > 0 {
+			find, subGroupName := GetGroupName(child.Child, groupId)
+			if find {
+				groupName = fmt.Sprintf("%s/%s", child.GroupName, subGroupName)
+				return true, groupName
+			}
+		}
+	}
+	return false, ""
+}

+ 19 - 0
utils/common.go

@@ -2543,3 +2543,22 @@ func GetCurrentTime() string {
 func IsAdminRole(roleTypeCode string) bool {
 	return roleTypeCode == ROLE_TYPE_CODE_ADMIN
 }
+
+func GetDuration(filePath string) (duration string, err error) {
+	// 构建 FFmpeg 命令,使用 ffprobe 来获取媒体文件信息
+	cmd := exec.Command("ffprobe", "-i", filePath, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0")
+	var out bytes.Buffer
+	cmd.Stdout = &out
+
+	// 执行命令并捕获输出
+	err = cmd.Run()
+	if err != nil {
+		return "", err
+	}
+
+	// 使用正则表达式匹配输出中的时长信息
+	re := regexp.MustCompile(`\d+\.\d+`)
+	duration = re.FindString(out.String())
+
+	return duration, nil
+}

+ 15 - 0
utils/config.go

@@ -98,6 +98,8 @@ var (
 	EsEnglishReportIndexName string //英文研报ES索引
 	MY_CHART_INDEX_NAME      string //研究图库(MY ETA)索引
 	SmartReportIndexName     string //智能研报ES索引
+	EsExcelIndexName         string // 表格ES索引名称
+	EsDataSourceIndexName    string // 数据源ES索引名称
 )
 
 // 科大讯飞--语音合成
@@ -207,6 +209,13 @@ var (
 	UseMongo bool // 是否使用mongo
 )
 
+// eta_forum_hub ETA社区桥接服务地址
+var (
+	ETA_FORUM_HUB_URL     string
+	ETA_FORUM_HUB_NAME_EN string
+	ETA_FORUM_HUB_MD5_KEY string
+)
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {
@@ -458,6 +467,12 @@ func init() {
 	// chrome配置
 	ChromePath = config["chrome_path"]
 
+	// eta_forum_hub ETA社区桥接服务地址
+	{
+		ETA_FORUM_HUB_URL = config["eta_forum_hub_url"]
+		ETA_FORUM_HUB_NAME_EN = config["eta_forum_hub_name_en"]
+		ETA_FORUM_HUB_MD5_KEY = config["eta_forum_hub_md5_key"]
+	}
 	// 初始化ES
 	initEs()
 }

+ 1 - 0
utils/constants.go

@@ -354,6 +354,7 @@ const (
 // 图表样式类型
 const (
 	CHART_TYPE_CURVE           = 1  //曲线图
+	CHART_TYPE_AREA            = 3  // 面积图
 	CHART_TYPE_BAR             = 7  //柱形图
 	CHART_TYPE_SECTION_SCATTER = 10 //截面散点图样式
 	CHART_TYPE_RADAR           = 11 //雷达图

+ 135 - 0
utils/date_util.go

@@ -0,0 +1,135 @@
+package utils
+
+import (
+	"time"
+)
+
+// 定义时间格式常量
+const (
+	YearMonthDay     = "2006-01-02"                     // yyyy-MM-dd
+	YearMonthDayTime = "2006-01-02 15:04:05"            // yyyy-MM-dd HH:mm:ss
+	MonthDay         = "01-02"                          // MM-dd
+	DayMonthYear     = "02-01-2006"                     // dd-MM-yyyy
+	YearMonth        = "2006-01"                        // yyyy-MM
+	FullDate         = "Monday, 02-Jan-06 15:04:05 PST" // 完整日期:例如:Monday, 02-Jan-06 15:04:05 PST
+)
+
+// GetPreYearTime 获取当前时间 前n年的时间 返回yyyy-MM-dd 格式的时间
+func GetPreYearTime(n int) string {
+	// 获取当前时间
+	now := time.Now()
+	// 计算前n年的时间
+	preYearTime := now.AddDate(-n, 0, 0)
+	// 格式化时间
+	return preYearTime.Format("2006-01-02")
+}
+
+// IsMoreThanOneDay 判断两个yyyy-MM-dd类型的时间,相差是否大于1天
+func IsMoreThanOneDay(startDate, endDate string) bool {
+	startTime, _ := time.Parse("2006-01-02", startDate)
+	endTime, _ := time.Parse("2006-01-02", endDate)
+	diff := endTime.Sub(startTime)
+	days := diff.Hours() / 24
+	return days > 1
+}
+
+// GetNextDay 获取 yyyy-MM-dd类型的时间的下一天
+func GetNextDay(date string) string {
+	t, _ := time.Parse("2006-01-02", date)
+	nextDay := t.AddDate(0, 0, 1)
+	return nextDay.Format("2006-01-02")
+}
+
+// GetNextDayN 获取 yyyy-MM-dd 类型的时间的下n天
+func GetNextDayN(date string, n int) string {
+	t, _ := time.Parse("2006-01-02", date)
+	nextDay := t.AddDate(0, 0, n)
+	return nextDay.Format("2006-01-02")
+}
+
+var daysOfMonth = [...]int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+
+// AddDate 解决 Go time 包 AddDate() 添加年份/月份溢出到下一个月的问题。
+// 例如:
+//
+//	2024-02-29 AddDate(1, 0, 0) 期望结果: 2025-02-28
+//	2024-08-31 AddDate(0, 1, 1) 期望结果: 2024-09-30
+func AddDate(t time.Time, years, months int) time.Time {
+	month := t.Month()
+
+	// 规范年份和月份
+	years, months = norm(years, months, 12)
+
+	// 计算目标月份
+	targetMonth := int(month) + months
+	if targetMonth <= 0 {
+		// 处理负值月份
+		targetMonth += 12 * ((-targetMonth)/12 + 1)
+	}
+	// 取余计算目标月份
+	targetMonth = (targetMonth-1)%12 + 1
+
+	// 计算目标年份
+	targetYear := t.Year() + years + (int(month)+months-1)/12
+
+	// 计算目标月份最大天数
+	maxDayOfTargetMonth := daysOfMonth[targetMonth-1]
+	if isLeap(targetYear) && targetMonth == 2 {
+		maxDayOfTargetMonth++ // 闰年2月多一天
+	}
+
+	// 计算目标日期
+	targetDay := t.Day()
+	if targetDay > maxDayOfTargetMonth {
+		// 如果目标日期超出该月的天数,设置为该月的最后一天
+		targetDay = maxDayOfTargetMonth
+	}
+
+	// 返回新的日期
+	return time.Date(targetYear, time.Month(targetMonth), targetDay, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
+}
+
+// norm 规范化年份和月份
+func norm(hi, lo, base int) (nhi, nlo int) {
+	if lo < 0 {
+		n := (-lo-1)/base + 1
+		hi -= n
+		lo += n * base
+	}
+	if lo >= base {
+		n := lo / base
+		hi += n
+		lo -= n * base
+	}
+	return hi, lo
+}
+
+// isLeap 判断是否为闰年
+func isLeap(year int) bool {
+	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+}
+
+// StringToTime string 类型时间 转换为 time.Time 类型
+func StringToTime(date string) time.Time {
+	t, _ := time.ParseInLocation("2006-01-02", date, time.Local)
+	return t
+}
+
+// StringToFormatTime string 类型时间 转换为指定格式的 time.Time类型
+func StringToFormatTime(data string, format string) time.Time {
+	t, _ := time.ParseInLocation(format, data, time.Local)
+	return t
+}
+
+// TimeToString time.Time 类型时间 转换为 string 类型
+func TimeToString(t time.Time, format string) string {
+	formattedTime := t.Format(format)
+	return formattedTime
+}
+
+// CompareDate 判断传入的两个字符串时间的前后顺序
+func CompareDate(data1, data2 string) bool {
+	t1, _ := time.Parse("2006-01-02", data1)
+	t2, _ := time.Parse("2006-01-02", data2)
+	return !t1.After(t2)
+}