Pārlūkot izejas kodu

feat:新增指标接口

Roc 3 gadi atpakaļ
vecāks
revīzija
28efd8d86a

+ 531 - 0
controllers/base_from_calculate.go

@@ -0,0 +1,531 @@
+package controllers
+
+import (
+	"encoding/json"
+	"fmt"
+	"hongze/hongze_edb_lib/models"
+	"hongze/hongze_edb_lib/services"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// CalculateController 计算指标
+type CalculateController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 编辑指标接口
+// @Description 编辑指标接口
+// @Success 200 {object} data_manage.EditEdbInfoReq
+// @router /add [post]
+func (this *CalculateController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	//source := utils.DATA_SOURCE_WIND
+	var req models.EdbInfoCalculateSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	if len(req.EdbInfoIdArr) <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	if req.CalculateFormula == "" {
+		br.Msg = "请填写指标"
+		return
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("edb_info:calculate:batch:save:", utils.DATA_SOURCE_CALCULATE, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		br.Msg = "指标正在处理,请勿重复提交"
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			utils.Rc.Delete(redisKey)
+		}()
+	}
+
+	calculateFormula := req.CalculateFormula
+	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
+	calculateFormula = strings.Replace(calculateFormula, ")", ")", -1)
+	calculateFormula = strings.Replace(calculateFormula, ",", ",", -1)
+	calculateFormula = strings.Replace(calculateFormula, "。", ".", -1)
+	if strings.Contains(req.CalculateFormula, "%") {
+		calculateFormula = strings.Replace(calculateFormula, "%", "*0.01", -1)
+	}
+	req.CalculateFormula = calculateFormula
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=? "
+	pars = append(pars, req.EdbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "判断指标名称是否存在失败"
+		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "指标名称已存在,请重新填写"
+		br.ErrMsg = "指标名称已存在,请重新填写"
+		return
+	}
+	//检验公式
+	var formulaStr string
+	var edbInfoIdBytes []string
+	for _, v := range req.EdbInfoIdArr {
+		formulaStr += v.FromTag + ","
+		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
+	}
+	formulaMap := services.CheckFormula(req.CalculateFormula)
+	for _, v := range formulaMap {
+		if !strings.Contains(formulaStr, v) {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
+	}
+
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C` + time.Now().Format("060102") + randStr
+
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode := utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+	edbInfo := &models.EdbInfo{
+		Source:           utils.DATA_SOURCE_CALCULATE,
+		SourceName:       "指标运算",
+		EdbCode:          edbCode,
+		EdbName:          req.EdbName,
+		EdbNameSource:    req.EdbName,
+		Frequency:        req.Frequency,
+		Unit:             req.Unit,
+		ClassifyId:       req.ClassifyId,
+		SysUserId:        req.AdminId,
+		SysUserRealName:  req.AdminName,
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		UniqueCode:       uniqueCode,
+		CalculateFormula: req.CalculateFormula,
+		EdbType:          2,
+	}
+
+	edbInfoId, err := models.AddEdbInfo(edbInfo)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,AddEdbInfo Err:" + err.Error()
+		return
+	}
+	//处理同名指标
+	{
+		edbNameList, err := models.GetEdbInfoByName(req.EdbName)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "修改指标名称失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+
+	edbInfoList := make([]*models.EdbInfo, 0)
+
+	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		fromEdbInfo, err := models.GetEdbInfoById(v.EdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "生成计算指标失败"
+				br.Msg = "指标 " + strconv.Itoa(v.EdbInfoId) + " 不存在"
+				return
+			}
+			br.Msg = "生成计算指标失败"
+			br.Msg = "获取指标失败:Err:" + err.Error()
+			return
+		}
+		edbInfoList = append(edbInfoList, fromEdbInfo)
+
+		//关联关系表
+		{
+			calculateMappingItem := &models.EdbInfoCalculateMapping{
+				CreateTime:     time.Now(),
+				ModifyTime:     time.Now(),
+				Sort:           k + 1,
+				EdbCode:        edbCode,
+				EdbInfoId:      int(edbInfoId),
+				FromEdbInfoId:  fromEdbInfo.EdbInfoId,
+				FromEdbCode:    fromEdbInfo.EdbCode,
+				FromEdbName:    fromEdbInfo.EdbName,
+				FromSource:     fromEdbInfo.Source,
+				FromSourceName: fromEdbInfo.SourceName,
+				FromTag:        v.FromTag,
+				Source:         utils.DATA_SOURCE_CALCULATE,
+				SourceName:     "指标运算",
+			}
+			calculateMappingList = append(calculateMappingList, calculateMappingItem)
+		}
+	}
+	//关联关系表
+	{
+		if len(calculateMappingList) > 0 {
+			go models.AddEdbInfoCalculateMappingMulti(calculateMappingList)
+		}
+	}
+
+	err = models.AddCalculate(edbInfoList, int(edbInfoId), edbCode, req.CalculateFormula, edbInfoIdBytes)
+	if err != nil {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,Calculate Err:" + err.Error()
+		return
+	}
+	maxAndMinItem, err := models.GetEdbInfoMaxAndMinInfo(utils.DATA_SOURCE_CALCULATE, edbCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "生成计算指标失败"
+		br.Msg = "生成计算指标失败,GetEdbInfoMaxAndMinInfo Err:" + err.Error()
+		return
+	}
+
+	if maxAndMinItem != nil {
+		err = models.ModifyEdbInfoMaxAndMinInfo(int(edbInfoId), maxAndMinItem)
+	}
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  int(edbInfoId),
+		UniqueCode: uniqueCode,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// CalculateBatchSave
+// @Title 累计值转月-同比值-同差等计算新增
+// @Description 累计值转月-同比值-同差等计算新增接口
+// @Param	request	body data_manage.EdbInfoCalculateBatchSaveReq true "type json string"
+// @Success Ret=200 返回指标id
+// @router /batch/save [post]
+func (this *CalculateController) CalculateBatchSave() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.EdbInfoCalculateBatchSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	if req.FromEdbInfoId <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	var formulaInt int
+	if req.Source == utils.DATA_SOURCE_CALCULATE_NSZYDPJJS ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_HBZ ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_HCZ ||
+		req.Source == utils.DATA_SOURCE_CALCULATE_TIME_SHIFT {
+		if req.Formula == "" {
+			br.Msg = "请填写N值"
+			return
+		}
+		formulaInt, _ = strconv.Atoi(req.Formula)
+		if formulaInt <= 0 {
+			br.Msg = "N值输入错误,请重新输入"
+			return
+		}
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_ZJPJ {
+		//直接拼接指标
+
+		//校验时间格式
+		_, err = time.ParseInLocation(utils.FormatDate, req.Formula, time.Local)
+		if err != nil {
+			br.Msg = "拼接日期有误,请重新输入"
+			return
+		}
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("edb_info:calculate:batch:save:", req.Source, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		br.Msg = "指标正在处理,请勿重复提交"
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			utils.Rc.Delete(redisKey)
+		}()
+	}
+	//加入缓存机制,避免创建同一个名称的指标 end
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=? "
+	pars = append(pars, req.EdbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "判断指标名称是否存在失败"
+		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		br.Msg = "指标名称已存在,请重新填写"
+		br.ErrMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	fromEdbInfo, err := models.GetEdbInfoById(req.FromEdbInfoId)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+		return
+	}
+
+	//生成指标编码
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C` + time.Now().Format("060102") + randStr
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode := utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+
+	adminId := req.AdminId
+	adminName := req.AdminName
+	var sourName string
+	var edbInfoId int
+	if req.Source == utils.DATA_SOURCE_CALCULATE_LJZZY {
+		sourName = "累计值转月值"
+		if fromEdbInfo.Frequency != "月度" {
+			br.Msg = "请选择月度指标"
+			return
+		}
+		edbInfoId, err = models.AddCalculateLjzzy(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TBZ {
+		sourName = "同比值"
+		edbInfoId, err = models.AddCalculateTbz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TCZ {
+		sourName = "同差值"
+		edbInfoId, err = models.AddCalculateTcz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_NSZYDPJJS {
+		sourName = "N数值移动平均计算"
+		edbInfoId, err = models.AddCalculateNszydpjjs(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_HBZ { //环比值
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id =? "
+		pars = append(pars, req.FromEdbInfoId)
+		condition += " AND value <=0 "
+		checkCount, err := models.GetEdbDataCount(condition, pars, fromEdbInfo.Source)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "判断环比值是否可计算失败"
+			br.ErrMsg = "判断环比值是否可计算失败,Err:" + err.Error()
+			return
+		}
+		if checkCount > 0 {
+			br.Msg = "原始数据中存在0或负数,该指标不能进行环比运算"
+			br.ErrMsg = "原始数据中出现0和负值时,提示该指标不能进行环比运算"
+			return
+		}
+		sourName = "环比值"
+		edbInfoId, err = models.AddCalculateHbz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_HCZ {
+		sourName = "环差值"
+		edbInfoId, err = models.AddCalculateHcz(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_BP {
+		if fromEdbInfo.Frequency == "日度" {
+			br.Msg = "日度指标,无法进行变频操作"
+			br.ErrMsg = "日度指标,无法进行变频操作:edbcode:" + fromEdbInfo.EdbCode
+			return
+		}
+		sourName = "变频"
+		edbInfoId, err = models.AddCalculateBp(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_TIME_SHIFT { //时间移位
+		sourName = "时间移位"
+		edbInfoId, err = models.AddCalculateTimeShift(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_ZJPJ { //直接拼接
+		sourName = "直接拼接"
+
+		if len(req.EdbInfoIdArr) != 1 {
+			br.Msg = "请传入拼接日期之后的指标"
+			br.ErrMsg = "请传入拼接日期之后的指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		secondEdbInfo, err := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if err != nil {
+			br.Msg = "获取拼接日期之后的指标信息失败"
+			br.ErrMsg = "获取拼接日期之后的指标信息失败:Err:" + err.Error()
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			br.Msg = "两个指标不允许为同一个"
+			br.ErrMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfoId, err = models.AddCalculateZjpj(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_CALCULATE_LJZTBPJ { //累计值同比拼接
+		sourName = "累计值同比拼接"
+
+		if fromEdbInfo.Frequency != "月度" {
+			br.Msg = "待拼接指标只能筛选月度指标"
+			br.ErrMsg = "待拼接指标只能筛选月度指标"
+			return
+		}
+		if len(req.EdbInfoIdArr) != 1 {
+			br.Msg = "请传入同比值指标"
+			br.ErrMsg = "请传入同比值指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		tbzEdbInfo, err := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if err != nil {
+			br.Msg = "获取同比值指标信息失败"
+			br.ErrMsg = "获取同比值指标信息失败:Err:" + err.Error()
+			return
+		}
+		if tbzEdbInfo.Source != utils.DATA_SOURCE_CALCULATE_TBZ {
+			br.Msg = "指标必须是传入同比值指标类型"
+			br.ErrMsg = "指标必须是传入同比值指标类型"
+			return
+		}
+		if tbzEdbInfo.Frequency != "月度" {
+			br.Msg = "同比值指标只能筛选月度指标"
+			br.ErrMsg = "同比值指标只能筛选月度指标"
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == tbzEdbInfo.EdbInfoId {
+			br.Msg = "两个指标不允许为同一个"
+			br.ErrMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfoId, err = models.AddCalculateLjztbpj(&req, fromEdbInfo, tbzEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else {
+		br.Msg = "无效计算方式"
+		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+		return
+	}
+
+	if err != nil {
+		br.Msg = "生成" + sourName + "失败"
+		br.Msg = "生成" + sourName + "失败 Err:" + err.Error()
+		return
+	}
+	if edbInfoId <= 0 {
+		br.Msg = "生成" + sourName + "失败"
+		br.ErrMsg = "生成" + sourName + "失败,指标ID错误:" + strconv.Itoa(edbInfoId)
+		return
+	}
+	//处理同名指标
+	{
+		edbNameList, err := models.GetEdbInfoByName(req.EdbName)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "修改指标名称失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+	maxAndMinItem, err := models.GetEdbInfoMaxAndMinInfo(req.Source, edbCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "生成" + sourName + "失败"
+		br.Msg = "生成" + sourName + "失败,GetEdbInfoMaxAndMinInfo Err:" + err.Error()
+		return
+	}
+	if maxAndMinItem != nil {
+		err = models.ModifyEdbInfoMaxAndMinInfo(edbInfoId, maxAndMinItem)
+	}
+	resp := models.AddEdbInfoResp{
+		EdbInfoId:  edbInfoId,
+		UniqueCode: uniqueCode,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}

+ 274 - 0
models/base_from_calculate.go

@@ -0,0 +1,274 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/yidane/formula"
+	"hongze/hongze_edb_lib/services"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// EdbInfoCalculateSaveReq 计算(运算)指标请求参数
+type EdbInfoCalculateSaveReq struct {
+	AdminId int `description:"添加人id"`
+	AdminName string `description:"添加人名称"`
+	EdbName          string `description:"指标名称"`
+	Frequency        string `description:"频率"`
+	Unit             string `description:"单位"`
+	ClassifyId       int    `description:"分类id"`
+	CalculateFormula string `description:"计算公式"`
+	EdbInfoIdArr     []struct {
+		EdbInfoId int    `description:"指标id"`
+		FromTag   string `description:"指标对应标签"`
+	}
+}
+
+// CalculateItems 计算(运算)指标信息
+type CalculateItems struct {
+	EdbInfoId int
+	DataMap   map[string]float64
+}
+
+// AddCalculate 新增计算(运算)指标
+func AddCalculate(edbInfoIdArr []*EdbInfo, edbInfoId int, edbCode, formulaStr string, edbInfoIdBytes []string) (err error) {
+	o := orm.NewOrm()
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info("Calculate Err:%s" + err.Error())
+		}
+	}()
+	saveDataMap := make(map[string]map[int]float64)
+	for _, v := range edbInfoIdArr {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, v.EdbInfoId)
+		dataList, err := GetEdbDataListAll(condition, pars, v.Source, 1)
+		if err != nil {
+			return err
+		}
+		dataMap := make(map[string]float64)
+		for _, dv := range dataList {
+			if val, ok := saveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp := make(map[int]float64)
+				temp[v.EdbInfoId] = dv.Value
+				saveDataMap[dv.DataTime] = temp
+			}
+		}
+		item := new(CalculateItems)
+		item.EdbInfoId = v.EdbInfoId
+		item.DataMap = dataMap
+	}
+	formulaMap := services.CheckFormula(formulaStr)
+	addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	nowStr := time.Now().Format(utils.FormatDateTime)
+	var isAdd bool
+	for sk, sv := range saveDataMap {
+		formulaStr = strings.ToUpper(formulaStr)
+		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		if formulaStr == "" {
+			return
+		}
+		if formulaFormStr != "" {
+			expression := formula.NewExpression(formulaFormStr)
+			calResult, err := expression.Evaluate()
+			if err != nil {
+				err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+			calVal, err := calResult.Float64()
+			if err != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+				fmt.Println(err)
+				return err
+			}
+
+			//需要存入的数据
+			{
+				dataTime, _ := time.Parse(utils.FormatDate, sk)
+				timestamp := dataTime.UnixNano() / 1e6
+				timeStr := fmt.Sprintf("%d", timestamp)
+				addSql += "("
+				addSql += strconv.Itoa(edbInfoId) + "," + "'" + edbCode + "'" + "," + "'" + sk + "'" + "," + utils.SubFloatToString(calVal, 4) + "," + "'" + nowStr + "'" +
+					"," + "'" + nowStr + "'" + "," + "1"
+				addSql += "," + "'" + timeStr + "'"
+				addSql += "),"
+				isAdd = true
+			}
+		} else {
+			fmt.Println("formulaFormStr is empty")
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("AddEdbDataCalculate Err:" + err.Error())
+			//errMsg = " tx.Exec Err :" + err.Error()
+			return
+		}
+	}
+	return
+}
+
+// ReplaceFormula 替换计算方式
+func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr map[int]float64, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string) string {
+	funMap := GetFormulaMap()
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, k, v, -1)
+	}
+	replaceCount := 0
+	for dk, dv := range edbInfoIdArr {
+		if dk == 0 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				} else {
+					fmt.Println("valArr not found:", valArr, valOk)
+				}
+			} else {
+				fmt.Println("formulaMap not found:", dKey, dk)
+			}
+		}
+		if dk == 1 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				} else {
+					fmt.Println("valArr not found:", valArr, valOk)
+				}
+			} else {
+				fmt.Println("formulaMap not found:", dKey, dk)
+			}
+		}
+		if dk == 2 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 3 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 4 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 5 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 6 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 7 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+		if dk == 8 {
+			dKey := edbInfoIdBytes[dk]
+			if _, ok := formulaMap[dKey]; ok { //公式中存在
+				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
+					dvStr := fmt.Sprintf("%v", val)
+					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
+					replaceCount++
+				}
+			}
+		}
+	}
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, v, k, -1)
+	}
+	if replaceCount == len(formulaMap) {
+		return formulaStr
+	} else {
+		return ""
+	}
+}
+
+// GetFormulaMap 获取计算公式的map
+func GetFormulaMap() map[string]string {
+	funMap := make(map[string]string)
+	funMap["MAX"] = "[@@]"
+	funMap["MIN"] = "[@!]"
+	funMap["ABS"] = "[@#]"
+	funMap["CEIL"] = "[@$]"
+	funMap["COS"] = "[@%]"
+	funMap["FLOOR"] = "[@^]"
+	funMap["MOD"] = "[@&]"
+	funMap["POW"] = "[@*]"
+	funMap["ROUND"] = "[@(]"
+	return funMap
+}
+
+// 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:"指标对应标签"`
+	}
+	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
+}

+ 239 - 0
models/edb_calculate_tcz.go

@@ -0,0 +1,239 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateTcz 同差值
+func AddCalculateTcz(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to,err := o.Begin()
+	if err != nil{
+		return
+	}
+	defer func() {
+		if err != nil {
+			_=to.Rollback()
+		} else {
+			_=to.Commit()
+		}
+	}()
+	fmt.Println("req.EdbInfoId:", req.EdbInfoId)
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_TCZ
+		edbInfo.SourceName = "同差值"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_TCZ)
+		fmt.Println("dataTableName:", dataTableName)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+
+	fmt.Println("EdbInfoId:", req.FromEdbInfoId)
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	fmt.Println("Frequency:", fromEdbInfo.Frequency)
+	addSql := ` INSERT INTO edb_data_calculate_tcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	for _, av := range dateArr {
+		//fmt.Println("dateArr:", ak, av)
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, err := time.Parse(utils.FormatDate, av)
+			if err != nil {
+				return edbInfoId, err
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				if _, ok := existMap[edbCode+av]; !ok {
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+
+					val := TczSub(currentItem.Value, findItem.Value)
+					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+					isAdd = true
+				}
+				existMap[edbCode+av] = av
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i <= 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //下一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TczSub(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+								}
+								existMap[edbCode+av] = av
+								break
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						if _, ok := existMap[edbCode+av]; !ok {
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TczSub(currentItem.Value, findItem.Value)
+							addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+							isAdd = true
+						}
+						existMap[edbCode+av] = av
+						break
+					}
+				} else {
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i < 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TczSub(currentItem.Value, findItem.Value)
+
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TczSub(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+								}
+								existMap[edbCode+av] = av
+								break
+							} else {
+
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}
+
+// TczSub 计算同差值
+func TczSub(a, b float64) string {
+	af := decimal.NewFromFloat(a)
+	fmt.Println(af)
+	bf := decimal.NewFromFloat(b)
+	val, _ := af.Sub(bf).Float64()
+	valStr := utils.SubFloatToString(val, 4)
+	return valStr
+}

+ 156 - 0
models/edb_data_calculate_bp.go

@@ -0,0 +1,156 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//变频
+func AddCalculateBp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateBp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_BP
+		edbInfo.SourceName = "变频"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_BP)
+		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
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	fmt.Println("edbInfoIdStr:" + edbInfoIdStr)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_bp(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	dataLen := len(dataList)
+	fmt.Println("dataLen:", dataLen)
+
+	for i := 0; i < dataLen; i++ {
+		//当期
+		currentItem := dataList[i]
+		currentDate, _ := time.Parse(utils.FormatDate, currentItem.DataTime)
+		var day int
+		var preItem *EdbInfoSearchData
+		var preDate time.Time
+		if i == 0 {
+			day = int(time.Now().Sub(currentDate).Hours() / float64(24))
+			preDate = time.Now()
+		} else {
+			j := i - 1
+			if j < dataLen {
+				preItem = dataList[j]
+				preDate, _ = time.Parse(utils.FormatDate, preItem.DataTime)
+				day = int(preDate.Sub(currentDate).Hours() / float64(24))
+				utils.FileLog.Info("preItem.DataTime:" + preItem.DataTime + ";currentItem.DataTime" + currentItem.DataTime)
+			}
+		}
+		for k := 0; k <= day; k++ {
+			needDay := preDate.AddDate(0, 0, -k)
+			needDayStr := needDay.Format(utils.FormatDate)
+			existKey := edbCode + needDayStr
+			if _, ok := existMap[existKey]; !ok {
+				timestamp := needDay.UnixNano() / 1e6
+				timestampStr := fmt.Sprintf("%d", timestamp)
+				valStr := decimal.NewFromFloat(currentItem.Value).String()
+				addSql += GetAddSql(edbInfoIdStr, edbCode, needDayStr, timestampStr, valStr)
+				isAdd = true
+			}
+			existMap[existKey] = needDayStr
+		}
+		existKey := edbCode + currentItem.DataTime
+		if _, ok := existMap[existKey]; !ok {
+			currentDate, _ := time.Parse(utils.FormatDate, currentItem.DataTime)
+			timestamp := currentDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			valStr := decimal.NewFromFloat(currentItem.Value).String()
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currentItem.DataTime, timestampStr, valStr)
+			isAdd = true
+		}
+		existMap[existKey] = currentItem.DataTime
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}

+ 145 - 0
models/edb_data_calculate_hbz.go

@@ -0,0 +1,145 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//环比值
+func AddCalculateHbz(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string, formulaInt int) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateHbz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_HBZ
+		edbInfo.SourceName = "环比值"
+		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, err := o.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_HBZ)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = o.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	fmt.Println("edbInfoIdStr:" + edbInfoIdStr)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_hbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	dataLen := len(dataList)
+	for i := 0; i < dataLen; i++ {
+		j := i + formulaInt
+		if j < dataLen {
+			//当期
+			currentItem := dataList[i]
+			preItem := dataList[j]
+			fmt.Println("preItem.Value:", preItem.Value)
+			if currentItem != nil && preItem != nil && preItem.Value != 0 {
+				existKey := edbCode + currentItem.DataTime
+				if _, ok := existMap[existKey]; !ok {
+					currentDate, _ := time.Parse(utils.FormatDate, currentItem.DataTime)
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+					val := HbzDiv(currentItem.Value, preItem.Value)
+					if val != "" {
+						addSql += GetAddSql(edbInfoIdStr, edbCode, currentItem.DataTime, timestampStr, val)
+						isAdd = true
+					}
+				}
+				existMap[existKey] = currentItem.DataTime
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}
+
+//环比值,current:当期,pre:上期 公式: (当期-上期)/上期
+func HbzDiv(current, pre float64) string {
+	if pre == 0 {
+		return ""
+	}
+	currentVal := decimal.NewFromFloat(float64(current))
+	preVal := decimal.NewFromFloat(float64(pre))
+	val, _ := currentVal.Sub(preVal).Div(preVal).Float64()
+	valStr := utils.SubFloatToString(val, 4)
+	return valStr
+}

+ 151 - 0
models/edb_data_calculate_hcz.go

@@ -0,0 +1,151 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//环差值
+func AddCalculateHcz(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string, formulaInt int) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateHcz,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_HCZ
+		edbInfo.SourceName = "环差值"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_HCZ)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	fmt.Println("edbInfoIdStr:" + edbInfoIdStr)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_hcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	dataLen := len(dataList)
+	fmt.Println("dataLen:", dataLen)
+	for i := 0; i < dataLen; i++ {
+		j := i + formulaInt
+		if j < dataLen {
+			//当期
+			currentItem := dataList[i]
+			preItem := dataList[j]
+			if currentItem != nil && preItem != nil {
+				existKey := edbCode + currentItem.DataTime
+				if _, ok := existMap[existKey]; !ok {
+					currentDate, _ := time.Parse(utils.FormatDate, currentItem.DataTime)
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+					val := ""
+					if preItem.Value == 0 {
+						val = "0"
+					} else {
+						val = HczDiv(currentItem.Value, preItem.Value)
+					}
+					if val != "" {
+						addSql += GetAddSql(edbInfoIdStr, edbCode, currentItem.DataTime, timestampStr, val)
+						isAdd = true
+					}
+				}
+				existMap[existKey] = currentItem.DataTime
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}
+
+// HczDiv 环差值计算,current:当期,pre:上期 公式:当期-上期
+func HczDiv(current, pre float64) string {
+	if pre == 0 {
+		return ""
+	}
+	currentVal := decimal.NewFromFloat(current)
+	preVal := decimal.NewFromFloat(pre)
+	val, _ := currentVal.Sub(preVal).Float64()
+	valStr := utils.SubFloatToString(val, 4)
+	return valStr
+}

+ 285 - 0
models/edb_data_calculate_ljztbpj.go

@@ -0,0 +1,285 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"sort"
+	"time"
+)
+
+// EdbDataCalculateLjztbpj 累计值同比拼接数据结构体
+type EdbDataCalculateLjztbpj struct {
+	EdbDataId     int `orm:"column(edb_data_id);pk"`
+	EdbInfoId     int
+	EdbCode       string
+	DataTime      string
+	Value         float64
+	Status        int
+	CreateTime    time.Time
+	ModifyTime    time.Time
+	DataTimestamp int64
+}
+
+// AddCalculateLjztbpj 新增累计值同比拼接数据
+func AddCalculateLjztbpj(req *EdbInfoCalculateBatchSaveReq, firstEdbInfo, secondEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateLjztbpj,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	//待拼接指标map
+	pjDataMap := make(map[string]float64)
+
+	//拼接指标的日期切片数据
+	pjEdbDataTimeList := make([]string, 0)
+
+	//最近开始的时间
+	var lastDateTime time.Time
+	//获取待拼接指标
+	{
+		var condition string
+		var pars []interface{}
+
+		//获取待拼接指标最近的个12月31日有值的年份
+		condition += " AND data_time like ? AND edb_info_id=? "
+		pars = append(pars, "%12-31", firstEdbInfo.EdbInfoId)
+
+		lastEdbData, tmpErr := GetLastEdbData(condition, pars, firstEdbInfo.Source)
+		if tmpErr != nil {
+			return edbInfoId, tmpErr
+		}
+		lastDateTime, _ = time.ParseInLocation(utils.FormatDate, lastEdbData.DataTime, time.Local)
+
+		//获取待拼接指标的数据列表
+		condition = ``
+		pars = make([]interface{}, 0)
+		condition += " AND data_time <= ? AND edb_info_id=? "
+		pars = append(pars, lastEdbData.DataTime, firstEdbInfo.EdbInfoId)
+
+		firstDataList, tmpErr := GetEdbDataListAll(condition, pars, firstEdbInfo.Source, 0)
+		if tmpErr != nil {
+			return edbInfoId, tmpErr
+		}
+
+		for _, v := range firstDataList {
+			pjDataMap[v.DataTime] = v.Value
+			pjEdbDataTimeList = append(pjEdbDataTimeList, v.DataTime)
+		}
+	}
+
+	var edbInfo *EdbInfo
+	if req.EdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			SourceName:       "累计值同比拼接",
+			Source:           utils.DATA_SOURCE_CALCULATE_LJZTBPJ,
+			EdbCode:          edbCode,
+			EdbName:          req.EdbName,
+			EdbNameSource:    req.EdbName,
+			Frequency:        req.Frequency,
+			Unit:             req.Unit,
+			StartDate:        firstEdbInfo.StartDate,
+			EndDate:          firstEdbInfo.EndDate,
+			ClassifyId:       req.ClassifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			CalculateFormula: lastDateTime.Format(utils.FormatDate),
+			EdbType:          2,
+		}
+		newEdbInfoId, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+	} else {
+		edbInfoId = req.EdbInfoId
+		//查询
+		tmpEdbInfo, tmpErr := GetEdbInfoById(edbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		tmpEdbInfo.EdbName = req.EdbName
+		tmpEdbInfo.ClassifyId = req.ClassifyId
+		tmpEdbInfo.Frequency = req.Frequency
+		tmpEdbInfo.Unit = req.Unit
+		tmpEdbInfo.CalculateFormula = req.Formula
+
+		edbInfo = tmpEdbInfo
+
+		//删除指标数据
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_LJZTBPJ)
+		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 0, err
+		}
+
+		//删除指标关系
+		sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id=? `
+		_, err = to.Raw(sql, edbInfoId).Exec()
+	}
+
+	//关联关系
+
+	//拼接指标
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfoId
+		calculateMappingItem.FromEdbInfoId = firstEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = firstEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = firstEdbInfo.EdbName
+		calculateMappingItem.FromSource = firstEdbInfo.Source
+		calculateMappingItem.FromSourceName = firstEdbInfo.SourceName
+		calculateMappingItem.FromTag = "A"
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//同比值指标
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfoId
+		calculateMappingItem.FromEdbInfoId = secondEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = secondEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = secondEdbInfo.EdbName
+		calculateMappingItem.FromSource = secondEdbInfo.Source
+		calculateMappingItem.FromSourceName = secondEdbInfo.SourceName
+		calculateMappingItem.FromTag = "B"
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//同比值指标map
+	tbzEdbDataMap := make(map[string]float64)
+
+	//同比值日期切片列表
+	tbzEdbDataTimeList := make([]string, 0)
+
+	//同比值指标
+	{
+		var condition string
+		var pars []interface{}
+
+		condition += " AND data_time > ? AND edb_info_id = ? "
+		pars = append(pars, lastDateTime, secondEdbInfo.EdbInfoId)
+
+		//同比值指标的数据列表
+		secondDataList, tmpErr := GetEdbDataListAll(condition, pars, secondEdbInfo.Source, 0)
+		if tmpErr != nil {
+			return edbInfoId, tmpErr
+		}
+
+		for _, v := range secondDataList {
+			tbzEdbDataMap[v.DataTime] = v.Value
+			tbzEdbDataTimeList = append(tbzEdbDataTimeList, v.DataTime)
+		}
+	}
+
+	sort.Strings(tbzEdbDataTimeList)
+	for _, v := range tbzEdbDataTimeList {
+		tbzDataTime, _ := time.ParseInLocation(utils.FormatDate, v, time.Local)
+
+		//获取拼接指标上一年同一天的数据
+		var pjDataTime time.Time
+		if tbzDataTime.Month() == 2 {
+			pjDataTime = tbzDataTime.AddDate(0, -11, 0)
+			pjDataTime = time.Date(pjDataTime.Year(), pjDataTime.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 0, -1)
+		} else {
+			pjDataTime = tbzDataTime.AddDate(-1, 0, 0)
+		}
+
+		//如果存在数据,那么就去计算当前的数据
+		if pjDataValue, ok := pjDataMap[pjDataTime.Format(utils.FormatDate)]; ok {
+			tbzDataValue := tbzEdbDataMap[v] //同比值
+			currValue := pjDataValue * (1 + tbzDataValue/100)
+
+			currValue, _ = decimal.NewFromFloat(currValue).Truncate(4).Float64()
+			//将计算后的数据存入待拼接指标map里面,以便后续计算
+			pjDataMap[v] = currValue
+
+			pjEdbDataTimeList = append(pjEdbDataTimeList, v)
+		}
+	}
+
+	addDataList := make([]*EdbDataCalculateLjztbpj, 0)
+
+	//日期排序下
+	sort.Strings(pjEdbDataTimeList)
+	//这么做的目的是为了让数据插入的时候,可以正序插入(业务上没啥卵用,就是为了让我看数据的时候舒服点,手动狗头-_-|)
+	for _, dataTime := range pjEdbDataTimeList {
+		if dataValue, ok := pjDataMap[dataTime]; ok {
+			//时间戳
+			currentDate, _ := time.Parse(utils.FormatDate, dataTime)
+			timestamp := currentDate.UnixNano() / 1e6
+
+			edbDataLjztbpj := &EdbDataCalculateLjztbpj{
+				EdbInfoId:     edbInfoId,
+				EdbCode:       edbInfo.EdbCode,
+				DataTime:      dataTime,
+				Value:         dataValue,
+				Status:        1,
+				CreateTime:    time.Now(),
+				ModifyTime:    time.Now(),
+				DataTimestamp: timestamp,
+			}
+			addDataList = append(addDataList, edbDataLjztbpj)
+		}
+	}
+
+	//数据入库
+	tmpAddDataList := make([]*EdbDataCalculateLjztbpj, 0)
+	for _, v := range addDataList {
+		tmpAddDataList = append(tmpAddDataList, v)
+
+		if len(tmpAddDataList) >= 200 {
+			_, tmpErr := to.InsertMulti(len(tmpAddDataList), tmpAddDataList)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			//重新初始化需要加入的数据切片
+			tmpAddDataList = make([]*EdbDataCalculateLjztbpj, 0)
+		}
+	}
+	//最后如果还有需要新增的数据,那么就统一入库
+	if len(tmpAddDataList) > 0 {
+		_, tmpErr := to.InsertMulti(len(tmpAddDataList), tmpAddDataList)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}

+ 199 - 0
models/edb_data_calculate_ljzzy.go

@@ -0,0 +1,199 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateLjzzy 累计值转月
+func AddCalculateLjzzy(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_LJZZY
+		edbInfo.SourceName = "累计值转月值"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return int(newEdbInfoId), err
+		}
+		edbInfoId = int(newEdbInfoId)
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_LJZZY)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	fmt.Println("EdbInfoId:", req.FromEdbInfoId)
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 1)
+	if err != nil {
+		return edbInfoId, err
+	}
+
+	yearMap := make(map[int]map[int]*EdbInfoSearchData)
+	dataLen := len(dataList)
+	for i := 0; i < dataLen; i++ {
+		item := dataList[i]
+		//日其中获取年
+		itemDate, err := time.Parse(utils.FormatDate, item.DataTime)
+		if err != nil {
+			return edbInfoId, err
+		}
+		year := itemDate.Year()
+		month := int(itemDate.Month())
+		if monthMap, yok := yearMap[year]; yok {
+			monthMap[month] = item
+			yearMap[year] = monthMap
+		} else {
+			monthMap = make(map[int]*EdbInfoSearchData)
+			monthMap[month] = item
+			yearMap[year] = monthMap
+		}
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_ljzzy(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	for yk, yv := range yearMap {
+		_, oneMonthOk := yv[1]
+		_, twoMonthOk := yv[2]
+		if !oneMonthOk && !twoMonthOk {
+			continue
+		}
+		for i := 1; i <= 12; i++ {
+			fmt.Println(yk, i, yv[i])
+			dataCurrentItem := yv[i]
+			fmt.Println("i:", i, yk, dataCurrentItem)
+			var date string
+			var val float64
+			if i == 1 || i == 2 {
+				if _, mok := yv[1]; mok { //1月有值
+					if i == 1 {
+						date = dataCurrentItem.DataTime
+						val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Float64() //a.Div(b).Float64()
+					}
+					if i == 2 {
+						dataOneItem := yv[1]
+						if dataCurrentItem != nil {
+							date = dataCurrentItem.DataTime
+							twoMonth := decimal.NewFromFloat(dataCurrentItem.Value)
+							oneMonth := decimal.NewFromFloat(dataOneItem.Value)
+							val, _ = twoMonth.Sub(oneMonth).Float64()
+						} else {
+							continue
+						}
+					}
+				} else { //1月无值
+					dataTwoItem := yv[2]
+					if i == 1 {
+						date = strconv.Itoa(yk) + "-01-31"
+						a := decimal.NewFromFloat(dataTwoItem.Value)
+						b := decimal.NewFromFloat(2.0)
+						val, _ = a.Div(b).Float64()
+					}
+					if i == 2 {
+						date = dataCurrentItem.DataTime
+						a := decimal.NewFromFloat(dataTwoItem.Value)
+						b := decimal.NewFromFloat(2.0)
+						val, _ = a.Div(b).Float64()
+					}
+				}
+			} else {
+				dataPreItem := yv[i-1]
+				if dataCurrentItem != nil && dataPreItem != nil {
+					date = dataCurrentItem.DataTime
+					//val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Sub(decimal.NewFromFloat(dataPreItem.Value)).Float64()
+					a := decimal.NewFromFloat(dataCurrentItem.Value)
+					b := decimal.NewFromFloat(dataPreItem.Value)
+					val, _ = a.Sub(b).Float64()
+				}
+			}
+
+			if date != "" {
+				dataTime, _ := time.Parse(utils.FormatDate, date)
+				timestamp := dataTime.UnixNano() / 1e6
+				timeStr := fmt.Sprintf("%d", timestamp)
+
+				if _, ok := existMap[edbCode+date]; !ok {
+					addSql += GetAddSql(edbInfoIdStr, edbCode, date, timeStr, utils.SubFloatToString(val, 4))
+					isAdd = true
+				}
+				existMap[edbCode+date] = date
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}

+ 157 - 0
models/edb_data_calculate_nszydbpjjs.go

@@ -0,0 +1,157 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//N数值移动平均计算
+func AddCalculateNszydpjjs(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string, formulaInt int) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to,err := o.Begin()
+	defer func() {
+		if err != nil {
+			_=to.Rollback()
+		} else {
+			_=to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_NSZYDPJJS
+		edbInfo.SourceName = "N数值移动平均计算"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_NSZYDPJJS)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err !=nil{
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_nszydpjjs(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+
+	arrLen := len(dateArr)
+	for ak, av := range dateArr {
+		//处理第一个值
+		var valArr []float64
+		if findItem, ok := dataMap[av]; ok {
+			valArr = append(valArr, findItem.Value)
+		} else {
+			continue
+		}
+		if ak+1 != arrLen {
+			//处理除第一个值之外的N-1个值
+			for i := 1; i < formulaInt; i++ {
+				arrIndex := ak + i
+				if arrIndex >= arrLen {
+					break
+				}
+				arrVal := dateArr[arrIndex]
+				if findItem, ok := dataMap[arrVal]; ok {
+					valArr = append(valArr, findItem.Value)
+				} else {
+					continue
+				}
+			}
+		}
+		valArrLen := len(valArr)
+		totalVal := decimal.NewFromFloat(0.00)
+		for _, v := range valArr {
+			newDecimal := decimal.NewFromFloat(v)
+			totalVal = totalVal.Add(newDecimal)
+		}
+		af := totalVal //decimal.NewFromFloat(totalVal)
+		bf := decimal.NewFromFloat(float64(valArrLen))
+		val, _ := af.Div(bf).Float64()
+		currentDate, err := time.Parse(utils.FormatDate, av)
+		if err != nil {
+			return edbInfoId, err
+		}
+
+		timestamp := currentDate.UnixNano() / 1e6
+		timestampStr := fmt.Sprintf("%d", timestamp)
+		valStr := utils.SubFloatToString(val, 4)
+		addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, valStr)
+		isAdd = true
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}

+ 149 - 0
models/edb_data_calculate_shift.go

@@ -0,0 +1,149 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//时间移位
+func AddCalculateTimeShift(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateTimeShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_TIME_SHIFT
+		edbInfo.SourceName = "时间移位"
+		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
+		edbInfo.MoveType = req.MoveType
+		edbInfo.MoveFrequency = req.MoveFrequency
+		newEdbInfoId, err := o.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_TIME_SHIFT)
+		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 0, err
+		}
+	}
+
+	var shiftDay int
+	formulaInt, _ := strconv.Atoi(req.Formula)
+	switch req.MoveFrequency {
+	case "天":
+		shiftDay = formulaInt
+	case "周":
+		shiftDay = formulaInt * 7
+	case "月":
+		shiftDay = formulaInt * 30
+	case "季":
+		shiftDay = formulaInt * 90
+	case "年":
+		shiftDay = formulaInt * 365
+	default:
+		shiftDay = formulaInt
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_time_shift(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	dataLen := len(dataList)
+	if req.MoveType == 2 {
+		shiftDay = -shiftDay
+	}
+	for i := 0; i < dataLen; i++ {
+		//当期
+		currentItem := dataList[i]
+		currentDate, _ := time.Parse(utils.FormatDate, currentItem.DataTime)
+
+		existKey := edbCode + currentItem.DataTime
+		if _, ok := existMap[existKey]; !ok {
+			newDate := currentDate.AddDate(0, 0, shiftDay)
+			timestamp := newDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			valStr := decimal.NewFromFloat(currentItem.Value).String()
+			addSql += GetAddSql(edbInfoIdStr, edbCode, newDate.Format(utils.FormatDate), timestampStr, valStr)
+			isAdd = true
+		}
+		existMap[existKey] = currentItem.DataTime
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}

+ 241 - 0
models/edb_data_calculate_tbz.go

@@ -0,0 +1,241 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculateTbz 同比值
+func AddCalculateTbz(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	fmt.Println("AddCalculateTbz")
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil{
+		return
+	}
+	defer func() {
+		if err != nil {
+			_=to.Rollback()
+		} else {
+			_=to.Commit()
+		}
+	}()
+	if req.EdbInfoId <= 0 {
+		edbInfo := new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_TBZ
+		edbInfo.SourceName = "同比值"
+		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, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = 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 {
+		edbInfoId = req.EdbInfoId
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_TBZ)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err !=nil{
+			return
+		}
+	}
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	//计算数据
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_info_id=? "
+	if req.EdbInfoId <= 0 {
+		pars = append(pars, req.FromEdbInfoId)
+	} else {
+		pars = append(pars, fromEdbInfo.EdbInfoId)
+	}
+	dataList, err := GetEdbDataListAll(condition, pars, fromEdbInfo.Source, 0)
+	if err != nil {
+		return edbInfoId, err
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO edb_data_calculate_tbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
+	var isAdd bool
+	existMap := make(map[string]string)
+	for _, av := range dateArr {
+		//fmt.Println(ak, av)
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			//当前日期
+			currentDate, err := time.Parse(utils.FormatDate, av)
+			if err != nil {
+				return edbInfoId, err
+			}
+			//上一年的日期
+			preDate := currentDate.AddDate(-1, 0, 0)
+			preDateStr := preDate.Format(utils.FormatDate)
+			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+				//dataTime, _ := time.Parse(utils.FormatDate, date)
+				if _, ok := existMap[edbCode+av]; !ok {
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+					val := TbzDiv(currentItem.Value, findItem.Value)
+					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+					isAdd = true
+					utils.FileLog.Info("同期找到:" + av + ";" + preDateStr)
+				}
+				existMap[edbCode+av] = av
+			} else {
+				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
+					nextDateDay := preDate
+					preDateDay := preDate
+					for i := 0; i <= 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TbzDiv(currentItem.Value, findItem.Value)
+
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+								}
+								existMap[edbCode+av] = av
+								break
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
+					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+						if _, ok := existMap[edbCode+av]; !ok {
+							timestamp := currentDate.UnixNano() / 1e6
+							timestampStr := fmt.Sprintf("%d", timestamp)
+							val := TbzDiv(currentItem.Value, findItem.Value)
+
+							addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+							isAdd = true
+						}
+						existMap[edbCode+av] = av
+						break
+					}
+				} else {
+					nextDateDay := preDate
+					preDateDay := preDate
+
+					for i := 0; i < 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							if _, ok := existMap[edbCode+av]; !ok {
+								timestamp := currentDate.UnixNano() / 1e6
+								timestampStr := fmt.Sprintf("%d", timestamp)
+								val := TbzDiv(currentItem.Value, findItem.Value)
+								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+								isAdd = true
+							}
+							existMap[edbCode+av] = av
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								if _, ok := existMap[edbCode+av]; !ok {
+									timestamp := currentDate.UnixNano() / 1e6
+									timestampStr := fmt.Sprintf("%d", timestamp)
+									val := TbzDiv(currentItem.Value, findItem.Value)
+									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+									isAdd = true
+								}
+								existMap[edbCode+av] = av
+								break
+							} else {
+								//fmt.Println("pre not find:", preDateStr, "i:", i)
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				}
+			}
+		}
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return edbInfoId, err
+		}
+	}
+	return
+}
+
+func TbzDiv(a, b float64) string {
+	var valStr string
+	if b != 0 {
+		af := decimal.NewFromFloat(float64(a))
+		bf := decimal.NewFromFloat(float64(b))
+		val, _ := af.Div(bf).Float64()
+		val = val - 1
+		valStr = utils.SubFloatToString(val, 4)
+	} else {
+		valStr = "0"
+	}
+	return valStr
+}

+ 238 - 0
models/edb_data_calculate_zjpj.go

@@ -0,0 +1,238 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_edb_lib/utils"
+	"time"
+)
+
+// EdbDataCalculateZjpj 直接拼接数据结构体
+type EdbDataCalculateZjpj struct {
+	EdbDataId     int `orm:"column(edb_data_id);pk"`
+	EdbInfoId     int
+	EdbCode       string
+	DataTime      string
+	Value         float64
+	Status        int
+	CreateTime    time.Time
+	ModifyTime    time.Time
+	DataTimestamp int64
+}
+
+// AddCalculateZjpj 新增直接拼接数据
+func AddCalculateZjpj(req *EdbInfoCalculateBatchSaveReq, firstEdbInfo, secondEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateZjpj,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var edbInfo *EdbInfo
+	if req.EdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			SourceName:       "直接拼接",
+			Source:           utils.DATA_SOURCE_CALCULATE_ZJPJ,
+			EdbCode:          edbCode,
+			EdbName:          req.EdbName,
+			EdbNameSource:    req.EdbName,
+			Frequency:        req.Frequency,
+			Unit:             req.Unit,
+			StartDate:        firstEdbInfo.StartDate,
+			EndDate:          firstEdbInfo.EndDate,
+			ClassifyId:       req.ClassifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			CalculateFormula: req.Formula,
+			EdbType:          2,
+		}
+		newEdbInfoId, err := to.Insert(edbInfo)
+		if err != nil {
+			return edbInfoId, err
+		}
+		edbInfoId = int(newEdbInfoId)
+	} else {
+		edbInfoId = req.EdbInfoId
+		//查询
+		tmpEdbInfo, tmpErr := GetEdbInfoById(edbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		tmpEdbInfo.EdbName = req.EdbName
+		tmpEdbInfo.ClassifyId = req.ClassifyId
+		tmpEdbInfo.Frequency = req.Frequency
+		tmpEdbInfo.Unit = req.Unit
+		tmpEdbInfo.CalculateFormula = req.Formula
+
+		edbInfo = tmpEdbInfo
+
+		//删除指标数据
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_ZJPJ)
+		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 0, err
+		}
+
+		//删除指标关系
+		sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id=? `
+		_, err = to.Raw(sql, edbInfoId).Exec()
+	}
+
+	//关联关系
+
+	//第一个指标
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfoId
+		calculateMappingItem.FromEdbInfoId = firstEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = firstEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = firstEdbInfo.EdbName
+		calculateMappingItem.FromSource = firstEdbInfo.Source
+		calculateMappingItem.FromSourceName = firstEdbInfo.SourceName
+		calculateMappingItem.FromTag = "A"
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	//第二个指标
+	{
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = 1
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfoId
+		calculateMappingItem.FromEdbInfoId = secondEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = secondEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = secondEdbInfo.EdbName
+		calculateMappingItem.FromSource = secondEdbInfo.Source
+		calculateMappingItem.FromSourceName = secondEdbInfo.SourceName
+		calculateMappingItem.FromTag = "B"
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		_, err = to.Insert(calculateMappingItem)
+		if err != nil {
+			return
+		}
+	}
+
+	addDataList := make([]*EdbDataCalculateZjpj, 0)
+
+	//拼接数据
+
+	//第一个指标
+	{
+		var condition string
+		var pars []interface{}
+
+		condition += " AND data_time < ? AND edb_info_id=? "
+		pars = append(pars, req.Formula, firstEdbInfo.EdbInfoId)
+
+		//第一个指标的数据列表
+		firstDataList, tmpErr := GetEdbDataListAll(condition, pars, firstEdbInfo.Source, 0)
+		if tmpErr != nil {
+			return edbInfoId, tmpErr
+		}
+
+		for _, v := range firstDataList {
+			//时间戳
+			currentDate, _ := time.Parse(utils.FormatDate, v.DataTime)
+			timestamp := currentDate.UnixNano() / 1e6
+
+			edbDataZjpj := &EdbDataCalculateZjpj{
+				EdbInfoId:     edbInfoId,
+				EdbCode:       edbInfo.EdbCode,
+				DataTime:      v.DataTime,
+				Value:         v.Value,
+				Status:        1,
+				CreateTime:    time.Now(),
+				ModifyTime:    time.Now(),
+				DataTimestamp: timestamp,
+			}
+			addDataList = append(addDataList, edbDataZjpj)
+			if len(addDataList) >= 200 {
+				_, tmpErr := to.InsertMulti(len(addDataList), addDataList)
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+				//重新初始化需要加入的数据切片
+				addDataList = make([]*EdbDataCalculateZjpj, 0)
+			}
+		}
+	}
+
+	//第二个指标
+	{
+		var condition string
+		var pars []interface{}
+
+		condition += " AND data_time >= ? AND edb_info_id = ? "
+		pars = append(pars, req.Formula, secondEdbInfo.EdbInfoId)
+
+		//第二个指标的数据列表
+		secondDataList, tmpErr := GetEdbDataListAll(condition, pars, secondEdbInfo.Source, 0)
+		if tmpErr != nil {
+			return edbInfoId, tmpErr
+		}
+
+		for _, v := range secondDataList {
+			//时间戳
+			currentDate, _ := time.Parse(utils.FormatDate, v.DataTime)
+			timestamp := currentDate.UnixNano() / 1e6
+
+			edbDataZjpj := &EdbDataCalculateZjpj{
+				EdbInfoId:     edbInfoId,
+				EdbCode:       edbInfo.EdbCode,
+				DataTime:      v.DataTime,
+				Value:         v.Value,
+				Status:        1,
+				CreateTime:    time.Now(),
+				ModifyTime:    time.Now(),
+				DataTimestamp: timestamp,
+			}
+			addDataList = append(addDataList, edbDataZjpj)
+			if len(addDataList) >= 200 {
+				_, tmpErr := to.InsertMulti(len(addDataList), addDataList)
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+				//重新初始化需要加入的数据切片
+				addDataList = make([]*EdbDataCalculateZjpj, 0)
+			}
+		}
+	}
+
+	//将剩余的数据入库
+	if len(addDataList) > 0 {
+		_, tmpErr := to.InsertMulti(len(addDataList), addDataList)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+	return
+}

+ 176 - 0
models/edb_info.go

@@ -0,0 +1,176 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type EdbInfo struct {
+	EdbInfoId        int    `orm:"column(edb_info_id);pk"`
+	SourceName       string `description:"来源名称"`
+	Source           int    `description:"来源id"`
+	EdbCode          string `description:"指标编码"`
+	EdbName          string `description:"指标名称"`
+	EdbNameSource    string `description:"指标名称来源"`
+	Frequency        string `description:"频率"`
+	Unit             string `description:"单位"`
+	StartDate        string `description:"起始日期"`
+	EndDate          string `description:"终止日期"`
+	ClassifyId       int    `description:"分类id"`
+	SysUserId        int
+	SysUserRealName  string
+	UniqueCode       string `description:"指标唯一编码"`
+	CreateTime       time.Time
+	ModifyTime       time.Time
+	MinValue         float64 `description:"指标最小值"`
+	MaxValue         float64 `description:"指标最大值"`
+	CalculateFormula string  `description:"计算公式"`
+	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
+	Sort             int     `description:"排序字段"`
+	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string  `description:"移动频度"`
+}
+
+// AddEdbInfo 添加指标
+func AddEdbInfo(item *EdbInfo) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// EdbInfoList 指标数据列表
+type EdbInfoList struct {
+	EdbInfoId       int    `orm:"column(edb_info_id);pk"`
+	SourceName      string `description:"来源名称"`
+	Source          int    `description:"来源id"`
+	EdbCode         string `description:"指标编码"`
+	EdbName         string `description:"指标名称"`
+	Frequency       string `description:"频率"`
+	Unit            string `description:"单位"`
+	StartDate       string `description:"起始日期"`
+	EndDate         string `description:"终止日期"`
+	ClassifyId      int    `description:"分类id"`
+	UniqueCode      string `description:"指标唯一编码"`
+	SysUserId       int    `description:"创建人id"`
+	SysUserRealName string `description:"创建人姓名"`
+	ModifyTime      string `description:"最新修改时间"`
+	EdbNameAlias    string `json:"-" description:"指标名称,别名"`
+	EdbType         int    `description:"指标类型:1:基础指标,2:计算指标"`
+	DataList        []*EdbData
+}
+
+// GetEdbInfoByName 根据指标名称获取所有的指标数据列表
+func GetEdbInfoByName(edbName string) (items []*EdbInfoList, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE edb_name=? `
+	_, err = o.Raw(sql, edbName).QueryRows(&items)
+	return
+}
+
+// ModifyEdbInfoNameSource 根据来源修改指标名称
+func ModifyEdbInfoNameSource(edbNameSource string, edbInfoId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  edb_info SET edb_name_source=? WHERE edb_info_id = ? `
+	_, err = o.Raw(sql, edbNameSource, edbInfoId).Exec()
+	return
+}
+
+// GetEdbInfoById 根据指标id获取指标信息
+func GetEdbInfoById(edbInfoId int) (item *EdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// EdbInfoSearchData
+type EdbInfoSearchData struct {
+	DataTime string  `description:"数据日期"`
+	Value    float64 `description:"数据"`
+}
+
+// GetEdbDataListAll 获取指标数据列表 order:1升序,其余值为降序
+func GetEdbDataListAll(condition string, pars []interface{}, source, order int) (item []*EdbInfoSearchData, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	tableName := GetEdbDataTableName(source)
+	sql = ` SELECT * FROM %s WHERE 1=1 `
+	sql = fmt.Sprintf(sql, tableName)
+
+	if condition != "" {
+		sql += condition
+	}
+	if order == 1 {
+		sql += ` ORDER BY data_time ASC `
+	} else {
+		sql += ` ORDER BY data_time DESC `
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}
+
+// EdbInfoMaxAndMinInfo 指标最新数据记录结构体
+type EdbInfoMaxAndMinInfo struct {
+	MinDate     string  `description:"最小日期"`
+	MaxDate     string  `description:"最大日期"`
+	MinValue    float64 `description:"最小值"`
+	MaxValue    float64 `description:"最大值"`
+	LatestValue float64 `description:"最新值"`
+}
+
+// GetEdbInfoMaxAndMinInfo 获取指标的最新数据记录信息
+func GetEdbInfoMaxAndMinInfo(source int, edbCode string) (item *EdbInfoMaxAndMinInfo, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	tableName := GetEdbDataTableName(source)
+	sql = ` SELECT MIN(data_time) AS min_date,MAX(data_time) AS max_date,MIN(value) AS min_value,MAX(value) AS max_value FROM %s WHERE edb_code=? `
+	sql = fmt.Sprintf(sql, tableName)
+	err = o.Raw(sql, edbCode).QueryRow(&item)
+
+	var latest_value float64
+	sql = ` SELECT value AS latest_value FROM %s WHERE edb_code=? ORDER BY data_time DESC LIMIT 1 `
+	sql = fmt.Sprintf(sql, tableName)
+	err = o.Raw(sql, edbCode).QueryRow(&latest_value)
+	item.LatestValue = latest_value
+	return
+}
+
+// ModifyEdbInfoMaxAndMinInfo 修改指标的最新数据信息
+func ModifyEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, edbInfoId).Exec()
+	return
+}
+
+// GetEdbDataCount 获取edb指标数据的数量; order:1升序,其余值为降序
+func GetEdbDataCount(condition string, pars []interface{}, source int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	tableName := GetEdbDataTableName(source)
+	sql = ` SELECT COUNT(1) AS count FROM %s WHERE 1=1 `
+	sql = fmt.Sprintf(sql, tableName)
+
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetLastEdbData 获取最近的一条指标数据
+func GetLastEdbData(condition string, pars []interface{}, source int) (item *EdbInfoSearchData, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	tableName := GetEdbDataTableName(source)
+	sql = ` SELECT * FROM %s WHERE 1=1 `
+	sql = fmt.Sprintf(sql, tableName)
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY data_time DESC `
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}

