Browse Source

Merge remote-tracking branch 'origin/aj_mysteel' into ths_replace_url

# Conflicts:
#	models/base_from_ths.go
#	models/future_good/future_good_edb_data.go
Roc 1 year ago
parent
commit
bd3a67b2a8
85 changed files with 11650 additions and 482 deletions
  1. 1 0
      controllers/base_auth.go
  2. 100 17
      controllers/base_from_calculate.go
  3. 157 0
      controllers/base_from_fubao.go
  4. 1 1
      controllers/base_from_mysteel.go
  5. 49 0
      controllers/base_from_mysteel_chemical.go
  6. 135 0
      controllers/base_from_national_statistics.go
  7. 2 2
      controllers/base_from_predict.go
  8. 55 9
      controllers/base_from_predict_calculate.go
  9. 33 22
      controllers/base_from_sci.go
  10. 138 0
      controllers/base_from_stock_plant.go
  11. 81 0
      controllers/future_good/future_good_edb_info.go
  12. 118 0
      docs/富宝数据API.md
  13. BIN
      docs/富宝数据终端202210最终.xlsx
  14. 6 0
      go.mod
  15. 12 0
      go.sum
  16. 20 19
      logic/predict_edb.go
  17. 864 0
      logic/profit_chart_info.go
  18. 39 0
      logic/sic.go
  19. 0 3
      models/base_from_adjust.go
  20. 17 16
      models/base_from_calculate.go
  21. 157 0
      models/base_from_fubao.go
  22. 13 0
      models/base_from_manual.go
  23. 104 0
      models/base_from_mysteel_chemical.go
  24. 183 0
      models/base_from_national_statistics.go
  25. 9 14
      models/base_from_pb.go
  26. 2 14
      models/base_from_pb_finance.go
  27. 9 19
      models/base_from_python.go
  28. 79 2
      models/base_from_sci.go
  29. 129 0
      models/base_from_stock_plant.go
  30. 17 13
      models/base_from_ths.go
  31. 9 12
      models/base_from_wind.go
  32. 2 3
      models/base_predict_from_calculate.go
  33. 41 0
      models/chart.go
  34. 11 0
      models/chart_edb_mapping.go
  35. 93 0
      models/common.go
  36. 29 2
      models/db.go
  37. 48 2
      models/edb_data_base.go
  38. 1 1
      models/edb_data_calculate_cjjx.go
  39. 538 0
      models/edb_data_calculate_correlation.go
  40. 614 0
      models/edb_data_calculate_ljz.go
  41. 464 0
      models/edb_data_calculate_ljznczj.go
  42. 397 0
      models/edb_data_calculate_ljzzj.go
  43. 1 1
      models/edb_data_calculate_nh.go
  44. 27 4
      models/edb_data_calculate_nszydbpjjs.go
  45. 433 0
      models/edb_data_calculate_percentile.go
  46. 372 0
      models/edb_data_calculate_standard_deviation.go
  47. 2 2
      models/edb_data_calculate_tbz.go
  48. 28 0
      models/edb_data_table.go
  49. 58 3
      models/edb_info.go
  50. 88 0
      models/future_good/chart_info_future_good_profit.go
  51. 43 13
      models/future_good/future_good_edb_data.go
  52. 22 0
      models/future_good/future_good_edb_info.go
  53. 22 1
      models/handle_data.go
  54. 3 11
      models/predict_edb.go
  55. 9 11
      models/predict_edb_conf.go
  56. 17 1
      models/predict_edb_data_calculate_jp.go
  57. 627 0
      models/predict_edb_data_calculate_ljz.go
  58. 475 0
      models/predict_edb_data_calculate_ljznczj.go
  59. 420 0
      models/predict_edb_data_calculate_ljzzj.go
  60. 16 1
      models/predict_edb_data_calculate_nh.go
  61. 24 0
      models/predict_edb_data_calculate_nszydbpjjs.go
  62. 441 0
      models/predict_edb_data_calculate_percentile.go
  63. 423 0
      models/predict_edb_data_calculate_standard_deviation.go
  64. 3 3
      models/predict_edb_data_calculate_tbz.go
  65. 420 20
      models/predict_edb_info_rule.go
  66. 377 0
      models/supply_analysis/base_from_stock_plant_data.go
  67. 215 0
      models/supply_analysis/variety.go
  68. 22 0
      models/supply_analysis/variety_admin_permission.go
  69. 101 0
      models/supply_analysis/variety_edb_info.go
  70. 113 0
      models/supply_analysis/variety_plant.go
  71. 90 0
      routers/commentsRouter.go
  72. 15 0
      routers/router.go
  73. 29 0
      services/base_from_fubao.go
  74. 148 0
      services/base_from_mysteel_chemical.go
  75. 5 11
      services/base_from_pb.go
  76. 3 8
      services/base_from_python.go
  77. 175 0
      services/base_from_sci.go
  78. 10 46
      services/base_from_ths.go
  79. 5 11
      services/base_from_wind.go
  80. 715 70
      swagger/swagger.json
  81. 665 39
      swagger/swagger.yml
  82. 1 1
      utils/base_from_calculate.go
  83. 76 0
      utils/calculate.go
  84. 18 0
      utils/common.go
  85. 116 54
      utils/constants.go

+ 1 - 0
controllers/base_auth.go

@@ -50,6 +50,7 @@ func (this *BaseAuthController) Prepare() {
 				return
 			}
 			checkAuthorization := utils.MD5(utils.APP_NAME_EN + utils.Md5Key)
+			fmt.Println(checkAuthorization)
 			if authorization != checkAuthorization {
 				this.JSON(models.BaseResponse{Ret: 408, Msg: "签名错误!", ErrMsg: "签名错误:authorization is err "}, false, false)
 				this.StopRun()

+ 100 - 17
controllers/base_from_calculate.go

@@ -138,7 +138,7 @@ func (this *CalculateController) Add() {
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := services.CheckFormula(req.CalculateFormula)
+	formulaMap := utils.CheckFormula(req.CalculateFormula)
 	for _, v := range formulaMap {
 		if !strings.Contains(formulaStr, v) {
 			br.Msg = "公式错误,请重新填写"
@@ -402,7 +402,7 @@ func (this *CalculateController) Edit() {
 		}
 	}
 
-	formulaMap := services.CheckFormula(req.CalculateFormula)
+	formulaMap := utils.CheckFormula(req.CalculateFormula)
 	for _, v := range formulaMap {
 		if !strings.Contains(formulaStr, v) {
 			br.Msg = "公式错误,请重新填写"
@@ -543,7 +543,7 @@ func (this *CalculateController) BatchSave() {
 		}
 	}
 
-	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_CALCULATE_KSZS} // 不需要传入来源指标id的 指标类型
+	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_CALCULATE_KSZS, utils.DATA_SOURCE_CALCULATE_CORRELATION} // 不需要传入来源指标id的 指标类型
 	if fromEdbInfoId <= 0 && !utils.InArrayByInt(notNeedFromEdbSourceList, req.Source) {
 		br.Msg = "请选择指标"
 		return
@@ -605,6 +605,18 @@ func (this *CalculateController) BatchSave() {
 	var sourName string
 	var edbInfoId int
 	var edbInfo *models.EdbInfo
+	var errMsg string
+
+	var baseEdbInfoModel models.BaseEdbInfoInterface
+	addParam := models.AddCalculateBatchParams{
+		Req:             &req,
+		FromEdbInfo:     fromEdbInfo,
+		EdbCode:         edbCode,
+		UniqueCode:      uniqueCode,
+		SysUserId:       sysUserId,
+		SysUserRealName: sysUserName,
+	}
+
 	switch req.Source {
 	case utils.DATA_SOURCE_CALCULATE_LJZZY:
 		sourName = "累计值转月值"
@@ -767,14 +779,35 @@ func (this *CalculateController) BatchSave() {
 		}
 		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
 		edbInfo, err = models.AddCalculateKszs(&req, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			br.Msg = "指标数量不能小于2个,请重新选择"
+			br.ErrMsg = "指标数量不能小于2个,请重新选择"
+			br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_CORRELATION
+		edbInfo, err, errMsg = models.AddCalculateCorrelation(&req, edbCode, uniqueCode, sysUserId, sysUserName)
 	default:
-		br.Msg = "无效计算方式"
-		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
-		return
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBaseEdbInfoModel(req.Source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			br.Msg = "无效计算方式"
+			br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+			return
+		}
+		sourName = baseEdbInfoModel.GetSourceName()
+		edbInfo, err, errMsg = baseEdbInfoModel.Add(addParam)
 	}
+
 	if err != nil {
 		br.Msg = "生成" + sourName + "失败"
-		br.Msg = "生成" + sourName + "失败 Err:" + err.Error()
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "生成" + sourName + "失败 Err:" + err.Error()
 		return
 	}
 
@@ -807,7 +840,7 @@ func (this *CalculateController) BatchSave() {
 	}
 
 	// 更新指标最大最小值
-	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()
@@ -989,8 +1022,14 @@ func (this *CalculateController) BatchEdit() {
 		}
 	}
 
-	var sourName string
+	var sourName, errMsg string
 	var edbInfoId int
+	var baseEdbInfoModel models.BaseEdbInfoInterface
+	editParams := models.EditCalculateBatchParams{
+		Req:         &req,
+		EdbInfo:     edbInfo,
+		FromEdbInfo: fromEdbInfo,
+	}
 
 	switch req.Source {
 	case utils.DATA_SOURCE_CALCULATE_LJZZY:
@@ -1143,14 +1182,33 @@ func (this *CalculateController) BatchEdit() {
 		}
 		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
 		err = models.EditCalculateKszs(edbInfo, &req)
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			br.Msg = "指标数量不能小于2个,请重新选择"
+			br.ErrMsg = "指标数量不能小于2个,请重新选择"
+			br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_CORRELATION
+		err, errMsg = models.EditCalculateCorrelation(edbInfo, &req)
 	default:
-		br.Msg = "无效计算方式"
-		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
-		return
-
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBaseEdbInfoModel(req.Source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			br.Msg = "无效计算方式"
+			br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+			return
+		}
+		sourName = baseEdbInfoModel.GetSourceName()
+		err, errMsg = baseEdbInfoModel.Edit(editParams)
 	}
 	if err != nil {
 		br.Msg = "生成" + sourName + "失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
 		br.Msg = "生成" + sourName + "失败 Err:" + err.Error()
 		return
 	}
@@ -1183,7 +1241,7 @@ func (this *CalculateController) BatchEdit() {
 	}
 
 	// 更新指标最大最小值
-	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()
@@ -1258,6 +1316,13 @@ func (this *CalculateController) Refresh() {
 	edbInfoId := edbInfo.EdbInfoId
 	source := edbInfo.Source
 
+	var baseEdbInfoModel models.BaseEdbInfoInterface
+	refreshParams := models.RefreshParams{
+		EdbInfo:   edbInfo,
+		StartDate: "", // 更新所有
+		EndDate:   "", // 更新所有数据
+	}
+
 	switch source {
 	case utils.DATA_SOURCE_CALCULATE:
 		//startDate = edbInfo.StartDate
@@ -1557,10 +1622,28 @@ func (this *CalculateController) Refresh() {
 			errMsg = "RefreshAllCalculateKszs Err:" + err.Error()
 			break
 		}
+	case utils.DATA_SOURCE_STOCK_PLANT: //存量装置
+		//startDate = edbInfo.StartDate
+		endDate = time.Now().Format(utils.FormatDate)
+		req.StartDate = `` // 开始日期也给过滤吧,万一研究员补充了之前的数据呢,还是过滤掉吧
+		err = models.RefreshEdbDataFromStockPlant(req.EdbInfoId, req.EdbCode, req.StartDate)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshEdbDataFromStockPlant Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		err, errMsg = models.RefreshAllCalculateCorrelation(edbInfo)
 	default:
-		br.Msg = "来源异常,请联系相关开发!"
-		br.ErrMsg = "来源异常,请联系相关开发"
-		return
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBaseEdbInfoModel(source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			br.Msg = "来源异常,请联系相关开发!"
+			br.ErrMsg = "来源异常,请联系相关开发"
+			return
+		}
+		err, errMsg = baseEdbInfoModel.Refresh(refreshParams)
+
 	}
 	if errMsg != `` {
 		br.Msg = "刷新指标失败!"

+ 157 - 0
controllers/base_from_fubao.go

@@ -0,0 +1,157 @@
+package controllers
+
+import (
+	"encoding/json"
+	"hongze/hongze_edb_lib/logic"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 富宝-数据对接
+type FuBaoController struct {
+	BaseAuthController
+}
+
+// @Title 新增富宝指标接口
+// @Description  新增富宝指标接口
+// @Success 200 {object} models.AddEdbInfoReq
+// @router /add [post]
+func (this *FuBaoController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_FUBAO
+	var req models.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_ADD + strconv.Itoa(source) + "_" + req.EdbCode
+	if !utils.Rc.IsExist(cacheKey) {
+		utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+		dataItem, err := services.GetEdbDataFromFuBao(req.EdbCode, utils.BASE_START_DATE, utils.BASE_END_DATE)
+		if err != nil {
+			br.Msg = "获取指标信息失败!"
+			br.ErrMsg = "获取指标信息失败 GetEdbDataFromLt,Err:" + err.Error()
+			return
+		}
+		if dataItem.Code != 200 {
+			br.Msg = "获取富宝指标数据失败!"
+			br.ErrMsg = "获取富宝指标数据失败 GetEdbDataFromLt,Code:" + strconv.Itoa(dataItem.Code)
+			return
+		}
+		if len(dataItem.ReturnValue.Data) > 0 {
+			priceStr := dataItem.ReturnValue.Data[0].PriceStr
+			strIndex := strings.Index(priceStr, "-")
+			if strIndex > 0 {
+				br.Msg = "此指标的数据格式暂不支持!"
+				br.ErrMsg = "此指标的数据格式暂不支持,Val:" + priceStr + ";pid:" + req.EdbCode
+				return
+			}
+		}
+
+		err = models.AddEdbDataFromFubao(req.EdbCode, dataItem)
+		if err != nil {
+			br.Msg = "获取指标信息失败!"
+			br.ErrMsg = "获取指标信息失败 AddEdbDataFromFubao,Err:" + err.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+	} else {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+	}
+}
+
+// @Title 刷新富宝指标接口
+// @Description 刷新路透社指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *FuBaoController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_FUBAO
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+
+	dataItem, err := services.GetEdbDataFromFuBao(req.EdbCode, req.StartDate, utils.BASE_END_DATE)
+	if err != nil {
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取指标信息失败 GetEdbDataFromFuBao,Err:" + err.Error()
+		return
+	}
+	err = models.RefreshEdbDataFromFubao(req.EdbInfoId, req.EdbCode, req.StartDate, dataItem)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromFubao,Err:" + err.Error()
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 1 - 1
controllers/base_from_mysteel.go

@@ -9,7 +9,7 @@ import (
 	"time"
 )
 
-//钢联
+// 钢联
 type MySteelController struct {
 	BaseAuthController
 }

+ 49 - 0
controllers/base_from_mysteel_chemical.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"hongze/hongze_edb_lib/logic"
 	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"time"
@@ -128,3 +129,51 @@ func (this *MySteelChemicalController) Refresh() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// @Title 查询刷新钢联指标的接口
+// @Description 查询刷新钢联指标的接口
+// @Success 200 {object} models.CheckDeleteClassifyResp
+// @router /query/refresh [post]
+func (this *MySteelChemicalController) QueryRefresh() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	mysteelIndex := new(models.BaseFromMysteelChemicalIndex)
+	list, err := mysteelIndex.GetIndexRefreshAllByMergeFile()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败!"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Data = list
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// @Title 处理钢联指标的接口
+// @Description 处理钢联指标的接口
+// @Success 200 {object} models.HandleMysteelIndexResp
+// @router /handle/mysteel/index [post]
+func (this *MySteelChemicalController) HandleMysteelIndex() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.HandleMysteelIndexResp
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	services.HandleMysteelIndex(&req)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 135 - 0
controllers/base_from_national_statistics.go

@@ -0,0 +1,135 @@
+package controllers
+
+import (
+	"encoding/json"
+	"hongze/hongze_edb_lib/logic"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"time"
+)
+
+// NationalStatisticsController 国家统计局
+type NationalStatisticsController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增指标
+// @Description 新增指标
+// @Success 200 {object} models.AddEdbInfoReq
+// @router /add [post]
+func (this *NationalStatisticsController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+
+	source := utils.DATA_SOURCE_NATIONAL_STATISTICS
+	cacheKey = utils.CACHE_EDB_DATA_ADD + strconv.Itoa(source) + "_" + req.EdbCode
+	if !utils.Rc.IsExist(cacheKey) {
+		utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+		err = models.AddEdbDataFromNationalStatistics(req.EdbCode)
+		if err != nil {
+			br.Msg = "获取指标信息失败!"
+			br.ErrMsg = "获取指标信息失败 AddEdbDataFromNationalStatistics,Err:" + err.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+	} else {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+	}
+}
+
+// Refresh
+// @Title 刷新指标
+// @Description 刷新指标
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *NationalStatisticsController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId < 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+	source := utils.DATA_SOURCE_NATIONAL_STATISTICS
+
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+	}()
+	if req.EdbInfoId <= 0 {
+		req.EdbInfoId = edbInfo.EdbInfoId
+	}
+	err = models.RefreshEdbDataFromNationalStatistics(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromNationalStatistics,Err:" + err.Error()
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 2 - 2
controllers/base_from_predict.go

@@ -58,7 +58,7 @@ func addPredict(br *models.BaseResponse, req models.AddPredictEdbInfoReq) {
 	}
 
 	// 添加指标
-	edbInfo, err, errMsg := logic.AddPredictEdbInfo(req.SourceEdbInfoId, req.ClassifyId, req.EdbName, req.RuleList, req.AdminId, req.AdminName)
+	edbInfo, err, errMsg := logic.AddPredictEdbInfo(req.SourceEdbInfoId, req.ClassifyId, req.EdbName, req.RuleList, req.MinValue, req.MaxValue, req.AdminId, req.AdminName)
 	if err != nil {
 		br.Msg = "保存失败"
 		if errMsg != `` {
@@ -96,7 +96,7 @@ func editPredict(br *models.BaseResponse, req models.AddPredictEdbInfoReq) {
 	}
 
 	// 编辑指标
-	edbInfo, err, errMsg := logic.EditPredictEdbInfo(req.EdbInfoId, req.ClassifyId, req.EdbName, req.RuleList)
+	edbInfo, err, errMsg := logic.EditPredictEdbInfo(req.EdbInfoId, req.ClassifyId, req.EdbName, req.RuleList, req.MinValue, req.MaxValue)
 	if err != nil {
 		br.Msg = "保存失败"
 		if errMsg != `` {

+ 55 - 9
controllers/base_from_predict_calculate.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"hongze/hongze_edb_lib/logic"
 	"hongze/hongze_edb_lib/models"
-	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -107,7 +106,7 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := services.CheckFormula(req.CalculateFormula)
+	formulaMap := utils.CheckFormula(req.CalculateFormula)
 	for _, v := range formulaMap {
 		if !strings.Contains(formulaStr, v) {
 			br.Msg = "公式错误,请重新填写"
@@ -335,7 +334,7 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := services.CheckFormula(req.CalculateFormula)
+	formulaMap := utils.CheckFormula(req.CalculateFormula)
 	for _, v := range formulaMap {
 		if !strings.Contains(formulaStr, v) {
 			br.Msg = "公式错误,请重新填写"
@@ -653,6 +652,16 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		}
 	}
 
+	var baseEdbInfoModel models.BasePredictEdbInfoInterface
+	saveParams := models.BatchSaveCalculateBatchParams{
+		Req:             req,
+		FromEdbInfo:     fromEdbInfo,
+		EdbCode:         edbCode,
+		UniqueCode:      uniqueCode,
+		SysUserId:       req.AdminId,
+		SysUserRealName: req.AdminName,
+	}
+
 	if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_TBZ {
 		if req.EdbInfoId <= 0 {
 			if req.Frequency != fromEdbInfo.Frequency {
@@ -743,9 +752,33 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS
 		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateKszs(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, req.EdbInfoIdArr, edbCode, uniqueCode, adminId, adminName)
 	} else {
-		br.Msg = "无效计算方式"
-		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
-		return
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBasePredictEdbInfoModel(req.Source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			br.Msg = "无效计算方式"
+			br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+			return
+		}
+
+		sourName = baseEdbInfoModel.GetSourceName()
+		if req.EdbInfoId <= 0 {
+			edbInfo, latestDateStr, latestValue, err, errMsg = baseEdbInfoModel.Add(saveParams)
+		} else {
+			edbInfo, err = models.GetEdbInfoById(req.EdbInfoId)
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					br.Msg = "指标已被删除,请刷新页面"
+					br.ErrMsg = "指标已被删除,请刷新页面:Err:" + err.Error()
+					return
+				}
+				br.Msg = "获取指标信息失败"
+				br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+				return
+			}
+			saveParams.EdbInfo = edbInfo
+			latestDateStr, latestValue, err, errMsg = baseEdbInfoModel.Edit(saveParams)
+		}
 	}
 
 	if err != nil {
@@ -859,6 +892,12 @@ func (this *PredictCalculateController) Refresh() {
 
 	var latestDateStr string // 最近实际数据的日期
 	var latestValue float64  // 最近实际数据的值
+
+	var baseEdbInfoModel models.BasePredictEdbInfoInterface
+	refreshParams := models.RefreshParams{
+		EdbInfo: edbInfo,
+	}
+
 	switch source {
 	case utils.DATA_SOURCE_PREDICT_CALCULATE:
 		//startDate = edbInfo.StartDate
@@ -1112,9 +1151,16 @@ func (this *PredictCalculateController) Refresh() {
 			break
 		}
 	default:
-		br.Msg = "来源异常,请联系相关开发!"
-		br.ErrMsg = "来源异常,请联系相关开发"
-		return
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBasePredictEdbInfoModel(source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			br.Msg = "来源异常,请联系相关开发!"
+			br.ErrMsg = "来源异常,请联系相关开发"
+			return
+		}
+		latestDateStr, latestValue, err, errMsg = baseEdbInfoModel.Refresh(refreshParams)
+
 	}
 	if errMsg != `` {
 		br.Msg = "刷新指标失败!"

+ 33 - 22
controllers/base_from_sci.go

@@ -4,16 +4,18 @@ import (
 	"encoding/json"
 	"hongze/hongze_edb_lib/logic"
 	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"time"
 )
 
-//有色
+// SciController 卓创(红桃3)
 type SciController struct {
 	BaseAuthController
 }
 
+// Add
 // @Title 新增Sci指标接口
 // @Description 新增Sci指标接口
 // @Success 200 {object} models.AddEdbInfoReq
@@ -58,13 +60,13 @@ func (this *SciController) Add() {
 	}
 }
 
+// Refresh
 // @Title 刷新sci指标接口
 // @Description 刷新sci指标接口
 // @Success 200 {object} models.RefreshEdbInfoReq
 // @router /refresh [post]
 func (this *SciController) Refresh() {
 	br := new(models.BaseResponse).Init()
-	var cacheKey string
 	defer func() {
 		this.Data["json"] = br
 		this.ServeJSON()
@@ -95,38 +97,47 @@ func (this *SciController) Refresh() {
 		br.ErrMsg = "指标不存在"
 		return
 	}
-	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
-	if utils.Rc.IsExist(cacheKey) {
+	isHandling, errMsg, err := logic.RefreshSci(edbInfo, req.StartDate)
+	if isHandling {
 		br.Ret = 501
 		br.Success = true
 		br.Msg = "系统处理中,请稍后重试"
 		return
 	}
-	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
-	defer func() {
-		utils.Rc.Delete(cacheKey)
-	}()
-	if req.EdbInfoId <= 0 {
-		req.EdbInfoId = edbInfo.EdbInfoId
-	}
-	err = models.RefreshEdbDataFromSci(req.EdbInfoId, req.EdbCode, req.StartDate)
-	if err != nil && err.Error() != utils.ErrNoRow() {
-		br.Msg = "刷新指标信息失败!"
-		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromSci,Err:" + err.Error()
-		return
-	}
-	// 更新指标最大最小值
-	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()
 		return
 	}
 
-	// 更新ES
-	go logic.UpdateEs(edbInfo.EdbInfoId)
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// HandleExcelData
+// @Title 处理卓创(红桃3)指标的接口
+// @Description 处理卓创(红桃3)指标的接口
+// @Success 200 {object} models.HandleMysteelIndexResp
+// @router /handle/excel_data [post]
+func (this *SciController) HandleExcelData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.HandleSciExcelDataReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	// 处理excel表数据
+	services.HandleSciIndex(req.IndexNameList, req.ThirdIndexIdList, req.FrequencyList, req.UnitList, req.DataMap)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 138 - 0
controllers/base_from_stock_plant.go

@@ -0,0 +1,138 @@
+package controllers
+
+import (
+	"encoding/json"
+	"hongze/hongze_edb_lib/logic"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/models/supply_analysis"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"time"
+)
+
+// StockPlantController 存量装置
+type StockPlantController struct {
+	BaseAuthController
+}
+
+// Calculate
+// @Title 计算减产数
+// @Description 计算减产数
+// @Success 200 {object} supply_analysis.CalculateVarietyReq
+// @router /calculate [post]
+func (this *StockPlantController) Calculate() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req supply_analysis.CalculateVarietyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.VarietyId <= 0 {
+		br.Msg = "请选择品种!"
+		br.ErrMsg = "请选择品种"
+		return
+	}
+
+	//cacheKey = utils.CACHE_STOCK_PLANT_CALCULATE + strconv.Itoa(req.VarietyId)
+	//if utils.Rc.IsExist(cacheKey) {
+	//	br.Ret = 501
+	//	br.Success = true
+	//	br.Msg = "系统处理中,请稍后重试"
+	//	return
+	//}
+
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+
+	err = supply_analysis.Calculate(req.VarietyId, req.AdminId, req.AdminName)
+	if err != nil {
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取指标信息失败 AddEdbDataFromManual,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// @Title 刷新手工指标接口
+// @Description 刷新手工指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *StockPlantController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_STOCK_PLANT
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+
+	cacheKey := utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+
+	// 设置缓存
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+
+	req.StartDate = `` // 开始日期也给过滤吧,万一研究员补充了之前的数据呢,还是过滤掉吧
+	err = models.RefreshEdbDataFromStockPlant(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromManual,Err:" + err.Error()
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 81 - 0
controllers/future_good/future_good_edb_info.go

@@ -2,7 +2,9 @@ package future_good
 
 import (
 	"encoding/json"
+	"fmt"
 	"hongze/hongze_edb_lib/controllers"
+	"hongze/hongze_edb_lib/logic"
 	"hongze/hongze_edb_lib/models"
 	"hongze/hongze_edb_lib/models/future_good"
 	"hongze/hongze_edb_lib/services"
@@ -192,6 +194,85 @@ func (this *FutureGoodEdbInfoController) Refresh() {
 	br.Msg = "获取成功"
 }
 
+// RefreshRelation
+// @Title 刷新商品期货指标相关的接口
+// @Description 刷新商品期货指标相关的接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /relation/refresh [post]
+func (this *FutureGoodEdbInfoController) RefreshRelation() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req future_good.RefreshFutureEdbEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.FutureGoodEdbInfoId <= 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + "_futuregood_relation_" + req.FutureGoodEdbCode
+
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+
+	//获取指标信息
+	futureGoodEdbInfo, err := future_good.GetFutureGoodEdbInfo(req.FutureGoodEdbInfoId)
+	if err != nil {
+		if err.Error() != utils.ErrNoRow() {
+			br.Msg = "系统内找不到该指标"
+		} else {
+			br.Msg = "刷新失败"
+			br.ErrMsg = "添加失败,ERR:" + err.Error()
+		}
+		return
+	}
+
+	// 获取相关图表
+	list, err := models.GetGroupChartEdbMappingListByEdbInfoId(futureGoodEdbInfo.FutureGoodEdbInfoId, 2)
+	if err != nil {
+		br.Msg = "查找相关图表id失败"
+		br.ErrMsg = "添加失败,ERR:" + err.Error()
+		return
+	}
+
+	go func() {
+		errMsgList := make([]string, 0)
+		for _, v := range list {
+			err, errMsg := logic.RefreshByChartId(v.ChartInfoId)
+			if err != nil {
+				errMsgList = append(errMsgList, fmt.Sprint(v.ChartInfoId, "更新失败,"+errMsg))
+			}
+		}
+
+		if len(errMsgList) > 0 {
+			//br.Msg = "部分刷新失败"
+			//br.ErrMsg = "部分刷新失败,Err:" + strings.Join(errMsgList, ";")
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "刷新成功"
+}
+
 //func init() {
 //
 //	list, err := future_good.GetFutureGoodEdbInfoList(" AND start_date = '0000-00-00' ", []interface{}{})

+ 118 - 0
docs/富宝数据API.md

@@ -0,0 +1,118 @@
+### 查询单个产品及数据(json)
+
+> get HTTP://{ip}:{port}/cooperation/wande/queryData.do
+
+#### 参数
+
+- `pid` 产品id
+
+#### 响应 
+
+```json5
+{
+	"code": 200,
+	"returnValue": {
+		"product": {
+			"alikeName": "147家钢厂废钢日耗",
+			"diffName": "华北",
+			"propValueStr": "区域:华北地区,报价类型:废钢日耗",
+			"pattern":"华北:147家钢厂废钢日耗"
+		},
+		"data": [{
+				"priceStr": "344460",
+				"uad": "涨2170",
+				"datadate": "2019-07-04 16:31:33"
+			},
+			{
+				"priceStr": "342290",
+				"uad": "跌4010",
+				"datadate": "2019-07-01 09:14:14"
+			}
+		]
+	}
+}
+```
+
+### 查询所有产品的数据(json)
+
+> get HTTP://{ip}:{port}/cooperation/wande/queryLastData.do
+
+#### 响应
+
+```json5
+{
+	"code": 200,
+	"returnValue": {
+		"data": [{
+				"priceStr": "2459.82",
+				"uad": "跌0.07%",
+				"datadate": "2019-07-08 16:14:08",
+				"pid": 13170
+			},
+			{
+				"priceStr": "2470.32",
+				"uad": "跌0.05%",
+				"datadate": "2019-07-08 16:14:08", 
+				"pid": 16865
+			}
+		]
+	}
+}
+
+```
+
+### 查询修正产品的数据(json)
+
+> get HTTP://{ip}:{port}/cooperation/wande/queryReviseData.do
+
+- 如果当天没有修正的价格,不返回returnValue
+- 如果当天有修正的价格,返回的returnValue中的key是产品的id,value[product产品类型,data价格数组]
+
+#### 响应
+
+```json5
+
+	{
+	"code": 200,
+	"returnValue": {
+		"23617": {
+			"product": {
+				"alikeName": "废钢日耗(按区)",
+				"diffName": "华北",
+				"propValueStr": "区域:华北地区,报价类型:废钢日耗"
+			},
+			"data": [{
+					"priceStr": "79000",
+					"uad": "跌500",
+					"datadate": "2019-07-11 15:59:23"
+				},
+				{
+					"priceStr": "79500",
+					"uad": "涨1800",
+					"datadate": "2019-07-04 00:00:00"
+				}
+			]
+		},
+		"23622": {
+			"product": {
+				"alikeName": "废钢日耗(按省)",
+				"diffName": "江苏",
+				"propValueStr": "区域:华东地区,省份:江苏,报价类型:废钢日耗"
+			},
+			"data": [{
+					"priceStr": "82300",
+					"uad": "稳",
+					"datadate": "2019-07-11 15:59:23"
+				},
+				{
+					"priceStr": "82300",
+					"uad": "跌2000",
+					"datadate": "2019-07-04 00:00:00"
+				}
+			]
+		}
+	}
+}
+
+
+``` 

BIN
docs/富宝数据终端202210最终.xlsx


+ 6 - 0
go.mod

@@ -8,6 +8,12 @@ require (
 	github.com/beego/beego/v2 v2.0.2
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-sql-driver/mysql v1.6.0
+	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
+	github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
+	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
+	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
+	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
+	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b // indirect
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/rdlucklib/rdluck_tools v1.0.3

+ 12 - 0
go.sum

@@ -185,6 +185,18 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV38221VAK7qc2zhaO17bKys/18=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 h1:7qnwS9+oeSiOIsiUMajT+0R7HR6hw5NegnKPmn/94oI=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFRbV5juy/C3MGdj4ePi+g6ePIp4=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

+ 20 - 19
logic/predict_edb.go

@@ -3,7 +3,6 @@ package logic
 import (
 	"errors"
 	"hongze/hongze_edb_lib/models"
-	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -11,7 +10,7 @@ import (
 )
 
 // AddPredictEdbInfo 新增预测指标
-func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig, sysUserId int, sysUserName string) (edbInfo *models.EdbInfo, err error, errMsg string) {
+func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig, minValue, maxValue float64, sysUserId int, sysUserName string) (edbInfo *models.EdbInfo, err error, errMsg string) {
 	var sourceEdbInfo *models.EdbInfo
 	// 来源指标信息校验
 	{
@@ -32,11 +31,11 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList
 			err = errors.New(errMsg)
 			return
 		}
-		if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
-			errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
-			err = errors.New(errMsg)
-			return
-		}
+		//if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
+		//	errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
 	}
 
 	var classifyInfo *models.EdbClassify
@@ -104,8 +103,8 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList
 		UniqueCode:       utils.MD5(utils.DATA_PREFIX + "_" + timestamp),
 		CreateTime:       time.Now(),
 		ModifyTime:       time.Now(),
-		MinValue:         sourceEdbInfo.MinValue,
-		MaxValue:         sourceEdbInfo.MaxValue,
+		MinValue:         minValue,
+		MaxValue:         maxValue,
 		CalculateFormula: sourceEdbInfo.CalculateFormula,
 		EdbType:          1,
 		//Sort:             sourceEdbInfo.,
@@ -196,7 +195,7 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := services.CheckFormula(formula)
+			formulaMap := utils.CheckFormula(formula)
 			for _, formula := range formulaMap {
 				if !strings.Contains(formulaStr, formula) {
 					errMsg = "公式错误,请重新填写"
@@ -382,7 +381,7 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList
 }
 
 // EditPredictEdbInfo 编辑预测指标
-func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig) (edbInfo *models.EdbInfo, err error, errMsg string) {
+func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig, minValue, maxValue float64) (edbInfo *models.EdbInfo, err error, errMsg string) {
 	// 指标信息校验
 	{
 		edbInfo, err = models.GetEdbInfoById(edbInfoId)
@@ -451,8 +450,10 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []mo
 	edbInfo.EdbName = edbName
 	edbInfo.EdbNameSource = edbName
 	edbInfo.ClassifyId = classifyId
+	edbInfo.MinValue = minValue
+	edbInfo.MaxValue = maxValue
 	edbInfo.ModifyTime = time.Now()
-	updateEdbInfoCol := []string{"EdbName", "EdbNameSource", "ClassifyId", "EndDate", "ModifyTime"}
+	updateEdbInfoCol := []string{"EdbName", "EdbNameSource", "ClassifyId", "EndDate", "MinValue", "MaxValue", "ModifyTime"}
 
 	var sourceEdbInfo *models.EdbInfo
 	// 来源指标信息校验
@@ -474,11 +475,11 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []mo
 			err = errors.New(errMsg)
 			return
 		}
-		if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
-			errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
-			err = errors.New(errMsg)
-			return
-		}
+		//if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
+		//	errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
 	}
 
 	// 预测指标配置
@@ -560,7 +561,7 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []mo
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := services.CheckFormula(formula)
+			formulaMap := utils.CheckFormula(formula)
 			for _, formula := range formulaMap {
 				if !strings.Contains(formulaStr, formula) {
 					errMsg = "公式错误,请重新填写"
@@ -863,7 +864,7 @@ func RefreshPredictEdbInfo(edbInfoId int) (edbInfo *models.EdbInfo, err error, e
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := services.CheckFormula(formula)
+			formulaMap := utils.CheckFormula(formula)
 			for _, formula := range formulaMap {
 				if !strings.Contains(formulaStr, formula) {
 					errMsg = "公式错误,请重新填写"

+ 864 - 0
logic/profit_chart_info.go

@@ -0,0 +1,864 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"github.com/yidane/formula"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/models/future_good"
+	"hongze/hongze_edb_lib/utils"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ChartInfoDateReq 图表的日期数据(日期相关)
+type ChartInfoDateReq struct {
+	Type  int    `description:"配置类型"`
+	Date  string `description:"固定日期"`
+	Value int    `description:"N天的值"`
+	Color string `description:"颜色"`
+	Name  string `description:"别名"`
+}
+
+// SectionScatterReq 截面散点请求
+type SectionScatterReq struct {
+	XName       string `description:"x轴名称"`
+	XNameEn     string `description:"x轴名称(英文)"`
+	XUnitName   string `description:"x轴单位名称"`
+	XUnitNameEn string `description:"x轴单位名称(英文)"`
+	YName       string `description:"y轴名称"`
+	YNameEn     string `description:"y轴名称(英文)"`
+	YUnitName   string `description:"y轴单位名称"`
+	YUnitNameEn string `description:"y轴单位名称(英文)"`
+	XMinValue   string `description:"X轴的最小值"`
+	XMaxValue   string `description:"X轴的最大值"`
+	YMinValue   string `description:"Y轴的最小值"`
+	YMaxValue   string `description:"Y轴的最大值"`
+	//EdbList     []SectionScatterEdbItemReq    `description:"指标数据"`
+	SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
+}
+
+// SectionScatterSeriesItemReq 系列的请求
+type SectionScatterSeriesItemReq struct {
+	Name            string `description:"系列名"`
+	NameEn          string `description:"系列名(英文名)"`
+	Color           string `description:"颜色"`
+	EdbInfoList     []SectionScatterEdbItemReq
+	ShowTrendLine   bool `description:"是否展示趋势线"`
+	ShowFitEquation bool `description:"是否展示方程式"`
+	ShowRSquare     bool `description:"是否展示R平方"`
+}
+
+// SectionScatterEdbItemReq 截面散点请求的指标
+type SectionScatterEdbItemReq struct {
+	XEdbInfoId int    `description:"X轴的指标ID"`
+	YEdbInfoId int    `description:"Y轴的指标ID"`
+	Name       string `description:"别名"`
+	NameEn     string `description:"英文别名"`
+	XDateType  int    `description:"X轴的日期配置类型"`
+	XDate      string `description:"X轴的日期固定日期"`
+	XDateValue int    `description:"X轴的日期N天的值"`
+	YDateType  int    `description:"Y轴的日期配置类型"`
+	YDate      string `description:"Y轴的日期固定日期"`
+	YDateValue int    `description:"Y轴的日期N天的值"`
+	IsShow     bool   `description:"是否展示"`
+}
+
+// XData 商品价格曲线的的x轴数据
+type XData struct {
+	Name   string `description:"别名"`
+	NameEn string `description:"英文别名"`
+}
+
+// YData 柱方图的y轴数据
+type YData struct {
+	Date           string          `description:"数据日期"`
+	ConfigDate     time.Time       `description:"配置的日期" json:"-"`
+	Color          string          `description:"数据颜色"`
+	Name           string          `description:"别名"`
+	NameEn         string          `description:"英文别名"`
+	Value          []float64       `description:"每个指标的值"`
+	NoDataEdbList  []int           `description:"没有数据的指标列表"`
+	XEdbInfoIdList []int           `description:"对应X轴的指标id列表"`
+	NameList       []string        `description:"每个值对应的名称"`
+	EnNameList     []string        `description:"每个值对应的英文名称"`
+	EdbValMap      map[int]float64 `description:"指标与值的对应" json:"-"`
+	M              []int           `description:"对应开始日期的间隔值" json:"-"`
+}
+
+// BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
+type BarChartInfoEdbItemReq struct {
+	EdbInfoId int    `description:"指标ID"`
+	Name      string `description:"别名"`
+	NameEn    string `description:"英文别名"`
+	Source    int    `description:"1:ETA图库;2:商品价格"`
+}
+
+// ChartInfoReq 图表预览请求数据
+type ChartInfoReq struct {
+	FutureGoodEdbInfoIdList []models.EdbInfoFromTag `description:"指标信息"`
+	CalculateFormula        string                  `description:"计算公式"`
+	BaseEdbInfoId           int                     `description:"基础的指标id"`
+	DateList                []ChartInfoDateReq      `description:"日期配置"`
+	ProfitNameEn            string                  `description:"利润英文名称"`
+}
+
+// RefreshByChartId 根据图表id刷新图表
+func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
+	// 查找图表
+	chartInfo, err := models.GetChartInfoById(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		}
+		return
+	}
+	if chartInfo.Source != utils.CHART_SOURCE_FUTURE_GOOD_PROFIT {
+		return
+	}
+	var extraConf ChartInfoReq
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf)
+	if err != nil {
+		errMsg = "商品利润曲线图配置异常"
+		return
+	}
+
+	// 查找商品利润图表的扩展信息
+	chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit)
+	if err = chartInfoFutureGoodProfit.GetItemById(chartInfoId); err != nil {
+		errMsg = "获取失败"
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		}
+		return
+	}
+
+	baseEdbInfo, err := models.GetEdbInfoById(extraConf.BaseEdbInfoId)
+	if err != nil {
+		errMsg = "获取失败"
+		return
+	}
+	// 商品数据库指标
+	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
+	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
+	for _, v := range extraConf.FutureGoodEdbInfoIdList {
+		if _, ok := futureGoodEdbInfoMap[v.EdbInfoId]; ok {
+			continue
+		}
+
+		zlFutureGoodEdbInfo, tmpErr := future_good.GetFutureGoodEdbInfo(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsg = "获取失败"
+			return
+		}
+
+		futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo
+		zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo)
+	}
+
+	xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList)
+
+	xDataListByte, err := json.Marshal(xDataList)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("X轴数据转换失败,ERR:" + err.Error())
+		return
+	}
+	yDataListByte, err := json.Marshal(yDataList)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("Y轴数据转换失败,ERR:" + err.Error())
+		return
+	}
+
+	extraUpdateCol := make([]string, 0)
+	chartInfoFutureGoodProfit.XValue = string(xDataListByte)
+	chartInfoFutureGoodProfit.YValue = string(yDataListByte)
+	chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
+	chartInfoFutureGoodProfit.ModifyTime = time.Now()
+	extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"}
+	err = chartInfoFutureGoodProfit.Update(extraUpdateCol)
+
+	return
+}
+
+// GetProfitChartEdbData 获取利润图表的指标数据
+func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag) (xDataList []XData, yDataList []YData, err error) {
+	if baseEdbInfo == nil {
+		err = errors.New("ETA指标未选取")
+		return
+	}
+	if len(zlFutureGoodEdbInfoList) <= 0 {
+		err = errors.New("商品指标未选取")
+		return
+	}
+
+	// 标签与期货商品指标的关联关系
+	tagEdbIdMap := make(map[string]int)
+	// 有效的期货商品指标
+	futureGoodEdbInfoIdMap := make(map[int]int)
+	{
+		tmpTagEdbIdMap := make(map[string]int)
+		for _, v := range edbInfoFromTagList {
+			tmpTagEdbIdMap[v.FromTag] = v.EdbInfoId
+		}
+		formulaMap := CheckFormula(formulaStr)
+		for _, tag := range formulaMap {
+			tagEdbIdMap[tag] = tmpTagEdbIdMap[tag]
+			futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
+		}
+	}
+
+	// 普通的指标数据
+	baseDataList := make([]*models.EdbDataList, 0)
+	baseDataList, err = models.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.EdbInfoId, "", "")
+	if err != nil {
+		return
+	}
+
+	latestDate := zlFutureGoodEdbInfoList[0].EndDate
+	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
+
+	earliestDateTime := latestDateTime // 数据的最早日期,目的是为了找出最早的合约
+	for _, barChartInfoDate := range chartInfoDateList {
+		var findDateTime time.Time
+		switch barChartInfoDate.Type {
+		case 1: //最新值
+			findDateTime = latestDateTime
+		case 2: //近期几天
+			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
+		case 3: // 固定日期
+			//寻找固定日期的数据
+			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			findDateTime = tmpFindDateTime
+		default:
+			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
+			return
+		}
+		if findDateTime.IsZero() {
+			err = errors.New("错误的日期")
+			return
+		}
+		if findDateTime.Before(earliestDateTime) {
+			earliestDateTime = findDateTime
+		}
+	}
+
+	monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())
+
+	// 存储主力合约下的所有月份合约
+	futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo)
+	futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0)
+	// 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约)
+	specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)
+
+	isAllChina := true // 是否都是国内的期货合约
+	for _, v := range zlFutureGoodEdbInfoList {
+		if v.RegionType != "国内" {
+			isAllChina = false
+			break
+		}
+	}
+
+	nMap := make(map[string]string)
+	var maxN int // 最大N值
+	for _, v := range zlFutureGoodEdbInfoList {
+		// 如果不是有效的商品期货指标,那么就过滤掉,不做数据查询处理,避免没必要的请求
+		if _, ok := futureGoodEdbInfoIdMap[v.FutureGoodEdbInfoId]; !ok {
+			continue
+		}
+		// 获取期货指标以及期货数据
+		tmpFutureGoodEdbInfoList, tmpErr := future_good.GetChildFutureGoodEdbInfoListByParentId(v.FutureGoodEdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		if maxN < tmpMaxN {
+			maxN = tmpMaxN
+		}
+		futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap
+
+		if v.FutureGoodEdbType == 2 {
+			specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo)
+		}
+		// 获取数据
+		for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
+			nMap[date] = date
+			dataList := make([]*models.EdbDataList, 0)
+
+			tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
+			if tmpErr != nil {
+				return
+			}
+			for _, tmpData := range tmpDataList {
+				dataList = append(dataList, &models.EdbDataList{
+					EdbDataId:     tmpData.FutureGoodEdbDataId,
+					EdbInfoId:     tmpData.FutureGoodEdbInfoId,
+					DataTime:      tmpData.DataTime.Format(utils.FormatDate),
+					DataTimestamp: tmpData.DataTimestamp,
+					Value:         tmpData.Close,
+				})
+			}
+			futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList
+
+			if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
+				specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
+			}
+		}
+	}
+
+	// 找出所有的N值,并进行正序排列
+	dateList := make([]string, 0)
+	for _, n := range nMap {
+		dateList = append(dateList, n)
+	}
+	sort.Slice(dateList, func(i, j int) bool {
+		return dateList[i] < dateList[j]
+	})
+
+	_, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, latestDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN)
+	if err != nil {
+		return
+	}
+
+	tmpXDataList, newYDataList, err := handleProfitResultData(baseEdbInfo, yDataList, earliestDateTime)
+	if err != nil {
+		return
+	}
+	xDataList = []XData{
+		{
+			Name:   "现货利润",
+			NameEn: "Spot Price",
+		},
+	}
+	xDataList = append(xDataList, tmpXDataList...)
+	yDataList = newYDataList
+
+	return
+}
+
+// ProfitChartChartData 获取数据
+func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int) (edbIdList []int, yDataList []YData, err error) {
+	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
+	//earliestDateTime time.Time
+	// ETA指标数据
+	baseEdbDateData := make(map[string]float64)
+	for _, edbData := range baseDataList {
+		baseEdbDateData[edbData.DataTime] = edbData.Value
+	}
+
+	// 商品指标数据
+	edbDataMap := make(map[int]map[string]float64)
+	for edbInfoId, edbDataList := range futureGoodEdbDataListMap {
+		edbDateData := make(map[string]float64)
+		for _, edbData := range edbDataList {
+			edbDateData[edbData.DataTime] = edbData.Value
+		}
+		edbDataMap[edbInfoId] = edbDateData
+	}
+
+	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
+
+	yDataList = make([]YData, 0) //y轴的数据列表
+
+	// 将计算公式中的字母转大写
+	formulaStr = strings.ToUpper(formulaStr)
+
+	for _, barChartInfoDate := range chartInfoDateList {
+		yDataMap := make(map[int]float64)
+		var maxDate time.Time
+
+		var findDateTime time.Time
+		switch barChartInfoDate.Type {
+		case 1: //最新值
+			findDateTime = latestDateTime
+		case 2: //近期几天
+			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
+		case 3: // 固定日期
+			//寻找固定日期的数据
+			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			findDateTime = tmpFindDateTime
+		default:
+			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
+			return
+		}
+		if findDateTime.IsZero() {
+			err = errors.New("错误的日期")
+			return
+		}
+
+		findDataList := make([]float64, 0) // 当前日期的数据值
+		noDataIdList := make([]int, 0)     // 没有数据的指标id
+		xEdbInfoIdList := make([]int, 0)   // 当前数据的指标id列表
+
+		// 现货指标
+		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataList, baseEdbDateData)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		findDataList = append(findDataList, findDataValue)
+		yDataMap[0] = findDataValue
+		if isFind {
+			maxDate = realDateTime
+		}
+
+		xEdbInfoIdList = append(xEdbInfoIdList, 0)
+
+		mList := make([]int, 0) // 间隔月份
+
+		// 最小开始的n值
+		//minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
+		for _, date := range dateList {
+			currDate, _ := time.ParseInLocation(utils.FormatYearMonthDate, date, time.Local)
+			// 如果当前的n值小于最小开始的n值,那么就不处理
+			//if n < minN {
+			//	continue
+			//}
+			//findDateTime
+
+			// 获取当前日期相对开始日期的期数
+			tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month())
+			if tmpN <= 0 {
+				continue
+			}
+
+			// 如果期数大于最大期数,那么就退出当前匹配
+			if tmpN >= maxN {
+				break
+			}
+
+			zlAndChildEdbId := make(map[int]int)
+			childFutureGoodEdbInfoIdList := make([]int, 0)
+			for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap {
+				// 判断是否特殊合约
+				if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok {
+					if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 {
+						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
+						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
+					}
+
+				} else {
+					if childFutureGoodEdbInfo, ok2 := futureGoodEdbInfoList[date]; ok2 {
+						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
+						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
+					}
+				}
+			}
+
+			// 合约不全,不参与计算
+			//if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo {
+			//	continue
+			//}
+
+			calculateMap := make(map[int]float64)
+			for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
+				tmpRealDateTime := findDateTime
+				tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][findDateTime.Format(utils.FormatDate)]
+
+				if tmpIsFind && tmpFindDataValue != 0 {
+					calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
+					if maxDate.IsZero() || maxDate.Before(tmpRealDateTime) {
+						maxDate = tmpRealDateTime
+					}
+				}
+			}
+			// 合约的数据不全,不参与计算
+			//if len(calculateMap) != lenZlFutureGoodEdbInfo {
+			//	continue
+			//}
+
+			newTagEdbIdMap := make(map[string]int)
+			for tag, zlEdbId := range tagEdbIdMap {
+				newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
+			}
+			//, formulaStr string, tagEdbIdMap map[string]int
+			formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr)
+			//计算公式异常,那么就移除该指标
+			if formulaFormStr == `` {
+				//removeDateList = append(removeDateList, sk)
+				//fmt.Println("异常了")
+				continue
+			}
+
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, evaluateErr := expression.Evaluate()
+			if evaluateErr != nil {
+				// 分母为0的报错
+				if strings.Contains(evaluateErr.Error(), "divide by zero") {
+					//removeDateList = append(removeDateList, sk)
+					continue
+				}
+				err = errors.New("计算失败:Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				continue
+			}
+			// 如果计算结果是NAN,那么就退出当前循环
+			if calResult.IsNan() {
+				continue
+			}
+			calVal, tmpErr := calResult.Float64()
+			if tmpErr != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				continue
+			}
+			//yDataMap[n] = calVal
+			//xEdbInfoIdList = append(xEdbInfoIdList, n)
+			calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
+			yDataMap[tmpN] = calVal
+			xEdbInfoIdList = append(xEdbInfoIdList, tmpN)
+			findDataList = append(findDataList, calVal)
+		}
+		yName := barChartInfoDate.Name
+		yNameEn := barChartInfoDate.Name
+		if yName == `` {
+			if barChartInfoDate.Type == 2 {
+				yName = strconv.Itoa(barChartInfoDate.Value) + "天前"
+				if barChartInfoDate.Value == 1 {
+					yNameEn = strconv.Itoa(barChartInfoDate.Value) + "day ago"
+				} else {
+					yNameEn = strconv.Itoa(barChartInfoDate.Value) + " days ago"
+				}
+			} else {
+				yName = maxDate.Format(utils.FormatDate)
+				yNameEn = maxDate.Format(utils.FormatDate)
+			}
+		}
+		yDate := "0000-00-00"
+		if !maxDate.IsZero() {
+			yDate = maxDate.Format(utils.FormatDate)
+		}
+
+		yDataList = append(yDataList, YData{
+			Date:           yDate,
+			ConfigDate:     findDateTime,
+			Value:          findDataList,
+			NoDataEdbList:  noDataIdList,
+			XEdbInfoIdList: xEdbInfoIdList,
+			Color:          barChartInfoDate.Color,
+			Name:           yName,
+			NameEn:         yNameEn,
+			EdbValMap:      yDataMap,
+			M:              mList,
+		})
+	}
+
+	return
+}
+
+// getFutureGoodEdbInfoList 获取适用的指标列表
+func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
+	maxN := 36 //最大36期合约
+	futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
+	futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo)
+
+	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
+	if zlFutureGoodEdbInfo.RegionType == "国内" {
+		startMonth := int(earliestDateTime.Month())
+		if startMonth == 1 {
+			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList
+		} else {
+			// 因为是下标,所以对应想下标是需要-1的
+			index := startMonth - 1
+			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList[index:]
+			futureGoodEdbInfoList = append(futureGoodEdbInfoList, tmpFutureGoodEdbInfoList[:index]...)
+		}
+
+		lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
+		//futureGoodEdbInfoList
+		//if isAllChina {
+		//	// 如果全是国内指标,那么只需要拼上多出的几期合约即可
+		//	maxN = lenFutureGoodEdbInfoList + monthNum
+		//}
+		// 如果全是国内指标,那么只需要拼上多出的几期合约即可
+		maxN = lenFutureGoodEdbInfoList + monthNum
+
+		for i := 1; i < maxN; i++ {
+			k := i % lenFutureGoodEdbInfoList
+			futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k]
+			if i > newMaxN {
+				newMaxN = i
+			}
+		}
+
+		//需求池604,只要是国内合约,最大必须是12期
+		newMaxN = 12
+
+		return
+	}
+
+	for _, v := range tmpFutureGoodEdbInfoList {
+		//海外的连续日期,目前
+		if v.FutureGoodEdbType == 2 {
+			if v.Month <= newMaxN {
+				futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = v
+				if v.Month > newMaxN {
+					newMaxN = v.Month
+				}
+			}
+			continue
+		}
+
+		if v.Year < earliestDateTime.Year() {
+			continue
+		}
+		// 小于等于当前年,那么就肯定是ok的
+		if v.Year == earliestDateTime.Year() && v.Month <= int(earliestDateTime.Month()) {
+			continue
+		}
+
+		subYear := v.Year - earliestDateTime.Year()
+		subMonth := v.Month - int(earliestDateTime.Month())
+		// 如果(当前年-最新日期的年份) * 12个月 + (当前月-最新日期的月份) 小于总月份
+		tmpN := subYear*12 + subMonth
+		if tmpN < newMaxN {
+			tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local)
+			futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = v
+			if tmpN > newMaxN {
+				newMaxN = tmpN
+			}
+			continue
+		}
+	}
+
+	return
+}
+
+// handleProfitResultData 处理成最终的结果数据
+func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earliestDateTime time.Time) (xDataList []XData, newYDataList []YData, err error) {
+	xDataList = make([]XData, 0)
+	newYDataList = yDataList
+
+	nMap := make(map[int]int)
+
+	for _, v := range yDataList {
+		for _, n := range v.XEdbInfoIdList {
+			nMap[n] = n
+		}
+	}
+
+	// 找出所有的N值,并进行正序排列
+	nList := make([]int, 0)
+	for _, n := range nMap {
+		nList = append(nList, n)
+	}
+	sort.Slice(nList, func(i, j int) bool {
+		return nList[i] < nList[j]
+	})
+
+	for _, n := range nList {
+		if n == 0 {
+			continue
+		}
+		xDataList = append(xDataList, XData{
+			Name:   fmt.Sprint("M+", n),
+			NameEn: fmt.Sprint("M+", n),
+		})
+	}
+
+	for yIndex, yData := range yDataList {
+		newYDataList[yIndex].XEdbInfoIdList = []int{}
+		newYDataList[yIndex].Value = []float64{}
+		tmpNList := nList
+
+		xEdbInfoIdList := yData.XEdbInfoIdList
+		valIndex := 0
+		needNum := 0
+		for _, n := range tmpNList {
+			if len(xEdbInfoIdList) > 0 {
+				currN := xEdbInfoIdList[0]
+				// 当前距离最早的日期相差的N数
+				if n == currN {
+					if needNum > 0 {
+						currVal := yData.Value[valIndex]
+						preVal := yData.Value[valIndex-1]
+						hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(needNum + 1)))
+
+						for tmpNum := 0; tmpNum < needNum; tmpNum++ {
+							newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)
+
+							// 赋值平均值
+							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
+							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
+						}
+					}
+
+					newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
+					newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
+					valIndex++
+					needNum = 0
+					if len(xEdbInfoIdList) > 0 {
+						xEdbInfoIdList = xEdbInfoIdList[1:]
+					}
+				} else {
+					needNum++
+				}
+			}
+		}
+	}
+
+	maxI := 0
+	for _, yData := range newYDataList {
+		lenEdb := len(yData.XEdbInfoIdList)
+		for i := 0; i < lenEdb; i++ {
+			if yData.XEdbInfoIdList[i] != 0 && !utils.InArrayByInt(yData.NoDataEdbList, yData.XEdbInfoIdList[i]) {
+				if maxI < i {
+					maxI = i
+				}
+			}
+		}
+	}
+
+	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
+	xDataList = xDataList[0:maxI]
+	for yIndex, yData := range newYDataList {
+		if len(yData.XEdbInfoIdList) > maxI+1 {
+			newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
+		}
+		if len(yData.Value) > maxI+1 {
+			newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
+		}
+
+		currDate, _ := time.ParseInLocation(utils.FormatDate, yData.Date, time.Local)
+
+		nameList := make([]string, 0)
+		enNameList := make([]string, 0)
+		for _, n := range newYDataList[yIndex].XEdbInfoIdList {
+			if n == 1 { // 现货价不处理
+				nameList = append(nameList, baseEdbInfo.EdbName)
+				enNameList = append(enNameList, baseEdbInfo.EdbNameEn)
+				continue
+			}
+			if n <= 0 {
+				nameList = append(nameList, `无合约`)
+				enNameList = append(enNameList, `no contract`)
+			} else {
+				var date string
+
+				currDateTime := currDate.AddDate(0, n-1, 0)
+				month := int(currDateTime.Month())
+				date = fmt.Sprintf("%d%d", currDateTime.Year(), month)
+				if month < 10 {
+					date = fmt.Sprintf("%d0%d", currDateTime.Year(), month)
+				}
+				nameList = append(nameList, date)
+				enNameList = append(enNameList, date)
+			}
+		}
+		newYDataList[yIndex].NameList = nameList
+		newYDataList[yIndex].EnNameList = enNameList
+	}
+	return
+}
+
+func CheckFormula(formula string) map[string]string {
+	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}
+
+	str := strings.ToUpper(formula)
+	for _, v := range mathFormula {
+		str = strings.Replace(str, v, "", -1)
+	}
+	str = strings.Replace(str, "(", "", -1)
+	str = strings.Replace(str, ")", "", -1)
+
+	byteMap := make(map[string]string)
+	for i := 0; i < len(str); i++ {
+		byteInt := str[i]
+		if byteInt >= 65 && byteInt <= 90 {
+			byteStr := string(byteInt)
+			if _, ok := byteMap[byteStr]; !ok {
+				byteMap[byteStr] = byteStr
+			}
+		}
+	}
+	return byteMap
+}
+
+func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaStr string) string {
+	funMap := GetFormulaMap()
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, k, v, -1)
+	}
+
+	replaceCount := 0
+	for tag, edbInfoId := range tagEdbIdMap {
+		if val, valOk := valArr[edbInfoId]; valOk { //值存在
+			dvStr := fmt.Sprintf("%v", val)
+			formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
+			replaceCount++
+		}
+	}
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, v, k, -1)
+	}
+	if replaceCount == len(tagEdbIdMap) {
+		return formulaStr
+	} else {
+		return ""
+	}
+}
+
+func GetFormulaMap() map[string]string {
+	funMap := make(map[string]string)
+	funMap["MAX"] = "[@@]"
+	funMap["MIN"] = "[@!]"
+	funMap["ABS"] = "[@#]"
+	funMap["CEIL"] = "[@$]"
+	funMap["COS"] = "[@%]"
+	funMap["FLOOR"] = "[@^]"
+	funMap["MOD"] = "[@&]"
+	funMap["POW"] = "[@*]"
+	funMap["ROUND"] = "[@(]"
+	return funMap
+}
+
+// GetNeedDateData 获取合约内需要的日期数据
+func GetNeedDateData(needDateTime time.Time, dataList []*models.EdbDataList, edbDataMap map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
+	//dataList := edbDataListMap[edbInfoId] //指标的所有数据值
+	if len(dataList) <= 0 {
+		// 没有数据的指标id
+		return
+	}
+
+	//最早的日期
+	minDateTime, err := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+	if err != nil {
+		return
+	}
+
+	for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
+		tmpDate := tmpDateTime.Format(utils.FormatDate)
+		if tmpValue, ok := edbDataMap[tmpDate]; ok { //如果能找到数据,那么就返回
+			// 数据为0,也直接返回,做无值处理
+			if tmpValue == 0 {
+				return
+			}
+			findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
+			findDataValue = tmpValue
+			isFind = true
+			return
+		}
+	}
+
+	return
+}

