Răsfoiți Sursa

Merge branch '11.3' into debug

# Conflicts:
#	routers/router.go
Roc 2 ani în urmă
părinte
comite
66875c767f

+ 254 - 0
controllers/base_from_predict.go

@@ -0,0 +1,254 @@
+package controllers
+
+import (
+	"encoding/json"
+	"fmt"
+	"hongze/hongze_edb_lib/logic"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// PredictController 预测指标
+type PredictController struct {
+	BaseAuthController
+}
+
+// Save
+// @Title 新增/编辑预测指标运算接口
+// @Description 新增预测指标运算接口
+// @Success 200 {object} models.EdbInfoCalculateSaveReq
+// @router /save [post]
+func (this *PredictController) Save() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.AddPredictEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		addPredict(br, req)
+	} else {
+		editPredict(br, req)
+	}
+}
+
+// AddPredictCalculate 添加预测指标运算
+func addPredict(br *models.BaseResponse, req models.AddPredictEdbInfoReq) {
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("predict_edb_info:calculate:batch:save:", utils.DATA_SOURCE_PREDICT, ":", 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)
+		}()
+	}
+
+	// 添加指标
+	edbInfo, err, errMsg := logic.AddPredictEdbInfo(req.SourceEdbInfoId, req.ClassifyId, req.EdbName, req.RuleList, req.AdminId, req.AdminName)
+	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
+}
+
+// editPredictCalculate 编辑预测指标运算
+func editPredict(br *models.BaseResponse, req models.AddPredictEdbInfoReq) {
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("predict_edb_info:calculate:batch:save:", utils.DATA_SOURCE_PREDICT, ":", 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)
+		}()
+	}
+
+	// 编辑指标
+	edbInfo, err, errMsg := logic.EditPredictEdbInfo(req.EdbInfoId, req.ClassifyId, req.EdbName, req.RuleList)
+	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
+}
+
+// Refresh
+// @Title 刷新计算指标接口
+// @Description 刷新计算指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *PredictController) 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 = "获取成功"
+}

+ 563 - 0
logic/predict_edb.go

