package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/shopspring/decimal"
	"hongze/hongze_edb_lib/utils"
	"strconv"
	"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:"移动频度"`
	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
	ServerUrl        string  `description:"服务器地址"`

	EdbInfoType int     `description:"指标类型,0:普通指标,1:预测指标"`
	EdbNameEn   string  `description:"英文指标名称"`
	UnitEn      string  `description:"英文单位"`
	LatestDate  string  `description:"数据最新日期"`
	LatestValue float64 `description:"数据最新值"`
	ChartImage  string  `description:"图表图片"`
	Calendar    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"`
	EdbInfoType     int                    `description:"指标类型,0:普通指标,1:预测指标"`
	SourceName      string                 `description:"来源名称"`
	Source          int                    `description:"来源id"`
	EdbCode         string                 `description:"指标编码"`
	EdbNameEn       string                 `description:"英文指标名称"`
	EdbName         string                 `description:"指标名称"`
	Frequency       string                 `description:"频率"`
	FrequencyEn     string                 `description:"英文频率"`
	Unit            string                 `description:"单位"`
	UnitEn          string                 `description:"英文单位"`
	StartDate       string                 `description:"起始日期"`
	EndDate         string                 `description:"终止日期"`
	LatestDate      string                 `description:"数据最新日期"`
	LatestValue     float64                `description:"数据最新值"`
	ClassifyId      int                    `description:"分类id"`
	UniqueCode      string                 `description:"指标唯一编码"`
	SysUserId       int                    `description:"创建人id"`
	SysUserRealName string                 `description:"创建人姓名"`
	ModifyTime      string                 `description:"最新修改时间"`
	CreateTime      string                 `description:"创建时间"`
	EdbNameAlias    string                 `json:"-" description:"指标名称,别名"`
	EdbType         int                    `description:"指标类型:1:基础指标,2:计算指标"`
	ChartImage      string                 `description:"图表图片"`
	RuleType        int                    `description:"预测规则,1:最新,2:固定值"`
	FixedValue      float64                `description:"固定值"`
	DataList        []*EdbData             `description:"实际指标数据"`
	PredictDataList []*EdbData             `description:"预测指标数据"`
	Button          EdbClassifyItemsButton `description:"操作权限"`
	IsEnEdb         bool                   `description:"是否展示英文标识"`
}

// EdbClassifyItemsButton 操作按钮
type EdbClassifyItemsButton struct {
	AddButton         bool `description:"是否可添加"`
	OpButton          bool `description:"是否可编辑"`
	DeleteButton      bool `description:"是否可删除"`
	MoveButton        bool `description:"是否可移动"`
	ShowEdbRelation   bool `description:"是否展示关联指标"`
	ShowChartRelation bool `description:"是否展示关联图表"`
}

// 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
}

// GetEdbInfoByIdList 根据指标id列表获取指标信息
func GetEdbInfoByIdList(edbInfoIdList []int) (items []*EdbInfo, err error) {
	num := len(edbInfoIdList)
	if num <= 0 {
		return
	}
	o := orm.NewOrm()
	sql := ` SELECT * FROM edb_info WHERE edb_info_id in (` + utils.GetOrmInReplace(num) + `) `
	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&items)
	return
}

// Update 更新EdbInfo信息
func (edbInfo *EdbInfo) Update(cols []string) (err error) {
	o := orm.NewOrm()
	_, err = o.Update(edbInfo, cols...)
	return
}

// EdbInfoSearchData
type EdbInfoSearchData struct {
	EdbDataId int     `description:"数据ID"`
	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
}

// GetEdbDataListAllByTo 根据事务链接获取指标数据列表 order:1升序,其余值为降序
func GetEdbDataListAllByTo(to orm.TxOrmer, condition string, pars []interface{}, source, order int) (item []*EdbInfoSearchData, err error) {
	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 = to.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:"最新值"`
	LatestDate  string  `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
}

// GetEdbInfoByEdbCode 根据指标code获取指标信息
func GetEdbInfoByEdbCode(source int, edbCode string) (item *EdbInfo, err error) {
	o := orm.NewOrm()
	sql := ` SELECT * FROM edb_info WHERE source=? AND edb_code=? `
	err = o.Raw(sql, source, edbCode).QueryRow(&item)
	return
}