+ 39 - 0
logic/sic.go

@@ -0,0 +1,39 @@
+package logic
+
+import (
+	"errors"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"time"
+)
+
+// RefreshSci 卓创(红桃3)刷新
+func RefreshSci(edbInfo *models.EdbInfo, startDate string) (isHandling bool, errMsg string, err error) {
+	cacheKey := utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(edbInfo.Source) + "_" + edbInfo.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		isHandling = true
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+
+	err = models.RefreshEdbDataFromSci(edbInfo.EdbInfoId, edbInfo.EdbCode, startDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "刷新指标信息失败!"
+		err = errors.New("刷新指标信息失败 RefreshEdbDataFromSci,Err:" + err.Error())
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		return
+	}
+
+	// 更新ES
+	go UpdateEs(edbInfo.EdbInfoId)
+
+	return
+}

+ 0 - 3
models/base_from_adjust.go

@@ -4,7 +4,6 @@ import (
 	"errors"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -41,8 +40,6 @@ func SaveAdjustEdb(req SaveAdjustEdbReq) (edbInfo *EdbInfo, err error, errMsg st
 	defer func() {
 		if err != nil {
 			_ = to.Rollback()
-			fmt.Println("SaveAdjustEdb,Err:" + err.Error())
-			go alarm_msg.SendAlarmMsg(" 保存数据调整指标失败:err:"+err.Error(), 3)
 		} else {
 			_ = to.Commit()
 		}

+ 17 - 16
models/base_from_calculate.go

@@ -6,7 +6,6 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/shopspring/decimal"
 	"github.com/yidane/formula"
-	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -72,7 +71,7 @@ func AddCalculate(edbInfoIdArr []*EdbInfo, edbInfoId int, edbCode, formulaStr st
 		item.EdbInfoId = v.EdbInfoId
 		item.DataMap = dataMap
 	}
-	formulaMap := services.CheckFormula(formulaStr)
+	formulaMap := utils.CheckFormula(formulaStr)
 	addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	nowStr := time.Now().Format(utils.FormatDateTime)
 	var isAdd bool
@@ -415,7 +414,7 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 	//数据处理,将日期内不全的数据做补全
 	handleDateSaveDataMap(dateList, realSaveDataMap, saveDataMap, edbInfoIdArr)
 
-	formulaMap := services.CheckFormula(formulaStr)
+	formulaMap := utils.CheckFormula(formulaStr)
 	addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 
@@ -653,19 +652,21 @@ func handleDateSaveDataMap(dateList []string, realSaveDataMap, saveDataMap map[s
 			tmpEdbInfoId := edbInfo.EdbInfoId // 当前指标id
 			// 如果该日期不存在该指标数据,那么需要找寻前后日期的数据,进行填补
 			if _, ok := tmpDataMap[tmpEdbInfoId]; !ok {
-				day := 0
-				switch edbInfo.Frequency {
-				case "周度":
-					day = 7
-				case "旬度":
-					day = 15
-				case "月度":
-					day = 30
-				case "季度":
-					day = 90
-				case "年度":
-					day = 365
-				}
+				//day := 0
+				//switch edbInfo.Frequency {
+				//case "周度":
+				//	day = 7
+				//case "旬度":
+				//	day = 15
+				//case "月度":
+				//	day = 30
+				//case "季度":
+				//	day = 90
+				//case "年度":
+				//	day = 365
+				//}
+				// 需求池 255 指标运算文案修改,补数据遍历区间修改(2023-3-7 09:37:23修改)
+				day := 35
 				handleDateDataMap(realSaveDataMap, saveDataMap, date, tmpEdbInfoId, day)
 			}
 		}

+ 157 - 0
models/base_from_fubao.go

@@ -0,0 +1,157 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type FuBaoData struct {
+	ReturnValue struct {
+		Product struct {
+			AlikeName    string `json:"alikeName"`
+			DiffName     string `json:"diffName"`
+			PropValueStr string `json:"propValueStr"`
+			Pattern      string `json:"pattern"`
+		} `json:"product"`
+		Data []struct {
+			PriceStr string `json:"priceStr"`
+			Uad      string `json:"uad"`
+			Datadate string `json:"datadate"`
+		} `json:"data"`
+	} `json:"returnValue"`
+	Code int `json:"code"`
+}
+
+// 新增富宝指标数据
+func AddEdbDataFromFubao(edbCode string, fubaoData *FuBaoData) (err error) {
+	o := orm.NewOrm()
+	var isAdd bool
+	addSql := ` INSERT INTO edb_data_fubao(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	for _, dataItem := range fubaoData.ReturnValue.Data {
+		dataTime, err := time.ParseInLocation(utils.FormatDate, dataItem.Datadate, time.Local)
+		if err != nil {
+			return err
+		}
+		eDate := dataTime.Format(utils.FormatDate)
+		if err != nil {
+			return err
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		addSql += GetAddSql("0", edbCode, eDate, timeStr, dataItem.PriceStr)
+		isAdd = true
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshAllEdbDataByFubao add Err", err.Error())
+			return
+		}
+	}
+	return
+}
+
+// 刷新路透指标数据
+func RefreshEdbDataFromFubao(edbInfoId int, edbCode, startDate string, fubaoData *FuBaoData) (err error) {
+	source := utils.DATA_SOURCE_FUBAO
+	o := orm.NewOrm()
+
+	// 真实数据的最大日期  , 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool //是否找到配置日期的实际数据的值
+	{
+		edbDataInsertConfig, err = GetEdbDataInsertConfigByEdbId(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		}
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += " AND edb_info_id=? "
+	existPars = append(existPars, edbInfoId)
+	//if startDate != "" {
+	//	existCondition += " AND data_time>=? "
+	//	existPars = append(existPars, startDate)
+	//}
+
+	existList, err := GetEdbDataByCondition(source, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+
+	fmt.Println("existMap start")
+	fmt.Println(existMap)
+	fmt.Println("existMap end")
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	addSql := ` INSERT INTO edb_data_fubao(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for _, dataItem := range fubaoData.ReturnValue.Data {
+
+		dataTime, err := time.ParseInLocation(utils.FormatDate, dataItem.Datadate, time.Local)
+		if err != nil {
+			return err
+		}
+
+		if findItem, ok := existMap[dataItem.Datadate]; !ok {
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			addSql += GetAddSql(edbInfoIdStr, edbCode, dataItem.Datadate, timeStr, dataItem.PriceStr)
+			isAdd = true
+		} else {
+			fmt.Println("update start")
+			fmt.Println(findItem.Value)
+			fmt.Println(dataItem.PriceStr)
+			fmt.Println(utils.SubFloatToString(findItem.Value, 30))
+			fmt.Println("update end")
+
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 30) != dataItem.PriceStr {
+				err = ModifyEdbDataById(source, findItem.EdbDataId, dataItem.PriceStr)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		// 下面代码主要目的是处理掉手动插入的数据判断
+		{
+			if realDataMaxDate.IsZero() || dataTime.After(realDataMaxDate) {
+				realDataMaxDate = dataTime
+			}
+			if edbDataInsertConfigDate.IsZero() || dataTime.Equal(edbDataInsertConfigDate) {
+				isFindConfigDateRealData = true
+			}
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfoId, source, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshEdbDataFromFubao Err", err.Error())
+			return
+		}
+	}
+	return
+}

+ 13 - 0
models/base_from_manual.go

@@ -3,6 +3,7 @@ package models
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -54,6 +55,13 @@ func AddEdbDataFromManual(edbCode string) (err error) {
 			item := manualDataList[i]
 			eDate := item.Dt
 			sValue := item.Close
+			tmpDecimal, err := decimal.NewFromString(sValue)
+			if err != nil {
+				return err
+			}
+			sValue = tmpDecimal.Round(4).String()
+
+
 			dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 			if err != nil {
 				return err
@@ -147,6 +155,11 @@ func RefreshEdbDataFromManual(edbInfoId int, edbCode, startDate string) (err err
 		item := v
 		eDate := item.Dt
 		sValue := item.Close
+		tmpDecimal, err := decimal.NewFromString(sValue)
+		if err != nil {
+			return err
+		}
+		sValue = tmpDecimal.Round(4).String()
 
 		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 		if err != nil {

+ 104 - 0
models/base_from_mysteel_chemical.go

@@ -197,3 +197,107 @@ func RefreshEdbDataFromMysteelChemical(edbInfoId int, edbCode, startDate string)
 	}
 	return
 }
+
+// 钢联化工指标数据
+type BaseFromMysteelChemicalIndex struct {
+	BaseFromMysteelChemicalIndexId int64     `orm:"column(base_from_mysteel_chemical_index_id);pk"`
+	IndexCode                      string    `description:"指标编码"`
+	IndexName                      string    `description:"指标名称"`
+	Unit                           string    `description:"单位"`
+	Source                         string    `description:"数据来源"`
+	Frequency                      string    `description:"频度"`
+	StartDate                      time.Time `description:"开始日期"`
+	EndDate                        time.Time `description:"结束日期"`
+	Describe                       string    `description:"指标描述"`
+	UpdateWeek                     string    `description:"更新周期"`
+	UpdateTime                     string    `description:"更新时间,多个时间点用英文,隔开"`
+	UpdateTime2                    string    `description:"更新时间2"`
+	SysUserId                      int       `description:"创建人id"`
+	SysUserRealName                string    `description:"创建人姓名"`
+	FilePath                       string    `description:"文件存储路径"`
+	MergeFilePath                  string    `description:"更新文件"`
+	FileIndex                      int       `description:"文件索引"`
+	MergeUpdateWeek                string    `description:"合并文件的更新周"`
+	UpdateDate                     string    `description:"更新日期"`
+	CreateTime                     time.Time `description:"创建时间"`
+	ModifyTime                     time.Time `description:"修改时间"`
+}
+
+// GetIndexRefreshAllByMergeFile 根据合并文件去分组查询需要刷新的文件
+func (d *BaseFromMysteelChemicalIndex) GetIndexRefreshAllByMergeFile() (items []*BaseFromMysteelChemicalIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_mysteel_chemical_index WHERE merge_file_path <>'' AND base_from_mysteel_chemical_classify_id NOT IN(54,55,56,57)`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type HandleMysteelIndex struct {
+	IndexName    string `description:"指标名称"`
+	IndexCode    string `description:"指标编码"`
+	Unit         string `description:"单位"`
+	Source       string `description:"数据来源"`
+	Frequency    string `description:"频度"`
+	StartDate    string `description:"开始日期"`
+	EndDate      string `description:"结束日期"`
+	Describe     string `description:"指标描述"`
+	UpdateDate   string `description:"更新日期"`
+	ExcelDataMap map[string]string
+}
+
+type HandleMysteelIndexResp struct {
+	List []*HandleMysteelIndex
+}
+
+// 钢联化工指标数据
+type BaseFromMysteelChemicalData struct {
+	BaseFromMysteelChemicalDataId  int64 `orm:"column(base_from_mysteel_chemical_data_id);pk"`
+	BaseFromMysteelChemicalIndexId int64
+	IndexCode                      string
+	DataTime                       time.Time
+	Value                          string
+	UpdateDate                     string
+	CreateTime                     time.Time `description:"创建时间"`
+	ModifyTime                     time.Time `description:"修改时间"`
+}
+
+func (d *BaseFromMysteelChemicalIndex) GetIndexItem(indexCode string) (item *BaseFromMysteelChemicalIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_mysteel_chemical_index WHERE index_code = ? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}
+
+// 新增
+func (r *BaseFromMysteelChemicalIndex) Add() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(r)
+	return
+}
+
+func (m *BaseFromMysteelChemicalIndex) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+// GetIndexDataList 根据指标编码获取数据
+func (d *BaseFromMysteelChemicalData) GetIndexDataList(indexCode string) (item []*BaseFromMysteelChemicalData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_mysteel_chemical_data WHERE index_code = ? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&item)
+	return
+}
+
+// Update 修改
+func (r *BaseFromMysteelChemicalData) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(r, updateCols...)
+	return
+}
+
+// Add 新增
+func (r *BaseFromMysteelChemicalData) Add(list []BaseFromMysteelChemicalData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(list), list)
+	return
+}

+ 183 - 0
models/base_from_national_statistics.go

@@ -0,0 +1,183 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseFromNationalStatisticsData 国家统计局-指标数据表
+type BaseFromNationalStatisticsData struct {
+	BaseFromNationalStatisticsDataId  int `orm:"column(base_from_national_statistics_data_id);pk"`
+	BaseFromNationalStatisticsIndexId int
+	IndexCode                         string
+	DataTime                          string
+	Value                             string
+	CreateTime                        time.Time
+	ModifyTime                        time.Time
+}
+
+// GetBaseFromNationalStatisticsDataByCondition 获取指标值
+func GetBaseFromNationalStatisticsDataByCondition(condition string, pars []interface{}) (list []*BaseFromNationalStatisticsData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_national_statistics_data WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}
+
+// AddEdbDataFromNationalStatistics 新增国家统计局指标数据
+func AddEdbDataFromNationalStatistics(edbCode string) (err error) {
+	o := orm.NewOrm()
+
+	var condition string
+	var pars []interface{}
+	if edbCode != "" {
+		condition += ` AND index_code = ? `
+		pars = append(pars, edbCode)
+	}
+
+	dataList, err := GetBaseFromNationalStatisticsDataByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return
+	}
+	var isAdd bool
+	addSql := `INSERT INTO edb_data_national_statistics(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	existMap := make(map[string]string)
+	for _, sv := range dataList {
+		eDate := sv.DataTime
+		dataTime, err := time.Parse(utils.FormatDate, eDate)
+		if err != nil {
+			return err
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, ok := existMap[eDate]; !ok {
+			addSql += GetAddSql("0", edbCode, eDate, timeStr, sv.Value)
+			isAdd = true
+		}
+		existMap[eDate] = sv.Value
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		utils.FileLog.Info("addSql:" + addSql)
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+// RefreshEdbDataFromNationalStatistics 刷新国家统计局指标数据
+func RefreshEdbDataFromNationalStatistics(edbInfoId int, edbCode, startDate string) (err error) {
+	source := utils.DATA_SOURCE_NATIONAL_STATISTICS
+	o := orm.NewOrm()
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	var condition string
+	var pars []interface{}
+	if edbCode != "" {
+		condition += ` AND index_code = ? `
+		pars = append(pars, edbCode)
+	}
+	if startDate != "" {
+		condition += ` AND data_time >= ? `
+		pars = append(pars, startDate)
+	}
+
+	dataList, err := GetBaseFromNationalStatisticsDataByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+
+	// 真实数据的最大日期  , 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool //是否找到配置日期的实际数据的值
+	{
+		edbDataInsertConfig, err = GetEdbDataInsertConfigByEdbId(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		}
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += ` AND edb_info_id = ? `
+	existPars = append(existPars, edbInfoId)
+	if startDate != "" {
+		existCondition += ` AND data_time >= ? `
+		existPars = append(existPars, startDate)
+	}
+
+	existList, err := GetEdbDataByCondition(source, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+	addSql := ` INSERT INTO edb_data_national_statistics(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, v := range dataList {
+		item := v
+		eDate := item.DataTime
+		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+		if err != nil {
+			return err
+		}
+		if findItem, ok := existMap[v.DataTime]; !ok {
+			sValue := item.Value
+
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			addSql += GetAddSql(edbInfoIdStr, edbCode, eDate, timeStr, sValue)
+			isAdd = true
+		} else {
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 30) != item.Value {
+				err = ModifyEdbDataById(source, findItem.EdbDataId, item.Value)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		// 下面代码主要目的是处理掉手动插入的数据判断
+		{
+			if realDataMaxDate.IsZero() || dataTime.After(realDataMaxDate) {
+				realDataMaxDate = dataTime
+			}
+			if edbDataInsertConfigDate.IsZero() || dataTime.Equal(edbDataInsertConfigDate) {
+				isFindConfigDateRealData = true
+			}
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfoId, source, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshEdbDataFromNationalStatistics add Err", err.Error())
+			return
+		}
+	}
+	return
+}

+ 9 - 14
models/base_from_pb.go

@@ -3,25 +3,21 @@ package models
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
 	"time"
 )
 
-func AddEdbDataFromPb(edbCode string, item *services.EdbDataFromPb) (err error) {
-	var errMsg string
-	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 彭博数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
-			go alarm_msg.SendAlarmMsg("彭博数据获取失败:err:"+errMsg, 3)
-
-		}
-	}()
+type EdbDataFromPb struct {
+	Date   map[string]int64   `json:"date"`
+	Ticker map[string]string  `json:"ticker"`
+	Field  map[string]string  `json:"field"`
+	Value  map[string]float64 `json:"value"`
+}
 
+func AddEdbDataFromPb(edbCode string, item *EdbDataFromPb) (err error) {
+	o := orm.NewOrm()
 	if len(item.Date) > 0 {
 		var isAdd bool
 		dateMap := item.Date
@@ -49,7 +45,6 @@ func AddEdbDataFromPb(edbCode string, item *services.EdbDataFromPb) (err error)
 			addSql = strings.TrimRight(addSql, ",")
 			_, err = o.Raw(addSql).Exec()
 			if err != nil {
-				errMsg = " tx.Exec Err :" + err.Error()
 				return
 			}
 		}
@@ -58,7 +53,7 @@ func AddEdbDataFromPb(edbCode string, item *services.EdbDataFromPb) (err error)
 }
 
 // 刷新彭博指标数据
-func RefreshEdbDataFromPb(edbInfoId int, edbCode, startDate string, item *services.EdbDataFromPb) (err error) {
+func RefreshEdbDataFromPb(edbInfoId int, edbCode, startDate string, item *EdbDataFromPb) (err error) {
 	o := orm.NewOrm()
 	source := utils.DATA_SOURCE_PB
 

+ 2 - 14
models/base_from_pb_finance.go

@@ -3,25 +3,14 @@ package models
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
 	"time"
 )
 
-func AddEdbDataFromPbFinance(edbCode string, item *services.EdbDataFromPb) (err error) {
-	var errMsg string
+func AddEdbDataFromPbFinance(edbCode string, item *EdbDataFromPb) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 彭博数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
-			go alarm_msg.SendAlarmMsg("彭博数据获取失败:err:"+errMsg, 3)
-
-		}
-	}()
-
 	if len(item.Date) > 0 {
 		var isAdd bool
 		dateMap := item.Date
@@ -47,7 +36,6 @@ func AddEdbDataFromPbFinance(edbCode string, item *services.EdbDataFromPb) (err
 			addSql = strings.TrimRight(addSql, ",")
 			_, err = o.Raw(addSql).Exec()
 			if err != nil {
-				errMsg = " tx.Exec Err :" + err.Error()
 				return
 			}
 		}
@@ -56,7 +44,7 @@ func AddEdbDataFromPbFinance(edbCode string, item *services.EdbDataFromPb) (err
 }
 
 // 刷新彭博指标数据
-func RefreshEdbDataFromPbFinance(edbInfoId int, edbCode, startDate string, item *services.EdbDataFromPb) (err error) {
+func RefreshEdbDataFromPbFinance(edbInfoId int, edbCode, startDate string, item *EdbDataFromPb) (err error) {
 	o := orm.NewOrm()
 	source := utils.DATA_SOURCE_PB_FINANCE
 

+ 9 - 19
models/base_from_python.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/shopspring/decimal"
-	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strings"
@@ -24,15 +23,15 @@ type EdbDataPython struct {
 	DataTimestamp int64
 }
 
+// EdbDataFromPython 通过python代码获取到的指标数据
+type EdbDataFromPython struct {
+	Date  map[int]string  `json:"date"`
+	Value map[int]float64 `json:"value"`
+}
+
 // AddPythonEdb 新增python运算指标
-func AddPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPython, edbInfoList []*EdbInfo) (err error) {
-	var errMsg string
+func AddPythonEdb(edbInfoId int, edbCode string, item EdbDataFromPython, edbInfoList []*EdbInfo) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			go alarm_msg.SendAlarmMsg(" python代码运算数据添加失败:err:"+errMsg, 3)
-		}
-	}()
 
 	//添加指标关系
 	for _, tmpEdbInfo := range edbInfoList {
@@ -79,7 +78,6 @@ func AddPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPython
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()
 		if err != nil {
-			errMsg = " tx.Exec Err :" + err.Error()
 			return
 		}
 	}
@@ -87,15 +85,8 @@ func AddPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPython
 }
 
 // EditPythonEdb 编辑python运算指标
-func EditPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPython) (err error) {
-	var errMsg string
+func EditPythonEdb(edbInfoId int, edbCode string, item EdbDataFromPython) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			go alarm_msg.SendAlarmMsg(" 编辑python运算指标:err:"+errMsg, 3)
-		}
-	}()
-
 	var isAdd bool
 	addSql := ` INSERT INTO edb_data_python (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	for k, dateTimeStr := range item.Date {
@@ -120,7 +111,6 @@ func EditPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPytho
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()
 		if err != nil {
-			errMsg = " tx.Exec Err :" + err.Error()
 			return
 		}
 	}
@@ -128,7 +118,7 @@ func EditPythonEdb(edbInfoId int, edbCode string, item services.EdbDataFromPytho
 }
 
 // RefreshAllPythonEdb 刷新所有 python运算指标
-func RefreshAllPythonEdb(edbInfo *EdbInfo, item services.EdbDataFromPython) (err error) {
+func RefreshAllPythonEdb(edbInfo *EdbInfo, item EdbDataFromPython) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {

+ 79 - 2
models/base_from_sci.go

@@ -21,6 +21,31 @@ type BaseFromSciData struct {
 	DataTimestamp      int64
 }
 
+// Update 修改
+func (r *BaseFromSciData) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(r, updateCols...)
+
+	return
+}
+
+// GetIndexDataList 获取所有的指标数据
+func (r *BaseFromSciData) GetIndexDataList(indexCode string) (items []*BaseFromSciData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_sci_index WHERE 1=1 AND index_code = ? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+
+	return
+}
+
+// BatchAdd 批量添加指标
+func (r *BaseFromSciData) BatchAdd(list []*BaseFromSciData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(list), list)
+
+	return
+}
+
 func GetBaseFromSciDataByCondition(condition string, pars []interface{}) (list []*BaseFromSciData, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT * FROM base_from_sci_data WHERE 1=1 `
@@ -31,7 +56,7 @@ func GetBaseFromSciDataByCondition(condition string, pars []interface{}) (list [
 	return
 }
 
-// 新增有色指标数据
+// AddEdbDataFromSci 新增卓创(红桃3)指标数据
 func AddEdbDataFromSci(edbCode string) (err error) {
 	o := orm.NewOrm()
 
@@ -73,7 +98,7 @@ func AddEdbDataFromSci(edbCode string) (err error) {
 	return
 }
 
-// 刷新有色指标数据
+// RefreshEdbDataFromSci 刷新卓创(红桃3)指标数据
 func RefreshEdbDataFromSci(edbInfoId int, edbCode, startDate string) (err error) {
 	source := utils.DATA_SOURCE_SCI
 	o := orm.NewOrm()
@@ -182,3 +207,55 @@ func RefreshEdbDataFromSci(edbInfoId int, edbCode, startDate string) (err error)
 	}
 	return
 }
+
+// HandleSciExcelDataReq 卓创(红桃3)的excel数据
+type HandleSciExcelDataReq struct {
+	DataMap          map[string]map[string]string
+	IndexNameList    []string
+	ThirdIndexIdList []string
+	FrequencyList    []string
+	UnitList         []string
+}
+
+// BaseFromSciIndex 红桃3指标表格
+type BaseFromSciIndex struct {
+	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"  json:"base_from_sci_index_id"` //序号
+	ClassifyId         int       `gorm:"column:classify_id" json:"classify_id"`
+	IndexCode          string    `gorm:"column:index_code" json:"index_code" description:"指标编码"`
+	IndexName          string    `gorm:"column:index_name" json:"index_name" description:"指标名称"`
+	Frequency          string    `gorm:"column:frequency" json:"frequency"`
+	Unit               string    `gorm:"column:unit" json:"unit"`
+	StartDate          time.Time `gorm:"column:start_date" json:"start_date"`
+	EndDate            time.Time `gorm:"column:end_date" json:"end_date"`
+	CreateTime         time.Time `gorm:"autoCreateTime;column:create_time" json:"create_time"`       //创建时间
+	ModifyTime         time.Time `gorm:"autoUpdateTime:milli;column:modify_time" json:"modify_time"` //最后更新时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (r *BaseFromSciIndex) TableName() string {
+	return "base_from_sci_index"
+}
+
+// Update 修改
+func (r *BaseFromSciIndex) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(r, updateCols...)
+
+	return
+}
+
+// GetAllIndex 获取所有的指标
+func (r *BaseFromSciIndex) GetAllIndex() (items []*BaseFromSciIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_sci_index WHERE 1=1 `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// BatchAdd 批量添加指标
+func (r *BaseFromSciIndex) BatchAdd(list []*BaseFromSciIndex) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(list), list)
+
+	return
+}

+ 129 - 0
models/base_from_stock_plant.go

@@ -0,0 +1,129 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_edb_lib/models/supply_analysis"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// StockPlantEdbdata 存量装置数据
+type StockPlantEdbdata struct {
+	VarietyEdbId string    `orm:"column(variety_edb_id);pk" description:"指标编码"`
+	DataTime     string    `orm:"column(data_time)" description:"日期"`
+	Value        string    `orm:"column(value)" description:"值"`
+	ModifyTime   time.Time `orm:"column(modify_time)" description:"修改时间"`
+}
+
+func GetStockPlantEdbDataByCondition(condition string, pars []interface{}) (item []*StockPlantEdbdata, err error) {
+	sql := ` SELECT  * FROM base_from_stock_plant_data WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY data_time DESC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}
+
+// RefreshEdbDataFromStockPlant 刷新存量装置数据
+func RefreshEdbDataFromStockPlant(edbInfoId int, edbCode, startDate string) (err error) {
+	source := utils.DATA_SOURCE_STOCK_PLANT
+	o := orm.NewOrm()
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+
+	// 获取供应分析的指标信息
+	varietyEdbInfo, err := supply_analysis.GetVarietyEdbInfoByEdbInfoId(edbInfoId)
+	if err != nil {
+		return
+	}
+
+	// 获取基础库的指标数据
+	condition += " AND variety_edb_id=? "
+	pars = append(pars, varietyEdbInfo.VarietyEdbId)
+
+	if startDate != "" {
+		condition += " AND data_time >=? "
+		pars = append(pars, startDate)
+	} else {
+		condition += " AND data_time != ? "
+		pars = append(pars, `0000-00-00`)
+	}
+
+	baseDataList, err := GetStockPlantEdbDataByCondition(condition, pars)
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += " AND edb_info_id=? "
+	existPars = append(existPars, edbInfoId)
+	if startDate != "" {
+		existCondition += " AND data_time>=? "
+		existPars = append(existPars, startDate)
+	}
+
+	existList, err := GetEdbDataByCondition(source, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO edb_data_stock_plant (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	manualMap := make(map[string]*StockPlantEdbdata)
+	for _, v := range baseDataList {
+		item := v
+		//fmt.Println("Item:", item.Dt, item.Close, item.TradeCode, item.ModifyTime)
+		if findItem, ok := existMap[v.DataTime]; !ok {
+			eDate := item.DataTime
+			sValue := item.Value
+
+			dataTime, err := time.Parse(utils.FormatDate, eDate)
+			if err != nil {
+				return err
+			}
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			addSql += GetAddSql(edbInfoIdStr, edbCode, eDate, timeStr, sValue)
+			isAdd = true
+		} else {
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 4) != item.Value {
+				err = ModifyEdbDataById(source, findItem.EdbDataId, item.Value)
+				if err != nil {
+					return err
+				}
+			}
+		}
+		manualMap[v.DataTime] = v
+	}
+	// 删除不存在的日期数据
+	for _, v := range existList {
+		if _, ok := manualMap[v.DataTime]; !ok {
+			go DeleteEdbDataById(source, v.EdbDataId)
+		}
+	}
+
+	// 将数据入库
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshAllEdbDataByStockPlant add Err", err.Error())
+			return
+		}
+	}
+	return
+}

+ 17 - 13
models/base_from_ths.go

@@ -3,24 +3,30 @@ package models
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
 	"time"
 )
 
+type EdbDataFromThs struct {
+	DataVol   int64       `json:"dataVol"`
+	Errmsg    string      `json:"errmsg"`
+	Errorcode int64       `json:"errorcode"`
+	Perf      interface{} `json:"perf"`
+	Tables    []Tables    `json:"tables"`
+}
+
+// Tables 表格数据
+type Tables struct {
+	ID    []string  `json:"id"`
+	Time  []string  `json:"time"`
+	Value []float64 `json:"value"`
+}
+
 // 新增同花顺指标数据
-func AddEdbDataFromThs(edbCode string, item services.EdbDataFromThs) (err error) {
-	var errMsg string
+func AddEdbDataFromThs(edbCode string, item *EdbDataFromThs) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 同花顺数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
-			go alarm_msg.SendAlarmMsg("同花顺数据获取失败:err:"+errMsg, 3)
-		}
-	}()
 
 	if len(item.Tables) > 0 {
 		table := item.Tables[0]
@@ -36,7 +42,6 @@ func AddEdbDataFromThs(edbCode string, item services.EdbDataFromThs) (err error)
 			sValue := table.Value[i]
 			dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 			if err != nil {
-				errMsg = " time.Parse :" + err.Error()
 				return err
 			}
 			timestamp := dataTime.UnixNano() / 1e6
@@ -48,7 +53,6 @@ func AddEdbDataFromThs(edbCode string, item services.EdbDataFromThs) (err error)
 			addSql = strings.TrimRight(addSql, ",")
 			_, err = o.Raw(addSql).Exec()
 			if err != nil {
-				errMsg = " tx.Exec Err :" + err.Error()
 				return
 			}
 		}
@@ -57,7 +61,7 @@ func AddEdbDataFromThs(edbCode string, item services.EdbDataFromThs) (err error)
 }
 
 // 刷新同花顺指标数据
-func RefreshEdbDataFromThs(edbInfoId int, edbCode, startDate string, item services.EdbDataFromThs) (err error) {
+func RefreshEdbDataFromThs(edbInfoId int, edbCode, startDate string, item *EdbDataFromThs) (err error) {
 	o := orm.NewOrm()
 	source := utils.DATA_SOURCE_THS
 

+ 9 - 12
models/base_from_wind.go

@@ -3,23 +3,21 @@ package models
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
 	"time"
 )
 
-func AddEdbDataFromWind(edbCode string, item *services.EdbDataFromWind) (err error) {
-	var errMsg string
+type EdbDataFromWind struct {
+	Close     map[string]float64 `json:"CLOSE"`
+	Dt        map[string]int64   `json:"DT"`
+	ErrorCode map[string]int64   `json:"ErrorCode"`
+	ErrMsg    string
+}
+
+func AddEdbDataFromWind(edbCode string, item *EdbDataFromWind) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 同花顺数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
-			go alarm_msg.SendAlarmMsg("wind数据获取失败:err:"+errMsg, 3)
-		}
-	}()
 
 	var isAdd bool
 	addSql := ` INSERT INTO edb_data_wind(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
@@ -39,7 +37,6 @@ func AddEdbDataFromWind(edbCode string, item *services.EdbDataFromWind) (err err
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()
 		if err != nil {
-			errMsg = " tx.Exec Err :" + err.Error()
 			return
 		}
 	}
@@ -47,7 +44,7 @@ func AddEdbDataFromWind(edbCode string, item *services.EdbDataFromWind) (err err
 }
 
 // RefreshEdbDataFromWind 刷新wind指标数据
-func RefreshEdbDataFromWind(edbInfoId int, edbCode, startDate string, item *services.EdbDataFromWind) (err error) {
+func RefreshEdbDataFromWind(edbInfoId int, edbCode, startDate string, item *EdbDataFromWind) (err error) {
 	o := orm.NewOrm()
 	source := utils.DATA_SOURCE_WIND
 

+ 2 - 3
models/base_predict_from_calculate.go

@@ -6,7 +6,6 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/shopspring/decimal"
 	"github.com/yidane/formula"
-	"hongze/hongze_edb_lib/services"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -158,7 +157,7 @@ func AddPredictCalculateDataBak(edbInfoIdList []*EdbInfo, edbInfoId int, edbCode
 	//数据处理,将日期内不全的数据做补全
 	handleDateSaveDataMap(dateList, oldSaveDataMap, saveDataMap, edbInfoIdList)
 
-	formulaMap := services.CheckFormula(formulaStr)
+	formulaMap := utils.CheckFormula(formulaStr)
 	addSql := ` INSERT INTO edb_data_predict_calculate (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	nowStr := time.Now().Format(utils.FormatDateTime)
 	var isAdd bool
@@ -318,7 +317,7 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 	//数据处理,将日期内不全的数据做补全
 	handleDateSaveDataMap(dateList, realSaveDataMap, saveDataMap, edbInfoIdList)
 
-	formulaMap := services.CheckFormula(formulaStr)
+	formulaMap := utils.CheckFormula(formulaStr)
 	addSql := ` INSERT INTO edb_data_predict_calculate (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 	if !minLatestDate.IsZero() {

+ 41 - 0
models/chart.go

@@ -3,7 +3,9 @@ package models
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/beego/beego/v2/client/orm"
 	"hongze/hongze_edb_lib/utils"
+	"time"
 )
 
 type EdbInfoReq struct {
@@ -37,3 +39,42 @@ func GetChartInfoDataKey(chartInfoId int) string {
 	key := fmt.Sprint(utils.CACHE_CHART_INFO_DATA, chartInfoId)
 	return key
 }
+
+type ChartInfo struct {
+	ChartInfoId     int    `orm:"column(chart_info_id);pk"`
+	ChartName       string `description:"来源名称"`
+	ChartNameEn     string `description:"英文图表名称"`
+	ChartClassifyId int    `description:"图表分类id"`
+	SysUserId       int
+	SysUserRealName string
+	UniqueCode      string `description:"图表唯一编码"`
+	CreateTime      time.Time
+	ModifyTime      time.Time
+	DateType        int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate       string `description:"自定义开始日期"`
+	EndDate         string `description:"自定义结束日期"`
+	IsSetName       int    `description:"设置名称"`
+	EdbInfoIds      string `description:"指标id"`
+	ChartType       int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
+	Calendar        string `description:"公历/农历"`
+	SeasonStartDate string `description:"季节性图开始日期"`
+	SeasonEndDate   string `description:"季节性图开始日期"`
+	ChartImage      string `description:"图表图片"`
+	Sort            int    `description:"排序字段,数字越小越排前面"`
+	LeftMin         string `description:"图表左侧最小值"`
+	LeftMax         string `description:"图表左侧最大值"`
+	RightMin        string `description:"图表右侧最小值"`
+	RightMax        string `description:"图表右侧最大值"`
+	Disabled        int    `description:"是否禁用,0:启用,1:禁用,默认:0"`
+	BarConfig       string `description:"柱方图的配置,json数据"`
+	Source          int    `description:"1:ETA图库;2:商品价格曲线"`
+	ExtraConfig     string `description:"图表额外配置,json数据"`
+}
+
+// GetChartInfoById 通过id获取图表信息
+func GetChartInfoById(chartInfoId int) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id=? `
+	err = o.Raw(sql, chartInfoId).QueryRow(&item)
+	return
+}

+ 11 - 0
models/chart_edb_mapping.go

@@ -22,6 +22,7 @@ type ChartEdbMapping struct {
 	ChartStyle        string    `description:"图表类型"`
 	ChartColor        string    `description:"颜色"`
 	ChartWidth        float64   `description:"线条大小"`
+	Source            int       `description:"1:ETA图库;2:商品价格曲线"`
 }
 
 // GetChartEdbMappingListByEdbInfoId 根据指标id获取关联图表列表
@@ -34,3 +35,13 @@ func GetChartEdbMappingListByEdbInfoId(edbInfoId int) (list []*ChartEdbMapping,
 	_, err = o.Raw(sql, edbInfoId).QueryRows(&list)
 	return
 }
+
+// GetGroupChartEdbMappingListByEdbInfoId 根据指标id和来源分组获取关联图表列表
+func GetGroupChartEdbMappingListByEdbInfoId(edbInfoId, source int) (list []*ChartEdbMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT *
+             FROM chart_edb_mapping AS a
+			 WHERE edb_info_id=? AND source = ?  GROUP BY chart_info_id `
+	_, err = o.Raw(sql, edbInfoId, source).QueryRows(&list)
+	return
+}

+ 93 - 0
models/common.go

@@ -0,0 +1,93 @@
+package models
+
+import "hongze/hongze_edb_lib/utils"
+
+// BaseEdbInfoInterface 基础指标接口
+type BaseEdbInfoInterface interface {
+	Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string)
+	Edit(params EditCalculateBatchParams) (err error, errMsg string)
+	Refresh(params RefreshParams) (err error, errMsg string)
+	GetSourceName() string
+}
+
+// AddCalculateBatchParams 添加的请求参
+type AddCalculateBatchParams struct {
+	Req             *EdbInfoCalculateBatchSaveReq
+	FromEdbInfo     *EdbInfo
+	EdbCode         string
+	UniqueCode      string
+	SysUserId       int
+	SysUserRealName string
+}
+
+// EditCalculateBatchParams 编辑的请求参
+type EditCalculateBatchParams struct {
+	Req         *EdbInfoCalculateBatchEditReq
+	EdbInfo     *EdbInfo
+	FromEdbInfo *EdbInfo
+}
+
+// RefreshParams 编辑的请求参
+type RefreshParams struct {
+	EdbInfo     *EdbInfo
+	FromEdbInfo *EdbInfo
+	StartDate   string
+	EndDate     string
+}
+
+// GetBaseEdbInfoModel 获取普通指标的model
+func GetBaseEdbInfoModel(source int) (baseEdbInfoModel BaseEdbInfoInterface) {
+	switch source {
+	case utils.DATA_SOURCE_CALCULATE_LJZZJ:
+		baseEdbInfoModel = Ljzzj{}
+	case utils.DATA_SOURCE_CALCULATE_LJZ:
+		baseEdbInfoModel = Ljz{}
+	case utils.DATA_SOURCE_CALCULATE_LJZNCZJ:
+		baseEdbInfoModel = LjzNczj{}
+	case utils.DATA_SOURCE_CALCULATE_STANDARD_DEVIATION:
+		baseEdbInfoModel = StandardDeviation{}
+	case utils.DATA_SOURCE_CALCULATE_PERCENTILE:
+		baseEdbInfoModel = Percentile{}
+	default:
+
+	}
+	return
+}
+
+// BatchSaveCalculateBatchParams 编辑的请求参
+type BatchSaveCalculateBatchParams struct {
+	Req             EdbInfoCalculateBatchSaveReq
+	EdbInfo         *EdbInfo
+	FromEdbInfo     *EdbInfo
+	EdbCode         string
+	UniqueCode      string
+	SysUserId       int
+	SysUserRealName string
+}
+
+// BasePredictEdbInfoInterface 基础指标接口
+type BasePredictEdbInfoInterface interface {
+	Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string)
+	Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string)
+	Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string)
+	GetSourceName() string
+}
+
+// GetBasePredictEdbInfoModel 获取预测指标的model
+func GetBasePredictEdbInfoModel(source int) (baseEdbInfoModel BasePredictEdbInfoInterface) {
+	switch source {
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZZJ:
+		baseEdbInfoModel = PredictLjzzj{}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZ:
+		baseEdbInfoModel = PredictLjz{}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ:
+		baseEdbInfoModel = PredictLjzNczj{}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION:
+		baseEdbInfoModel = PredictStandardDeviation{}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE:
+		baseEdbInfoModel = PredictPercentile{}
+	default:
+
+	}
+	return
+}

+ 29 - 2
models/db.go

@@ -4,6 +4,7 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 	_ "github.com/go-sql-driver/mysql"
 	"hongze/hongze_edb_lib/models/future_good"
+	"hongze/hongze_edb_lib/models/supply_analysis"
 	"hongze/hongze_edb_lib/utils"
 	"time"
 )
@@ -56,13 +57,39 @@ func init() {
 
 	// 注册期货数据 数据表
 	initFutureGood()
+
+	// 供应分析 数据表
+	initSupplyAnalysis()
+
+	// 基础指标表
+	initBaseIndex()
 }
 
 // initFutureGood 注册期货数据 数据表
 func initFutureGood() {
 	//注册对象
 	orm.RegisterModel(
-		new(future_good.FutureGoodEdbInfo), //期货指标表
-		new(future_good.FutureGoodEdbData), //期货指标数据表
+		new(future_good.FutureGoodEdbInfo),         //期货指标表
+		new(future_good.FutureGoodEdbData),         //期货指标数据表
+		new(future_good.ChartInfoFutureGoodProfit), //商品期货利润图表相关的信息
+	)
+}
+
+// initSupplyAnalysis 供应分析
+func initSupplyAnalysis() {
+	orm.RegisterModel(
+		new(supply_analysis.Variety),
+		new(supply_analysis.VarietyAdminPermission),
+		new(supply_analysis.VarietyEdbInfo),
+		new(supply_analysis.VarietyPlant),
+		new(supply_analysis.BaseFromStockPlantData),
+	)
+}
+
+// initBaseIndex 基础指标表
+func initBaseIndex() {
+	orm.RegisterModel(
+		new(BaseFromSciIndex),
+		new(BaseFromSciData),
 	)
 }

+ 48 - 2
models/edb_data_base.go

@@ -1,13 +1,15 @@
 package models
 
 import (
+	"errors"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"hongze/hongze_edb_lib/utils"
+	"strconv"
 	"time"
 )
 
-//指标检索数据
+// 指标检索数据
 type EdbDataItem struct {
 	EdbCode   string `description:"指标编码"`
 	StartDate string `description:"起始日期"`
@@ -141,7 +143,18 @@ func GetAllEdbDataListByTo(to orm.TxOrmer, edbInfoId, source int) (existDataList
 	return
 }
 
-//新版本
+// GetFinalLastByTo 获取所有的指标数据列表
+func GetFinalLastByTo(to orm.TxOrmer, edbInfoId, source int, latestDate string) (finalLast EdbInfoSearchData, err error) {
+	dataTableName := GetEdbDataTableName(source)
+	sql := fmt.Sprintf(` SELECT data_time , value FROM %s WHERE edb_info_id=? and data_time<=? ORDER BY data_time DESC `, dataTableName)
+	err = to.Raw(sql, edbInfoId, latestDate).QueryRow(&finalLast)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return
+	}
+	return
+}
+
+// 新版本
 type EdbDataV1 struct {
 	EdbDataId     int `orm:"column(edb_data_id);pk"`
 	EdbInfoId     int
@@ -153,3 +166,36 @@ type EdbDataV1 struct {
 	ModifyTime    time.Time
 	DataTimestamp int64
 }
+
+type EdbDataList struct {
+	EdbDataId     int     `description:" 指标数据ID"`
+	EdbInfoId     int     `description:"指标ID"`
+	DataTime      string  //`json:"-" description:"数据日期"`
+	DataTimestamp int64   `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+}
+
+func GetEdbDataList(source, endInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	tableName := GetEdbDataTableName(source)
+	if tableName == "" {
+		err = errors.New("无效的渠道:" + strconv.Itoa(source))
+		list = make([]*EdbDataList, 0)
+		return list, err
+	}
+	var pars []interface{}
+	sql := `SELECT edb_data_id,edb_info_id,data_time,value,data_timestamp FROM %s WHERE edb_info_id=? `
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, tableName)
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, endInfoId, pars).QueryRows(&list)
+	return
+}

+ 1 - 1
models/edb_data_calculate_cjjx.go

@@ -254,7 +254,7 @@ func refreshAllCalculateCjjx(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo
 
 	// 通过插值法补全所有数据(包含周末)
 	handleDataMap := make(map[string]float64)
-	err = HandleDataByLinearRegression(dataList, handleDataMap)
+	_, err = HandleDataByLinearRegression(dataList, handleDataMap)
 	if err != nil {
 		return
 	}

+ 538 - 0
models/edb_data_calculate_correlation.go

@@ -0,0 +1,538 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateCorrelation 扩散指数
+func AddCalculateCorrelation(req *EdbInfoCalculateBatchSaveReq, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateCorrelation,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = utils.DATA_SOURCE_CALCULATE_CORRELATION
+	edbInfo.SourceName = utils.DATA_SOURCE_NAME_CALCULATE_CORRELATION
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = sysUserId
+	edbInfo.SysUserRealName = sysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = uniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	var edbInfoA, edbInfoB *EdbInfo
+	//关联关系
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for _, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		if v.FromTag == `A` {
+			edbInfoA = tmpEdbInfo
+		}
+		if v.FromTag == `B` {
+			edbInfoB = tmpEdbInfo
+		}
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+	}
+
+	if edbInfoA == nil || edbInfoB == nil {
+		errMsg = "指标异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if edbInfoA.EdbInfoId == edbInfoB.EdbInfoId {
+		errMsg = "两个指标不允许为同一个"
+		err = errors.New(errMsg)
+		return
+	}
+
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err, errMsg = refreshAllCalculateCorrelation(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfoA, edbInfoB, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+// EditCalculateCorrelation 修改扩散指数数据
+func EditCalculateCorrelation(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq) (err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateCorrelation,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	var edbInfoA, edbInfoB *EdbInfo
+	//关联关系
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for _, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		if v.FromTag == `A` {
+			edbInfoA = tmpEdbInfo
+		}
+		if v.FromTag == `B` {
+			edbInfoB = tmpEdbInfo
+		}
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbInfo.EdbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+	}
+
+	if edbInfoA == nil || edbInfoB == nil {
+		errMsg = "指标异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if edbInfoA.EdbInfoId == edbInfoB.EdbInfoId {
+		errMsg = "两个指标不允许为同一个"
+		err = errors.New(errMsg)
+		return
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err, errMsg = refreshAllCalculateCorrelation(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfoA, edbInfoB, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func RefreshAllCalculateCorrelation(edbInfo *EdbInfo) (err error, errMsg string) {
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	var edbInfoA, edbInfoB *EdbInfo
+	for _, v := range edbInfoCalculateDetailList {
+		tmpEdbInfo, _ := GetEdbInfoById(v.FromEdbInfoId)
+		if v.FromTag == `A` {
+			edbInfoA = tmpEdbInfo
+		}
+		if v.FromTag == `B` {
+			edbInfoB = tmpEdbInfo
+		}
+	}
+
+	if edbInfoA == nil || edbInfoB == nil {
+		errMsg = "指标异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if edbInfoA.EdbInfoId == edbInfoB.EdbInfoId {
+		errMsg = "两个指标不允许为同一个"
+		err = errors.New(errMsg)
+		return
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateCorrelation,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err, errMsg = refreshAllCalculateCorrelation(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfoA, edbInfoB, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+// CorrelationConfig 扩散指数配置
+type CorrelationConfig struct {
+	DateType  int      `description:"扩散指标日期;1:全部指标日期并集;2:部分指标日期并集"`
+	CheckList []string `description:"选中的数据,A,B,C"`
+}
+
+type EdbCalculateFormula struct {
+	BaseCalculateValue int    `description:"基础计算窗口"`
+	BaseCalculateUnit  string `description:"基础计算频度"`
+	LeadValue          int    `description:"领先期数"`
+	LeadUnit           string `description:"频度"`
+	CalculateValue     int    `description:"计算窗口"`
+	CalculateUnit      string `description:"计算频度"`
+}
+
+// refreshAllCalculateCorrelation 刷新扩散指数数据
+func refreshAllCalculateCorrelation(to orm.TxOrmer, edbInfoId, source int, edbInfoA, edbInfoB *EdbInfo, edbCode, calculateFormula string) (err error, errMsg string) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_CORRELATION)
+
+	//获取扩散指数指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+	var correlationConf EdbCalculateFormula
+	//fmt.Println("calculateFormula:", calculateFormula)
+	err = json.Unmarshal([]byte(calculateFormula), &correlationConf)
+	if err != nil {
+		return
+	}
+	aStartDate, _ := time.ParseInLocation(utils.FormatDate, edbInfoA.StartDate, time.Local)
+	bStartDate, _ := time.ParseInLocation(utils.FormatDate, edbInfoB.StartDate, time.Local)
+	var startDateTime time.Time
+	if aStartDate.Before(bStartDate) {
+		startDateTime = aStartDate
+	} else {
+		startDateTime = bStartDate
+	}
+	startDate := startDateTime.Format(utils.FormatDate)
+	endDate := time.Now().Format(utils.FormatDate)
+	correlationChartDataMap, err := GetRollingCorrelationChartDataByEdbInfo(edbInfoA, edbInfoB, correlationConf.LeadValue, correlationConf.LeadUnit, correlationConf.CalculateValue, correlationConf.CalculateUnit, startDate, endDate)
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for currDateStr, val := range correlationChartDataMap {
+		currDate, tmpErr := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		currVal, _ := decimal.NewFromFloat(val).Round(4).Float64()
+
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	// 数据入库
+	{
+
+		if isAdd {
+			addSql = strings.TrimRight(addSql, ",")
+			_, err = to.Raw(addSql).Exec()
+		}
+
+		// 移除不存在的日期数据
+		if len(removeDateMap) > 0 {
+			removeDateList := make([]string, 0) //需要移除的日期
+			for k := range removeDateMap {
+				removeDateList = append(removeDateList, k)
+			}
+			removeDateStr := strings.Join(removeDateList, `","`)
+			removeDateStr = `"` + removeDateStr + `"`
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+			_, err = to.Raw(sql, edbInfoId).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除扩散指数指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	return
+}
+
+// GetRollingCorrelationChartDataByEdbInfo 滚动相关性计算
+func GetRollingCorrelationChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB *EdbInfo, leadValue int, leadUnit string, calculateValue int, calculateUnit string, startDate, endDate string) (dateData map[string]float64, err error) {
+	dateData = make(map[string]float64)
+
+	// 计算窗口,不包含第一天
+	startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+	startDate = startDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+
+	baseEdbInfo := edbInfoMappingA
+	changeEdbInfo := edbInfoMappingB
+
+	// 获取时间基准指标在时间区间内的值
+	aDataList := make([]*EdbInfoSearchData, 0)
+	switch baseEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, baseEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		aDataList, err = GetEdbDataListAll(condition, pars, baseEdbInfo.Source, 1)
+	case 1:
+		aDataList, err = GetPredictEdbDataListAllByStartDate(baseEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", baseEdbInfo.EdbInfoType))
+		return
+	}
+
+	// 获取变频指标所有日期的值, 插值法完善数据
+	bDataList := make([]*EdbInfoSearchData, 0)
+	switch changeEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, changeEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		bDataList, err = GetEdbDataListAll(condition, pars, changeEdbInfo.Source, 1)
+	case 1:
+		bDataList, err = GetPredictEdbDataListAllByStartDate(changeEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标change类型异常", baseEdbInfo.EdbInfoType))
+		return
+	}
+
+	// 数据平移变频指标领先/滞后的日期(单位天)
+	frequencyDaysMap := map[string]int{
+		"天": 1, "周": 7, "月": 30, "季": 90, "年": 365,
+	}
+	// 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
+	//baseDataList := make([]*data_manage.EdbDataList, 0)
+	baseDataMap := make(map[string]float64)
+	changeDataList := make([]*EdbInfoSearchData, 0)
+	changeDataMap := make(map[string]float64)
+
+	// A指标不管三七二十一,先变个频再说
+	{
+		_, e := HandleDataByLinearRegression(aDataList, baseDataMap)
+		if e != nil {
+			err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+			return
+		}
+		//baseDataList = tmpNewChangeDataList
+	}
+	// A指标不管三七二十一,先变个频再说
+	{
+		tmpNewChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
+		if e != nil {
+			err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+			return
+		}
+		changeDataList = tmpNewChangeDataList
+
+		// 平移下日期
+		moveUnitDays := frequencyDaysMap[leadUnit]
+		_, changeDataMap = MoveDataDaysToNewDataList(changeDataList, leadValue*moveUnitDays)
+	}
+
+	// 计算计算时,需要多少个日期内数据
+	calculateDay := frequencyDaysMap[calculateUnit] * calculateValue
+
+	// 计算 每个日期的相关性值
+	{
+		startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		endDateTime, _ := time.ParseInLocation(utils.FormatDate, baseEdbInfo.EndDate, time.Local)
+		endDateTime = endDateTime.AddDate(0, 0, -(calculateDay - 1))
+
+		for currDay := startDateTime; !currDay.After(endDateTime); currDay = currDay.AddDate(0, 0, 1) {
+			coordinateData := make([]utils.Coordinate, 0)
+			// 取出对应的基准日期的值
+			for i := 0; i < calculateDay; i++ {
+				iDay := currDay.AddDate(0, 0, i).Format(utils.FormatDate)
+				tmpCoordinate := utils.Coordinate{
+					X: baseDataMap[iDay],
+					Y: changeDataMap[iDay],
+				}
+				coordinateData = append(coordinateData, tmpCoordinate)
+			}
+
+			// 公式计算出领先/滞后频度对应点的相关性系数
+			var ratio float64
+			if len(coordinateData) > 0 {
+				ratio = utils.ComputeCorrelation(coordinateData)
+			}
+			dateData[currDay.AddDate(0, 0, calculateDay-1).Format(utils.FormatDate)] = ratio
+		}
+	}
+	return
+}
+
+// MoveDataDaysToNewDataList 平移指标数据生成新的数据序列
+func MoveDataDaysToNewDataList(dataList []*EdbInfoSearchData, moveDay int) (newDataList []EdbInfoSearchData, dateDataMap map[string]float64) {
+	dateMap := make(map[time.Time]float64)
+	var minDate, maxDate time.Time
+	dateDataMap = make(map[string]float64)
+
+	for _, v := range dataList {
+		currDate, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if minDate.IsZero() || currDate.Before(minDate) {
+			minDate = currDate
+		}
+		if maxDate.IsZero() || currDate.After(maxDate) {
+			maxDate = currDate
+		}
+		dateMap[currDate] = v.Value
+	}
+
+	// 处理领先、滞后数据
+	newDateMap := make(map[time.Time]float64)
+	for currDate, value := range dateMap {
+		newDate := currDate.AddDate(0, 0, moveDay)
+		newDateMap[newDate] = value
+	}
+	minDate = minDate.AddDate(0, 0, moveDay)
+	maxDate = maxDate.AddDate(0, 0, moveDay)
+
+	// 获取日期相差日
+	dayNum := utils.GetTimeSubDay(minDate, maxDate)
+
+	for i := 0; i <= dayNum; i++ {
+		currDate := minDate.AddDate(0, 0, i)
+		tmpValue, ok := newDateMap[currDate]
+		if !ok {
+			//找不到数据,那么就用前面的数据吧
+			if len(newDataList)-1 < 0 {
+				tmpValue = 0
+			} else {
+				tmpValue = newDataList[len(newDataList)-1].Value
+			}
+		}
+		tmpData := EdbInfoSearchData{
+			DataTime: currDate.Format(utils.FormatDate),
+			Value:    tmpValue,
+		}
+		dateDataMap[tmpData.DataTime] = tmpData.Value
+		newDataList = append(newDataList, tmpData)
+	}
+	return
+}

+ 614 - 0
models/edb_data_calculate_ljz.go

@@ -0,0 +1,614 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Ljz 累计值
+type Ljz struct {
+}
+
+// Add 添加
+func (obj Ljz) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+
+	frequencyInt := utils.CheckFrequency(fromEdbInfo.Frequency, req.Frequency)
+	if frequencyInt < 0 {
+		errMsg = "频度异常,不允许低频转为高频"
+		err = errors.New(errMsg)
+		return
+	}
+	if frequencyInt == 0 {
+		errMsg = "频度异常,不允许同频计算累计值"
+		err = errors.New(errMsg)
+		return
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      0,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj Ljz) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	frequencyInt := utils.CheckFrequency(fromEdbInfo.Frequency, req.Frequency)
+	if frequencyInt < 0 {
+		errMsg = "频度异常,不允许低频转为高频"
+		err = errors.New(errMsg)
+		return
+	}
+	if frequencyInt == 0 {
+		errMsg = "频度异常,不允许同频计算累计值"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.Frequency != req.Frequency {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj Ljz) Refresh(params RefreshParams) (err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj Ljz) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_LJZ
+}
+
+// GetSourceName 获取来源名称
+func (obj Ljz) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_LJZ
+}
+
+func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, fromEdbInfo.EdbInfoId)
+
+	//if startDate != "" {
+	//	condition += " AND data_time>=? "
+	//	pars = append(pars, startDate)
+	//}
+
+	var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7
+	// 周度数据需要先变成日度的
+	if fromEdbInfo.Frequency == `周度` {
+		isWeekData = true
+	}
+
+	dataList, err := GetEdbDataListAllByTo(to, condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return err
+	}
+
+	fromEdbDataMap := make(map[string]float64)
+	if isWeekData {
+		dataList, err = HandleDataByLinearRegression(dataList, fromEdbDataMap)
+		if err != nil {
+			return
+		}
+	}
+
+	//日度转周度:日期选周五,计算上周六到本周五的日度值的加总,最新日期为最新值对应的周五。
+	//日度转月度:日期选每个月最后一天,计算当月所有日度值的加总,最新日期为最新值对应当月最后一天。
+	//日度转季度、年度:方法类似转月度。
+	//周度转月度/季度/年度:将周度值转成日度,空值用插值法插值,计算当月/当季/当年所有值的加总,然后除以7。
+	//月度转季度/年度: 当季/当年月度值相加。
+
+	dateList := make([]time.Time, 0)
+	valueMap := make(map[time.Time]float64)
+	switch edbInfo.Frequency {
+	case "年度":
+		yearMap := make(map[int]float64)
+		yearList := make([]int, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearList = append(yearList, year)
+				yearMap[year] = item.Value
+			}
+		}
+		for _, v := range yearList {
+			currTime := time.Date(v, 12, 31, 0, 0, 0, 0, time.Local)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMap[v]
+		}
+	case "半年度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			if itemDate.Month() <= 6 {
+				tmpK = fmt.Sprint(year, "06")
+			} else {
+				tmpK = fmt.Sprint(year, "12")
+			}
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "季度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			if itemDate.Month() <= 3 {
+				tmpK = fmt.Sprint(year, "03")
+			} else if itemDate.Month() <= 6 {
+				tmpK = fmt.Sprint(year, "06")
+			} else if itemDate.Month() <= 9 {
+				tmpK = fmt.Sprint(year, "09")
+			} else {
+				tmpK = fmt.Sprint(year, "12")
+			}
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "月度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			tmpK = fmt.Sprint(year*100 + int(itemDate.Month()))
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "旬度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			dayInt := itemDate.Year()*100 + int(itemDate.Month())
+			var currTime time.Time
+			if itemDate.Month() <= 10 {
+				tmpK := fmt.Sprint(dayInt*100, "10")
+				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
+				if err != nil {
+					return
+				}
+
+			} else if itemDate.Month() <= 20 {
+				tmpK := fmt.Sprint(dayInt*100, "20")
+				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
+				if err != nil {
+					return
+				}
+			} else {
+				currTime, err = time.ParseInLocation(utils.FormatYearMonthUnSpace, fmt.Sprint(dayInt), time.Local)
+				if err != nil {
+					return
+				}
+				currTime = currTime.AddDate(0, 1, -1)
+			}
+
+			yearVal, ok := tmpDateDataMap[currTime]
+			if ok {
+				tmpDateDataMap[currTime] = item.Value + yearVal
+			} else {
+				tmpDateList = append(tmpDateList, currTime)
+				tmpDateDataMap[currTime] = item.Value
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+	case "周度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			var currTime time.Time
+			// 周六周日,这是下一个周五的数据
+			if itemDate.Weekday() == 0 {
+				currTime = itemDate.AddDate(0, 0, 5)
+			} else if itemDate.Weekday() == 6 {
+				currTime = itemDate.AddDate(0, 0, 6)
+			} else {
+				currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday()))
+			}
+
+			yearVal, ok := tmpDateDataMap[currTime]
+			if ok {
+				tmpDateDataMap[currTime] = item.Value + yearVal
+			} else {
+				tmpDateList = append(tmpDateList, currTime)
+				tmpDateDataMap[currTime] = item.Value
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return err
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, currTime := range dateList {
+		currDateStr := currTime.Format(utils.FormatDate)
+		existVal, ok := existDataMap[currDateStr]
+
+		tmpVal, ok2 := valueMap[currTime]
+		if !ok2 {
+			err = errors.New("数据异常,date:" + currDateStr)
+			return
+		}
+		var saveValue string
+		if isWeekData { //周度指标转的话,最后结果要除以7
+			saveValue = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).String()
+		} else {
+			saveValue = decimal.NewFromFloat(tmpVal).Round(4).String()
+		}
+
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	return
+}

+ 464 - 0
models/edb_data_calculate_ljznczj.go

@@ -0,0 +1,464 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// LjzNczj 累计值年初至今
+type LjzNczj struct {
+}
+
+// Add 添加
+func (obj LjzNczj) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	if fromEdbInfo.Frequency == `年度` {
+		errMsg = "年初至今累计值计算中,可选指标范围为非年度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if fromEdbInfo.Frequency != req.Frequency {
+		errMsg = "生成指标频度与原指标频度不同"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      0,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj LjzNczj) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	if fromEdbInfo.Frequency == `年度` {
+		errMsg = "年初至今累计值计算中,可选指标范围为非年度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if fromEdbInfo.Frequency != req.Frequency {
+		errMsg = "生成指标频度与原指标频度不同"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.Frequency != req.Frequency {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj LjzNczj) Refresh(params RefreshParams) (err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzNczjzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj LjzNczj) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_LJZNCZJ
+}
+
+// GetSourceName 获取来源名称
+func (obj LjzNczj) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_LJZNCZJ
+}
+
+func (obj LjzNczj) refresh(to orm.TxOrmer, edbInfoId, source int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, fromEdbInfo.EdbInfoId)
+
+	//if startDate != "" {
+	//	condition += " AND data_time>=? "
+	//	pars = append(pars, startDate)
+	//}
+
+	var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7
+	// 周度数据需要先变成日度的
+	if fromEdbInfo.Frequency == `周度` {
+		isWeekData = true
+	}
+
+	dataList, err := GetEdbDataListAllByTo(to, condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return err
+	}
+
+	fromEdbDataMap := make(map[string]float64)
+	if isWeekData {
+		dataList, err = HandleDataByLinearRegression(dataList, fromEdbDataMap)
+		if err != nil {
+			return
+		}
+	}
+
+	//日度数据年初至今:日期同原日度数据。将每年1月1日(含)到日度数据所在日期含间的日度值,进行加总。
+	//周度数据年初至今:日期同原周度数据。将周度值转成日度频率,空值用插值法插值,然后算法同日度年度至今,再除以7
+	//月度/季度数据年初至今:日期同原月度/季度数据,将每年1月1日(含)到月度数据所在日期(含)之间的月度/季度值,进行加总
+	//以此类推
+
+	dateList := make([]time.Time, 0)
+	valueMap := make(map[time.Time]float64)
+	yearMap := make(map[int]float64)
+	switch edbInfo.Frequency {
+	case "周度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			var currTime time.Time
+			// 周六周日,这是下一个周五的数据
+			if itemDate.Weekday() == 0 {
+				currTime = itemDate.AddDate(0, 0, 5)
+			} else if itemDate.Weekday() == 6 {
+				currTime = itemDate.AddDate(0, 0, 6)
+			} else {
+				currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday()))
+			}
+
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearMap[year] = item.Value
+			}
+
+			if itemDate.Equal(currTime) {
+				tmpDateDataMap[itemDate] = yearMap[year]
+				tmpDateList = append(tmpDateList, itemDate)
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+	default:
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearMap[year] = item.Value
+			}
+			valueMap[itemDate] = yearMap[year]
+			dateList = append(dateList, itemDate)
+		}
+
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return err
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, currTime := range dateList {
+		currDateStr := currTime.Format(utils.FormatDate)
+		existVal, ok := existDataMap[currDateStr]
+
+		tmpVal, ok2 := valueMap[currTime]
+		if !ok2 {
+			err = errors.New("数据异常,date:" + currDateStr)
+			return
+		}
+		var saveValue string
+		if isWeekData { //周度指标转的话,最后结果要除以7
+			saveValue = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).String()
+		} else {
+			saveValue = decimal.NewFromFloat(tmpVal).Round(4).String()
+		}
+
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	return
+}

+ 397 - 0
models/edb_data_calculate_ljzzj.go

@@ -0,0 +1,397 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Ljzzj 累计值转季度
+type Ljzzj struct {
+}
+
+// Add 添加
+func (obj Ljzzj) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	if fromEdbInfo.Frequency != "季度" {
+		errMsg = "请选择季度指标"
+		err = errors.New(errMsg)
+		return
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Edit 编辑
+func (obj Ljzzj) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+	if fromEdbInfo.Frequency != "季度" {
+		errMsg = "请选择季度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需重新计算
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Refresh 刷新
+func (obj Ljzzj) Refresh(params RefreshParams) (err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, fromEdbInfo, params.EdbInfo.EdbCode)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj Ljzzj) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_LJZZJ
+}
+
+// GetSourceName 获取来源名称
+func (obj Ljzzj) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_LJZZJ
+}
+
+func (obj Ljzzj) refresh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	// 获取来源指标的数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, fromEdbInfo.EdbInfoId)
+	dataList, err := GetEdbDataListAllByTo(to, condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return err
+	}
+
+	yearMap := make(map[int]map[int]*EdbInfoSearchData)
+	dataLen := len(dataList)
+	for i := 0; i < dataLen; i++ {
+		item := dataList[i]
+		//日其中获取年
+		itemDate, err := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+		if err != nil {
+			return err
+		}
+		year := itemDate.Year()
+		quarter := utils.MonthQuarterMap[int(itemDate.Month())]
+		if quarterMap, yok := yearMap[year]; yok {
+			quarterMap[quarter] = item
+			yearMap[year] = quarterMap
+		} else {
+			quarterMap = make(map[int]*EdbInfoSearchData)
+			quarterMap[quarter] = item
+			yearMap[year] = quarterMap
+		}
+	}
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return err
+	}
+	dataMap := make(map[string]string)
+	for _, v := range existDataList {
+		dataMap[v.DataTime] = v.Value
+	}
+	existDataMap := make(map[string]string)
+	for yk, yv := range yearMap {
+		_, oneQuarterOk := yv[1]
+		_, twoQuarterOk := yv[2]
+		if !oneQuarterOk && !twoQuarterOk {
+			continue
+		}
+		for i := 1; i <= 4; i++ {
+			fmt.Println(yk, i, yv[i])
+			dataCurrentItem := yv[i]
+			var date string
+			var val float64
+			if dataCurrentItem != nil {
+				if i == 1 || i == 2 {
+					if _, mok := yv[1]; mok { //第1个季度有值
+						if i == 1 {
+							date = dataCurrentItem.DataTime
+							val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Float64() //a.Div(b).Float64()
+						}
+						if i == 2 {
+							dataOneItem := yv[1]
+							date = dataCurrentItem.DataTime
+							twoQuarter := decimal.NewFromFloat(dataCurrentItem.Value)
+							oneQuarter := decimal.NewFromFloat(dataOneItem.Value)
+							val, _ = twoQuarter.Sub(oneQuarter).Float64()
+						}
+					} else { //第1个季度无值
+						dataTwoItem := yv[2]
+						if i == 1 {
+							date = strconv.Itoa(yk) + "-03-31"
+							a := decimal.NewFromFloat(dataTwoItem.Value)
+							b := decimal.NewFromFloat(2.0)
+							val, _ = a.Div(b).Float64()
+						}
+						if i == 2 {
+							//第1个季度无值:第1个季度=第2个季度/2
+							{
+								date = strconv.Itoa(yk) + "-03-31"
+								a := decimal.NewFromFloat(dataTwoItem.Value)
+								b := decimal.NewFromFloat(2.0)
+								val, _ = a.Div(b).Float64()
+
+								tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
+								if !isAdd {
+									isAdd = newAdd
+								}
+								addSql = tmpSql
+								if tmpErr != nil {
+									return tmpErr
+								}
+							}
+							//end 第1个季度无值
+
+							date = dataCurrentItem.DataTime
+							a := decimal.NewFromFloat(dataTwoItem.Value)
+							b := decimal.NewFromFloat(2.0)
+							val, _ = a.Div(b).Float64()
+						}
+					}
+				} else {
+					dataPreItem := yv[i-1]
+					if dataCurrentItem != nil && dataPreItem != nil {
+						date = dataCurrentItem.DataTime
+						//val =  dataCurrentItem.Value - dataPreItem.Value
+						a := decimal.NewFromFloat(dataCurrentItem.Value)
+						b := decimal.NewFromFloat(dataPreItem.Value)
+						val, _ = a.Sub(b).Float64()
+					}
+				}
+			}
+			if date != "" {
+				tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
+				if !isAdd {
+					isAdd = newAdd
+				}
+				addSql = tmpSql
+				if tmpErr != nil {
+					return tmpErr
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+	return
+}
+
+func (obj Ljzzj) calculate(edbInfoId int, date, edbInfoIdStr, edbCode, dataTableName, addSql string, val float64, dataMap, existDataMap map[string]string, to orm.TxOrmer) (newSql string, isAdd bool, err error) {
+	newSql = addSql
+	saveValue := utils.SubFloatToString(val, 4)
+	//判断数据是否存在
+	if existVal, ok := dataMap[date]; !ok {
+		dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := existDataMap[date]; !existOk {
+			newSql += GetAddSql(edbInfoIdStr, edbCode, date, timeStr, saveValue)
+			isAdd = true
+		}
+		existDataMap[date] = date
+	} else {
+		if existVal != saveValue {
+			sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+			sql = fmt.Sprintf(sql, dataTableName)
+			_, err = to.Raw(sql, saveValue, edbInfoId, date).Exec()
+		}
+	}
+	return
+}

+ 1 - 1
models/edb_data_calculate_nh.go

@@ -232,7 +232,7 @@ func refreshAllCalculateNh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *E
 
 	// 插值法数据处理
 	handleDataMap := make(map[string]float64)
-	err = HandleDataByLinearRegression(fromDataList, handleDataMap)
+	_, err = HandleDataByLinearRegression(fromDataList, handleDataMap)
 	if err != nil {
 		return
 	}

+ 27 - 4
models/edb_data_calculate_nszydbpjjs.go

@@ -213,8 +213,10 @@ func refreshAllCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, formulaInt
 	}
 
 	existDataMap := make(map[string]*EdbInfoSearchDataV1)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v
+		removeDataTimeMap[v.DataTime] = 1
 	}
 
 	//计算来源数据
@@ -222,10 +224,10 @@ func refreshAllCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, formulaInt
 	var fromPars []interface{}
 	fromCondition += " AND edb_info_id=? "
 	fromPars = append(fromPars, fromEdbInfo.EdbInfoId)
-	if startDate != `` {
-		fromCondition += " AND data_time>=? "
-		fromPars = append(fromPars, startDate)
-	}
+	//if startDate != `` {
+	//	fromCondition += " AND data_time>=? "
+	//	fromPars = append(fromPars, startDate)
+	//}
 
 	//fmt.Println("fromPars:", fromPars)
 	fromDataList, err := GetEdbDataListAllV1ByTo(to, fromCondition, fromPars, fromEdbInfo.Source, 0)
@@ -294,6 +296,9 @@ func refreshAllCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, formulaInt
 			}
 			existAddDataMap[av] = av
 		} else {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, av)
+
 			existValDecimal, err := decimal.NewFromString(existVal.Value)
 			existStr := existValDecimal.String()
 			fmt.Println(existStr, valStr, av)
@@ -307,6 +312,24 @@ func refreshAllCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, formulaInt
 			}
 		}
 	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfoId, removeDateList).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		utils.FileLog.Info("addSql:" + addSql)

