chart.go 12 KB

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