Browse Source

Merge remote-tracking branch 'origin/chart/13.0'

# Conflicts:
#	controllers/base_from_predict_calculate.go
#	models/edb_data_table.go
#	models/handle_data.go
#	utils/constants.go
Roc 2 years ago
parent
commit
41c4c44462

+ 131 - 28
controllers/base_from_calculate.go

@@ -85,7 +85,7 @@ func (this *CalculateController) Add() {
 		//设置3分钟缓存
 		utils.Rc.SetNX(redisKey, 1, time.Second*300)
 		defer func() {
-			utils.Rc.Delete(redisKey)
+			_ = utils.Rc.Delete(redisKey)
 		}()
 	}
 
@@ -536,9 +536,15 @@ func (this *CalculateController) BatchSave() {
 		}
 		nhccDate.StartDate = startDate
 		nhccDate.EndDate = endDate
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if req.Formula != "期末值" && req.Formula != "平均值" {
+			br.Msg = "数据取值类型错误:" + req.Formula
+			return
+		}
 	}
 
-	if fromEdbInfoId <= 0 {
+	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_CALCULATE_KSZS} // 不需要传入来源指标id的 指标类型
+	if fromEdbInfoId <= 0 && !utils.InArrayByInt(notNeedFromEdbSourceList, req.Source) {
 		br.Msg = "请选择指标"
 		return
 	}
@@ -552,7 +558,7 @@ func (this *CalculateController) BatchSave() {
 		//设置3分钟缓存
 		utils.Rc.SetNX(redisKey, 1, time.Second*300)
 		defer func() {
-			utils.Rc.Delete(redisKey)
+			_ = utils.Rc.Delete(redisKey)
 		}()
 	}
 	//加入缓存机制,避免创建同一个名称的指标 end
@@ -576,11 +582,15 @@ func (this *CalculateController) BatchSave() {
 		return
 	}
 
