|
@@ -0,0 +1,426 @@
|
|
|
+package cross_variety
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "eta/eta_chart_lib/models"
|
|
|
+ cross_varietyModel "eta/eta_chart_lib/models/data_manage/cross_variety"
|
|
|
+ "eta/eta_chart_lib/models/data_manage/cross_variety/request"
|
|
|
+ "eta/eta_chart_lib/services/data"
|
|
|
+ "eta/eta_chart_lib/utils"
|
|
|
+ "fmt"
|
|
|
+ "github.com/shopspring/decimal"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+// ChartInfoResp 截面散点图数据
|
|
|
+type ChartInfoResp struct {
|
|
|
+ XName string `description:"x轴名称"`
|
|
|
+ XNameEn string `description:"x轴名称(英文)"`
|
|
|
+ XUnitName string `description:"x轴单位名称"`
|
|
|
+ XUnitNameEn string `description:"x轴单位名称(英文)"`
|
|
|
+ YName string `description:"y轴名称"`
|
|
|
+ YNameEn string `description:"y轴名称(英文)"`
|
|
|
+ YUnitName string `description:"y轴单位名称"`
|
|
|
+ YUnitNameEn string `description:"y轴单位名称(英文)"`
|
|
|
+ XMinValue string `description:"X轴的最小值"`
|
|
|
+ XMaxValue string `description:"X轴的最大值"`
|
|
|
+ YMinValue string `description:"Y轴的最小值"`
|
|
|
+ YMaxValue string `description:"Y轴的最大值"`
|
|
|
+ DataList []SectionScatterSeriesItemResp `description:"数据列"`
|
|
|
+}
|
|
|
+
|
|
|
+// SectionScatterSeriesItemResp 系列的返回
|
|
|
+type SectionScatterSeriesItemResp struct {
|
|
|
+ Name string `description:"系列名"`
|
|
|
+ NameEn string `description:"系列名(英文)"`
|
|
|
+ Color string `description:"颜色"`
|
|
|
+ CoordinatePointData []CoordinatePoint `description:"趋势线的前后坐标点"`
|
|
|
+}
|
|
|
+
|
|
|
+// SectionScatterEdbItemResp 截面散点的返回参数
|
|
|
+type SectionScatterEdbItemResp struct {
|
|
|
+ XEdbInfoId int `description:"X轴指标id"`
|
|
|
+ XDate string `description:"X轴指标实际日期"`
|
|
|
+ XName string `description:"X轴指标名称"`
|
|
|
+ XNameEn string `description:"X轴指标英文名称"`
|
|
|
+ XValue float64 `description:"X轴实际值"`
|
|
|
+ YEdbInfoId int `description:"Y轴指标id"`
|
|
|
+ YDate string `description:"Y轴指标实际日期"`
|
|
|
+ YName string `description:"Y轴指标名称"`
|
|
|
+ YNameEn string `description:"Y轴指标英文名称"`
|
|
|
+ YValue float64 `description:"Y轴实际值"`
|
|
|
+ IsShow bool `description:"是否展示"`
|
|
|
+ Name string `description:"标签名称"`
|
|
|
+ NameEn string `description:"英文标签名称"`
|
|
|
+}
|
|
|
+
|
|
|
+// CoordinatePoint 坐标点
|
|
|
+type CoordinatePoint struct {
|
|
|
+ X float64
|
|
|
+ Y float64
|
|
|
+ XEdbInfoId int
|
|
|
+ YEdbInfoId int
|
|
|
+ XDate string
|
|
|
+ YDate string
|
|
|
+}
|
|
|
+
|
|
|
+// GetChartData
|
|
|
+// @Description: 获取跨品种分析图表数据
|
|
|
+// @author: Roc
|
|
|
+// @datetime 2023-11-24 09:42:59
|
|
|
+// @param chartInfoId int
|
|
|
+// @param config request.ChartConfigReq
|
|
|
+// @return edbList []*data_manage.ChartEdbInfoMapping
|
|
|
+// @return dataResp ChartInfoResp
|
|
|
+// @return err error
|
|
|
+// @return errMsg string
|
|
|
+// @return isSendEmail bool
|
|
|
+func GetChartData(chartInfoId int, config request.ChartConfigReq) (edbList []*models.ChartEdbInfoMapping, dataResp ChartInfoResp, err error, errMsg string, isSendEmail bool) {
|
|
|
+ moveUnitDays, ok := utils.FrequencyDaysMap[config.CalculateUnit]
|
|
|
+ if !ok {
|
|
|
+ errMsg = "错误的分析周期"
|
|
|
+ err = errors.New(errMsg)
|
|
|
+ isSendEmail = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ isSendEmail = true
|
|
|
+
|
|
|
+ // 品种map
|
|
|
+ varietyMap := make(map[int]*cross_varietyModel.ChartVariety)
|
|
|
+ {
|
|
|
+ varietyList, tmpErr := cross_varietyModel.GetVarietyListByIdList(config.VarietyList)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, v := range varietyList {
|
|
|
+ varietyMap[v.ChartVarietyId] = v
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标签m
|
|
|
+ var xTagInfo, yTagInfo *cross_varietyModel.ChartTag
|
|
|
+ {
|
|
|
+ tagList, tmpErr := cross_varietyModel.GetTagListByIdList([]int{config.TagX, config.TagY})
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, v := range tagList {
|
|
|
+ if v.ChartTagId == config.TagX {
|
|
|
+ xTagInfo = v
|
|
|
+ } else if v.ChartTagId == config.TagY {
|
|
|
+ yTagInfo = v
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if xTagInfo == nil {
|
|
|
+ errMsg = "找不到对应的X轴标签"
|
|
|
+ err = errors.New(errMsg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if yTagInfo == nil {
|
|
|
+ errMsg = "找不到对应的Y轴标签"
|
|
|
+ err = errors.New(errMsg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ xVarietyEdbMap, yVarietyEdbMap, edbInfoIdList, err := GetXYEdbIdList(config.TagX, config.TagY, config.VarietyList)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(edbInfoIdList) <= 0 {
|
|
|
+ errMsg = "品种未配置指标"
|
|
|
+ err = errors.New(errMsg)
|
|
|
+ isSendEmail = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mappingList, err := models.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
|
|
|
+ if err != nil {
|
|
|
+ errMsg = "获取指标信息失败"
|
|
|
+ err = errors.New("获取指标信息失败,ERR:" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 指标对应的所有数据
|
|
|
+ chartType := 1 //1:普通图,2:季节性图
|
|
|
+ calendar := "公历"
|
|
|
+ edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, "", "", mappingList, "")
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ currDay := time.Now()
|
|
|
+ currDay = time.Date(currDay.Year(), currDay.Month(), currDay.Day(), 0, 0, 0, 0, time.Local)
|
|
|
+
|
|
|
+ dataMap := make(map[string]float64)
|
|
|
+ dateMap := make(map[string]string)
|
|
|
+ for dateIndex, dateConfig := range config.DateConfigList {
|
|
|
+ for _, edbInfoMapping := range mappingList {
|
|
|
+ // 数据会是正序的
|
|
|
+ dataList, ok := edbDataListMap[edbInfoMapping.EdbInfoId]
|
|
|
+ if !ok {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ lenData := len(dataList)
|
|
|
+ if lenData <= 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据开始日期
|
|
|
+ endDateStr := ``
|
|
|
+ var endDate time.Time
|
|
|
+ // 数据的开始索引
|
|
|
+ k := lenData - 1
|
|
|
+ var currVal float64
|
|
|
+ switch dateConfig.DateType {
|
|
|
+ case 1: // 1:最新日期;
|
|
|
+ endDateStr = dataList[k].DataTime
|
|
|
+ tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ endDate = tmpDate
|
|
|
+ currVal = dataList[k].Value
|
|
|
+ case 2: // 2:N天前
|
|
|
+ tmpEndDate := currDay.AddDate(0, 0, -dateConfig.Num)
|
|
|
+ tmpEndDateStr := tmpEndDate.Format(utils.FormatDate)
|
|
|
+
|
|
|
+ for i := k; i >= 0; i-- {
|
|
|
+ tmpDateStr := dataList[i].DataTime
|
|
|
+ // 如果正好是这一天,那么就直接break了
|
|
|
+ if tmpEndDateStr == tmpDateStr {
|
|
|
+ k = i
|
|
|
+ endDateStr = tmpDateStr
|
|
|
+ currVal = dataList[i].Value
|
|
|
+ break
|
|
|
+ }
|
|
|
+ tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 如果这期的日期晚于选择的日期,那么继续遍历
|
|
|
+ if tmpDate.After(tmpEndDate) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ k = i
|
|
|
+ endDateStr = tmpDateStr
|
|
|
+ endDate = tmpDate
|
|
|
+ currVal = dataList[i].Value
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有找到日期,那么就不处理
|
|
|
+ if endDateStr == `` || endDate.IsZero() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 最早的日期
|
|
|
+ earliestDate := endDate.AddDate(0, 0, -config.CalculateValue*moveUnitDays)
|
|
|
+ earliestDateStr := earliestDate.Format(utils.FormatDate)
|
|
|
+
|
|
|
+ var minVal, maxVal float64
|
|
|
+ var isNotFirst bool // 是否是第一条数据
|
|
|
+ for i := k; i >= 0; i-- {
|
|
|
+ tmpData := dataList[i]
|
|
|
+ if !isNotFirst {
|
|
|
+ maxVal = tmpData.Value
|
|
|
+ minVal = tmpData.Value
|
|
|
+ isNotFirst = true
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ tmpDateStr := dataList[i].DataTime
|
|
|
+ // 如果正好是这一天,那么就直接break了
|
|
|
+ if earliestDateStr == tmpDateStr {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 如果这期的日期早于选择的日期,那么继续停止遍历
|
|
|
+ if tmpDate.Before(earliestDate) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if tmpData.Value > maxVal {
|
|
|
+ maxVal = tmpData.Value
|
|
|
+ }
|
|
|
+ if tmpData.Value < minVal {
|
|
|
+ minVal = tmpData.Value
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 最大值等于最小值,说明计算结果无效
|
|
|
+ if maxVal == minVal {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ //百分位=(现值-Min)/(Max-Min)
|
|
|
+ tmpV := (currVal - minVal) / (maxVal - minVal) * 100
|
|
|
+ tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
|
|
|
+
|
|
|
+ // key的生成(日期配置下标+指标id)
|
|
|
+ key := fmt.Sprint(dateIndex, "_", edbInfoMapping.EdbInfoId)
|
|
|
+ dataMap[key] = tmpV
|
|
|
+ dateMap[key] = endDateStr
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回数据处理
|
|
|
+ dataList := make([]SectionScatterSeriesItemResp, 0)
|
|
|
+ var xMinVal, xMaxVal, yMinVal, yMaxVal float64
|
|
|
+ var isNotFirst bool
|
|
|
+
|
|
|
+ for _, varietyId := range config.VarietyList {
|
|
|
+ xEdbInfoId, ok1 := xVarietyEdbMap[varietyId]
|
|
|
+ if !ok1 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ yEdbInfoId, ok2 := yVarietyEdbMap[varietyId]
|
|
|
+ if !ok2 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ variety, ok := varietyMap[varietyId]
|
|
|
+ if !ok {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ coordinatePointList := make([]CoordinatePoint, 0)
|
|
|
+
|
|
|
+ for dateIndex := range config.DateConfigList {
|
|
|
+ key1 := fmt.Sprint(dateIndex, "_", xEdbInfoId)
|
|
|
+ xVal, ok1 := dataMap[key1]
|
|
|
+ if !ok1 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ key2 := fmt.Sprint(dateIndex, "_", yEdbInfoId)
|
|
|
+ yVal, ok2 := dataMap[key2]
|
|
|
+ if !ok2 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if !isNotFirst {
|
|
|
+ xMinVal = xVal
|
|
|
+ xMaxVal = xVal
|
|
|
+ yMinVal = yVal
|
|
|
+ yMaxVal = yVal
|
|
|
+ isNotFirst = true
|
|
|
+ } else {
|
|
|
+ if xVal < xMinVal {
|
|
|
+ xMinVal = xVal
|
|
|
+ }
|
|
|
+ if xVal > xMaxVal {
|
|
|
+ xMaxVal = xVal
|
|
|
+ }
|
|
|
+ if yVal < yMinVal {
|
|
|
+ yMinVal = yVal
|
|
|
+ }
|
|
|
+ if yVal > yMaxVal {
|
|
|
+ yMaxVal = yVal
|
|
|
+ }
|
|
|
+ }
|
|
|
+ coordinatePointList = append(coordinatePointList, CoordinatePoint{
|
|
|
+ X: xVal,
|
|
|
+ Y: yVal,
|
|
|
+ XEdbInfoId: xEdbInfoId,
|
|
|
+ YEdbInfoId: yEdbInfoId,
|
|
|
+ XDate: dateMap[key1],
|
|
|
+ YDate: dateMap[key2],
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ dataList = append(dataList, SectionScatterSeriesItemResp{
|
|
|
+ Name: variety.ChartVarietyName,
|
|
|
+ NameEn: "",
|
|
|
+ Color: "",
|
|
|
+ CoordinatePointData: coordinatePointList,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ dataResp = ChartInfoResp{
|
|
|
+ XName: xTagInfo.ChartTagName,
|
|
|
+ XNameEn: "",
|
|
|
+ XUnitName: "%",
|
|
|
+ XUnitNameEn: "%",
|
|
|
+ YName: yTagInfo.ChartTagName,
|
|
|
+ YNameEn: "",
|
|
|
+ YUnitName: "%",
|
|
|
+ YUnitNameEn: "%",
|
|
|
+ XMinValue: fmt.Sprint(xMinVal),
|
|
|
+ XMaxValue: fmt.Sprint(xMaxVal),
|
|
|
+ YMinValue: fmt.Sprint(yMinVal),
|
|
|
+ YMaxValue: fmt.Sprint(yMaxVal),
|
|
|
+ DataList: dataList,
|
|
|
+ }
|
|
|
+
|
|
|
+ // 去除返回指标中的数据信息,避免没必要的数据传输
|
|
|
+ for k := range edbList {
|
|
|
+ edbList[k].DataList = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// GetXYEdbIdList
|
|
|
+// @Description: 根据标签id和品种获取指标列表信息
|
|
|
+// @author: Roc
|
|
|
+// @datetime 2023-11-27 14:31:23
|
|
|
+// @param tagX int
|
|
|
+// @param tagY int
|
|
|
+// @param varietyList []int
|
|
|
+// @return xVarietyEdbMap map[int]int
|
|
|
+// @return yVarietyEdbMap map[int]int
|
|
|
+// @return edbInfoIdList []int
|
|
|
+// @return errMsg string
|
|
|
+// @return err error
|
|
|
+func GetXYEdbIdList(tagX, tagY int, varietyList []int) (xVarietyEdbMap, yVarietyEdbMap map[int]int, edbInfoIdList []int, err error) {
|
|
|
+ edbInfoIdList = make([]int, 0)
|
|
|
+ xVarietyEdbMap = make(map[int]int)
|
|
|
+ yVarietyEdbMap = make(map[int]int)
|
|
|
+ xList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagX, varietyList)
|
|
|
+ if err != nil {
|
|
|
+ err = errors.New("获取X轴的品种指标配置信息失败,Err:" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ yList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagY, varietyList)
|
|
|
+ if err != nil {
|
|
|
+ err = errors.New("获取Y轴的品种指标配置信息失败,Err:" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ baseVarietyIdMap := make(map[int]int)
|
|
|
+ for _, v := range xList {
|
|
|
+ baseVarietyIdMap[v.ChartVarietyId] = v.ChartVarietyId
|
|
|
+ }
|
|
|
+
|
|
|
+ // 两个标签里面的品种并集
|
|
|
+ needVarietyIdMap := make(map[int]int)
|
|
|
+ for _, v := range yList {
|
|
|
+ if val, ok := baseVarietyIdMap[v.ChartVarietyId]; ok {
|
|
|
+ // 如果在 map2 中存在相同的键,则将键和值添加到结果中
|
|
|
+ needVarietyIdMap[v.ChartVarietyId] = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, v := range xList {
|
|
|
+ if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
|
|
|
+ xVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
|
|
|
+ edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for _, v := range yList {
|
|
|
+ if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
|
|
|
+ yVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
|
|
|
+ edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|