Browse Source

Merge branch 'master' into feature/eta2.1.2_edb_predit

xyxie 4 months ago
parent
commit
dc04a8431b

+ 15 - 0
controllers/chart.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_chart_lib/models/data_manage/excel"
 	"eta/eta_chart_lib/services/data"
 	"eta/eta_chart_lib/services/data/cross_variety"
+	"eta/eta_chart_lib/services/data/range_analysis"
 	dwmini "eta/eta_chart_lib/services/dw_mini"
 	"eta/eta_chart_lib/utils"
 	"fmt"
@@ -122,6 +123,13 @@ func (this *ChartController) ChartInfoDetail() {
 			br.ErrMsg = errMsg
 			return
 		}
+	case utils.CHART_SOURCE_RANGE_ANALYSIS:
+		resp, isOk, msg, errMsg = GetRangeAnalysisChartInfoDetailFromUniqueCode(chartInfo, key, this.Lang)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
@@ -221,6 +229,13 @@ func (this *ChartController) ChartInfoRefresh() {
 			br.ErrMsg = "刷新失败,Err:" + err.Error()
 			return
 		}
+	case utils.CHART_SOURCE_RANGE_ANALYSIS:
+		//// 刷新相关性图表
+		if _, e := range_analysis.ChartInfoRefresh(chartInfo.ChartInfoId, ""); e != nil {
+			br.Msg = "刷新失败"
+			br.ErrMsg = "刷新相关性图表失败, Err:" + e.Error()
+			return
+		}
 	default:
 		err = data.ChartInfoRefreshV2(chartInfo.ChartInfoId)
 	}

+ 7 - 0
controllers/chart_auth.go

@@ -99,6 +99,13 @@ func (this *ChartAuthController) ChartInfoDetail() {
 			br.ErrMsg = errMsg
 			return
 		}
+	case utils.CHART_SOURCE_RANGE_ANALYSIS:
+		resp, isOk, msg, errMsg = GetRangeAnalysisChartInfoDetailFromUniqueCode(chartInfo, key, this.Lang)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"

+ 84 - 0
controllers/chart_common.go

@@ -17,6 +17,7 @@ import (
 	future_goodServ "eta/eta_chart_lib/services/data/future_good"
 	"eta/eta_chart_lib/services/data/line_equation"
 	lineFeatureServ "eta/eta_chart_lib/services/data/line_feature"
+	"eta/eta_chart_lib/services/data/range_analysis"
 	dwmini "eta/eta_chart_lib/services/dw_mini"
 	"eta/eta_chart_lib/utils"
 	"fmt"
@@ -129,6 +130,13 @@ func (this *ChartController) CommonChartInfoDetailFromUniqueCode() {
 			br.ErrMsg = errMsg
 			return
 		}
+	case utils.CHART_SOURCE_RANGE_ANALYSIS:
+		resp, isOk, msg, errMsg = GetRangeAnalysisChartInfoDetailFromUniqueCode(chartInfo, key, this.Lang)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
@@ -935,3 +943,79 @@ func (this *ChartController) CorrelationChartInfoRefresh() {
 	br.Success = true
 	br.Msg = "刷新成功"
 }
+
+// GetRangeAnalysisChartInfoDetailFromUniqueCode 根据编码获取区间计算图表详情
+func GetRangeAnalysisChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, key, lang string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
+	resp = new(models.ChartInfoDetailResp)
+	// 获取主题样式
+	chartTheme, err := data.GetChartThemeConfig(chartInfo.ChartThemeId, utils.CHART_SOURCE_DEFAULT, 1)
+	if err != nil {
+		msg = "获取失败"
+		errMsg = "获取主题信息失败,Err:" + err.Error()
+		return
+	}
+	chartInfo.ChartThemeStyle = chartTheme.Config
+	chartInfo.ChartThemeId = chartTheme.ChartThemeId
+
+	chartInfoId := chartInfo.ChartInfoId
+
+	// 获取指标信息
+	//chartInfo.CorrelationLeadUnit = req.LeadUnit
+	edbInfoMappingList, err := models.GetChartEdbMappingList(chartInfoId)
+	if err != nil {
+		msg = "获取失败"
+		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	dateType := chartInfo.DateType
+	// 开始/结束日期
+	startYear := chartInfo.StartYear
+	startDate := chartInfo.StartDate
+	endDate := chartInfo.EndDate
+
+	// 区间计算图表配置校验
+	var extraConfig models.ChartRangeAnalysisExtraConf
+	if chartInfo.ExtraConfig == `` {
+		msg = "配置信息错误"
+		return
+	}
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		msg = "配置信息错误"
+		errMsg = "图表配置信息错误,Err:" + err.Error()
+		return
+	}
+
+	// 获取图表数据
+	if len(edbInfoMappingList) == 0 {
+		msg = "获取失败"
+		errMsg = "图表没有指标,无法计算"
+		return
+	}
+
+	// 获取图表x轴y轴
+	edbList, xEdbIdValue, dataResp, e := range_analysis.GetChartDataByEdbInfoList(chartInfoId, dateType, startYear, startDate, endDate, edbInfoMappingList, &extraConfig)
+	if e != nil {
+		msg = "获取失败"
+		errMsg = "获取区间计算图表, 图表计算值失败, Err:" + e.Error()
+		return
+	}
+
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.DataResp = dataResp
+
+	if utils.Re == nil {
+		jsonData, _ := json.Marshal(resp)
+		utils.Rc.Put(key, jsonData, 10*time.Minute)
+	}
+
+	isOk = true
+	return
+}

+ 6 - 1
controllers/excel_info.go

@@ -11,9 +11,10 @@ import (
 	"eta/eta_chart_lib/services/excel"
 	"eta/eta_chart_lib/utils"
 	"fmt"
-	"github.com/shopspring/decimal"
 	"strings"
 	"time"
+
+	"github.com/shopspring/decimal"
 )
 
 // ExcelInfoController excel表格
@@ -179,6 +180,10 @@ func (this *ExcelInfoController) GetTableDetail() {
 	}
 
 	tableData = excel.HandleTableCell(tableData)
+	tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
+	if err != nil {
+		utils.FileLog.Info("表格管理规则处理失败,HandleRuleToTableCell err:", err.Error())
+	}
 	config := response.ExcelTableDetailConfigResp{
 		FontSize: 9,
 	}

+ 2 - 0
models/chart.go

@@ -119,6 +119,8 @@ type ChartEdbInfoMapping struct {
 	ConvertValue        float64 `description:"数据转换值"`
 	ConvertUnit         string  `description:"数据转换单位"`
 	ConvertEnUnit       string  `description:"数据转换单位"`
+	ClassifyId          int     `description:"分类id"`
+	IsJoinPermission    int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 }
 
 func GetChartEdbMappingList(chartInfoId int) (list []*ChartEdbInfoMapping, err error) {

+ 118 - 0
models/chart_info_range_analysis.go

@@ -0,0 +1,118 @@
+package models
+
+import (
+	"time"
+)
+
+type ChartRangeAnalysisExtraConf struct {
+	EdbInfoMode           int                                `description:"指标模式 0: 单指标,1: 多指标"`
+	DateRangeType         int                                `description:"区间划分类型 0:智能划分,1:手工划分,2:跨年划分"`
+	AutoDateConf          ChartRangeAnalysisAutoDateConf     `description:"智能划分时间区间配置"`
+	ManualDateConf        []ChartRangeAnalysisManualDateConf `description:"手工划分时间区间配置"`
+	YearDateConf          ChartRangeAnalysisYearDateConf     `description:"跨年划分时间区间配置"`
+	CalculateType         int                                `description:"计算类型 0: 区间均值,1: 区间累计值,2:区间涨幅,3:区间年化增长率,4:区间最大值,5:区间最小值"`
+	UnNormalDataDealType  int                                `description:"异常值处理配置 0:不处理,1:剔除,2替换"`
+	UnNormalDataConf      ChartRangeAnalysisDeleteDataConf
+	DataConvertType       int                               `description:"数据转换类型 0不转, 1乘 2除 3对数"`
+	DataConvertConf       ChartRangeAnalysisDataConvertConf `description:"数据转换详情"`
+	SeriesName            string                            `description:"指标系列名称"`
+	EdbInfoType           int                               `description:"指标类型:0普通指标,1预测指标"`
+	MultipleGraphConfigId int                               `description:"配置ID"`
+}
+
+type ChartRangeAnalysisAutoDateChangeConf struct {
+	BaseDateType int `description:"基准日期类型:0指标日期,1系统日期"`
+	MoveForward  int `description:"前移的期数"`
+	DateChange   []*EdbDataDateChangeConf
+}
+
+type EdbDataDateChangeConf struct {
+	Year         int
+	Month        int
+	Day          int
+	Frequency    string `description:"频度变换"`
+	FrequencyDay string `description:"频度的固定日期"`
+	ChangeType   int    `description:"日期变换类型1日期位移,2指定频率"`
+}
+
+type ChartRangeAnalysisDeleteDataConf struct {
+	Formula      string  `description:"比较符号:=、>、<、>=、<="`
+	Value        float64 `description:"比较的值"`
+	ReplaceValue float64 `description:"替换的值"`
+}
+
+type ChartRangeAnalysisDataConvertConf struct {
+	Value  float64 `description:"数据转换值"`
+	Unit   string  `description:"数据转换单位"`
+	EnUnit string  `description:"数据转换单位"`
+}
+
+type ChartRangeAnalysisManualDateConf struct { //手工划分
+	StartDate string `description:"开始日期"`
+	EndDate   string `description:"结束日期"`
+}
+
+type ChartRangeAnalysisAutoDateConf struct { //智能划分
+	IsAutoStartDate int                                  `description:"起始日期是否是动态设置:0固定,1动态"`
+	StartDate       string                               `description:"固定模式下的起始日期"`
+	EndDate         string                               `description:"固定模式下的截止日期"`
+	IsAutoEndDate   int                                  `description:"截止日期是否是动态设置:0固定,1动态"`
+	StartDateConf   ChartRangeAnalysisAutoDateChangeConf `description:"动态起始日期配置"`
+	EndDateConf     ChartRangeAnalysisAutoDateChangeConf `description:"动态截止日期配置"`
+}
+
+type ChartRangeAnalysisYearDateConf struct {
+	StartDay string `description:"开始日"`
+	EndDay   string `description:"结束日"`
+}
+
+type ChartRangeAnalysisManualDateConfList []ChartRangeAnalysisManualDateConf
+
+func (a ChartRangeAnalysisManualDateConfList) Len() int      { return len(a) }
+func (a ChartRangeAnalysisManualDateConfList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ChartRangeAnalysisManualDateConfList) Less(i, j int) bool {
+	return a[i].StartDate < a[j].StartDate
+}
+
+type ChartRangeAnalysisDataResp struct { //图表详情返回值
+	*ChartRangeAnalysisExtraConf
+	SeriesId     int `description:"指标系列ID"`
+	ConfigEdbNum int `description:"生成的指标数"`
+}
+
+type ChartRangeAnalysisDateDataItem struct {
+	StartDate time.Time
+	EndDate   time.Time
+	DataList  []*EdbDataList
+}
+
+// ChartRangeAnalysisConfigEdbResp 指标列表
+type ChartRangeAnalysisConfigEdbResp struct {
+	EdbInfoList []*ChartRangeAnalysisConfigEdbItem `description:"指标列表"`
+}
+
+type ChartRangeAnalysisConfigEdbItem struct {
+	EdbInfoId     int    `description:"指标id"`
+	EdbName       string `description:"指标名称"`
+	EdbNameEn     string `description:"指标名称"`
+	Frequency     string `description:"频度"`
+	Unit          string `description:"单位"`
+	UnitEn        string `description:"单位"`
+	ClassifyId    int    `description:"分类id"`
+	FromEdbInfoId int    `description:"计算来源指标id"`
+	EdbTypeInfo   int    `description:"指标类型,0普通指标,1预测指标"`
+}
+
+type SortEdbDataList []*EdbDataList
+
+func (m SortEdbDataList) Len() int {
+	return len(m)
+}
+
+func (m SortEdbDataList) Less(i, j int) bool {
+	return m[i].DataTime > m[j].DataTime
+}
+
+func (m SortEdbDataList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}

+ 7 - 0
models/data_manage/factor_edb_series_chart_mapping.go

@@ -165,3 +165,10 @@ func (m *FactorEdbSeriesChartMapping) GetDistinctSeriesIdByChartId(chartId int)
 	_, err = o.Raw(sql, chartId).QueryRows(&seriesIds)
 	return
 }