+ 433 - 0
models/edb_data_calculate_percentile.go

@@ -0,0 +1,433 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Percentile 百分位
+type Percentile struct {
+}
+
+// Add 添加
+func (obj Percentile) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Edit 编辑
+func (obj Percentile) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	oldEdbInfo := *edbInfo
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需处理逻辑
+		// 如果相关配置更改了,那么重新计算
+		if oldEdbInfo.CalculateFormula != edbInfo.CalculateFormula {
+			err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+		}
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Refresh 刷新
+func (obj Percentile) Refresh(params RefreshParams) (err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err, errMsg = obj.refresh(to, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj Percentile) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_PERCENTILE
+}
+
+// GetSourceName 获取来源名称
+func (obj Percentile) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_PERCENTILE
+}
+
+// PercentileConfig 百分位的配置
+type PercentileConfig struct {
+	CalculateValue int    `description:"时间长度期数"`
+	CalculateUnit  string `description:"时间长度频度"`
+}
+
+// refresh 刷新
+func (obj Percentile) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbCode string) (err error, errMsg string) {
+	edbInfoId := edbInfo.EdbInfoId
+	dataTableName := GetEdbDataTableName(edbInfo.Source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	var percentileConfig PercentileConfig
+	err = json.Unmarshal([]byte(edbInfo.CalculateFormula), &percentileConfig)
+	if err != nil {
+		return
+	}
+
+	// 获取百分位的指标数据
+	fromDataList, err, errMsg := obj.getPercentileData(fromEdbInfo, percentileConfig.CalculateValue, percentileConfig.CalculateUnit)
+	if err != nil {
+		return
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, tmpData := range fromDataList {
+		currDateStr := tmpData.DataTime
+		currTime, tmpErr := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 当前的实际值
+		saveValue := decimal.NewFromFloat(tmpData.Value).Round(4).String()
+
+		existVal, ok := existDataMap[currDateStr]
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+func (obj Percentile) calculate(edbInfoId int, date, edbInfoIdStr, edbCode, dataTableName, addSql string, val float64, dataMap, existDataMap map[string]string, to orm.TxOrmer) (newSql string, isAdd bool, err error) {
+	newSql = addSql
+	saveValue := utils.SubFloatToString(val, 4)
+	//判断数据是否存在
+	if existVal, ok := dataMap[date]; !ok {
+		dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := existDataMap[date]; !existOk {
+			newSql += GetAddSql(edbInfoIdStr, edbCode, date, timeStr, saveValue)
+			isAdd = true
+		}
+		existDataMap[date] = date
+	} else {
+		if existVal != saveValue {
+			sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+			sql = fmt.Sprintf(sql, dataTableName)
+			_, err = to.Raw(sql, saveValue, edbInfoId, date).Exec()
+		}
+	}
+	return
+}
+
+// GetPercentileData 获取百分位图表的指标数据
+func (obj Percentile) getPercentileData(fromEdbInfo *EdbInfo, calculateValue int, calculateUnit string) (newDataList []EdbInfoSearchData, err error, errMsg string) {
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*EdbInfoSearchData, 0)
+	switch fromEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		dataList, err = GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 1)
+	case 1:
+		dataList, err = GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", fromEdbInfo.EdbInfoType))
+		return
+	}
+
+	moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit]
+	if !ok {
+		errMsg = `错误的周期`
+		err = errors.New(errMsg)
+		return
+	}
+	calculateDay := calculateValue * moveUnitDays
+	// 指标对应的所有数据
+
+	newDataList = make([]EdbInfoSearchData, 0)
+
+	dataMap := make(map[time.Time]float64, 0)
+	for _, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		dataMap[currDateTime] = tmpData.Value
+	}
+
+	//百分位:对所选指标滚动地取对应时间长度的数据值,取最大值Max,最小值Min,计算Max-Min,百分位=(现值-Min)/(Max-Min),Max=Min时不予计算。
+	for i, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		maxVal := tmpData.Value
+		minVal := tmpData.Value
+		for k := 0; k < calculateDay; k++ {
+			preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)]
+			if ok2 {
+				if preVal > maxVal {
+					maxVal = preVal
+				}
+				if preVal < minVal {
+					minVal = preVal
+				}
+			}
+		}
+
+		if maxVal == minVal {
+			continue
+		}
+		tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100
+		tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
+		//百分位=(现值-Min)/(Max-Min)
+		newDataList = append(newDataList, EdbInfoSearchData{
+			EdbDataId: i,
+			DataTime:  dataList[i].DataTime,
+			Value:     tmpV,
+		})
+	}
+
+	return
+}

