chart.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. package cross_variety
  2. import (
  3. "errors"
  4. "eta/eta_chart_lib/models"
  5. cross_varietyModel "eta/eta_chart_lib/models/data_manage/cross_variety"
  6. "eta/eta_chart_lib/models/data_manage/cross_variety/request"
  7. "eta/eta_chart_lib/services/data"
  8. "eta/eta_chart_lib/utils"
  9. "fmt"
  10. "github.com/shopspring/decimal"
  11. "time"
  12. )
  13. // ChartInfoResp 截面散点图数据
  14. type ChartInfoResp struct {
  15. XName string `description:"x轴名称"`
  16. XNameEn string `description:"x轴名称(英文)"`
  17. XUnitName string `description:"x轴单位名称"`
  18. XUnitNameEn string `description:"x轴单位名称(英文)"`
  19. YName string `description:"y轴名称"`
  20. YNameEn string `description:"y轴名称(英文)"`
  21. YUnitName string `description:"y轴单位名称"`
  22. YUnitNameEn string `description:"y轴单位名称(英文)"`
  23. XMinValue string `description:"X轴的最小值"`
  24. XMaxValue string `description:"X轴的最大值"`
  25. YMinValue string `description:"Y轴的最小值"`
  26. YMaxValue string `description:"Y轴的最大值"`
  27. DataList []SectionScatterSeriesItemResp `description:"数据列"`
  28. }
  29. // SectionScatterSeriesItemResp 系列的返回
  30. type SectionScatterSeriesItemResp struct {
  31. Name string `description:"系列名"`
  32. NameEn string `description:"系列名(英文)"`
  33. Color string `description:"颜色"`
  34. CoordinatePointData []CoordinatePoint `description:"趋势线的前后坐标点"`
  35. }
  36. // CoordinatePoint 坐标点
  37. type CoordinatePoint struct {
  38. X float64
  39. Y float64
  40. XEdbInfoId int
  41. YEdbInfoId int
  42. XDate string
  43. YDate string
  44. }
  45. // GetChartData
  46. // @Description: 获取跨品种分析图表数据
  47. // @author: Roc
  48. // @datetime 2023-11-24 09:42:59
  49. // @param chartInfoId int
  50. // @param config request.ChartConfigReq
  51. // @return edbList []*data_manage.ChartEdbInfoMapping
  52. // @return dataResp ChartInfoResp
  53. // @return err error
  54. // @return errMsg string
  55. // @return isSendEmail bool
  56. func GetChartData(chartInfoId int, config request.ChartConfigReq) (edbList []*models.ChartEdbInfoMapping, dataResp ChartInfoResp, err error, errMsg string, isSendEmail bool) {
  57. moveUnitDays, ok := utils.FrequencyDaysMap[config.CalculateUnit]
  58. if !ok {
  59. errMsg = "错误的分析周期"
  60. err = errors.New(errMsg)
  61. isSendEmail = false
  62. return
  63. }
  64. isSendEmail = true
  65. // 品种map
  66. varietyMap := make(map[int]*cross_varietyModel.ChartVariety)
  67. {
  68. varietyList, tmpErr := cross_varietyModel.GetVarietyListByIdList(config.VarietyList)
  69. if tmpErr != nil {
  70. err = tmpErr
  71. return
  72. }
  73. for _, v := range varietyList {
  74. varietyMap[v.ChartVarietyId] = v
  75. }
  76. }
  77. // 标签m
  78. var xTagInfo, yTagInfo *cross_varietyModel.ChartTag
  79. {
  80. tagList, tmpErr := cross_varietyModel.GetTagListByIdList([]int{config.TagX, config.TagY})
  81. if tmpErr != nil {
  82. err = tmpErr
  83. return
  84. }
  85. for _, v := range tagList {
  86. if v.ChartTagId == config.TagX {
  87. xTagInfo = v
  88. } else if v.ChartTagId == config.TagY {
  89. yTagInfo = v
  90. }
  91. }
  92. }
  93. if xTagInfo == nil {
  94. errMsg = "找不到对应的X轴标签"
  95. err = errors.New(errMsg)
  96. return
  97. }
  98. if yTagInfo == nil {
  99. errMsg = "找不到对应的Y轴标签"
  100. err = errors.New(errMsg)
  101. return
  102. }
  103. xVarietyEdbMap, yVarietyEdbMap, edbInfoIdList, err := GetXYEdbIdList(config.TagX, config.TagY, config.VarietyList)
  104. if err != nil {
  105. return
  106. }
  107. if len(edbInfoIdList) <= 0 {
  108. errMsg = "品种未配置指标"
  109. err = errors.New(errMsg)
  110. isSendEmail = false
  111. return
  112. }
  113. mappingList, err := models.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
  114. if err != nil {
  115. errMsg = "获取指标信息失败"
  116. err = errors.New("获取指标信息失败,ERR:" + err.Error())
  117. return
  118. }
  119. // 指标对应的所有数据
  120. chartType := 1 //1:普通图,2:季节性图
  121. calendar := "公历"
  122. edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, "", "", mappingList, "")
  123. if err != nil {
  124. return
  125. }
  126. currDay := time.Now()
  127. currDay = time.Date(currDay.Year(), currDay.Month(), currDay.Day(), 0, 0, 0, 0, time.Local)
  128. dataMap := make(map[string]float64)
  129. dateMap := make(map[string]string)
  130. for dateIndex, dateConfig := range config.DateConfigList {
  131. for _, edbInfoMapping := range mappingList {
  132. // 数据会是正序的
  133. dataList, ok := edbDataListMap[edbInfoMapping.EdbInfoId]
  134. if !ok {
  135. continue
  136. }
  137. lenData := len(dataList)
  138. if lenData <= 0 {
  139. continue
  140. }
  141. // 数据的开始索引
  142. k := lenData - 1
  143. // 数据的最晚日期
  144. dataEndDateStr := dataList[k].DataTime
  145. dataEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, dataEndDateStr, time.Local)
  146. if tmpErr != nil {
  147. err = tmpErr
  148. return
  149. }
  150. // 数据开始日期
  151. endDateStr := ``
  152. var endDate time.Time
  153. var currVal float64
  154. switch dateConfig.DateType {
  155. case 1: // 1:最新日期;
  156. endDateStr = dataEndDateStr
  157. endDate = dataEndDate
  158. currVal = dataList[k].Value
  159. case 2: // 2:N天前
  160. tmpEndDate := currDay.AddDate(0, 0, -dateConfig.Num)
  161. tmpEndDateStr := tmpEndDate.Format(utils.FormatDate)
  162. for i := k; i >= 0; i-- {
  163. tmpDateStr := dataList[i].DataTime
  164. // 如果正好是这一天,那么就直接break了
  165. if tmpEndDateStr == tmpDateStr {
  166. k = i
  167. endDateStr = tmpDateStr
  168. endDate = tmpEndDate
  169. currVal = dataList[i].Value
  170. break
  171. }
  172. tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
  173. if tmpErr != nil {
  174. err = tmpErr
  175. return
  176. }
  177. // 如果这期的日期晚于选择的日期,那么继续遍历
  178. if tmpDate.After(tmpEndDate) {
  179. continue
  180. }
  181. k = i
  182. endDateStr = tmpDateStr
  183. endDate = tmpDate
  184. currVal = dataList[i].Value
  185. break
  186. }
  187. }
  188. // 没有找到日期,那么就不处理
  189. if endDateStr == `` || endDate.IsZero() {
  190. continue
  191. }
  192. // 最早的日期
  193. earliestDate := endDate.AddDate(0, 0, -config.CalculateValue*moveUnitDays)
  194. earliestDateStr := earliestDate.Format(utils.FormatDate)
  195. var minVal, maxVal float64
  196. var isNotFirst bool // 是否是第一条数据
  197. for i := k; i >= 0; i-- {
  198. tmpData := dataList[i]
  199. if !isNotFirst {
  200. maxVal = tmpData.Value
  201. minVal = tmpData.Value
  202. isNotFirst = true
  203. continue
  204. }
  205. tmpDateStr := dataList[i].DataTime
  206. // 如果正好是这一天,那么就直接break了
  207. if earliestDateStr == tmpDateStr {
  208. break
  209. }
  210. tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
  211. if tmpErr != nil {
  212. err = tmpErr
  213. return
  214. }
  215. // 如果这期的日期早于选择的日期,那么继续停止遍历
  216. if tmpDate.Before(earliestDate) {
  217. continue
  218. }
  219. if tmpData.Value > maxVal {
  220. maxVal = tmpData.Value
  221. }
  222. if tmpData.Value < minVal {
  223. minVal = tmpData.Value
  224. }
  225. }
  226. // 最大值等于最小值,说明计算结果无效
  227. if maxVal == minVal {
  228. continue
  229. }
  230. //百分位=(现值-Min)/(Max-Min)
  231. tmpV := (currVal - minVal) / (maxVal - minVal) * 100
  232. tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
  233. // key的生成(日期配置下标+指标id)
  234. key := fmt.Sprint(dateIndex, "_", edbInfoMapping.EdbInfoId)
  235. dataMap[key] = tmpV
  236. dateMap[key] = endDateStr
  237. }
  238. }
  239. // 返回数据处理
  240. dataList := make([]SectionScatterSeriesItemResp, 0)
  241. var xMinVal, xMaxVal, yMinVal, yMaxVal float64
  242. var isNotFirst bool
  243. for _, varietyId := range config.VarietyList {
  244. xEdbInfoId, ok1 := xVarietyEdbMap[varietyId]
  245. if !ok1 {
  246. continue
  247. }
  248. yEdbInfoId, ok2 := yVarietyEdbMap[varietyId]
  249. if !ok2 {
  250. continue
  251. }
  252. variety, ok := varietyMap[varietyId]
  253. if !ok {
  254. continue
  255. }
  256. coordinatePointList := make([]CoordinatePoint, 0)
  257. for dateIndex := range config.DateConfigList {
  258. key1 := fmt.Sprint(dateIndex, "_", xEdbInfoId)
  259. xVal, ok1 := dataMap[key1]
  260. if !ok1 {
  261. continue
  262. }
  263. key2 := fmt.Sprint(dateIndex, "_", yEdbInfoId)
  264. yVal, ok2 := dataMap[key2]
  265. if !ok2 {
  266. continue
  267. }
  268. if !isNotFirst {
  269. xMinVal = xVal
  270. xMaxVal = xVal
  271. yMinVal = yVal
  272. yMaxVal = yVal
  273. isNotFirst = true
  274. } else {
  275. if xVal < xMinVal {
  276. xMinVal = xVal
  277. }
  278. if xVal > xMaxVal {
  279. xMaxVal = xVal
  280. }
  281. if yVal < yMinVal {
  282. yMinVal = yVal
  283. }
  284. if yVal > yMaxVal {
  285. yMaxVal = yVal
  286. }
  287. }
  288. coordinatePointList = append(coordinatePointList, CoordinatePoint{
  289. X: xVal,
  290. Y: yVal,
  291. XEdbInfoId: xEdbInfoId,
  292. YEdbInfoId: yEdbInfoId,
  293. XDate: dateMap[key1],
  294. YDate: dateMap[key2],
  295. })
  296. }
  297. dataList = append(dataList, SectionScatterSeriesItemResp{
  298. Name: variety.ChartVarietyName,
  299. NameEn: variety.ChartVarietyNameEn,
  300. Color: "",
  301. CoordinatePointData: coordinatePointList,
  302. })
  303. }
  304. dataResp = ChartInfoResp{
  305. XName: xTagInfo.ChartTagName + "百分位",
  306. XNameEn: xTagInfo.ChartTagNameEn,
  307. XUnitName: "%",
  308. XUnitNameEn: "%",
  309. YName: yTagInfo.ChartTagName + "百分位",
  310. YNameEn: yTagInfo.ChartTagNameEn,
  311. YUnitName: "%",
  312. YUnitNameEn: "%",
  313. XMinValue: fmt.Sprint(xMinVal),
  314. XMaxValue: fmt.Sprint(xMaxVal),
  315. YMinValue: fmt.Sprint(yMinVal),
  316. YMaxValue: fmt.Sprint(yMaxVal),
  317. DataList: dataList,
  318. }
  319. // 去除返回指标中的数据信息,避免没必要的数据传输
  320. for k := range edbList {
  321. edbList[k].DataList = nil
  322. }
  323. return
  324. }
  325. // GetXYEdbIdList
  326. // @Description: 根据标签id和品种获取指标列表信息
  327. // @author: Roc
  328. // @datetime 2023-11-27 14:31:23
  329. // @param tagX int
  330. // @param tagY int
  331. // @param varietyList []int
  332. // @return xVarietyEdbMap map[int]int
  333. // @return yVarietyEdbMap map[int]int
  334. // @return edbInfoIdList []int
  335. // @return errMsg string
  336. // @return err error
  337. func GetXYEdbIdList(tagX, tagY int, varietyList []int) (xVarietyEdbMap, yVarietyEdbMap map[int]int, edbInfoIdList []int, err error) {
  338. edbInfoIdList = make([]int, 0)
  339. xVarietyEdbMap = make(map[int]int)
  340. yVarietyEdbMap = make(map[int]int)
  341. xList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagX, varietyList)
  342. if err != nil {
  343. err = errors.New("获取X轴的品种指标配置信息失败,Err:" + err.Error())
  344. return
  345. }
  346. yList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagY, varietyList)
  347. if err != nil {
  348. err = errors.New("获取Y轴的品种指标配置信息失败,Err:" + err.Error())
  349. return
  350. }
  351. baseVarietyIdMap := make(map[int]int)
  352. for _, v := range xList {
  353. baseVarietyIdMap[v.ChartVarietyId] = v.ChartVarietyId
  354. }
  355. // 两个标签里面的品种并集
  356. needVarietyIdMap := make(map[int]int)
  357. for _, v := range yList {
  358. if val, ok := baseVarietyIdMap[v.ChartVarietyId]; ok {
  359. // 如果在 map2 中存在相同的键,则将键和值添加到结果中
  360. needVarietyIdMap[v.ChartVarietyId] = val
  361. }
  362. }
  363. for _, v := range xList {
  364. if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
  365. xVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
  366. edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
  367. }
  368. }
  369. for _, v := range yList {
  370. if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
  371. yVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
  372. edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
  373. }
  374. }
  375. return
  376. }