+
+func (m *FactorEdbSeriesChartMapping) GetItemByChartInfoId(id int) (item *FactorEdbSeriesChartMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().ChartInfoId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}

+ 8 - 0
models/data_manage/multiple_graph_config_edb_mapping.go

@@ -51,3 +51,11 @@ func GetMultipleGraphConfigEdbMappingListById(configId int) (items []*MultipleGr
 
 	return
 }
+
+func GetMultipleGraphConfigEdbMappingListByIdAndSource(configId, source int) (items []*MultipleGraphConfigEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM multiple_graph_config_edb_mapping WHERE multiple_graph_config_id = ? AND source = ? `
+	_, err = o.Raw(sql, configId, source).QueryRows(&items)
+
+	return
+}

+ 1 - 0
models/db.go

@@ -38,6 +38,7 @@ func init() {
 		new(data_manage.PredictEdbRuleData),             //预测指标配置生成的数据
 		new(data_manage.PredictEdbConfCalculateMapping), //预测指标关系表
 		new(data_manage.ChartInfoCorrelation),           // 图表相关性信息
+		new(FactorEdbSeriesCalculateDataQjjs),           // 因子指标系列-区间计算数据
 	)
 	// 期货数据库
 	initFutureGood()

+ 82 - 0
models/excel_info_rule_mapping.go

@@ -0,0 +1,82 @@
+package models
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type ExcelInfoRuleMapping struct {
+	ExcelInfoRuleMappingId int       `orm:"pk" description:"主键"`
+	ExcelInfoId            int       `description:"Excel信息ID"`
+	RuleType               int       `description:"规则类型"`
+	LeftValue              string    `description:"左值"`
+	LeftValueShow          string    `description:"左值前端显示"`
+	LeftValueType          int       `description:"左值类型"`
+	RightValue             string    `description:"右值"`
+	RightValueShow         string    `description:"右值前端显示"`
+	RightValueType         int       `description:"右值类型"`
+	FontColor              string    `description:"字体颜色"`
+	BackgroundColor        string    `description:"背景颜色"`
+	Remark                 string    `description:"预设颜色说明"`
+	RemarkEn               string    `description:"预设颜色英文说明"`
+	Scope                  string    `description:"作用范围"`
+	ScopeCoord             string    `description:"作用范围坐标"`
+	ScopeShow              string    `description:"作用范围坐标前端显示"`
+	CreateTime             time.Time `description:"创建时间"`
+}
+
+type ExcelInfoRuleMappingView struct {
+	ExcelInfoRuleMappingId int    `orm:"pk" description:"主键"`
+	ExcelInfoId            int    `description:"Excel信息ID"`
+	RuleType               int    `description:"规则类型:1-大于,2-小于,3-介于,4-等于,5-发生日期"`
+	LeftValue              string `description:"左值"`
+	LeftValueBack          string `description:"左值前端显示"`
+	LeftValueType          int    `description:"左值类型"`
+	RightValue             string `description:"右值"`
+	RightValueBack         string `description:"右值前端显示"`
+	RightValueType         int    `description:"右值类型"`
+	FontColor              string `description:"字体颜色"`
+	BackgroundColor        string `description:"背景颜色"`
+	Remark                 string `description:"预设颜色说明"`
+	RemarkEn               string `description:"预设颜色英文说明"`
+	Scope                  string `description:"作用范围"`
+	ScopeCoord             string `description:"作用范围坐标"`
+	ScopeShow              string `description:"作用范围坐标前端显示"`
+	CreateTime             string `description:"创建时间"`
+}
+
+func (e *ExcelInfoRuleMapping) Insert() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(e)
+	return
+}
+
+func (e *ExcelInfoRuleMapping) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// GetExcelRuleMappingByExcelInfoId 根据excelInfoId获取规则映射信息
+func GetExcelRuleMappingByExcelInfoId(id int) (items []*ExcelInfoRuleMappingView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_rule_mapping WHERE excel_info_id = ? ORDER BY create_time ASC`
+	_, err = o.Raw(sql, id).QueryRows(&items)
+	return
+}
+
+// GetExcelRuleMappingById 根据主键Id获取规则映射信息
+func GetExcelRuleMappingById(id int) (item *ExcelInfoRuleMappingView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_rule_mapping WHERE excel_info_rule_mapping_id = ?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func DeleteExcelRuleMappingById(id int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM excel_info_rule_mapping WHERE excel_info_rule_mapping_id = ?`
+	_, err = o.Raw(sql, id).Exec()
+	return
+}

+ 198 - 0
models/factor_edb_series_calculate_data_qjjs.go

@@ -0,0 +1,198 @@
+package models
+
+import (
+	"eta/eta_chart_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// FactorEdbSeriesCalculateDataQjjs 因子指标系列-区间计算数据表
+type FactorEdbSeriesCalculateDataQjjs struct {
+	FactorEdbSeriesCalculateDataId int       `orm:"column(factor_edb_series_calculate_data_id);pk"`
+	FactorEdbSeriesId              int       `description:"因子指标系列ID"`
+	EdbInfoId                      int       `description:"指标ID"`
+	EdbCode                        string    `description:"指标编码"`
+	DataTime                       time.Time `description:"数据日期"`
+	Value                          float64   `description:"数据值"`
+	CreateTime                     time.Time `description:"创建时间"`
+	ModifyTime                     time.Time `description:"修改时间"`
+	DataTimestamp                  int64     `description:"数据日期时间戳"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) TableName() string {
+	return "factor_edb_series_calculate_data_qjjs"
+}
+
+type FactorEdbSeriesCalculateDataQjjsCols struct {
+	PrimaryId         string
+	FactorEdbSeriesId string
+	EdbInfoId         string
+	EdbCode           string
+	DataTime          string
+	Value             string
+	CreateTime        string
+	ModifyTime        string
+	DataTimestamp     string
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Cols() FactorEdbSeriesCalculateDataQjjsCols {
+	return FactorEdbSeriesCalculateDataQjjsCols{
+		PrimaryId:         "factor_edb_series_calculate_data_id",
+		FactorEdbSeriesId: "factor_edb_series_id",
+		EdbInfoId:         "edb_info_id",
+		EdbCode:           "edb_code",
+		DataTime:          "data_time",
+		Value:             "value",
+		CreateTime:        "create_time",
+		ModifyTime:        "modify_time",
+		DataTimestamp:     "data_timestamp",
+	}
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.FactorEdbSeriesCalculateDataId = int(id)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) CreateMulti(items []*FactorEdbSeriesCalculateDataQjjs) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(500, items)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.FactorEdbSeriesCalculateDataId).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemById(id int) (item *FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrmUsingDB("data")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// FactorEdbSeriesCalculateDataQjjsItem 因子指标系列-计算数据信息
+type FactorEdbSeriesCalculateDataQjjsItem struct {
+	DataId            int     `description:"数据ID"`
+	FactorEdbSeriesId int     `description:"因子指标系列ID"`
+	EdbInfoId         int     `description:"指标ID"`
+	EdbCode           string  `description:"指标编码"`
+	DataTime          string  `description:"数据日期"`
+	Value             float64 `description:"数据值"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Format2Item() (item *FactorEdbSeriesCalculateDataQjjsItem) {
+	item = new(FactorEdbSeriesCalculateDataQjjsItem)
+	item.DataId = m.FactorEdbSeriesCalculateDataId
+	item.FactorEdbSeriesId = m.FactorEdbSeriesId
+	item.EdbInfoId = m.EdbInfoId
+	item.EdbCode = m.EdbCode
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetEdbDataList(seriesId int, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	var pars []interface{}
+	sql := `SELECT factor_edb_series_calculate_data_id as edb_data_id  ,edb_info_id,data_time,value,data_timestamp FROM %s WHERE edb_info_id=? and factor_edb_series_id=? `
+	pars = append(pars, edbInfoId, seriesId)
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, m.TableName())
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}

+ 6 - 2
models/request/mixed_table.go

@@ -160,8 +160,12 @@ type MixDateCalculateTagReq struct {
 
 // MixCellShowStyle 混合表格 单元格样式展示计算
 type MixCellShowStyle struct {
-	Pn int    `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
-	Nt string `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
+	Pn              int    `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
+	Nt              string `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
+	Decimal         *int   `description:"小数点位数"`
+	Last            string `description:"起始操作:nt|decimal"`
+	Color           string `description:"颜色值,#RRG" json:"color"`
+	BackgroundColor string `description:"背景颜色值,#RRG" json:"background-color"`
 }
 
 type DateDataBeforeAfterReq struct {

+ 320 - 3
services/data/chart_info.go

@@ -293,7 +293,7 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 	case 2: // 季节性图
 		if seasonExtraConfig != "" {
 			// 季节性图计算不管图上数据时间,拿所有数据
-			_, tempEdbList, e := getEdbDataMapList(chartInfoId, chartType, calendar, "1990-01-01", "", mappingList, seasonExtraConfig)
+			_, tempEdbList, e := getEdbDataMapListForSeason(chartInfoId, chartType, calendar, "1990-01-01", "", mappingList, seasonExtraConfig)
 			if e != nil {
 				err = e
 				return
@@ -2013,6 +2013,11 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 					if v.ChartLegend == strconv.Itoa(time.Now().Year()) {
 						continue
 					}
+					// 农历时dataTimeList的年份为最新年份,图例时间的年份才是真实年份
+					chartLegend,_ := strconv.Atoi(v.ChartLegend)
+					if chartLegend < startYear {
+						continue
+					}
 					for _, date := range dataTimeList {
 						dateTime, e := time.Parse(utils.FormatDate, date)
 						if e != nil {
@@ -2144,6 +2149,11 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 						if dateTime.Year() < startYear {
 							continue
 						}
+						// 农历时dataTimeList的年份为最新年份,图例时间的年份才是真实年份
+						chartLegend,_ := strconv.Atoi(v.ChartLegend)
+						if chartLegend < startYear {
+							continue
+						}
 						// 月度的2月29号日期改为2月28日
 						if dateTime.Month() == 2 && dateTime.Day() == 29 {
 							dateTime = dateTime.AddDate(0, 0, -1)
@@ -2294,6 +2304,7 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 			dataTimeList := make([]string, 0)
 			valueMap := make(map[time.Time]float64)
 			averageDataList := make([]*models.SamePeriodAverageData, 0)
+			dateNumMap := make(map[time.Time]float64)
 			for i := len(quarterDataList) - 1; i >= 0; i-- {
 				// 插值成日度
 				dataTimeList, _, err = HandleDataByLinearRegressionToList(quarterDataList[i].DataList, handleDataMap)
@@ -2311,6 +2322,11 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 					if dateTime.Year() < startYear {
 						continue
 					}
+					// 农历时dataTimeList的年份为最新年份,图例时间的年份才是真实年份
+					chartLegend,_ := strconv.Atoi(quarterDataList[i].ChartLegend)
+					if chartLegend < startYear {
+						continue
+					}
 					// 不包含2月29号
 					if dateTime.Month() == 2 && dateTime.Day() == 29 {
 						continue
@@ -2322,19 +2338,26 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 					newDate := dateTime.AddDate(time.Now().Year()-dateTime.Year(), 0, 0)
 					// 处理均值
 					if value, ok := valueMap[newDate]; ok {
-						valueMap[newDate] = (handleDataMap[date] + value) / 2
+						valueMap[newDate] = handleDataMap[date] + value
 					} else {
 						valueMap[newDate] = handleDataMap[date]
 					}
+					dateNumMap[newDate] += 1
 
 					dataTimeMap[newDate] = newDate
 				}
 			}
 
+			//year := float64(seasonConfig.SamePeriodAverage.Year)
 			for _, v := range dataTimeMap {
 				averageItem := &models.SamePeriodAverageData{}
+				year := dateNumMap[v]
 				if value, ok := valueMap[v]; ok {
-					averageItem.Value = value
+					if year > 0 {
+						averageItem.Value = value / year
+					} else {
+						averageItem.Value = value
+					}
 				}
 				averageItem.DataTime = v.Format(utils.FormatDate)
 				averageItem.DataTimestamp = v.UnixNano() / 1e6
@@ -2387,6 +2410,11 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 					if dateTime.Year() < startYear {
 						continue
 					}
+					// 农历时dataTimeList的年份为最新年份,图例时间的年份才是真实年份
+					chartLegend,_ := strconv.Atoi(quarterDataList[i].ChartLegend)
+					if chartLegend < startYear {
+						continue
+					}
 					// 不包含2月29号
 					if dateTime.Month() == 2 && dateTime.Day() == 29 {
 						continue
@@ -2464,3 +2492,292 @@ func SeasonChartData(dataList []*models.ChartEdbInfoMapping, seasonExtraConfig s
 
 	return
 }
+
+// getEdbDataMapListForSeason 获取指标最后的基础数据-插值补充好数据-季节性图用
+func getEdbDataMapListForSeason(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*models.EdbDataList, edbList []*models.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap = make(map[int][]*models.EdbDataList)
+
+	for _, v := range mappingList {
+		//fmt.Println("v:", v.EdbInfoId)
+		item := new(models.ChartEdbInfoMapping)
+		item.EdbInfoId = v.EdbInfoId
+		item.SourceName = v.SourceName
+		item.Source = v.Source
+		item.EdbCode = v.EdbCode
+		item.EdbName = v.EdbName
+		item.EdbNameEn = v.EdbNameEn
+		item.Frequency = v.Frequency
+		item.EdbType = v.EdbType
+		item.FrequencyEn = GetFrequencyEn(v.Frequency)
+		if v.Unit != `无` {
+			item.Unit = v.Unit
+		}
+		item.UnitEn = v.UnitEn
+		item.StartDate = v.StartDate
+		item.EndDate = v.EndDate
+		item.ModifyTime = v.ModifyTime
+		item.EdbInfoCategoryType = v.EdbInfoCategoryType
+		item.PredictChartColor = v.PredictChartColor
+		item.ClassifyId = v.ClassifyId
+		if chartInfoId <= 0 {
+			item.IsAxis = 1
+			item.LeadValue = 0
+			item.LeadUnit = ""
+			item.ChartEdbMappingId = 0
+			item.ChartInfoId = 0
+			item.IsOrder = false
+			item.EdbInfoType = 1
+			item.ChartStyle = ""
+			item.ChartColor = ""
+			item.ChartWidth = 0
+			item.MaxData = v.MaxValue
+			item.MinData = v.MinValue
+		} else {
+			item.IsAxis = v.IsAxis
+			item.EdbInfoType = v.EdbInfoType
+			item.LeadValue = v.LeadValue
+			item.LeadUnit = v.LeadUnit
+			item.LeadUnitEn = GetLeadUnitEn(v.LeadUnit)
+			item.ChartEdbMappingId = v.ChartEdbMappingId
+			item.ChartInfoId = v.ChartInfoId
+			item.ChartStyle = v.ChartStyle
+			item.ChartColor = v.ChartColor
+			item.ChartWidth = v.ChartWidth
+			item.IsOrder = v.IsOrder
+			item.MaxData = v.MaxData
+			item.MinData = v.MinData
+		}
+		item.LatestValue = v.LatestValue
+		item.LatestDate = v.LatestDate
+		item.UniqueCode = v.UniqueCode
+		item.MoveLatestDate = v.LatestDate
+		item.EdbAliasName = v.EdbAliasName
+		item.IsConvert = v.IsConvert
+		item.ConvertType = v.ConvertType
+		item.ConvertValue = v.ConvertValue
+		item.ConvertUnit = v.ConvertUnit
+		item.ConvertEnUnit = v.ConvertEnUnit
+		item.IsJoinPermission = v.IsJoinPermission
+
+		var startDateReal string
+		var diffSeconds int64
+		if chartType == 2 { //季节性图
+			startDateReal = startDate
+			if len(mappingList) > 1 {
+				item.IsAxis = v.IsAxis
+			}
+		} else {
+			if v.EdbInfoType == 0 && v.LeadUnit != "" && v.LeadValue > 0 { //领先指标
+				var startTimeRealTemp time.Time
+				startDateParse, _ := time.Parse(utils.FormatDate, startDate)
+				switch v.LeadUnit {
+				case "天":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -v.LeadValue)
+				case "月":
+					startTimeRealTemp = startDateParse.AddDate(0, -v.LeadValue, 0)
+				case "季":
+					startTimeRealTemp = startDateParse.AddDate(0, -3*v.LeadValue, 0)
+				case "周":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -7*v.LeadValue)
+				case "年":
+					startTimeRealTemp = startDateParse.AddDate(-v.LeadValue, 0, 0)
+				}
+				if startTimeRealTemp.Before(startDateParse) {
+					startDateReal = startTimeRealTemp.Format(utils.FormatDate)
+					diffSeconds = (int64(startTimeRealTemp.UnixNano()) - int64(startDateParse.UnixNano())) / 1e6
+				} else {
+					startDateReal = startDate
+					diffSeconds = 0
+				}
+
+				// 预测指标的开始日期也要偏移
+				{
+					day, tmpErr := utils.GetDaysBetween2Date(utils.FormatDate, startDate, startDateReal)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					moveLatestDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, item.MoveLatestDate, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					item.MoveLatestDate = moveLatestDateTime.AddDate(0, 0, day).Format(utils.FormatDate)
+				}
+			} else {
+				startDateReal = startDate
+			}
+		}
+		//fmt.Println("line 1011 chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+		calendarPreYear := 0
+		if calendar == "农历" {
+			newStartDateReal, err := time.Parse(utils.FormatDate, startDateReal)
+			if err != nil {
+				fmt.Println("time.Parse:" + err.Error())
+			}
+			calendarPreYear = newStartDateReal.Year() - 1
+			newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+			startDateReal = newStartDateReal.Format(utils.FormatDate)
+		}
+		dataList := make([]*models.EdbDataList, 0)
+		//fmt.Println("chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+		//fmt.Println("calendarPreYear:", calendarPreYear)
+		//var newEdbInfo *data_manage.EdbInfo
+		switch v.EdbInfoCategoryType {
+		case 0:
+			dataList, err = models.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, startDateReal, endDate)
+		case 1:
+			_, dataList, _, _, err, _ = GetPredictDataListByPredictEdbInfoId(v.EdbInfoId, startDateReal, endDate, true)
+		default:
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.EdbInfoCategoryType))
+		}
+		if err != nil {
+			return
+		}
+
+		handleDataMap := make(map[string]float64)
+		newDataList, _, _, e := HandleDataByLinearRegressionToListV3(dataList, handleDataMap)
+		if e != nil {
+			err = e
+			return
+		}
+
+		if v.IsConvert == 1 {
+			switch v.ConvertType {
+			case 1:
+				for i, data := range newDataList {
+					newDataList[i].Value = data.Value * v.ConvertValue
+				}
+				//item.MaxData = item.MaxData * v.ConvertValue
+				//item.MinData = item.MinData * v.ConvertValue
+			case 2:
+				for i, data := range newDataList {
+					newDataList[i].Value = data.Value / v.ConvertValue
+				}
+				//item.MaxData = item.MaxData / v.ConvertValue
+				//item.MinData = item.MinData / v.ConvertValue
+			case 3:
+				for i, data := range newDataList {
+					if data.Value <= 0 {
+						err = errors.New("数据中含有负数或0,无法对数运算")
+						return
+					}
+					newDataList[i].Value = math.Log(data.Value) / math.Log(v.ConvertValue)
+				}
+				//item.MaxData = math.Log(item.MaxData) / math.Log(v.ConvertValue)
+				//item.MinData = math.Log(item.MinData) / math.Log(v.ConvertValue)
+			}
+		}
+
+		edbDataListMap[v.EdbInfoId] = newDataList
+
+		if diffSeconds != 0 && v.EdbInfoType == 0 {
+			newDataListLen := len(newDataList)
+			for i := 0; i < newDataListLen; i++ {
+				newDataList[i].DataTimestamp = newDataList[i].DataTimestamp - diffSeconds
+			}
+		}
+
+		if chartType == 2 && item.IsAxis == 1 {
+			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
+			if tmpErr != nil {
+				//item.DataList = newDataList
+				item.IsNullData = true
+				edbList = append(edbList, item)
+				continue
+				err = errors.New(fmt.Sprint("获取最后实际数据的日期失败,Err:" + tmpErr.Error() + ";LatestDate:" + v.LatestDate))
+				return
+			}
+
+			if calendar == "农历" {
+				if len(newDataList) <= 0 {
+					result := new(models.EdbDataResult)
+					item.DataList = result
+				} else {
+					result, tmpErr := models.AddCalculateQuarterV6(newDataList)
+					if tmpErr != nil {
+						err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
+						return
+					}
+					quarterDataList, tErr := GetSeasonEdbInfoDataListByXDateNong(result, latestDate, seasonExtraConfig, calendarPreYear)
+					if tErr != nil {
+						err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+						return
+					}
+					item.DataList = quarterDataList
+				}
+
+			} else {
+				quarterDataList, tErr := GetSeasonEdbInfoDataListByXDate(newDataList, latestDate, seasonExtraConfig)
+				if tErr != nil {
+					err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+					return
+				}
+				item.DataList = quarterDataList
+			}
+
+		} else if chartType == 2 && item.IsAxis == 0 {
+			// 右轴数据处理
+			xStartDate := "01-01"
+
+			jumpYear := 0
+			var seasonExtra models.SeasonExtraItem
+			if seasonExtraConfig != "" {
+				err = json.Unmarshal([]byte(seasonExtraConfig), &seasonExtra)
+				if err != nil {
+					return
+				}
+			}
+
+			if seasonExtra.XStartDate != "" {
+				xStartDate = seasonExtra.XStartDate
+				jumpYear = seasonExtra.JumpYear
+			}
+
+			length := len(newDataList)
+			if length == 0 {
+				return
+			}
+			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
+			if tmpErr != nil {
+				//item.DataList = newDataList
+				item.IsNullData = true
+				edbList = append(edbList, item)
+				continue
+				err = errors.New(fmt.Sprint("获取最后实际数据的日期失败,Err:" + tmpErr.Error() + ";LatestDate:" + v.LatestDate))
+				return
+			}
+
+			var rightAxisDate time.Time
+			if jumpYear == 1 {
+				latestDate = latestDate.AddDate(-1, 0, 0)
+			}
+			latestDateStr := fmt.Sprintf("%d-%s", latestDate.Year(), xStartDate)
+			rightAxisDate, err = time.Parse(utils.FormatDate, latestDateStr)
+			if err != nil {
+				return
+			}
+
+			newList := make([]*models.EdbDataList, 0)
+			for _, v := range newDataList {
+				dataTime, e := time.Parse(utils.FormatDate, v.DataTime)
+				if e != nil {
+					err = errors.New("季节性图处理右轴指标数据转换日期失败,Err:" + e.Error())
+					return
+				}
+				if dataTime.Equal(rightAxisDate) || dataTime.After(rightAxisDate) {
+					newList = append(newList, v)
+				}
+			}
+			item.DataList = newList
+		} else if chartType == 7 || chartType == utils.CHART_TYPE_RADAR { //柱方图
+			//item.DataList = newDataList
+		} else {
+			item.DataList = newDataList
+		}
+		edbList = append(edbList, item)
+	}
+
+	return
+}

+ 82 - 1
services/data/edb_info.go

@@ -1406,4 +1406,85 @@ func HandleDataByLinearRegressionToListV2(edbInfoDataList []*models.EdbDataList,
 	}
 
 	return
-}
+}
+
+
+// HandleDataByLinearRegressionToListV3 插值法补充数据(线性方程式)-直接补充指标起始日期间的所有数据
+func HandleDataByLinearRegressionToListV3 (edbInfoDataList []*models.EdbDataList, handleDataMap map[string]float64) (newEdbInfoDataList []*models.EdbDataList, dataTimeList []string,valueList []float64, err error) {
+	if len(edbInfoDataList) < 2 {
+		return
+	}
+
+	var startEdbInfoData *models.EdbDataList
+	for _, v := range edbInfoDataList {
+		handleDataMap[v.DataTime] = v.Value
+		newEdbInfoDataList = append(newEdbInfoDataList, v)
+		dataTimeList = append(dataTimeList, v.DataTime)
+		// 第一个数据就给过滤了,给后面的试用
+		if startEdbInfoData == nil {
+			startEdbInfoData = v
+			//startEdbInfoData.DataTime = startEdbInfoData.DataTime[:5]+ "01-01"
+			continue
+		}
+
+		// 获取两条数据之间相差的天数
+		startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
+		currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
+		betweenDay := betweenHour / 24
+
+		// 如果相差一天,那么过滤
+		if betweenDay <= 1 {
+			startEdbInfoData = v
+			continue
+		}
+
+		// 生成线性方程式
+		var a, b float64
+		{
+			coordinateData := make([]utils.Coordinate, 0)
+			tmpCoordinate1 := utils.Coordinate{
+				X: 1,
+				Y: startEdbInfoData.Value,
+			}
+			coordinateData = append(coordinateData, tmpCoordinate1)
+			tmpCoordinate2 := utils.Coordinate{
+				X: float64(betweenDay) + 1,
+				Y: v.Value,
+			}
+			coordinateData = append(coordinateData, tmpCoordinate2)
+
+			a, b = utils.GetLinearResult(coordinateData)
+			if math.IsNaN(a) || math.IsNaN(b) {
+				err = errors.New("线性方程公式生成失败")
+				return
+			}
+		}
+
+		// 生成对应的值
+		{
+			for i := 1; i < betweenDay; i++ {
+				tmpDataTime := startDataTime.AddDate(0, 0, i)
+				aDecimal := decimal.NewFromFloat(a)
+				xDecimal := decimal.NewFromInt(int64(i) + 1)
+				bDecimal := decimal.NewFromFloat(b)
+
+				val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
+				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
+				dataTimeList = append(dataTimeList, tmpDataTime.Format(utils.FormatDate))
+				valueList = append(valueList, val)
+				newEdbInfoDataList = append(newEdbInfoDataList, &models.EdbDataList{
+					EdbDataId:     v.EdbDataId,
+					EdbInfoId:     v.EdbInfoId,
+					DataTime:      tmpDataTime.Format(utils.FormatDate),
+					DataTimestamp: tmpDataTime.UnixNano() / 1e6,
+					Value:         val,
+				})
+			}
+		}
+
+		startEdbInfoData = v
+	}
+
+	return
+}

+ 58 - 3
services/data/excel/time_table.go

@@ -8,12 +8,13 @@ import (
 	"eta/eta_chart_lib/services/data"
 	"eta/eta_chart_lib/utils"
 	"fmt"
-	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/shopspring/decimal"
+	"github.com/yidane/formula"
 )
 
 // TableDataConfig
@@ -27,6 +28,13 @@ type TableDataConfig struct {
 	ManualDate       []string                  `description:"手动配置的日期(未来的日期)"`
 	TableEdbInfoList []TableEdbInfo            `description:"表格内指标信息"`
 	TextRowData      [][]request.ManualDataReq `description:"文本列表"`
+	DecimalConfig    []DecimalConfig           `description:"小数位数配置"`
+}
+
+type DecimalConfig struct {
+	Row     string `description:"行上的索引, 目前仅保存指标的日期"`
+	Col     int    `description:"列上的索引, 目前仅保存edbInfoId"`
+	Decimal int    `description:"小数位数, 从左至右,从上到下的顺序"`
 }
 
 // TableEdbInfo
@@ -329,7 +337,7 @@ func GetDataByTableDataConfig(tableDataConfig TableDataConfig) (resultResp reque
 			d[k2].ShowValue = utils.FormatTableDataShowValue(vf)
 		}
 	}
-
+	data = SetExcelShowValueByDecimalConfig(data, tableDataConfig.DecimalConfig)
 	resultResp = request.TableDataReq{
 		EdbInfoIdList: edbInfoIdList,
 		Sort:          tableDataConfig.Sort,
@@ -340,6 +348,53 @@ func GetDataByTableDataConfig(tableDataConfig TableDataConfig) (resultResp reque
 	return
 }
 
+func SetExcelShowValueByDecimalConfig(edbData []request.EdbInfoData, decimalConfig []DecimalConfig) []request.EdbInfoData {
+	if len(decimalConfig) <= 0 || len(edbData) <= 0 {
+		return edbData
+	}
+	edbInfoIndexMap := make(map[int]int)
+	dateIndexMap := make(map[string]int)
+	for i, edbInfo := range edbData {
+		edbInfoIndexMap[edbInfo.EdbInfoId] = i
+	}
+	for i, date := range edbData[0].Data {
+		dateIndexMap[date.DataTime] = i
+	}
+	for _, conf := range decimalConfig {
+		if conf.Col > 0 {
+			if edbIndex, ok := edbInfoIndexMap[conf.Col]; ok {
+				for i := 0; i < len(edbData[edbIndex].Data); i++ {
+					f, err := strconv.ParseFloat(edbData[edbIndex].Data[i].Value, 64)
+					if err != nil {
+						continue
+					}
+					if conf.Decimal == -1 {
+						continue
+					}
+					formatStr := fmt.Sprintf("%%.%df", conf.Decimal)
+					edbData[edbIndex].Data[i].ShowValue = fmt.Sprintf(formatStr, f)
+				}
+			}
+		}
+		if conf.Row != `` {
+			if dateIndex, ok := dateIndexMap[conf.Row]; ok {
+				for i := 0; i < len(edbData); i++ {
+					f, err := strconv.ParseFloat(edbData[i].Data[dateIndex].Value, 64)
+					if err != nil {
+						continue
+					}
+					if conf.Decimal == -1 {
+						continue
+					}
+					formatStr := fmt.Sprintf("%%.%df", conf.Decimal)
+					edbData[i].Data[dateIndex].ShowValue = fmt.Sprintf(formatStr, f)
+				}
+			}
+		}
+	}
+	return edbData
+}
+
 // GetTableDataConfig 根据TableDataReq配置获取相关数据配置
 func GetTableDataConfig(reqData request.TableDataReq) (tableDataConfig TableDataConfig, err error) {
 	// 指标数据

+ 1046 - 0
services/data/range_analysis/chart_info.go

@@ -0,0 +1,1046 @@
+package range_analysis
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/models/data_manage"
+	"eta/eta_chart_lib/services/alarm_msg"
+	"eta/eta_chart_lib/services/data"
+	"eta/eta_chart_lib/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"math"
+	"time"
+)
+
+// GetAutoCalculateDateDataList 获取当前时间相关的区间作为计算依据
+func GetAutoCalculateDateDataList(currentDate string, dataList []*models.EdbDataList, req *models.ChartRangeAnalysisExtraConf) (newDataList []*models.EdbDataList, err error) {
+	currentDateTime, _ := time.ParseInLocation(utils.FormatDate, currentDate, time.Local)
+	switch req.DateRangeType {
+	case 0:
+		// 智能划分得到一个开始日期,和结束日期
+		var startDateTime time.Time
+		if req.AutoDateConf.IsAutoStartDate == 0 { //固定设置
+			startDate := req.AutoDateConf.StartDate
+			if startDate == "" {
+				startDate = "2020-01-01"
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		} else {
+			startConf := req.AutoDateConf.StartDateConf
+			startDate := ""
+			if startConf.BaseDateType == 0 { //
+				startDate = currentDate
+			} else if startConf.BaseDateType == 1 {
+				startDate = time.Now().Format(utils.FormatDate)
+			}
+			if startConf.MoveForward > 0 {
+				startDate = GetEdbDateByMoveForward(startDate, startConf.MoveForward, dataList)
+			}
+			if len(startConf.DateChange) > 0 {
+				startDate, err = HandleEdbDateChange(startDate, startConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分开始日期处理失败:%s", err.Error())
+					return
+				}
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		}
+		var calStartTime, calEndTime time.Time
+		if currentDateTime.Before(startDateTime) {
+			calStartTime = currentDateTime
+			calEndTime = startDateTime
+		} else {
+			calStartTime = startDateTime
+			calEndTime = currentDateTime
+		}
+		// 根据日期,获取数据
+		for _, vv := range dataList {
+			dataTimeT, _ := time.ParseInLocation(utils.FormatDate, vv.DataTime, time.Local)
+			if dataTimeT.After(calStartTime) && dataTimeT.Before(calEndTime) ||
+				dataTimeT.Equal(calStartTime) ||
+				dataTimeT.Equal(calEndTime) {
+				newDataList = append(newDataList, vv)
+			}
+		}
+	}
+	return
+}
+
+// HandleDataByCalculateType 根据计算公式处理数据
+func HandleDataByCalculateType(originList []*models.ChartRangeAnalysisDateDataItem, originDataList []*models.EdbDataList, req *models.ChartRangeAnalysisExtraConf) (newList []*models.EdbDataList, err error) {
+	if len(originList) == 0 {
+		return
+	}
+	calculateType := req.CalculateType
+	switch calculateType {
+	case 0: //均值
+		var sum float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					sum = 0
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for _, vv := range calDataList {
+						sum += vv.Value
+					}
+					val := sum / float64(len(calDataList))
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				sum = 0
+				for k, v := range item.DataList {
+					sum += v.Value
+					val := sum / float64(k+1)
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+
+	case 1: //累计值
+		var sum float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				sum = 0
+				for _, v := range item.DataList {
+					sum = 0
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for _, vv := range calDataList {
+						sum += vv.Value
+					}
+					val := sum
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				sum = 0
+				for _, v := range item.DataList {
+					sum += v.Value
+					val := sum
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+
+	case 2: //涨幅
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					var baseVal float64
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					if len(calDataList) == 0 {
+						continue
+					}
+					baseVal = calDataList[0].Value
+					baseDate := calDataList[0].DataTime
+					if baseVal == 0 {
+						continue
+					}
+					if v.DataTime == baseDate {
+						continue
+					}
+
+					val := (v.Value - baseVal) / baseVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				if len(item.DataList) == 0 {
+					break
+				}
+				baseVal := item.DataList[0].Value
+				baseDate := item.DataList[0].DataTime
+				if baseVal == 0 {
+					break
+				}
+				for _, v := range item.DataList {
+					if v.DataTime == baseDate {
+						continue
+					}
+					val := (v.Value - baseVal) / baseVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+	case 3: //复合增长率
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					var baseVal float64
+					var baseDate string
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					if len(calDataList) == 0 {
+						continue
+					}
+					baseVal = calDataList[0].Value
+					baseDate = calDataList[0].DataTime
+					if v.DataTime == baseDate {
+						continue
+					}
+					if baseVal == 0 {
+						continue
+					}
+
+					baseDateT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					tmpT, e := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					// 计算两个日期相差的天数
+					diff := tmpT.Sub(baseDateT).Hours() / 24 / 365
+					val := v.Value / baseVal
+					val = math.Pow(val, 1/diff) - 1
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				if len(item.DataList) == 0 {
+					break
+				}
+				baseVal := item.DataList[0].Value
+				baseDate := item.DataList[0].DataTime
+				if baseVal == 0 {
+					break
+				}
+				for _, v := range item.DataList {
+					if v.DataTime == baseDate {
+						continue
+					}
+					baseDateT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					tmpT, e := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					// 计算两个日期相差的天数
+					diff := tmpT.Sub(baseDateT).Hours() / 24 / 365
+					val := v.Value / baseVal
+					val = math.Pow(val, 1/diff) - 1
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	case 4: //最大值
+		var maxVal float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for kk, vv := range calDataList {
+						if kk == 0 {
+							maxVal = vv.Value
+						}
+						if vv.Value > maxVal {
+							maxVal = vv.Value
+						}
+					}
+
+					val := maxVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				for k, v := range item.DataList {
+					if k == 0 {
+						maxVal = v.Value
+					}
+					if v.Value > maxVal {
+						maxVal = v.Value
+					}
+					val := maxVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	case 5: //最小值
+		var minVal float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for kk, vv := range calDataList {
+						if kk == 0 {
+							minVal = vv.Value
+						}
+						if vv.Value < minVal {
+							minVal = vv.Value
+						}
+					}
+					val := minVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				for k, v := range item.DataList {
+					if k == 0 {
+						minVal = v.Value
+					}
+					if v.Value < minVal {
+						minVal = v.Value
+					}
+					val := minVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &models.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// GetChartEdbInfoFormat 区间计算图表-获取指标信息
+func GetChartEdbInfoFormat(chartInfoId int, edbInfoMappingList []*models.ChartEdbInfoMapping) (edbList []*models.ChartEdbInfoMapping, err error) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+	for _, edbInfoMapping := range edbInfoMappingList {
+		if edbInfoMapping == nil {
+			err = fmt.Errorf("指标信息有误")
+			return
+		}
+
+		edbInfoMapping.FrequencyEn = data.GetFrequencyEn(edbInfoMapping.Frequency)
+		if edbInfoMapping.Unit == `无` {
+			edbInfoMapping.Unit = ``
+		}
+		if chartInfoId <= 0 {
+			edbInfoMapping.IsAxis = 1
+			edbInfoMapping.LeadValue = 0
+			edbInfoMapping.LeadUnit = ""
+			edbInfoMapping.ChartEdbMappingId = 0
+			edbInfoMapping.ChartInfoId = 0
+			edbInfoMapping.IsOrder = false
+			edbInfoMapping.EdbInfoType = 1
+			edbInfoMapping.ChartStyle = ""
+			edbInfoMapping.ChartColor = ""
+			edbInfoMapping.ChartWidth = 0
+		} else {
+			edbInfoMapping.LeadUnitEn = data.GetLeadUnitEn(edbInfoMapping.LeadUnit)
+		}
+		edbList = append(edbList, edbInfoMapping)
+	}
+	return
+}
+
+// GetChartDataByEdbInfoList 区间计算图表-根据指标信息获取x轴和y轴
+func GetChartDataByEdbInfoList(chartInfoId int, dateType, startYear int, startDate, endDate string, edbInfoMappingList []*models.ChartEdbInfoMapping, req *models.ChartRangeAnalysisExtraConf) (edbList []*models.ChartEdbInfoMapping, xEdbIdValue []int, dataResp models.ChartRangeAnalysisDataResp, err error) {
+	if chartInfoId > 0 && req.EdbInfoMode == 1 {
+		edbList, xEdbIdValue, dataResp, err = GetChartDataByEdbInfoListBySeries(chartInfoId, dateType, startYear, startDate, endDate, edbInfoMappingList, req)
+		return
+	}
+	for _, edbInfoMapping := range edbInfoMappingList {
+		edbInfoMapping, err = getChartDataByEdbInfo(edbInfoMapping, req)
+		if err != nil {
+			return
+		}
+		edbInfoMapping.ConvertUnit = edbInfoMapping.Unit
+		edbInfoMapping.ConvertEnUnit = edbInfoMapping.UnitEn
+		if req.CalculateType == 2 || req.CalculateType == 3 {
+			edbInfoMapping.ConvertUnit = "无"
+			edbInfoMapping.ConvertEnUnit = "无"
+		}
+		if req.DataConvertType > 0 && req.DataConvertConf.Unit != "" {
+			edbInfoMapping.ConvertUnit = req.DataConvertConf.Unit
+			edbInfoMapping.ConvertEnUnit = req.DataConvertConf.Unit
+		}
+		dataList := edbInfoMapping.DataList.([]*models.EdbDataList)
+		// 处理上下限
+		var maxData, minData float64
+		if len(dataList) > 0 {
+			maxData = dataList[0].Value
+			minData = dataList[0].Value
+			for _, v := range dataList {
+				if v.Value > maxData {
+					maxData = v.Value
+				}
+				if v.Value < minData {
+					minData = v.Value
+				}
+			}
+		}
+		edbInfoMapping.MaxData = maxData
+		edbInfoMapping.MinData = minData
+		xEdbIdValue = append(xEdbIdValue, edbInfoMapping.EdbInfoId)
+	}
+	//根据时间类型来筛选最终的数据
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range edbInfoMappingList {
+			dataList := v.DataList.([]*models.EdbDataList)
+			if len(dataList) > 0 {
+				latestDate := dataList[len(dataList)-1].DataTime
+				if latestDate != "" {
+					lastDateT, tErr := time.Parse(utils.FormatDate, latestDate)
+					if tErr != nil {
+						err = fmt.Errorf("获取图表日期信息失败,Err:" + tErr.Error())
+						return
+					}
+					if lastDateT.Year() > yearMax {
+						yearMax = lastDateT.Year()
+					}
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	if startDate != "" {
+		for k, v := range edbInfoMappingList {
+			var maxData, minData float64
+			dataList := v.DataList.([]*models.EdbDataList)
+			newDataList := make([]*models.EdbDataList, 0)
+			if len(dataList) == 0 {
+				newDataList = dataList
+			} else {
+				firstFlag := true
+				for _, d := range dataList {
+					if endDate != "" && d.DataTime > endDate {
+						continue
+					}
+					if d.DataTime < startDate {
+						continue
+					}
+					newDataList = append(newDataList, d)
+					if firstFlag {
+						maxData = d.Value
+						minData = d.Value
+						firstFlag = false
+					} else {
+						if d.Value > maxData {
+							maxData = d.Value
+						}
+						if d.Value < minData {
+							minData = d.Value
+						}
+					}
+				}
+			}
+			edbInfoMappingList[k].DataList = newDataList
+			edbInfoMappingList[k].MinData = minData
+			edbInfoMappingList[k].MaxData = maxData
+		}
+	}
+	dataResp = models.ChartRangeAnalysisDataResp{ChartRangeAnalysisExtraConf: req}
+	if req.MultipleGraphConfigId > 0 {
+		multipleGraphConfigEdbMappingList, e := data_manage.GetMultipleGraphConfigEdbMappingListByIdAndSource(req.MultipleGraphConfigId, utils.CHART_SOURCE_RANGE_ANALYSIS)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取区间计算图表, 指标信息失败, Err:" + e.Error())
+			return
+		}
+		// 查询是否已经生成指标
+		dataResp.ConfigEdbNum = len(multipleGraphConfigEdbMappingList)
+	}
+	edbList, err = GetChartEdbInfoFormat(chartInfoId, edbInfoMappingList)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算图表, 完善指标信息失败, Err:" + err.Error())
+		return
+	}
+	return
+}
+
+func GetChartDataByEdbInfoListBySeries(chartInfoId int, dateType, startYear int, startDate, endDate string, edbInfoMappingList []*models.ChartEdbInfoMapping, req *models.ChartRangeAnalysisExtraConf) (edbList []*models.ChartEdbInfoMapping, xEdbIdValue []int, dataResp models.ChartRangeAnalysisDataResp, err error) {
+	//查询seriesId
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesMappingItem, err := chartSeriesOb.GetItemByChartInfoId(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("图表关联关系不存在")
+			return
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	}
+	//根据seriesId查询数据
+	//并把数据放到dataList中
+	for _, edbInfoMapping := range edbInfoMappingList {
+		dataOb := new(models.FactorEdbSeriesCalculateDataQjjs)
+		dataList, e := dataOb.GetEdbDataList(seriesMappingItem.FactorEdbSeriesId, edbInfoMapping.EdbInfoId, startDate, endDate)
+		if e != nil {
+			err = e
+			return
+		}
+		edbInfoMapping.ConvertUnit = edbInfoMapping.Unit
+		edbInfoMapping.ConvertEnUnit = edbInfoMapping.UnitEn
+		if req.CalculateType == 2 || req.CalculateType == 3 {
+			edbInfoMapping.ConvertUnit = "无"
+			edbInfoMapping.ConvertEnUnit = "无"
+		}
+		if req.DataConvertType > 0 && req.DataConvertConf.Unit != "" {
+			edbInfoMapping.ConvertUnit = req.DataConvertConf.Unit
+			edbInfoMapping.ConvertEnUnit = req.DataConvertConf.Unit
+		}
+		edbInfoMapping.DataList = dataList
+		// 处理上下限
+		var maxData, minData float64
+		if len(dataList) > 0 {
+			maxData = dataList[0].Value
+			minData = dataList[0].Value
+			for _, v := range dataList {
+				if v.Value > maxData {
+					maxData = v.Value
+				}
+				if v.Value < minData {
+					minData = v.Value
+				}
+			}
+		}
+		edbInfoMapping.MaxData = maxData
+		edbInfoMapping.MinData = minData
+		xEdbIdValue = append(xEdbIdValue, edbInfoMapping.EdbInfoId)
+	}
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range edbInfoMappingList {
+			dataList := v.DataList.([]*models.EdbDataList)
+			latestDate := dataList[len(dataList)-1].DataTime
+			if latestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, latestDate)
+				if tErr != nil {
+					err = fmt.Errorf("获取图表日期信息失败,Err:" + tErr.Error())
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	if startDate != "" {
+		for k, v := range edbInfoMappingList {
+			var maxData, minData float64
+			dataList := v.DataList.([]*models.EdbDataList)
+			newDataList := make([]*models.EdbDataList, 0)
+			if len(dataList) == 0 {
+				newDataList = dataList
+			} else {
+				firstFlag := true
+				for _, d := range dataList {
+					if endDate != "" && d.DataTime > endDate {
+						continue
+					}
+					if d.DataTime < startDate {
+						continue
+					}
+					newDataList = append(newDataList, d)
+					if firstFlag {
+						maxData = d.Value
+						minData = d.Value
+						firstFlag = false
+					} else {
+						if d.Value > maxData {
+							maxData = d.Value
+						}
+						if d.Value < minData {
+							minData = d.Value
+						}
+					}
+				}
+			}
+			edbInfoMappingList[k].DataList = newDataList
+			edbInfoMappingList[k].MinData = minData
+			edbInfoMappingList[k].MaxData = maxData
+		}
+	}
+	dataResp = models.ChartRangeAnalysisDataResp{ChartRangeAnalysisExtraConf: req, SeriesId: seriesMappingItem.FactorEdbSeriesId}
+	// 查询配置关联关系
+	if req.MultipleGraphConfigId > 0 {
+		multipleGraphConfigEdbMappingList, e := data_manage.GetMultipleGraphConfigEdbMappingListByIdAndSource(req.MultipleGraphConfigId, utils.CHART_SOURCE_RANGE_ANALYSIS)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取区间计算图表, 指标信息失败, Err:" + e.Error())
+			return
+		}
+		// 查询是否已经生成指标
+		dataResp.ConfigEdbNum = len(multipleGraphConfigEdbMappingList)
+	}
+
+	edbList, err = GetChartEdbInfoFormat(chartInfoId, edbInfoMappingList)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算图表, 完善指标信息失败, Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// getChartDataByEdbInfo 区间计算图表-根据指标信息获取x轴和y轴
+func getChartDataByEdbInfo(edbInfoMapping *models.ChartEdbInfoMapping, req *models.ChartRangeAnalysisExtraConf) (newEdbInfoMapping *models.ChartEdbInfoMapping, err error) {
+	newEdbInfoMapping = edbInfoMapping
+	// 指标的开始日期和结束日期
+	edbStartDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfoMapping.StartDate, time.Local)
+	//edbStartDate := edbStartDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+	edbEndDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfoMapping.EndDate, time.Local)
+	edbEndDate := edbEndDateTime.Format(utils.FormatDate)
+
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*models.EdbDataList, 0)
+	switch edbInfoMapping.EdbInfoCategoryType {
+	case 0:
+		dataList, err = models.GetEdbDataList(edbInfoMapping.Source, edbInfoMapping.SubSource, edbInfoMapping.EdbInfoId, "", "")
+	case 1:
+		_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfoMapping.EdbInfoId, "", "", false)
+	default:
+		err = errors.New("指标base类型异常")
+		return
+	}
+
+	dateList := make([]*models.ChartRangeAnalysisDateDataItem, 0)
+	switch req.DateRangeType {
+	case 0:
+		// 智能划分得到一个开始日期,和结束日期
+		var startDateTime, endDateTime time.Time
+		startDateTime = edbStartDateTime
+		if req.AutoDateConf.IsAutoStartDate == 0 { //固定设置
+			startDate := req.AutoDateConf.StartDate
+			if startDate == "" {
+				startDate = "2020-01-01"
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		}
+		if req.AutoDateConf.IsAutoEndDate == 0 { //固定设置
+			endDate := req.AutoDateConf.EndDate
+			if endDate == "" {
+				err = fmt.Errorf("智能划分截止日期处理失败:请输入截止日期")
+				return
+			}
+			// todo 如果截止日期比指标日期还要大,则用指标的最新日期
+			endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		} else {
+			endConf := req.AutoDateConf.EndDateConf
+			endDate := edbEndDate
+			if endConf.MoveForward > 0 {
+				endDate = GetEdbDateByMoveForward(endDate, endConf.MoveForward, dataList)
+			}
+			if len(endConf.DateChange) > 0 {
+				endDate, err = HandleEdbDateChange(endDate, endConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分结束日期处理失败:%s", err.Error())
+					return
+				}
+			}
+			endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		}
+
+		dateList = append(dateList, &models.ChartRangeAnalysisDateDataItem{
+			StartDate: startDateTime,
+			EndDate:   endDateTime})
+	case 1:
+		// 手工划分得到多个开始日期和结束日期(已排序)
+		for _, v := range req.ManualDateConf {
+			startDateT, _ := time.ParseInLocation(utils.FormatDate, v.StartDate, time.Local)
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+			tmp := &models.ChartRangeAnalysisDateDataItem{
+				StartDate: startDateT,
+				EndDate:   endDateT,
+			}
+			dateList = append(dateList, tmp)
+		}
+	case 2:
+		// 跨年划分得到多个开始日期和结束日期
+		startYear := edbStartDateTime.Year()
+		endYear := edbEndDateTime.Year()
+		startDay := req.YearDateConf.StartDay
+		endDay := req.YearDateConf.EndDay
+		for year := startYear; year <= endYear; year++ {
+			startDate := fmt.Sprintf("%d-%s", year, startDay)
+			endDate := fmt.Sprintf("%d-%s", year+1, endDay)
+			startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+			endDateTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			if startDateTime.Before(edbStartDateTime) {
+				break
+			}
+
+			tmp := &models.ChartRangeAnalysisDateDataItem{
+				StartDate: startDateTime,
+				EndDate:   endDateTime,
+			}
+			dateList = append(dateList, tmp)
+		}
+	}
+	// 根据日期,获取数据
+	for _, v := range dateList {
+		for _, vv := range dataList {
+			dataTimeT, _ := time.ParseInLocation(utils.FormatDate, vv.DataTime, time.Local)
+			if dataTimeT.After(v.StartDate) && dataTimeT.Before(v.EndDate) ||
+				dataTimeT.Equal(v.StartDate) ||
+				dataTimeT.Equal(v.EndDate) {
+				v.DataList = append(v.DataList, vv)
+			}
+		}
+	}
+	// 根据时间区间类型来获取数据的计算窗口,然后再拼接成整段数据
+	newDataList, err := HandleDataByCalculateType(dateList, dataList, req)
+	if err != nil {
+		return
+	}
+	if req.UnNormalDataDealType > 0 {
+		switch req.UnNormalDataDealType { //0:不处理,1:剔除,2替换
+		case 1:
+			dealDataList := make([]*models.EdbDataList, 0)
+			for _, v := range newDataList {
+				if !utils.CompareFloatByOpStrings(req.UnNormalDataConf.Formula, v.Value, req.UnNormalDataConf.Value) {
+					dealDataList = append(dealDataList, v)
+				}
+			}
+		case 2:
+			for i, v := range newDataList {
+				if utils.CompareFloatByOpStrings(req.UnNormalDataConf.Formula, v.Value, req.UnNormalDataConf.Value) {
+					newDataList[i].Value = req.UnNormalDataConf.ReplaceValue
+				}
+			}
+		}
+	}
+
+	if req.DataConvertType > 0 {
+		// 数据转换类型 0不转, 1乘 2除 3对数
+		switch req.DataConvertType {
+		case 1:
+			for i, v := range newDataList {
+				val := v.Value * req.DataConvertConf.Value
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = item.MaxData * v.ConvertValue
+			//item.MinData = item.MinData * v.ConvertValue
+		case 2:
+			for i, v := range newDataList {
+				val := v.Value / req.DataConvertConf.Value
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = item.MaxData / v.ConvertValue
+			//item.MinData = item.MinData / v.ConvertValue
+		case 3:
+			for i, v := range newDataList {
+				if v.Value <= 0 {
+					err = errors.New("数据中含有负数或0,无法对数运算")
+					return
+				}
+				val := math.Log(v.Value) / math.Log(req.DataConvertConf.Value)
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = math.Log(item.MaxData) / math.Log(v.ConvertValue)
+			//item.MinData = math.Log(item.MinData) / math.Log(v.ConvertValue)
+		}
+	}
+	newEdbInfoMapping.DataList = newDataList
+	//时间处理
+
+	return
+}
+
+// RollingCorrelationChartDataResp 滚动区间计算图表数据
+type RollingCorrelationChartDataResp struct {
+	MaxData             float64
+	MinData             float64
+	LatestDate          string `description:"真实数据的最后日期"`
+	EdbInfoCategoryType int
+	ChartColor          string
+	ChartStyle          string
+	PredictChartColor   string
+	ChartType           int
+	ChartWidth          int
+	EdbName             string
+	EdbNameEn           string
+	Unit                string
+	UnitEn              string
+	IsAxis              int
+	DataList            []models.EdbDataList
+}
+
+// ChartInfoRefresh 图表刷新
+func ChartInfoRefresh(chartInfoId int, uniqueCode string) (isAsync bool, err error) {
+	var errMsg string
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("CorrelationChartInfoRefresh: %s", errMsg)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+	var chartInfo *models.ChartInfo
+	if chartInfoId > 0 {
+		chartInfo, err = models.GetChartInfoById(chartInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "图表已被删除,请刷新页面"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取图表信息失败"
+			err = errors.New("获取图表信息失败,Err:" + err.Error())
+			return
+		}
+	} else {
+		chartInfo, err = models.GetChartInfoByUniqueCode(uniqueCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "图表已被删除,请刷新页面"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取图表信息失败"
+			err = errors.New("获取图表信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	// 1.刷新图表关联的指标
+	mappings, e := data_manage.GetChartEdbMappingList(chartInfoId)
+	if e != nil {
+		utils.FileLog.Info(fmt.Sprintf("获取图表关联指标失败, err: %v", e))
+		return
+	}
+	if len(mappings) == 0 {
+		utils.FileLog.Info("图表无关联指标")
+		return
+	}
+	var edbIds []int
+	for _, v := range mappings {
+		edbIds = append(edbIds, v.EdbInfoId)
+	}
+	if e, _ = data.EdbInfoRefreshAllFromBase(edbIds, false); e != nil {
+		utils.FileLog.Info(fmt.Sprintf("批量刷新指标失败, err: %v", e))
+		return
+	}
+
+	// todo 重新计算
+	// 区间计算图表配置校验
+	var extraConfig models.ChartRangeAnalysisExtraConf
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg + ", Err: " + err.Error())
+		return
+	}
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesMappingItem, err := chartSeriesOb.GetItemByChartInfoId(chartInfo.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	} else {
+		_, e = FactorEdbStepCalculateRange(seriesMappingItem.FactorEdbSeriesId, edbIds, extraConfig, true)
+		if e != nil {
+			err = fmt.Errorf("计算因子指标失败, Err: " + e.Error())
+			return
+		}
+	}
+	// 4.清除图表缓存
+	key := utils.HZ_CHART_LIB_DETAIL + uniqueCode
+	_ = utils.Rc.Delete(key)
+
+	return
+}
+
+func GetEdbDateByMoveForward(startDate string, moveForward int, edbDataList []*models.EdbDataList) (date string) {
+	// 根据日期进行排序
+	index := 0
+	length := len(edbDataList)
+	for i := length - 1; i >= 0; i-- {
+		item := edbDataList[i]
+		if item.DataTime == startDate {
+			index += 1
+			continue
+		}
+		if index >= moveForward {
+			date = item.DataTime
+			break
+		}
+		if index > 0 {
+			index += 1
+			date = item.DataTime
+		}
+	}
+	return
+}
+
+// HandleEdbDateChange 处理日期变换
+func HandleEdbDateChange(date string, dateChange []*models.EdbDataDateChangeConf) (newDate string, err error) {
+	newDate = date
+	if newDate != "" {
+		if len(dateChange) > 0 {
+			var dateTime time.Time
+			dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+			if err != nil {
+				err = fmt.Errorf("日期解析失败: %s", err.Error())
+				return
+			}
+			for _, v := range dateChange {
+				if v.ChangeType == 1 {
+					dateTime = dateTime.AddDate(v.Year, v.Month, v.Day)
+					newDate = dateTime.Format(utils.FormatDate)
+				} else if v.ChangeType == 2 {
+					newDate, err, _ = utils.HandleSystemAppointDateT(dateTime, v.FrequencyDay, v.Frequency)
+					if err != nil {
+						return
+					}
+					dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+					if err != nil {
+						err = fmt.Errorf("日期解析失败: %s", err.Error())
+						return
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// FactorEdbStepCalculateRange 因子指标-区间计算
+func FactorEdbStepCalculateRange(seriesId int, edbArr []int, extraConf models.ChartRangeAnalysisExtraConf, recalculate bool) (calculateResp data_manage.FactorEdbSeriesStepCalculateResp, err error) {
+	// todo 如果指标已保存,则用指标数据还是图表指标数据?
+	// 获取图表x轴y轴
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("FactorEdbStepCalculateRange 区间计算, ErrMsg: %v", err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+		/*if len(calculateResp.Fail) > 0 {
+			tips := "StepCalculate计算失败, ErrMsg: "
+			for _, f := range calculateResp.Fail {
+				tips += fmt.Sprintf("code: %s, err: %s\n", f.EdbCode, f.ErrMsg)
+			}
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}*/
+	}()
+
+	edbInfoMappingList, e := models.GetChartEdbMappingListByEdbInfoIdList(edbArr)
+	if e != nil {
+		err = fmt.Errorf("获取区间计算图表, A指标mapping信息失败, Err:%v", e)
+		return
+	}
+
+	_, _, _, err = GetChartDataByEdbInfoList(0, 0, 0, "", "", edbInfoMappingList, &extraConf)
+	if err != nil {
+		err = fmt.Errorf("获取图表数据失败, ErrMsg: %v", err)
+		return
+	}
+
+	// 重新计算-先清除原数据
+	calculateDataOb := new(models.FactorEdbSeriesCalculateDataQjjs)
+
+	cond := fmt.Sprintf("%s = ?", calculateDataOb.Cols().FactorEdbSeriesId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, seriesId)
+	if e := calculateDataOb.RemoveByCondition(cond, pars); e != nil {
+		err = fmt.Errorf("清除原数据失败, err: %v", e)
+		return
+	}
+
+	// 计算成功的保存结果
+	dataArr := make([]*models.FactorEdbSeriesCalculateDataQjjs, 0)
+	for _, v := range edbInfoMappingList {
+		dataList := v.DataList.([]*models.EdbDataList)
+		for _, dataItem := range dataList {
+			dataTime, _ := time.ParseInLocation(utils.FormatDate, dataItem.DataTime, time.Local)
+			dataArr = append(dataArr, &models.FactorEdbSeriesCalculateDataQjjs{
+				FactorEdbSeriesId: seriesId,
+				EdbInfoId:         v.EdbInfoId,
+				EdbCode:           v.EdbCode,
+				DataTime:          dataTime,
+				Value:             dataItem.Value,
+				CreateTime:        time.Now().Local(),
+				ModifyTime:        time.Now().Local(),
+				DataTimestamp:     dataItem.DataTimestamp,
+			})
+		}
+	}
+	if len(dataArr) == 0 {
+		err = fmt.Errorf("计算结果无数据, seriesId: %d", seriesId)
+		return
+	}
+	if e = calculateDataOb.CreateMulti(dataArr); e != nil {
+		err = fmt.Errorf("保存计算结果失败, seriesId: %d, err: %v, ", seriesId, e)
+		return
+	}
+	return
+}

+ 228 - 0
services/excel/luck_sheet_table.go

@@ -0,0 +1,228 @@
+package excel
+
+import (
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// HandleRuleToTableCell 根据管理规则渲染单元格数据
+func HandleRuleToTableCell(excelInfoId int, oldTableData TableData) (newTableData TableData, err error) {
+	newTableData = oldTableData
+	excelRuleMappingList, err := models.GetExcelRuleMappingByExcelInfoId(excelInfoId)
+	if err != nil {
+		return
+	}
+	if len(excelRuleMappingList) == 0 {
+		return
+	}
+	tableDataList := oldTableData.TableDataList
+	excelRuleMap := make(map[int]*models.ExcelInfoRuleMappingView)
+	for _, v := range excelRuleMappingList {
+		excelRuleMap[v.ExcelInfoRuleMappingId] = v
+	}
+	ruleScopeMap := generateRuleScopeIndexMap(excelRuleMappingList)
+	for row, scopeValues := range ruleScopeMap {
+		for col, ruleId := range scopeValues {
+			if v, ok := excelRuleMap[ruleId]; ok {
+				if len(tableDataList) > row && len(tableDataList[row]) > col {
+					// 符合管理规则要求,则进行字体和背景颜色的渲染
+					if checkCellRule(v, tableDataList[row][col].Monitor, tableDataList) {
+						tableDataList[row][col].Background = v.BackgroundColor
+						tableDataList[row][col].FontColor = v.FontColor
+					}
+				} else {
+					continue
+				}
+			} else {
+				continue
+			}
+		}
+	}
+	return
+}
+
+func getCellValueByType(value string, valueType int, tableDataList [][]LuckySheetDataValue) (float64, bool) {
+	if valueType == 2 {
+		coords := strings.Split(value, ",")
+		var coordIntArr []int
+		for _, v := range coords {
+			t, _ := strconv.Atoi(v)
+			coordIntArr = append(coordIntArr, t)
+		}
+		if len(coordIntArr) == 2 {
+			x, y := coordIntArr[0]-1, coordIntArr[1]-1
+			conditionValue, err := strconv.ParseFloat(tableDataList[y][x].Monitor, 64)
+			if err != nil {
+				return 0, false
+			}
+			return conditionValue, true
+		}
+	} else {
+		conditionValue, err := strconv.ParseFloat(value, 64)
+		if err != nil {
+			return 0, false
+		}
+		return conditionValue, true
+	}
+	return 0, false
+}
+
+func checkCellRule(ruleInfo *models.ExcelInfoRuleMappingView, value string, tableDataList [][]LuckySheetDataValue) bool {
+	var tableValue float64
+	var tableTime time.Time
+	var err error
+
+	if ruleInfo.RuleType == 5 {
+		tableTime, err = time.ParseInLocation(utils.FormatDate, value, time.Local)
+		if err != nil {
+			return false
+		}
+	} else {
+		tableValue, err = strconv.ParseFloat(value, 64)
+		if err != nil {
+			return false
+		}
+	}
+	switch ruleInfo.RuleType {
+	// 大于
+	case 1:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue > conditionValue {
+			return true
+		}
+	// 小于
+	case 2:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue < conditionValue {
+			return true
+		}
+	// 介于
+	case 3:
+		leftcondValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		rightcondValue, ok := getCellValueByType(ruleInfo.RightValue, ruleInfo.RightValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if leftcondValue <= tableValue && tableValue <= rightcondValue {
+			return true
+		}
+	// 等于
+	case 4:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue == conditionValue {
+			return true
+		}
+	// 发生日期
+	case 5:
+		// 发生日期
+		var dateStart, dataEnd time.Time
+		switch ruleInfo.LeftValueBack {
+		// 今天
+		case "today":
+			// 获得今天的零点零分零秒
+			dateStart = utils.Today()
+			if tableTime == dateStart {
+				return true
+			}
+		// 明天
+		case "tomorrow":
+			dateStart = utils.Tomorrow()
+			if tableTime == dateStart {
+				return true
+			}
+		// 最近7天
+		case "last7days":
+			dateStart, dataEnd = utils.Last7Days()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 上周
+		case "lastweek":
+			dateStart, dataEnd = utils.LastWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 本周
+		case "thisweek":
+			dateStart, dataEnd = utils.ThisWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 下周
+		case "nextweek":
+			dateStart, dataEnd = utils.NextWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 上月
+		case "lastmonth":
+			dateStart, dataEnd = utils.LastMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 本月
+		case "thismonth":
+			dateStart, dataEnd = utils.ThisMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 下月
+		case "nextmonth":
+			dateStart, dataEnd = utils.NextMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		default:
+			return false
+		}
+
+	}
+	return false
+}
+
+func generateRuleScopeIndexMap(items []*models.ExcelInfoRuleMappingView) (ruleScopeMap map[int]map[int]int) {
+	ruleScopeMap = make(map[int]map[int]int)
+	for _, item := range items {
+		coords := strings.Split(item.ScopeCoord, ",")
+		var coordIntArr []int
+		for _, v := range coords {
+			t, _ := strconv.Atoi(v)
+			coordIntArr = append(coordIntArr, t)
+		}
+
+		if len(coords) == 4 {
+			xmin, ymin, xmax, ymax := coordIntArr[0]-1, coordIntArr[1]-1, coordIntArr[2]-1, coordIntArr[3]-1
+			for i := ymin; i <= ymax; i++ {
+				for j := xmin; j <= xmax; j++ {
+					if _, ok := ruleScopeMap[i]; !ok {
+						ruleScopeMap[i] = make(map[int]int)
+					}
+					ruleScopeMap[i][j] = item.ExcelInfoRuleMappingId
+				}
+			}
+		}
+		if len(coords) == 2 {
+			x, y := coordIntArr[0]-1, coordIntArr[1]-1
+			if _, ok := ruleScopeMap[y]; !ok {
+				ruleScopeMap[y] = make(map[int]int)
+			}
+			ruleScopeMap[y][x] = item.ExcelInfoRuleMappingId
+		}
+	}
+	return
+}

+ 45 - 0
services/excel/lucky_sheet.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_chart_lib/models/request"
 	"eta/eta_chart_lib/utils"
 	"fmt"
+	"math"
 	"reflect"
 	"sort"
 	"strconv"
@@ -1211,7 +1212,51 @@ func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hide
 					tmp.MergeCell.Column = cell.MerData.Mer.Col
 				}
 				if cell.ShowStyle != "" {
+					var styleConfig request.MixCellShowStyle
+					if err := json.Unmarshal([]byte(cell.ShowStyle), &styleConfig); err != nil {
+						utils.FileLog.Info("表格样式showStyle解析失败", err.Error())
+					}
+					if styleConfig.BackgroundColor != "" {
+						tmp.Background = styleConfig.BackgroundColor
+					}
+					if styleConfig.Color != "" {
+						tmp.FontColor = styleConfig.Color
+					}
 					tmp.Monitor = cell.ShowFormatValue
+					tmpShowValue, err := strconv.ParseFloat(cell.Value, 64)
+					if err == nil {
+						switch styleConfig.Last {
+						case "nt":
+							// 先进行数字的百分比计算,然后保留小数点位数
+							percent := tmpShowValue * 100
+							if styleConfig.Decimal == nil {
+								continue
+							}
+							factor := math.Pow(10, float64(*styleConfig.Decimal))
+							rounded := math.Round(percent*factor) / factor
+							tmp.Monitor = fmt.Sprintf("%g%%", rounded)
+						case "decimal":
+							// 先保留小数点位数,再进行百分比计算
+							if styleConfig.Decimal == nil {
+								continue
+							}
+							factor := math.Pow(10, float64(*styleConfig.Decimal))
+							rounded := math.Round(tmpShowValue*factor) / factor
+							if styleConfig.Nt == "percent" {
+								percent := rounded * 100
+								var precisionStr string
+								if *styleConfig.Decimal > 2 {
+									precision := *styleConfig.Decimal - 2
+									precisionStr = strconv.FormatFloat(rounded, 'f', precision, 64)
+								} else {
+									precisionStr = strconv.FormatFloat(math.Round(percent), 'f', -1, 64)
+								}
+								tmp.Monitor = fmt.Sprintf("%s%%", precisionStr)
+							} else {
+								tmp.Monitor = fmt.Sprintf("%g", rounded)
+							}
+						}
+					}
 				}
 				dataCol = append(dataCol, tmp)
 			}

+ 146 - 0
utils/common.go

@@ -9,6 +9,7 @@ import (
 	"encoding/base64"
 	"encoding/hex"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"image"
 	"image/png"
@@ -1213,3 +1214,148 @@ func TimeTransferString(format string, t time.Time) string {
 	}
 	return str
 }
+
+// handleSystemAppointDateT
+// @Description: 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+// @author: Roc
+// @datetime2023-10-27 09:31:35
+// @param Frequency string
+// @param Day string
+// @return date string
+// @return err error
+// @return errMsg string
+func HandleSystemAppointDateT(currDate time.Time, appointDay, frequency string) (date string, err error, errMsg string) {
+	//currDate := time.Now()
+	switch frequency {
+	case "本周":
+		day := int(currDate.Weekday())
+		if day == 0 { // 周日
+			day = 7
+		}
+		num := 0
+		switch appointDay {
+		case "周一":
+			num = 1
+		case "周二":
+			num = 2
+		case "周三":
+			num = 3
+		case "周四":
+			num = 4
+		case "周五":
+			num = 5
+		case "周六":
+			num = 6
+		case "周日":
+			num = 7
+		}
+		day = num - day
+		date = currDate.AddDate(0, 0, day).Format(FormatDate)
+	case "本旬":
+		day := currDate.Day()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 11, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 21, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本月":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本季":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 4, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 10, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本半年":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本年":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+		}
+		date = tmpDate.Format(FormatDate)
+	default:
+		errMsg = "错误的日期频度:" + frequency
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+func CompareFloatByOpStrings(op string, a, b float64) bool {
+	switch op {
+	case "=":
+		return a == b
+	case ">":
+		return a > b
+	case ">=":
+		return a >= b
+	case "<=":
+		return a <= b
+	case "<":
+		return a < b
+	}
+	return false
+}

+ 1 - 0
utils/constants.go

@@ -141,6 +141,7 @@ const (
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9  // 统计特征-频率分布图表
 	CHART_SOURCE_CROSS_HEDGING                   = 10 // 跨品种分析图表
 	CHART_SOURCE_BALANCE_EXCEL                   = 11 // 平衡表图表
+	CHART_SOURCE_RANGE_ANALYSIS                  = 12 // 	区间分析图表
 )
 
 // 图表来源

+ 74 - 0
utils/time.go

@@ -0,0 +1,74 @@
+package utils
+
+import "time"
+
+// 获取今天的日期(零点零分零秒)
+func Today() time.Time {
+	now := time.Now()
+	return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
+}
+
+// 获取明天的日期(零点零分零秒)
+func Tomorrow() time.Time {
+	return Today().AddDate(0, 0, 1)
+}
+
+// 获取最近7天的日期范围
+func Last7Days() (time.Time, time.Time) {
+	today := Today()
+	return today.AddDate(0, 0, -6), today.Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+}
+
+// 获取上周的日期范围
+func LastWeek() (time.Time, time.Time) {
+	start := Today().AddDate(0, 0, -7)
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取本周的日期范围
+func ThisWeek() (time.Time, time.Time) {
+	start := Today().Round(0).Local().AddDate(0, 0, 0-int(time.Now().Weekday())+1) // 0是本周的第一天,+1是因为AddDate是相对于当前时间的
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取下周的日期范围
+func NextWeek() (time.Time, time.Time) {
+	_, thisEnd := ThisWeek()
+	start := thisEnd.AddDate(0, 0, 1)
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取上月的日期范围
+func LastMonth() (time.Time, time.Time) {
+	today := time.Now()
+	year, month, _ := today.Date()
+	start := time.Date(year, month-1, 1, 0, 0, 0, 0, today.Location())
+	end := time.Date(year, month, 0, 23, 59, 59, 999999999, today.Location())
+	return start, end
+}
+
+// 获取本月的日期范围
+func ThisMonth() (time.Time, time.Time) {
+	today := time.Now()
+	year, month, _ := today.Date()
+	start := time.Date(year, month, 1, 0, 0, 0, 0, today.Location())
+	end := time.Date(year, month+1, 0, 23, 59, 59, 999999999, today.Location())
+	return start, end
+}
+
+// 获取下月的日期范围
+func NextMonth() (time.Time, time.Time) {
+	now := time.Now()
+	year, month, _ := now.Date()
+	nextMonth := month + 1
+	if nextMonth > 12 {
+		nextMonth = 1
+		year++
+	}
+	startOfNextMonth := time.Date(year, nextMonth, 1, 0, 0, 0, 0, now.Location())
+	endOfNextMonth := startOfNextMonth.AddDate(0, 1, -1).Add(time.Hour*23 + time.Minute*59 + time.Second*59)
+	return startOfNextMonth, endOfNextMonth
+}