@@ -0,0 +1,563 @@
+package logic
+
+import (
+	"errors"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddPredictEdbInfo 新增预测指标
+func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig, sysUserId int, sysUserName string) (edbInfo *models.EdbInfo, err error, errMsg string) {
+	var sourceEdbInfo *models.EdbInfo
+	// 来源指标信息校验
+	{
+		sourceEdbInfo, err = models.GetEdbInfoById(sourceEdbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "新增失败"
+			err = errors.New("获取来源指标失败,Err:" + err.Error())
+			return
+		}
+		if sourceEdbInfo == nil {
+			errMsg = "找不到该来源指标"
+			err = nil
+			return
+		}
+		//必须是普通的指标
+		if sourceEdbInfo.EdbInfoType != 0 {
+			errMsg = "来源指标异常,不是普通的指标"
+			return
+		}
+		if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, sourceEdbInfo.Frequency) {
+			errMsg = "预测指标只支持选择日度、周度、月度的指标"
+			return
+		}
+	}
+
+	var classifyInfo *models.EdbClassify
+	// 来源分类信息校验
+	{
+		classifyInfo, err = models.GetEdbClassifyById(classifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "新增失败"
+			err = errors.New("获取预测指标分类失败,Err:" + err.Error())
+			return
+		}
+		if classifyInfo == nil {
+			errMsg = "找不到该预测指标分类"
+			err = nil
+			return
+		}
+		//必须是预测指标分类
+		if classifyInfo.ClassifyType != 1 {
+			errMsg = "预测指标分类异常,不是预测指标分类"
+			return
+		}
+	}
+
+	edbName = strings.Trim(edbName, " ")
+
+	edbCode := sourceEdbInfo.EdbCode + "_" + time.Now().Format(utils.FormatShortDateTimeUnSpace)
+
+	//判断指标名称是否存在
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_type=? "
+	pars = append(pars, 1)
+
+	condition += " AND edb_name=? "
+	pars = append(pars, edbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断指标名称是否存在失败"
+		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 {
+		errMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	edbInfo = &models.EdbInfo{
+		//EdbInfoId:   0,
+		EdbInfoType:      1,
+		SourceName:       "预测指标",
+		Source:           utils.DATA_SOURCE_PREDICT,
+		EdbCode:          edbCode,
+		EdbName:          edbName,
+		EdbNameSource:    edbName,
+		Frequency:        sourceEdbInfo.Frequency,
+		Unit:             sourceEdbInfo.Unit,
+		StartDate:        sourceEdbInfo.StartDate,
+		ClassifyId:       classifyId,
+		SysUserId:        sysUserId,
+		SysUserRealName:  sysUserName,
+		UniqueCode:       utils.MD5(utils.DATA_PREFIX + "_" + timestamp),
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		MinValue:         sourceEdbInfo.MinValue,
+		MaxValue:         sourceEdbInfo.MaxValue,
+		CalculateFormula: sourceEdbInfo.CalculateFormula,
+		EdbType:          1,
+		//Sort:             sourceEdbInfo.,
+		LatestDate:    sourceEdbInfo.LatestDate,
+		LatestValue:   sourceEdbInfo.LatestValue,
+		MoveType:      sourceEdbInfo.MoveType,
+		MoveFrequency: sourceEdbInfo.MoveFrequency,
+		NoUpdate:      sourceEdbInfo.NoUpdate,
+		ServerUrl:     "",
+	}
+
+	// 关联关系表
+	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
+	fromEdbMap := make(map[int]int)
+
+	// 源指标关联关系表
+	calculateMappingItem := &models.EdbInfoCalculateMapping{
+		//EdbInfoCalculateMappingId: 0,
+		//EdbInfoId:                 0,
+		Source:         edbInfo.Source,
+		SourceName:     edbInfo.SourceName,
+		EdbCode:        edbInfo.EdbCode,
+		FromEdbInfoId:  sourceEdbInfo.EdbInfoId,
+		FromEdbCode:    sourceEdbInfo.EdbCode,
+		FromEdbName:    sourceEdbInfo.EdbName,
+		FromSource:     sourceEdbInfo.Source,
+		FromSourceName: sourceEdbInfo.SourceName,
+		//FromTag:        "",
+		Sort:       1,
+		CreateTime: time.Now(),
+		ModifyTime: time.Now(),
+	}
+	fromEdbMap[sourceEdbInfoId] = sourceEdbInfoId
+	calculateMappingList = append(calculateMappingList, calculateMappingItem)
+
+	// 动态环差 计算列表
+	calculateRule9List := make([]models.CalculateRule, 0)
+
+	// 预测指标配置
+	predictEdbConfList := make([]*models.PredictEdbConf, 0)
+	for _, v := range ruleList {
+		// 预测指标配置
+		ruleEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+		if tmpErr != nil {
+			errMsg = "规则配置的截止日期异常,请重新填写"
+			return
+		}
+		switch v.RuleType {
+		case 8: //N期段线性外推值
+			valInt, tmpErr := strconv.Atoi(v.Value)
+			if tmpErr != nil {
+				errMsg = "N期段线性外推值的N值异常"
+				return
+			}
+			if valInt <= 1 {
+				errMsg = "N期段线性外推值的N值必须大于1"
+				return
+			}
+		case 9: //9:动态环差
+			if v.Value == "" {
+				errMsg = "请填写计算规则"
+				return
+			}
+			formula := v.Value
+			formula = strings.Replace(formula, "(", "(", -1)
+			formula = strings.Replace(formula, ")", ")", -1)
+			formula = strings.Replace(formula, ",", ",", -1)
+			formula = strings.Replace(formula, "。", ".", -1)
+			formula = strings.Replace(formula, "%", "*0.01", -1)
+			v.Value = formula
+
+			//检验公式
+			var formulaStr string
+			var edbInfoIdBytes []string
+			for _, tmpEdbInfoId := range v.EdbInfoIdArr {
+				formulaStr += tmpEdbInfoId.FromTag + ","
+				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
+			}
+			formulaMap := services.CheckFormula(formula)
+			for _, formula := range formulaMap {
+				if !strings.Contains(formulaStr, formula) {
+					errMsg = "公式错误,请重新填写"
+					return
+				}
+			}
+
+			//关联的指标信息
+			edbInfoList := make([]*models.EdbInfo, 0)
+			// 动态环差规则 关系表
+			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)
+
+			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
+				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
+				if tmpErr != nil {
+					err = tmpErr
+					if err.Error() == utils.ErrNoRow() {
+						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
+						return
+					}
+					errMsg = "获取指标失败:Err:" + err.Error()
+					return
+				}
+
+				edbInfoList = append(edbInfoList, fromEdbInfo)
+
+				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
+				{
+					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
+						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
+						calculateMappingItem := &models.EdbInfoCalculateMapping{
+							EdbInfoCalculateMappingId: 0,
+							EdbInfoId:                 0,
+							Source:                    utils.DATA_SOURCE_CALCULATE,
+							SourceName:                "指标运算",
+							EdbCode:                   "",
+							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+							FromEdbCode:               fromEdbInfo.EdbCode,
+							FromEdbName:               fromEdbInfo.EdbName,
+							FromSource:                fromEdbInfo.Source,
+							FromSourceName:            fromEdbInfo.SourceName,
+							//FromTag:                   tmpEdbInfoId.FromTag,
+							Sort:       k + 1,
+							CreateTime: time.Now(),
+							ModifyTime: time.Now(),
+						}
+						calculateMappingList = append(calculateMappingList, calculateMappingItem)
+					}
+				}
+				// 动态环差规则 关系表
+				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
+					//PredictEdbConfCalculateMappingId: 0,
+					EdbInfoId:      0,
+					ConfigId:       0,
+					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
+					FromEdbCode:    fromEdbInfo.EdbCode,
+					FromEdbName:    fromEdbInfo.EdbName,
+					FromSource:     fromEdbInfo.Source,
+					FromSourceName: fromEdbInfo.SourceName,
+					FromTag:        tmpEdbInfoId.FromTag,
+					Sort:           k + 1,
+					CreateTime:     time.Now(),
+					ModifyTime:     time.Now(),
+				}
+				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
+			}
+			ok, _ := models.CheckFormula2(edbInfoList, formulaMap, formula, edbInfoIdBytes)
+			if !ok {
+				errMsg = "生成计算指标失败,请使用正确的计算公式"
+				return
+			}
+
+			calculateRule9List = append(calculateRule9List, models.CalculateRule{
+				TrendsCalculateMappingList: trendsMappingList,
+				EdbInfoList:                edbInfoList,
+				EdbInfoIdBytes:             edbInfoIdBytes,
+				Formula:                    formula,
+				RuleType:                   v.RuleType,
+				EndDate:                    v.EndDate,
+				EdbInfoIdArr:               v.EdbInfoIdArr,
+			})
+		}
+
+		tmpPredictEdbConf := &models.PredictEdbConf{
+			PredictEdbInfoId: 0,
+			SourceEdbInfoId:  sourceEdbInfoId,
+			RuleType:         v.RuleType,
+			//FixedValue:       v.Value,
+			Value:      v.Value,
+			EndDate:    ruleEndDate,
+			ModifyTime: time.Now(),
+			CreateTime: time.Now(),
+		}
+
+		edbInfo.EndDate = v.EndDate
+
+		predictEdbConfList = append(predictEdbConfList, tmpPredictEdbConf)
+	}
+	err = models.AddPredictEdb(edbInfo, calculateMappingList, predictEdbConfList, calculateRule9List)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("保存失败,Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// EditPredictEdbInfo 编辑预测指标
+func EditPredictEdbInfo(edbInfoId, classifyId int, edbName string, ruleList []models.RuleConfig) (edbInfo *models.EdbInfo, err error, errMsg string) {
+	// 指标信息校验
+	{
+		edbInfo, err = models.GetEdbInfoById(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "修改失败"
+			err = errors.New("获取预测指标失败,Err:" + err.Error())
+			return
+		}
+		if edbInfo == nil {
+			errMsg = "找不到该预测指标"
+			err = nil
+			return
+		}
+		//必须是普通的指标
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = "指标异常,不是预测指标"
+			return
+		}
+	}
+
+	var predictEdbConf *models.PredictEdbConf
+	// 指标配置信息校验
+	{
+		// 查找该预测指标配置
+		predictEdbConfList, tmpErr := models.GetPredictEdbConfListById(edbInfo.EdbInfoId)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			errMsg = "修改失败"
+			err = errors.New("获取预测指标配置信息失败,Err:" + tmpErr.Error())
+			return
+		}
+		if len(predictEdbConfList) == 0 {
+			errMsg = "找不到该预测指标配置"
+			err = nil
+			return
+		}
+		predictEdbConf = predictEdbConfList[0]
+	}
+
+	//判断指标名称是否存在
+	var condition string
+	var pars []interface{}
+
+	condition += " AND edb_info_id<>? "
+	pars = append(pars, edbInfoId)
+
+	condition += " AND edb_info_type=? "
+	pars = append(pars, 1)
+
+	condition += " AND edb_name=? "
+	pars = append(pars, edbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断指标名称是否存在失败"
+		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		errMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	edbInfo.EdbName = edbName
+	edbInfo.EdbNameSource = edbName
+	edbInfo.ClassifyId = classifyId
+	edbInfo.ModifyTime = time.Now()
+	updateEdbInfoCol := []string{"EdbName", "EdbNameSource", "ClassifyId", "EndDate", "ModifyTime"}
+
+	var sourceEdbInfo *models.EdbInfo
+	// 来源指标信息校验
+	{
+		sourceEdbInfo, err = models.GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "新增失败"
+			err = errors.New("获取来源指标失败,Err:" + err.Error())
+			return
+		}
+		if sourceEdbInfo == nil {
+			errMsg = "找不到该来源指标"
+			err = nil
+			return
+		}
+		//必须是普通的指标
+		if sourceEdbInfo.EdbInfoType != 0 {
+			errMsg = "来源指标异常,不是普通的指标"
+			return
+		}
+		if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, sourceEdbInfo.Frequency) {
+			errMsg = "预测指标只支持选择日度、周度、月度的指标"
+			return
+		}
+	}
+
+	// 预测指标配置
+	// 关联关系表
+	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
+	fromEdbMap := make(map[int]int)
+
+	// 源指标关联关系表
+	calculateMappingItem := &models.EdbInfoCalculateMapping{
+		//EdbInfoCalculateMappingId: 0,
+		EdbInfoId:      edbInfoId,
+		Source:         edbInfo.Source,
+		SourceName:     edbInfo.SourceName,
+		EdbCode:        edbInfo.EdbCode,
+		FromEdbInfoId:  sourceEdbInfo.EdbInfoId,
+		FromEdbCode:    sourceEdbInfo.EdbCode,
+		FromEdbName:    sourceEdbInfo.EdbName,
+		FromSource:     sourceEdbInfo.Source,
+		FromSourceName: sourceEdbInfo.SourceName,
+		//FromTag:        "",
+		Sort:       1,
+		CreateTime: time.Now(),
+		ModifyTime: time.Now(),
+	}
+	fromEdbMap[sourceEdbInfo.EdbInfoId] = sourceEdbInfo.EdbInfoId
+	calculateMappingList = append(calculateMappingList, calculateMappingItem)
+
+	// 动态环差 计算列表
+	calculateRule9List := make([]models.CalculateRule, 0)
+
+	// 预测指标配置
+	predictEdbConfList := make([]*models.PredictEdbConf, 0)
+	for _, v := range ruleList {
+		// 预测指标配置
+		ruleEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+		if tmpErr != nil {
+			errMsg = "规则配置的截止日期异常,请重新填写"
+			return
+		}
+		switch v.RuleType {
+		case 8: //N期段线性外推值
+			valInt, tmpErr := strconv.Atoi(v.Value)
+			if tmpErr != nil {
+				errMsg = "N期段线性外推值的N值异常"
+				return
+			}
+			if valInt <= 1 {
+				errMsg = "N期段线性外推值的N值必须大于1"
+				return
+			}
+		case 9: //9:动态环差
+			if v.Value == "" {
+				errMsg = "请填写计算规则"
+				return
+			}
+			formula := v.Value
+			formula = strings.Replace(formula, "(", "(", -1)
+			formula = strings.Replace(formula, ")", ")", -1)
+			formula = strings.Replace(formula, ",", ",", -1)
+			formula = strings.Replace(formula, "。", ".", -1)
+			formula = strings.Replace(formula, "%", "*0.01", -1)
+			v.Value = formula
+
+			//检验公式
+			var formulaStr string
+			var edbInfoIdBytes []string
+			for _, tmpEdbInfoId := range v.EdbInfoIdArr {
+				formulaStr += tmpEdbInfoId.FromTag + ","
+				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
+			}
+			formulaMap := services.CheckFormula(formula)
+			for _, formula := range formulaMap {
+				if !strings.Contains(formulaStr, formula) {
+					errMsg = "公式错误,请重新填写"
+					return
+				}
+			}
+
+			//关联的指标信息
+			edbInfoList := make([]*models.EdbInfo, 0)
+			// 动态环差规则 关系表
+			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)
+
+			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
+				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
+				if tmpErr != nil {
+					err = tmpErr
+					if err.Error() == utils.ErrNoRow() {
+						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
+						return
+					}
+					errMsg = "获取指标失败:Err:" + err.Error()
+					return
+				}
+
+				edbInfoList = append(edbInfoList, fromEdbInfo)
+
+				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
+				{
+					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
+						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
+						calculateMappingItem := &models.EdbInfoCalculateMapping{
+							EdbInfoCalculateMappingId: 0,
+							EdbInfoId:                 edbInfoId,
+							Source:                    utils.DATA_SOURCE_CALCULATE,
+							SourceName:                "指标运算",
+							EdbCode:                   "",
+							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+							FromEdbCode:               fromEdbInfo.EdbCode,
+							FromEdbName:               fromEdbInfo.EdbName,
+							FromSource:                fromEdbInfo.Source,
+							FromSourceName:            fromEdbInfo.SourceName,
+							//FromTag:                   tmpEdbInfoId.FromTag,
+							Sort:       k + 1,
+							CreateTime: time.Now(),
+							ModifyTime: time.Now(),
+						}
+						calculateMappingList = append(calculateMappingList, calculateMappingItem)
+					}
+				}
+				// 动态环差规则 关系表
+				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
+					//PredictEdbConfCalculateMappingId: 0,
+					EdbInfoId:      edbInfoId,
+					ConfigId:       0,
+					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
+					FromEdbCode:    fromEdbInfo.EdbCode,
+					FromEdbName:    fromEdbInfo.EdbName,
+					FromSource:     fromEdbInfo.Source,
+					FromSourceName: fromEdbInfo.SourceName,
+					FromTag:        tmpEdbInfoId.FromTag,
+					Sort:           k + 1,
+					CreateTime:     time.Now(),
+					ModifyTime:     time.Now(),
+				}
+				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
+			}
+			ok, _ := models.CheckFormula2(edbInfoList, formulaMap, formula, edbInfoIdBytes)
+			if !ok {
+				errMsg = "生成计算指标失败,请使用正确的计算公式"
+				return
+			}
+
+			calculateRule9List = append(calculateRule9List, models.CalculateRule{
+				TrendsCalculateMappingList: trendsMappingList,
+				EdbInfoList:                edbInfoList,
+				EdbInfoIdBytes:             edbInfoIdBytes,
+				Formula:                    formula,
+				RuleType:                   v.RuleType,
+				EndDate:                    v.EndDate,
+				EdbInfoIdArr:               v.EdbInfoIdArr,
+			})
+		}
+
+		tmpPredictEdbConf := &models.PredictEdbConf{
+			PredictEdbInfoId: edbInfoId,
+			SourceEdbInfoId:  sourceEdbInfo.EdbInfoId,
+			RuleType:         v.RuleType,
+			//FixedValue:       v.Value,
+			Value:      v.Value,
+			EndDate:    ruleEndDate,
+			ModifyTime: time.Now(),
+			CreateTime: time.Now(),
+		}
+
+		edbInfo.EndDate = v.EndDate
+
+		predictEdbConfList = append(predictEdbConfList, tmpPredictEdbConf)
+	}
+
+	err = models.EditPredictEdb(edbInfo, updateEdbInfoCol, calculateMappingList, predictEdbConfList, calculateRule9List)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("保存失败,Err:" + err.Error())
+		return
+	}
+	return
+}