+ 372 - 0
models/edb_data_calculate_standard_deviation.go

@@ -0,0 +1,372 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// StandardDeviation 标准差
+type StandardDeviation struct {
+}
+
+// Add 添加
+func (obj StandardDeviation) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Edit 编辑
+func (obj StandardDeviation) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	oldEdbInfo := *edbInfo
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需处理逻辑
+		// 如果相关配置更改了,那么重新计算
+		if oldEdbInfo.CalculateFormula != edbInfo.CalculateFormula {
+			err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+		}
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Refresh 刷新
+func (obj StandardDeviation) Refresh(params RefreshParams) (err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj StandardDeviation) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_STANDARD_DEVIATION
+}
+
+// GetSourceName 获取来源名称
+func (obj StandardDeviation) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_STANDARD_DEVIATION
+}
+
+func (obj StandardDeviation) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbCode string) (err error) {
+	edbInfoId := edbInfo.EdbInfoId
+	dataTableName := GetEdbDataTableName(edbInfo.Source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	calculateValue, err := strconv.Atoi(edbInfo.CalculateFormula)
+	if err != nil {
+		return
+	}
+
+	// 获取标准差图表的指标数据
+	fromDataList, err := obj.getStandardDeviationData(fromEdbInfo, calculateValue)
+	if err != nil {
+		return err
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return err
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, tmpData := range fromDataList {
+		currDateStr := tmpData.DataTime
+		currTime, tmpErr := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 当前的实际值
+		saveValue := decimal.NewFromFloat(tmpData.Value).Round(4).String()
+
+		existVal, ok := existDataMap[currDateStr]
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+// GetStandardDeviationData 获取标准差图表的指标数据
+func (obj StandardDeviation) getStandardDeviationData(fromEdbInfo *EdbInfo, calculateValue int) (newDataList []EdbInfoSearchData, err error) {
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*EdbInfoSearchData, 0)
+	switch fromEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		dataList, err = GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 1)
+	case 1:
+		dataList, err = GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", fromEdbInfo.EdbInfoType))
+		return
+	}
+
+	// 指标对应的所有数据
+	newDataList = make([]EdbInfoSearchData, 0)
+	lenData := len(dataList)
+	if lenData >= calculateValue {
+		tmpDataList := make([]float64, 0)
+		for _, tmpData := range dataList {
+			tmpDataList = append(tmpDataList, tmpData.Value)
+		}
+		for i := calculateValue; i <= lenData; i++ {
+			tmpV := utils.CalculateStandardDeviation(tmpDataList[i-calculateValue : i])
+			newDataList = append(newDataList, EdbInfoSearchData{
+				EdbDataId: i,
+				DataTime:  dataList[i-1].DataTime,
+				Value:     tmpV,
+			})
+		}
+	}
+
+	return
+}

+ 2 - 2
models/edb_data_calculate_tbz.go

@@ -406,8 +406,8 @@ func refreshAllCalculateTbz(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *
 							break
 						} else {
 							if i >= 1 {
-								preDateDay = preDate.AddDate(0, 0, -i)
-								preDateDayStr = nextDateDay.Format(utils.FormatDate)
+								preDateDay = preDateDay.AddDate(0, 0, -i)
+								preDateDayStr = preDateDay.Format(utils.FormatDate)
 							}
 							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
 								timestamp := currentDate.UnixNano() / 1e6

+ 28 - 0
models/edb_data_table.go

@@ -117,6 +117,34 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_predict_calculate_kszs"
 	case utils.DATA_SOURCE_BAIINFO:
 		tableName = "edb_data_baiinfo"
+	case utils.DATA_SOURCE_STOCK_PLANT:
+		tableName = "edb_data_stock_plant"
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		tableName = "edb_data_calculate_correlation"
+	case utils.DATA_SOURCE_NATIONAL_STATISTICS:
+		tableName = "edb_data_national_statistics"
+	case utils.DATA_SOURCE_CALCULATE_LJZZJ: //累计值转季 -> 61
+		tableName = "edb_data_calculate_ljzzj"
+	case utils.DATA_SOURCE_CALCULATE_LJZ: //累计值 -> 62
+		tableName = "edb_data_calculate_ljz"
+	case utils.DATA_SOURCE_CALCULATE_LJZNCZJ: //累计值(年初至今) -> 63
+		tableName = "edb_data_calculate_ljznczj"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZZJ: // 预测指标 - 累计值 -> 65
+		tableName = "edb_data_predict_calculate_ljzzj"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZ: //预测指标 - 累计值转季->64
+		tableName = "edb_data_predict_calculate_ljz"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ: //预测指标 - 累计值(年初至今) -> 66
+		tableName = "edb_data_predict_calculate_ljznczj"
+	case utils.DATA_SOURCE_CALCULATE_STANDARD_DEVIATION: //标准差->67
+		tableName = "edb_data_calculate_standard_deviation"
+	case utils.DATA_SOURCE_CALCULATE_PERCENTILE: //百分位->68
+		tableName = "edb_data_calculate_percentile"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION: //预测标准差->69
+		tableName = "edb_data_predict_ccalculate_standard_deviation"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE: //预测百分位->70
+		tableName = "edb_data_predict_ccalculate_percentile"
+	case utils.DATA_SOURCE_FUBAO:
+		tableName = "edb_data_fubao"
 	default:
 		tableName = ""
 	}

+ 58 - 3
models/edb_info.go

@@ -700,11 +700,31 @@ func GetChartPredictEdbInfoDataListByConfList(predictEdbConfList []*PredictEdbCo
 			if err != nil {
 				return
 			}
+
+		case 15: //15:N年均值:过去N年同期均值。过去N年可以连续或者不连续,指标数据均用线性插值补全为日度数据后计算;
+			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleNAnnualAverage(predictEdbConf.PredictEdbInfoId, predictEdbConf.Value, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
+			if err != nil {
+				return
+			}
+
+		case 16: //16:年度值倒推
+			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(predictEdbConf.PredictEdbInfoId, predictEdbConf.Value, dayList, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
+			if err != nil {
+				return
+			}
 		}
-		//startDate = dataEndTime.AddDate(0, 0, 1)
-		if startDate.Before(dataEndTime) {
-			startDate = dataEndTime
+
+		// 下一个规则的开始日期
+		{
+			lenPredictEdbInfoData := len(predictEdbInfoData)
+			if lenPredictEdbInfoData > 0 {
+				tmpDataEndTime, _ := time.ParseInLocation(utils.FormatDate, predictEdbInfoData[lenPredictEdbInfoData-1].DataTime, time.Local)
+				if startDate.Before(tmpDataEndTime) {
+					startDate = tmpDataEndTime
+				}
+			}
 		}
+
 		if tmpMinValue < minValue {
 			minValue = tmpMinValue
 		}
@@ -716,6 +736,8 @@ func GetChartPredictEdbInfoDataListByConfList(predictEdbConfList []*PredictEdbCo
 	return
 }
 
+// GetPredictEdbDayList 获取预测指标日期列表
+
 // GetPredictEdbDayList 获取预测指标日期列表
 func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
 	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
@@ -733,6 +755,15 @@ func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayLi
 		for currDate := startDate.AddDate(0, 0, 7); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 7) {
 			dayList = append(dayList, currDate)
 		}
+	case "旬度":
+		for currDate := startDate.AddDate(0, 0, 1); currDate.Before(endDate) || currDate.Equal(endDate); {
+			nextDate := currDate.AddDate(0, 0, 1)
+			//每个月的10号、20号、最后一天,那么就写入
+			if nextDate.Day() == 11 || nextDate.Day() == 21 || nextDate.Day() == 1 {
+				dayList = append(dayList, currDate)
+			}
+			currDate = nextDate
+		}
 	case "月度":
 		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
 			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
@@ -741,6 +772,30 @@ func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayLi
 			}
 			currDate = currDate.AddDate(0, 0, 1)
 		}
+	case "季度":
+		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
+			// 每月的最后一天
+			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
+			if !currDate.After(endDate) && !currDate.Equal(startDate) {
+				// 季度日期就写入,否则不写入
+				if currDate.Month() == 3 || currDate.Month() == 6 || currDate.Month() == 9 || currDate.Month() == 12 {
+					dayList = append(dayList, currDate)
+				}
+			}
+			currDate = currDate.AddDate(0, 0, 1)
+		}
+	case "半年度":
+		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
+			// 每月的最后一天
+			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
+			if !currDate.After(endDate) && !currDate.Equal(startDate) {
+				// 半年度日期就写入,否则不写入
+				if currDate.Month() == 6 || currDate.Month() == 12 {
+					dayList = append(dayList, currDate)
+				}
+			}
+			currDate = currDate.AddDate(0, 0, 1)
+		}
 	case "年度":
 		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
 			currDate = time.Date(currDate.Year()+1, 12, 31, 0, 0, 0, 0, time.Now().Location())

