Эх сурвалжийг харах

Merge branch 'chart/10.6'

# Conflicts:
#	models/edb_data_table.go
#	routers/router.go
#	utils/constants.go
Roc 2 жил өмнө
parent
commit
ddd91a5a19

+ 796 - 0
controllers/base_from_predict_calculate.go

@@ -0,0 +1,796 @@
+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
+}
+
+// Save
+// @Title 新增/编辑预测指标运算接口
+// @Description 新增预测指标运算接口
+// @Success 200 {object} models.EdbInfoCalculateSaveReq
+// @router /save [post]
+func (this *PredictCalculateController) Save() {
+	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
+	}
+	if req.EdbInfoId <= 0 {
+		addPredictCalculate(br, req)
+	} else {
+		editPredictCalculate(br, req)
+	}
+}
+
+// AddPredictCalculate 添加预测指标运算
+func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSaveReq) {
+
+	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)
+		}
+	}
+
+	//预先计算,判断公式是否正常
+	ok, _ := models.CheckFormula2(edbInfoList, formulaMap, calculateFormula, edbInfoIdBytes)
+	if !ok {
+		br.Msg = "生成计算指标失败,请使用正确的计算公式"
+		return
+	}
+
+	// 指标入库
+	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)
+		}
+	}
+
+	// 开始添加预测指标
+	latestDateStr, latestValue, err := models.AddPredictCalculateData(edbInfoList, int(edbInfoId), edbCode, req.CalculateFormula, edbInfoIdBytes)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,Calculate Err:" + err.Error()
+		return
+	}
+
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo, latestDateStr, latestValue)
+	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
+}
+
+// editPredictCalculate 编辑预测指标运算
+func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSaveReq) {
+	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
+		}
+	}
+
+	// 获取计算指标信息
+	edbInfo, err := models.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	// 判断指标名称是否存在
+	{
+		var condition string
+		var pars []interface{}
+		// 指标类型,0:普通指标,1:预测指标
+		condition += " AND edb_info_id !=?  AND edb_info_type=?  AND edb_name=? "
+		pars = append(pars, edbInfo.EdbInfoId, edbInfo.EdbInfoType, 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:        edbInfo.EdbCode,
+				EdbInfoId:      edbInfo.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)
+		}
+	}
+
+	//预先计算,判断公式是否正常
+	ok, _ := models.CheckFormula2(edbInfoList, formulaMap, calculateFormula, edbInfoIdBytes)
+	if !ok {
+		br.Msg = "生成计算指标失败,请使用正确的计算公式"
+		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
+				}
+			}
+		}
+	}
+
+	edbInfo.EdbName = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.CalculateFormula
+	err = models.EditPredictCalculate(edbInfo, calculateMappingList)
+	if err != nil {
+		br.Msg = "修改计算指标失败"
+		br.Msg = "修改计算指标失败,Calculate Err:" + err.Error()
+		return
+	}
+
+	// 开始添加预测指标
+	latestDateStr, latestValue, err := models.AddPredictCalculateData(edbInfoList, edbInfo.EdbInfoId, edbInfo.EdbCode, req.CalculateFormula, edbInfoIdBytes)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,Calculate Err:" + err.Error()
+		return
+	}
+
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo, latestDateStr, latestValue)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  edbInfo.EdbInfoId,
+		UniqueCode: edbInfo.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
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 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)
+
+	if req.EdbInfoId > 0 {
+		condition += " AND edb_info_id !=? "
+		pars = append(pars, req.EdbInfoId)
+	}
+
+	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
+	}
+	if fromEdbInfo.EdbInfoType != 1 {
+		br.Msg = "来源指标不是预测指标"
+		br.ErrMsg = "来源指标不是预测指标"
+		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 errMsg string
+	var edbInfo *models.EdbInfo
+
+	var latestDateStr string // 最近实际数据的日期
+	var latestValue float64  // 最近实际数据的值
+
+	if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_TBZ {
+		sourName = "预测同比"
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateTbz(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_TCZ {
+		sourName = "预测同差"
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateTcz(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else {
+		br.Msg = "无效计算方式"
+		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+		return
+	}
+
+	if err != nil {
+		br.Msg = "生成" + sourName + "失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.Msg = "生成" + sourName + "失败 Err:" + err.Error()
+		return
+	}
+	if edbInfo == nil {
+		br.Msg = "生成" + sourName + "失败"
+		br.ErrMsg = "生成" + sourName + "失败,edbInfo is nil"
+		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.UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo, latestDateStr, latestValue)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  edbInfo.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() {
+		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) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		br.IsSendEmail = false
+	}
+
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+
+	}()
+	startDate := req.StartDate
+	var errMsg string
+	edbInfoId := edbInfo.EdbInfoId
+	source := edbInfo.Source
+
+	var latestDateStr string // 最近实际数据的日期
+	var latestValue float64  // 最近实际数据的值
+	switch source {
+	case utils.DATA_SOURCE_PREDICT_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)
+		}
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculate(edbInfoList, edbInfo.EdbInfoId, source, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, edbInfoIdBytes)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshCalculate Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_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
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateTbz(edbInfoId, source, fromEdbInfo, calculateTbz.EdbCode, startDate)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllCalculateTbz Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_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
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateTcz(edbInfoId, source, fromEdbInfo, calculateTcz.EdbCode, startDate)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshCalculateTcz Err:" + err.Error()
+			break
+		}
+	default:
+		br.Msg = "来源异常,请联系相关开发!"
+		br.ErrMsg = "来源异常,请联系相关开发"
+		return
+	}
+	if errMsg != `` {
+		br.Msg = "刷新指标失败!"
+		br.ErrMsg = "刷新指标失败,err:" + errMsg
+		return
+	}
+
+	// 更新指标最大最小值
+	err, errMsg = models.UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo, latestDateStr, latestValue)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 21 - 0
models/base_from_calculate.go