+ 31 - 0
models/edb_info_calculate_mapping.go

@@ -0,0 +1,31 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// EdbInfoCalculateMapping 计算指标于基础指标,关系表
+type EdbInfoCalculateMapping struct {
+	EdbInfoCalculateMappingId int       `orm:"column(edb_info_calculate_mapping_id);pk"`
+	EdbInfoId                 int       `description:"计算指标id"`
+	Source                    int       `description:"计算指标来源"`
+	SourceName                string    `description:"计算指标来源名称"`
+	EdbCode                   string    `description:"计算指标编码"`
+	FromEdbInfoId             int       `description:"基础指标id"`
+	FromEdbCode               string    `description:"基础指标编码"`
+	FromEdbName               string    `description:"基础指标名称"`
+	FromSource                int       `description:"基础指标来源"`
+	FromSourceName            string    `description:"基础指标来源名称"`
+	FromTag                   string    `description:"来源指标标签"`
+	Sort                      int       `description:"计算指标名称排序"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"修改时间"`
+}
+
+// AddEdbInfoCalculateMappingMulti 批量添加指标关系表
+func AddEdbInfoCalculateMappingMulti(items []*EdbInfoCalculateMapping) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(1, items)
+	return
+}

+ 6 - 0
models/response.go

@@ -0,0 +1,6 @@
+package models
+
+type AddEdbInfoResp struct {
+	EdbInfoId  int    `description:"指标ID"`
+	UniqueCode string `description:"指标唯一编码"`
+}

+ 29 - 0
services/base_from_calculate.go

@@ -0,0 +1,29 @@
+package services
+
+import (
+	"strings"
+)
+
+// CheckFormula 检测计算公式是否异常
+func CheckFormula(formula string) map[string]string {
+	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG"}
+
+	str := strings.ToUpper(formula)
+	for _, v := range mathFormula {
+		str = strings.Replace(str, v, "", -1)
+	}
+	str = strings.Replace(str, "(", "", -1)
+	str = strings.Replace(str, ")", "", -1)
+
+	byteMap := make(map[string]string)
+	for i := 0; i < len(str); i++ {
+		byteInt := str[i]
+		if byteInt >= 65 && byteInt <= 90 {
+			byteStr := string(byteInt)
+			if _, ok := byteMap[byteStr]; !ok {
+				byteMap[byteStr] = byteStr
+			}
+		}
+	}
+	return byteMap
+}