+ 88 - 0
models/future_good/chart_info_future_good_profit.go

@@ -0,0 +1,88 @@
+package future_good
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ChartInfoFutureGoodProfit 商品利润图表-扩展信息
+type ChartInfoFutureGoodProfit struct {
+	ChartInfoId  int       `orm:"column(chart_info_id);pk" description:"商品利润图表ID(chart_info表source=5的)"`
+	ProfitName   string    `description:"利润名称"`
+	ProfitNameEn string    `description:"利润英文名称"`
+	XValue       string    `description:"X轴数据值"`
+	YValue       string    `description:"Y轴数据值"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time `description:"更新时间"`
+}
+
+type CorrelationInfo struct {
+	LeadValue       int    `description:"领先值"`
+	LeadUnit        string `description:"领先单位"`
+	CalculateValue  int    `description:"计算窗口"`
+	CalculateUnit   string `description:"计算频度"`
+	StartDate       string `description:"开始日期"`
+	EndDate         string `description:"结束日期"`
+	EdbInfoIdFirst  int    `description:"A指标ID"`
+	EdbInfoIdSecond int    `description:"B指标ID"`
+	PeriodData      string `description:"X轴-期数数据"`
+	CorrelationData string `description:"Y轴-商品利润系数"`
+}
+
+func (m *ChartInfoFutureGoodProfit) TableName() string {
+	return "chart_info_future_good_profit"
+}
+
+func (m *ChartInfoFutureGoodProfit) Create() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(m)
+	if err != nil {
+		return
+	}
+	//m.CorrelationChartInfoId = int(id)
+	return
+}
+
+func (m *ChartInfoFutureGoodProfit) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ChartInfoFutureGoodProfit) Delete() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE chart_info_id = ? LIMIT 1`, m.TableName())
+	_, err = o.Raw(sql, m.ChartInfoId).Exec()
+	return
+}
+
+func (m *ChartInfoFutureGoodProfit) GetItemById(id int) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE chart_info_id = ? LIMIT 1`, m.TableName())
+	err = o.Raw(sql, id).QueryRow(&m)
+	return
+}
+
+func (m *ChartInfoFutureGoodProfit) GetItemByCondition(condition string, pars []interface{}) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&m)
+	return
+}
+
+func (m *ChartInfoFutureGoodProfit) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ChartInfoFutureGoodProfit, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 43 - 13
models/future_good/future_good_edb_data.go

@@ -3,8 +3,6 @@ package future_good
 import (
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strconv"
 	"strings"
@@ -51,6 +49,29 @@ type FutureGoodEdbDataItem struct {
 	CreateTime          time.Time
 }
 
+// FutureGoodDataFromThs 同花顺期货数据
+type FutureGoodDataFromThs struct {
+	DataVol   int64                `json:"dataVol"`
+	Errmsg    string               `json:"errmsg"`
+	Errorcode int64                `json:"errorcode"`
+	Perf      interface{}          `json:"perf"`
+	Tables    FutureGoodDataTables `json:"tables"`
+}
+
+// FutureGoodDataTables 同花顺表格数据
+type FutureGoodDataTables struct {
+	//TradeCode    []string  `json:"id"`
+	Time       []string  `json:"time"`
+	Open       []float64 `json:"open"`
+	High       []float64 `json:"high"`
+	Low        []float64 `json:"low"`
+	Close      []float64 `json:"close"`
+	Volume     []float64 `json:"volume"`
+	Amount     []float64 `json:"amount"`
+	Ccl        []float64 `json:"ccl"`
+	Settlement []float64 `json:"settlement"`
+}
+
 // GetFutureGoodEdbDataList 获取期货指标数据列表
 func GetFutureGoodEdbDataList(condition string, pars []interface{}) (list []*FutureGoodEdbDataItem, err error) {
 	o := orm.NewOrm()
@@ -64,15 +85,8 @@ func GetFutureGoodEdbDataList(condition string, pars []interface{}) (list []*Fut
 }
 
 // AddEdbDataFromWind 添加wind商品指标数据
-func AddEdbDataFromWind(futureGoodEdbInfoId int, edbCode string, item services.FutureGoodDataFromThs) (err error) {
-	var errMsg string
+func AddEdbDataFromWind(futureGoodEdbInfoId int, edbCode string, item *FutureGoodDataFromThs) (err error) {
 	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 同花顺数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
-			go alarm_msg.SendAlarmMsg("wind商品数据获取失败:err:"+errMsg, 3)
-		}
-	}()
 
 	var isAdd bool
 	addSql := ` INSERT INTO future_good_edb_data(future_good_edb_info_id,future_good_edb_code,data_time,trade_code,open,high,low,close,volume,amt,oi,settle,create_time,modify_time,data_timestamp) values `
@@ -83,7 +97,6 @@ func AddEdbDataFromWind(futureGoodEdbInfoId int, edbCode string, item services.F
 		eDate := table.Time[k]
 		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 		if err != nil {
-			errMsg = " time.Parse :" + err.Error()
 			return err
 		}
 		timestamp := dataTime.UnixNano() / 1e6
@@ -108,7 +121,6 @@ func AddEdbDataFromWind(futureGoodEdbInfoId int, edbCode string, item services.F
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()
 		if err != nil {
-			errMsg = " tx.Exec Err :" + err.Error()
 			return
 		}
 	}
@@ -123,7 +135,7 @@ type RefreshFutureEdbEdbInfoReq struct {
 }
 
 // RefreshFutureGoodEdbDataFromThs 刷新wind期货指标数据
-func RefreshFutureGoodEdbDataFromThs(futureGoodEdbInfoId int, edbCode, startDate string, item services.FutureGoodDataFromThs) (err error) {
+func RefreshFutureGoodEdbDataFromThs(futureGoodEdbInfoId int, edbCode, startDate string, item *FutureGoodDataFromThs) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {
@@ -271,3 +283,21 @@ func GetAddSql(futureGoodEdbInfoId, futureGoodEdbCode, dataTime, tradeCode, open
 	addSql = fmt.Sprintf("(%s,'%s','%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,'%s','%s',%s),", futureGoodEdbInfoId, futureGoodEdbCode, dataTime, tradeCode, open, high, low, close, volume, amt, oi, settle, nowStr, nowStr, timestampStr)
 	return
 }
+
+func GetFutureGoodEdbDataListByDate(futureGoodEdbInfoId int, startDate, endDate string) (list []*FutureGoodEdbData, err error) {
+	var pars []interface{}
+	sql := `SELECT * FROM future_good_edb_data WHERE 1=1 AND future_good_edb_info_id = ? `
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, futureGoodEdbInfoId, pars).QueryRows(&list)
+	return
+}

+ 22 - 0
models/future_good/future_good_edb_info.go

@@ -11,8 +11,13 @@ type FutureGoodEdbInfo struct {
 	FutureGoodEdbInfoId int       `orm:"column(future_good_edb_info_id);pk"`
 	FutureGoodEdbCode   string    `description:"期货指标code"`
 	FutureGoodEdbName   string    `description:"期货指标名称"`
+	FutureGoodEdbNameEn string    `description:"期货指标英文名称"`
 	ParentId            int       `description:"上级期货id"`
+	RegionType          string    `description:"交易所来源,海外还是国内"`
 	Exchange            string    `description:"所属交易所"`
+	FutureGoodEdbType   int       `description:"指标类型,1:年月是固定的合约;2:只有M+N期的合约,未固定年月"`
+	DateSourceId        int       `description:"画图时,日期来源的指标id"`
+	Year                int       `description:"所属年份"`
 	Month               int       `description:"所属月份"`
 	StartDate           string    `description:"起始日期"`
 	EndDate             string    `description:"终止日期"`
@@ -25,6 +30,15 @@ type FutureGoodEdbInfo struct {
 	ModifyTime          time.Time
 }
 
+// GetFutureGoodEdbInfo 期货指标
+func GetFutureGoodEdbInfo(edbInfoId int) (item *FutureGoodEdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM future_good_edb_info WHERE future_good_edb_info_id = ? `
+	sql += ` ORDER BY future_good_edb_info_id DESC `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
 // GetFutureGoodEdbInfoByCode 根据指标code获取指标信息
 func GetFutureGoodEdbInfoByCode(edbCode string) (item *FutureGoodEdbInfo, err error) {
 	o := orm.NewOrm()
@@ -64,6 +78,14 @@ func AddFutureGoodEdbInfo(item *FutureGoodEdbInfo) (err error) {
 	return
 }
 
+// GetChildFutureGoodEdbInfoListByParentId 根据父级ID获取下面所有的指标数据列表
+func GetChildFutureGoodEdbInfoListByParentId(parentId int) (list []*FutureGoodEdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM future_good_edb_info WHERE parent_id = ? ORDER BY future_good_edb_info_id ASC `
+	_, err = o.Raw(sql, parentId).QueryRows(&list)
+	return
+}
+
 // Update 更新指标基础信息
 func (FutureGoodEdbInfo *FutureGoodEdbInfo) Update(cols []string) (err error) {
 	o := orm.NewOrm()

+ 22 - 1
models/handle_data.go

@@ -9,7 +9,7 @@ import (
 )
 
 // HandleDataByLinearRegression 插值法补充数据(线性方程式)
-func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDataMap map[string]float64) (err error) {
+func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDataMap map[string]float64) (newList []*EdbInfoSearchData, err error) {
 	if len(edbInfoDataList) < 2 {
 		return
 	}
@@ -21,6 +21,11 @@ func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDa
 		// 第一个数据就给过滤了,给后面的试用
 		if startEdbInfoData == nil {
 			startEdbInfoData = v
+			newList = append(newList, &EdbInfoSearchData{
+				EdbDataId: v.EdbDataId,
+				DataTime:  v.DataTime,
+				Value:     v.Value,
+			})
 			continue
 		}
 
@@ -33,6 +38,11 @@ func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDa
 		// 如果相差一天,那么过滤
 		if betweenDay <= 1 {
 			startEdbInfoData = v
+			newList = append(newList, &EdbInfoSearchData{
+				EdbDataId: v.EdbDataId,
+				DataTime:  v.DataTime,
+				Value:     v.Value,
+			})
 			continue
 		}
 
@@ -68,9 +78,20 @@ func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDa
 
 				val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
 				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
+				newList = append(newList, &EdbInfoSearchData{
+					DataTime: tmpDataTime.Format(utils.FormatDate),
+					Value:    val,
+				})
 			}
 		}
 
+		// 最后将自己赋值
+		newList = append(newList, &EdbInfoSearchData{
+			EdbDataId: v.EdbDataId,
+			DataTime:  v.DataTime,
+			Value:     v.Value,
+		})
+
 		startEdbInfoData = v
 	}
 

+ 3 - 11
models/predict_edb.go

@@ -7,8 +7,6 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/shopspring/decimal"
 	"github.com/yidane/formula"
-	"hongze/hongze_edb_lib/services"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"strings"
 	"time"
@@ -36,10 +34,7 @@ func RefreshCalculateByRuleBy9(rule CalculateRule) (resultDataList []*EdbInfoSea
 	}
 	defer func() {
 		if err != nil {
-			tmpErr := to.Rollback()
-			if tmpErr != nil {
-				go alarm_msg.SendAlarmMsg("RefreshCalculateByRuleBy9 事务回滚失败,Err:"+tmpErr.Error(), 3)
-			}
+			to.Rollback()
 		} else {
 			err = to.Commit()
 		}
@@ -105,7 +100,7 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 	addDataList := make([]*PredictEdbRuleData, 0)
 
 	// 计算规则
-	formulaMap := services.CheckFormula(formulaStr)
+	formulaMap := utils.CheckFormula(formulaStr)
 
 	//获取指标所有数据
 	dataList := make([]*PredictEdbRuleData, 0)
@@ -229,10 +224,7 @@ func RefreshCalculateByRuleByLineNh(predictEdbInfo EdbInfo, predictEdbConfAndDat
 	}
 	defer func() {
 		if err != nil {
-			tmpErr := to.Rollback()
-			if tmpErr != nil {
-				go alarm_msg.SendAlarmMsg("RefreshCalculateByRuleBy9 事务回滚失败,Err:"+tmpErr.Error(), 3)
-			}
+			to.Rollback()
 		} else {
 			err = to.Commit()
 		}

+ 9 - 11
models/predict_edb_conf.go

@@ -2,7 +2,6 @@ package models
 
 import (
 	"github.com/beego/beego/v2/client/orm"
-	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"time"
 )
@@ -16,6 +15,8 @@ type AddPredictEdbInfoReq struct {
 	SourceEdbInfoId int          `description:"来源指标id"`
 	EdbName         string       `description:"指标名称"`
 	RuleList        []RuleConfig `description:"配置规则列表"`
+	MaxValue        float64      `description:"最大值"`
+	MinValue        float64      `description:"最小值"`
 }
 
 // RuleConfig 预测规则配置
@@ -106,6 +107,7 @@ func GetPredictEdbConfListByConfigIdList(configIdList []int) (items []*PredictEd
 
 // ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId 根据来源指标修改预测指标的最新数据信息
 func ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(sourceEdbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
+	//return
 	o := orm.NewOrm()
 	var list []*PredictEdbConf
 	sql := ` SELECT * FROM predict_edb_conf WHERE source_edb_info_id=? `
@@ -120,8 +122,10 @@ func ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(sourceEdbInfoId int, ite
 			idList = append(idList, v.PredictEdbInfoId)
 		}
 
-		sql := ` UPDATE edb_info SET start_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
-		_, err = o.Raw(sql, item.MinDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, idList).Exec()
+		//sql := ` UPDATE edb_info SET start_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
+		//_, err = o.Raw(sql, item.MinDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, idList).Exec()
+		sql := ` UPDATE edb_info SET start_date=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
+		_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.LatestValue, idList).Exec()
 	}
 	return
 }
@@ -151,10 +155,7 @@ func AddPredictEdb(item *EdbInfo, calculateMappingList []*EdbInfoCalculateMappin
 	}
 	defer func() {
 		if err != nil {
-			tmpErr := tx.Rollback()
-			if tmpErr != nil {
-				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
-			}
+			tx.Rollback()
 		} else {
 			err = tx.Commit()
 		}
@@ -270,10 +271,7 @@ func EditPredictEdb(edbInfo *EdbInfo, updateEdbInfoCol []string, calculateMappin
 	}
 	defer func() {
 		if err != nil {
-			tmpErr := tx.Rollback()
-			if tmpErr != nil {
-				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
-			}
+			tx.Rollback()
 		} else {
 			err = tx.Commit()
 		}

+ 17 - 1
models/predict_edb_data_calculate_jp.go

@@ -106,6 +106,22 @@ func SavePredictCalculateJp(reqEdbInfoId, classifyId int, edbName, frequency, un
 			return
 		}
 
+		latestDateStr = edbInfo.LatestDate
+		latestValue = edbInfo.LatestValue
+		oldCalculateFormula := edbInfo.CalculateFormula
+		//修改指标信息
+		edbInfo.EdbName = edbName
+		edbInfo.EdbNameSource = edbName
+		edbInfo.Frequency = frequency
+		edbInfo.Unit = unit
+		edbInfo.ClassifyId = classifyId
+		edbInfo.CalculateFormula = formula
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+		if err != nil {
+			return
+		}
+
 		//判断计算指标是否被更换
 		var existCondition string
 		var existPars []interface{}
@@ -117,7 +133,7 @@ func SavePredictCalculateJp(reqEdbInfoId, classifyId int, edbName, frequency, un
 			err = errors.New("判断指标是否改变失败,Err:" + tmpErr.Error())
 			return
 		}
-		if count > 0 { // 指标未被替换,无需重新计算
+		if count > 0 && formula == oldCalculateFormula { // 指标未被替换,无需重新计算
 			return
 		}
 

+ 627 - 0
models/predict_edb_data_calculate_ljz.go

@@ -0,0 +1,627 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictLjz 预测累计值
+type PredictLjz struct {
+}
+
+// Add 添加
+func (obj PredictLjz) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+
+	frequencyInt := utils.CheckFrequency(fromEdbInfo.Frequency, req.Frequency)
+	if frequencyInt < 0 {
+		errMsg = "频度异常,不允许低频转为高频"
+		err = errors.New(errMsg)
+		return
+	}
+	if frequencyInt == 0 {
+		errMsg = "频度异常,不允许同频计算预测累计值"
+		err = errors.New(errMsg)
+		return
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      1,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj PredictLjz) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	frequencyInt := utils.CheckFrequency(fromEdbInfo.Frequency, req.Frequency)
+	if frequencyInt < 0 {
+		errMsg = "频度异常,不允许低频转为高频"
+		err = errors.New(errMsg)
+		return
+	}
+	if frequencyInt == 0 {
+		errMsg = "频度异常,不允许同频计算预测累计值"
+		err = errors.New(errMsg)
+		return
+	}
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.Frequency != req.Frequency {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj PredictLjz) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculatePredictLjzDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictLjz) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_LJZ
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictLjz) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZ
+}
+
+func (obj PredictLjz) refresh(to orm.TxOrmer, edbInfoId, source int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7
+	// 周度数据需要先变成日度的
+	if fromEdbInfo.Frequency == `周度` {
+		isWeekData = true
+	}
+
+	// 获取来源指标的数据
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+
+	fromEdbDataMap := make(map[string]float64)
+	if isWeekData {
+		dataList, err = HandleDataByLinearRegression(dataList, fromEdbDataMap)
+		if err != nil {
+			return
+		}
+	}
+
+	//日度转周度:日期选周五,计算上周六到本周五的日度值的加总,最新日期为最新值对应的周五。
+	//日度转月度:日期选每个月最后一天,计算当月所有日度值的加总,最新日期为最新值对应当月最后一天。
+	//日度转季度、年度:方法类似转月度。
+	//周度转月度/季度/年度:将周度值转成日度,空值用插值法插值,计算当月/当季/当年所有值的加总,然后除以7。
+	//月度转季度/年度: 当季/当年月度值相加。
+
+	dateList := make([]time.Time, 0)
+	valueMap := make(map[time.Time]float64)
+	switch edbInfo.Frequency {
+	case "年度":
+		yearMap := make(map[int]float64)
+		yearList := make([]int, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearList = append(yearList, year)
+				yearMap[year] = item.Value
+			}
+		}
+		for _, v := range yearList {
+			currTime := time.Date(v, 12, 31, 0, 0, 0, 0, time.Local)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMap[v]
+		}
+	case "半年度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			if itemDate.Month() <= 6 {
+				tmpK = fmt.Sprint(year, "06")
+			} else {
+				tmpK = fmt.Sprint(year, "12")
+			}
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "季度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			if itemDate.Month() <= 3 {
+				tmpK = fmt.Sprint(year, "03")
+			} else if itemDate.Month() <= 6 {
+				tmpK = fmt.Sprint(year, "06")
+			} else if itemDate.Month() <= 9 {
+				tmpK = fmt.Sprint(year, "09")
+			} else {
+				tmpK = fmt.Sprint(year, "12")
+			}
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "月度":
+		yearMonthMap := make(map[string]float64)
+		yearMonthList := make([]string, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			var tmpK string
+			tmpK = fmt.Sprint(year*100 + int(itemDate.Month()))
+
+			yearVal, ok := yearMonthMap[tmpK]
+			if ok {
+				yearMonthMap[tmpK] = item.Value + yearVal
+			} else {
+				yearMonthList = append(yearMonthList, tmpK)
+				yearMonthMap[tmpK] = item.Value
+			}
+		}
+		for _, v := range yearMonthList {
+			currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			currTime = currTime.AddDate(0, 1, -1)
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = yearMonthMap[v]
+		}
+	case "旬度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			dayInt := itemDate.Year()*100 + int(itemDate.Month())
+			var currTime time.Time
+			if itemDate.Month() <= 10 {
+				tmpK := fmt.Sprint(dayInt*100, "10")
+				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
+				if err != nil {
+					return
+				}
+
+			} else if itemDate.Month() <= 20 {
+				tmpK := fmt.Sprint(dayInt*100, "20")
+				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
+				if err != nil {
+					return
+				}
+			} else {
+				currTime, err = time.ParseInLocation(utils.FormatYearMonthUnSpace, fmt.Sprint(dayInt), time.Local)
+				if err != nil {
+					return
+				}
+				currTime = currTime.AddDate(0, 1, -1)
+			}
+
+			yearVal, ok := tmpDateDataMap[currTime]
+			if ok {
+				tmpDateDataMap[currTime] = item.Value + yearVal
+			} else {
+				tmpDateList = append(tmpDateList, currTime)
+				tmpDateDataMap[currTime] = item.Value
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+	case "周度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			var currTime time.Time
+			// 周六周日,这是下一个周五的数据
+			if itemDate.Weekday() == 0 {
+				currTime = itemDate.AddDate(0, 0, 5)
+			} else if itemDate.Weekday() == 6 {
+				currTime = itemDate.AddDate(0, 0, 6)
+			} else {
+				currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday()))
+			}
+
+			yearVal, ok := tmpDateDataMap[currTime]
+			if ok {
+				tmpDateDataMap[currTime] = item.Value + yearVal
+			} else {
+				tmpDateList = append(tmpDateList, currTime)
+				tmpDateDataMap[currTime] = item.Value
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, currTime := range dateList {
+		currDateStr := currTime.Format(utils.FormatDate)
+		existVal, ok := existDataMap[currDateStr]
+
+		tmpVal, ok2 := valueMap[currTime]
+		if !ok2 {
+			err = errors.New("数据异常,date:" + currDateStr)
+			return
+		}
+		var saveValue string
+		if isWeekData { //周度指标转的话,最后结果要除以7
+			saveValue = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).String()
+		} else {
+			saveValue = decimal.NewFromFloat(tmpVal).Round(4).String()
+		}
+
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+	// 确定实际数据的最终值
+	{
+		var finalLast EdbInfoSearchData
+		sql = fmt.Sprintf(` SELECT data_time , value FROM %s WHERE edb_info_id=? and data_time<=? ORDER BY data_time DESC `, dataTableName)
+		err = to.Raw(sql, edbInfoId, fromEdbInfo.LatestDate).QueryRow(&finalLast)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+		if err != nil {
+			err = nil
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+
+	return
+}

+ 475 - 0
models/predict_edb_data_calculate_ljznczj.go

@@ -0,0 +1,475 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictLjzNczj 预测累计值年初至今
+type PredictLjzNczj struct {
+}
+
+// Add 添加
+func (obj PredictLjzNczj) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	if fromEdbInfo.Frequency == `年度` {
+		errMsg = "年初至今累计值计算中,可选指标范围为非年度指标"
+		err = errors.New(errMsg)
+		return
+	}
+	if fromEdbInfo.Frequency != req.Frequency {
+		errMsg = "生成指标频度与原指标频度不同"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      1,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj PredictLjzNczj) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	if fromEdbInfo.Frequency == `年度` {
+		errMsg = "年初至今累计值计算中,可选指标范围为非年度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if fromEdbInfo.Frequency != req.Frequency {
+		errMsg = "生成指标频度与原指标频度不同"
+		err = errors.New(errMsg)
+		return
+	}
+
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.Frequency != req.Frequency {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj PredictLjzNczj) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculatePredictLjzNczjzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictLjzNczj) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictLjzNczj) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZNCZJ
+}
+
+func (obj PredictLjzNczj) refresh(to orm.TxOrmer, edbInfoId, source int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7
+	// 周度数据需要先变成日度的
+	if fromEdbInfo.Frequency == `周度` {
+		isWeekData = true
+	}
+
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+
+	fromEdbDataMap := make(map[string]float64)
+	if isWeekData {
+		dataList, err = HandleDataByLinearRegression(dataList, fromEdbDataMap)
+		if err != nil {
+			return
+		}
+	}
+
+	//日度数据年初至今:日期同原日度数据。将每年1月1日(含)到日度数据所在日期含间的日度值,进行加总。
+	//周度数据年初至今:日期同原周度数据。将周度值转成日度频率,空值用插值法插值,然后算法同日度年度至今,再除以7
+	//月度/季度数据年初至今:日期同原月度/季度数据,将每年1月1日(含)到月度数据所在日期(含)之间的月度/季度值,进行加总
+	//以此类推
+
+	dateList := make([]time.Time, 0)
+	valueMap := make(map[time.Time]float64)
+	yearMap := make(map[int]float64)
+	switch edbInfo.Frequency {
+	case "周度":
+		tmpDateDataMap := make(map[time.Time]float64)
+		tmpDateList := make([]time.Time, 0)
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			var currTime time.Time
+			// 周六周日,这是下一个周五的数据
+			if itemDate.Weekday() == 0 {
+				currTime = itemDate.AddDate(0, 0, 5)
+			} else if itemDate.Weekday() == 6 {
+				currTime = itemDate.AddDate(0, 0, 6)
+			} else {
+				currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday()))
+			}
+
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearMap[year] = item.Value
+			}
+
+			if itemDate.Equal(currTime) {
+				tmpDateDataMap[itemDate] = yearMap[year]
+				tmpDateList = append(tmpDateList, itemDate)
+			}
+		}
+		for _, currTime := range tmpDateList {
+			dateList = append(dateList, currTime)
+			valueMap[currTime] = tmpDateDataMap[currTime]
+		}
+	default:
+		for _, item := range dataList {
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			year := itemDate.Year()
+			yearVal, ok := yearMap[year]
+			if ok {
+				yearMap[year] = item.Value + yearVal
+			} else {
+				yearMap[year] = item.Value
+			}
+			valueMap[itemDate] = yearMap[year]
+			dateList = append(dateList, itemDate)
+		}
+
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, currTime := range dateList {
+		currDateStr := currTime.Format(utils.FormatDate)
+		existVal, ok := existDataMap[currDateStr]
+
+		tmpVal, ok2 := valueMap[currTime]
+		if !ok2 {
+			err = errors.New("数据异常,date:" + currDateStr)
+			return
+		}
+		var saveValue string
+		if isWeekData { //周度指标转的话,最后结果要除以7
+			saveValue = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).String()
+		} else {
+			saveValue = decimal.NewFromFloat(tmpVal).Round(4).String()
+		}
+
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	//确定实际数据的最终值
+	{
+		var finalLast EdbInfoSearchData
+		sql = fmt.Sprintf(` SELECT data_time , value FROM %s WHERE edb_info_id=? and data_time<=? ORDER BY data_time DESC `, dataTableName)
+		err = to.Raw(sql, edbInfoId, fromEdbInfo.LatestDate).QueryRow(&finalLast)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+		if err != nil {
+			err = nil
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+
+	return
+}

+ 420 - 0
models/predict_edb_data_calculate_ljzzj.go

@@ -0,0 +1,420 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictLjzzj 预测累计值转季度
+type PredictLjzzj struct {
+}
+
+// Add 添加
+func (obj PredictLjzzj) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+	if fromEdbInfo.Frequency != "季度" {
+		errMsg = "请选择季度指标"
+		err = errors.New(errMsg)
+		return
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbInfoType = 1
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Edit 编辑
+func (obj PredictLjzzj) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+	if fromEdbInfo.Frequency != "季度" {
+		errMsg = "请选择季度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需重新计算
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// Refresh 刷新
+func (obj PredictLjzzj) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoCalculateLjzzyDetail Err:" + err.Error()
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, fromEdbInfo, params.EdbInfo.EdbCode)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictLjzzj) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_LJZZJ
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictLjzzj) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZZJ
+}
+
+func (obj PredictLjzzj) refresh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (latestDateStr string, latestValue float64, err error) {
+	dataTableName := GetEdbDataTableName(source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	// 获取来源指标的数据
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+
+	yearMap := make(map[int]map[int]*EdbInfoSearchData)
+	dataLen := len(dataList)
+	for i := 0; i < dataLen; i++ {
+		item := dataList[i]
+		//日其中获取年
+		itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		year := itemDate.Year()
+		quarter := utils.MonthQuarterMap[int(itemDate.Month())]
+		if quarterMap, yok := yearMap[year]; yok {
+			quarterMap[quarter] = item
+			yearMap[year] = quarterMap
+		} else {
+			quarterMap = make(map[int]*EdbInfoSearchData)
+			quarterMap[quarter] = item
+			yearMap[year] = quarterMap
+		}
+	}
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	dataMap := make(map[string]string)
+	for _, v := range existDataList {
+		dataMap[v.DataTime] = v.Value
+	}
+	existDataMap := make(map[string]string)
+	for yk, yv := range yearMap {
+		_, oneQuarterOk := yv[1]
+		_, twoQuarterOk := yv[2]
+		if !oneQuarterOk && !twoQuarterOk {
+			continue
+		}
+		for i := 1; i <= 4; i++ {
+			fmt.Println(yk, i, yv[i])
+			dataCurrentItem := yv[i]
+			var date string
+			var val float64
+			if dataCurrentItem != nil {
+				if i == 1 || i == 2 {
+					if _, mok := yv[1]; mok { //第1个季度有值
+						if i == 1 {
+							date = dataCurrentItem.DataTime
+							val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Float64() //a.Div(b).Float64()
+						}
+						if i == 2 {
+							dataOneItem := yv[1]
+							date = dataCurrentItem.DataTime
+							twoQuarter := decimal.NewFromFloat(dataCurrentItem.Value)
+							oneQuarter := decimal.NewFromFloat(dataOneItem.Value)
+							val, _ = twoQuarter.Sub(oneQuarter).Float64()
+						}
+					} else { //第1个季度无值
+						dataTwoItem := yv[2]
+						if i == 1 {
+							date = strconv.Itoa(yk) + "-03-31"
+							a := decimal.NewFromFloat(dataTwoItem.Value)
+							b := decimal.NewFromFloat(2.0)
+							val, _ = a.Div(b).Float64()
+						}
+						if i == 2 {
+							//第1个季度无值:第1个季度=第2个季度/2
+							{
+								date = strconv.Itoa(yk) + "-03-31"
+								a := decimal.NewFromFloat(dataTwoItem.Value)
+								b := decimal.NewFromFloat(2.0)
+								val, _ = a.Div(b).Float64()
+
+								tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
+								if !isAdd {
+									isAdd = newAdd
+								}
+								addSql = tmpSql
+								if tmpErr != nil {
+									err = tmpErr
+									return
+								}
+							}
+							//end 第1个季度无值
+
+							date = dataCurrentItem.DataTime
+							a := decimal.NewFromFloat(dataTwoItem.Value)
+							b := decimal.NewFromFloat(2.0)
+							val, _ = a.Div(b).Float64()
+						}
+					}
+				} else {
+					dataPreItem := yv[i-1]
+					if dataCurrentItem != nil && dataPreItem != nil {
+						date = dataCurrentItem.DataTime
+						//val =  dataCurrentItem.Value - dataPreItem.Value
+						a := decimal.NewFromFloat(dataCurrentItem.Value)
+						b := decimal.NewFromFloat(dataPreItem.Value)
+						val, _ = a.Sub(b).Float64()
+					}
+				}
+			}
+			if date != "" {
+				tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
+				if !isAdd {
+					isAdd = newAdd
+				}
+				addSql = tmpSql
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	//确定实际数据的最终值
+	{
+		var finalLast EdbInfoSearchData
+		sql = fmt.Sprintf(` SELECT data_time , value FROM %s WHERE edb_info_id=? and data_time<=? ORDER BY data_time DESC `, dataTableName)
+		err = to.Raw(sql, edbInfoId, fromEdbInfo.LatestDate).QueryRow(&finalLast)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+		if err != nil {
+			err = nil
+		} else {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+
+	return
+}
+
+func (obj PredictLjzzj) calculate(edbInfoId int, date, edbInfoIdStr, edbCode, dataTableName, addSql string, val float64, dataMap, existDataMap map[string]string, to orm.TxOrmer) (newSql string, isAdd bool, err error) {
+	newSql = addSql
+	saveValue := utils.SubFloatToString(val, 4)
+	//判断数据是否存在
+	if existVal, ok := dataMap[date]; !ok {
+		dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := existDataMap[date]; !existOk {
+			newSql += GetAddSql(edbInfoIdStr, edbCode, date, timeStr, saveValue)
+			isAdd = true
+		}
+		existDataMap[date] = date
+	} else {
+		if existVal != saveValue {
+			sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+			sql = fmt.Sprintf(sql, dataTableName)
+			_, err = to.Raw(sql, saveValue, edbInfoId, date).Exec()
+		}
+	}
+	return
+}

+ 16 - 1
models/predict_edb_data_calculate_nh.go

@@ -106,6 +106,21 @@ func SavePredictCalculateNh(reqEdbInfoId, classifyId int, edbName, frequency, un
 			return
 		}
 
+		latestDateStr = edbInfo.LatestDate
+		latestValue = edbInfo.LatestValue
+		//修改指标信息
+		edbInfo.EdbName = edbName
+		edbInfo.EdbNameSource = edbName
+		edbInfo.Frequency = frequency
+		edbInfo.Unit = unit
+		edbInfo.ClassifyId = classifyId
+		edbInfo.CalculateFormula = formula
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+		if err != nil {
+			return
+		}
+
 		//判断计算指标是否被更换
 		var existCondition string
 		var existPars []interface{}
@@ -216,7 +231,7 @@ func refreshAllPredictCalculateNh(to orm.TxOrmer, edbInfoId, source int, fromEdb
 	}
 	// 插值法数据处理
 	handleDataMap := make(map[string]float64)
-	err = HandleDataByLinearRegression(fromDataList, handleDataMap)
+	_, err = HandleDataByLinearRegression(fromDataList, handleDataMap)
 	if err != nil {
 		return
 	}

+ 24 - 0
models/predict_edb_data_calculate_nszydbpjjs.go

@@ -230,8 +230,10 @@ func refreshAllPredictCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, form
 	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
 
 	existDataMap := make(map[string]*EdbData)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v
+		removeDataTimeMap[v.DataTime] = 1
 	}
 
 	//计算来源数据
@@ -316,6 +318,9 @@ func refreshAllPredictCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, form
 			}
 			existAddDataMap[av] = av
 		} else {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, av)
+
 			existValDecimal, _ := decimal.NewFromString(existVal.Value)
 			existStr := existValDecimal.String()
 			fmt.Println(existStr, valStr, av)
@@ -329,6 +334,25 @@ func refreshAllPredictCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, form
 			}
 		}
 	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println("refreshAllPredictCalculateNszydpjjs add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		//utils.FileLog.Info("addSql:" + addSql)

+ 441 - 0
models/predict_edb_data_calculate_percentile.go

@@ -0,0 +1,441 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictPercentile 预测累计值年初至今
+type PredictPercentile struct {
+}
+
+// Add 添加
+func (obj PredictPercentile) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      1,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj PredictPercentile) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.CalculateFormula != req.Formula {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj PredictPercentile) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictPercentile) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictPercentile) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_PERCENTILE
+}
+
+func (obj PredictPercentile) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	edbInfoId := edbInfo.EdbInfoId
+	dataTableName := GetEdbDataTableName(edbInfo.Source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	var percentileConfig PercentileConfig
+	err = json.Unmarshal([]byte(edbInfo.CalculateFormula), &percentileConfig)
+	if err != nil {
+		return
+	}
+
+	// 获取百分位的指标数据
+	fromDataList, err, errMsg := obj.getPercentileData(fromEdbInfo, percentileConfig.CalculateValue, percentileConfig.CalculateUnit)
+	if err != nil {
+		return
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, tmpData := range fromDataList {
+		currDateStr := tmpData.DataTime
+		currTime, tmpErr := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 当前的实际值
+		saveValue := decimal.NewFromFloat(tmpData.Value).Round(4).String()
+
+		existVal, ok := existDataMap[currDateStr]
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	//确定实际数据的最终值
+	{
+		finalLast, tmpErr := GetFinalLastByTo(to, edbInfoId, edbInfo.Source, fromEdbInfo.LatestDate)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			return
+		}
+		if tmpErr == nil {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+
+	return
+}
+
+// GetPercentileData 获取百分位的指标数据
+func (obj PredictPercentile) getPercentileData(fromEdbInfo *EdbInfo, calculateValue int, calculateUnit string) (newDataList []EdbInfoSearchData, err error, errMsg string) {
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*EdbInfoSearchData, 0)
+	switch fromEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		dataList, err = GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 1)
+	case 1:
+		dataList, err = GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", fromEdbInfo.EdbInfoType))
+		return
+	}
+
+	moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit]
+	if !ok {
+		errMsg = `错误的周期`
+		err = errors.New(errMsg)
+		return
+	}
+	calculateDay := calculateValue * moveUnitDays
+	// 指标对应的所有数据
+
+	newDataList = make([]EdbInfoSearchData, 0)
+
+	dataMap := make(map[time.Time]float64, 0)
+	for _, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		dataMap[currDateTime] = tmpData.Value
+	}
+
+	//百分位:对所选指标滚动地取对应时间长度的数据值,取最大值Max,最小值Min,计算Max-Min,百分位=(现值-Min)/(Max-Min),Max=Min时不予计算。
+	for i, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		maxVal := tmpData.Value
+		minVal := tmpData.Value
+		for k := 0; k < calculateDay; k++ {
+			preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)]
+			if ok2 {
+				if preVal > maxVal {
+					maxVal = preVal
+				}
+				if preVal < minVal {
+					minVal = preVal
+				}
+			}
+		}
+
+		if maxVal == minVal {
+			continue
+		}
+		tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100
+		tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
+		//百分位=(现值-Min)/(Max-Min)
+		newDataList = append(newDataList, EdbInfoSearchData{
+			EdbDataId: i,
+			DataTime:  dataList[i].DataTime,
+			Value:     tmpV,
+		})
+	}
+
+	return
+}

+ 423 - 0
models/predict_edb_data_calculate_standard_deviation.go

@@ -0,0 +1,423 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictStandardDeviation 预测累计值年初至今
+type PredictStandardDeviation struct {
+}
+
+// Add 添加
+func (obj PredictStandardDeviation) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	fromEdbInfo := params.FromEdbInfo
+	edbCode := params.EdbCode
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Add,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	edbInfo = &EdbInfo{
+		//EdbInfoId:        0,
+		SourceName:       obj.GetSourceName(),
+		Source:           obj.GetSource(),
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		StartDate:        "",
+		EndDate:          "",
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        params.SysUserId,
+		SysUserRealName:  params.SysUserRealName,
+		UniqueCode:       params.UniqueCode,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         0,
+		MaxValue:         0,
+		CalculateFormula: req.Formula,
+		EdbType:          2,
+		Sort:             0,
+		MoveType:         0,
+		MoveFrequency:    "",
+		NoUpdate:         0,
+		ServerUrl:        "",
+		EdbInfoType:      1,
+		EdbNameEn:        "",
+		UnitEn:           "",
+		LatestDate:       "",
+		LatestValue:      0,
+		ChartImage:       "",
+		Calendar:         "",
+	}
+
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = ""
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Edit 编辑
+func (obj PredictStandardDeviation) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	fromEdbInfo := params.FromEdbInfo
+
+	if fromEdbInfo.Frequency == `年度` {
+		errMsg = "年初至今累计值计算中,可选指标范围为非年度指标"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if fromEdbInfo.Frequency != req.Frequency {
+		errMsg = "生成指标频度与原指标频度不同"
+		err = errors.New(errMsg)
+		return
+	}
+
+	latestDateStr = edbInfo.LatestDate
+	latestValue = edbInfo.LatestValue
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Edit,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	tableName := GetEdbDataTableName(edbInfo.Source)
+
+	var isRecalculate bool
+	if edbInfo.CalculateFormula != req.Formula {
+		isRecalculate = true
+	}
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	//判断计算指标是否被更换
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需删除关联数据
+
+		// 频度被换了,需要重新计算
+		if isRecalculate {
+			latestDateStr, latestValue, err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+		}
+
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    obj.GetSource(),
+			SourceName:                obj.GetSourceName(),
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+
+	return
+}
+
+// Refresh 刷新
+func (obj PredictStandardDeviation) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	calculateMapping, err := GetEdbInfoCalculateMappingDetail(params.EdbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	fromEdbInfo, err := GetEdbInfoById(calculateMapping.FromEdbInfoId)
+	if err != nil {
+		errMsg = "GetEdbInfoById Err:" + err.Error()
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+			fmt.Println(reflect.TypeOf(obj).Name(), ";Refresh,Err:"+err.Error())
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = obj.refresh(to, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictStandardDeviation) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictStandardDeviation) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_STANDARD_DEVIATION
+}
+
+func (obj PredictStandardDeviation) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoId := edbInfo.EdbInfoId
+	dataTableName := GetEdbDataTableName(edbInfo.Source)
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	latestDateStr = fromEdbInfo.LatestDate
+
+	calculateValue, err := strconv.Atoi(edbInfo.CalculateFormula)
+	if err != nil {
+		return
+	}
+
+	// 获取标准差图表的指标数据
+	fromDataList, err := obj.getStandardDeviationData(fromEdbInfo, calculateValue)
+	if err != nil {
+		return
+	}
+
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+	needAddDateMap := make(map[time.Time]int)
+
+	addSql := ` INSERT INTO ` + dataTableName + `(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, tmpData := range fromDataList {
+		currDateStr := tmpData.DataTime
+		currTime, tmpErr := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 当前的实际值
+		saveValue := decimal.NewFromFloat(tmpData.Value).Round(4).String()
+
+		existVal, ok := existDataMap[currDateStr]
+		// 如果库中已经存在该数据的话,那么就进行值的变更操作
+		if ok {
+			//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+			delete(removeDataTimeMap, currDateStr)
+
+			if existVal != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, currDateStr).Exec()
+				if err != nil {
+					return
+				}
+			}
+
+			continue
+		}
+
+		// 库中不存在该日期的数据
+		timestamp := currTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, existOk := needAddDateMap[currTime]; !existOk {
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timeStr, saveValue)
+			isAdd = true
+		}
+		needAddDateMap[currTime] = 1
+	}
+
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
+			_, err = to.Raw(sql, edbInfo.EdbInfoId, removeDateList).Exec()
+			if err != nil {
+				fmt.Println(reflect.TypeOf(obj).Name(), " add data ;delete Err", err.Error())
+				err = fmt.Errorf("删除不存在的指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name(), " add data Err", err.Error())
+			return
+		}
+	}
+
+	//确定实际数据的最终值
+	{
+		finalLast, tmpErr := GetFinalLastByTo(to, edbInfoId, edbInfo.Source, fromEdbInfo.LatestDate)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			return
+		}
+		if tmpErr == nil {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+
+	return
+}
+
+// GetStandardDeviationData 获取标准差图表的指标数据
+func (obj PredictStandardDeviation) getStandardDeviationData(fromEdbInfo *EdbInfo, calculateValue int) (newDataList []EdbInfoSearchData, err error) {
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*EdbInfoSearchData, 0)
+	switch fromEdbInfo.EdbInfoType {
+	case 0:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+
+		//获取来源指标的数据
+		dataList, err = GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 1)
+	case 1:
+		dataList, err = GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", fromEdbInfo.EdbInfoType))
+		return
+	}
+
+	// 指标对应的所有数据
+	newDataList = make([]EdbInfoSearchData, 0)
+	lenData := len(dataList)
+	if lenData >= calculateValue {
+		tmpDataList := make([]float64, 0)
+		for _, tmpData := range dataList {
+			tmpDataList = append(tmpDataList, tmpData.Value)
+		}
+		for i := calculateValue; i <= lenData; i++ {
+			tmpV := utils.CalculateStandardDeviation(tmpDataList[i-calculateValue : i])
+			newDataList = append(newDataList, EdbInfoSearchData{
+				EdbDataId: i,
+				DataTime:  dataList[i-1].DataTime,
+				Value:     tmpV,
+			})
+		}
+	}
+
+	return
+}