@@ -17,6 +17,7 @@ import (
 type EdbInfoCalculateSaveReq struct {
 	AdminId          int    `description:"添加人id"`
 	AdminName        string `description:"添加人名称"`
+	EdbInfoId        int    `description:"指标id"`
 	EdbName          string `description:"指标名称"`
 	Frequency        string `description:"频率"`
 	Unit             string `description:"单位"`
@@ -363,3 +364,23 @@ type EdbInfoCalculateBatchEditReq struct {
 		FromTag   string `description:"指标对应标签"`
 	}
 }
+
+// CheckFormula2 校验公式是否正常(比如说除法的分母不能为0之类的,实际上就是用预设的字段数据做一次计算)
+func CheckFormula2(edbInfoArr []*EdbInfo, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string) (ok bool, err error) {
+	valArr := make(map[int]float64)
+	for _, v := range edbInfoArr {
+		valArr[v.EdbInfoId] = 100
+	}
+	formulaStr = strings.ToUpper(formulaStr)
+	formulaFormStr := ReplaceFormula(edbInfoArr, valArr, formulaMap, formulaStr, edbInfoIdBytes)
+	if formulaFormStr == "" {
+		return
+	}
+	expression := formula.NewExpression(formulaFormStr)
+	_, err = expression.Evaluate()
+	if err != nil {
+	} else {
+		ok = true
+	}
+	return
+}

+ 359 - 0
models/base_predict_from_calculate.go

@@ -0,0 +1,359 @@
+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
+}
+
+// EditPredictCalculate 新增预测计算(运算)指标
+func EditPredictCalculate(edbInfo *EdbInfo, calculateMappingList []*EdbInfoCalculateMapping) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tmpErr := to.Rollback()
+			errMsg := fmt.Sprintf("AddPredictCalculate Err:%s" + err.Error())
+			if tmpErr != nil {
+				errMsg += `;rollback失败:` + tmpErr.Error()
+			}
+			utils.FileLog.Info(errMsg)
+			return
+		}
+
+		tmpErr := to.Commit()
+		if tmpErr != nil {
+			errMsg := fmt.Sprintf("AddPredictCalculate commit Err:%s" + tmpErr.Error())
+			utils.FileLog.Info(errMsg)
+		}
+	}()
+
+	// 修改指标信息
+	_, err = to.Update(edbInfo, "EdbName", "Frequency", "Unit", "ClassifyId", "CalculateFormula")
+	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
+	}
+
+	// 插入新的关系表
+	if len(calculateMappingList) > 0 {
+		_, err = to.InsertMulti(len(calculateMappingList), calculateMappingList)
+	}
+
+	//数据表
+	tableName := GetEdbDataTableName(edbInfo.Source)
+	//清空原有数据
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = o.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// AddPredictCalculateData 新增预测计算(运算)指标的数据
+func AddPredictCalculateData(edbInfoIdList []*EdbInfo, edbInfoId int, edbCode, formulaStr string, edbInfoIdBytes []string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(fmt.Sprintf("Calculate Err:%s" + err.Error()))
+		}
+	}()
+
+	var minLatestDate time.Time
+	saveDataMap := make(map[string]map[int]float64)
+	for _, v := range edbInfoIdList {
+		tmpLatestDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		if tmpErr != nil {
+			err = errors.New(`最近的日期格式化失败;日期:` + v.LatestDate + `;err:` + tmpErr.Error())
+			return
+		}
+		// 如果不存在最小的真实数据日期,那么将当前指标的真实数据日期给赋值;
+		// 如果存在最小的真实数据日期,同时当前指标的真实数据日期 早于 最小的真实数据日期, 那么将当前指标的真实数据日期给赋值;
+		if minLatestDate.IsZero() || tmpLatestDate.Before(minLatestDate) {
+			minLatestDate = tmpLatestDate
+		}
+
+		dataList, tmpErr := GetPredictEdbDataListAll(v, 1)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		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_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
+
+	if !minLatestDate.IsZero() {
+		latestDateStr = minLatestDate.Format(utils.FormatDate)
+	}
+	for sk, sv := range saveDataMap {
+		formulaStr = strings.ToUpper(formulaStr)
+		formulaFormStr := ReplaceFormula(edbInfoIdList, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		if formulaStr == "" {
+			return
+		}
+		if formulaFormStr != "" {
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, tmpErr := expression.Evaluate()
+			if tmpErr != nil {
+				err = errors.New("计算失败:Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return
+			}
+			calVal, tmpErr := calResult.Float64()
+			if tmpErr != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return
+			}
+
+			//需要存入的数据
+			{
+				if latestDateStr == sk {
+					latestValue, _ = decimal.NewFromFloat(calVal).Truncate(4).Float64() //保留4位小数
+				}
+				dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
+				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(edbInfoIdList []*EdbInfo, edbInfoId, source int, edbCode, formulaStr, startDate string, edbInfoIdBytes []string) (latestDateStr string, latestValue float64, 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)
+
+	var minLatestDate time.Time
+	saveDataMap := make(map[string]map[int]float64)
+	for _, v := range edbInfoIdList {
+		tmpLatestDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		if tmpErr != nil {
+			err = errors.New(`最近的日期格式化失败;日期:` + v.LatestDate + `;err:` + tmpErr.Error())
+			return
+		}
+		if minLatestDate.IsZero() || tmpLatestDate.Before(minLatestDate) {
+			minLatestDate = tmpLatestDate
+		}
+
+		// 获取关联指标数据
+		dataList, tmpErr := GetPredictEdbDataListAllByStartDate(v, 1, startDate)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		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_predict_calculate (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	if !minLatestDate.IsZero() {
+		latestDateStr = minLatestDate.Format(utils.FormatDate)
+	}
+
+	//获取指标所有数据
+	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
+	}
+	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(edbInfoIdList, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		if formulaFormStr != "" {
+			utils.FileLog.Info(fmt.Sprintf("formulaFormStr:%s", formulaFormStr))
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, tmpErr := expression.Evaluate()
+			if tmpErr != nil {
+				// 分母为0的报错
+				if strings.Contains(err.Error(), "divide by zero") {
+					removeDateList = append(removeDateList, sk)
+					continue
+				}
+				err = errors.New("计算失败:Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return
+			}
+			calVal, tmpErr := calResult.Float64()
+			if tmpErr != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return
+			}
+
+			// 最近实际日期的实际数据
+			if latestDateStr == sk {
+				latestValue, _ = decimal.NewFromFloat(calVal).Truncate(4).Float64() //保留4位小数
+			}
+
+			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, tmpErr := decimal.NewFromString(existVal)
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+				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
+					}
+				}
+			}
+		} 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
+}

+ 227 - 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 添加指标
@@ -129,6 +136,7 @@ type EdbInfoMaxAndMinInfo struct {
 	MinValue    float64 `description:"最小值"`
 	MaxValue    float64 `description:"最大值"`
 	LatestValue float64 `description:"最新值"`
+	LatestDate  string  `description:"实际数据最新日期"`
 }
 
 // GetEdbInfoMaxAndMinInfo 获取指标的最新数据记录信息
@@ -297,3 +305,222 @@ func UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo *EdbInfo) (err error, errMsg stri
 	go ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(edbInfo.EdbInfoId, maxAndMinItem)
 	return
 }
+
+// UnifiedModifyPredictEdbInfoMaxAndMinInfo 统一修改预测运算指标的最大最小值
+func UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo *EdbInfo, latestDateStr string, latestValue float64) (err error, errMsg string) {
+	// 修改最大最小值
+	maxAndMinItem, err := GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.EdbCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+			return
+		}
+
+		errMsg = "刷新指标失败!"
+		err = errors.New("获取指标最大最小值失败,err:" + err.Error())
+		return
+	}
+
+	if maxAndMinItem != nil {
+		maxAndMinItem.LatestDate = latestDateStr
+		maxAndMinItem.LatestValue = latestValue
+		err = ModifyPredictEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
+		if err != nil {
+			errMsg = "刷新指标失败!"
+			err = errors.New("修改指标最大最小值失败,err:" + err.Error())
+			return
+		}
+	}
+	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, startDate string) (dataList []*EdbInfoSearchData, sourceEdbInfoItem *EdbInfo, predictEdbConf *PredictEdbConf, err error, errMsg string) {
+	edbInfo, err := GetEdbInfoById(edbInfoId)
+	if err != nil {
+		errMsg = `获取预测指标信息失败`
+		return
+	}
+	return GetPredictDataListByPredictEdbInfo(edbInfo, order, startDate)
+}
+
+// GetPredictDataListByPredictEdbInfo 根据预测指标信息获取预测指标的数据,order:1升序,其余值为降序
+func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int, startDate string) (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)
+	if startDate != "" {
+		condition += " AND data_time>=? "
+		pars = append(pars, startDate)
+	}
+	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
+}
+
+// GetPredictEdbDataListAllByStartDate 根据开始日期获取该预测指标所有的数据 ,order:1升序,其余值为降序
+func GetPredictEdbDataListAllByStartDate(edbInfo *EdbInfo, order int, startDate string) (items []*EdbInfoSearchData, err error) {
+	if edbInfo.Source == utils.DATA_SOURCE_PREDICT { //普通的预测指标是没有入库数据的,直接往配置里面获取
+		items, _, _, err, _ = GetPredictDataListByPredictEdbInfo(edbInfo, 1, startDate)
+	} else {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, edbInfo.EdbInfoId)
+		if startDate != "" {
+			condition += " AND data_time>=? "
+			pars = append(pars, startDate)
+		}
+		items, err = GetEdbDataListAll(condition, pars, edbInfo.Source, order)
+	}
+	return
+}
+
+// ModifyPredictEdbInfoMaxAndMinInfo 修改预测指标的最新数据信息
+func ModifyPredictEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.LatestDate, item.LatestValue, edbInfoId).Exec()
+	return
+}

