Forráskód Böngészése

fix:支持预测指标预算

Roc 2 éve
szülő
commit
87a2cb71a1

+ 832 - 0
controllers/base_from_predict_calculate.go

@@ -0,0 +1,832 @@
+package controllers
+
+import (
+	"encoding/json"
+	"fmt"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictCalculateController 预测计算指标
+type PredictCalculateController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 编辑指标接口
+// @Description 编辑指标接口
+// @Success 200 {object} models.EditEdbInfoReq
+// @router /add [post]
+func (this *PredictCalculateController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.EdbInfoCalculateSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	if len(req.EdbInfoIdArr) <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	if req.CalculateFormula == "" {
+		br.Msg = "请填写指标"
+		return
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("predict_edb_info:calculate:batch:save:", utils.DATA_SOURCE_PREDICT_CALCULATE, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		br.Msg = "指标正在处理,请勿重复提交"
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			utils.Rc.Delete(redisKey)
+		}()
+	}
+
+	calculateFormula := req.CalculateFormula
+	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
+	calculateFormula = strings.Replace(calculateFormula, ")", ")", -1)
+	calculateFormula = strings.Replace(calculateFormula, ",", ",", -1)
+	calculateFormula = strings.Replace(calculateFormula, "。", ".", -1)
+	if strings.Contains(req.CalculateFormula, "%") {
+		calculateFormula = strings.Replace(calculateFormula, "%", "*0.01", -1)
+	}
+	req.CalculateFormula = calculateFormula
+
+	//检验公式
+	var formulaStr string
+	var edbInfoIdBytes []string
+	for _, v := range req.EdbInfoIdArr {
+		formulaStr += v.FromTag + ","
+		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
+	}
+	formulaMap := services.CheckFormula(req.CalculateFormula)
+	for _, v := range formulaMap {
+		if !strings.Contains(formulaStr, v) {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
+	}
+
+	// 判断指标名称是否存在
+	{
+		var condition string
+		var pars []interface{}
+		// 指标类型,0:普通指标,1:预测指标
+		condition += " AND edb_info_type=?  AND edb_name=? "
+		pars = append(pars, 1, req.EdbName)
+
+		count, err := models.GetEdbInfoCountByCondition(condition, pars)
+		if err != nil {
+			br.Msg = "判断指标名称是否存在失败"
+			br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "指标名称已存在,请重新填写"
+			br.ErrMsg = "指标名称已存在,请重新填写"
+			return
+		}
+	}
+
+	// 关联指标信息
+	edbInfoList := make([]*models.EdbInfo, 0)
+	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		fromEdbInfo, err := models.GetEdbInfoById(v.EdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "生成计算指标失败"
+				br.Msg = "指标 " + strconv.Itoa(v.EdbInfoId) + " 不存在"
+				return
+			}
+			br.Msg = "生成计算指标失败"
+			br.Msg = "获取指标失败:Err:" + err.Error()
+			return
+		}
+		edbInfoList = append(edbInfoList, fromEdbInfo)
+
+		//关联关系表
+		{
+			calculateMappingItem := &models.EdbInfoCalculateMapping{
+				CreateTime: time.Now(),
+				ModifyTime: time.Now(),
+				Sort:       k + 1,
+				//EdbCode:        edbCode,
+				//EdbInfoId:      int(edbInfoId),
+				FromEdbInfoId:  fromEdbInfo.EdbInfoId,
+				FromEdbCode:    fromEdbInfo.EdbCode,
+				FromEdbName:    fromEdbInfo.EdbName,
+				FromSource:     fromEdbInfo.Source,
+				FromSourceName: fromEdbInfo.SourceName,
+				FromTag:        v.FromTag,
+				Source:         utils.DATA_SOURCE_PREDICT_CALCULATE,
+				SourceName:     "预测指标运算",
+			}
+			calculateMappingList = append(calculateMappingList, calculateMappingItem)
+		}
+	}
+
+	// 指标入库
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C2` + time.Now().Format("060102") + randStr
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode := utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+	edbInfo := &models.EdbInfo{
+		EdbInfoType:      1,
+		Source:           utils.DATA_SOURCE_PREDICT_CALCULATE,
+		SourceName:       "预测指标运算",
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        req.AdminId,
+		SysUserRealName:  req.AdminName,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		UniqueCode:       uniqueCode,
+		CalculateFormula: req.CalculateFormula,
+		EdbType:          2,
+	}
+	edbInfoId, err := models.AddEdbInfo(edbInfo)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,AddEdbInfo Err:" + err.Error()
+		return
+	}
+	edbInfo.EdbInfoId = int(edbInfoId)
+
+	//处理同名指标
+	{
+		edbNameList, err := models.GetEdbInfoByName(req.EdbName)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "修改指标名称失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+
+	//关联关系表入库
+	{
+		if len(calculateMappingList) > 0 {
+			for k, v := range calculateMappingList {
+				v.EdbCode = edbInfo.EdbCode
+				v.EdbInfoId = edbInfo.EdbInfoId
+				calculateMappingList[k] = v
+			}
+			go models.AddEdbInfoCalculateMappingMulti(calculateMappingList)
+		}
+	}
+
+	// 开始添加预测指标
+	err = models.AddPredictCalculate(edbInfoList, int(edbInfoId), edbCode, req.CalculateFormula, edbInfoIdBytes)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,Calculate Err:" + err.Error()
+		return
+	}
+
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  int(edbInfoId),
+		UniqueCode: uniqueCode,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// CalculateBatchSave
+// @Title 累计值转月-同比值-同差等计算新增
+// @Description 累计值转月-同比值-同差等计算新增接口
+// @Param	request	body models.EdbInfoCalculateBatchSaveReq true "type json string"
+// @Success Ret=200 返回指标id
+// @router /batch/save [post]
+func (this *PredictCalculateController) CalculateBatchSave() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.EdbInfoCalculateBatchSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	if req.FromEdbInfoId <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	var formulaInt int
+	if req.Source == utils.DATA_SOURCE_CALCULATE_NSZYDPJJS ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_HBZ ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_HCZ ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_TIME_SHIFT {
+		if req.Formula == "" {
+			br.Msg = "请填写N值"
+			return
+		}
+		formulaInt, _ = strconv.Atoi(req.Formula)
+		if formulaInt <= 0 {
+			br.Msg = "N值输入错误,请重新输入"
+			return
+		}
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_ZJPJ {
+		//直接拼接指标
+
+		//校验时间格式
+		_, err = time.ParseInLocation(utils.FormatDate, req.Formula, time.Local)
+		if err != nil {
+			br.Msg = "拼接日期有误,请重新输入"
+			return
+		}
+	}
+
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("edb_info:calculate:batch:save:", req.Source, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		br.Msg = "指标正在处理,请勿重复提交"
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			utils.Rc.Delete(redisKey)
+		}()
+	}
+	//加入缓存机制,避免创建同一个名称的指标 end
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=? "
+	pars = append(pars, req.EdbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "判断指标名称是否存在失败"
+		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		br.Msg = "指标名称已存在,请重新填写"
+		br.ErrMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	fromEdbInfo, err := models.GetEdbInfoById(req.FromEdbInfoId)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+		return
+	}
+
+	//生成指标编码
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C` + time.Now().Format("060102") + randStr
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode := utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+
+	adminId := req.AdminId
+	adminName := req.AdminName
+	var sourName string
+	var edbInfoId int
+	if req.Source == utils.DATA_SOURCE_CALCULATE_LJZZY {
+		sourName = "累计值转月值"
+		if fromEdbInfo.Frequency != "月度" {
+			br.Msg = "请选择月度指标"
+			return
+		}
+		edbInfoId, err = models.AddCalculateLjzzy(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TBZ {
+		sourName = "同比值"
+		edbInfoId, err = models.AddCalculateTbz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TCZ {
+		sourName = "同差值"
+		edbInfoId, err = models.AddCalculateTcz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_NSZYDPJJS {
+		sourName = "N数值移动平均计算"
+		edbInfoId, err = models.AddCalculateNszydpjjs(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_HBZ { //环比值
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id =? "
+		pars = append(pars, req.FromEdbInfoId)
+		condition += " AND value <=0 "
+		checkCount, err := models.GetEdbDataCount(condition, pars, fromEdbInfo.Source)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "判断环比值是否可计算失败"
+			br.ErrMsg = "判断环比值是否可计算失败,Err:" + err.Error()
+			return
+		}
+		if checkCount > 0 {
+			br.Msg = "原始数据中存在0或负数,该指标不能进行环比运算"
+			br.ErrMsg = "原始数据中出现0和负值时,提示该指标不能进行环比运算"
+			return
+		}
+		sourName = "环比值"
+		edbInfoId, err = models.AddCalculateHbz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_HCZ {
+		sourName = "环差值"
+		edbInfoId, err = models.AddCalculateHcz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_BP {
+		if fromEdbInfo.Frequency == "日度" {
+			br.Msg = "日度指标,无法进行变频操作"
+			br.ErrMsg = "日度指标,无法进行变频操作:edbcode:" + fromEdbInfo.EdbCode
+			return
+		}
+		sourName = "变频"
+		edbInfoId, err = models.AddCalculateBp(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TIME_SHIFT { //时间移位
+		sourName = "时间移位"
+		edbInfoId, err = models.AddCalculateTimeShift(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_ZJPJ { //直接拼接
+		sourName = "直接拼接"
+
+		if len(req.EdbInfoIdArr) != 1 {
+			br.Msg = "请传入拼接日期之后的指标"
+			br.ErrMsg = "请传入拼接日期之后的指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		secondEdbInfo, err := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if err != nil {
+			br.Msg = "获取拼接日期之后的指标信息失败"
+			br.ErrMsg = "获取拼接日期之后的指标信息失败:Err:" + err.Error()
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			br.Msg = "两个指标不允许为同一个"
+			br.ErrMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfoId, err = models.AddCalculateZjpj(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_LJZTBPJ { //累计值同比拼接
+		sourName = "累计值同比拼接"
+
+		if fromEdbInfo.Frequency != "月度" {
+			br.Msg = "待拼接指标只能筛选月度指标"
+			br.ErrMsg = "待拼接指标只能筛选月度指标"
+			return
+		}
+		if len(req.EdbInfoIdArr) != 1 {
+			br.Msg = "请传入同比值指标"
+			br.ErrMsg = "请传入同比值指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		tbzEdbInfo, err := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if err != nil {
+			br.Msg = "获取同比值指标信息失败"
+			br.ErrMsg = "获取同比值指标信息失败:Err:" + err.Error()
+			return
+		}
+		if tbzEdbInfo.Source != utils.DATA_SOURCE_CALCULATE_TBZ {
+			br.Msg = "指标必须是传入同比值指标类型"
+			br.ErrMsg = "指标必须是传入同比值指标类型"
+			return
+		}
+		if tbzEdbInfo.Frequency != "月度" {
+			br.Msg = "同比值指标只能筛选月度指标"
+			br.ErrMsg = "同比值指标只能筛选月度指标"
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == tbzEdbInfo.EdbInfoId {
+			br.Msg = "两个指标不允许为同一个"
+			br.ErrMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfoId, err = models.AddCalculateLjztbpj(&req, fromEdbInfo, tbzEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else {
+		br.Msg = "无效计算方式"
+		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+		return
+	}
+
+	if err != nil {
+		br.Msg = "生成" + sourName + "失败"
+		br.Msg = "生成" + sourName + "失败 Err:" + err.Error()
+		return
+	}
+	if edbInfoId <= 0 {
+		br.Msg = "生成" + sourName + "失败"
+		br.ErrMsg = "生成" + sourName + "失败,指标ID错误:" + strconv.Itoa(edbInfoId)
+		return
+	}
+	//处理同名指标
+	{
+		edbNameList, err := models.GetEdbInfoByName(req.EdbName)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "修改指标名称失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  edbInfoId,
+		UniqueCode: uniqueCode,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// Refresh
+// @Title 刷新计算指标接口
+// @Description 刷新计算指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *PredictCalculateController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+		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
+	}
+	edbInfo, err := models.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(edbInfo.Source) + "_" + req.EdbCode
+	if !utils.Rc.IsExist(cacheKey) {
+		utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+
+		startDate := req.StartDate
+		var errMsg string
+		endDate := time.Now().Format(utils.FormatDate)
+		edbInfoId := edbInfo.EdbInfoId
+		source := edbInfo.Source
+
+		switch source {
+		case utils.DATA_SOURCE_CALCULATE:
+			//startDate = edbInfo.StartDate
+			//sTime, err := time.Parse(utils.FormatDate, edbInfo.EndDate)
+			//if err != nil {
+			//	return
+			//}
+			//startDate = sTime.Format(utils.FormatDate)
+			startDate = ""
+			var edbInfoIdBytes []string
+			calculateMap, err := models.GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateDetail Err:" + err.Error()
+				break
+			}
+			var formulaStr string
+			edbInfoList := make([]*models.EdbInfo, 0)
+
+			for _, v := range calculateMap {
+				formulaStr += v.FromTag + ","
+				edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
+				edbInfo, _ := models.GetEdbInfoById(v.FromEdbInfoId)
+				edbInfoList = append(edbInfoList, edbInfo)
+			}
+			err = models.RefreshAllCalculate(edbInfoList, edbInfo.EdbInfoId, source, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, endDate, edbInfoIdBytes)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshCalculate Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_LJZZY: //刷新累计值转月值
+			calculateLjzzy, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateLjzzyDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateLjzzy.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			err = models.RefreshAllCalculateLjzzy(edbInfoId, source, fromEdbInfo, calculateLjzzy.EdbCode, startDate, endDate)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateLjzzy Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_TBZ: //刷新同比值
+			calculateTbz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateTbz.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			err = models.RefreshAllCalculateTbz(edbInfoId, source, fromEdbInfo, calculateTbz.EdbCode, startDate, endDate)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateTbz Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_TCZ: //同差值
+			calculateTcz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateTczDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateTcz.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			err = models.RefreshAllCalculateTcz(edbInfoId, source, fromEdbInfo, calculateTcz.EdbCode, startDate, endDate)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshCalculateTcz Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_NSZYDPJJS: //N数值移动平均计算
+			calculateNszydpjjs, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateNszydpjjsDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateNszydpjjs.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			formulaInt, _ := strconv.Atoi(edbInfo.CalculateFormula)
+			startDate = edbInfo.StartDate
+			err = models.RefreshAllCalculateNszydpjjs(edbInfoId, edbInfo.Source, formulaInt, fromEdbInfo, calculateNszydpjjs.EdbCode, startDate)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshCalculateNszydpjjs Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_HBZ: //刷新环比值
+			calculateTbz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateHbzDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateTbz.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			formulaInt, _ := strconv.Atoi(edbInfo.CalculateFormula)
+			err = models.RefreshAllCalculateHbz(edbInfoId, source, fromEdbInfo, calculateTbz.EdbCode, startDate, endDate, formulaInt)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateHbz Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_HCZ: //刷新环差值
+			calculateTbz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateHczDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateTbz.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			formulaInt, _ := strconv.Atoi(edbInfo.CalculateFormula)
+			err = models.RefreshAllCalculateHcz(edbInfoId, source, fromEdbInfo, calculateTbz.EdbCode, startDate, endDate, formulaInt)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateHcz Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_BP: //刷新变频
+			calculateTbz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculateTbz.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			err = models.RefreshAllCalculateBp(edbInfoId, source, fromEdbInfo, calculateTbz.EdbCode, startDate, endDate)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateBp Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
+			calculate, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
+				break
+			}
+			fromEdbInfo, err := models.GetEdbInfoById(calculate.FromEdbInfoId)
+			if err != nil {
+				errMsg = "GetEdbInfoById Err:" + err.Error()
+				break
+			}
+			startDate = edbInfo.StartDate
+			endDate = time.Now().Format(utils.FormatDate)
+			formulaInt, _ := strconv.Atoi(calculate.CalculateFormula)
+			err = models.RefreshAllCalculateTimeShift(edbInfoId, source, formulaInt, calculate.MoveType, fromEdbInfo, calculate.EdbCode, startDate, endDate, calculate.MoveFrequency)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateTimeShift Err:" + err.Error()
+			}
+		case utils.DATA_SOURCE_CALCULATE_ZJPJ: //刷新直接拼接
+			err = models.RefreshAllCalculateZjpj(edbInfo)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateZjpj Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_CALCULATE_LJZTBPJ: //刷新累计值同比拼接
+			err = models.RefreshAllCalculateLjztbpj(edbInfo)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllCalculateLjztbpj Err:" + err.Error()
+				break
+			}
+		case utils.DATA_SOURCE_PYTHON: //python代码运算
+			edbPythonCode, err := models.GetEdbPythonCodeById(edbInfo.EdbInfoId)
+			if err != nil {
+				errMsg = "获取python代码失败 Err:" + err.Error()
+				break
+			}
+			edbData, err, errMsg := services.ExecPythonCode(edbInfo.EdbCode, edbPythonCode.PythonCode)
+			if err != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "python代码获取数据失败,err:" + err.Error()
+				if errMsg != "" {
+					br.ErrMsg = errMsg
+				}
+				return
+			}
+			err = models.RefreshAllPythonEdb(edbInfo, edbData)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				errMsg = "RefreshAllPythonEdb Err:" + err.Error()
+				break
+			}
+
+		default:
+			br.Msg = "来源异常,请联系相关开发!"
+			br.ErrMsg = "来源异常,请联系相关开发"
+			return
+		}
+		if errMsg != `` {
+			br.Msg = "刷新指标失败!"
+			br.ErrMsg = "刷新指标失败,err:" + errMsg
+			return
+		}
+
+		// 更新指标最大最小值
+		err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+		if err != nil {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+	} else {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+	}
+}

+ 271 - 0
models/base_predict_from_calculate.go

@@ -0,0 +1,271 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"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"
+	"time"
+)
+
+// PredictEdbInfoCalculateSaveReq 预测计算(运算)指标请求参数
+type PredictEdbInfoCalculateSaveReq struct {
+	AdminId          int    `description:"添加人id"`
+	AdminName        string `description:"添加人名称"`
+	EdbName          string `description:"指标名称"`
+	Frequency        string `description:"频率"`
+	Unit             string `description:"单位"`
+	ClassifyId       int    `description:"分类id"`
+	CalculateFormula string `description:"计算公式"`
+	EdbInfoIdArr     []struct {
+		EdbInfoId int    `description:"指标id"`
+		FromTag   string `description:"指标对应标签"`
+	}
+}
+
+// PredictCalculateItems 预测计算(运算)指标信息
+type PredictCalculateItems struct {
+	EdbInfoId int
+	DataMap   map[string]float64
+}
+
+// AddPredictCalculate 新增预测计算(运算)指标的数据
+func AddPredictCalculate(edbInfoIdArr []*EdbInfo, edbInfoId int, edbCode, formulaStr string, edbInfoIdBytes []string) (err error) {
+	o := orm.NewOrm()
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(fmt.Sprintf("Calculate Err:%s" + err.Error()))
+		}
+	}()
+
+	saveDataMap := make(map[string]map[int]float64)
+	for _, v := range edbInfoIdArr {
+		dataList, err := GetPredictEdbDataListAll(v, 1)
+		if err != nil {
+			return err
+		}
+		dataMap := make(map[string]float64)
+		for _, dv := range dataList {
+			if val, ok := saveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp := make(map[int]float64)
+				temp[v.EdbInfoId] = dv.Value
+				saveDataMap[dv.DataTime] = temp
+			}
+		}
+		item := new(PredictCalculateItems)
+		item.EdbInfoId = v.EdbInfoId
+		item.DataMap = dataMap
+	}
+	formulaMap := services.CheckFormula(formulaStr)
+	addSql := ` INSERT INTO edb_predict_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
+	for sk, sv := range saveDataMap {
+		formulaStr = strings.ToUpper(formulaStr)
+		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		if formulaStr == "" {
+			return
+		}
+		if formulaFormStr != "" {
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, err := expression.Evaluate()
+			if err != nil {
+				err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+			calVal, err := calResult.Float64()
+			if err != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+
+			//需要存入的数据
+			{
+				dataTime, _ := time.Parse(utils.FormatDate, sk)
+				timestamp := dataTime.UnixNano() / 1e6
+				timeStr := fmt.Sprintf("%d", timestamp)
+				addSql += "("
+				addSql += strconv.Itoa(edbInfoId) + "," + "'" + edbCode + "'" + "," + "'" + sk + "'" + "," + utils.SubFloatToString(calVal, 4) + "," + "'" + nowStr + "'" +
+					"," + "'" + nowStr + "'" + "," + "'" + timeStr + "'"
+				addSql += "),"
+				isAdd = true
+			}
+		} else {
+			fmt.Println("formulaFormStr is empty")
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("AddPredictCalculate Err:" + err.Error())
+			//errMsg = " tx.Exec Err :" + err.Error()
+			return
+		}
+	}
+	return
+}
+
+// RefreshAllPredictCalculate 刷新预测计算指标的全部数据
+func RefreshAllPredictCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculate,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println(startDate, endDate)
+	saveDataMap := make(map[string]map[int]float64)
+	for _, v := range edbInfoIdArr {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, v.EdbInfoId)
+		if startDate != "" {
+			condition += " AND data_time>=? "
+			pars = append(pars, startDate)
+		}
+		//if endDate != "" {
+		//	condition += " AND data_time<=? "
+		//	pars = append(pars, endDate)
+		//}
+		//fmt.Println("v.Source:", v.Source)
+		dataList, err := GetEdbDataListAll(condition, pars, v.Source, 1)
+		if err != nil {
+			return err
+		}
+		dataMap := make(map[string]float64)
+		for _, dv := range dataList {
+			if val, ok := saveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp := make(map[int]float64)
+				temp[v.EdbInfoId] = dv.Value
+				saveDataMap[dv.DataTime] = temp
+			}
+		}
+		item := new(CalculateItems)
+		item.EdbInfoId = v.EdbInfoId
+		item.DataMap = dataMap
+	}
+
+	formulaMap := services.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
+
+	//获取指标所有数据
+	dataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&dataList)
+	if err != nil {
+		return err
+	}
+	dataMap := make(map[string]string)
+	for _, v := range dataList {
+		dataMap[v.DataTime] = v.Value
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	existDataMap := make(map[string]string)
+
+	removeDateList := make([]string, 0) //需要移除的日期
+	for sk, sv := range saveDataMap {
+		//fmt.Println(sk, sv)
+		formulaStr = strings.ToUpper(formulaStr)
+		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		if formulaFormStr != "" {
+			utils.FileLog.Info(fmt.Sprintf("formulaFormStr:%s", formulaFormStr))
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, err := expression.Evaluate()
+			if err != nil {
+				// 分母为0的报错
+				if strings.Contains(err.Error(), "divide by zero") {
+					removeDateList = append(removeDateList, sk)
+					continue
+				}
+				err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+			calVal, err := calResult.Float64()
+			if err != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+
+			saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String() //utils.SubFloatToString(calVal, 4)
+			if existVal, ok := dataMap[sk]; !ok {
+				dataTime, _ := time.Parse(utils.FormatDate, sk)
+				timestamp := dataTime.UnixNano() / 1e6
+				timeStr := fmt.Sprintf("%d", timestamp)
+
+				if _, existOk := existDataMap[sk]; !existOk {
+					addSql += GetAddSql(edbInfoIdStr, edbCode, sk, timeStr, saveValue)
+					isAdd = true
+				}
+				existDataMap[sk] = sk
+			} else {
+				existValDecimal, err := decimal.NewFromString(existVal)
+				existStr := existValDecimal.String()
+				if existStr != saveValue {
+					sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+					sql = fmt.Sprintf(sql, dataTableName)
+					_, err = o.Raw(sql, saveValue, edbInfoId, sk).Exec()
+					if err != nil {
+						return err
+					}
+				}
+			}
+		} else {
+			//计算公式异常,那么就移除该指标
+			removeDateList = append(removeDateList, sk)
+			continue
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshAllCalculate add Err", err.Error())
+			return
+		}
+	}
+
+	if len(removeDateList) > 0 {
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source)
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+
+		_, err = o.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除计算失败的计算指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}

+ 2 - 0
models/edb_data_table.go

@@ -63,6 +63,8 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_python"
 	case utils.DATA_SOURCE_GOOGLE_TRAVEL:
 		tableName = "edb_data_google_travel"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE:
+		tableName = "edb_predict_data_calculate"
 	default:
 		tableName = ""
 	}

+ 168 - 0
models/edb_info.go

@@ -34,6 +34,13 @@ type EdbInfo struct {
 	MoveFrequency    string  `description:"移动频度"`
 	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
 	ServerUrl        string  `description:"服务器地址"`
+
+	EdbInfoType int     `description:"指标类型,0:普通指标,1:预测指标"`
+	EdbNameEn   string  `description:"英文指标名称"`
+	UnitEn      string  `description:"英文单位"`
+	LatestDate  string  `description:"数据最新日期"`
+	LatestValue float64 `description:"数据最新值"`
+	ChartImage  string  `description:"图表图片"`
 }
 
 // AddEdbInfo 添加指标
@@ -297,3 +304,164 @@ func UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo *EdbInfo) (err error, errMsg stri
 	go ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(edbInfo.EdbInfoId, maxAndMinItem)
 	return
 }
+
+// GetChartPredictEdbInfoDataList 获取图表的预测指标的未来数据
+func GetChartPredictEdbInfoDataList(predictEdbConf PredictEdbConf, latestDateStr string, lastDataValue float64, endDateStr, frequency string, order int) (predictEdbInfoData []*EdbInfoSearchData, err error) {
+	endDate, err := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	latestDate, err := time.ParseInLocation(utils.FormatDate, latestDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	// 开始预测数据的时间
+	startDate := latestDate
+
+	dataValue := lastDataValue
+	if predictEdbConf.RuleType == 2 {
+		dataValue = predictEdbConf.FixedValue
+	}
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+
+	// order:1升序,其余值为降序
+	if order == 1 {
+		for k, v := range dayList {
+			predictEdbInfoData = append(predictEdbInfoData, &EdbInfoSearchData{
+				EdbDataId: predictEdbConf.PredictEdbInfoId + 10000000000 + k,
+				DataTime:  v.Format(utils.FormatDate),
+				Value:     dataValue,
+			})
+		}
+	} else {
+		lenDayList := len(dayList)
+		if lenDayList > 0 {
+			for i := lenDayList - 1; i >= 0; i-- {
+				v := dayList[i]
+				predictEdbInfoData = append(predictEdbInfoData, &EdbInfoSearchData{
+					EdbDataId: predictEdbConf.PredictEdbInfoId + 10000000000 + i,
+					DataTime:  v.Format(utils.FormatDate),
+					Value:     dataValue,
+				})
+			}
+		}
+	}
+
+	return
+}
+
+// GetPredictEdbDayList 获取预测指标日期列表
+func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
+	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
+	switch frequency {
+	case "日度":
+		for currDate := startDate.AddDate(0, 0, 1); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 1) {
+			//周六、日排除
+			if currDate.Weekday() == time.Sunday || currDate.Weekday() == time.Saturday {
+				continue
+			}
+			dayList = append(dayList, currDate)
+		}
+	case "周度":
+		//nextDate := startDate.AddDate(0, 0, 7)
+		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; currDate.Before(endDate) || currDate.Equal(endDate); {
+			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 2, -1)
+			if !currDate.After(endDate) {
+				dayList = append(dayList, currDate)
+			}
+		}
+	}
+	return
+}
+
+// GetPredictDataListByPredictEdbInfoId 根据预测指标id获取预测指标的数据,order:1升序,其余值为降序
+func GetPredictDataListByPredictEdbInfoId(edbInfoId, order int) (dataList []*EdbInfoSearchData, sourceEdbInfoItem *EdbInfo, predictEdbConf *PredictEdbConf, err error, errMsg string) {
+	edbInfo, err := GetEdbInfoById(edbInfoId)
+	if err != nil {
+		errMsg = `获取预测指标信息失败`
+		return
+	}
+	return GetPredictDataListByPredictEdbInfo(edbInfo, order)
+}
+
+// GetPredictDataListByPredictEdbInfo 根据预测指标信息获取预测指标的数据,order:1升序,其余值为降序
+func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int) (dataList []*EdbInfoSearchData, sourceEdbInfoItem *EdbInfo, predictEdbConf *PredictEdbConf, err error, errMsg string) {
+	// 查找该预测指标配置
+	predictEdbConf, err = GetPredictEdbConfById(edbInfo.EdbInfoId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取预测指标配置信息失败"
+		return
+	}
+	if predictEdbConf == nil {
+		errMsg = "获取预测指标配置信息失败"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 来源指标
+	sourceEdbInfoItem, err = GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "找不到来源指标信息"
+			err = errors.New(errMsg)
+		}
+		return
+	}
+
+	//获取指标数据(实际已生成)
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, sourceEdbInfoItem.EdbInfoId)
+	dataList, err = GetEdbDataListAll(condition, pars, sourceEdbInfoItem.Source, order)
+	if err != nil {
+		return
+	}
+
+	// 获取预测指标未来的数据
+	predictDataList := make([]*EdbInfoSearchData, 0)
+
+	endDateStr := edbInfo.EndDate //预测指标的结束日期
+
+	predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, sourceEdbInfoItem.LatestDate, sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency, order)
+	if err != nil {
+		return
+	}
+	dataList = append(dataList, predictDataList...)
+	if len(predictDataList) > 0 {
+		tmpValue := predictDataList[0]
+
+		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
+		if edbInfo.MaxValue < tmpValue.Value {
+			edbInfo.MaxValue = tmpValue.Value
+		}
+
+		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
+		if edbInfo.MinValue > tmpValue.Value {
+			edbInfo.MinValue = tmpValue.Value
+		}
+	}
+	return
+}
+
+// GetPredictEdbDataListAll 获取该预测指标所有的数据 ,order:1升序,其余值为降序
+func GetPredictEdbDataListAll(edbInfo *EdbInfo, order int) (items []*EdbInfoSearchData, err error) {
+	if edbInfo.Source == utils.DATA_SOURCE_PREDICT { //普通的预测指标是没有入库数据的,直接往配置里面获取
+		items, _, _, err, _ = GetPredictDataListByPredictEdbInfo(edbInfo, 1)
+	} else {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, edbInfo.EdbInfoId)
+		items, err = GetEdbDataListAll(condition, pars, edbInfo.Source, order)
+	}
+	return
+}

+ 27 - 0
routers/commentsRouter.go

@@ -232,6 +232,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"],
+        beego.ControllerComments{
+            Method: "CalculateBatchSave",
+            Router: `/batch/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PythonController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PythonController"],
         beego.ControllerComments{
             Method: "Add",

+ 5 - 0
routers/router.go

@@ -110,6 +110,11 @@ func init() {
 				&controllers.GoogleTravelController{},
 			),
 		),
+		beego.NSNamespace("/predict_calculate",
+			beego.NSInclude(
+				&controllers.PredictCalculateController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 1 - 0
utils/constants.go

@@ -58,6 +58,7 @@ const (
 	DATA_SOURCE_PB_FINANCE                      //彭博财务数据->28
 	DATA_SOURCE_GOOGLE_TRAVEL                   //谷歌出行数据->29
 	DATA_SOURCE_PREDICT                         //普通预测指标->30
+	DATA_SOURCE_PREDICT_CALCULATE               //预测指标运算->31
 )
 
 //基础数据初始化日期