+ 3 - 3
models/predict_edb_data_calculate_tbz.go

@@ -367,7 +367,7 @@ func RefreshAllPredictCalculateTbz(edbInfoId, source int, fromEdbInfo *EdbInfo,
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 
 	// 获取关联指标数据
-	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, startDate)
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, startDate)
 	if err != nil {
 		return
 	}
@@ -625,8 +625,8 @@ func RefreshAllPredictCalculateTbz(edbInfoId, source int, fromEdbInfo *EdbInfo,
 							break
 						} else {
 							if i >= 1 {
-								preDateDay = preDate.AddDate(0, 0, -i)
-								preDateDayStr = nextDateDay.Format(utils.FormatDate)
+								preDateDay = preDateDay.AddDate(0, 0, -i)
+								preDateDayStr = preDateDay.Format(utils.FormatDate)
 							}
 							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
 								timestamp := currentDate.UnixNano() / 1e6

+ 420 - 20
models/predict_edb_info_rule.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"github.com/nosixtools/solarlunar"
@@ -134,24 +135,20 @@ func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, dayL
 // @params a float64 去年同期值
 // @params b float64 固定同比增速
 func PredictTbzDiv(a, b float64) (result float64) {
-	if b != 0 {
-		// 去年同期值
-		af := decimal.NewFromFloat(a)
+	// 去年同期值
+	af := decimal.NewFromFloat(a)
 
-		// 同比增速
-		bf := decimal.NewFromFloat(b)
+	// 同比增速
+	bf := decimal.NewFromFloat(b)
 
-		// 默认1
-		cf := decimal.NewFromFloat(1)
+	// 默认1
+	cf := decimal.NewFromFloat(1)
 
-		// 总增速
-		val := bf.Add(cf)
+	// 总增速
+	val := bf.Add(cf)
 
-		// 计算
-		result, _ = val.Mul(af).RoundCeil(4).Float64()
-	} else {
-		result = 0
-	}
+	// 计算
+	result, _ = val.Mul(af).RoundCeil(4).Float64()
 	return
 }
 
@@ -690,7 +687,7 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, yearsList []int,
 
 	// 插值法数据处理
 	handleDataMap := make(map[string]float64)
-	err = HandleDataByLinearRegression(allDataList, handleDataMap)
+	_, err = HandleDataByLinearRegression(allDataList, handleDataMap)
 	if err != nil {
 		return
 	}
@@ -828,7 +825,7 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, yearsList []int,
 		existMap[currentDateStr] = val
 
 		// 继续使用插值法补充新预测日期的数据之间的值
-		err = HandleDataByLinearRegression([]*EdbInfoSearchData{
+		_, err = HandleDataByLinearRegression([]*EdbInfoSearchData{
 			lastDayData, tmpData,
 		}, handleDataMap)
 		if err != nil {
@@ -884,6 +881,8 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 	}
 	// 分母
 	decimalN := decimal.NewFromInt(int64(nValue))
+	// 需要减去的年份
+	subYear := year - dayList[0].Year()
 
 	//获取后面的预测数据
 	for k, currentDate := range dayList {
@@ -895,7 +894,7 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 		// 数据集合中的最后一个数据
 		tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
 		averageDateList = append(averageDateList, allDataList[tmpIndex].DataTime)
-		for tmpK := 2; tmpK <= nValue; tmpK++ {
+		for tmpK := 1; tmpK < nValue; tmpK++ {
 			tmpIndex2 := tmpIndex - tmpK //上N期的值
 			tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
 			tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
@@ -913,7 +912,7 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 			tmpHistoryValNum := 0
 			{
 				//前几年当日的日期
-				tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, 0)
+				tmpHistoryCurrentDate := currentDate.AddDate(subYear, 0, 0)
 				for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
 					tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
 					if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
@@ -938,7 +937,7 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 					return
 				}
 				//前几年上一期的日期
-				tmpHistoryLastDate := lastDay.AddDate(year-lastDay.Year(), 0, 0)
+				tmpHistoryLastDate := lastDay.AddDate(subYear, 0, 0)
 				for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
 					tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
 					if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
@@ -969,8 +968,10 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 			continue
 		}
 
+		// 计算最近N期平均值
+		tmpHistoryAverageVal := tmpHistoryDecimalVal.Div(decimalN)
 		// 计算最近N期同比值
-		tbVal := tmpAverageVal.Div(tmpHistoryDecimalVal)
+		tbVal := tmpAverageVal.Div(tmpHistoryAverageVal)
 
 		// 预测值结果 = 同比年份同期值(tmpHistoryCurrentVal的值)* 同比值(tbVal的值)
 		val, _ := decimal.NewFromFloat(tmpHistoryCurrentVal).Mul(tbVal).RoundCeil(4).Float64()
@@ -1373,3 +1374,402 @@ func getCalculateNhccData(secondDataList []*EdbInfoSearchData, ruleConf RuleLine
 	}
 	return
 }
+
+// GetChartPredictEdbInfoDataListByRuleNAnnualAverage 根据 N年均值 规则获取预测数据
+// ETA预测规则:N年均值:过去N年同期均值。过去N年可以连续或者不连续,指标数据均用线性插值补全为日度数据后计算;
+func GetChartPredictEdbInfoDataListByRuleNAnnualAverage(edbInfoId int, configValue string, dayList []time.Time, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
+	// 获取配置的年份列表
+	yearList, _, err := getYearListBySeasonConf(configValue)
+	if err != nil {
+		return
+	}
+
+	allDataList := make([]*EdbInfoSearchData, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+	newPredictEdbInfoData = predictEdbInfoData
+
+	// 插值法数据处理
+	handleDataMap := make(map[string]float64)
+	_, err = HandleDataByLinearRegression(allDataList, handleDataMap)
+	if err != nil {
+		return
+	}
+
+	index := len(allDataList)
+	//获取后面的预测数据
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+	for k, currentDate := range dayList {
+		// 如果遇到闰二月,如2.29,去掉该天数据
+		if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") {
+			continue
+		}
+		tmpK := len(allDataList) - 1     //上1期数据的下标
+		lastDayData := allDataList[tmpK] // 上1期的数据
+
+		tmpHistoryVal := decimal.NewFromFloat(0) //往期的差值总和
+		tmpHistoryValNum := 0                    // 往期差值计算的数量
+		for _, year := range yearList {
+			//前几年当日的日期
+			tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, 0)
+			if val, ok := handleDataMap[tmpHistoryCurrentDate.Format(utils.FormatDate)]; ok {
+				tmpHistoryVal = tmpHistoryVal.Add(decimal.NewFromFloat(val))
+				tmpHistoryValNum++
+			}
+		}
+
+		//计算的差值与选择的年份数量不一致,那么当前日期不计算
+		if tmpHistoryValNum != len(yearList) {
+			continue
+		}
+		val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).RoundCeil(4).Float64()
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		tmpData := &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 100000 + index + k,
+			//EdbInfoId:     edbInfoId,
+			DataTime: currentDateStr,
+			Value:    val,
+			//DataTimestamp: currentDate.UnixNano() / 1e6,
+		}
+		newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+		allDataList = append(allDataList, tmpData)
+		existMap[currentDateStr] = val
+
+		// 继续使用插值法补充新预测日期的数据之间的值
+		_, err = HandleDataByLinearRegression([]*EdbInfoSearchData{
+			lastDayData, tmpData,
+		}, handleDataMap)
+		if err != nil {
+			return
+		}
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val > maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// AnnualValueInversionConf 年度值倒推规则
+type AnnualValueInversionConf struct {
+	Value float64 `description:"年度值"`
+	Type  int     `description:"分配方式,1:均值法;2:同比法"`
+	Year  int     `description:"同比年份"`
+}
+
+// GetChartPredictEdbInfoDataListByRuleAnnualValueInversion 根据 年度值倒推 规则获取预测数据
+// ETA预测规则:年度值倒推:设定年度值,余额=年度值-年初至今累计值(算法参考累计值),进行余额分配,均值法分配时保证每期数值相等(日度/周度:剩余期数=剩余自然日历天数/今年指标最新日期自然日历天数*今年至今指标数据期数;旬度/月度/季度/半年度:剩余期数=全年期数(36\12\4\2)-今年至今自然日历期数),同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速)
+// 举例:
+// 指标A 日度 最新日期 2023-05-19 年初至今累计值100
+// 设置年度值1000
+// 则余额=1000-100=900
+// 均值法分配:剩余期数=226/139*120=195.11
+// 今年之后的每一期预测值=900/195.11=4.6128
+// 同比法分配:同比增速=900/同比年份5.19的余额
+// 预测值=同比年份5-20的值*(1+同比增速)
+func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, configValue string, dayList []time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
+	if frequency == "年度" {
+		err = errors.New("当前指标频度是年度,不允许配置年度值倒推")
+		return
+	}
+
+	// 获取配置
+	var annualValueInversionConf AnnualValueInversionConf
+	err = json.Unmarshal([]byte(configValue), &annualValueInversionConf)
+	if err != nil {
+		err = errors.New("年度值倒推配置信息异常:" + err.Error())
+		return
+	}
+
+	allDataList := make([]*EdbInfoSearchData, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(allDataList)
+
+	// 配置的年度值
+	yearValueConfig := annualValueInversionConf.Value
+
+	// 最新数据的日期
+	currDayTime, err := time.ParseInLocation(utils.FormatDate, allDataList[index-1].DataTime, time.Local)
+	if err != nil {
+		return
+	}
+	// 当前年的日期
+	lastDayTime := dayList[len(dayList)-1]
+	if currDayTime.Year() != lastDayTime.Year() {
+		err = errors.New("年度值倒推不支持跨年预测")
+		return
+	}
+
+	// 均值法
+	if annualValueInversionConf.Type == 1 {
+
+		// 当前年的期数
+		currYearN := 0
+		// 当前已经消耗的额度
+		var currYearVal float64
+
+		// 计算当前年的期数以及已经消耗的额度
+		{
+			if frequency != "周度" {
+				for _, v := range allDataList {
+					currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					// 只是计算今年的
+					if currTime.Year() != currDayTime.Year() {
+						continue
+					}
+					currYearN++
+					currYearVal = currYearVal + v.Value
+				}
+			} else {
+				tmpDataList := make([]*EdbInfoSearchData, 0)
+				// 上一期的数据
+				var lastData *EdbInfoSearchData
+				// 是否第一条数据
+				isFirst := true
+				for _, v := range allDataList {
+					currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					// 只是计算今年的
+					if currTime.Year() != currDayTime.Year() {
+						lastData = v
+						continue
+					}
+
+					if isFirst {
+						tmpDataList = append(tmpDataList, lastData)
+					}
+					isFirst = false
+					tmpDataList = append(tmpDataList, v)
+					currYearN++
+				}
+
+				// 需要插值法处理
+				tmpHandleDataMap := make(map[string]float64)
+				_, err = HandleDataByLinearRegression(tmpDataList, tmpHandleDataMap)
+				if err != nil {
+					return
+				}
+
+				for tmpDate, val := range tmpHandleDataMap {
+					tmpDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					if tmpDateTime.Year() != currDayTime.Year() {
+						continue
+					}
+					currYearVal = currYearVal + val
+				}
+
+				currYearVal = currYearVal / 7
+			}
+		}
+
+		var averageVal float64
+		switch frequency {
+		case "半年度":
+			averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(2 - currYearN))).Float64()
+		case "季度":
+			averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(4 - currYearN))).Float64()
+		case "月度":
+			averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(12 - currYearN))).Float64()
+		case "旬度":
+			averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(36 - currYearN))).Float64()
+		case "周度", "日度":
+			//剩余期数=剩余自然日历天数/今年指标最新日期自然日历天数*今年至今指标数据期数
+
+			// 当前年的第一天
+			yearFirstDay := time.Date(currDayTime.Year(), 1, 1, 0, 0, 0, 0, time.Local)
+			subDay := utils.GetTimeSubDay(yearFirstDay, currDayTime) + 1
+
+			// 当前年的最后一天
+			yearLastDay := time.Date(currDayTime.Year(), 12, 31, 0, 0, 0, 0, time.Local)
+			subDay2 := utils.GetTimeSubDay(yearFirstDay, yearLastDay) + 1
+
+			// 剩余期数
+			surplusN := decimal.NewFromInt(int64(subDay2 - subDay)).Div(decimal.NewFromInt(int64(subDay))).Mul(decimal.NewFromInt(int64(currYearN)))
+			// 剩余余额
+			balance := decimal.NewFromFloat(annualValueInversionConf.Value).Sub(decimal.NewFromFloat(currYearVal))
+			averageVal, _ = balance.Div(surplusN).Round(4).Float64()
+
+		}
+
+		// 保留四位小数
+		averageVal, _ = decimal.NewFromFloat(averageVal).Round(4).Float64()
+
+		for k, currentDate := range dayList {
+			currentDateStr := currentDate.Format(utils.FormatDate)
+			tmpData := &EdbInfoSearchData{
+				EdbDataId: edbInfoId + 100000 + index + k,
+				//EdbInfoId:     edbInfoId,
+				DataTime: currentDateStr,
+				Value:    averageVal,
+				//DataTimestamp: currentDate.UnixNano() / 1e6,
+			}
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+			allDataList = append(allDataList, tmpData)
+			existMap[currentDateStr] = averageVal
+		}
+
+		// 最大最小值
+		if averageVal < minValue {
+			minValue = averageVal
+		}
+		if averageVal > maxValue {
+			maxValue = averageVal
+		}
+
+		return
+	}
+
+	// 同比法分配
+	// 同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
+	// 同比法分配:同比增速=900/同比年份5.19的余额
+
+	// 每年截止到当前日期的累计值
+	dateTotalMap := make(map[time.Time]float64)
+	// 每年的累计值(计算使用)
+	yearTotalMap := make(map[int]float64)
+	for _, v := range allDataList {
+		currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		yearVal := yearTotalMap[currTime.Year()]
+		yearVal = yearVal + v.Value
+		yearTotalMap[currTime.Year()] = yearVal
+		dateTotalMap[currTime] = yearVal
+	}
+
+	//(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
+	for k, currentDate := range dayList {
+		currYearBalance := yearValueConfig - yearTotalMap[currentDate.Year()] // 当年的余额
+
+		// 上一期的日期
+		prevDateStr := allDataList[len(allDataList)-1].DataTime
+		prevDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, prevDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//同比年份相应日期
+		lastYear := annualValueInversionConf.Year + (currentDate.Year() - currDayTime.Year())
+
+		// 前N年的上一期时间;前N年的当期时间;
+		var lastPrevDateTime, lastDateTime time.Time
+
+		switch frequency {
+		case "半年度", "季度":
+			lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
+			lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
+		case "月度":
+			lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
+			lastPrevDateTime = time.Date(lastYear, prevDateTime.Month()+1, 1, 0, 0, 0, 0, prevDateTime.Location()).AddDate(0, 0, -1)
+		case "旬度":
+			if prevDateTime.Day() == 10 || prevDateTime.Day() == 20 {
+				lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
+				lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
+			} else {
+				lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
+				lastPrevDateTime = time.Date(lastYear, prevDateTime.Month()+1, 1, 0, 0, 0, 0, prevDateTime.Location()).AddDate(0, 0, -1)
+			}
+		case "周度", "日度":
+			lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
+			lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
+		}
+
+		// 同比年份相应日期的累计值
+		var dateTotal float64
+
+		dateTotal, ok := dateTotalMap[lastPrevDateTime]
+		if !ok { //如果没有找到这个日期,那么就往前面找,一直到找到这个累计值,或者找完这一年
+			yearFirstDayTime := time.Date(lastPrevDateTime.Year(), 1, 1, 0, 0, 0, 0, lastDateTime.Location())
+			for tmpDateTime := lastPrevDateTime.AddDate(0, 0, -1); tmpDateTime.After(yearFirstDayTime) || tmpDateTime.Equal(yearFirstDayTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
+				dateTotal, ok = dateTotalMap[tmpDateTime]
+				if ok {
+					break
+				}
+			}
+		}
+
+		//同比年份相应的上一期日期的余额
+		lastYearDateBalance := yearTotalMap[lastPrevDateTime.Year()] - dateTotal
+
+		// 同比增速=当年余额/同比年份上一期日期的余额
+		tbVal := decimal.NewFromFloat(currYearBalance).Div(decimal.NewFromFloat(lastYearDateBalance))
+
+		// 获取同比年份同期值,获取失败的话,就不处理
+		if lastDateVal, ok := existMap[lastDateTime.Format(utils.FormatDate)]; ok {
+			//预测值 = 同比年份同期值*同比增速
+			tmpVal, _ := decimal.NewFromFloat(lastDateVal).Mul(tbVal).Round(4).Float64()
+			currentDateStr := currentDate.Format(utils.FormatDate)
+			tmpData := &EdbInfoSearchData{
+				EdbDataId: edbInfoId + 100000 + index + k,
+				//EdbInfoId:     edbInfoId,
+				DataTime: currentDateStr,
+				Value:    tmpVal,
+				//DataTimestamp: currentDate.UnixNano() / 1e6,
+			}
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+			allDataList = append(allDataList, tmpData)
+			existMap[currentDateStr] = tmpVal
+
+			yearVal := yearTotalMap[currentDate.Year()]
+			yearVal = yearVal + tmpVal
+			yearTotalMap[currentDate.Year()] = yearVal
+			dateTotalMap[currentDate] = yearVal
+
+			// 最大最小值
+			if tmpVal < minValue {
+				minValue = tmpVal
+			}
+			if tmpVal > maxValue {
+				maxValue = tmpVal
+			}
+		}
+	}
+
+	return
+}
+
+// getYearListBySeasonConf 根据配置获取年份列表
+func getYearListBySeasonConf(configValue string) (yearList []int, seasonConf SeasonConf, err error) {
+	tmpErr := json.Unmarshal([]byte(configValue), &seasonConf)
+	if tmpErr != nil {
+		err = errors.New("年份配置信息异常:" + tmpErr.Error())
+		return
+	}
+	//选择方式,1:连续N年;2:指定年份
+	if seasonConf.YearType == 1 {
+		if seasonConf.NValue < 1 {
+			err = errors.New("连续N年不允许小于1")
+			return
+		}
+
+		currYear := time.Now().Year()
+		for i := 0; i < seasonConf.NValue; i++ {
+			yearList = append(yearList, currYear-i-1)
+		}
+	} else {
+		yearList = seasonConf.YearList
+	}
+
+	return
+}

+ 377 - 0
models/supply_analysis/base_from_stock_plant_data.go