// GetEdbInfoOnlyByEdbCode 仅根据指标code获取指标信息
func GetEdbInfoOnlyByEdbCode(edbCode string) (item *EdbInfo, err error) {
	o := orm.NewOrm()
	sql := ` SELECT * FROM edb_info WHERE edb_code=? `
	err = o.Raw(sql, edbCode).QueryRow(&item)
	return
}

// GetEdbInfoCalculateListByCondition 获取指标关系列表
func GetEdbInfoCalculateListByCondition(condition string, pars []interface{}) (items []*EdbInfoCalculateMapping, err error) {
	o := orm.NewOrm()

	sql := ` SELECT * FROM edb_info_calculate_mapping WHERE 1=1 `
	if condition != "" {
		sql += condition
	}
	_, err = o.Raw(sql, pars).QueryRows(&items)
	return
}

// GetEdbInfoCalculateCountByCondition 获取关联指标数量
func GetEdbInfoCalculateCountByCondition(condition string, pars []interface{}) (count int, err error) {
	o := orm.NewOrm()

	sql := ` SELECT COUNT(1) AS count FROM edb_info_calculate_mapping WHERE 1=1 `
	if condition != "" {
		sql += condition
	}
	err = o.Raw(sql, pars).QueryRow(&count)
	return
}

// 优化版本-处理数据精度问题
type EdbInfoSearchDataV1 struct {
	EdbDataId int    `description:"数据ID"`
	DataTime  string `description:"数据日期"`
	Value     string `description:"数据"`
}

// 优化版本-处理数据精度问题
func GetEdbDataListAllV1(condition string, pars []interface{}, source, order int) (item []*EdbInfoSearchDataV1, 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
}

// GetEdbDataListAllV1ByTo 通过事务链接获取数据列表
func GetEdbDataListAllV1ByTo(to orm.TxOrmer, condition string, pars []interface{}, source, order int) (item []*EdbInfoSearchDataV1, err error) {
	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 = to.Raw(sql, pars).QueryRows(&item)
	return
}

// GetEdbInfoByCondition 获取指标列表
func GetEdbInfoByCondition(condition string, pars []interface{}, order int) (item []*EdbInfo, err error) {
	o := orm.NewOrm()
	sql := ` SELECT * FROM edb_info WHERE 1=1 `
	if condition != "" {
		sql += condition
	}
	if order == 1 {
		sql += ` ORDER BY end_date ASC `
	} else {
		sql += ` ORDER BY edb_info_id ASC `
	}
	_, err = o.Raw(sql, pars).QueryRows(&item)
	return
}

// UnifiedModifyEdbInfoMaxAndMinInfo 统一修改指标的最大最小值
func UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo *EdbInfo) (err error, errMsg string) {
	// 修改最大最小值
	maxAndMinItem, err := GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.EdbCode)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			err = nil
			return
		}

		errMsg = "刷新指标失败!"
		err = errors.New("获取指标最大最小值失败,err:" + err.Error())
		return
	}

	if maxAndMinItem != nil {
		err = ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
		if err != nil {
			errMsg = "刷新指标失败!"
			err = errors.New("修改指标最大最小值失败,err:" + err.Error())
			return
		}
	}
	// 修改关联的预测指标
	go ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(edbInfo.EdbInfoId, maxAndMinItem)
	return
}

// UnifiedModifyPredictEdbInfoMaxAndMinInfo 统一修改预测运算指标的最大最小值
func UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo *EdbInfo, latestDateStr string, latestValue float64) (err error, errMsg string) {
	// 修改最大最小值
	maxAndMinItem, err := GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.EdbCode)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			err = nil
			return
		}

		errMsg = "刷新指标失败!"
		err = errors.New("获取指标最大最小值失败,err:" + err.Error())
		return
	}

	if maxAndMinItem != nil {
		maxAndMinItem.LatestDate = latestDateStr
		maxAndMinItem.LatestValue = latestValue
		err = ModifyPredictEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
		if err != nil {
			errMsg = "刷新指标失败!"
			err = errors.New("修改指标最大最小值失败,err:" + err.Error())
			return
		}
	}
	return
}

