瀏覽代碼

兼容平衡表图表

xyxie 9 月之前
父節點
當前提交
23d9b63729

+ 54 - 0
controllers/chart_common.go

@@ -13,6 +13,7 @@ import (
 	"eta/eta_chart_lib/services/data"
 	correlationServ "eta/eta_chart_lib/services/data/correlation"
 	"eta/eta_chart_lib/services/data/cross_variety"
+	"eta/eta_chart_lib/services/data/excel"
 	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"
@@ -104,6 +105,13 @@ func (this *ChartController) CommonChartInfoDetailFromUniqueCode() {
 		resp, isOk, msg, errMsg = GetLineFeatureChartInfoDetailFromUniqueCode(chartInfo, key)
 	case utils.CHART_SOURCE_CROSS_HEDGING:
 		resp, isOk, msg, errMsg = GetCrossVarietyChartInfoDetailFromUniqueCode(chartInfo, key)
+	case utils.CHART_SOURCE_BALANCE_EXCEL:
+		resp, isOk, msg, errMsg = GetBalanceChartInfoDetailFromUniqueCode(chartInfo, key)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
@@ -566,6 +574,52 @@ func GetCrossVarietyChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, k
 	return
 }
 
+func GetBalanceChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, key string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
+	resp = new(models.ChartInfoDetailResp)
+	msg = `获取失败`
+	var err error
+	defer func() {
+		if err != nil {
+			if errMsg != "" {
+				msg = errMsg
+			}
+			errMsg = err.Error()
+		}
+	}()
+	// 相关联指标
+	mappingListTmp, dataListMap, err, errMsg := excel.GetBalanceExcelChartSingle(chartInfo, "")
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	var chartInfoResp *models.ChartInfoDetailResp
+	chartInfoResp, err, errMsg = data.GetBalanceExcelChartDetail(chartInfo, mappingListTmp, dataListMap)
+	if err != nil {
+		msg = "查询图表详情失败"
+		errMsg = "查询图表详情失败,Err:" + err.Error()
+		return
+	}
+	resp = &models.ChartInfoDetailResp{
+		ChartInfo:            chartInfoResp.ChartInfo,
+		EdbInfoList:          chartInfoResp.EdbInfoList,
+		XEdbIdValue:          chartInfoResp.XEdbIdValue,
+		YDataList:            chartInfoResp.YDataList,
+		XDataList:            chartInfoResp.XDataList,
+		CorrelationChartInfo: chartInfoResp.CorrelationChartInfo,
+		DataResp:             chartInfoResp.DataResp,
+	}
+
+	if utils.Re == nil {
+		jsonData, _ := json.Marshal(resp)
+		utils.Rc.Put(key, jsonData, 10*time.Minute)
+	}
+
+	isOk = true
+
+	return
+}
+
 // FutureGoodChartInfoRefresh
 // @Title 商品价格图表刷新接口
 // @Description 商品价格图表刷新接口

+ 9 - 3
go.mod