-	fromEdbInfo, err := models.GetEdbInfoById(fromEdbInfoId)
-	if err != nil {
-		br.Msg = "获取指标信息失败"
-		br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
-		return
+	// 来源指标
+	var fromEdbInfo *models.EdbInfo
+	if req.FromEdbInfoId > 0 {
+		fromEdbInfo, err = models.GetEdbInfoById(fromEdbInfoId)
+		if err != nil {
+			br.Msg = "获取指标信息失败"
+			br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+			return
+		}
 	}
 
 	//生成指标编码
@@ -651,12 +661,7 @@ func (this *CalculateController) BatchSave() {
 		sourName = "环差值"
 		edbInfo, err = models.AddCalculateHcz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName, formulaInt)
 	case utils.DATA_SOURCE_CALCULATE_BP:
-		//if fromEdbInfo.Frequency == "日度" {
-		//	br.Msg = "日度指标,无法进行变频操作"
-		//	br.ErrMsg = "日度指标,无法进行变频操作:edbcode:" + fromEdbInfo.EdbCode
-		//	return
-		//}
-		sourName = "变频"
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_BP
 		edbInfo, err = models.AddCalculateBp(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
 	case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
 		sourName = "时间移位"
@@ -742,6 +747,26 @@ func (this *CalculateController) BatchSave() {
 			return
 		}
 		edbInfo, err = models.AddCalculateNhcc(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, nhccDate, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if !models.CheckFrequency(fromEdbInfo.Frequency, req.Frequency) {
+			br.Msg = "频度异常,不允许低频降频到高频"
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_JP
+		edbInfo, err = models.AddCalculateJp(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_NH:
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_NH
+		edbInfo, err = models.AddCalculateNh(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_KSZS:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			br.Msg = "指标数量不能小于2个,请重新选择"
+			br.ErrMsg = "指标数量不能小于2个,请重新选择"
+			br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
+		edbInfo, err = models.AddCalculateKszs(&req, edbCode, uniqueCode, sysUserId, sysUserName)
 	default:
 		br.Msg = "无效计算方式"
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
@@ -941,19 +966,27 @@ func (this *CalculateController) BatchEdit() {
 		}
 		nhccDate.StartDate = startDate
 		nhccDate.EndDate = endDate
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if req.Formula != "期末值" && req.Formula != "平均值" {
+			br.Msg = "数据取值类型错误:" + req.Formula
+			return
+		}
 	}
 
-	// 获取基础指标信息
-	fromEdbInfo, err := models.GetEdbInfoById(fromEdbInfoId)
-	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
-			br.Msg = "指标已被删除,请刷新页面"
-			br.ErrMsg = "指标已被删除,请刷新页面:Err:" + err.Error()
+	var fromEdbInfo *models.EdbInfo
+	if fromEdbInfoId > 0 {
+		// 获取基础指标信息
+		fromEdbInfo, err = models.GetEdbInfoById(fromEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "指标已被删除,请刷新页面"
+				br.ErrMsg = "指标已被删除,请刷新页面:Err:" + err.Error()
+				return
+			}
+			br.Msg = "获取指标信息失败"
+			br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
 			return
 		}
-		br.Msg = "获取指标信息失败"
-		br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
-		return
 	}
 
 	var sourName string
@@ -1001,7 +1034,7 @@ func (this *CalculateController) BatchEdit() {
 		sourName = "环差值"
 		err = models.EditCalculateHcz(edbInfo, &req, fromEdbInfo, formulaInt, edbInfo.CalculateFormula)
 	case utils.DATA_SOURCE_CALCULATE_BP:
-		sourName = "变频"
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_BP
 		err = models.EditCalculateBp(edbInfo, &req, fromEdbInfo)
 	case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
 		sourName = "时间移位"
@@ -1086,6 +1119,30 @@ func (this *CalculateController) BatchEdit() {
 			return
 		}
 		err = models.EditCalculateNhcc(&req, edbInfo, fromEdbInfo, secondEdbInfo, nhccDate)
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if !models.CheckFrequency(fromEdbInfo.Frequency, req.Frequency) {
+			br.Msg = "频度异常,不允许低频降频到高频"
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_JP
+		err = models.EditCalculateJp(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_NH:
+		if !models.CheckFrequency(fromEdbInfo.Frequency, req.Frequency) {
+			br.Msg = "频度异常,不允许低频降频到高频"
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_NH
+		err = models.EditCalculateNh(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_KSZS:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			br.Msg = "指标数量不能小于2个,请重新选择"
+			br.ErrMsg = "指标数量不能小于2个,请重新选择"
+			br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
+		err = models.EditCalculateKszs(edbInfo, &req)
 	default:
 		br.Msg = "无效计算方式"
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
@@ -1192,7 +1249,7 @@ func (this *CalculateController) Refresh() {
 	// 刷新指标
 	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
 	defer func() {
-		utils.Rc.Delete(cacheKey)
+		_ = utils.Rc.Delete(cacheKey)
 	}()
 
 	startDate := req.StartDate
@@ -1343,7 +1400,7 @@ func (this *CalculateController) Refresh() {
 			errMsg = "RefreshAllCalculateHcz Err:" + err.Error()
 			break
 		}
-	case utils.DATA_SOURCE_CALCULATE_BP: //刷新
+	case utils.DATA_SOURCE_CALCULATE_BP: //刷新
 		calculateTbz, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
 		if err != nil {
 			errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
@@ -1451,7 +1508,53 @@ func (this *CalculateController) Refresh() {
 		}
 		err = models.RefreshAllAdjustEdb(edbInfo, fromEdbInfo)
 		if err != nil && err.Error() != utils.ErrNoRow() {
-			errMsg = "RefreshAllCalculateLjzzy Err:" + err.Error()
+			errMsg = "RefreshAllAdjustEdb Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_CALCULATE_JP: //刷新降频
+		calculateMapping, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
+			break
+		}
+
+		fromEdbInfo, err := models.GetEdbInfoById(calculateMapping.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		//startDate = edbInfo.StartDate
+		endDate = time.Now().Format(utils.FormatDate)
+		err = models.RefreshAllCalculateJp(edbInfoId, source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllCalculateJp Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_CALCULATE_NH: //刷新年化
+		calculateMapping, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTbzDetail Err:" + err.Error()
+			break
+		}
+
+		fromEdbInfo, err := models.GetEdbInfoById(calculateMapping.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		//startDate = edbInfo.StartDate
+		endDate = time.Now().Format(utils.FormatDate)
+		err = models.RefreshAllCalculateNh(edbInfoId, source, fromEdbInfo, edbInfo.EdbCode)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllCalculateNh Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_CALCULATE_KSZS: //刷新扩散指数
+		//startDate = edbInfo.StartDate
+		endDate = time.Now().Format(utils.FormatDate)
+		err = models.RefreshAllCalculateKszs(edbInfo)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllCalculateKszs Err:" + err.Error()
 			break
 		}
 	default:
@@ -1535,7 +1638,7 @@ func (this *CalculateController) SaveAdjust() {
 		//设置3分钟缓存
 		utils.Rc.SetNX(redisKey, 1, time.Second*300)
 		defer func() {
-			utils.Rc.Delete(redisKey)
+			_ = utils.Rc.Delete(redisKey)
 		}()
 	}
 

+ 71 - 0
controllers/base_from_predict_calculate.go

@@ -517,6 +517,14 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		return
 	}
 
+	// 基础指标id
+	fromEdbInfoId := req.FromEdbInfoId
+	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS} // 不需要传入来源指标id的 指标类型
+	if fromEdbInfoId <= 0 && !utils.InArrayByInt(notNeedFromEdbSourceList, req.Source) {
+		br.Msg = "请选择指标"
+		return
+	}
+
 	//加入缓存机制,避免创建同一个名称的指标 start
 	redisKey := fmt.Sprint("edb_lib:predict_calculate:batch:save:", req.Source, ":", req.EdbName)
 	isExist := utils.Rc.IsExist(redisKey)
@@ -555,6 +563,22 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		return
 	}
 
+	// 来源预测指标信息
+	var fromEdbInfo *models.EdbInfo
+	if req.FromEdbInfoId > 0 {
+		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
@@ -723,6 +747,15 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 			return
 		}
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateNhcc(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, nhccDate, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_JP {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_JP
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateJp(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_NH {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_NH
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateNh(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_KSZS {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateKszs(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, req.EdbInfoIdArr, edbCode, uniqueCode, adminId, adminName)
 	} else {
 		br.Msg = "无效计算方式"
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
@@ -1054,6 +1087,44 @@ func (this *PredictCalculateController) Refresh() {
 			errMsg = "RefreshAllPredictCalculateNhcc Err:" + err.Error()
 			break
 		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_JP: //降频
+		calculateInfo, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTczDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculateInfo.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateJp(edbInfoId, source, fromEdbInfo, calculateInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateJp Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_NH: //年化
+		calculateInfo, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTczDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculateInfo.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateNh(edbInfoId, source, fromEdbInfo, calculateInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateNh Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS: //扩散指数
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateKszs(edbInfo)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateKszs Err:" + err.Error()
+			break
+		}
 	default:
 		br.Msg = "来源异常,请联系相关开发!"
 		br.ErrMsg = "来源异常,请联系相关开发"

+ 23 - 20
models/base_from_calculate.go

@@ -581,25 +581,28 @@ func GetFormulaMap() map[string]string {
 
 // EdbInfoCalculateBatchSaveReq 添加计算指标的请求参数
 type EdbInfoCalculateBatchSaveReq struct {
-	AdminId          int    `description:"添加人id"`
-	AdminName        string `description:"添加人名称"`
-	EdbInfoId        int    `description:"指标id"`
-	EdbName          string `description:"指标名称"`
-	Frequency        string `description:"频度"`
-	Unit             string `description:"单位"`
-	ClassifyId       int    `description:"分类id"`
-	Formula          string `description:"N值/移动天数"`
-	FromEdbInfoId    int    `description:"计算来源指标id"`
-	Source           int    `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:变频"`
-	CalculateFormula string `description:"计算公式"`
-	EdbInfoIdArr     []struct {
-		EdbInfoId int    `description:"指标id"`
-		FromTag   string `description:"指标对应标签"`
-		MoveValue int    `description:"移动的值"`
-	}
-	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
-	Calendar      string `description:"公历/农历"`
+	AdminId          int                            `description:"添加人id"`
+	AdminName        string                         `description:"添加人名称"`
+	EdbInfoId        int                            `description:"指标id"`
+	EdbName          string                         `description:"指标名称"`
+	Frequency        string                         `description:"频度"`
+	Unit             string                         `description:"单位"`
+	ClassifyId       int                            `description:"分类id"`
+	Formula          string                         `description:"N值/移动天数"`
+	FromEdbInfoId    int                            `description:"计算来源指标id"`
+	Source           int                            `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:升频"`
+	CalculateFormula string                         `description:"计算公式"`
+	EdbInfoIdArr     []EdbInfoCalculateEdbInfoIdReq `description:"关联指标列表"`
+	MoveType         int                            `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年"`
+	Calendar         string                         `description:"公历/农历"`
+}
+
+// EdbInfoCalculateEdbInfoIdReq 新增/编辑请求 关联的指标列表
+type EdbInfoCalculateEdbInfoIdReq struct {
+	EdbInfoId int    `description:"指标id"`
+	FromTag   string `description:"指标对应标签"`
+	MoveValue int    `description:"移动的值"`
 }
 
 // EdbInfoCalculateBatchEditReq 编辑计算指标的请求参数
@@ -611,7 +614,7 @@ type EdbInfoCalculateBatchEditReq struct {
 	Formula       string `description:"N值"`
 	EdbInfoId     int    `description:"编辑指标id"`
 	FromEdbInfoId int    `description:"计算来源指标id"`
-	Source        int    `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:频"`
+	Source        int    `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:频"`
 	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
 	EdbInfoIdArr  []struct {

+ 9 - 9
models/edb_data_calculate_bp.go

@@ -11,7 +11,7 @@ import (
 	"time"
 )
 
-// AddCalculateBp 
+// AddCalculateBp 
 func AddCalculateBp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
@@ -29,7 +29,7 @@ func AddCalculateBp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edb
 	if req.EdbInfoId <= 0 {
 		edbInfo = new(EdbInfo)
 		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_BP
-		edbInfo.SourceName = "变频"
+		edbInfo.SourceName = utils.DATA_SOURCE_NAME_CALCULATE_BP
 		edbInfo.EdbCode = edbCode
 		edbInfo.EdbName = req.EdbName
 		edbInfo.EdbNameSource = req.EdbName
@@ -91,7 +91,7 @@ func AddCalculateBp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edb
 	return
 }
 
-// EditCalculateBp 修改频数据
+// EditCalculateBp 修改频数据
 func EditCalculateBp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEdbInfo *EdbInfo) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
@@ -153,7 +153,7 @@ func EditCalculateBp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEd
 			EdbInfoCalculateMappingId: 0,
 			EdbInfoId:                 edbInfo.EdbInfoId,
 			Source:                    utils.DATA_SOURCE_CALCULATE_BP,
-			SourceName:                "变频",
+			SourceName:                utils.DATA_SOURCE_NAME_CALCULATE_BP,
 			EdbCode:                   edbInfo.EdbCode,
 			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
 			FromEdbCode:               fromEdbInfo.EdbCode,
@@ -177,7 +177,7 @@ func EditCalculateBp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEd
 	return
 }
 
-// RefreshAllCalculateBpBak 刷新所有频数据
+// RefreshAllCalculateBpBak 刷新所有频数据
 func RefreshAllCalculateBpBak(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate string) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
@@ -339,7 +339,7 @@ func RefreshAllCalculateBp(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode,
 	return
 }
 
-// refreshAllCalculateBp 刷新频数据
+// refreshAllCalculateBp 刷新频数据
 func refreshAllCalculateBp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate string, order int) (err error) {
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 
@@ -373,7 +373,7 @@ func refreshAllCalculateBp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *E
 	}
 	fmt.Println("source:", source)
 
-	//获取频指标所有数据
+	//获取频指标所有数据
 	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
 	if err != nil {
 		return
@@ -474,7 +474,7 @@ func refreshAllCalculateBp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *E
 			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ?`, tableName)
 			_, err = to.Raw(sql, edbInfoId).Exec()
 			if err != nil {
-				err = fmt.Errorf("删除所有的频指标数据失败,Err:" + err.Error())
+				err = fmt.Errorf("删除所有的频指标数据失败,Err:" + err.Error())
 				return
 			}
 
@@ -541,7 +541,7 @@ func refreshAllCalculateBp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *E
 
 		_, err = to.Raw(sql, edbInfoId).Exec()
 		if err != nil {
-			err = fmt.Errorf("删除不存在的频指标数据失败,Err:" + err.Error())
+			err = fmt.Errorf("删除不存在的频指标数据失败,Err:" + err.Error())
 			return
 		}
 	}

+ 464 - 0
models/edb_data_calculate_jp.go

@@ -0,0 +1,464 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// CheckFrequency 判断是否可以降频
+func CheckFrequency(fromEdbFrequency, edbFrequency string) (ok bool) {
+	frequencyList := make([]string, 0)
+	switch fromEdbFrequency {
+	case "日度":
+		frequencyList = []string{"日度"}
+	case "周度":
+		frequencyList = []string{"日度", "周度"}
+	case "旬度":
+		frequencyList = []string{"日度", "周度", "旬度"}
+	case "月度":
+		frequencyList = []string{"日度", "周度", "旬度", "月度"}
+	case "季度":
+		frequencyList = []string{"日度", "周度", "旬度", "月度", "季度"}
+	default:
+		return
+	}
+
+	// 如果存在,那么就标识不允许降频
+	if utils.InArrayByStr(frequencyList, edbFrequency) {
+		return
+	}
+	ok = true
+
+	return
+}
+
+// AddCalculateJp 降频
+func AddCalculateJp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo = new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_JP
+		edbInfo.SourceName = utils.DATA_SOURCE_NAME_CALCULATE_JP
+		edbInfo.EdbCode = edbCode
+		edbInfo.EdbName = req.EdbName
+		edbInfo.EdbNameSource = req.EdbName
+		edbInfo.Frequency = req.Frequency
+		edbInfo.Unit = req.Unit
+		edbInfo.ClassifyId = req.ClassifyId
+		edbInfo.SysUserId = sysUserId
+		edbInfo.SysUserRealName = sysUserRealName
+		edbInfo.CreateTime = time.Now()
+		edbInfo.ModifyTime = time.Now()
+		edbInfo.UniqueCode = uniqueCode
+		edbInfo.CalculateFormula = req.Formula
+		edbInfo.EdbType = 2
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+			calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+			calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+			calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+			calculateMappingItem.FromSource = fromEdbInfo.Source
+			calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+			calculateMappingItem.FromTag = ""
+			calculateMappingItem.Source = edbInfo.Source
+			calculateMappingItem.SourceName = edbInfo.SourceName
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_JP)
+		fmt.Println("dataTableName:" + dataTableName)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+
+	return
+}
+
+// EditCalculateJp 修改降频数据
+func EditCalculateJp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEdbInfo *EdbInfo) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	//判断计算指标是否被更换
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需重新计算
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    edbInfo.Source,
+			SourceName:                edbInfo.SourceName,
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+
+	return
+}
+
+func RefreshAllCalculateJp(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = refreshAllCalculateJp(to, edbInfoId, source, fromEdbInfo, edbCode, edbFrequency, formula)
+
+	return
+}
+
+// refreshAllCalculateJp 刷新降频数据
+func refreshAllCalculateJp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, fromEdbInfo.EdbInfoId)
+
+	//if startDate != "" {
+	//	condition += " AND data_time>=? "
+	//	pars = append(pars, startDate)
+	//}
+	//if endDate != "" {
+	//	condition += " AND data_time<=? "
+	//	pars = append(pars, endDate)
+	//}
+	//获取来源指标的数据
+	dataList, err := GetEdbDataListAllByTo(to, condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return err
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	fromDataMap := make(map[string]float64)
+	//来源指指标数据
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+		fromDataMap[v.DataTime] = v.Value
+	}
+	fmt.Println("source:", source)
+
+	//获取降频指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	//existDataMap := make(map[string]string, 0)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+	}
+
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_JP)
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	//existMap := make(map[string]string)
+	dataLen := len(dataList)
+	if dataLen <= 0 {
+		return
+	}
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[dataLen-1].DataTime, time.Local)
+
+	var lastValue float64     // 最近的值
+	var nextEndDate time.Time // 下一个节点的日期
+	weekDayDataList := make([]float64, 0)
+	for tmpStartDataTime := startDataTime; !tmpStartDataTime.After(endDataTime); tmpStartDataTime = tmpStartDataTime.AddDate(0, 0, 1) {
+		// 将当前数据加入到 weekDayDataList
+		if tmpData, ok := dataMap[tmpStartDataTime.Format(utils.FormatDate)]; ok {
+			tmpValue := decimal.NewFromFloat(tmpData.Value)
+			tmpValueFloat, _ := tmpValue.Round(4).Float64()
+			weekDayDataList = append(weekDayDataList, tmpValueFloat)
+		}
+		// 如果下个节点的日期不存在,那么就先给赋值(兼容时间区间内只有一组数据的情况)
+		if nextEndDate.IsZero() {
+			nextEndDate = utils.GetFrequencyEndDay(tmpStartDataTime, edbFrequency)
+		}
+
+		// 日期处理过滤
+		switch edbFrequency {
+		case "周度":
+			if tmpStartDataTime.Weekday() != 0 {
+				//不是周日,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = tmpStartDataTime.AddDate(0, 0, 7)
+			}
+		case "旬度":
+			nextDay := tmpStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 && nextDay.Day() != 11 && nextDay.Day() != 21 {
+				//不是每月10、20、最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				if nextDay.Day() == 1 || nextDay.Day() == 11 {
+					//月初或者月末的时候,加10天就好了
+					nextEndDate = nextDay.AddDate(0, 0, 9)
+				} else {
+					tmpNextMonth := nextDay.AddDate(0, 1, 0)
+					nextEndDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+				}
+			}
+		case "月度":
+			nextDay := tmpStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 {
+				//不是每月最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 1, -1)
+			}
+		case "季度":
+			nextDay := tmpStartDataTime.AddDate(0, 0, 1)
+			if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 3, -1)
+			} else {
+				//不是3,6,9,12 月份的最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		case "年度":
+			if tmpStartDataTime.Month() == 12 && tmpStartDataTime.Day() == 31 {
+				//记录下一个结束节点的日期
+				nextEndDate = tmpStartDataTime.AddDate(1, 0, 0)
+			} else {
+				//不是每年的12-31日,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		default:
+			err = errors.New("错误的频度:" + edbFrequency)
+			return
+		}
+
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) <= 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[tmpStartDataTime.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := tmpStartDataTime.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, tmpStartDataTime.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		// 一轮结束后,数据清空
+		weekDayDataList = make([]float64, 0)
+	}
+
+	// 最后已有的日期处理完成后,需要对剩余不在时间段内的数据做处理
+	if len(weekDayDataList) > 0 {
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) < 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[nextEndDate.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := nextEndDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, nextEndDate.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	return
+}

+ 392 - 0
models/edb_data_calculate_kszs.go

@@ -0,0 +1,392 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateKszs 扩散指数
+func AddCalculateKszs(req *EdbInfoCalculateBatchSaveReq, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = utils.DATA_SOURCE_CALCULATE_KSZS
+	edbInfo.SourceName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = sysUserId
+	edbInfo.SysUserRealName = sysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = uniqueCode
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbType = 2
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for _, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = refreshAllCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbInfo.EdbCode, edbInfo.CalculateFormula, tagMap)
+
+	return
+}
+
+// EditCalculateKszs 修改扩散指数数据
+func EditCalculateKszs(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for _, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbInfo.EdbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = refreshAllCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbInfo.EdbCode, edbInfo.CalculateFormula, tagMap)
+
+	return
+}
+
+func RefreshAllCalculateKszs(edbInfo *EdbInfo) (err error) {
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	for _, v := range edbInfoCalculateDetailList {
+		tagMap[v.FromTag] = v.FromEdbInfoId
+		fromEdbInfo, _ := GetEdbInfoById(v.FromEdbInfoId)
+		relationEdbInfoList = append(relationEdbInfoList, fromEdbInfo)
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = refreshAllCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbInfo.EdbCode, edbInfo.CalculateFormula, tagMap)
+
+	return
+}
+
+// KszsConfig 扩散指数配置
+type KszsConfig struct {
+	DateType  int      `description:"扩散指标日期;1:全部指标日期并集;2:部分指标日期并集"`
+	CheckList []string `description:"选中的数据,A,B,C"`
+}
+
+// refreshAllCalculateKszs 刷新扩散指数数据
+func refreshAllCalculateKszs(to orm.TxOrmer, edbInfoId, source int, relationEdbInfoList []*EdbInfo, edbCode, calculateFormula string, tagMap map[string]int) (err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_KSZS)
+
+	// 获取扩散指标关联的指标id
+	checkEdbInfoIdMap := make(map[int]int)
+	{
+		var config KszsConfig
+		err = json.Unmarshal([]byte(calculateFormula), &config)
+		if err != nil {
+			return
+		}
+		if config.DateType == 1 {
+			for _, tmpEdbInfoId := range tagMap {
+				checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+			}
+		} else {
+			for _, v := range config.CheckList {
+				if tmpEdbInfoId, ok := tagMap[v]; ok {
+					checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+				}
+			}
+		}
+	}
+
+	//获取扩散指数指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	//获取来源指标的数据
+	relationEdbDataMap := make(map[int]map[string]float64)
+	// 获取选择指标的 需要数据的 开始日期和结束日期
+	var startDate, endDate time.Time
+	for _, v := range relationEdbInfoList {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, v.EdbInfoId)
+		tmpDataList, tmpErr := GetEdbDataListAllByTo(to, condition, pars, v.Source, 1)
+		if tmpErr != nil {
+			err = tmpErr
+			return err
+		}
+		if tmpDataList != nil {
+			if _, ok2 := checkEdbInfoIdMap[v.EdbInfoId]; ok2 {
+				lenTmpDataList := len(tmpDataList)
+				if lenTmpDataList > 0 {
+					tmpStartTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[0].DataTime, time.Local)
+					tmpEndTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[lenTmpDataList-1].DataTime, time.Local)
+
+					if startDate.IsZero() || tmpStartTime.Before(startDate) {
+						startDate = tmpStartTime
+					}
+
+					if tmpEndTime.IsZero() || tmpEndTime.After(endDate) {
+						endDate = tmpEndTime
+					}
+				}
+			}
+			// 用上期的数据补充当期的数据处理
+			handleDataMap := make(map[string]float64)
+			err = HandleDataByPreviousData(tmpDataList, handleDataMap)
+			if err != nil {
+				return
+			}
+			relationEdbDataMap[v.EdbInfoId] = handleDataMap
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for currDate := startDate.AddDate(0, 0, 1); !currDate.After(endDate); currDate = currDate.AddDate(0, 0, 1) {
+		currDateStr := currDate.Format(utils.FormatDate)
+
+		//环差指数列表
+		tmpValList := make([]float64, 0)
+		for _, dataMap := range relationEdbDataMap {
+			currVal, ok := dataMap[currDateStr]
+			if !ok {
+				continue
+			}
+
+			perVal, ok := dataMap[currDate.AddDate(0, 0, -1).Format(utils.FormatDate)]
+			if !ok {
+				continue
+			}
+
+			var tmpVal float64
+			if currVal > perVal {
+				tmpVal = 1
+			} else if currVal == perVal {
+				tmpVal = 0.5
+			} else {
+				tmpVal = 0
+			}
+			tmpValList = append(tmpValList, tmpVal)
+		}
+
+		lenTmpValList := len(tmpValList)
+		if lenTmpValList <= 0 {
+			continue
+		}
+
+		currValDeci := decimal.NewFromFloat(0)
+		for _, tmpVal := range tmpValList {
+			currValDeci = currValDeci.Add(decimal.NewFromFloat(tmpVal))
+		}
+		currVal, _ := currValDeci.Div(decimal.NewFromInt(int64(lenTmpValList))).Round(4).Float64()
+
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除扩散指数指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}

+ 370 - 0
models/edb_data_calculate_nh.go

@@ -0,0 +1,370 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateNh 年化
+func AddCalculateNh(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo = new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_NH
+		edbInfo.SourceName = utils.DATA_SOURCE_NAME_CALCULATE_NH
+		edbInfo.EdbCode = edbCode
+		edbInfo.EdbName = req.EdbName
+		edbInfo.EdbNameSource = req.EdbName
+		edbInfo.Frequency = req.Frequency
+		edbInfo.Unit = req.Unit
+		edbInfo.ClassifyId = req.ClassifyId
+		edbInfo.SysUserId = sysUserId
+		edbInfo.SysUserRealName = sysUserRealName
+		edbInfo.CreateTime = time.Now()
+		edbInfo.ModifyTime = time.Now()
+		edbInfo.UniqueCode = uniqueCode
+		edbInfo.CalculateFormula = req.Formula
+		edbInfo.EdbType = 2
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+			calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+			calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+			calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+			calculateMappingItem.FromSource = fromEdbInfo.Source
+			calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+			calculateMappingItem.FromTag = ""
+			calculateMappingItem.Source = edbInfo.Source
+			calculateMappingItem.SourceName = edbInfo.SourceName
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_NH)
+		fmt.Println("dataTableName:" + dataTableName)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = refreshAllCalculateNh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+// EditCalculateNh 修改年化数据
+func EditCalculateNh(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEdbInfo *EdbInfo) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	//判断计算指标是否被更换
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+	if count > 0 { // 指标未被替换,无需重新计算
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+	//关联关系
+	{
+		calculateMappingItem := &EdbInfoCalculateMapping{
+			EdbInfoCalculateMappingId: 0,
+			EdbInfoId:                 edbInfo.EdbInfoId,
+			Source:                    edbInfo.Source,
+			SourceName:                edbInfo.SourceName,
+			EdbCode:                   edbInfo.EdbCode,
+			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+			FromEdbCode:               fromEdbInfo.EdbCode,
+			FromEdbName:               fromEdbInfo.EdbName,
+			FromSource:                fromEdbInfo.Source,
+			FromSourceName:            fromEdbInfo.SourceName,
+			FromTag:                   "",
+			Sort:                      1,
+			CreateTime:                time.Now(),
+			ModifyTime:                time.Now(),
+		}
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//计算数据
+	err = refreshAllCalculateNh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode)
+
+	return
+}
+
+func RefreshAllCalculateNh(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = refreshAllCalculateNh(to, edbInfoId, source, fromEdbInfo, edbCode)
+
+	return
+}
+
+// refreshAllCalculateNh 刷新年化数据
+func refreshAllCalculateNh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_NH)
+
+	//获取年化指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	pars = append(pars, fromEdbInfo.EdbInfoId)
+
+	//获取来源指标的数据
+	fromDataList, err := GetEdbDataListAllByTo(to, condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return err
+	}
+
+	// 插值法数据处理
+	handleDataMap := make(map[string]float64)
+	err = HandleDataByLinearRegression(fromDataList, handleDataMap)
+	if err != nil {
+		return
+	}
+
+	lenFromDataList := len(fromDataList)
+	// 如果来源指标没有数据,那么就直接返回得了
+	if lenFromDataList <= 0 {
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? `, tableName)
+
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除年化指标数据失败,Err:" + err.Error())
+			return
+		}
+		return
+	}
+	// 每年的最后一天的数据值
+	yearLastValMap := make(map[int]float64)
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[lenFromDataList-1].DataTime, time.Local)
+	for i := startDataTime.Year(); i <= endDataTime.Year(); i++ {
+		tmpDateStr := fmt.Sprintf("%d-12-31", i)
+		if tmpVal, ok := handleDataMap[tmpDateStr]; ok {
+			yearLastValMap[i] = tmpVal
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//来源指指标数据
+	for _, v := range fromDataList {
+		currDateStr := v.DataTime
+		currDate, _ := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+
+		perValMap := make(map[time.Time]float64)
+		//前3年当日的数据
+		for i := 1; i <= 3; i++ {
+			tmpDateTime := currDate.AddDate(-i, 0, 0)
+			if tmpVal, ok := handleDataMap[tmpDateTime.Format(utils.FormatDate)]; ok {
+				perValMap[tmpDateTime] = tmpVal
+			}
+		}
+		lenPerValMap := len(perValMap)
+		// 如果数据少于2年,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenPerValMap < 2 {
+			continue
+		}
+
+		// N年 当前值占全年比重 的值列表
+		divValList := make([]decimal.Decimal, 0)
+		for tmpDateTime, tmpVal := range perValMap {
+			yearLastVal, ok2 := yearLastValMap[tmpDateTime.Year()]
+			// 如果当年最后一天没有数据
+			if !ok2 {
+				continue
+			}
+
+			// 当前值占全年比重
+			tmpYearLastVal := decimal.NewFromFloat(yearLastVal)
+			if tmpYearLastVal.IsZero() { //如果是0,那么就退出当前循环,进入下一个循环
+				continue
+			}
+			divVal := decimal.NewFromFloat(tmpVal).Div(tmpYearLastVal)
+			divValList = append(divValList, divVal)
+		}
+
+		lenDivValList := len(divValList)
+		// 如果 N年 当前值占全年比重 的值 小于 2个,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenDivValList < 2 {
+			continue
+		}
+
+		divValSum := decimal.NewFromFloat(0)
+		for _, divVal := range divValList {
+			divValSum = divValSum.Add(divVal)
+		}
+
+		// 当前计算出来的结果
+		tmpDivVal := divValSum.Div(decimal.NewFromInt(int64(lenDivValList)))
+		if tmpDivVal.IsZero() { //如果是0,那么就退出当前循环,进入下一个循环
+			continue
+		}
+		currVal, _ := decimal.NewFromFloat(v.Value).Div(tmpDivVal).Round(4).Float64()
+
+		// 判断年化指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理年化数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断年化数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除年化指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}

+ 1 - 1
models/edb_data_calculate_nhcc.go

@@ -668,7 +668,7 @@ func handleNhccData(dataList []*EdbInfoSearchData, moveDay int) (newDataList []E
 	minDate = minDate.AddDate(0, 0, moveDay)
 	maxDate = maxDate.AddDate(0, 0, moveDay)
 
-	// 开始变频
+	// 开始平移天数
 	dayNum := utils.GetTimeSubDay(minDate, maxDate)
 
 	for i := 0; i <= dayNum; i++ {

+ 13 - 1
models/edb_data_table.go

@@ -4,7 +4,7 @@ import (
 	"hongze/hongze_edb_lib/utils"
 )
 
-//指标数据->存储表
+// 指标数据->存储表
 func GetEdbDataTableName(source int) (tableName string) {
 	switch source {
 	case utils.DATA_SOURCE_THS:
@@ -103,6 +103,18 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_predict_calculate_hcz"
 	case utils.DATA_SOURCE_PREDICT_CALCULATE_BP:
 		tableName = "edb_data_predict_calculate_bp"
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		tableName = "edb_data_calculate_jp"
+	case utils.DATA_SOURCE_CALCULATE_NH:
+		tableName = "edb_data_calculate_nh"
+	case utils.DATA_SOURCE_CALCULATE_KSZS:
+		tableName = "edb_data_calculate_kszs"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_JP:
+		tableName = "edb_data_predict_calculate_jp"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_NH:
+		tableName = "edb_data_predict_calculate_nh"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS:
+		tableName = "edb_data_predict_calculate_kszs"
 	default:
 		tableName = ""
 	}

+ 42 - 0
models/handle_data.go

@@ -76,3 +76,45 @@ func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDa
 
 	return
 }
+
+// HandleDataByPreviousData 当前日期无值时,用上一个日期的数据补充当前日期的数据
+func HandleDataByPreviousData(edbInfoDataList []*EdbInfoSearchData, handleDataMap map[string]float64) (err error) {
+	if len(edbInfoDataList) < 2 {
+		return
+	}
+
+	var startEdbInfoData *EdbInfoSearchData
+	for _, v := range edbInfoDataList {
+		handleDataMap[v.DataTime] = v.Value
+
+		// 第一个数据就给过滤了,给后面的试用
+		if startEdbInfoData == nil {
+			startEdbInfoData = v
+			continue
+		}
+
+		// 获取两条数据之间相差的天数
+		startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
+		currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
+		betweenDay := betweenHour / 24
+
+		// 如果相差一天,那么过滤
+		if betweenDay <= 1 {
+			startEdbInfoData = v
+			continue
+		}
+
+		// 生成对应的值
+		{
+			for i := 1; i < betweenDay; i++ {
+				tmpDataTime := startDataTime.AddDate(0, 0, i)
+				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = startEdbInfoData.Value
+			}
+		}
+
+		startEdbInfoData = v
+	}
+
+	return
+}

+ 419 - 0
models/predict_edb_data_calculate_jp.go

@@ -0,0 +1,419 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateJp 预测降频值
+func SavePredictCalculateJp(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("SavePredictCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_JP,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_JP,
+			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
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, fromEdbInfo.EdbInfoId)
+
+		count, tmpErr := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if tmpErr != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + tmpErr.Error())
+			return
+		}
+		if count > 0 { // 指标未被替换,无需重新计算
+			return
+		}
+
+		//删除,计算指标关联的,基础指标的关联关系
+		sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+			return
+		}
+
+		//清空原有已经入库的数据
+		tableName := GetEdbDataTableName(edbInfo.Source)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		{
+			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
+			}
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbCode, edbInfo.Frequency, formula)
+
+	return
+}
+
+// RefreshAllPredictCalculateJp 刷新全部预测降频值数据
+func RefreshAllPredictCalculateJp(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula 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("RefreshAllCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateJp(to, edbInfoId, source, fromEdbInfo, edbCode, edbFrequency, formula)
+	return
+}
+
+// refreshAllPredictCalculateJp 刷新预测降频数据
+func refreshAllPredictCalculateJp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//获取源指标数据
+	fmt.Println("EdbInfoId:", fromEdbInfo.EdbInfoId)
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	fromDataMap := make(map[string]float64)
+	//来源指指标数据
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+		fromDataMap[v.DataTime] = v.Value
+	}
+	fmt.Println("source:", source)
+
+	//获取降频指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_JP)
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	//existMap := make(map[string]string)
+	dataLen := len(dataList)
+	if dataLen <= 0 {
+		return
+	}
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[dataLen-1].DataTime, time.Local)
+
+	var lastValue float64     // 最近的值
+	var nextEndDate time.Time // 下一个节点的日期
+	weekDayDataList := make([]float64, 0)
+	for currStartDataTime := startDataTime; !currStartDataTime.After(endDataTime); currStartDataTime = currStartDataTime.AddDate(0, 0, 1) {
+		// 将当前数据加入到 weekDayDataList
+		if tmpData, ok := dataMap[currStartDataTime.Format(utils.FormatDate)]; ok {
+			tmpValue := decimal.NewFromFloat(tmpData.Value)
+			tmpValueFloat, _ := tmpValue.Round(4).Float64()
+			weekDayDataList = append(weekDayDataList, tmpValueFloat)
+		}
+		// 如果下个节点的日期不存在,那么就先给赋值(兼容时间区间内只有一组数据的情况)
+		if nextEndDate.IsZero() {
+			nextEndDate = utils.GetFrequencyEndDay(currStartDataTime, edbFrequency)
+		}
+
+		// 日期处理过滤
+		switch edbFrequency {
+		case "周度":
+			if currStartDataTime.Weekday() != 0 {
+				//不是周日,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = currStartDataTime.AddDate(0, 0, 7)
+			}
+		case "旬度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 && nextDay.Day() != 11 && nextDay.Day() != 21 {
+				//不是每月10、20、最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				if nextDay.Day() == 1 || nextDay.Day() == 11 {
+					//月初或者月末的时候,加10天就好了
+					nextEndDate = nextDay.AddDate(0, 0, 9)
+				} else {
+					tmpNextMonth := nextDay.AddDate(0, 1, 0)
+					nextEndDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+				}
+			}
+		case "月度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 {
+				//不是每月最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 1, -1)
+			}
+		case "季度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 3, -1)
+			} else {
+				//不是3,6,9,12 月份的最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		case "年度":
+			if currStartDataTime.Month() == 12 && currStartDataTime.Day() == 31 {
+				//记录下一个结束节点的日期
+				nextEndDate = currStartDataTime.AddDate(1, 0, 0)
+			} else {
+				//不是每年的12-31日,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		default:
+			err = errors.New("错误的频度:" + edbFrequency)
+			return
+		}
+
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) <= 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 给实际日期数据的值赋值
+		if fromEdbInfo.LatestDate == currStartDataTime.Format(utils.FormatDate) {
+			latestValue = currVal
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[currStartDataTime.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currStartDataTime.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currStartDataTime.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		// 一轮结束后,数据清空
+		weekDayDataList = make([]float64, 0)
+	}
+
+	// 最后已有的日期处理完成后,需要对剩余不在时间段内的数据做处理
+	if len(weekDayDataList) > 0 {
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) < 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[nextEndDate.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := nextEndDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, nextEndDate.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	return
+}

+ 405 - 0
models/predict_edb_data_calculate_kszs.go

@@ -0,0 +1,405 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateKszs 预测扩散指数
+func SavePredictCalculateKszs(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, relationCalculateEdbInfoIdList []EdbInfoCalculateEdbInfoIdReq, 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("SavePredictCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	tagMap := make(map[string]int) //指标的标签map
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS,
+			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)
+
+		//关联关系
+		calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+		for _, v := range relationCalculateEdbInfoIdList {
+			tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+			calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+			calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+			calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+			calculateMappingItem.FromSource = tmpEdbInfo.Source
+			calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+			calculateMappingItem.FromTag = v.FromTag
+			calculateMappingItem.Source = edbInfo.Source
+			calculateMappingItem.SourceName = edbInfo.SourceName
+			calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+			tagMap[v.FromTag] = v.EdbInfoId
+		}
+		_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+		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
+		}
+		//修改指标信息
+		edbInfo.EdbName = edbName
+		edbInfo.EdbNameSource = edbName
+		edbInfo.Frequency = frequency
+		edbInfo.Unit = unit
+		edbInfo.ClassifyId = classifyId
+		edbInfo.CalculateFormula = formula
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+		if err != nil {
+			return
+		}
+
+		//删除,计算指标关联的,基础指标的关联关系
+		sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+			return
+		}
+		//清空原有数据
+		tableName := GetEdbDataTableName(edbInfo.Source)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+		for _, v := range relationCalculateEdbInfoIdList {
+			tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbInfo.EdbCode
+			calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+			calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+			calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+			calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+			calculateMappingItem.FromSource = tmpEdbInfo.Source
+			calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+			calculateMappingItem.FromTag = v.FromTag
+			calculateMappingItem.Source = edbInfo.Source
+			calculateMappingItem.SourceName = edbInfo.SourceName
+			calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+			tagMap[v.FromTag] = v.EdbInfoId
+		}
+		_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+		if err != nil {
+			return
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbCode, formula, tagMap)
+
+	return
+}
+
+// RefreshAllPredictCalculateKszs 刷新全部预测扩散指数数据
+func RefreshAllPredictCalculateKszs(edbInfo *EdbInfo) (latestDateStr string, latestValue float64, err error) {
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	for _, v := range edbInfoCalculateDetailList {
+		tagMap[v.FromTag] = v.FromEdbInfoId
+		fromEdbInfo, _ := GetEdbInfoById(v.FromEdbInfoId)
+		relationEdbInfoList = append(relationEdbInfoList, fromEdbInfo)
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbInfo.EdbCode, edbInfo.CalculateFormula, tagMap)
+	return
+}
+
+// refreshAllPredictCalculateKszs 刷新预测年化数据
+func refreshAllPredictCalculateKszs(to orm.TxOrmer, edbInfoId, source int, relationEdbInfoList []*EdbInfo, edbCode, calculateFormula string, tagMap map[string]int) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS)
+
+	// 获取扩散指标关联的指标id
+	checkEdbInfoIdMap := make(map[int]int)
+	{
+		var config KszsConfig
+		err = json.Unmarshal([]byte(calculateFormula), &config)
+		if err != nil {
+			return
+		}
+		if config.DateType == 1 {
+			for _, tmpEdbInfoId := range tagMap {
+				checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+			}
+		} else {
+			for _, v := range config.CheckList {
+				if tmpEdbInfoId, ok := tagMap[v]; ok {
+					checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+				}
+			}
+		}
+	}
+
+	//获取当前指标的所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	var latestDateTime time.Time       //真实数据的最后日期
+	var hasValLatestDateTime time.Time // 存在数据的真实日期(正常情况下,这两个值是相等的,除非出现一种情况:真实数据的最后日期当天 计算不出结果,那么 hasValLatestDateTime 不等于 latestDateTime)
+	//获取来源指标的数据
+	relationEdbDataMap := make(map[int]map[string]float64)
+	// 获取选择指标的 需要数据的 开始日期和结束日期
+	var startDate, endDate time.Time
+	for _, v := range relationEdbInfoList {
+		tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		if latestDateTime.IsZero() || latestDateTime.After(tmpLatestDate) {
+			// 真实数据的最后日期
+			latestDateTime = tmpLatestDate
+		}
+
+		tmpDataList, tmpErr := GetPredictEdbDataListAll(v, 1)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		if tmpDataList != nil {
+			if _, ok2 := checkEdbInfoIdMap[v.EdbInfoId]; ok2 {
+				lenTmpDataList := len(tmpDataList)
+				if lenTmpDataList > 0 {
+					tmpStartTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[0].DataTime, time.Local)
+					tmpEndTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[lenTmpDataList-1].DataTime, time.Local)
+
+					if startDate.IsZero() || tmpStartTime.Before(startDate) {
+						startDate = tmpStartTime
+					}
+
+					if tmpEndTime.IsZero() || tmpEndTime.After(endDate) {
+						endDate = tmpEndTime
+					}
+				}
+			}
+			// 用上期的数据补充当期的数据处理
+			handleDataMap := make(map[string]float64)
+			err = HandleDataByPreviousData(tmpDataList, handleDataMap)
+			if err != nil {
+				return
+			}
+			relationEdbDataMap[v.EdbInfoId] = handleDataMap
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for currDate := startDate.AddDate(0, 0, 1); !currDate.After(endDate); currDate = currDate.AddDate(0, 0, 1) {
+		currDateStr := currDate.Format(utils.FormatDate)
+
+		//环差指数列表
+		tmpValList := make([]float64, 0)
+		for _, dataMap := range relationEdbDataMap {
+			currVal, ok := dataMap[currDateStr]
+			if !ok {
+				continue
+			}
+
+			perVal, ok := dataMap[currDate.AddDate(0, 0, -1).Format(utils.FormatDate)]
+			if !ok {
+				continue
+			}
+
+			var tmpVal float64
+			if currVal > perVal {
+				tmpVal = 1
+			} else if currVal == perVal {
+				tmpVal = 0.5
+			} else {
+				tmpVal = 0
+			}
+			tmpValList = append(tmpValList, tmpVal)
+		}
+
+		lenTmpValList := len(tmpValList)
+		if lenTmpValList <= 0 {
+			continue
+		}
+
+		currValDeci := decimal.NewFromFloat(0)
+		for _, tmpVal := range tmpValList {
+			currValDeci = currValDeci.Add(decimal.NewFromFloat(tmpVal))
+		}
+		currVal, _ := currValDeci.Div(decimal.NewFromInt(int64(lenTmpValList))).Round(4).Float64()
+
+		// 如果存在数据的真实日期为空,或者 当前日期 早于或等于 实际数据的日期,那么就给 存在数据的真实日期 赋值
+		if hasValLatestDateTime.IsZero() || currDate.Before(latestDateTime) || currDate.Equal(latestDateTime) {
+			latestValue = currVal
+			hasValLatestDateTime = currDate
+		}
+
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除扩散指数指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	// 真实数据的最后日期
+	latestDateStr = hasValLatestDateTime.Format(utils.FormatDate)
+
+	return
+}

+ 363 - 0
models/predict_edb_data_calculate_nh.go

@@ -0,0 +1,363 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateNh 预测年化值
+func SavePredictCalculateNh(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("SavePredictCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_NH,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_NH,
+			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
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, fromEdbInfo.EdbInfoId)
+
+		count, tmpErr := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if tmpErr != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + tmpErr.Error())
+			return
+		}
+		if count > 0 { // 指标未被替换,无需重新计算
+			return
+		}
+
+		//删除,计算指标关联的,基础指标的关联关系
+		sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+			return
+		}
+
+		//清空原有已经入库的数据
+		tableName := GetEdbDataTableName(edbInfo.Source)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		{
+			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
+			}
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateNh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbCode)
+
+	return
+}
+
+// RefreshAllPredictCalculateNh 刷新全部预测年化值数据
+func RefreshAllPredictCalculateNh(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula 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("RefreshAllCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateNh(to, edbInfoId, source, fromEdbInfo, edbCode)
+	return
+}
+
+// refreshAllPredictCalculateNh 刷新预测年化数据
+func refreshAllPredictCalculateNh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_NH)
+
+	//获取年化指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	//获取源指标数据
+	fmt.Println("EdbInfoId:", fromEdbInfo.EdbInfoId)
+	fromDataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+	// 插值法数据处理
+	handleDataMap := make(map[string]float64)
+	err = HandleDataByLinearRegression(fromDataList, handleDataMap)
+	if err != nil {
+		return
+	}
+
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	fromDataMap := make(map[string]float64)
+	//来源指指标数据
+	for _, v := range fromDataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+		fromDataMap[v.DataTime] = v.Value
+	}
+	lenFromDataList := len(fromDataList)
+	// 如果来源指标没有数据,那么就直接返回得了
+	if lenFromDataList <= 0 {
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? `, tableName)
+
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除年化指标数据失败,Err:" + err.Error())
+			return
+		}
+		return
+	}
+	fmt.Println("source:", source)
+
+	// 真实数据的最后日期
+	latestDateStr = fromEdbInfo.LatestDate
+	// 每年的最后一天的数据值
+	yearLastValMap := make(map[int]float64)
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[lenFromDataList-1].DataTime, time.Local)
+	for i := startDataTime.Year(); i <= endDataTime.Year(); i++ {
+		tmpDateStr := fmt.Sprintf("%d-12-31", i)
+		if tmpVal, ok := handleDataMap[tmpDateStr]; ok {
+			yearLastValMap[i] = tmpVal
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//来源指指标数据
+	for _, v := range fromDataList {
+		currDateStr := v.DataTime
+		currDate, _ := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+
+		perValMap := make(map[time.Time]float64)
+		//前3年当日的数据
+		for i := 1; i <= 3; i++ {
+			tmpDateTime := currDate.AddDate(-i, 0, 0)
+			if tmpVal, ok := handleDataMap[tmpDateTime.Format(utils.FormatDate)]; ok {
+				perValMap[tmpDateTime] = tmpVal
+			}
+		}
+		lenPerValMap := len(perValMap)
+		// 如果数据少于2年,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenPerValMap < 2 {
+			continue
+		}
+
+		// N年 当前值占全年比重 的值列表
+		divValList := make([]decimal.Decimal, 0)
+		for tmpDateTime, tmpVal := range perValMap {
+			yearLastVal, ok2 := yearLastValMap[tmpDateTime.Year()]
+			// 如果当年最后一天没有数据
+			if !ok2 {
+				continue
+			}
+
+			// 当前值占全年比重
+			divVal := decimal.NewFromFloat(tmpVal).Div(decimal.NewFromFloat(yearLastVal))
+			divValList = append(divValList, divVal)
+		}
+
+		lenDivValList := len(divValList)
+		// 如果 N年 当前值占全年比重 的值 小于 2个,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenDivValList < 2 {
+			continue
+		}
+
+		divValSum := decimal.NewFromFloat(0)
+		for _, divVal := range divValList {
+			divValSum = divValSum.Add(divVal)
+		}
+
+		// 当前计算出来的结果
+		currVal, _ := decimal.NewFromFloat(v.Value).Div(divValSum.Div(decimal.NewFromInt(int64(lenDivValList)))).Round(4).Float64()
+
+		// 给实际日期数据的值赋值
+		if fromEdbInfo.LatestDate == currDateStr {
+			latestValue = currVal
+		}
+		// 判断年化指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理年化数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断年化数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除年化指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}

+ 88 - 27
utils/common.go

@@ -27,7 +27,7 @@ import (
 	"time"
 )
 
-//随机数种子
+// 随机数种子
 var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
 
 func GetRandString(size int) string {
@@ -64,13 +64,13 @@ func StringsToJSON(str string) string {
 	return jsons
 }
 
-//序列化
+// 序列化
 func ToString(v interface{}) string {
 	data, _ := json.Marshal(v)
 	return string(data)
 }
 
-//md5加密
+// md5加密
 func MD5(data string) string {
 	m := md5.Sum([]byte(data))
 	return hex.EncodeToString(m[:])
@@ -98,7 +98,7 @@ func GetToday(format string) string {
 	return today
 }
 
-//获取今天剩余秒数
+// 获取今天剩余秒数
 func GetTodayLastSecond() time.Duration {
 	today := GetToday(FormatDate) + " 23:59:59"
 	end, _ := time.ParseInLocation(FormatDateTime, today, time.Local)
@@ -120,7 +120,7 @@ func GetBrithDate(idcard string) string {
 	return GetToday(FormatDate)
 }
 
-//处理性别
+// 处理性别
 func WhichSexByIdcard(idcard string) string {
 	var sexs = [2]string{"女", "男"}
 	length := len(idcard)
@@ -134,7 +134,7 @@ func WhichSexByIdcard(idcard string) string {
 	return "男"
 }
 
-//截取小数点后几位
+// 截取小数点后几位
 func SubFloatToString(f float64, m int) string {
 	n := strconv.FormatFloat(f, 'f', -1, 64)
 	if n == "" {
@@ -153,20 +153,20 @@ func SubFloatToString(f float64, m int) string {
 	return newn[0] + "." + newn[1][:m]
 }
 
-//截取小数点后几位
+// 截取小数点后几位
 func SubFloatToFloat(f float64, m int) float64 {
 	newn := SubFloatToString(f, m)
 	newf, _ := strconv.ParseFloat(newn, 64)
 	return newf
 }
 
-//截取小数点后几位
+// 截取小数点后几位
 func SubFloatToFloatStr(f float64, m int) string {
 	newn := SubFloatToString(f, m)
 	return newn
 }
 
-//获取相差时间-年
+// 获取相差时间-年
 func GetYearDiffer(start_time, end_time string) int {
 	t1, _ := time.ParseInLocation("2006-01-02", start_time, time.Local)
 	t2, _ := time.ParseInLocation("2006-01-02", end_time, time.Local)
@@ -177,7 +177,7 @@ func GetYearDiffer(start_time, end_time string) int {
 	return age
 }
 
-//获取相差时间-秒
+// 获取相差时间-秒
 func GetSecondDifferByTime(start_time, end_time time.Time) int64 {
 	diff := end_time.Unix() - start_time.Unix()
 	return diff
@@ -204,7 +204,7 @@ func StrListToString(strList []string) (str string) {
 	return ""
 }
 
-//Token
+// Token
 func GetToken() string {
 	randStr := GetRandString(64)
 	token := MD5(randStr + Md5Key)
@@ -212,18 +212,18 @@ func GetToken() string {
 	return strings.ToUpper(token + GetRandString(tokenLen))
 }
 
-//数据没有记录
+// 数据没有记录
 func ErrNoRow() string {
 	return "<QuerySeter> no row found"
 }
 
-//判断文件是否存在
+// 判断文件是否存在
 func FileIsExist(filePath string) bool {
 	_, err := os.Stat(filePath)
 	return err == nil || os.IsExist(err)
 }
 
-//获取图片扩展名
+// 获取图片扩展名
 func GetImgExt(file string) (ext string, err error) {
 	var headerByte []byte
 	headerByte = make([]byte, 8)
@@ -266,7 +266,7 @@ func GetImgExt(file string) (ext string, err error) {
 	return ext, nil
 }
 
-//保存图片
+// 保存图片
 func SaveImage(path string, img image.Image) (err error) {
 	//需要保持的文件
 	imgfile, err := os.Create(path)
@@ -276,7 +276,7 @@ func SaveImage(path string, img image.Image) (err error) {
 	return err
 }
 
-//下载图片
+// 下载图片
 func DownloadImage(imgUrl string) (filePath string, err error) {
 	imgPath := "./static/imgs/"
 	fileName := path.Base(imgUrl)
@@ -302,7 +302,7 @@ func DownloadImage(imgUrl string) (filePath string, err error) {
 	return
 }
 
-//保存base64数据为文件
+// 保存base64数据为文件
 func SaveBase64ToFile(content, path string) error {
 	data, err := base64.StdEncoding.DecodeString(content)
 	if err != nil {
@@ -432,7 +432,7 @@ func GetWilsonScore(p, n float64) float64 {
 	return toFixed(((p+1.9208)/(p+n)-1.96*math.Sqrt(p*n/(p+n)+0.9604)/(p+n))/(1+3.8416/(p+n)), 2)
 }
 
-//将中文数字转化成数字,比如 第三百四十五章,返回第345章 不支持一亿及以上
+// 将中文数字转化成数字,比如 第三百四十五章,返回第345章 不支持一亿及以上
 func ChangeWordsToNum(str string) (numStr string) {
 	words := ([]rune)(str)
 	num := 0
@@ -603,7 +603,7 @@ func GetMonthStartAndEnd(myYear string, myMonth string) (startDate, endDate stri
 	return t1, t2
 }
 
-//移除字符串中的空格
+// 移除字符串中的空格
 func TrimStr(str string) (str2 string) {
 	if str == "" {
 		return str
@@ -611,7 +611,7 @@ func TrimStr(str string) (str2 string) {
 	return strings.Replace(str, " ", "", -1)
 }
 
-//字符串转换为time
+// 字符串转换为time
 func StrTimeToTime(strTime string) time.Time {
 	timeLayout := "2006-01-02 15:04:05"  //转化所需模板
 	loc, _ := time.LoadLocation("Local") //重要:获取时区
@@ -619,7 +619,7 @@ func StrTimeToTime(strTime string) time.Time {
 	return resultTime
 }
 
-//字符串类型时间转周几
+// 字符串类型时间转周几
 func StrDateTimeToWeek(strTime string) string {
 	var WeekDayMap = map[string]string{
 		"Monday":    "周一",
@@ -636,7 +636,7 @@ func StrDateTimeToWeek(strTime string) string {
 	return WeekDayMap[staweek_int]
 }
 
-//时间格式转年月日字符串
+// 时间格式转年月日字符串
 func TimeToStrYmd(time2 time.Time) string {
 	var Ymd string
 	year := time2.Year()
@@ -646,7 +646,7 @@ func TimeToStrYmd(time2 time.Time) string {
 	return Ymd
 }
 
-//时间格式去掉时分秒
+// 时间格式去掉时分秒
 func TimeRemoveHms(strTime string) string {
 	var Ymd string
 	var resultTime = StrTimeToTime(strTime)
@@ -657,7 +657,7 @@ func TimeRemoveHms(strTime string) string {
 	return Ymd
 }
 
-//时间格式去掉时分秒
+// 时间格式去掉时分秒
 func TimeRemoveHms2(strTime string) string {
 	var Ymd string
 	var resultTime = StrTimeToTime(strTime)
@@ -668,7 +668,7 @@ func TimeRemoveHms2(strTime string) string {
 	return Ymd
 }
 
-//文章上一次编辑时间
+// 文章上一次编辑时间
 func ArticleLastTime(strTime string) string {
 	var newTime string
 	stamp, _ := time.ParseInLocation("2006-01-02 15:04:05", strTime, time.Local)
@@ -689,7 +689,7 @@ func ArticleLastTime(strTime string) string {
 	return newTime
 }
 
-//人民币小写转大写
+// 人民币小写转大写
 func ConvertNumToCny(num float64) (str string, err error) {
 	strNum := strconv.FormatFloat(num*100, 'f', 0, 64)
 	sliceUnit := []string{"仟", "佰", "拾", "亿", "仟", "佰", "拾", "万", "仟", "佰", "拾", "元", "角", "分"}
@@ -1001,7 +1001,7 @@ func HideString(src string, hideLen int) string {
 	return string(str[:frontLen]) + hideStr + string(str[frontLen+int64(hideLen):])
 }
 
-//用户参会时间转换
+// 用户参会时间转换
 func GetAttendanceDetailSeconds(secondNum int) string {
 	var timeStr string
 	if secondNum <= 60 {
@@ -1118,3 +1118,64 @@ func GetTimeSubDay(t1, t2 time.Time) int {
 
 	return day
 }
+
+// GetFrequencyEndDay 根据当前时间和频度,获取该频度下最后一天的日期
+func GetFrequencyEndDay(currDate time.Time, frequency string) (endDate time.Time) {
+	switch frequency {
+	case "周度":
+		// 如果当前就是最后一天,那么就直接返回本日期就好了
+		if currDate.Weekday() == 0 {
+			endDate = currDate
+		} else {
+			endDate = currDate.AddDate(0, 0, 7-int(currDate.Weekday()))
+		}
+	case "旬度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if nextDay.Day() == 1 || currDate.Day() == 10 || currDate.Day() == 20 {
+			//如果是每月10、20、最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			if currDate.Day() < 10 { // 每月10号
+				endDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, time.Local)
+			} else if currDate.Day() < 20 { // 每月10号
+				endDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, time.Local)
+			} else {
+				// 下旬,多种可能,最大天数可能存在8天,9天,10天,11天,
+				tmpNextMonth := currDate.AddDate(0, 0, 13)
+				endDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+			}
+		}
+	case "月度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if nextDay.Day() == 1 {
+			//如果是每月的最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			endDate = time.Date(nextDay.Year(), nextDay.Month()+1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+		}
+	case "季度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 {
+			//如果是每季的最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			if currDate.Month() < 4 { // 1季度
+				endDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, time.Local)
+			} else if currDate.Month() < 7 { // 2季度
+				endDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, time.Local)
+			} else if currDate.Month() < 10 { // 3季度
+				endDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, time.Local)
+			} else {
+				// 4季度
+				endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local)
+			}
+		}
+	case "年度":
+		endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local)
+	default:
+		endDate = currDate
+		return
+	}
+
+	return
+}

+ 70 - 4
utils/constants.go

@@ -6,7 +6,7 @@ const (
 	Md5Key = "GuRaB6dY1bXOJcwG"
 )
 
-//常量定义
+// 常量定义
 const (
 	FormatTime                 = "15:04:05"            //时间格式
 	FormatDate                 = "2006-01-02"          //日期格式
@@ -27,7 +27,7 @@ const (
 	EmailSendToUsers = "glji@hzinsights.com;pyan@hzinsights.com;cxzhang@hzinsights.com"
 )
 
-//数据来源渠道
+// 数据来源渠道
 const (
 	DATA_SOURCE_THS                         = iota + 1 //同花顺
 	DATA_SOURCE_WIND                                   //wind
@@ -79,9 +79,75 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ              //预测指标 - 累计值同比拼接->48
 	DATA_SOURCE_PREDICT_CALCULATE_CJJX                 //预测指标 - 超季节性->49
 	DATA_SOURCE_PREDICT_CALCULATE_NHCC                 //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_CALCULATE_JP                            //降频->51
+	DATA_SOURCE_CALCULATE_NH                            //年化->52
+	DATA_SOURCE_CALCULATE_KSZS                          //扩散指数->53
+	DATA_SOURCE_PREDICT_CALCULATE_JP                    //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_PREDICT_CALCULATE_NH                    //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_PREDICT_CALCULATE_KSZS                  //预测指标 - 计算指标(扩散指数)->56
 )
 
-//基础数据初始化日期
+// 指标来源的中文展示
+const (
+	DATA_SOURCE_NAME_THS                          = `同花顺`               //同花顺
+	DATA_SOURCE_NAME_WIND                         = `wind`              //wind
+	DATA_SOURCE_NAME_PB                           = `彭博`                //彭博
+	DATA_SOURCE_NAME_CALCULATE                    = `指标运算`              //指标运算
+	DATA_SOURCE_NAME_CALCULATE_LJZZY              = `累计值转月值`            //累计值转月
+	DATA_SOURCE_NAME_CALCULATE_TBZ                = `同比值`               //同比值
+	DATA_SOURCE_NAME_CALCULATE_TCZ                = `同差值`               //同差值
+	DATA_SOURCE_NAME_CALCULATE_NSZYDPJJS          = `N数值移动平均计算`         //N数值移动平均计算
+	DATA_SOURCE_NAME_MANUAL                       = `手工数据`              //手工指标
+	DATA_SOURCE_NAME_LZ                           = `隆众`                //隆众
+	DATA_SOURCE_NAME_YS                           = `SMM`               //有色
+	DATA_SOURCE_NAME_CALCULATE_HBZ                = `环比值`               //环比值->12
+	DATA_SOURCE_NAME_CALCULATE_HCZ                = `环差值`               //环差值->13
+	DATA_SOURCE_NAME_CALCULATE_BP                 = `升频`                //变频,2023-2-10 13:56:01调整为"升频"->14
+	DATA_SOURCE_NAME_GL                           = `钢联`                //钢联->15
+	DATA_SOURCE_NAME_ZZ                           = `郑商所`               //郑商所->16
+	DATA_SOURCE_NAME_DL                           = `大商所`               //大商所->17
+	DATA_SOURCE_NAME_SH                           = `上期所`               //上期所->18
+	DATA_SOURCE_NAME_CFFEX                        = `中金所`               //中金所->19
+	DATA_SOURCE_NAME_SHFE                         = `上期能源`              //上期能源->20
+	DATA_SOURCE_NAME_GIE                          = `欧洲天然气`             //欧洲天然气->21
+	DATA_SOURCE_NAME_CALCULATE_TIME_SHIFT         = `时间移位`              //时间移位->22
+	DATA_SOURCE_NAME_CALCULATE_ZJPJ               = `直接拼接`              //直接拼接->23
+	DATA_SOURCE_NAME_CALCULATE_LJZTBPJ            = `累计值同比拼接`           //累计值同比拼接->24
+	DATA_SOURCE_NAME_LT                           = `路透`                //路透->25
+	DATA_SOURCE_NAME_COAL                         = `中国煤炭网`             //煤炭网->26
+	DATA_SOURCE_NAME_PYTHON                       = `代码运算`              //python代码->27
+	DATA_SOURCE_NAME_PB_FINANCE                   = `彭博财务`              //彭博财务数据->28
+	DATA_SOURCE_NAME_GOOGLE_TRAVEL                = `our world in data` //谷歌出行数据->29
+	DATA_SOURCE_NAME_PREDICT                      = `预测指标`              //普通预测指标->30
+	DATA_SOURCE_NAME_PREDICT_CALCULATE            = `预测指标运算`            //预测指标运算->31
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TBZ        = `预测同比`              //预测指标 - 同比值->32
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TCZ        = `预测同差`              //预测指标 - 同差值->33
+	DATA_SOURCE_NAME_MYSTEEL_CHEMICAL             = `钢联化工`              //钢联化工->34
+	DATA_SOURCE_NAME_CALCULATE_CJJX               = `超季节性`              //超季节性->35
+	DATA_SOURCE_NAME_EIA_STEO                     = `EIA STERO报告`       //eia stero报告->36
+	DATA_SOURCE_NAME_CALCULATE_NHCC               = `拟合残差`              //计算指标(拟合残差)->37
+	DATA_SOURCE_NAME_COM_TRADE                    = `UN`                //联合国商品贸易数据->38
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NSZYDPJJS  = `预测N数值移动平均计算`       //预测指标 - N数值移动平均计算 -> 39
+	DATA_SOURCE_NAME_CALCULATE_ADJUST             = `数据调整`              //数据调整->40
+	DATA_SOURCE_NAME_SCI                          = `SCI`               //卓创数据(红桃三)->41
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZZY      = `预测累计值转月值`          //预测指标 - 累计值转月->42
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_HBZ        = `预测环比值`             //预测指标 - 环比值->43
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_HCZ        = `预测环差值`             //预测指标 - 环差值->44
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_BP         = `预测升频`              //预测指标 - 升频->45
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TIME_SHIFT = `预测时间移位`            //预测指标 - 时间移位->46
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_ZJPJ       = `预测直接拼接`            //预测指标 - 直接拼接->47
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZTBPJ    = `预测累计值同比拼接`         //预测指标 - 累计值同比拼接->48
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_CJJX       = `预测超季节性`            //预测指标 - 超季节性->49
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NHCC       = `预测拟合残差`            //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_NAME_CALCULATE_JP                 = `降频`                //降频->51
+	DATA_SOURCE_NAME_CALCULATE_NH                 = `年化`                //年化->52
+	DATA_SOURCE_NAME_CALCULATE_KSZS               = `扩散指数`              //扩散指数->53
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_JP         = `预测降频`              //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NH         = `预测年化`              //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS       = `预测扩散指数`            //预测指标 - 计算指标(扩散指数)->56
+)
+
+// 基础数据初始化日期
 var (
 	BASE_START_DATE         = time.Now().AddDate(-30, 0, 0).Format(FormatDate)        //基础数据开始日期
 	BASE_END_DATE           = time.Now().AddDate(4, 0, 0).Format(FormatDate)          //基础数据结束日期
@@ -112,7 +178,7 @@ const (
 	CHART_PREFIX = "hz_chart"
 )
 
-//数据刷新频率
+// 数据刷新频率
 const (
 	DATA_REFRESH        = 7 //7个单位,日/周/月/季度/年
 	DATA_END_DATE_LIMIT = 4 //数据结束日期为,当前日期,加上4年时间