+ 648 - 0
models/predict_edb_data_calculate_tbz.go

@@ -0,0 +1,648 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateTbz 新增/编辑 预测同比指标
+func SavePredictCalculateTbz(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateTbz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    "预测同比",
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_TBZ,
+			EdbCode:       edbCode,
+			EdbName:       edbName,
+			EdbNameSource: edbName,
+			Frequency:     frequency,
+			Unit:          unit,
+			//StartDate:        "",
+			//EndDate:          "",
+			ClassifyId:       classifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			MinValue:         0,
+			MaxValue:         0,
+			CalculateFormula: formula,
+			EdbType:          2,
+			Sort:             0,
+			MoveType:         0,
+			MoveFrequency:    "",
+			NoUpdate:         0,
+			ServerUrl:        "",
+			EdbNameEn:        "",
+			UnitEn:           "",
+			LatestDate:       "",
+			LatestValue:      0,
+			ChartImage:       "",
+		}
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		// 添加关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   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
+			}
+		}
+
+	} else {
+		edbInfo, err = GetEdbInfoById(reqEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = `获取指标信息失败`
+			}
+			return
+		}
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = `该指标不是预测指标`
+			err = errors.New(errMsg)
+			return
+		}
+
+		// 删除该指标所有的同比数据
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_TBZ)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfo.EdbInfoId)
+	// 获取源指标数据
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 0)
+	if err != nil {
+		return
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+	// 计算数据
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO edb_data_predict_calculate_tbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	for _, av := range dateArr {
+		//fmt.Println(ak, av)
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, tmpErr := time.Parse(utils.FormatDate, av)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				if _, ok := existMap[edbCode+av]; !ok {
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+					val := TbzDiv(currentItem.Value, findItem.Value)
+					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+					isAdd = true
+					utils.FileLog.Info("同期找到:" + av + ";" + preDateStr)
+
+					// 实际数据的值
+					if fromEdbInfo.LatestDate == av {
+						latestValueDecimal, tmpErr := decimal.NewFromString(val)
+						if tmpErr != nil {
+							return
+						}
+						latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+					}
+				}
+				existMap[edbCode+av] = av
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i <= 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TbzDiv(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existMap[edbCode+av] = av
+								break
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						if _, ok := existMap[edbCode+av]; !ok {
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TbzDiv(currentItem.Value, findItem.Value)
+
+							addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+							isAdd = true
+
+							// 实际数据的值
+							if fromEdbInfo.LatestDate == av {
+								latestValueDecimal, tmpErr := decimal.NewFromString(val)
+								if tmpErr != nil {
+									return
+								}
+								latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+							}
+						}
+						existMap[edbCode+av] = av
+						break
+					}
+				} else {
+					nextDateDay := preDate
+					preDateDay := preDate
+
+					for i := 0; i < 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TbzDiv(currentItem.Value, findItem.Value)
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existMap[edbCode+av] = av
+								break
+							} else {
+								//fmt.Println("pre not find:", preDateStr, "i:", i)
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				}
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// RefreshAllPredictCalculateTbz 刷新所有同比值数据
+func RefreshAllPredictCalculateTbz(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllPredictCalculateTbz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	// 获取关联指标数据
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, startDate)
+	if err != nil {
+		return
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+	//计算数据
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	fmt.Println("source:", source)
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source)
+	fmt.Println("dataTableName:", dataTableName)
+
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+	}
+	fmt.Println("existDataMap:", existDataMap)
+	addSql := ` INSERT INTO edb_data_predict_calculate_tbz (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	existAddDataMap := make(map[string]string)
+	for _, av := range dateArr {
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, tmpErr := time.Parse(utils.FormatDate, av)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				timestamp := currentDate.UnixNano() / 1e6
+				timestampStr := fmt.Sprintf("%d", timestamp)
+				val := TbzDiv(currentItem.Value, findItem.Value)
+
+				if existVal, ok := existDataMap[av]; !ok {
+					if _, existOk := existAddDataMap[av]; !existOk {
+						addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+						isAdd = true
+
+						// 实际数据的值
+						if fromEdbInfo.LatestDate == av {
+							latestValueDecimal, tmpErr := decimal.NewFromString(val)
+							if tmpErr != nil {
+								return
+							}
+							latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+						}
+					}
+					existAddDataMap[av] = av
+				} else {
+					existValDecimal, tmpErr := decimal.NewFromString(existVal)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					existStr := existValDecimal.String()
+					if existStr != val {
+						sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+						sql = fmt.Sprintf(sql, dataTableName)
+						_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+						if err != nil {
+							return
+						}
+					}
+				}
+				continue
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					for i := 0; i <= 35; i++ {
+						nextDateDay := preDate.AddDate(0, 0, i)
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TbzDiv(currentItem.Value, findItem.Value)
+
+							if existVal, ok := existDataMap[av]; !ok {
+								if _, existOk := existAddDataMap[av]; !existOk {
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existAddDataMap[av] = av
+							} else {
+								existValDecimal, tmpErr := decimal.NewFromString(existVal)
+								if tmpErr != nil {
+									err = tmpErr
+									return
+								}
+								existStr := existValDecimal.String()
+								if existStr != val {
+									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+									sql = fmt.Sprintf(sql, dataTableName)
+									_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+									if err != nil {
+										return
+									}
+								}
+							}
+							break
+						} else {
+							preDateDay := preDate.AddDate(0, 0, -i)
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+
+								if existVal, ok := existDataMap[av]; !ok {
+									if _, existOk := existAddDataMap[av]; !existOk {
+										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+										isAdd = true
+
+										// 实际数据的值
+										if fromEdbInfo.LatestDate == av {
+											latestValueDecimal, tmpErr := decimal.NewFromString(val)
+											if tmpErr != nil {
+												return
+											}
+											latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+										}
+									}
+									existAddDataMap[av] = av
+								} else {
+									existValDecimal, tmpErr := decimal.NewFromString(existVal)
+									if tmpErr != nil {
+										err = tmpErr
+										return
+									}
+									existStr := existValDecimal.String()
+									if existStr != val {
+										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+										sql = fmt.Sprintf(sql, dataTableName)
+										_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+										if err != nil {
+											return
+										}
+									}
+								}
+								break
+							}
+						}
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						timestamp := currentDate.UnixNano() / 1e6
+						timestampStr := fmt.Sprintf("%d", timestamp)
+						val := TbzDiv(currentItem.Value, findItem.Value)
+
+						if existVal, ok := existDataMap[av]; !ok {
+							if _, existOk := existAddDataMap[av]; !existOk {
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existAddDataMap[av] = av
+						} else {
+							existValDecimal, tmpErr := decimal.NewFromString(existVal)
+							if tmpErr != nil {
+								err = tmpErr
+								return
+							}
+							existStr := existValDecimal.String()
+							if existStr != val {
+								sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+								sql = fmt.Sprintf(sql, dataTableName)
+								_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+								if err != nil {
+									return
+								}
+							}
+						}
+						break
+					}
+				} else {
+					nextDateDay := preDate.AddDate(0, 0, 1)
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+
+					preDateDay := preDate.AddDate(0, 0, -1)
+					preDateDayStr := preDateDay.Format(utils.FormatDate)
+
+					for i := 0; i < 35; i++ {
+						if i >= 1 {
+							nextDateDay = nextDateDay.AddDate(0, 0, i)
+							nextDateDayStr = nextDateDay.Format(utils.FormatDate)
+						}
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TbzDiv(currentItem.Value, findItem.Value)
+
+							if existVal, ok := existDataMap[av]; !ok {
+								if _, existOk := existAddDataMap[av]; !existOk {
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existAddDataMap[av] = av
+							} else {
+								existValDecimal, tmpErr := decimal.NewFromString(existVal)
+								if tmpErr != nil {
+									err = tmpErr
+									return
+								}
+								existStr := existValDecimal.String()
+								if existStr != val {
+									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+									sql = fmt.Sprintf(sql, dataTableName)
+									_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+									if err != nil {
+										return
+									}
+								}
+							}
+							break
+						} else {
+							if i >= 1 {
+								preDateDay = preDate.AddDate(0, 0, -i)
+								preDateDayStr = nextDateDay.Format(utils.FormatDate)
+							}
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+
+								if existVal, ok := existDataMap[av]; !ok {
+									if _, existOk := existAddDataMap[av]; !existOk {
+										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+										isAdd = true
+
+										// 实际数据的值
+										if fromEdbInfo.LatestDate == av {
+											latestValueDecimal, tmpErr := decimal.NewFromString(val)
+											if tmpErr != nil {
+												return
+											}
+											latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+										}
+									}
+									existAddDataMap[av] = av
+								} else {
+									existValDecimal, tmpErr := decimal.NewFromString(existVal)
+									if tmpErr != nil {
+										err = tmpErr
+										return
+									}
+									existStr := existValDecimal.String()
+									if existStr != val {
+										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+										sql = fmt.Sprintf(sql, dataTableName)
+										_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+										if err != nil {
+											return
+										}
+									}
+								}
+								break
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 640 - 0
models/predict_edb_data_calculate_tcz.go

@@ -0,0 +1,640 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateTcz 同差值
+func SavePredictCalculateTcz(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateTcz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    "预测同差",
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_TCZ,
+			EdbCode:       edbCode,
+			EdbName:       edbName,
+			EdbNameSource: edbName,
+			Frequency:     frequency,
+			Unit:          unit,
+			//StartDate:        "",
+			//EndDate:          "",
+			ClassifyId:       classifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			MinValue:         0,
+			MaxValue:         0,
+			CalculateFormula: formula,
+			EdbType:          2,
+			Sort:             0,
+			MoveType:         0,
+			MoveFrequency:    "",
+			NoUpdate:         0,
+			ServerUrl:        "",
+			EdbNameEn:        "",
+			UnitEn:           "",
+			LatestDate:       "",
+			LatestValue:      0,
+			ChartImage:       "",
+		}
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		// 添加关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   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
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(reqEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = `获取指标信息失败`
+			}
+			return
+		}
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = `该指标不是预测指标`
+			err = errors.New(errMsg)
+			return
+		}
+
+		// 删除该指标所有的同差数据
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_TCZ)
+		fmt.Println("dataTableName:", dataTableName)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfo.EdbInfoId)
+
+	//获取源指标数据
+	fmt.Println("EdbInfoId:", fromEdbInfo.EdbInfoId)
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 0)
+	if err != nil {
+		return
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+	// 计算数据
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	//fmt.Println("Frequency:", fromEdbInfo.Frequency)
+
+	addSql := ` INSERT INTO edb_data_predict_calculate_tcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	for _, av := range dateArr {
+		//fmt.Println("dateArr:", ak, av)
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, tmpErr := time.Parse(utils.FormatDate, av)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				if _, ok := existMap[edbCode+av]; !ok {
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+
+					val := TczSub(currentItem.Value, findItem.Value)
+					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+					isAdd = true
+
+					// 实际数据的值
+					if fromEdbInfo.LatestDate == av {
+						latestValueDecimal, tmpErr := decimal.NewFromString(val)
+						if tmpErr != nil {
+							return
+						}
+						latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+					}
+				}
+				existMap[edbCode+av] = av
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i <= 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //下一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TczSub(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existMap[edbCode+av] = av
+								break
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						if _, ok := existMap[edbCode+av]; !ok {
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TczSub(currentItem.Value, findItem.Value)
+							addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+							isAdd = true
+
+							// 实际数据的值
+							if fromEdbInfo.LatestDate == av {
+								latestValueDecimal, tmpErr := decimal.NewFromString(val)
+								if tmpErr != nil {
+									return
+								}
+								latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+							}
+						}
+						existMap[edbCode+av] = av
+						break
+					}
+				} else {
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i < 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TczSub(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existMap[edbCode+av] = av
+								break
+							} else {
+
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// RefreshAllPredictCalculateTcz 刷新全部同差值数据
+func RefreshAllPredictCalculateTcz(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateTcz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	// 获取关联指标数据
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, startDate)
+	if err != nil {
+		return
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+	//计算数据
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	//获取指标所有数据
+	existDataList := 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(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+	}
+
+	addSql := ` INSERT INTO edb_data_predict_calculate_tcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	existAddDataMap := make(map[string]string)
+	for _, av := range dateArr {
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, tmpErr := time.Parse(utils.FormatDate, av)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				timestamp := currentDate.UnixNano() / 1e6
+				timestampStr := fmt.Sprintf("%d", timestamp)
+				val := TczSub(currentItem.Value, findItem.Value)
+
+				if existVal, ok := existDataMap[av]; !ok {
+					if _, existOk := existAddDataMap[av]; !existOk {
+						addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+						isAdd = true
+
+						// 实际数据的值
+						if fromEdbInfo.LatestDate == av {
+							latestValueDecimal, tmpErr := decimal.NewFromString(val)
+							if tmpErr != nil {
+								return
+							}
+							latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+						}
+					}
+					existAddDataMap[av] = av
+				} else {
+					existValDecimal, tmpErr := decimal.NewFromString(existVal)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					existStr := existValDecimal.String()
+					if existStr != val {
+						sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+						sql = fmt.Sprintf(sql, dataTableName)
+						_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+						if err != nil {
+							return
+						}
+					}
+				}
+				utils.FileLog.Info("同期找到:" + av + ";" + preDateStr)
+				continue
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					for i := 0; i <= 35; i++ {
+						nextDateDay := preDate.AddDate(0, 0, 1)
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TczSub(currentItem.Value, findItem.Value)
+
+							if existVal, ok := existDataMap[av]; !ok {
+								if _, existOk := existAddDataMap[av]; !existOk {
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existAddDataMap[av] = av
+							} else {
+								existValDecimal, tmpErr := decimal.NewFromString(existVal)
+								if tmpErr != nil {
+									err = tmpErr
+									return
+								}
+								existStr := existValDecimal.String()
+								if existStr != val {
+									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+									sql = fmt.Sprintf(sql, dataTableName)
+									_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+									if err != nil {
+										return
+									}
+								}
+							}
+							break
+						} else {
+							preDateDay := preDate.AddDate(0, 0, -1)
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								if existVal, ok := existDataMap[av]; !ok {
+									if _, existOk := existAddDataMap[av]; !existOk {
+										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+										isAdd = true
+
+										// 实际数据的值
+										if fromEdbInfo.LatestDate == av {
+											latestValueDecimal, tmpErr := decimal.NewFromString(val)
+											if tmpErr != nil {
+												return
+											}
+											latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+										}
+									}
+									existAddDataMap[av] = av
+								} else {
+									existValDecimal, tmpErr := decimal.NewFromString(existVal)
+									if tmpErr != nil {
+										err = tmpErr
+										return
+									}
+									existStr := existValDecimal.String()
+									if existStr != val {
+										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+										sql = fmt.Sprintf(sql, dataTableName)
+										_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+										if err != nil {
+											return
+										}
+									}
+								}
+								break
+							}
+						}
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						timestamp := currentDate.UnixNano() / 1e6
+						timestampStr := fmt.Sprintf("%d", timestamp)
+						val := TczSub(currentItem.Value, findItem.Value)
+
+						if existVal, ok := existDataMap[av]; !ok {
+							if _, existOk := existAddDataMap[av]; !existOk {
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+
+								// 实际数据的值
+								if fromEdbInfo.LatestDate == av {
+									latestValueDecimal, tmpErr := decimal.NewFromString(val)
+									if tmpErr != nil {
+										return
+									}
+									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+								}
+							}
+							existAddDataMap[av] = av
+						} else {
+							existValDecimal, tmpErr := decimal.NewFromString(existVal)
+							if tmpErr != nil {
+								err = tmpErr
+								return
+							}
+							existStr := existValDecimal.String()
+							if existStr != val {
+								sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+								sql = fmt.Sprintf(sql, dataTableName)
+								_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+								if err != nil {
+									return
+								}
+							}
+						}
+						break
+					}
+				} else {
+					for i := 0; i < 35; i++ {
+						nextDateDay := preDate.AddDate(0, 0, 1)
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TczSub(currentItem.Value, findItem.Value)
+
+							if existVal, ok := existDataMap[av]; !ok {
+								if _, existOk := existAddDataMap[av]; !existOk {
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+
+									// 实际数据的值
+									if fromEdbInfo.LatestDate == av {
+										latestValueDecimal, tmpErr := decimal.NewFromString(val)
+										if tmpErr != nil {
+											return
+										}
+										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+									}
+								}
+								existAddDataMap[av] = av
+							} else {
+								existValDecimal, tmpErr := decimal.NewFromString(existVal)
+								if tmpErr != nil {
+									err = tmpErr
+									return
+								}
+								existStr := existValDecimal.String()
+								if existStr != val {
+									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+									sql = fmt.Sprintf(sql, dataTableName)
+									_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+									if err != nil {
+										return
+									}
+								}
+							}
+							break
+						} else {
+							preDateDay := preDate.AddDate(0, 0, -1)
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								if existVal, ok := existDataMap[av]; !ok {
+									if _, existOk := existAddDataMap[av]; !existOk {
+										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+										isAdd = true
+
+										// 实际数据的值
+										if fromEdbInfo.LatestDate == av {
+											latestValueDecimal, tmpErr := decimal.NewFromString(val)
+											if tmpErr != nil {
+												return
+											}
+											latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+										}
+									}
+									existAddDataMap[av] = av
+								} else {
+									existValDecimal, tmpErr := decimal.NewFromString(existVal)
+									if tmpErr != nil {
+										err = tmpErr
+										return
+									}
+									existStr := existValDecimal.String()
+									if existStr != val {
+										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+										sql = fmt.Sprintf(sql, dataTableName)
+										_, err = o.Raw(sql, val, edbInfoId, av).Exec()
+										if err != nil {
+											return
+										}
+									}
+								}
+								break
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 27 - 0
routers/commentsRouter.go

@@ -250,6 +250,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: "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:PredictCalculateController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictCalculateController"],
+        beego.ControllerComments{
+            Method: "Save",
+            Router: `/save`,
+            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

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

+ 1 - 1
services/base_from_calculate.go

@@ -26,4 +26,4 @@ func CheckFormula(formula string) map[string]string {
 		}
 	}
 	return byteMap
-}
+}