+ 3 - 0
models/db.go

@@ -42,5 +42,8 @@ func init() {
 		new(EdbDataPython),
 		new(ChartEdbMapping),
 		new(PredictEdbConf),
+		new(EdbClassify),
+		new(PredictEdbConfCalculateMapping),
+		new(PredictEdbRuleData),
 	)
 }

+ 23 - 0
models/edb_classify.go

@@ -2,8 +2,31 @@ package models
 
 import (
 	"github.com/beego/beego/v2/client/orm"
+	"time"
 )
 
+type EdbClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyType    uint8     `description:"分类类型,0:普通指标分类,1:预测指标分类"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	HasData         int       `description:"是否含有指标数据"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	UniqueCode      string    `description:"唯一编码"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+}
+
+func GetEdbClassifyById(classifyId int) (item *EdbClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM edb_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
 type EdbClassifySimplify struct {
 	ClassifyId   int    `description:"分类id"`
 	ClassifyName string `description:"分类名称"`

+ 1 - 1
models/edb_info.go

@@ -711,7 +711,7 @@ func ModifyPredictEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo
 
 // ModifyCalculateEdbInfo 修改计算指标信息
 func ModifyCalculateEdbInfo(edbName, frequency, unit, calculateFormula string, classifyId, edbInfoId int) (err error) {
-	o := orm.NewOrmUsingDB("data")
+	o := orm.NewOrm()
 	sql := ` UPDATE  edb_info
 			SET
 			  edb_name =?,