@@ -12,6 +12,7 @@ require (
 	github.com/qiniu/qmgo v1.1.8
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/shopspring/decimal v1.3.1
+	github.com/xuri/excelize/v2 v2.8.1
 	github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476
 	go.mongodb.org/mongo-driver v1.15.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -39,21 +40,26 @@ require (
 	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/prometheus/client_golang v1.16.0 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.10.1 // indirect
+	github.com/richardlehane/mscfb v1.0.4 // indirect
+	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
+	github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
-	golang.org/x/crypto v0.18.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/crypto v0.19.0 // indirect
+	golang.org/x/net v0.21.0 // indirect
 	golang.org/x/sync v0.2.0 // indirect
-	golang.org/x/sys v0.16.0 // indirect
+	golang.org/x/sys v0.17.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

+ 23 - 8
go.sum

@@ -141,6 +141,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -188,6 +190,11 @@ github.com/qiniu/qmgo v1.1.8 h1:E64M+P59aqQpXKI24ClVtluYkLaJLkkeD2hTVhrdMks=
 github.com/qiniu/qmgo v1.1.8/go.mod h1:QvZkzWNEv0buWPx0kdZsSs6URhESVubacxFPlITmvB8=
 github.com/rdlucklib/rdluck_tools v1.0.3 h1:iOtK2QPlPQ6CL6c1htCk5VnFCHzyG6DCfJtunrMswK0=
 github.com/rdlucklib/rdluck_tools v1.0.3/go.mod h1:9Onw9o4w19C8KE5lxb8GyxgRBbZweRVkQSc79v38EaA=
+github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
+github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
+github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
+github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
@@ -208,8 +215,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -223,6 +230,12 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
 github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
 github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0=
+github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
+github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
+github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
+github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
+github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
 github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476 h1:66fLxv8xlhSr42ZhVAYjUY/sEF0olUUAESVlsxVduuw=
 github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476/go.mod h1:9/dQiKiN04yPMdgsuFmKGuI2Hdp6OmFV9gSWS1col6g=
 github.com/ylywyn/jpush-api-go-client v0.0.0-20190906031852-8c4466c6e369/go.mod h1:Nv7wKD2/bCdKUFNKcJRa99a+1+aSLlCRJFriFYdjz/I=
@@ -239,8 +252,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
+golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -250,8 +265,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -275,8 +290,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

+ 44 - 0
models/data_manage/excel/excel_chart_data.go

@@ -0,0 +1,44 @@
+package excel
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ExcelChartData struct {
+	ExcelChartDataId int `orm:"column(excel_chart_data_id);pk"`
+	ExcelInfoId      int `description:"表格id"`
+	ExcelChartEdbId  int `description:"指标ID"`
+	DataTime         string
+	Value            float64
+	ModifyTime       time.Time `description:"修改时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	DataTimestamp    int64
+}
+
+func (e *ExcelChartData) TableName() string {
+	return "excel_chart_data"
+}
+
+// 新增
+func (e *ExcelChartData) Add() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
+// 修改
+func (e *ExcelChartData) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 删除
+func (e *ExcelChartData) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Delete(e)
+	return
+}
+
+// 查询

+ 142 - 0
models/data_manage/excel/excel_chart_edb.go

@@ -0,0 +1,142 @@
+package excel
+
+import (
+	"eta/eta_chart_lib/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ExcelChartEdb struct {
+	ExcelChartEdbId int       `orm:"column(excel_chart_edb_id);pk"`
+	ExcelInfoId     int       `description:"表格id"`
+	ChartInfoId     int       `description:"图表id"`
+	EdbCode         string    `description:"指标编码"`
+	EdbName         string    `description:"指标名称"`
+	DateSequence    string    `description:"日期序列选区"`
+	DataSequence    string    `description:"数据序列选区"`
+	SysUserId       int       `description:"创建人"`
+	SysUserRealName string    `description:"创建人姓名"`
+	MaxData         float64   `description:"上限"`
+	MinData         float64   `description:"下限"`
+	IsOrder         bool      `description:"true:正序,false:逆序"`
+	IsAxis          int       `description:"true:左轴,false:右轴"`
+	EdbInfoType     int       `description:"true:标准指标,false:领先指标"`
+	LeadValue       int       `description:"领先值"`
+	LeadUnit        string    `description:"领先单位"`
+	FromTag         string    `description:"标签"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+type ExcelChartEdbView struct {
+	ExcelChartEdbId int
+	ExcelInfoId     int    `description:"表格id"`
+	ChartInfoId     int    `description:"图表id"`
+	EdbCode         string `description:"指标编码"`
+	EdbName         string `description:"指标名称"`
+	DateSequenceStr string `description:"日期序列选区"`
+	DataSequenceStr string `description:"数据序列选区"`
+	/*MaxData         float64 `description:"上限"`
+	MinData         float64 `description:"下限"`
+	IsOrder         bool    `description:"true:正序,false:逆序"`
+	IsAxis          int     `description:"true:左轴,false:右轴"`
+	EdbInfoType     int     `description:"true:标准指标,false:领先指标"`
+	LeadValue       int     `description:"领先值"`
+	LeadUnit        string  `description:"领先单位"`*/
+	FromTag string `description:"标签"`
+}
+
+type BalanceTableChart struct {
+	ChartInfoId       int    `description:"图表id,新增时传0"`
+	ChartName         string `description:"图表名称"`
+	ChartType         int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	Calendar          string `description:"公历/农历"`
+	LeftMin           string `description:"图表左侧最小值"`
+	LeftMax           string `description:"图表左侧最大值"`
+	RightMin          string `description:"图表右侧最小值"`
+	RightMax          string `description:"图表右侧最大值"`
+	Right2Min         string `description:"图表右侧2最小值"`
+	Right2Max         string `description:"图表右侧2最大值"`
+	MinMaxSave        int    `description:"是否手动保存过上下限:0-否;1-是"`
+	ExtraConfig       string `description:"图表额外配置信息,json字符串"`
+	ChartImage        string `description:"封面图" json:"-"`
+	SeasonExtraConfig string `description:"季节性图表中的配置,json数据"`
+	SourcesFrom       string `description:"图表来源"`
+	//	ChartEdbInfoList  []ExcelChartEdbView
+}
+
+func (e *ExcelChartEdb) TableName() string {
+	return "excel_chart_edb"
+}
+
+// 新增
+func (e *ExcelChartEdb) Add() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
+// 修改
+func (e *ExcelChartEdb) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 删除
+func (e *ExcelChartEdb) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Delete(e)
+	return
+}
+
+type AddChartEdbAndDataItem struct {
+	ChartEdb *ExcelChartEdb
+	DateList []string  `description:"日期列表"`
+	ValList  []float64 `description:"数据列表"`
+}
+
+func GetExcelChartEdbMappingByExcelInfoId(excelInfoId int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE excel_info_id=? 
+             ORDER BY excel_chart_edb_id ASC `
+	_, err = o.Raw(sql, excelInfoId).QueryRows(&list)
+	return
+}
+
+func GetExcelChartEdbMappingByExcelInfoIds(excelInfoIds []int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `)`
+	_, err = o.Raw(sql, excelInfoIds).QueryRows(&list)
+	return
+}
+
+func GetExcelChartEdbById(id int) (item *ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM excel_chart_edb WHERE excel_chart_edb_id=? `
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func GetExcelChartEdbMappingByChartInfoId(chartInfoId int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE chart_info_id=? 
+             ORDER BY excel_chart_edb_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+	return
+}
+
+func GetExcelInfoByChartInfoId(chartInfoId int) (item *ExcelInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT i.*
+             FROM excel_chart_edb e left join excel_info i on e.excel_info_id=i.excel_info_id
+			 WHERE e.chart_info_id=? limit 1`
+	err = o.Raw(sql, chartInfoId).QueryRow(&item)
+	return
+}

+ 93 - 0
services/data/chart_info.go

@@ -1419,3 +1419,96 @@ func RadarChartData(mappingList []*models.ChartEdbInfoMapping, edbDataListMap ma
 	}
 	return
 }
+
+// GetChartEdbDataV2 获取图表的指标数据
+func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, extraConfigStr, seasonExtraConfig string, chartInfoData ChartInfoDataShow) (edbList []*models.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []models.YData, dataResp interface{}, err error, errMsg string) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+	// 指标对应的所有数据
+	xEdbIdValue = make([]int, 0)
+	yDataList = make([]models.YData, 0)
+	var extraConfig interface{}
+	switch chartType {
+	case 7: // 柱形图
+		var barConfig data_manage.BarChartInfoReq
+		if extraConfigStr == `` {
+			errMsg = "柱方图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &barConfig)
+		if err != nil {
+			errMsg = "柱方图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = barConfig
+	case 10: // 截面散点图
+		var tmpExtraConfig data_manage.SectionScatterReq
+		if extraConfigStr == `` {
+			errMsg = "截面散点图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpExtraConfig)
+		if err != nil {
+			errMsg = "截面散点配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = tmpExtraConfig
+	case utils.CHART_TYPE_RADAR:
+		var barConfig data_manage.RadarChartInfoReq
+		if extraConfigStr == `` {
+			errMsg = "雷达图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &barConfig)
+		if err != nil {
+			errMsg = "雷达图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = barConfig
+	default:
+		xEdbIdValue = make([]int, 0)
+		yDataList = make([]models.YData, 0)
+	}
+
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err := chartInfoData.GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig)
+	if err != nil {
+		return
+	}
+
+	// 特殊图形数据处理
+	switch chartType {
+	case 7: // 柱形图
+		barChartConf := extraConfig.(data_manage.BarChartInfoReq)
+		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartConf.DateList, barChartConf.Sort)
+
+		for _, v := range edbList {
+			// 指标别名
+			if barChartConf.EdbInfoIdList != nil && len(barChartConf.EdbInfoIdList) > 0 {
+				for _, reqEdb := range barChartConf.EdbInfoIdList {
+					if v.EdbInfoId == reqEdb.EdbInfoId {
+						v.EdbAliasName = reqEdb.Name
+					}
+				}
+			}
+		}
+	case 10: // 截面散点图
+		sectionScatterConf := extraConfig.(data_manage.SectionScatterReq)
+		xEdbIdValue, dataResp, err = GetSectionScatterChartData(mappingList, edbDataListMap, sectionScatterConf)
+
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
+	case utils.CHART_TYPE_RADAR: //雷达图
+		radarConf := extraConfig.(data_manage.RadarChartInfoReq)
+		xEdbIdValue, dataResp, err = RadarChartData(mappingList, edbDataListMap, radarConf)
+	}
+
+	return
+}

+ 442 - 0
services/data/chart_info_excel_balance.go

@@ -0,0 +1,442 @@
+package data
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/models/data_manage"
+	"eta/eta_chart_lib/models/data_manage/excel"
+	"eta/eta_chart_lib/utils"
+	"fmt"
+	"math"
+	"strings"
+	"time"
+)
+
+func GetBalanceExcelChartDetail(chartInfo *models.ChartInfo, mappingListTmp []*excel.ExcelChartEdb, dataListMap map[int][]*models.EdbDataList) (resp *models.ChartInfoDetailResp, err error, errMsg string) {
+	chartInfoId := chartInfo.ChartInfoId
+	resp = new(models.ChartInfoDetailResp)
+
+	// 获取主题样式
+	chartTheme, err := GetChartThemeConfig(chartInfo.ChartThemeId, 1, chartInfo.ChartType)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取主题信息失败 Err:%s", err.Error())
+		return
+	}
+	chartInfo.ChartThemeStyle = chartTheme.Config
+	chartInfo.ChartThemeId = chartTheme.ChartThemeId
+
+	dateType := chartInfo.DateType
+	fmt.Println("dateType:", dateType)
+
+	chartType := chartInfo.ChartType
+	startDate := chartInfo.StartDate
+	endDate := chartInfo.EndDate
+	seasonStartDate := chartInfo.SeasonStartDate
+	seasonEndDate := chartInfo.SeasonEndDate
+	startYear := chartInfo.StartYear
+
+	calendar := chartInfo.Calendar
+
+	if calendar == "" {
+		calendar = "公历"
+	}
+
+	mappingList := make([]*models.ChartEdbInfoMapping, 0)
+	//循环组装映射关系
+	for _, v := range mappingListTmp {
+		dataList := make([]*models.EdbDataList, 0)
+		dataListTmp, ok := dataListMap[v.ExcelChartEdbId]
+		if ok {
+			dataList = dataListTmp
+		} else {
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.ExcelChartEdbId))
+			return
+		}
+		startDateStr, endDateStr, _, endVal, maxValue, minValue := getBalanceDataListStartDateAndValue(dataList)
+		mapping := &models.ChartEdbInfoMapping{
+			EdbInfoId:         v.ExcelChartEdbId,
+			SourceName:        "",
+			Source:            0,
+			SubSource:         0,
+			EdbCode:           v.EdbCode,
+			EdbName:           v.EdbName,
+			EdbAliasName:      v.EdbName,
+			EdbNameEn:         "",
+			EdbAliasNameEn:    "",
+			EdbType:           0,
+			Frequency:         "",
+			FrequencyEn:       "",
+			Unit:              "",
+			UnitEn:            "",
+			StartDate:         startDateStr,
+			EndDate:           endDateStr,
+			ModifyTime:        v.ModifyTime.Format(utils.FormatDateTime),
+			ChartEdbMappingId: v.ExcelChartEdbId,
+			ChartInfoId:       chartInfoId,
+			MaxData:           v.MaxData,
+			MinData:           v.MinData,
+			IsOrder:           v.IsOrder,
+			IsAxis:            v.IsAxis,
+			//EdbInfoType:         0,
+			//EdbInfoCategoryType: 0,
+			LeadValue:         v.LeadValue,
+			LeadUnit:          v.LeadUnit,
+			LeadUnitEn:        "",
+			ChartStyle:        "",
+			ChartColor:        "",
+			PredictChartColor: "",
+			ChartWidth:        0,
+			ChartType:         chartType,
+			LatestDate:        endDateStr,
+			LatestValue:       endVal,
+			MoveLatestDate:    "",
+			UniqueCode:        "",
+			MinValue:          minValue,
+			MaxValue:          maxValue,
+			DataList:          nil,
+			IsNullData:        false,
+			MappingSource:     0,
+			IsConvert:         0,
+			ConvertType:       0,
+			ConvertValue:      0,
+			ConvertUnit:       "",
+			ConvertEnUnit:     "",
+		}
+		mappingList = append(mappingList, mapping)
+	}
+	if chartType == 2 {
+		startDate = seasonStartDate
+		endDate = seasonEndDate
+		if dateType <= 0 {
+			if startDate != "" {
+				dateType = 5
+			} else {
+				dateType = utils.DateTypeNYears
+			}
+		}
+	} else {
+		if dateType <= 0 {
+			dateType = 3
+		}
+	}
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range mappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					errMsg = "获取失败"
+					err = fmt.Errorf("获取图表日期信息失败,Err:" + tErr.Error())
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	if chartInfo.ChartType == 2 {
+		chartInfo.StartDate = startDate
+		chartInfo.EndDate = endDate
+	}
+	// 图表额外数据参数
+	extraConfigStr := chartInfo.ExtraConfig
+	// 柱方图的一些配置
+	var barConfig data_manage.BarChartInfoReq
+	if chartInfo != nil && chartInfo.ChartType == 7 {
+		if chartInfo.BarConfig == `` {
+			err = fmt.Errorf("柱方图未配置")
+			errMsg = "柱方图未配置"
+			return
+		}
+		err = json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
+		if err != nil {
+			err = fmt.Errorf("柱方图配置异常 json.Unmarshal Err:%s", err.Error())
+			errMsg = "柱方图配置异常"
+			return
+		}
+		extraConfigStr = chartInfo.BarConfig
+	}
+	// 获取表格数据
+
+	excelChartInfoDataShow := new(ExcelChartInfoDataShow)
+	excelChartInfoDataShow.DataListMap = dataListMap
+
+	// 获取图表中的指标数据
+	edbList, xEdbIdValue, yDataList, dataResp, e, msg := GetChartEdbDataV2(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig, excelChartInfoDataShow)
+	if e != nil {
+		err = fmt.Errorf("获取图表,指标数据失败,Err:%s", e.Error())
+		errMsg = msg
+		return
+	}
+
+	for _, v := range edbList {
+		// 指标别名
+		if barConfig.EdbInfoIdList != nil && len(barConfig.EdbInfoIdList) > 0 {
+			for _, reqEdb := range barConfig.EdbInfoIdList {
+				if v.EdbInfoId == reqEdb.EdbInfoId {
+					v.EdbAliasName = reqEdb.Name
+				}
+			}
+		}
+	}
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.YDataList = yDataList
+	resp.DataResp = dataResp
+
+	resp.ChartInfo = chartInfo
+	return
+}
+
+// GetBalanceExcelEdbDataMapList 获取指标最后的基础数据
+func GetBalanceExcelEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, seasonExtraConfig string, dataListMap map[int][]*models.EdbDataList) (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
+		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
+
+		var startDateReal string
+		var diffSeconds int64
+		if chartType == 2 { //季节性图
+			startDateReal = startDate
+		} 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, e := time.Parse(utils.FormatDate, startDateReal)
+			if e != nil {
+				err = fmt.Errorf("时间解析 time.Parse(%s, %s) error: %v", utils.FormatDate, startDateReal, e)
+				return
+			}
+			calendarPreYear = newStartDateReal.Year() - 1
+			newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+			startDateReal = newStartDateReal.Format(utils.FormatDate)
+		}
+		dataList := make([]*models.EdbDataList, 0)
+		dataListTmp, ok := dataListMap[v.EdbInfoId]
+		if ok {
+			dataList = dataListTmp
+		} else {
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.EdbInfoId))
+			return
+		}
+		if v.IsConvert == 1 {
+			switch v.ConvertType {
+			case 1:
+				for i, data := range dataList {
+					dataList[i].Value = data.Value * v.ConvertValue
+				}
+				//item.MaxData = item.MaxData * v.ConvertValue
+				//item.MinData = item.MinData * v.ConvertValue
+			case 2:
+				for i, data := range dataList {
+					dataList[i].Value = data.Value / v.ConvertValue
+				}
+				//item.MaxData = item.MaxData / v.ConvertValue
+				//item.MinData = item.MinData / v.ConvertValue
+			case 3:
+				for i, data := range dataList {
+					if data.Value <= 0 {
+						err = errors.New("数据中含有负数或0,无法对数运算")
+						return
+					}
+					dataList[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] = dataList
+
+		if diffSeconds != 0 && v.EdbInfoType == 0 {
+			dataListLen := len(dataList)
+			for i := 0; i < dataListLen; i++ {
+				dataList[i].DataTimestamp = dataList[i].DataTimestamp - diffSeconds
+			}
+		}
+
+		if chartType == 2 {
+			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
+			if tmpErr != nil {
+				//item.DataList = dataList
+				item.IsNullData = true
+				edbList = append(edbList, item)
+				continue
+			}
+
+			if calendar == "农历" {
+				if len(dataList) <= 0 {
+					result := new(models.EdbDataResult)
+					item.DataList = result
+				} else {
+					result, tmpErr := models.AddCalculateQuarterV6(dataList)
+					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(dataList, latestDate, seasonExtraConfig)
+				if tErr != nil {
+					err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+					return
+				}
+				item.DataList = quarterDataList
+			}
+
+		} else if chartType == 7 || chartType == utils.CHART_TYPE_RADAR { //柱方图
+			//item.DataList = dataList
+		} else {
+			item.DataList = dataList
+		}
+		edbList = append(edbList, item)
+	}
+
+	return
+}
+
+func getBalanceDataListStartDateAndValue(dataList []*models.EdbDataList) (startDate, endDate string, startVal, endVal, maxVal, minVal float64) {
+	if len(dataList) == 0 {
+		return
+	}
+	startDate = dataList[0].DataTime
+	startVal = dataList[0].Value
+	maxVal = dataList[0].Value
+	minVal = dataList[0].Value
+	endDate = dataList[len(dataList)-1].DataTime
+	endVal = dataList[len(dataList)-1].Value
+	for _, v := range dataList {
+		if v.DataTime < startDate {
+			startDate = v.DataTime
+			startVal = v.Value
+		}
+		if v.DataTime > endDate {
+			endDate = v.DataTime
+			endVal = v.Value
+		}
+		if v.Value > maxVal {
+			maxVal = v.Value
+		}
+		if v.Value < minVal {
+			minVal = v.Value
+		}
+	}
+	return
+}

+ 30 - 0
services/data/chart_info_interface.go

@@ -0,0 +1,30 @@
+package data
+
+import (
+	"eta/eta_chart_lib/models"
+)
+
+type ChartInfoDataShow interface {
+	GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*models.EdbDataList, edbList []*models.ChartEdbInfoMapping, err error)
+}
+
+type BaseChartInfoDataShow struct {
+}
+
+// GetEdbDataMapList 获取指标最后的基础数据
+func (e *BaseChartInfoDataShow) GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*models.EdbDataList, edbList []*models.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err = GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig)
+	return
+}
+
+type ExcelChartInfoDataShow struct {
+	DataListMap map[int][]*models.EdbDataList
+}
+
+// GetEdbDataMapList 获取指标最后的基础数据
+func (e *ExcelChartInfoDataShow) GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*models.EdbDataList, edbList []*models.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err = GetBalanceExcelEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig, e.DataListMap)
+	return
+}

+ 422 - 0
services/data/excel/balance_table.go

@@ -0,0 +1,422 @@
+package excel
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/models/data_manage/excel"
+	"eta/eta_chart_lib/models/request"
+	"eta/eta_chart_lib/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"github.com/xuri/excelize/v2"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 将表格信息转化成指标数据
+func GetBalanceExcelData(excelDetail *excel.ExcelInfo, lang string) (newDataMap map[int]map[int]request.MixedTableCellDataReq, allRows, allCols int, err error, errMsg string) {
+	var result request.MixedTableReq
+	err = json.Unmarshal([]byte(excelDetail.Content), &result)
+	if err != nil {
+		err = errors.New("表格json转结构体失败,Err:" + err.Error())
+		return
+	}
+	newData, tmpErr, tmpErrMsg := GetMixedTableCellData(result)
+	if tmpErr != nil {
+		errMsg = "获取失败"
+		if tmpErrMsg != `` {
+			errMsg = tmpErrMsg
+		}
+		err = errors.New("获取最新的数据失败,Err:" + tmpErr.Error())
+		return
+	}
+
+	allRows = len(newData)
+	allCols = 0
+	newDataMap = make(map[int]map[int]request.MixedTableCellDataReq)
+	for r, row := range newData {
+		tmp := len(row)
+		if tmp > allCols {
+			allCols = tmp
+		}
+		colMap := make(map[int]request.MixedTableCellDataReq)
+		for c, col := range row {
+			colMap[c] = col
+		}
+		newDataMap[r] = colMap
+	}
+	return
+}
+
+// 获取单个图表信息
+func GetBalanceExcelChartSingle(chartInfo *models.ChartInfo, lang string) (mappingListTmp []*excel.ExcelChartEdb, dataListMap map[int][]*models.EdbDataList, err error, errMsg string) {
+	// 相关联指标
+	mappingListTmp, err = excel.GetExcelChartEdbMappingByChartInfoId(chartInfo.ChartInfoId)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	if len(mappingListTmp) <= 0 {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	excelInfoId := mappingListTmp[0].ExcelInfoId
+	// 查询所有子表
+	excelInfo, err := excel.GetExcelInfoById(excelInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "表格不存在"
+			err = fmt.Errorf(errMsg)
+			return
+		}
+		errMsg = "查询子表失败"
+		err = fmt.Errorf(" 查询子表失败图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	// 获取图表详情
+	newExcelDataMap, excelAllRows, excelAllCols, err, errMsg := GetBalanceExcelData(excelInfo, lang)
+	if err != nil {
+		return
+	}
+	dataListMap = make(map[int][]*models.EdbDataList)
+
+	for _, mapping := range mappingListTmp {
+		err, errMsg = GetBalanceExcelEdbData(mapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+		if err != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+			return
+		}
+	}
+	return
+}
+
+// 将表格信息转化成指标数据
+func GetBalanceExcelEdbData(excelEdbMappingItem *excel.ExcelChartEdb, newMixedTableCellDataListMap map[int]map[int]request.MixedTableCellDataReq, dataListMap map[int][]*models.EdbDataList, allRows, allCols int) (err error, errMsg string) {
+	var dateList, dataList []string
+	// 日期序列
+	{
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DateSequence)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		startNum = startNum - 1
+		endNum = endNum - 1
+		// 选择行的数据
+		if isRow {
+			// 因为是选择一行的数据,所以开始行和结束行时一样的
+			//endNum = startNum - 1
+
+			// 开始列名、结束列
+			var startColumn, endColumn int
+			if isAll {
+				// 结束列(其实也就是整列的个数)
+				endColumn = allCols - 1
+			} else {
+				tmpStartColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + startColumnName
+					err = errors.New(errMsg)
+					return
+				}
+
+				tmpEndColumn, tmpErr := excelize.ColumnNameToNumber(endColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + endColumnName
+					err = errors.New(errMsg)
+					return
+				}
+				startColumn = tmpStartColumn - 1
+				endColumn = tmpEndColumn - 1
+			}
+
+			// 最大列数,如果设置的超过了最大列数,那么结束列就是最大列数
+			maxCol := allCols
+			if endColumn > maxCol {
+				endColumn = maxCol - 1
+			}
+
+			// 长度固定,避免一直申请内存空间
+			dateList = make([]string, endColumn-startColumn+1)
+
+			i := 0
+			for currColumn := startColumn; currColumn <= endColumn; currColumn++ {
+				currCell, ok := newMixedTableCellDataListMap[startNum][currColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currColumn, startNum)
+					err = errors.New(errMsg)
+					return
+				}
+				dateList[i] = currCell.ShowValue
+				i++
+			}
+
+		} else if isColumn { // 选择列的数据
+			if isAll {
+				// 选择一整列的话,结束行得根据实际情况调整(其实也就是整个sheet有多少行)
+				endNum = allRows - 1
+			}
+
+			startColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+			if tmpErr != nil {
+				errMsg = "列名异常:" + startColumnName
+				err = errors.New(errMsg)
+				return
+			}
+			startColumn = startColumn - 1
+
+			// 最大行数,如果设置的超过了最大行数,那么结束行就是最大行数
+			maxRow := allRows
+			if endNum > maxRow {
+				endNum = maxRow - 1
+			}
+			// 长度固定,避免一直申请内存空间
+			dateList = make([]string, endNum-startNum+1)
+			i := 0
+			for currRow := startNum; currRow <= endNum; currRow++ {
+				currCell, ok := newMixedTableCellDataListMap[currRow][startColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currRow, startColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dateList = append(dateList, currCell.Value)
+				dateList[i] = currCell.ShowValue
+				i++
+			}
+		}
+
+	}
+
+	// 数据序列
+	{
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DataSequence)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		startNum = startNum - 1
+		endNum = endNum - 1
+		// 选择行的数据
+		if isRow {
+			// 开始列名、结束列
+			var startColumn, endColumn int
+			if isAll {
+				// 结束列(其实也就是整列的个数)
+				endColumn = allCols - 1
+			} else {
+
+				tmpStartColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + startColumnName
+					err = errors.New(errMsg)
+					return
+				}
+
+				tmpEndColumn, tmpErr := excelize.ColumnNameToNumber(endColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + endColumnName
+					err = errors.New(errMsg)
+					return
+				}
+				startColumn = tmpStartColumn - 1
+				endColumn = tmpEndColumn - 1
+			}
+
+			// 最大列数,如果设置的超过了最大列数,那么结束列就是最大列数
+			maxCol := allCols
+			if endColumn > maxCol {
+				endColumn = maxCol - 1
+			}
+			// 长度固定,避免一直申请内存空间
+			dataList = make([]string, endColumn-startColumn+1)
+			i := 0
+			for currColumn := startColumn; currColumn <= endColumn; currColumn++ {
+				currCell, ok := newMixedTableCellDataListMap[startNum][currColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", startColumn, currColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dataList = append(dataList, currCell.Value)
+				dataList[i] = currCell.ShowValue
+				i++
+			}
+
+		} else if isColumn { // 选择列的数据
+			if isAll {
+				// 选择一整列的话,结束行得根据实际情况调整(其实也就是整个sheet有多少行)
+				endNum = allRows - 1
+			}
+
+			startColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+			if tmpErr != nil {
+				errMsg = "列名异常:" + startColumnName
+				err = errors.New(errMsg)
+				return
+			}
+			startColumn = startColumn - 1
+
+			// 最大行数,如果设置的超过了最大行数,那么结束行就是最大行数
+			maxRow := allRows
+			if endNum > maxRow {
+				endNum = maxRow - 1
+			}
+
+			// 长度固定,避免一直申请内存空间
+			dataList = make([]string, endNum-startNum+1)
+			i := 0
+			for currRow := startNum; currRow <= endNum; currRow++ {
+				currCell, ok := newMixedTableCellDataListMap[currRow][startColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currRow, startColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dataList = append(dataList, currCell.Value)
+				dataList[i] = currCell.ShowValue
+				i++
+			}
+		}
+	}
+
+	//fmt.Println(dateList, dataList)
+	//fmt.Println("日期序列结束")
+
+	// 将excel中的日期、数据系列处理
+	//newDateList, newDataList, err, errMsg := excel2.HandleEdbSequenceVal(dateList, dataList)
+	newDateList, newDataList := dateList, dataList
+
+	newDataMap := make(map[int]float64, len(newDataList))
+	for i, v := range newDataList {
+		val, e := strconv.ParseFloat(v, 64)
+		if e != nil {
+			err = fmt.Errorf(" 处理日期和数据系列失败 %s", e.Error())
+			return
+		}
+		newDataMap[i] = val
+	}
+	//组装成excelEdbData
+	list := make([]*models.EdbDataList, 0)
+
+	for i, v := range newDateList {
+		// todo 处理DataTimestamp
+		dataTime, e := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if e != nil {
+			err = errors.New("time.Parse Err:" + err.Error())
+			return
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		tmp := &models.EdbDataList{
+			EdbDataId:     i,
+			EdbInfoId:     excelEdbMappingItem.ExcelChartEdbId,
+			DataTime:      v,
+			DataTimestamp: timestamp,
+			Value:         newDataMap[i],
+		}
+		list = append(list, tmp)
+	}
+	dataListMap[excelEdbMappingItem.ExcelChartEdbId] = list
+	return
+}
+
+// GetSheetStr
+// @return sheetName string 用户选择的sheet名称
+// @return startColumnName string 用户选择的开始列名称
+// @return endColumnName string 用户选择的结束列名称
+// @return startNum int 用户选择的开始列单元格位置
+// @return endNum int 用户选择的结束列单元格位置
+// @return isAll bool 是否选择整行/列数据
+// @return isRow bool 是否选择行数据
+// @return isColumn bool 是否选择列数据
+func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName string, startNum, endNum int, isAll, isRow, isColumn bool, err error) {
+	// 找出sheetName
+	tmpList := strings.Split(sequenceStr, "!")
+	if len(tmpList) != 2 {
+		err = errors.New("错误的公式,查找sheet异常:" + sequenceStr)
+		return
+	}
+
+	sheetName = tmpList[0]
+
+	// 分离开始/结束单元格
+	tmpList = strings.Split(tmpList[1], ":")
+	if len(tmpList) != 2 {
+		err = errors.New("错误的公式,查找开始/结束单元格异常:" + sequenceStr)
+		return
+	}
+
+	startList := strings.Split(tmpList[0], "$")
+	endList := strings.Split(tmpList[1], "$")
+
+	lenList := len(startList)
+	if lenList != len(endList) {
+		err = errors.New("错误的公式,开始与结束单元格异常:" + sequenceStr)
+		return
+	}
+
+	if lenList != 3 && lenList != 2 {
+		err = errors.New("错误的公式:" + sequenceStr)
+		return
+	}
+
+	startColumnName = startList[1]
+	endColumnName = endList[1]
+
+	// 长度为2的话,那说明是整行或整列
+	if lenList == 2 {
+		isAll = true
+
+		startDeci, tmpErr1 := decimal.NewFromString(startList[1])
+		endDeci, tmpErr2 := decimal.NewFromString(endList[1])
+
+		if tmpErr1 == nil && tmpErr2 == nil {
+			isRow = true // 正常转换的话,那么就是整行
+			startNum = int(startDeci.IntPart())
+			endNum = int(endDeci.IntPart())
+			startColumnName = ``
+			endColumnName = ``
+
+			return
+		}
+
+		if tmpErr1 == nil || tmpErr2 == nil {
+			err = errors.New("错误的公式2:" + sequenceStr)
+			return
+		}
+
+		// 如果不能转成数字,那么就是整列
+		isColumn = true
+
+		return
+	}
+
+	// 确定行
+	startDeci, tmpErr1 := decimal.NewFromString(startList[2])
+	endDeci, tmpErr2 := decimal.NewFromString(endList[2])
+	if tmpErr1 != nil && tmpErr1 != tmpErr2 {
+		err = errors.New("错误的公式3:" + sequenceStr)
+		return
+	}
+
+	startNum = int(startDeci.IntPart())
+	endNum = int(endDeci.IntPart())
+
+	if startColumnName != endColumnName && startNum != endNum {
+		err = errors.New("选区不允许跨行或者跨列")
+	}
+
+	if startColumnName == endColumnName {
+		isColumn = true // 列数据
+	} else {
+		isRow = true // 行数据
+	}
+
+	return
+}

+ 1 - 0
utils/constants.go

@@ -138,6 +138,7 @@ const (
 	CHART_SOURCE_LINE_FEATURE_PERCENTILE         = 8  // 统计特征-百分位图表
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9  // 统计特征-频率分布图表
 	CHART_SOURCE_CROSS_HEDGING                   = 10 // 跨品种分析图表
+	CHART_SOURCE_BALANCE_EXCEL                   = 11 // 平衡表图表
 )
 
 // ETA表格