@@ -0,0 +1,377 @@
+package supply_analysis
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"time"
+)
+
+// BaseFromStockPlantData base_from_stock_plant_data 存量装置的指标的计算数据
+type BaseFromStockPlantData struct {
+	StockPlantDataId int       `orm:"column(stock_plant_data_id);pk"`
+	VarietyEdbId     int       `description:"指标id"`
+	DataTime         time.Time `description:"数据日期"`
+	Value            float64   `description:"数据值"`
+	ModifyTime       time.Time `description:"修改时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	DataTimestamp    int64     `description:"数据日期时间戳"`
+}
+
+// CalculateVarietyReq 重新计算请求
+type CalculateVarietyReq struct {
+	VarietyId int    `description:"品种id"`
+	AdminId   int    `description:"操作人id"`
+	AdminName string `description:"操作人名称"`
+}
+
+func Calculate(varietyId int, sysUserId int, sysUserName string) (err error) {
+	varietyInfo, err := GetVarietyById(varietyId)
+	if err != nil {
+		return
+	}
+
+	// 获取所有的装置
+	plantList, err := GetAllVarietyPlantByVarietyId(varietyId)
+	if err != nil {
+		return
+	}
+
+	// 获取所有的指标
+	varietyEdbInfoList, err := GetAllVarietyEdbInfoByVarietyId(varietyId)
+	if err != nil {
+		return
+	}
+
+	// 产生的数据的结束日期
+	var lastDate time.Time
+	{
+		currDate := time.Now()
+		currHour := currDate.Hour()
+		if currHour < 22 {
+			lastDate, _ = time.ParseInLocation(utils.FormatDate, currDate.Format(utils.FormatDate), time.Local)
+		} else {
+			lastDate, _ = time.ParseInLocation(utils.FormatDate, currDate.AddDate(0, 0, 1).Format(utils.FormatDate), time.Local)
+		}
+		lastDate = lastDate.AddDate(0, 0, varietyInfo.ProductionDay) //业务需要计算的数据日期往当前日期后面加上一年
+	}
+
+	// 维修数据的开始日期和结束日期
+	var startDate time.Time
+	//所有设备在该日期的减产数
+	maintenanceDataMap := make(map[time.Time][]float64)
+
+	for _, plantInfo := range plantList {
+		if plantInfo.IsStop == 1 { // 如果是停产了,那么结束日期就是到 产生的数据的结束日期
+			plantInfo.ResumptionDate = lastDate
+		}
+		// 数据不全
+		if plantInfo.MaintenanceDate.IsZero() || plantInfo.ResumptionDate.IsZero() {
+			continue
+		}
+		// 日均产量减量为0,说明数据不全,不参与计算
+		if plantInfo.AverageDailyCapacityReduction == 0 {
+			continue
+		}
+		// 开始检修日期
+		if startDate.IsZero() || plantInfo.MaintenanceDate.Before(startDate) {
+			startDate = plantInfo.MaintenanceDate
+		}
+
+		// 结束检修日期
+		resumptionDate := plantInfo.ResumptionDate
+
+		for tmpDate := plantInfo.MaintenanceDate; !tmpDate.After(resumptionDate); tmpDate = tmpDate.AddDate(0, 0, 1) {
+			dataList, ok := maintenanceDataMap[tmpDate]
+			if !ok {
+				dataList = make([]float64, 0)
+			}
+			dataList = append(dataList, plantInfo.AverageDailyCapacityReduction)
+			maintenanceDataMap[tmpDate] = dataList
+		}
+	}
+
+	// 一个有用的装置都没有,那么就不需要计算了
+	if len(maintenanceDataMap) <= 0 {
+		return
+	}
+
+	// 计算出数据
+	dataMap := make(map[time.Time]float64)
+	for tmpDate := startDate; !tmpDate.After(lastDate); tmpDate = tmpDate.AddDate(0, 0, 1) {
+		var calculateVal float64
+		tmpDataList, ok := maintenanceDataMap[tmpDate]
+		// 如果没有维修数据,那么退出当前循环,进入下一个循环
+		if !ok {
+			calculateVal = 0
+		} else {
+			for _, tmpVal := range tmpDataList {
+				calculateVal += tmpVal
+			}
+		}
+
+		dataMap[tmpDate] = calculateVal
+	}
+
+	to, err := orm.NewOrm().Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			fmt.Println("Calculate,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 有数据的话,那就参与计算
+	if len(dataMap) > 0 {
+		for _, varietyEdbInfo := range varietyEdbInfoList {
+			err = calculateEdb(to, varietyEdbInfo, dataMap, startDate, lastDate)
+			if err != nil {
+				return
+			}
+		}
+	}
+
+	// 更新最后分析的人员信息
+	varietyInfo.LastUpdateSysUserId = sysUserId
+	varietyInfo.LastUpdateSysUserRealName = sysUserName
+	varietyInfo.ModifyTime = time.Now()
+	_, err = to.Update(varietyInfo, "LastUpdateSysUserId", "LastUpdateSysUserRealName", "ModifyTime")
+
+	return
+}
+
+func calculateEdb(to orm.TxOrmer, varietyEdbInfo *VarietyEdbInfo, dataMap map[time.Time]float64, startDate, lastDate time.Time) (err error) {
+	sql := `SELECT * FROM base_from_stock_plant_data WHERE variety_edb_id = ? `
+	var list []*BaseFromStockPlantData
+	_, err = to.Raw(sql, varietyEdbInfo.VarietyEdbId).QueryRows(&list)
+	if err != nil {
+		return
+	}
+
+	addList := make([]*BaseFromStockPlantData, 0)
+	deleteIdMap := make(map[int]int, 0)
+
+	existData := make(map[time.Time]*BaseFromStockPlantData)
+	for _, v := range list {
+		existData[v.DataTime] = v
+		deleteIdMap[v.StockPlantDataId] = v.StockPlantDataId
+	}
+
+	endDate := lastDate     // 实际数据最后结束的日期
+	var latestValue float64 // 最新值
+
+	// 影响周度产量:当前日期往前(更早日期)统计7天的影响日度产量(减产量)合计 ---- 七个数据和
+	// 周度产量变动:往前(更早日期)推第8天的日期对应的影响周度产量值 - 当前日期对应的影响周度产量值
+	// 影响月度产量:日期序列为最早检修日期所在月份开始 至 当前日期+365天的日期所在月份,每月最后一天当月的所有影响日度产量(减产量)合计
+	// 月度产量变动:上期影响月度产量值 - 当期影响月度产量值
+	switch varietyEdbInfo.Source {
+	//1:影响周度产量;2:周度产量变动;3:影响月度产量;4:月度产量变动
+	case 1, 2:
+		// 周度产量数据map
+		weekDataMap := make(map[time.Time]float64)
+
+		for currDate := startDate; !currDate.After(lastDate); currDate = currDate.AddDate(0, 0, 1) {
+			var weekVal, currVal float64
+			for i := 0; i <= 6; i++ {
+				tmpDate := currDate.AddDate(0, 0, -i)
+				tmpData, ok := dataMap[tmpDate]
+				if !ok {
+					continue
+				}
+				weekVal = weekVal + tmpData
+			}
+			weekDataMap[currDate] = weekVal // 周度产量数据
+
+			if varietyEdbInfo.Source == 1 {
+				currVal = weekVal
+			} else {
+				preVal, ok := weekDataMap[currDate.AddDate(0, 0, -7)]
+				if !ok {
+					preVal = 0
+				}
+				currVal = preVal - weekVal
+			}
+
+			currVal, _ = decimal.NewFromFloat(currVal).Round(4).Float64()
+			latestValue = currVal
+			timestamp := currDate.UnixNano() / 1e6
+			existItem, ok := existData[currDate]
+			if !ok {
+				addList = append(addList, &BaseFromStockPlantData{
+					//StockPlantDataId: 0,
+					VarietyEdbId:  varietyEdbInfo.VarietyEdbId,
+					DataTime:      currDate,
+					Value:         currVal,
+					ModifyTime:    time.Now(),
+					CreateTime:    time.Now(),
+					DataTimestamp: timestamp,
+				})
+			} else {
+				if existItem.Value != currVal {
+					existItem.Value = currVal
+					existItem.ModifyTime = time.Now()
+					_, err = to.Update(existItem, "Value", "ModifyTime")
+					if err != nil {
+						return
+					}
+				}
+
+				delete(deleteIdMap, existItem.StockPlantDataId)
+			}
+		}
+	case 3, 4:
+		tmpDataList := make([]float64, 0)        // 每天的值
+		preSumValDeci := decimal.NewFromFloat(0) // 上一期的值
+
+		// 期间月度数据
+		for tmpDate := startDate; !tmpDate.After(lastDate); tmpDate = tmpDate.AddDate(0, 0, 1) {
+			tmpData, ok := dataMap[tmpDate]
+			if !ok {
+				continue
+			}
+			tmpDataList = append(tmpDataList, tmpData)
+
+			if tmpDate.AddDate(0, 0, 1).Day() != 1 {
+				// 如果不是每个月的最后一日,那么就退出当前循环
+				continue
+			}
+
+			timestamp := tmpDate.UnixNano() / 1e6
+
+			// 日期期间汇总数据
+			var sumVal float64
+			for _, tmpVal := range tmpDataList {
+				sumVal += tmpVal
+			}
+			sumValDeci := decimal.NewFromFloat(sumVal)
+
+			var currVal float64
+			if varietyEdbInfo.Source == 3 { // 月度影响产量
+				currVal, _ = sumValDeci.Round(4).Float64()
+			} else { // 如果是月度产量变动的话
+				currVal, _ = (preSumValDeci.Sub(decimal.NewFromFloat(sumVal))).Round(4).Float64()
+			}
+			latestValue = currVal
+
+			preSumValDeci = sumValDeci       // 赋值给上一个周期的截止值
+			tmpDataList = make([]float64, 0) //重置值列表,便于下一个周期计算
+
+			existItem, ok := existData[tmpDate]
+			if !ok {
+				addList = append(addList, &BaseFromStockPlantData{
+					//StockPlantDataId: 0,
+					VarietyEdbId:  varietyEdbInfo.VarietyEdbId,
+					DataTime:      tmpDate,
+					Value:         currVal,
+					ModifyTime:    time.Now(),
+					CreateTime:    time.Now(),
+					DataTimestamp: timestamp,
+				})
+			} else {
+				if existItem.Value != currVal {
+					existItem.Value = currVal
+					existItem.ModifyTime = time.Now()
+					_, err = to.Update(existItem, "Value", "ModifyTime")
+					if err != nil {
+						return
+					}
+				}
+
+				delete(deleteIdMap, existItem.StockPlantDataId)
+			}
+		}
+
+		// 最后一月,不满一月时的计算
+		currMonthEndDay := time.Date(lastDate.Year(), lastDate.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, -1)
+		if !lastDate.Equal(currMonthEndDay) {
+			// 如果最后一天不是当月的最后一天,那么需要把日期调整到当月的最后一天
+			tmpDate := currMonthEndDay
+			endDate = tmpDate //数据实际结束的日期
+
+			existItem, ok := existData[tmpDate]
+
+			timestamp := tmpDate.UnixNano() / 1e6
+			// 日期期间汇总数据
+			var sumVal float64
+			for _, tmpVal := range tmpDataList {
+				sumVal += tmpVal
+			}
+			sumValDeci := decimal.NewFromFloat(sumVal)
+
+			var currVal float64
+			if varietyEdbInfo.Source == 3 { // 月度影响产量
+				currVal, _ = sumValDeci.Round(4).Float64()
+			} else { // 如果是月度产量变动的话
+				currVal, _ = (preSumValDeci.Sub(decimal.NewFromFloat(sumVal))).Round(4).Float64()
+			}
+			currVal, _ = decimal.NewFromFloat(currVal).Round(4).Float64()
+			latestValue = currVal
+
+			if !ok {
+				addList = append(addList, &BaseFromStockPlantData{
+					//StockPlantDataId: 0,
+					VarietyEdbId:  varietyEdbInfo.VarietyEdbId,
+					DataTime:      tmpDate,
+					Value:         currVal,
+					ModifyTime:    time.Now(),
+					CreateTime:    time.Now(),
+					DataTimestamp: timestamp,
+				})
+			} else {
+				if existItem.Value != currVal {
+					existItem.Value = currVal
+					existItem.ModifyTime = time.Now()
+					_, err = to.Update(existItem, "Value", "ModifyTime")
+					if err != nil {
+						return
+					}
+				}
+
+				delete(deleteIdMap, existItem.StockPlantDataId)
+			}
+		}
+
+	default:
+		err = errors.New("错误的指标类型")
+		return
+	}
+
+	// 需要添加的指标数据
+	lenAddList := len(addList)
+	if lenAddList > 0 {
+		_, err = to.InsertMulti(lenAddList, addList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 删除不存在的数据
+	lenDeleteIdMap := len(deleteIdMap)
+	if len(deleteIdMap) > 0 {
+		deleteIdList := make([]int, 0)
+		for _, v := range deleteIdMap {
+			deleteIdList = append(deleteIdList, v)
+		}
+		sql = ` DELETE FROM base_from_stock_plant_data WHERE stock_plant_data_id in (` + utils.GetOrmInReplace(lenDeleteIdMap) + `) `
+		_, err = to.Raw(sql, deleteIdList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	varietyEdbInfo.StartDate = startDate
+	varietyEdbInfo.EndDate = endDate
+	varietyEdbInfo.LatestValue = latestValue
+	varietyEdbInfo.ModifyTime = time.Now()
+	_, err = to.Update(varietyEdbInfo, "StartDate", "EndDate", "LatestValue", "ModifyTime")
+
+	return
+}

+ 215 - 0
models/supply_analysis/variety.go

@@ -0,0 +1,215 @@
+package supply_analysis
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// Variety variety 品种表
+type Variety struct {
+	VarietyId                 int       `orm:"column(variety_id);pk"`
+	VarietyName               string    `description:"品种名称"`
+	LastUpdateSysUserId       int       `description:"最后更新人id"`
+	LastUpdateSysUserRealName string    `description:"最后更新人名称"`
+	ProductionDay             int       `description:"生产天数"`
+	SysUserId                 int       `description:"创建人id"`
+	SysUserRealName           string    `description:"创建人姓名"`
+	ModifyTime                time.Time `description:"修改时间"`
+	CreateTime                time.Time `description:"创建时间"`
+}
+
+// GetVarietyById 根据品种id获取品种详情
+func GetVarietyById(id int) (item *Variety, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety WHERE variety_id = ?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// GetVarietyByName 根据品种名称获取品种详情
+func GetVarietyByName(name string) (item *Variety, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety WHERE variety_name = ?`
+	err = o.Raw(sql, name).QueryRow(&item)
+	return
+}
+
+// AddVariety 添加品种
+func AddVariety(item *Variety) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// CreateVariety 添加品种
+func CreateVariety(item *Variety, adminIdList []int) (err error) {
+	to, err := orm.NewOrm().Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	lastId, err := to.Insert(item)
+	if err != nil {
+		return
+	}
+
+	item.VarietyId = int(lastId)
+
+	varietyAdminPermissionList := make([]*VarietyAdminPermission, 0)
+	for _, adminId := range adminIdList {
+		varietyAdminPermissionList = append(varietyAdminPermissionList, &VarietyAdminPermission{
+			//Id:              0,
+			VarietyId:  item.VarietyId,
+			SysUserId:  adminId,
+			CreateTime: time.Now(),
+		})
+	}
+	if len(varietyAdminPermissionList) > 0 {
+		_, err = to.InsertMulti(len(varietyAdminPermissionList), varietyAdminPermissionList)
+	}
+
+	return
+}
+
+// EditVariety 编辑品种
+func EditVariety(item *Variety, adminIdList []int) (err error) {
+	to, err := orm.NewOrm().Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	_, err = to.Update(item, "VarietyName", "LastUpdateSysUserId", "LastUpdateSysUserRealName", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	// 删除历史的权限配置
+	sql := `DELETE FROM variety_admin_permission where variety_id = ? `
+	_, err = to.Raw(sql, item.VarietyId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 添加新的权限配置
+	varietyAdminPermissionList := make([]*VarietyAdminPermission, 0)
+	for _, adminId := range adminIdList {
+		varietyAdminPermissionList = append(varietyAdminPermissionList, &VarietyAdminPermission{
+			//Id:              0,
+			VarietyId:  item.VarietyId,
+			SysUserId:  adminId,
+			CreateTime: time.Now(),
+		})
+	}
+	if len(varietyAdminPermissionList) > 0 {
+		_, err = to.InsertMulti(len(varietyAdminPermissionList), varietyAdminPermissionList)
+	}
+
+	return
+}
+
+// VarietyItem 列表页返回的数据结构
+type VarietyItem struct {
+	VarietyId                 int           `orm:"column(variety_id);pk"`
+	VarietyName               string        `description:"品种名称"`
+	LastUpdateSysUserId       int           `description:"最后更新人id"`
+	LastUpdateSysUserRealName string        `description:"最后更新人名称"`
+	PermissionUserId          string        `description:"有操作权限的用户id" json:"-"`
+	ModifyTime                string        `description:"修改时间"`
+	CreateTime                string        `description:"创建时间"`
+	Button                    VarietyButton `description:"操作按钮权限"`
+}
+
+type VarietyButton struct {
+	Edit    bool `description:"操作权限"`
+	Delete  bool `description:"删除权限"`
+	Analyse bool `description:"分析权限"`
+}
+
+// GetListBySuperAdminPage 不区分是否有分析权限的获取分页数据
+func (item Variety) GetListBySuperAdminPage(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*VarietyItem, err error) {
+	o := orm.NewOrm()
+	baseSql := ` FROM ( SELECT a.*, GROUP_CONCAT(DISTINCT b.sys_user_id ORDER BY b.sys_user_id ASC SEPARATOR ',') AS permission_user_id FROM variety a 
+				LEFT JOIN variety_admin_permission b on a.variety_id=b.variety_id 
+				LEFT JOIN variety_edb_info c on a.variety_id=c.variety_id WHERE 1=1 `
+	if condition != "" {
+		baseSql += condition
+	}
+	baseSql += ` GROUP BY a.variety_id ) d `
+	// 数据总数
+	totalSql := `SELECT COUNT(1) total ` + baseSql
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	// 列表页数据
+	listSql := `SELECT * ` + baseSql + ` ORDER BY modify_time DESC,variety_id DESC LIMIT ?,?`
+	_, err = o.Raw(listSql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetListByPage 获取分页数据
+func (item Variety) GetListByPage(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*VarietyItem, err error) {
+	o := orm.NewOrm()
+	baseSql := ` FROM ( SELECT a.*, GROUP_CONCAT(DISTINCT b.sys_user_id ORDER BY b.sys_user_id ASC SEPARATOR ',') AS permission_user_id FROM variety a 
+				JOIN variety_admin_permission b on a.variety_id=b.variety_id 
+				JOIN variety_edb_info c on a.variety_id=c.variety_id WHERE 1=1 `
+	if condition != "" {
+		baseSql += condition
+	}
+	baseSql += ` GROUP BY a.variety_id ) d `
+	// 数据总数
+	totalSql := `SELECT COUNT(1) total ` + baseSql
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	// 列表页数据
+	listSql := `SELECT * ` + baseSql + ` ORDER BY modify_time DESC,variety_id DESC LIMIT ?,?`
+	_, err = o.Raw(listSql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// Update 更基础信息
+func (variety *Variety) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(variety, cols...)
+	return
+}
+
+// GetVarietyMaxSort 获取图表分类下最大的排序数
+func GetVarietyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM future_good_chart_classify WHERE parent_id=? AND is_delete=0 `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type VarietyView struct {
+	VarietyId   int    `orm:"column(future_good_chart_classify_id);pk"`
+	VarietyName string `description:"分类名称"`
+	ParentId    int    `description:"父级id"`
+}
+
+func GetVarietyViewById(classifyId int) (item *VarietyView, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM future_good_chart_classify WHERE future_good_chart_classify_id=? AND is_delete=0 `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}

+ 22 - 0
models/supply_analysis/variety_admin_permission.go

@@ -0,0 +1,22 @@
+package supply_analysis
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// VarietyAdminPermission variety_admin_permission 品种权限表
+type VarietyAdminPermission struct {
+	Id         int       `orm:"column(id);pk"`
+	VarietyId  int       `description:"品种id"`
+	SysUserId  int       `description:"用户id"`
+	CreateTime time.Time `description:"添加时间"`
+}
+
+// GetVarietyPermissionByVarietyIdAndUserId 根据品种id和用户id获取权限记录表
+func GetVarietyPermissionByVarietyIdAndUserId(varietyId, sysUserId int) (item *VarietyAdminPermission, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_admin_permission a  WHERE variety_id = ? AND sys_user_id = ? ORDER BY id desc `
+	err = o.Raw(sql, varietyId, sysUserId).QueryRow(&item)
+	return
+}

+ 101 - 0
models/supply_analysis/variety_edb_info.go

@@ -0,0 +1,101 @@
+package supply_analysis
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// VarietyEdbInfo variety_edb_info 品种指标表
+type VarietyEdbInfo struct {
+	VarietyEdbId int       `orm:"column(variety_edb_id);pk"`
+	VarietyId    int       `description:"品种id"`
+	EdbName      string    `description:"品种指标名称"`
+	Frequency    string    `description:"频度"`
+	StartDate    time.Time `description:"开始日期"`
+	EndDate      time.Time `description:"结束日期"`
+	LatestValue  float64   `description:"最新值"`
+	EdbInfoId    int       `description:"指标id"`
+	EdbCode      string    `description:"品种编码"`
+	Source       int       `description:"来源,1:影响周度产量;2:周度产量变动;3:影响月度产量;4:月度产量变动"`
+	ModifyTime   time.Time `description:"更新时间"`
+	CreateTime   time.Time `description:"添加时间"`
+}
+
+// GetAllVarietyEdbInfoByVarietyId 根据品种id获取所有的指标
+func GetAllVarietyEdbInfoByVarietyId(varietyId int) (items []*VarietyEdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_edb_info AS a WHERE a.variety_id = ? ORDER BY a.variety_edb_id ASC `
+	_, err = o.Raw(sql, varietyId).QueryRows(&items)
+	return
+}
+
+// GetVarietyEdbInfoByEdbInfoId 根据ETA指标库中的指标id获取指标
+func GetVarietyEdbInfoByEdbInfoId(varietyId int) (item *VarietyEdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_edb_info AS a WHERE a.edb_info_id = ? ORDER BY a.variety_edb_id ASC `
+	err = o.Raw(sql, varietyId).QueryRow(&item)
+
+	return
+}
+
+// BatchCreateVarietyEdbInfo 批量插入
+func BatchCreateVarietyEdbInfo(list []*VarietyEdbInfo) (err error) {
+	num := len(list)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(num, list)
+
+	return
+}
+
+// GetCountVarietyEdbByVarietyId 根据品种id获取添加到已经生成的指标数量(不管是否加入到ETA指标库)
+func GetCountVarietyEdbByVarietyId(varietyId int) (total int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total FROM variety_edb_info AS a WHERE a.variety_id = ?`
+	err = o.Raw(sql, varietyId).QueryRow(&total)
+	return
+}
+
+// GetCountVarietyEdbInfoByVarietyId 根据品种id获取添加到ETA指标库的指标数量
+func GetCountVarietyEdbInfoByVarietyId(varietyId int) (total int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total FROM variety_edb_info AS a
+			JOIN edb_info b on a.edb_info_id=b.edb_info_id
+			WHERE a.variety_id = ? and a.edb_info_id >0`
+	err = o.Raw(sql, varietyId).QueryRow(&total)
+	return
+}
+
+// VarietyEdbInfoItem 品种指标表(展示使用)
+type VarietyEdbInfoItem struct {
+	VarietyEdbId int                  `orm:"column(variety_edb_id);pk"`
+	VarietyId    int                  `description:"品种id"`
+	EdbName      string               `description:"品种指标名称"`
+	Frequency    string               `description:"频度"`
+	StartDate    string               `description:"开始日期"`
+	EndDate      string               `description:"结束日期"`
+	EdbInfoId    int                  `description:"指标id" json:"-"`
+	EdbCode      string               `description:"品种编码"`
+	ClassifyName string               `description:"分类名称"`
+	Source       int                  `description:"来源,1:影响周度产量;2:周度产量变动;3:影响月度产量;4:月度产量变动" json:"-"`
+	ModifyTime   string               `description:"最近一次更新时间"`
+	CreateTime   string               `description:"添加时间"`
+	Button       VarietyEdbInfoButton `description:"操作按钮权限"`
+}
+
+type VarietyEdbInfoButton struct {
+	Copy bool `description:"复制数据权限"`
+	Show bool `description:"查看数据权限"`
+	Add  bool `description:"添加到指标库"`
+}
+
+// GetAllVarietyEdbInfoItemByVarietyId 根据品种id获取所有的指标
+func GetAllVarietyEdbInfoItemByVarietyId(varietyId int) (items []*VarietyEdbInfoItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_edb_info AS a WHERE a.variety_id = ? ORDER BY a.variety_edb_id ASC `
+	_, err = o.Raw(sql, varietyId).QueryRows(&items)
+	return
+}

+ 113 - 0
models/supply_analysis/variety_plant.go

@@ -0,0 +1,113 @@
+package supply_analysis
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// VarietyPlant variety_plant 品种装置表
+type VarietyPlant struct {
+	VarietyPlantId                int       `orm:"column(variety_plant_id);pk"`
+	VarietyId                     int       `description:"品种id"`
+	Province                      string    `description:"所属省份"`
+	City                          string    `description:"所属城市"`
+	FactoryName                   string    `description:"工厂名称"`
+	PlantName                     string    `description:"装置/产线名称"`
+	MaintenanceDate               time.Time `description:"检修日期"`
+	ResumptionDate                time.Time `description:"复产日期"`
+	AnnualCapacity                float64   `description:"年产能"`
+	Coefficient                   float64   `description:"降负系数"`
+	AverageDailyCapacityReduction float64   `description:"日均产量减量"`
+	IsStop                        int       `description:"是否停产,0:未停产;1:停产;默认未停产"`
+	Sort                          int       `description:"排序字段,越小越靠前"`
+	SysUserId                     int       `description:"添加人id"`
+	SysUserRealName               string    `description:"添加人真实姓名"`
+	ModifyTime                    time.Time `description:"最近一次更新时间"`
+	CreateTime                    time.Time `description:"添加时间"`
+}
+
+// GetAllVarietyPlantByVarietyId 根据品种id获取所有的装置
+func GetAllVarietyPlantByVarietyId(varietyId int) (items []*VarietyPlant, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_plant a  WHERE variety_id = ? ORDER BY variety_plant_id desc `
+	_, err = o.Raw(sql, varietyId).QueryRows(&items)
+	return
+}
+
+// AddVarietyPlant 添加future_good_chart分类
+func AddVarietyPlant(item *VarietyPlant) (err error) {
+	o := orm.NewOrm()
+	lastId, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.VarietyPlantId = int(lastId)
+
+	return
+}
+
+// Update 更新基础信息
+func (varietyPlant *VarietyPlant) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(varietyPlant, cols...)
+	return
+}
+
+// GetVarietyPlantById 根据品种装置id获取品种装置详情
+func GetVarietyPlantById(id int) (item *VarietyPlant, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_plant WHERE variety_plant_id = ?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// VarietyPlantItem 品种装置数据(展示使用)
+type VarietyPlantItem struct {
+	VarietyPlantId                int                `orm:"column(variety_plant_id);pk"`
+	VarietyId                     int                `description:"品种id"`
+	Province                      string             `description:"所属省份"`
+	City                          string             `description:"所属城市"`
+	FactoryName                   string             `description:"工厂名称"`
+	PlantName                     string             `description:"装置/产线名称"`
+	MaintenanceDate               string             `description:"检修日期"`
+	ResumptionDate                string             `description:"复产日期"`
+	AnnualCapacity                float64            `description:"年产能"`
+	Coefficient                   float64            `description:"降负系数"`
+	AverageDailyCapacityReduction float64            `description:"日均产量减量"`
+	IsStop                        int                `description:"是否停产,0:未停产;1:停产;默认未停产"`
+	Sort                          int                `description:"排序字段,越小越靠前"`
+	SysUserId                     int                `description:"添加人id"`
+	SysUserRealName               string             `description:"添加人真实姓名"`
+	ModifyTime                    string             `description:"最近一次更新时间"`
+	CreateTime                    string             `description:"添加时间"`
+	Button                        VarietyPlantButton `description:"操作按钮权限"`
+}
+
+type VarietyPlantButton struct {
+	Edit   bool `description:"操作权限"`
+	Delete bool `description:"删除权限"`
+}
+
+// GetAllVarietyPlantItemByVarietyId 根据品种id获取所有的装置
+func GetAllVarietyPlantItemByVarietyId(varietyId int) (items []*VarietyPlantItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM variety_plant a  WHERE variety_id = ? ORDER BY variety_plant_id desc `
+	_, err = o.Raw(sql, varietyId).QueryRows(&items)
+	return
+}
+
+// GetCountVarietyPlantByVarietyId 根据品种id获取所有的装置数量
+func GetCountVarietyPlantByVarietyId(varietyId int) (total int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total FROM variety_plant a  WHERE variety_id = ? `
+	err = o.Raw(sql, varietyId).QueryRow(&total)
+	return
+}
+
+// DeleteVarietyPlantById 根据装置id删除
+func DeleteVarietyPlantById(id int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM variety_plant WHERE variety_plant_id = ?`
+	_, err = o.Raw(sql, id).Exec()
+	return
+}

+ 90 - 0
routers/commentsRouter.go

@@ -25,6 +25,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers/future_good:FutureGoodEdbInfoController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers/future_good:FutureGoodEdbInfoController"],
+        beego.ControllerComments{
+            Method: "RefreshRelation",
+            Router: `/relation/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:BaiinfoController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:BaiinfoController"],
         beego.ControllerComments{
             Method: "Add",
@@ -187,6 +196,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:FuBaoController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:FuBaoController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:FuBaoController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:FuBaoController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:GieController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:GieController"],
         beego.ControllerComments{
             Method: "Add",
@@ -286,6 +313,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"],
+        beego.ControllerComments{
+            Method: "HandleMysteelIndex",
+            Router: `/handle/mysteel/index`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"],
+        beego.ControllerComments{
+            Method: "QueryRefresh",
+            Router: `/query/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:MySteelChemicalController"],
         beego.ControllerComments{
             Method: "Refresh",
@@ -313,6 +358,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:NationalStatisticsController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:NationalStatisticsController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:NationalStatisticsController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:NationalStatisticsController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PbController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PbController"],
         beego.ControllerComments{
             Method: "Add",
@@ -430,6 +493,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:SciController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:SciController"],
+        beego.ControllerComments{
+            Method: "HandleExcelData",
+            Router: `/handle/excel_data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:SciController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:SciController"],
         beego.ControllerComments{
             Method: "Refresh",
@@ -493,6 +565,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:StockPlantController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:StockPlantController"],
+        beego.ControllerComments{
+            Method: "Calculate",
+            Router: `/calculate`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:StockPlantController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:StockPlantController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:ThsController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:ThsController"],
         beego.ControllerComments{
             Method: "Add",

+ 15 - 0
routers/router.go

@@ -150,6 +150,21 @@ func init() {
 				&controllers.BaiinfoController{},
 			),
 		),
+		beego.NSNamespace("/stock_plant",
+			beego.NSInclude(
+				&controllers.StockPlantController{},
+			),
+		),
+		beego.NSNamespace("/national_statistics",
+			beego.NSInclude(
+				&controllers.NationalStatisticsController{},
+			),
+		),
+		beego.NSNamespace("/fubao",
+			beego.NSInclude(
+				&controllers.FuBaoController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 29 - 0
services/base_from_fubao.go

@@ -0,0 +1,29 @@
+package services
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/http"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/utils"
+)
+
+const (
+	FuBaoQueryDataUrl     = "http://data.f139.com/cooperation/wande/queryData.do?pid=%s"
+	FuBaoQueryLastDataUrl = "http://data.f139.com/cooperation/wande/queryLastData.do"
+)
+
+// GetEdbDataFromLt 获取路透数据
+func GetEdbDataFromFuBao(edbCode, startDate, endDate string) (item *models.FuBaoData, err error) {
+	sbUrl := fmt.Sprintf(FuBaoQueryDataUrl, edbCode)
+	utils.FileLog.Info("sbUrl:%s", sbUrl)
+	body, err := http.Get(sbUrl)
+	utils.FileLog.Info("fubao result:%s", string(body))
+
+	if err != nil {
+		return
+	}
+	item = new(models.FuBaoData)
+	err = json.Unmarshal(body, &item)
+	return
+}

+ 148 - 0
services/base_from_mysteel_chemical.go

@@ -0,0 +1,148 @@
+package services
+
+import (
+	"fmt"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/utils"
+	"strings"
+	"time"
+)
+
+func HandleMysteelIndex(req *models.HandleMysteelIndexResp) {
+	for _, v := range req.List {
+		if v.IndexName == "" || v.IndexCode == "" {
+			continue
+		}
+		handleIndex(v)
+	}
+}
+
+func handleIndex(indexItem *models.HandleMysteelIndex) {
+	var err error
+
+	//return
+	indexObj := new(models.BaseFromMysteelChemicalIndex)
+	var indexId int64
+
+	addDataList := make([]models.BaseFromMysteelChemicalData, 0)
+
+	exitDataMap := make(map[string]*models.BaseFromMysteelChemicalData)
+
+	//判断指标是否存在
+	var isAdd int
+	item, err := indexObj.GetIndexItem(indexItem.IndexCode)
+	if err != nil {
+		if err.Error() == err.Error() {
+			isAdd = 1
+		} else {
+			isAdd = -1
+			return
+		}
+	}
+	if item != nil && item.BaseFromMysteelChemicalIndexId > 0 {
+		isAdd = 2
+	} else {
+		isAdd = 1
+	}
+
+	fmt.Println("isAdd:", isAdd)
+	if !strings.Contains(indexItem.Frequency, "度") {
+		indexItem.Frequency = indexItem.Frequency + "度"
+	}
+
+	if isAdd == 1 {
+		indexObj.IndexCode = indexItem.IndexCode
+		indexObj.IndexName = indexItem.IndexName
+		indexObj.Unit = indexItem.Unit
+		indexObj.Source = indexItem.Source
+		indexObj.Describe = indexItem.Describe
+		indexObj.StartDate, _ = time.ParseInLocation(utils.FormatDate, indexItem.StartDate, time.Local)
+		indexObj.EndDate, _ = time.ParseInLocation(utils.FormatDate, indexItem.EndDate, time.Local)
+		indexObj.Frequency = indexItem.Frequency
+		err = indexObj.Add()
+		if err != nil {
+			fmt.Println("add err:" + err.Error())
+			return
+		}
+		indexId = indexObj.BaseFromMysteelChemicalIndexId
+	} else if isAdd == 2 {
+		indexObj.IndexCode = indexItem.IndexCode
+		indexObj.IndexName = indexItem.IndexName
+		indexObj.Unit = indexItem.Unit
+		indexObj.Source = indexItem.Source
+		indexObj.Describe = indexItem.Describe
+		indexObj.StartDate, _ = time.ParseInLocation(utils.FormatDate, indexItem.StartDate, time.Local)
+		indexObj.EndDate, _ = time.ParseInLocation(utils.FormatDate, indexItem.EndDate, time.Local)
+		indexObj.Frequency = indexItem.Frequency
+		indexObj.ModifyTime = time.Now()
+		indexId = item.BaseFromMysteelChemicalIndexId
+		//修改数据
+		updateColsArr := make([]string, 0)
+		updateColsArr = append(updateColsArr, "index_name")
+		updateColsArr = append(updateColsArr, "unit")
+		updateColsArr = append(updateColsArr, "source")
+		updateColsArr = append(updateColsArr, "frequency")
+		updateColsArr = append(updateColsArr, "start_date")
+		updateColsArr = append(updateColsArr, "end_date")
+		updateColsArr = append(updateColsArr, "describe")
+		updateColsArr = append(updateColsArr, "end_date")
+		updateColsArr = append(updateColsArr, "modify_time")
+
+		indexObj.Update(updateColsArr)
+
+		dataObj := new(models.BaseFromMysteelChemicalData)
+
+		//获取已存在的所有数据
+		exitDataList, err := dataObj.GetIndexDataList(indexItem.IndexCode)
+		if err != nil {
+			fmt.Println("GetIndexDataList Err:" + err.Error())
+			return
+		}
+		fmt.Println("exitDataListLen:", len(exitDataList))
+		for _, v := range exitDataList {
+			dateStr := v.DataTime.Format(utils.FormatDate)
+			exitDataMap[dateStr] = v
+		}
+	}
+
+	dataObj := new(models.BaseFromMysteelChemicalData)
+	// 遍历excel数据,然后跟现有的数据做校验,不存在则入库
+	for date, value := range indexItem.ExcelDataMap {
+		if findData, ok := exitDataMap[date]; !ok {
+			dateTime, err := time.ParseInLocation(utils.FormatDate, date, time.Local)
+			if err != nil {
+				fmt.Println("time.ParseInLocation Err:" + err.Error())
+				return
+			}
+			if !strings.Contains(value, "#N/A") {
+				dataItem := new(models.BaseFromMysteelChemicalData)
+				dataItem.BaseFromMysteelChemicalIndexId = indexId
+				dataItem.IndexCode = indexItem.IndexCode
+				dataItem.DataTime = dateTime
+				dataItem.Value = value
+				dataItem.UpdateDate = indexItem.UpdateDate
+				dataItem.CreateTime = time.Now()
+				dataItem.ModifyTime = time.Now()
+				addDataList = append(addDataList, *dataItem)
+			}
+		} else {
+			if findData != nil && findData.Value != value && !strings.Contains(value, "#N/A") { //修改数据
+				dataObj.BaseFromMysteelChemicalDataId = findData.BaseFromMysteelChemicalDataId
+				dataObj.Value = value
+				dataObj.ModifyTime = time.Now()
+
+				updateDataColsArr := make([]string, 0)
+				updateDataColsArr = append(updateDataColsArr, "value")
+				updateDataColsArr = append(updateDataColsArr, "modify_time")
+				dataObj.Update(updateDataColsArr)
+			}
+		}
+	}
+
+	if len(addDataList) > 0 {
+		err = dataObj.Add(addDataList)
+		if err != nil {
+			fmt.Println("dataObj.Add() Err:" + err.Error())
+		}
+	}
+}

+ 5 - 11
services/base_from_pb.go

@@ -4,19 +4,13 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/http"
+	"hongze/hongze_edb_lib/models"
 	"hongze/hongze_edb_lib/utils"
 	"net/url"
 )
 
-type EdbDataFromPb struct {
-	Date   map[string]int64   `json:"date"`
-	Ticker map[string]string  `json:"ticker"`
-	Field  map[string]string  `json:"field"`
-	Value  map[string]float64 `json:"value"`
-}
-
 // GetEdbDataFromPb 获取Pb数据
-func GetEdbDataFromPb(edbCode, startDate, endDate string) (item *EdbDataFromPb, err error) {
+func GetEdbDataFromPb(edbCode, startDate, endDate string) (item *models.EdbDataFromPb, err error) {
 	edbCode = url.QueryEscape(edbCode)
 	bpUrl := utils.Hz_Pb_Data_Url + `edbInfo/pb?EdbCode=%s&StartDate=%s&EndDate=%s`
 	bpUrl = fmt.Sprintf(bpUrl, edbCode, startDate, endDate)
@@ -27,7 +21,7 @@ func GetEdbDataFromPb(edbCode, startDate, endDate string) (item *EdbDataFromPb,
 		return
 	}
 	utils.FileLog.Info("GetEdbDataByPb result:" + string(body))
-	item = new(EdbDataFromPb)
+	item = new(models.EdbDataFromPb)
 	err = json.Unmarshal(body, &item)
 	if err != nil {
 		utils.FileLog.Info("GetEdbDataByPb Unmarshal Err:" + err.Error())
@@ -38,7 +32,7 @@ func GetEdbDataFromPb(edbCode, startDate, endDate string) (item *EdbDataFromPb,
 }
 
 // GetEdbDataFromPbFinance 获取Pb财务数据
-func GetEdbDataFromPbFinance(companyCode, edbCode, startDate, endDate string) (item *EdbDataFromPb, err error) {
+func GetEdbDataFromPbFinance(companyCode, edbCode, startDate, endDate string) (item *models.EdbDataFromPb, err error) {
 	companyCode = url.QueryEscape(companyCode)
 	edbCode = url.QueryEscape(edbCode)
 	bpUrl := utils.Hz_Pb_Data_Url + `edbInfo/pb/finance?CompanyCode=%s&EdbCode=%s&StartDate=%s&EndDate=%s`
@@ -50,7 +44,7 @@ func GetEdbDataFromPbFinance(companyCode, edbCode, startDate, endDate string) (i
 		return
 	}
 	utils.FileLog.Info("GetEdbDataByPb result:" + string(body))
-	item = new(EdbDataFromPb)
+	item = new(models.EdbDataFromPb)
 	err = json.Unmarshal(body, &item)
 	if err != nil {
 		utils.FileLog.Info("GetEdbDataByPb Unmarshal Err:" + err.Error())

+ 3 - 8
services/base_from_python.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/models"
 	"hongze/hongze_edb_lib/utils"
 	"os"
 	"os/exec"
@@ -54,12 +55,6 @@ func Test() (err error) {
 	return
 }
 
-// EdbDataFromPython 通过python代码获取到的指标数据
-type EdbDataFromPython struct {
-	Date  map[int]string  `json:"date"`
-	Value map[int]float64 `json:"value"`
-}
-
 // EdbDataStrFromPython 通过python代码获取到的指标数据(interface数据)
 type EdbDataStrFromPython struct {
 	Date  map[int]string      `json:"date"`
@@ -67,7 +62,7 @@ type EdbDataStrFromPython struct {
 }
 
 // ExecPythonCode 执行Python代码
-func ExecPythonCode(edbCode, reqCode string) (dataMap EdbDataFromPython, err error, errMsg string) {
+func ExecPythonCode(edbCode, reqCode string) (dataMap models.EdbDataFromPython, err error, errMsg string) {
 	defer func() {
 		if err != nil {
 			fmt.Println("err:", err)
@@ -135,7 +130,7 @@ func ExecPythonCode(edbCode, reqCode string) (dataMap EdbDataFromPython, err err
 	//将value为nil的给过滤掉
 	i := 0
 	lenData := len(dataMapStr.Date)
-	for k := 1; k < lenData; k++ {
+	for k := 0; k < lenData; k++ {
 		date := dataMapStr.Date[k]
 		tmpValue := dataMapStr.Value[k]
 		if reflect.TypeOf(tmpValue) != nil {

+ 175 - 0
services/base_from_sci.go

@@ -0,0 +1,175 @@
+package services
+
+import (
+	"fmt"
+	"hongze/hongze_edb_lib/logic"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services/alarm_msg"
+	"hongze/hongze_edb_lib/utils"
+	"strings"
+	"time"
+)
+
+// HandleSciIndex 处理卓创(红桃3)的Excel数据
+func HandleSciIndex(indexNameList, thirdIndexIdList, frequencyList, unitList []string, dataMap map[string]map[string]string) {
+	// 卓创(红桃3)指标id列表
+
+	sciIndexModel := new(models.BaseFromSciIndex)
+	list, err := sciIndexModel.GetAllIndex()
+	if err != nil {
+		fmt.Println("获取指标失败:", err)
+		return
+	}
+	allIndexMap := make(map[string]string)
+	for _, v := range list {
+		allIndexMap[v.IndexCode] = ``
+	}
+
+	// 需要入库的指标下标
+	needAddIndexKeyList := make([]int, 0)
+	needAddIndexMap := make(map[string]int, 0)
+	for key, v := range thirdIndexIdList {
+		if _, ok := allIndexMap[v]; !ok {
+			if _, ok2 := needAddIndexMap[v]; !ok2 {
+				needAddIndexKeyList = append(needAddIndexKeyList, key)
+				needAddIndexMap[v] = key
+			}
+		}
+	}
+	//fmt.Println(needAddIndexKeyList)
+	//return
+	addSciIndexList := make([]*models.BaseFromSciIndex, 0)
+	// 新的指标入库
+	for _, key := range needAddIndexKeyList {
+		tmpSciIndex := &models.BaseFromSciIndex{
+			//BaseFromSciIndexId: 0,
+			//ClassifyId:         0,
+			IndexCode: thirdIndexIdList[key],
+			IndexName: indexNameList[key],
+			Frequency: frequencyList[key],
+			Unit:      unitList[key],
+			//StartDate: time.Time{},
+			//EndDate:      time.Time{},
+			CreateTime: time.Now(),
+			ModifyTime: time.Now(),
+		}
+		addSciIndexList = append(addSciIndexList, tmpSciIndex)
+	}
+	//fmt.Println(addSciIndexList)
+	if len(addSciIndexList) > 0 {
+		err = sciIndexModel.BatchAdd(addSciIndexList)
+		if err != nil {
+			fmt.Println("批量添加指标失败:", err)
+			return
+		}
+		fmt.Println("添加成功")
+	}
+
+	// 红桃3实际数据处理
+	HandleData(dataMap)
+
+}
+
+// HandleData 红桃3实际数据处理
+func HandleData(dataMap map[string]map[string]string) {
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			go alarm_msg.SendAlarmMsg(fmt.Sprint("红桃3实际数据处理失败,err:", strings.Join(errMsgList, "\n")), 3)
+		}
+	}()
+	// 获取所有的指标
+	sciIndexModel := new(models.BaseFromSciIndex)
+	list, err := sciIndexModel.GetAllIndex()
+	if err != nil {
+		fmt.Println("获取指标失败:", err)
+		return
+	}
+	allIndexMap := make(map[string]*models.BaseFromSciIndex)
+	for _, v := range list {
+		allIndexMap[v.IndexCode] = v
+	}
+
+	sciIndexDataModel := new(models.BaseFromSciData)
+	for indexCode, data := range dataMap {
+		indexInfo, ok := allIndexMap[indexCode]
+		if !ok {
+			fmt.Println("找不到该指标:", indexCode)
+			continue
+		}
+
+		indexDataList, err := sciIndexDataModel.GetIndexDataList(indexCode)
+		if err != nil {
+			errMsgList = append(errMsgList, fmt.Sprint("查找卓创基础指标失败,指标编码:", indexCode, ";错误原因:", err.Error()))
+			continue
+		}
+		indexDataExistMap := make(map[string]*models.BaseFromSciData)
+		for _, indexData := range indexDataList {
+			indexDataExistMap[indexData.DataTime] = indexData
+		}
+		addSciDataList := make([]*models.BaseFromSciData, 0)
+
+		for currDate, currVal := range data {
+			currDataTime, tmpErr := time.ParseInLocation(utils.FormatDate, currDate, time.Local)
+			if tmpErr != nil {
+				errMsgList = append(errMsgList, fmt.Sprint("时间格式化失败,指标编码:", currDate, ";错误原因:", tmpErr.Error()))
+				continue
+			}
+			timestamp := currDataTime.UnixNano() / 1e6
+
+			sciData, ok := indexDataExistMap[currDate]
+			//判断是否存在数据,如果不存在,那么插入数据,存在的话那么修改数据
+			if !ok {
+				tmpBaseFromSciData := &models.BaseFromSciData{
+					//SciDataId:          0,
+					BaseFromSciIndexId: indexInfo.BaseFromSciIndexId,
+					IndexCode:          indexInfo.IndexCode,
+					DataTime:           currDate,
+					Value:              currVal,
+					DataTimestamp:      timestamp,
+					CreateTime:         time.Now(),
+					ModifyTime:         time.Now(),
+				}
+				indexDataExistMap[currDate] = tmpBaseFromSciData
+				addSciDataList = append(addSciDataList, tmpBaseFromSciData)
+			} else {
+				// 更新数据
+				existValue := sciData.Value
+				if existValue != currVal {
+					//fmt.Println("existValue:", existValue, ";====;currVal:", currVal)
+					sciData.Value = currVal
+					sciData.ModifyTime = time.Now()
+					// 如果是已经入库了数据,那么就更新,否则只是单纯更改内存数据,而不去更新数据库
+					if sciData.SciDataId > 0 {
+						tmpErr = sciData.Update([]string{"Value", "ModifyTime"})
+						if tmpErr != nil {
+							errMsgList = append(errMsgList, fmt.Sprint("指标数据更新失败,指标编码:", indexCode, ",时间:", currDate, ";错误原因:", tmpErr.Error()))
+							continue
+						}
+					}
+				}
+			}
+		}
+
+		if len(addSciDataList) > 0 {
+			err = sciIndexDataModel.BatchAdd(addSciDataList)
+			if err != nil {
+				errMsgList = append(errMsgList, fmt.Sprint("批量添加数据失败,指标编码:", indexCode, err.Error()))
+				continue
+			}
+		}
+		indexInfo.ModifyTime = time.Now()
+		indexInfo.Update([]string{"ModifyTime"})
+
+		// 同步刷新ETA图库红桃3的指标
+		{
+			// 获取指标详情
+			edbInfo, err := models.GetEdbInfoByEdbCode(utils.DATA_SOURCE_SCI, indexInfo.IndexCode)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsgList = append(errMsgList, fmt.Sprint("刷新ETA指标异常,指标编码:", indexCode, err.Error()))
+				continue
+			}
+			go logic.RefreshSci(edbInfo, ``)
+		}
+	}
+}

+ 10 - 46
services/base_from_ths.go

@@ -6,25 +6,12 @@ import (
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/http"
 	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/models/future_good"
 	"hongze/hongze_edb_lib/utils"
 	"reflect"
 )
 
-type EdbDataFromThs struct {
-	DataVol   int64       `json:"dataVol"`
-	Errmsg    string      `json:"errmsg"`
-	Errorcode int64       `json:"errorcode"`
-	Perf      interface{} `json:"perf"`
-	Tables    []Tables    `json:"tables"`
-}
-
-// Tables 表格数据
-type Tables struct {
-	ID    []string  `json:"id"`
-	Time  []string  `json:"time"`
-	Value []float64 `json:"value"`
-}
-
 // EdbDataFromThsInterface 数据类型转为interface
 type EdbDataFromThsInterface struct {
 	DataVol   int64       `json:"dataVol"`
@@ -38,12 +25,12 @@ type EdbDataFromThsInterface struct {
 	} `json:"tables"`
 }
 
-func GetEdbDataFromThs(edbCode, startDate, endDate string) (item *EdbDataFromThs, err error) {
+func GetEdbDataFromThs(edbCode, startDate, endDate string) (item *models.EdbDataFromThs, err error) {
 	return getEdbDataFromThs(edbCode, startDate, endDate, 0)
 }
 
 // getEdbDataFromThs 获取同花顺接口数据
-func getEdbDataFromThs(edbCode, startDate, endDate string, num int) (item *EdbDataFromThs, err error) {
+func getEdbDataFromThs(edbCode, startDate, endDate string, num int) (item *models.EdbDataFromThs, err error) {
 	thsUrl := utils.Hz_Wind_Data_Url + `edbInfo/ths?EdbCode=%s&StartDate=%s&EndDate=%s`
 	thsUrl = fmt.Sprintf(thsUrl, edbCode, startDate, endDate)
 	utils.FileLog.Info("thsUrl:" + thsUrl)
@@ -77,7 +64,7 @@ func getEdbDataFromThs(edbCode, startDate, endDate string, num int) (item *EdbDa
 	}
 
 	// 因为table里面的value有的时候返回的是string,有的是float64,所以需要用interface来反射取值
-	tablesList := make([]Tables, 0)
+	tablesList := make([]models.Tables, 0)
 	for _, table := range tmpItems.Tables {
 		tableIdList := make([]string, 0)
 		tableTimeList := make([]string, 0)
@@ -108,14 +95,14 @@ func getEdbDataFromThs(edbCode, startDate, endDate string, num int) (item *EdbDa
 			}
 			tableValueList = append(tableValueList, tableValue)
 		}
-		tmpTable := Tables{
+		tmpTable := models.Tables{
 			ID:    tableIdList,
 			Time:  tableTimeList,
 			Value: tableValueList,
 		}
 		tablesList = append(tablesList, tmpTable)
 	}
-	item = &EdbDataFromThs{
+	item = &models.EdbDataFromThs{
 		DataVol:   tmpItems.DataVol,
 		Errmsg:    tmpItems.Errmsg,
 		Errorcode: tmpItems.Errorcode,
@@ -159,31 +146,8 @@ type FutureGoodDataFromThsInterface struct {
 	} `json:"tables"`
 }
 
-// FutureGoodDataFromThs 同花顺期货数据
-type FutureGoodDataFromThs struct {
-	DataVol   int64                `json:"dataVol"`
-	Errmsg    string               `json:"errmsg"`
-	Errorcode int64                `json:"errorcode"`
-	Perf      interface{}          `json:"perf"`
-	Tables    FutureGoodDataTables `json:"tables"`
-}
-
-// FutureGoodDataTables 同花顺表格数据
-type FutureGoodDataTables struct {
-	//TradeCode    []string  `json:"id"`
-	Time       []string  `json:"time"`
-	Open       []float64 `json:"open"`
-	High       []float64 `json:"high"`
-	Low        []float64 `json:"low"`
-	Close      []float64 `json:"close"`
-	Volume     []float64 `json:"volume"`
-	Amount     []float64 `json:"amount"`
-	Ccl        []float64 `json:"ccl"`
-	Settlement []float64 `json:"settlement"`
-}
-
 // GetFutureGoodDataFromThs 通过url获取wind的商品数据
-func GetFutureGoodDataFromThs(edbCode, startDate, endDate string, num int) (item *FutureGoodDataFromThs, err error) {
+func GetFutureGoodDataFromThs(edbCode, startDate, endDate string, num int) (item *future_good.FutureGoodDataFromThs, err error) {
 	thsUrl := utils.Hz_Wind_Data_Url + `edbInfo/ths/future_good?EdbCode=%s&StartDate=%s&EndDate=%s`
 	thsUrl = fmt.Sprintf(thsUrl, edbCode, startDate, endDate)
 	utils.FileLog.Info("thsUrl:" + thsUrl)
@@ -220,12 +184,12 @@ func GetFutureGoodDataFromThs(edbCode, startDate, endDate string, num int) (item
 		return
 	}
 	table := tmpItems.Tables[0]
-	item = &FutureGoodDataFromThs{
+	item = &future_good.FutureGoodDataFromThs{
 		DataVol:   tmpItems.DataVol,
 		Errmsg:    tmpItems.Errmsg,
 		Errorcode: tmpItems.Errorcode,
 		Perf:      tmpItems.Perf,
-		Tables: FutureGoodDataTables{
+		Tables: future_good.FutureGoodDataTables{
 			Time:       table.Time,
 			Open:       table.Table.Open,
 			High:       table.Table.High,

+ 5 - 11
services/base_from_wind.go

@@ -5,20 +5,14 @@ import (
 	"errors"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/http"
+	"hongze/hongze_edb_lib/models"
 	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"time"
 )
 
-type EdbDataFromWind struct {
-	Close     map[string]float64 `json:"CLOSE"`
-	Dt        map[string]int64   `json:"DT"`
-	ErrorCode map[string]int64   `json:"ErrorCode"`
-	ErrMsg    string
-}
-
 // GetEdbDataFromWind 获取wind数据
-func GetEdbDataFromWind(edbCode, startDate, endDate string) (item *EdbDataFromWind, errorCode int, err error) {
+func GetEdbDataFromWind(edbCode, startDate, endDate string) (item *models.EdbDataFromWind, errorCode int, err error) {
 	windUrl, err := GetWindUrl(edbCode)
 	if err != nil {
 		errorCode = 421
@@ -36,7 +30,7 @@ func GetEdbDataFromWind(edbCode, startDate, endDate string) (item *EdbDataFromWi
 	if err != nil {
 		return
 	}
-	item = new(EdbDataFromWind)
+	item = new(models.EdbDataFromWind)
 	err = json.Unmarshal(body, &item)
 
 	//异常的话,需要邮件通知
@@ -80,7 +74,7 @@ func GetEdbDataFromWind(edbCode, startDate, endDate string) (item *EdbDataFromWi
 }
 
 // GetEdbDataFromWindUrl 通过url获取wind数据
-func GetEdbDataFromWindUrl(windUrl, edbCode, startDate, endDate string) (item *EdbDataFromWind, errorCode int, err error) {
+func GetEdbDataFromWindUrl(windUrl, edbCode, startDate, endDate string) (item *models.EdbDataFromWind, errorCode int, err error) {
 	if windUrl == `` {
 		return GetEdbDataFromWind(edbCode, startDate, endDate)
 	}
@@ -96,7 +90,7 @@ func GetEdbDataFromWindUrl(windUrl, edbCode, startDate, endDate string) (item *E
 	if err != nil {
 		return
 	}
-	item = new(EdbDataFromWind)
+	item = new(models.EdbDataFromWind)
 	err = json.Unmarshal(body, &item)
 
 	//异常的话,需要邮件通知

File diff suppressed because it is too large
+ 715 - 70
swagger/swagger.json


File diff suppressed because it is too large
+ 665 - 39
swagger/swagger.yml


+ 1 - 1
services/base_from_calculate.go → utils/base_from_calculate.go

@@ -1,4 +1,4 @@
-package services
+package utils
 
 import (
 	"strings"

+ 76 - 0
utils/calculate.go

@@ -1,5 +1,11 @@
 package utils
 
+import (
+	"github.com/gonum/stat"
+	"github.com/shopspring/decimal"
+	"math"
+)
+
 // Series is a container for a series of data
 type Series []Coordinate
 
@@ -43,3 +49,73 @@ func GetLinearResult(s []Coordinate) (gradient, intercept float64) {
 
 	return
 }
+
+// ComputeCorrelation 通过一组数据获取相关系数R
+// 计算步骤
+// 1.分别计算两个序列的平均值Mx和My
+// 2.分别计算两个序列的标准偏差SDx和SDy	=> √{1/(n-1)*SUM[(Xi-Mx)²]}
+// 3.计算相关系数	=> SUM[(Xi-Mx)*(Yi-My)]/[(N-1)(SDx*SDy)]
+func ComputeCorrelation(sList []Coordinate) (r float64) {
+	var xBar, yBar float64
+	lenSList := len(sList)
+	// 必须两组数据及两组以上的数据才能计算
+	if lenSList < 2 {
+		return
+	}
+	decimalX := decimal.NewFromFloat(0)
+	decimalY := decimal.NewFromFloat(0)
+
+	// 计算两组数据X、Y的平均值
+	for _, coordinate := range sList {
+		decimalX = decimalX.Add(decimal.NewFromFloat(coordinate.X))
+		decimalY = decimalY.Add(decimal.NewFromFloat(coordinate.Y))
+	}
+	xBar, _ = decimalX.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	yBar, _ = decimalY.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	//fmt.Println(xBar)
+	//fmt.Println(yBar)
+
+	varXDeci := decimal.NewFromFloat(0)
+	varYDeci := decimal.NewFromFloat(0)
+	ssrDeci := decimal.NewFromFloat(0)
+
+	for _, coordinate := range sList {
+		// 分别计算X、Y的实际数据与平均值的差值
+		diffXXbarDeci := decimal.NewFromFloat(coordinate.X).Sub(decimal.NewFromFloat(xBar))
+		diffYYbarDeci := decimal.NewFromFloat(coordinate.Y).Sub(decimal.NewFromFloat(yBar))
+		ssrDeci = ssrDeci.Add(diffXXbarDeci.Mul(diffYYbarDeci))
+		//fmt.Println("i:", i, ";diffXXbar:", diffXXbarDeci.String(), ";diffYYbar:", diffYYbarDeci.String(), ";ssr:", ssrDeci.String())
+		varXDeci = varXDeci.Add(diffXXbarDeci.Mul(diffXXbarDeci))
+		varYDeci = varYDeci.Add(diffYYbarDeci.Mul(diffYYbarDeci))
+		//varY += diffYYbar ** 2
+	}
+	//当输入的两个数组完全相同时,计算相关系数会导致除以零的操作,从而产生 NaN(Not a Number)的结果。为了避免这种情况,可以在计算相关系数之前先进行一个判断,如果两个数组的标准差为零,则相关系数应为1
+	if varXDeci.IsZero() && varYDeci.IsZero() {
+		r = 1
+		return
+	}
+	sqrtVal, _ := varXDeci.Mul(varYDeci).Round(4).Float64()
+	//fmt.Println("sqrtVal:", sqrtVal)
+	sst := math.Sqrt(sqrtVal) // 平方根
+	//fmt.Println("sst:", sst)
+	// 如果计算出来的平方根是0,那么就直接返回,因为0不能作为除数
+	if sst == 0 {
+		return
+	}
+	r, _ = ssrDeci.Div(decimal.NewFromFloat(sst)).Round(4).Float64()
+
+	return
+}
+
+// CalculationDecisive 通过一组数据获取决定系数R2
+func CalculationDecisive(sList []Coordinate) (r2 float64) {
+	r := ComputeCorrelation(sList)
+	r2, _ = decimal.NewFromFloat(r).Mul(decimal.NewFromFloat(r)).Round(4).Float64()
+
+	return
+}
+
+// CalculateStandardDeviation 计算标准差
+func CalculateStandardDeviation(data []float64) float64 {
+	return stat.StdDev(data, nil)
+}

+ 18 - 0
utils/common.go

@@ -1179,3 +1179,21 @@ func GetFrequencyEndDay(currDate time.Time, frequency string) (endDate time.Time
 
 	return
 }
+
+// CheckFrequency 获取两个频度之间是否相对低高频
+// 大于0,代表左侧是高频(例:左侧:日度,右侧:周度)
+// 等于0,代表同频
+// 小于0,代表右侧是高频(例:左侧:周度,右侧:日度)
+func CheckFrequency(leftFrequency, rightFrequency string) int {
+	frequencyMap := map[string]int{
+		"年度":  0,
+		"半年度": 1,
+		"季度":  2,
+		"月度":  3,
+		"旬度":  4,
+		"周度":  5,
+		"日度":  6,
+	}
+
+	return frequencyMap[leftFrequency] - frequencyMap[rightFrequency]
+}

+ 116 - 54
utils/constants.go

@@ -14,6 +14,8 @@ const (
 	FormatDateTime             = "2006-01-02 15:04:05" //完整时间格式
 	FormatDateTimeUnSpace      = "20060102150405"      //完整时间格式
 	FormatShortDateTimeUnSpace = "060102150405"        //省去开头两位年份的时间格式
+	FormatYearMonthDate        = "2006-01"             //日期格式
+	FormatYearMonthUnSpace     = "200601"              //年月的日期格式
 	PageSize15                 = 15                    //列表页每页数据量
 	PageSize5                  = 5
 	PageSize10                 = 10
@@ -29,56 +31,56 @@ const (
 
 // 数据来源渠道
 const (
-	DATA_SOURCE_THS                         = iota + 1 //同花顺
-	DATA_SOURCE_WIND                                   //wind
-	DATA_SOURCE_PB                                     //彭博
-	DATA_SOURCE_CALCULATE                              //指标运算
-	DATA_SOURCE_CALCULATE_LJZZY                        //累计值转月
-	DATA_SOURCE_CALCULATE_TBZ                          //同比值
-	DATA_SOURCE_CALCULATE_TCZ                          //同差值
-	DATA_SOURCE_CALCULATE_NSZYDPJJS                    //N数值移动平均计算
-	DATA_SOURCE_MANUAL                                 //手工指标
-	DATA_SOURCE_LZ                                     //隆众
-	DATA_SOURCE_YS                                     //有色
-	DATA_SOURCE_CALCULATE_HBZ                          //环比值->12
-	DATA_SOURCE_CALCULATE_HCZ                          //环差值->13
-	DATA_SOURCE_CALCULATE_BP                           //变频->14
-	DATA_SOURCE_GL                                     //钢联->15
-	DATA_SOURCE_ZZ                                     //郑商所->16
-	DATA_SOURCE_DL                                     //大商所->17
-	DATA_SOURCE_SH                                     //上期所->18
-	DATA_SOURCE_CFFEX                                  //中金所->19
-	DATA_SOURCE_SHFE                                   //上期能源->20
-	DATA_SOURCE_GIE                                    //欧洲天然气->21
-	DATA_SOURCE_CALCULATE_TIME_SHIFT                   //时间移位->22
-	DATA_SOURCE_CALCULATE_ZJPJ                         //直接拼接->23
-	DATA_SOURCE_CALCULATE_LJZTBPJ                      //累计值同比拼接->24
-	DATA_SOURCE_LT                                     //路透->25
-	DATA_SOURCE_COAL                                   //煤炭网->26
-	DATA_SOURCE_PYTHON                                 //python代码->27
-	DATA_SOURCE_PB_FINANCE                             //彭博财务数据->28
-	DATA_SOURCE_GOOGLE_TRAVEL                          //谷歌出行数据->29
-	DATA_SOURCE_PREDICT                                //普通预测指标->30
-	DATA_SOURCE_PREDICT_CALCULATE                      //预测指标运算->31
-	DATA_SOURCE_PREDICT_CALCULATE_TBZ                  //预测指标 - 同比值->32
-	DATA_SOURCE_PREDICT_CALCULATE_TCZ                  //预测指标 - 同差值->33
-	DATA_SOURCE_MYSTEEL_CHEMICAL                       //钢联化工->34
-	DATA_SOURCE_CALCULATE_CJJX                         //超季节性->35
-	DATA_SOURCE_EIA_STEO                               //eia steo报告->36
-	DATA_SOURCE_CALCULATE_NHCC                         //计算指标(拟合残差)->37
-	DATA_SOURCE_COM_TRADE                              //联合国商品贸易数据->38
-	DATA_SOURCE_PREDICT_CALCULATE_NSZYDPJJS            //预测指标 - N数值移动平均计算 -> 39
-	DATA_SOURCE_CALCULATE_ADJUST                       //数据调整->40
-	DATA_SOURCE_SCI                                    //卓创数据(红桃三)->41
-	DATA_SOURCE_PREDICT_CALCULATE_LJZZY                //预测指标 - 累计值转月->42
-	DATA_SOURCE_PREDICT_CALCULATE_HBZ                  //预测指标 - 环比值->43
-	DATA_SOURCE_PREDICT_CALCULATE_HCZ                  //预测指标 - 环差值->44
-	DATA_SOURCE_PREDICT_CALCULATE_BP                   //预测指标 - 变频->45
-	DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT           //预测指标 - 时间移位->46
-	DATA_SOURCE_PREDICT_CALCULATE_ZJPJ                 //预测指标 - 直接拼接->47
-	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ              //预测指标 - 累计值同比拼接->48
-	DATA_SOURCE_PREDICT_CALCULATE_CJJX                 //预测指标 - 超季节性->49
-	DATA_SOURCE_PREDICT_CALCULATE_NHCC                 //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_THS                          = iota + 1 //同花顺
+	DATA_SOURCE_WIND                                    //wind
+	DATA_SOURCE_PB                                      //彭博
+	DATA_SOURCE_CALCULATE                               //指标运算
+	DATA_SOURCE_CALCULATE_LJZZY                         //累计值转月
+	DATA_SOURCE_CALCULATE_TBZ                           //同比值
+	DATA_SOURCE_CALCULATE_TCZ                           //同差值
+	DATA_SOURCE_CALCULATE_NSZYDPJJS                     //N数值移动平均计算
+	DATA_SOURCE_MANUAL                                  //手工指标
+	DATA_SOURCE_LZ                                      //隆众
+	DATA_SOURCE_YS                                      //有色
+	DATA_SOURCE_CALCULATE_HBZ                           //环比值->12
+	DATA_SOURCE_CALCULATE_HCZ                           //环差值->13
+	DATA_SOURCE_CALCULATE_BP                            //变频->14
+	DATA_SOURCE_GL                                      //钢联->15
+	DATA_SOURCE_ZZ                                      //郑商所->16
+	DATA_SOURCE_DL                                      //大商所->17
+	DATA_SOURCE_SH                                      //上期所->18
+	DATA_SOURCE_CFFEX                                   //中金所->19
+	DATA_SOURCE_SHFE                                    //上期能源->20
+	DATA_SOURCE_GIE                                     //欧洲天然气->21
+	DATA_SOURCE_CALCULATE_TIME_SHIFT                    //时间移位->22
+	DATA_SOURCE_CALCULATE_ZJPJ                          //直接拼接->23
+	DATA_SOURCE_CALCULATE_LJZTBPJ                       //累计值同比拼接->24
+	DATA_SOURCE_LT                                      //路透->25
+	DATA_SOURCE_COAL                                    //煤炭网->26
+	DATA_SOURCE_PYTHON                                  //python代码->27
+	DATA_SOURCE_PB_FINANCE                              //彭博财务数据->28
+	DATA_SOURCE_GOOGLE_TRAVEL                           //谷歌出行数据->29
+	DATA_SOURCE_PREDICT                                 //普通预测指标->30
+	DATA_SOURCE_PREDICT_CALCULATE                       //预测指标运算->31
+	DATA_SOURCE_PREDICT_CALCULATE_TBZ                   //预测指标 - 同比值->32
+	DATA_SOURCE_PREDICT_CALCULATE_TCZ                   //预测指标 - 同差值->33
+	DATA_SOURCE_MYSTEEL_CHEMICAL                        //钢联化工->34
+	DATA_SOURCE_CALCULATE_CJJX                          //超季节性->35
+	DATA_SOURCE_EIA_STEO                                //eia steo报告->36
+	DATA_SOURCE_CALCULATE_NHCC                          //计算指标(拟合残差)->37
+	DATA_SOURCE_COM_TRADE                               //联合国商品贸易数据->38
+	DATA_SOURCE_PREDICT_CALCULATE_NSZYDPJJS             //预测指标 - N数值移动平均计算 -> 39
+	DATA_SOURCE_CALCULATE_ADJUST                        //数据调整->40
+	DATA_SOURCE_SCI                                     //卓创数据(红桃三)->41
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZY                 //预测指标 - 累计值转月->42
+	DATA_SOURCE_PREDICT_CALCULATE_HBZ                   //预测指标 - 环比值->43
+	DATA_SOURCE_PREDICT_CALCULATE_HCZ                   //预测指标 - 环差值->44
+	DATA_SOURCE_PREDICT_CALCULATE_BP                    //预测指标 - 变频->45
+	DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT            //预测指标 - 时间移位->46
+	DATA_SOURCE_PREDICT_CALCULATE_ZJPJ                  //预测指标 - 直接拼接->47
+	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ               //预测指标 - 累计值同比拼接->48
+	DATA_SOURCE_PREDICT_CALCULATE_CJJX                  //预测指标 - 超季节性->49
+	DATA_SOURCE_PREDICT_CALCULATE_NHCC                  //预测指标 - 计算指标(拟合残差)->50
 	DATA_SOURCE_CALCULATE_JP                            //降频->51
 	DATA_SOURCE_CALCULATE_NH                            //年化->52
 	DATA_SOURCE_CALCULATE_KSZS                          //扩散指数->53
@@ -86,6 +88,20 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE_NH                    //预测指标 - 计算指标(年化)->55
 	DATA_SOURCE_PREDICT_CALCULATE_KSZS                  //预测指标 - 计算指标(扩散指数)->56
 	DATA_SOURCE_BAIINFO                                 //百川盈孚 ->57
+	DATA_SOURCE_STOCK_PLANT                             //存量装置 ->58
+	DATA_SOURCE_CALCULATE_CORRELATION                   //相关性计算->59
+	DATA_SOURCE_NATIONAL_STATISTICS                     //国家统计局->60
+	DATA_SOURCE_CALCULATE_LJZZJ                         //累计值转季 -> 61
+	DATA_SOURCE_CALCULATE_LJZ                           //累计值 -> 62
+	DATA_SOURCE_CALCULATE_LJZNCZJ                       //累计值(年初至今) -> 63
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZJ                 //预测指标 - 累计值转季->64
+	DATA_SOURCE_PREDICT_CALCULATE_LJZ                   //预测指标 - 累计值 -> 65
+	DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ               //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_CALCULATE_STANDARD_DEVIATION                    //标准差->67
+	DATA_SOURCE_CALCULATE_PERCENTILE                            //百分位->68
+	DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION            //预测标准差->69
+	DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE                    //预测百分位->70
+	DATA_SOURCE_FUBAO                                           //富宝数据->71
 )
 
 // 指标来源的中文展示
@@ -146,6 +162,21 @@ const (
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_JP         = `预测降频`              //预测指标 - 计算指标(降频)->54
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_NH         = `预测年化`              //预测指标 - 计算指标(年化)->55
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS       = `预测扩散指数`            //预测指标 - 计算指标(扩散指数)->56
+	DATA_SOURCE_NAME_BAIINFO                      = `百川盈孚`              //百川盈孚 ->57
+	DATA_SOURCE_NAME_STOCK_PLANT                  = `存量装置`              //存量装置 ->58
+	DATA_SOURCE_NAME_CALCULATE_CORRELATION        = `相关性计算`             //相关性计算->59
+	DATA_SOURCE_NAME_NATIONAL_STATISTICS          = `国家统计局`             //国家统计局->60
+	DATA_SOURCE_NAME_CALCULATE_LJZZJ              = `累计值转季值`            //累计值转季 -> 61
+	DATA_SOURCE_NAME_CALCULATE_LJZ                = `累计值`               //累计值 -> 62
+	DATA_SOURCE_NAME_CALCULATE_LJZNCZJ            = `年初至今累计值`           //累计值(年初至今) -> 63
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZZJ      = `预测累计值转季值`          //预测指标 - 累计值转季->64
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZ        = `预测累计值`             //预测指标 - 累计值 -> 65
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZNCZJ    = `预测年初至今累计值`         //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_NAME_CALCULATE_STANDARD_DEVIATION         = `标准差`               //标准差->67
+	DATA_SOURCE_NAME_CALCULATE_PERCENTILE                 = `百分位`               //百分位->68
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_STANDARD_DEVIATION = `预测标准差`             //预测标准差->69
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_PERCENTILE         = `预测百分位`             //预测百分位->70
+	DATA_SOURCE_NAME_FUBAO                                = `富宝数据`              //富宝数据->71
 )
 
 // 基础数据初始化日期
@@ -186,8 +217,39 @@ const (
 )
 
 const (
-	CACHE_EDB_DATA_ADD     = "CACHE_EDB_DATA_ADD_"
-	CACHE_EDB_DATA_REFRESH = "CACHE_EDB_DATA_REFRESH_"
-	CACHE_WIND_URL         = "CACHE_WIND_URL"
-	CACHE_CHART_INFO_DATA  = "chart:info:data:" //图表数据
+	CACHE_EDB_DATA_ADD          = "CACHE_EDB_DATA_ADD_"
+	CACHE_EDB_DATA_REFRESH      = "CACHE_EDB_DATA_REFRESH_"
+	CACHE_WIND_URL              = "CACHE_WIND_URL"
+	CACHE_CHART_INFO_DATA       = "chart:info:data:"             //图表数据
+	CACHE_STOCK_PLANT_CALCULATE = "CACHE_STOCK_PLANT_CALCULATE_" // 库存装置减产计算
 )
+
+// 图表类型
+const (
+	CHART_SOURCE_DEFAULT             = 1
+	CHART_SOURCE_FUTURE_GOOD         = 2
+	CHART_SOURCE_CORRELATION         = 3 // 相关性图表
+	CHART_SOURCE_ROLLING_CORRELATION = 4 // 滚动相关性图表
+	CHART_SOURCE_FUTURE_GOOD_PROFIT  = 5 // 商品利润曲线
+)
+
+// MonthQuarterMap 月份与季度的map
+var MonthQuarterMap = map[int]int{
+	1:  1,
+	2:  1,
+	3:  1,
+	4:  2,
+	5:  2,
+	6:  2,
+	7:  3,
+	8:  3,
+	9:  3,
+	10: 4,
+	11: 4,
+	12: 4,
+}
+
+// FrequencyDaysMap 频度日期的map关系
+var FrequencyDaysMap = map[string]int{
+	"天": 1, "周": 7, "月": 30, "季": 90, "年": 365,
+}

Some files were not shown because too many files changed in this diff