chart.go 12 KB

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