+ 162 - 0
models/predict_edb.go

@@ -0,0 +1,162 @@
+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"
+	"strings"
+	"time"
+)
+
+// CalculateRule 预测指标 规则 计算
+type CalculateRule struct {
+	EdbInfoId                  int `description:"指标id"`
+	ConfigId                   int `description:"配置id"`
+	TrendsCalculateMappingList []*PredictEdbConfCalculateMapping
+	EdbInfoList                []*EdbInfo
+	EdbInfoIdBytes             []string
+	Formula                    string
+	RuleType                   int              `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差"`
+	EndDate                    string           `description:"截止日期"`
+	EdbInfoIdArr               []EdbInfoFromTag `description:"指标信息"`
+}
+
+// CalculateByRuleBy9 动态环差规则计算入库
+func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (err error) {
+	saveDataMap := make(map[string]map[int]float64)
+	formulaStr := strings.ToUpper(rule.Formula)
+	// 获取关联指标数据
+	for _, v := range rule.EdbInfoList {
+		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(CalculateItems)
+		item.EdbInfoId = v.EdbInfoId
+		item.DataMap = dataMap
+	}
+
+	// 添加数据
+	addDataList := make([]*PredictEdbRuleData, 0)
+
+	// 计算规则
+	formulaMap := services.CheckFormula(formulaStr)
+
+	//获取指标所有数据
+	dataList := make([]*PredictEdbRuleData, 0)
+	sql := `SELECT * FROM predict_edb_rule_data WHERE config_id = ?`
+	_, err = to.Raw(sql, rule.ConfigId).QueryRows(&dataList)
+	if err != nil {
+		return err
+	}
+	dataMap := make(map[string]*PredictEdbRuleData)
+	for _, v := range dataList {
+		dataMap[v.DataTime] = v
+	}
+	existDataMap := make(map[string]string)
+
+	removeDateList := make([]string, 0) //需要移除的日期
+	for sk, sv := range saveDataMap {
+		//fmt.Println(sk, sv)
+		formulaFormStr := ReplaceFormula(rule.EdbInfoList, sv, formulaMap, formulaStr, rule.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 existPredictEdbRuleData, ok := dataMap[sk]; !ok {
+				dataTime, _ := time.Parse(utils.FormatDate, sk)
+				timestamp := dataTime.UnixNano() / 1e6
+
+				if _, existOk := existDataMap[sk]; !existOk {
+					tmpPredictEdbRuleData := &PredictEdbRuleData{
+						//PredictEdbRuleDataId: 0,
+						EdbInfoId:     rule.EdbInfoId,
+						ConfigId:      rule.ConfigId,
+						DataTime:      sk,
+						Value:         saveValue,
+						CreateTime:    time.Now(),
+						ModifyTime:    time.Now(),
+						DataTimestamp: timestamp,
+					}
+					addDataList = append(addDataList, tmpPredictEdbRuleData)
+				}
+				existDataMap[sk] = sk
+			} else {
+				existValDecimal, err := decimal.NewFromString(existPredictEdbRuleData.Value)
+				existStr := existValDecimal.String()
+				if existStr != saveValue {
+					existPredictEdbRuleData.Value = saveValue
+					existPredictEdbRuleData.ModifyTime = time.Now()
+					_, err = to.Update(existPredictEdbRuleData, "Value", "ModifyTime")
+					if err != nil {
+						return err
+					}
+				}
+			}
+		} else {
+			//计算公式异常,那么就移除该指标
+			removeDateList = append(removeDateList, sk)
+			continue
+		}
+	}
+
+	// 添加计算出来的值入库
+	lenAddDataList := len(addDataList)
+	if lenAddDataList > 0 {
+		_, err = to.InsertMulti(lenAddDataList, addDataList)
+		if err != nil {
+			return
+		}
+	}
+
+	//删除多余的值
+	lenRemoveDateList := len(removeDateList)
+	if lenRemoveDateList > 0 {
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		sql := ` DELETE FROM predict_edb_rule_data WHERE config_id = ? and data_time in (` + utils.GetOrmInReplace(lenRemoveDateList) + `) `
+
+		_, err = to.Raw(sql, rule.ConfigId, removeDateList).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除计算失败的预测规则计算指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}

+ 241 - 0
models/predict_edb_conf.go

@@ -2,10 +2,38 @@ package models
 
 import (
 	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_edb_lib/services/alarm_msg"
 	"hongze/hongze_edb_lib/utils"
 	"time"
 )
 
+// AddPredictEdbInfoReq 添加预测指标请求
+type AddPredictEdbInfoReq struct {
+	EdbInfoId       int          `description:"指标ID"`
+	ClassifyId      int          `description:"分类id"`
+	AdminId         int          `description:"添加人id"`
+	AdminName       string       `description:"添加人名称"`
+	SourceEdbInfoId int          `description:"来源指标id"`
+	EdbName         string       `description:"指标名称"`
+	RuleList        []RuleConfig `description:"配置规则列表"`
+}
+
+// RuleConfig 预测规则配置
+type RuleConfig struct {
+	RuleType     int              `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差"`
+	Value        string           `description:"值/计算公式"`
+	EndDate      string           `description:"截止日期"`
+	EdbInfoIdArr []EdbInfoFromTag `description:"指标信息"`
+}
+
+// EditPredictEdbInfoReq 编辑预测指标请求
+type EditPredictEdbInfoReq struct {
+	EdbInfoId  int          `description:"指标ID"`
+	ClassifyId int          `description:"分类id"`
+	EdbName    string       `description:"指标名称"`
+	RuleList   []RuleConfig `description:"配置规则列表"`
+}
+
 type PredictEdbConf struct {
 	ConfigId         int       `orm:"column(config_id);pk" description:"规则id"`
 	PredictEdbInfoId int       `orm:"column(predict_edb_info_id)" description:"预测指标id"`
@@ -63,3 +91,216 @@ func ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(sourceEdbInfoId int, ite
 	}
 	return
 }
+
+// GetPredictEdbConfBySourceEdbInfoId 根据来源指标id获取配置
+func GetPredictEdbConfBySourceEdbInfoId(sourceEdbInfoId int) (item *PredictEdbConf, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM predict_edb_conf WHERE source_edb_info_id=? `
+	err = o.Raw(sql, sourceEdbInfoId).QueryRow(&item)
+	return
+}
+
+// AddPredictEdbConf 添加预测指标规则
+func AddPredictEdbConf(item *PredictEdbConf) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// AddPredictEdb 添加预测指标
+//edbInfo, calculateMappingList, predictEdbConfList,calculateRule9List,trendsMappingList
+func AddPredictEdb(item *EdbInfo, calculateMappingList []*EdbInfoCalculateMapping, predictEdbConfList []*PredictEdbConf, calculateRuleList []CalculateRule) (err error) {
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tmpErr := tx.Rollback()
+			if tmpErr != nil {
+				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
+			}
+		} else {
+			err = tx.Commit()
+		}
+	}()
+	// 新增预测指标
+	edbInfoId, err := tx.Insert(item)
+	if err != nil {
+		return
+	}
+	item.EdbInfoId = int(edbInfoId)
+
+	// 新增预测指标的关联关系
+	lenCalculateMapping := len(calculateMappingList)
+	if lenCalculateMapping > 0 {
+		for _, calculateMappingItem := range calculateMappingList {
+			calculateMappingItem.EdbInfoId = item.EdbInfoId
+		}
+		_, err = tx.InsertMulti(lenCalculateMapping, calculateMappingList)
+		if err != nil {
+			return
+		}
+	}
+
+	calculateRuleIndex := 0 // 预测计算规则下标
+
+	// 新增预测指标配置
+	for _, v := range predictEdbConfList {
+		v.PredictEdbInfoId = item.EdbInfoId
+		configId, tmpErr := tx.Insert(v)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		v.ConfigId = int(configId)
+
+		switch v.RuleType {
+		case 9: //动态环差规则
+			calculateRule := calculateRuleList[calculateRuleIndex]
+			calculateRule.ConfigId = v.ConfigId
+			calculateRule.EdbInfoId = v.PredictEdbInfoId
+
+			// 指标与规则的动态数据生成入库
+			err = CalculateByRuleBy9(tx, calculateRule)
+			if err != nil {
+				return
+			}
+
+			// 规则与指标的关系入库
+			lenTrendsCalculateMapping := len(calculateRule.TrendsCalculateMappingList)
+			if lenTrendsCalculateMapping > 0 {
+				for _, vv := range calculateRule.TrendsCalculateMappingList {
+					vv.EdbInfoId = item.EdbInfoId
+					vv.ConfigId = v.ConfigId
+				}
+				_, err = tx.InsertMulti(lenTrendsCalculateMapping, calculateRule.TrendsCalculateMappingList)
+				if err != nil {
+					return
+				}
+			}
+			calculateRuleIndex++
+
+		}
+	}
+	return
+}
+
+// EditPredictEdb 修改预测指标
+func EditPredictEdb(edbInfo *EdbInfo, updateEdbInfoCol []string, calculateMappingList []*EdbInfoCalculateMapping, predictEdbConfList []*PredictEdbConf, calculateRuleList []CalculateRule) (err error) {
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tmpErr := tx.Rollback()
+			if tmpErr != nil {
+				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
+			}
+		} else {
+			err = tx.Commit()
+		}
+	}()
+	// 修改预测指标
+	_, err = tx.Update(edbInfo, updateEdbInfoCol...)
+	if err != nil {
+		return
+	}
+
+	// 先删除原有的预测指标 与 其他指标的 关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ?`
+	_, err = tx.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 先删除原有的配置
+	sql = ` DELETE FROM predict_edb_conf WHERE predict_edb_info_id = ?`
+	_, err = tx.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 删除基础预测指标 规则配置 与 其他指标的 关联关系
+	sql = ` DELETE FROM predict_edb_conf_calculate_mapping WHERE edb_info_id = ?`
+	_, err = tx.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 删除基础预测指标 规则配置 生成的动态数据值
+	sql = ` DELETE FROM predict_edb_rule_data WHERE edb_info_id = ?`
+	_, err = tx.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增预测指标的关联关系
+	lenCalculateMapping := len(calculateMappingList)
+	if lenCalculateMapping > 0 {
+		_, err = tx.InsertMulti(lenCalculateMapping, calculateMappingList)
+		if err != nil {
+			return
+		}
+	}
+
+	calculateRuleIndex := 0 // 预测计算规则下标
+
+	// 新增预测指标配置
+	for _, v := range predictEdbConfList {
+		configId, tmpErr := tx.Insert(v)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		v.ConfigId = int(configId)
+
+		switch v.RuleType {
+		case 9: //动态环差规则
+			calculateRule := calculateRuleList[calculateRuleIndex]
+			calculateRule.ConfigId = v.ConfigId
+			calculateRule.EdbInfoId = v.PredictEdbInfoId
+
+			// 指标与规则的动态数据生成入库
+			err = CalculateByRuleBy9(tx, calculateRule)
+			if err != nil {
+				return
+			}
+
+			// 规则与指标的关系入库
+			lenTrendsCalculateMapping := len(calculateRule.TrendsCalculateMappingList)
+			if lenTrendsCalculateMapping > 0 {
+				for _, vv := range calculateRule.TrendsCalculateMappingList {
+					vv.ConfigId = v.ConfigId
+				}
+				_, err = tx.InsertMulti(lenTrendsCalculateMapping, calculateRule.TrendsCalculateMappingList)
+				if err != nil {
+					return
+				}
+			}
+			calculateRuleIndex++
+
+		}
+	}
+
+	return
+}
+
+// GetPredictEdbInfoAllCalculate 根据基础预测指标id集合 获取 所有的普通指标列表数据
+func GetPredictEdbInfoAllCalculate(edbInfoIdList []int) (list []*EdbInfo, err error) {
+	num := len(edbInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT b.* FROM predict_edb_conf AS a
+			 INNER JOIN edb_info AS b ON a.source_edb_info_id=b.edb_info_id
+             WHERE a.predict_edb_info_id in (` + utils.GetOrmInReplace(num) + `)
+			 GROUP BY a.source_edb_info_id
+			 ORDER BY a.source_edb_info_id ASC `
+	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&list)
+	return
+}

+ 21 - 0
models/predict_edb_conf_calculate_mapping.go

@@ -0,0 +1,21 @@
+package models
+
+import (
+	"time"
+)
+
+// PredictEdbConfCalculateMapping 预测基础指标规则 与 计算预测指标关联关系表
+type PredictEdbConfCalculateMapping struct {
+	PredictEdbConfCalculateMappingId int       `orm:"column(predict_edb_conf_calculate_mapping_id);pk"`
+	EdbInfoId                        int       `description:"指标id"`
+	ConfigId                         int       `description:"配置id"`
+	FromEdbInfoId                    int       `description:"基础指标id"`
+	FromEdbCode                      string    `description:"基础指标编码"`
+	FromEdbName                      string    `description:"基础指标名称"`
+	FromSource                       int       `description:"基础指标来源"`
+	FromSourceName                   string    `description:"基础指标来源名称"`
+	FromTag                          string    `description:"来源指标标签"`
+	Sort                             int       `description:"计算指标名称排序"`
+	CreateTime                       time.Time `description:"创建时间"`
+	ModifyTime                       time.Time `description:"修改时间"`
+}

+ 15 - 0
models/predict_edb_rule_data.go

@@ -0,0 +1,15 @@
+package models
+
+import "time"
+
+// PredictEdbRuleData 预测指标,动态规则的计算数据
+type PredictEdbRuleData struct {
+	PredictEdbRuleDataId int `orm:"column(predict_edb_rule_data_id);pk"`
+	EdbInfoId            int
+	ConfigId             int
+	DataTime             string
+	Value                string
+	CreateTime           time.Time
+	ModifyTime           time.Time
+	DataTimestamp        int64
+}

+ 18 - 0
routers/commentsRouter.go

@@ -313,6 +313,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictController"] = append(beego.GlobalControllerRouter["hongze/hongze_edb_lib/controllers:PredictController"],
+        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

@@ -135,6 +135,11 @@ func init() {
 				&controllers.EiaSteroController{},
 			),
 		),
+		beego.NSNamespace("/predict",
+			beego.NSInclude(
+				&controllers.PredictController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 11 - 10
utils/constants.go

@@ -8,16 +8,17 @@ const (
 
 //常量定义
 const (
-	FormatTime            = "15:04:05"            //时间格式
-	FormatDate            = "2006-01-02"          //日期格式
-	FormatDateUnSpace     = "20060102"            //日期格式
-	FormatDateTime        = "2006-01-02 15:04:05" //完整时间格式
-	FormatDateTimeUnSpace = "20060102150405"      //完整时间格式
-	PageSize15            = 15                    //列表页每页数据量
-	PageSize5             = 5
-	PageSize10            = 10
-	PageSize20            = 20
-	PageSize30            = 30
+	FormatTime                 = "15:04:05"            //时间格式
+	FormatDate                 = "2006-01-02"          //日期格式
+	FormatDateUnSpace          = "20060102"            //日期格式
+	FormatDateTime             = "2006-01-02 15:04:05" //完整时间格式
+	FormatDateTimeUnSpace      = "20060102150405"      //完整时间格式
+	FormatShortDateTimeUnSpace = "060102150405"        //省去开头两位年份的时间格式
+	PageSize15                 = 15                    //列表页每页数据量
+	PageSize5                  = 5
+	PageSize10                 = 10
+	PageSize20                 = 20
+	PageSize30                 = 30
 )
 
 const (