// GetChartPredictEdbInfoDataList 获取图表的预测指标的未来数据
func GetChartPredictEdbInfoDataList(predictEdbConf PredictEdbConf, latestDateStr string, lastDataValue float64, endDateStr, frequency string, order int) (predictEdbInfoData []*EdbInfoSearchData, err error) {
	endDate, err := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
	if err != nil {
		return
	}

	latestDate, err := time.ParseInLocation(utils.FormatDate, latestDateStr, time.Local)
	if err != nil {
		return
	}

	// 开始预测数据的时间
	startDate := latestDate

	dataValue := lastDataValue
	if predictEdbConf.RuleType == 2 {
		dataValue = predictEdbConf.FixedValue
	}
	//获取后面的预测数据
	dayList := getPredictEdbDayList(startDate, endDate, frequency)
	predictEdbInfoData = make([]*EdbInfoSearchData, 0)

	// order:1升序,其余值为降序
	if order == 1 {
		for k, v := range dayList {
			predictEdbInfoData = append(predictEdbInfoData, &EdbInfoSearchData{
				EdbDataId: predictEdbConf.PredictEdbInfoId + 10000000000 + k,
				DataTime:  v.Format(utils.FormatDate),
				Value:     dataValue,
			})
		}
	} else {
		lenDayList := len(dayList)
		if lenDayList > 0 {
			for i := lenDayList - 1; i >= 0; i-- {
				v := dayList[i]
				predictEdbInfoData = append(predictEdbInfoData, &EdbInfoSearchData{
					EdbDataId: predictEdbConf.PredictEdbInfoId + 10000000000 + i,
					DataTime:  v.Format(utils.FormatDate),
					Value:     dataValue,
				})
			}
		}
	}

	return
}

