chart.go 12 KB

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