// GetChartPredictEdbInfoDataListByConfList 获取图表的预测指标的未来数据
func GetChartPredictEdbInfoDataListByConfList(predictEdbConfList []*PredictEdbConfAndData, filtrateStartDateStr, latestDateStr, endDateStr, frequency string, realPredictEdbInfoData []*EdbInfoSearchData) (predictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
	endDate, err := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
	if err != nil {
		return
	}

	latestDate, err := time.ParseInLocation(utils.FormatDate, latestDateStr, time.Local)
	if err != nil {
		return
	}

	// 开始预测数据的时间
	startDate := latestDate

	// 如果有筛选时间的话
	if filtrateStartDateStr != `` {
		filtrateStartDate, tmpErr := time.ParseInLocation(utils.FormatDate, filtrateStartDateStr, time.Local)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		//如果筛选时间晚于实际数据时间,那么就以筛选时间作为获取预测数据的时间
		if filtrateStartDate.After(latestDate) {
			startDate = filtrateStartDate.AddDate(0, 0, -1)
		}
	}

	//var dateArr []string
	// 对应日期的值
	existMap := make(map[string]float64)
	for _, v := range realPredictEdbInfoData {
		//dateArr = append(dateArr, v.DataTime)
		existMap[v.DataTime] = v.Value
	}

	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
	//dataValue := lastDataValue
	//预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值

	for _, predictEdbConf := range predictEdbConfList {
		dataEndTime := endDate
		if predictEdbConf.EndDate.Before(dataEndTime) {
			dataEndTime = predictEdbConf.EndDate
		}

		var tmpMinValue, tmpMaxValue float64 // 当前预测结果中的最大/最小值

		dayList := getPredictEdbDayList(startDate, dataEndTime, frequency)
		if len(dayList) <= 0 { // 如果未来没有日期的话,那么就退出当前循环,进入下一个循环
			continue
		}

		switch predictEdbConf.RuleType {
		case 1: //1:最新
			var lastDataValue float64 //最新值
			tmpAllData := make([]*EdbInfoSearchData, 0)
			tmpAllData = append(tmpAllData, realPredictEdbInfoData...)
			tmpAllData = append(tmpAllData, predictEdbInfoData...)
			lenTmpAllData := len(tmpAllData)
			if lenTmpAllData > 0 {
				lastDataValue = tmpAllData[lenTmpAllData-1].Value
			}
			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(predictEdbConf.PredictEdbInfoId, lastDataValue, dayList, predictEdbInfoData, existMap)
			tmpMaxValue = lastDataValue
			tmpMinValue = lastDataValue
		case 2: //2:固定值
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			dataValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(predictEdbConf.PredictEdbInfoId, dataValue, dayList, predictEdbInfoData, existMap)

			tmpMaxValue = dataValue
			tmpMinValue = dataValue
		case 3: //3:同比
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			tbValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTb(predictEdbConf.PredictEdbInfoId, tbValue, dayList, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 4: //4:同差
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			tcValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTc(predictEdbConf.PredictEdbInfoId, tcValue, dayList, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 5: //5:环比
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			hbValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHb(predictEdbConf.PredictEdbInfoId, hbValue, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 6: //6:环差
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			hcValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHc(predictEdbConf.PredictEdbInfoId, hcValue, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 7: //7:N期移动均值
			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(predictEdbConf.PredictEdbInfoId, nValue, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 8: //8:N期段线性外推值
			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleNLinearRegression(predictEdbConf.PredictEdbInfoId, nValue, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
			if err != nil {
				return
			}
		case 9: //9:动态环差”预测规则;
			hcDataMap := make(map[string]float64) //规则计算的环差值map

			if predictEdbConf.PredictEdbInfoId > 0 {
				tmpPredictEdbRuleDataList, tmpErr := GetPredictEdbRuleDataItemList(predictEdbConf.PredictEdbInfoId, predictEdbConf.ConfigId, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
				if tmpErr != nil {
					err = tmpErr
					return
				}
				for _, v := range tmpPredictEdbRuleDataList {
					hcDataMap[v.DataTime] = v.Value
				}
			} else {
				if len(predictEdbConf.DataList) <= 0 {
					return
				}
				for _, v := range predictEdbConf.DataList {
					currentDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
					if tmpErr != nil {
						continue
					}
					// 只处理时间段内的数据
					if currentDate.Before(startDate) || currentDate.After(endDate) {
						continue
					}
					hcDataMap[v.DataTime] = v.Value
				}
			}

			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTrendsHC(predictEdbConf.PredictEdbInfoId, dayList, realPredictEdbInfoData, predictEdbInfoData, hcDataMap, existMap)
		case 10: //10:根据 给定终值后插值 规则获取预测数据
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			finalValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleFinalValueHc(predictEdbConf.PredictEdbInfoId, finalValue, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 11: //11:根据 季节性 规则获取预测数据
			var seasonConf SeasonConf
			tmpErr := json.Unmarshal([]byte(predictEdbConf.Value), &seasonConf)
			if tmpErr != nil {
				err = errors.New("季节性配置信息异常:" + tmpErr.Error())
				return
			}
			calendar := "公历"
			if seasonConf.Calendar == "农历" {
				calendar = "农历"
			}
			yearList := make([]int, 0)
			//选择方式,1:连续N年;2:指定年份
			if seasonConf.YearType == 1 {
				if seasonConf.NValue < 1 {
					err = errors.New("连续N年不允许小于1")
					return
				}

				currYear := time.Now().Year()
				for i := 0; i < seasonConf.NValue; i++ {
					yearList = append(yearList, currYear-i-1)
				}
			} else {
				yearList = seasonConf.YearList
			}
			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleSeason(predictEdbConf.PredictEdbInfoId, yearList, calendar, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
			if err != nil {
				return
			}
		case 12: //12:根据 移动平均同比 规则获取预测数据
			var moveAverageConf MoveAverageConf
			tmpErr := json.Unmarshal([]byte(predictEdbConf.Value), &moveAverageConf)
			if tmpErr != nil {
				err = errors.New("季节性配置信息异常:" + tmpErr.Error())
				return
			}
			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleMoveAverageTb(predictEdbConf.PredictEdbInfoId, moveAverageConf.NValue, moveAverageConf.Year, dayList, realPredictEdbInfoData, predictEdbInfoData, existMap)
			if err != nil {
				return
			}
		case 13: //13:根据 同比增速差值 规则获取预测数据
			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			tbEndValue, _ := tmpValDecimal.Float64()
			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTbzscz(predictEdbConf.PredictEdbInfoId, tbEndValue, dayList, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
		case 14: //14:根据 一元线性拟合 规则获取预测数据
			var ruleConf RuleLineNhConf
			err = json.Unmarshal([]byte(predictEdbConf.Value), &ruleConf)
			if err != nil {
				err = errors.New("一元线性拟合规则配置信息异常:" + err.Error())
				return
			}

			// 规则计算的拟合残差值map
			newNhccDataMap := make(map[string]float64)
			if predictEdbConf.PredictEdbInfoId > 0 { //已经生成的动态数据
				tmpPredictEdbRuleDataList, tmpErr := GetPredictEdbRuleDataItemList(predictEdbConf.PredictEdbInfoId, predictEdbConf.ConfigId, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
				if tmpErr != nil {
					err = tmpErr
					return
				}
				for _, v := range tmpPredictEdbRuleDataList {
					newNhccDataMap[v.DataTime] = v.Value
				}
			} else { //未生成的动态数据,需要使用外部传入的数据进行计算
				newNhccDataMap, err, _ = getCalculateNhccData(append(realPredictEdbInfoData, predictEdbInfoData...), ruleConf)
			}

			predictEdbInfoData, tmpMinValue, tmpMaxValue, err = GetChartPredictEdbInfoDataListByRuleLineNh(predictEdbConf.PredictEdbInfoId, dayList, realPredictEdbInfoData, predictEdbInfoData, newNhccDataMap, existMap)
			if err != nil {
				return
			}
		}
		//startDate = dataEndTime.AddDate(0, 0, 1)
		if startDate.Before(dataEndTime) {
			startDate = dataEndTime
		}
		if tmpMinValue < minValue {
			minValue = tmpMinValue
		}
		if tmpMaxValue < maxValue {
			maxValue = tmpMaxValue
		}
	}

	return
}

// GetPredictEdbDayList 获取预测指标日期列表
func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
	switch frequency {
	case "日度":
		for currDate := startDate.AddDate(0, 0, 1); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 1) {
			//周六、日排除
			if currDate.Weekday() == time.Sunday || currDate.Weekday() == time.Saturday {
				continue
			}
			dayList = append(dayList, currDate)
		}
	case "周度":
		//nextDate := startDate.AddDate(0, 0, 7)
		for currDate := startDate.AddDate(0, 0, 7); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 7) {
			dayList = append(dayList, currDate)
		}
	case "月度":
		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
			if !currDate.After(endDate) && !currDate.Equal(startDate) {
				dayList = append(dayList, currDate)
			}
			currDate = currDate.AddDate(0, 0, 1)
		}
	case "年度":
		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
			currDate = time.Date(currDate.Year()+1, 12, 31, 0, 0, 0, 0, time.Now().Location())
			if !currDate.After(endDate) && !currDate.Equal(startDate) {
				dayList = append(dayList, currDate)
			}
		}
	}
	return
}

// GetPredictDataListByPredictEdbInfo 根据预测指标信息获取预测指标的数据,order:1升序,其余值为降序
func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int, startDate string) (dataList []*EdbInfoSearchData, sourceEdbInfoItem *EdbInfo, err error, errMsg string) {
	// 查找该预测指标配置
	predictEdbConfList, err := GetPredictEdbConfAndDataListById(edbInfo.EdbInfoId)
	if err != nil && err.Error() != utils.ErrNoRow() {
		errMsg = "获取预测指标配置信息失败"
		return
	}
	if len(predictEdbConfList) == 0 {
		errMsg = "获取预测指标配置信息失败"
		err = errors.New(errMsg)
		return
	}
	predictEdbConf := predictEdbConfList[0]

	// 来源指标
	sourceEdbInfoItem, err = GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			errMsg = "找不到来源指标信息"
			err = errors.New(errMsg)
		}
		return
	}

	dataList, err, errMsg = GetPredictDataListByPredictEdbConfList(edbInfo, sourceEdbInfoItem, predictEdbConfList, order, startDate)

	return
}

// GetPredictDataListByPredictEdbConfList 根据预测指标信息获取预测指标的数据,order:1升序,其余值为降序
func GetPredictDataListByPredictEdbConfList(edbInfo, sourceEdbInfoItem *EdbInfo, predictEdbConfList []*PredictEdbConfAndData, order int, startDate string) (dataList []*EdbInfoSearchData, err error, errMsg string) {

	allDataList := make([]*EdbInfoSearchData, 0)
	//获取指标数据(实际已生成)
	var condition string
	var pars []interface{}
	condition += " AND edb_info_id=? "
	pars = append(pars, sourceEdbInfoItem.EdbInfoId)
	if startDate != "" {
		condition += " AND data_time>=? "
		pars = append(pars, startDate)
	}
	tmpDataList, err := GetEdbDataListAll(condition, pars, sourceEdbInfoItem.Source, 1)
	if err != nil {
		return
	}
	// 如果选择了日期,那么需要筛选所有的数据,用于未来指标的生成
	if startDate != `` {
		allDataList, err = GetEdbDataListAll(" AND edb_info_id=? ", []interface{}{sourceEdbInfoItem.EdbInfoId}, sourceEdbInfoItem.Source, 1)
		if err != nil {
			return
		}
	} else {
		allDataList = tmpDataList
	}

	// 获取预测指标未来的数据
	predictDataList := make([]*EdbInfoSearchData, 0)

	endDateStr := edbInfo.EndDate //预测指标的结束日期

	var predictMinValue, predictMaxValue float64
	// 如果有配置的预测规则,那么就进行预测
	if len(predictEdbConfList) > 0 {
		predictDataList, predictMinValue, predictMaxValue, err = GetChartPredictEdbInfoDataListByConfList(predictEdbConfList, startDate, sourceEdbInfoItem.LatestDate, endDateStr, edbInfo.Frequency, allDataList)
		if err != nil {
			return
		}
	}

	//order:1升序,其余值为降序
	if order == 1 {
		dataList = append(tmpDataList, predictDataList...)
	} else {
		// 先倒序预测数据
		lenPredictDataList := len(predictDataList)
		if lenPredictDataList > 0 {
			for k := range predictDataList {
				dataList = append(dataList, predictDataList[lenPredictDataList-k-1])
			}
		}

		// 接着倒序实际指标
		lenDataList := len(tmpDataList)
		for k := range tmpDataList {
			dataList = append(dataList, tmpDataList[lenDataList-k-1])
		}
	}
	if len(predictDataList) > 0 {
		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
		if edbInfo.MinValue > predictMinValue {
			edbInfo.MinValue = predictMinValue
		}

		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
		if edbInfo.MaxValue < predictMaxValue {
			edbInfo.MaxValue = predictMaxValue
		}
	}
	return
}

// GetPredictEdbDataListAll 获取该预测指标所有的数据 ,order:1升序,其余值为降序
func GetPredictEdbDataListAll(edbInfo *EdbInfo, order int) (items []*EdbInfoSearchData, err error) {
	if edbInfo.Source == utils.DATA_SOURCE_PREDICT { //普通的预测指标是没有入库数据的,直接往配置里面获取
		items, _, err, _ = GetPredictDataListByPredictEdbInfo(edbInfo, 1, "")
	} else {
		var condition string
		var pars []interface{}
		condition += " AND edb_info_id=? "
		pars = append(pars, edbInfo.EdbInfoId)
		items, err = GetEdbDataListAll(condition, pars, edbInfo.Source, order)
	}
	return
}

// GetPredictEdbDataListAllByStartDate 根据开始日期获取该预测指标所有的数据 ,order:1升序,其余值为降序
func GetPredictEdbDataListAllByStartDate(edbInfo *EdbInfo, order int, startDate string) (items []*EdbInfoSearchData, err error) {
	if edbInfo.Source == utils.DATA_SOURCE_PREDICT { //普通的预测指标是没有入库数据的,直接往配置里面获取
		items, _, err, _ = GetPredictDataListByPredictEdbInfo(edbInfo, order, startDate)
	} else {
		var condition string
		var pars []interface{}
		condition += " AND edb_info_id=? "
		pars = append(pars, edbInfo.EdbInfoId)
		if startDate != "" {
			condition += " AND data_time>=? "
			pars = append(pars, startDate)
		}
		items, err = GetEdbDataListAll(condition, pars, edbInfo.Source, order)
	}
	return
}

// ModifyPredictEdbInfoMaxAndMinInfo 修改预测指标的最新数据信息
func ModifyPredictEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
	o := orm.NewOrm()
	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id=? `
	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.LatestDate, item.LatestValue, edbInfoId).Exec()
	return
}

// ModifyCalculateEdbInfo 修改计算指标信息
func ModifyCalculateEdbInfo(edbName, frequency, unit, calculateFormula string, classifyId, edbInfoId int) (err error) {
	o := orm.NewOrm()
	sql := ` UPDATE  edb_info
			SET
			  edb_name =?,
			  edb_name_source =?,
			  frequency = ?,
			  unit = ?,
			  classify_id = ?,
			  calculate_formula=?,
			  modify_time = NOW()
			WHERE edb_info_id = ?`
	_, err = o.Raw(sql, edbName, edbName, frequency, unit, classifyId, calculateFormula, edbInfoId).Exec()
	return
}

func GetEdbInfoItemByCondition(condition string, pars []interface{}) (item *EdbInfoList, err error) {
	o := orm.NewOrm()
	sql := ` SELECT * FROM edb_info WHERE 1=1 `
	if condition != "" {
		sql += condition
	}
	err = o.Raw(sql, pars).QueryRow(&item)
	return
}