residual_analysis_service.go 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. package residual_analysis_service
  2. import (
  3. "encoding/json"
  4. "eta/eta_api/models/data_manage"
  5. "eta/eta_api/models/residual_analysis_model"
  6. "eta/eta_api/models/system"
  7. "eta/eta_api/utils"
  8. "fmt"
  9. "math"
  10. "sort"
  11. "strconv"
  12. "time"
  13. )
  14. func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (residual_analysis_model.ResidualAnalysisResp, error) {
  15. mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList([]int{req.EdbInfoIdA, req.EdbInfoIdB})
  16. if err != nil {
  17. return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("获取图表,指标信息失败,Err:%s", err.Error())
  18. }
  19. var edbInfoMappingA, edbInfoMappingB *data_manage.ChartEdbInfoMapping
  20. for _, v := range mappingList {
  21. if v.Unit == "无" {
  22. v.Unit = ""
  23. }
  24. if v.EdbInfoId == req.EdbInfoIdA {
  25. edbInfoMappingA = v
  26. }
  27. if v.EdbInfoId == req.EdbInfoIdB {
  28. edbInfoMappingB = v
  29. }
  30. }
  31. if edbInfoMappingA == nil {
  32. return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("指标A不存在")
  33. }
  34. if edbInfoMappingB == nil {
  35. return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("指标B不存在")
  36. }
  37. // 时间处理
  38. var startDate, endDate string
  39. switch req.DateType {
  40. case 0:
  41. startDate = req.StartDate
  42. endDate = req.EndDate
  43. case 1:
  44. startDate = req.StartDate
  45. endDate = ""
  46. default:
  47. startDate = utils.GetPreYearTime(req.DateType)
  48. endDate = ""
  49. }
  50. resp := residual_analysis_model.ResidualAnalysisResp{}
  51. // 原始图表信息
  52. originalEdbList := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, 0)
  53. originalEdbList, fullADataList, fullBDataList, err := fillOriginalChart(req, mappingList, startDate, endDate, edbInfoMappingA, edbInfoMappingB, originalEdbList)
  54. if err != nil {
  55. return residual_analysis_model.ResidualAnalysisResp{}, err
  56. }
  57. originalChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName)
  58. resp.OriginalChartData = residual_analysis_model.ChartResp{
  59. ChartInfo: originalChartInfo,
  60. EdbInfoList: originalEdbList,
  61. }
  62. // 如果只需要第一张图表的数据 直接返回,避免继续处理
  63. if req.QueryType == 1 {
  64. return resp, nil
  65. }
  66. dataAList, ok := edbInfoMappingA.DataList.([]*data_manage.EdbDataList)
  67. if !ok {
  68. return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("数据类型转换失败")
  69. }
  70. indexADataMap := map[string]*data_manage.EdbDataList{}
  71. for _, indexData := range dataAList {
  72. indexADataMap[indexData.DataTime] = indexData
  73. }
  74. // 映射图表信息
  75. mappingEdbList, a, b, r, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap, startDate, endDate, fullADataList, fullBDataList)
  76. if err != nil {
  77. return residual_analysis_model.ResidualAnalysisResp{}, err
  78. }
  79. mappingChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName+"映射"+edbInfoMappingA.EdbName)
  80. resp.MappingChartData = residual_analysis_model.ChartResp{
  81. ChartInfo: mappingChartInfo,
  82. EdbInfoList: mappingEdbList,
  83. }
  84. // 残差图表信息
  85. residualEdbList, R2, err := fillResidualChartInfo(req, edbInfoMappingA, edbInfoMappingB, mappingEdbList)
  86. if err != nil {
  87. return residual_analysis_model.ResidualAnalysisResp{}, err
  88. }
  89. residualChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingA.EdbName+"映射残差/"+edbInfoMappingB.EdbName)
  90. resp.ResidualChartData = residual_analysis_model.ChartResp{
  91. ChartInfo: residualChartInfo,
  92. EdbInfoList: residualEdbList,
  93. }
  94. if req.ResidualType == 2 {
  95. resp.A = math.Round(a*10000) / 10000
  96. resp.B = math.Round(b*10000) / 10000
  97. resp.R = math.Round(r*10000) / 10000
  98. resp.R2 = math.Round(R2*10000) / 10000
  99. }
  100. return resp, nil
  101. }
  102. func createChartInfoResp(req residual_analysis_model.ResidualAnalysisReq, startDate, endDate, chartName string) residual_analysis_model.ResidualAnalysisChartInfo {
  103. return residual_analysis_model.ResidualAnalysisChartInfo{
  104. Calendar: `公历`,
  105. Source: utils.CHART_SOURCE_DEFAULT,
  106. DateType: req.DateType,
  107. StartDate: startDate,
  108. EndDate: endDate,
  109. ChartType: utils.CHART_TYPE_CURVE,
  110. ChartName: chartName,
  111. }
  112. }
  113. func fillResidualChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, mappingEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, error) {
  114. // 计算公式 映射残差 = 因变量指标 - 映射指标
  115. var edbInfoA, edbInfoB residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
  116. if mappingEdbList[0].EdbInfoId == edbInfoMappingA.EdbInfoId {
  117. edbInfoA = mappingEdbList[0]
  118. edbInfoB = mappingEdbList[1]
  119. } else {
  120. edbInfoA = mappingEdbList[1]
  121. edbInfoB = mappingEdbList[0]
  122. }
  123. dataAList, ok := edbInfoA.DataList.([]*data_manage.EdbDataList)
  124. if !ok {
  125. return nil, 0, fmt.Errorf("数据类型转换失败")
  126. }
  127. edbData := make([]*data_manage.EdbDataList, len(dataAList))
  128. for i, data := range dataAList {
  129. edbData[i] = &data_manage.EdbDataList{
  130. Value: data.Value, // 确保为每个元素创建一个新的对象
  131. DataTimestamp: data.DataTimestamp,
  132. DataTime: data.DataTime,
  133. EdbInfoId: data.EdbInfoId,
  134. EdbDataId: data.EdbDataId,
  135. }
  136. }
  137. dataBList, ok := edbInfoB.DataList.([]*data_manage.EdbDataList)
  138. if !ok {
  139. return nil, 0, fmt.Errorf("数据类型转换失败")
  140. }
  141. // 映射指标开始时间
  142. var startTime string
  143. if len(dataBList) > 0 {
  144. startTime = dataBList[0].DataTime
  145. }
  146. var indexDataBMap = make(map[string]*data_manage.EdbDataList)
  147. for _, data := range dataBList {
  148. indexDataBMap[data.DataTime] = data
  149. }
  150. // 求R2
  151. var valueB, sumValueA, averageValueA, residualQuadraticSum, totalQuadraticSum, R2 float64
  152. for _, indexData := range edbData {
  153. // 因变量的值总和
  154. sumValueA += indexData.Value
  155. }
  156. // 因变量平均值
  157. averageValueA = sumValueA / float64(len(edbData))
  158. var indexMax, indexMin float64
  159. var edbDataResp []*data_manage.EdbDataList
  160. if len(edbData) > 0 {
  161. indexMax = edbData[0].Value
  162. indexMin = edbData[0].Value
  163. for _, indexData := range edbData {
  164. if dataB, ok := indexDataBMap[indexData.DataTime]; ok {
  165. valueB = dataB.Value
  166. } else {
  167. continue
  168. }
  169. // 总因变量平方和
  170. totalQuadraticSum += math.Pow(indexData.Value-averageValueA, 2)
  171. // 补全残差值
  172. indexData.Value = math.Round((indexData.Value-valueB)*10000) / 10000
  173. // 残差平方和
  174. residualQuadraticSum += math.Pow(indexData.Value, 2)
  175. if indexData.Value > indexMax {
  176. indexMax = indexData.Value
  177. }
  178. if indexData.Value < indexMin {
  179. indexMin = indexData.Value
  180. }
  181. // 获取映射指标之后的数据
  182. if startTime != "" && utils.CompareDate(startTime, indexData.DataTime) {
  183. edbDataResp = append(edbDataResp, indexData)
  184. }
  185. }
  186. }
  187. // 计算R2 公式:R2=1-SSE/SST R2越大,越符合线性 R2 = 1 - 残差平方和/总平方和
  188. R2 = 1 - residualQuadraticSum/totalQuadraticSum
  189. mappingEdb := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, len(mappingEdbList))
  190. copy(mappingEdb, mappingEdbList)
  191. for i, mapping := range mappingEdb {
  192. if mapping.EdbInfoId != edbInfoMappingA.EdbInfoId {
  193. mappingEdb[i].DataList = edbDataResp
  194. mappingEdb[i].EdbName = edbInfoMappingA.EdbName + "映射残差/" + edbInfoMappingB.EdbName
  195. if req.IndexType == 2 {
  196. if req.LeadValue > 0 {
  197. mappingEdb[i].EdbName = edbInfoMappingA.EdbName + "映射残差/" + edbInfoMappingB.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")"
  198. }
  199. }
  200. mappingEdb[i].IsAxis = 1
  201. mappingEdb[i].ChartColor = `#00F`
  202. mappingEdb[i].IsOrder = false
  203. mappingEdb[i].MinValue = indexMin
  204. mappingEdb[i].MaxValue = indexMax
  205. } else {
  206. mappingEdb[i].IsAxis = 0
  207. mappingEdb[i].ChartColor = `#F00`
  208. }
  209. }
  210. return mappingEdb, R2, nil
  211. }
  212. func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, indexADataMap map[string]*data_manage.EdbDataList, startDate string, endDate string, fullADataList []*data_manage.EdbDataList, fullBDataList []*data_manage.EdbDataList) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, float64, float64, error) {
  213. // 计算公式:Y=aX+b,Y为映射后的指标,X为自变量指标
  214. // 正序:a=(L2-L1)/(R2-R1) b=L2-R2*a
  215. // 逆序:a=(L2-L1)/(R1-R2) b=L2-R1*a
  216. // L2:左轴下限 R2:右轴上限 L1:左轴上限 R1:右轴下限
  217. var a, b, r float64
  218. // 映射残差 计算a,b
  219. if req.ResidualType == 1 {
  220. if req.IsOrder {
  221. a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax)
  222. b = req.LeftIndexMax - req.RightIndexMin*a
  223. } else {
  224. a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin)
  225. b = req.LeftIndexMax - req.RightIndexMax*a
  226. }
  227. }
  228. //dataAList := edbInfoMappingA.DataList.([]*data_manage.EdbDataList)
  229. dataList, ok := edbInfoMappingB.DataList.([]*data_manage.EdbDataList)
  230. if !ok {
  231. return nil, a, b, r, fmt.Errorf("数据类型转换失败")
  232. }
  233. // 指标B数据补充
  234. // 新建一个切片来保存补充的数据
  235. var replenishDataList []*data_manage.EdbDataList
  236. for index := 0; index < len(dataList)-1; index++ {
  237. // 获取当前数据和下一个数据
  238. beforeIndexData := dataList[index]
  239. afterIndexData := dataList[index+1]
  240. // 从最早时间开始,补充时间为自然日
  241. for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  242. // 创建补充数据
  243. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  244. toTime := utils.StringToTime(nextDay)
  245. replenishIndexData := data_manage.EdbDataList{
  246. DataTime: nextDay, // 计算下一个自然日
  247. DataTimestamp: toTime.UnixMilli(),
  248. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  249. }
  250. // 将补充数据加入补充数据列表
  251. replenishDataList = append(replenishDataList, &replenishIndexData)
  252. // 更新 beforeIndexData 为新创建的补充数据
  253. beforeIndexData = &replenishIndexData
  254. }
  255. }
  256. // 将补充数据插入原始数据列表
  257. dataList = append(dataList, replenishDataList...)
  258. // 排序
  259. sort.Sort(ByDataTime(dataList))
  260. // 拟合残差 计算a,b
  261. var coordinateList []utils.Coordinate
  262. var replenishADataList []*data_manage.EdbDataList
  263. var replenishBDataList []*data_manage.EdbDataList
  264. if req.ResidualType == 2 {
  265. //
  266. // 因变量指标也转换为日度
  267. for index := 0; index < len(fullADataList)-1; index++ {
  268. // 获取当前数据和下一个数据
  269. beforeIndexData := fullADataList[index]
  270. afterIndexData := fullADataList[index+1]
  271. replenishADataList = append(replenishADataList, beforeIndexData)
  272. // 从最早时间开始,补充时间为自然日
  273. if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  274. for {
  275. // 创建补充数据
  276. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  277. toTime := utils.StringToTime(nextDay)
  278. replenishIndexData := data_manage.EdbDataList{
  279. DataTime: nextDay, // 计算下一个自然日
  280. DataTimestamp: toTime.UnixMilli(),
  281. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  282. }
  283. // 将补充数据加入补充数据列表
  284. replenishADataList = append(replenishADataList, &replenishIndexData)
  285. // 更新 beforeIndexData 为新创建的补充数据
  286. beforeIndexData = &replenishIndexData
  287. // 检查是否还需要继续补充数据
  288. if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  289. break
  290. }
  291. }
  292. }
  293. }
  294. replenishADataList = append(replenishADataList, fullADataList[len(fullADataList)-1])
  295. // 自变量指标也转换为日度
  296. for index := 0; index < len(fullBDataList)-1; index++ {
  297. // 获取当前数据和下一个数据
  298. beforeIndexData := fullBDataList[index]
  299. afterIndexData := fullBDataList[index+1]
  300. replenishBDataList = append(replenishBDataList, beforeIndexData)
  301. // 从最早时间开始,补充时间为自然日
  302. if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  303. for {
  304. // 创建补充数据
  305. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  306. toTime := utils.StringToTime(nextDay)
  307. replenishIndexData := data_manage.EdbDataList{
  308. DataTime: nextDay, // 计算下一个自然日
  309. DataTimestamp: toTime.UnixMilli(),
  310. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  311. }
  312. // 将补充数据加入补充数据列表
  313. replenishBDataList = append(replenishBDataList, &replenishIndexData)
  314. // 更新 beforeIndexData 为新创建的补充数据
  315. beforeIndexData = &replenishIndexData
  316. // 检查是否还需要继续补充数据
  317. if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  318. break
  319. }
  320. }
  321. }
  322. }
  323. replenishBDataList = append(replenishBDataList, fullBDataList[len(fullBDataList)-1])
  324. // replenishADataList --> map
  325. replenishADataMap := make(map[string]*data_manage.EdbDataList)
  326. for _, indexData := range replenishADataList {
  327. if (utils.StringToTime(indexData.DataTime).After(utils.StringToTime(startDate)) || utils.StringToTime(indexData.DataTime).Equal(utils.StringToTime(startDate))) && (endDate == "" || utils.StringToTime(indexData.DataTime).Before(utils.StringToTime(endDate))) {
  328. replenishADataMap[indexData.DataTime] = indexData
  329. }
  330. }
  331. for _, indexData := range replenishBDataList {
  332. if _, ok = replenishADataMap[indexData.DataTime]; ok {
  333. coordinate := utils.Coordinate{
  334. X: indexData.Value,
  335. Y: replenishADataMap[indexData.DataTime].Value,
  336. }
  337. coordinateList = append(coordinateList, coordinate)
  338. }
  339. }
  340. a, b = utils.GetLinearResult(coordinateList)
  341. r = utils.ComputeCorrelation(coordinateList)
  342. }
  343. // 填充映射指标值 使得时间长度一致
  344. dataList = FillDataBList(dataList, edbInfoMappingA)
  345. // 根据指标A的时间key,在B的映射指标中筛选出对应的值
  346. var dataBList []*data_manage.EdbDataList
  347. var indexMax, indexMin float64
  348. if len(dataList) > 0 {
  349. indexMax = dataList[0].Value
  350. indexMin = dataList[0].Value
  351. for _, indexData := range dataList {
  352. if _, ok := indexADataMap[indexData.DataTime]; ok {
  353. indexDataCopy := *indexData
  354. // 计算指标B映射值
  355. indexDataCopy.Value = math.Round((a*indexData.Value+b)*10000) / 10000
  356. // 比较最大值
  357. if indexData.Value > indexMax {
  358. indexMax = indexData.Value
  359. }
  360. // 比较最小值
  361. if indexData.Value < indexMin {
  362. indexMin = indexData.Value
  363. }
  364. // 将副本添加到 dataBList
  365. dataBList = append(dataBList, &indexDataCopy)
  366. }
  367. }
  368. }
  369. mappingEdbList := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, len(originalEdbList))
  370. copy(mappingEdbList, originalEdbList)
  371. for i, mapping := range mappingEdbList {
  372. if mapping.EdbInfoId != req.EdbInfoIdA {
  373. mappingEdbList[i].EdbInfoId = 0
  374. mappingEdbList[i].EdbCode = ""
  375. mappingEdbList[i].IsAxis = 1
  376. mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
  377. if req.IndexType == 2 {
  378. if req.LeadValue > 0 {
  379. mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")"
  380. }
  381. }
  382. mappingEdbList[i].DataList = dataBList
  383. mappingEdbList[i].MinValue = indexMin
  384. mappingEdbList[i].MaxValue = indexMax
  385. }
  386. }
  387. return mappingEdbList, a, b, r, nil
  388. }
  389. // FillDataBList 填充B的数据 使得与A的时间保持一致
  390. func FillDataBList(dataList []*data_manage.EdbDataList, edbInfoMappingA *data_manage.ChartEdbInfoMapping) []*data_manage.EdbDataList {
  391. dataAList, ok := edbInfoMappingA.DataList.([]*data_manage.EdbDataList)
  392. if !ok {
  393. return nil
  394. }
  395. for utils.StringToTime(dataList[len(dataList)-1].DataTime).Before(utils.StringToTime(dataAList[len(dataAList)-1].DataTime)) {
  396. // 使用A的时间填充时间差
  397. timeDiff := utils.GetNextDayN(dataList[len(dataList)-1].DataTime, 1)
  398. // 创建新的数据点并填充 前值填充
  399. newDataPoint := &data_manage.EdbDataList{
  400. DataTime: timeDiff,
  401. Value: dataList[len(dataList)-1].Value,
  402. DataTimestamp: utils.StringToTime(timeDiff).UnixMilli(),
  403. }
  404. // 将新数据点添加到dataList末尾
  405. dataList = append(dataList, newDataPoint)
  406. }
  407. return dataList
  408. }
  409. type ByDataTime []*data_manage.EdbDataList
  410. func (a ByDataTime) Len() int {
  411. return len(a)
  412. }
  413. func (a ByDataTime) Swap(i, j int) {
  414. a[i], a[j] = a[j], a[i]
  415. }
  416. func (a ByDataTime) Less(i, j int) bool {
  417. t1 := utils.StringToTime(a[i].DataTime)
  418. t2 := utils.StringToTime(a[j].DataTime)
  419. return t1.Before(t2)
  420. }
  421. func fillOriginalChart(req residual_analysis_model.ResidualAnalysisReq, mappingList []*data_manage.ChartEdbInfoMapping, startDate string, endDate string, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, []*data_manage.EdbDataList, []*data_manage.EdbDataList, error) {
  422. var fullADataList, fullBDataList []*data_manage.EdbDataList
  423. for _, v := range mappingList {
  424. var edbInfoMapping residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
  425. edbInfoMapping.EdbInfoType = 1
  426. edbInfoMapping.IsOrder = false
  427. edbInfoMapping.IsAxis = 1
  428. edbInfoMapping.ChartColor = `#00F`
  429. edbInfoMapping.ChartWidth = 3
  430. edbInfoMapping.EdbName = v.EdbName
  431. // 获取图表中的指标数据
  432. dataList, err := data_manage.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, startDate, endDate)
  433. if err != nil {
  434. return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error())
  435. }
  436. // 重新获取指标数据 产品要求需要和计算指标-拟合残差逻辑保持一致
  437. fullDataList, err := data_manage.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
  438. if err != nil {
  439. return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error())
  440. }
  441. if v.EdbInfoId == req.EdbInfoIdB {
  442. edbInfoMapping.LeadValue = req.LeadValue
  443. edbInfoMapping.LeadUnit = req.LeadFrequency
  444. edbInfoMapping.EdbInfoType = req.IndexType
  445. edbInfoMapping.IsOrder = req.IsOrder
  446. edbInfoMapping.IsAxis = 0
  447. edbInfoMapping.ChartColor = `#F00`
  448. edbInfoMapping.ChartWidth = 1
  449. // 领先指标 dataList进行数据处理
  450. if req.IndexType == 2 {
  451. if req.LeadValue < 0 {
  452. return nil, nil, nil, fmt.Errorf("领先值不能小于0")
  453. } else if req.LeadValue > 0 {
  454. edbInfoMapping.EdbName = v.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")"
  455. for _, indexData := range dataList {
  456. switch req.LeadFrequency {
  457. case "天":
  458. indexData.DataTime = utils.GetNextDayN(indexData.DataTime, req.LeadValue)
  459. case "周":
  460. indexData.DataTime = utils.GetNextDayN(indexData.DataTime, req.LeadValue*7)
  461. case "月":
  462. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, req.LeadValue), utils.YearMonthDay)
  463. case "季":
  464. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, req.LeadValue*3), utils.YearMonthDay)
  465. case "年":
  466. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), req.LeadValue, 0), utils.YearMonthDay)
  467. }
  468. indexData.DataTimestamp = utils.StringToTime(indexData.DataTime).UnixMilli()
  469. }
  470. }
  471. }
  472. edbInfoMappingB.DataList = dataList
  473. fullBDataList = fullDataList
  474. } else {
  475. edbInfoMappingA.DataList = dataList
  476. fullADataList = fullDataList
  477. }
  478. edbInfoMapping.EdbInfoId = v.EdbInfoId
  479. edbInfoMapping.EdbCode = v.EdbCode
  480. edbInfoMapping.Unit = v.Unit
  481. edbInfoMapping.Frequency = v.Frequency
  482. edbInfoMapping.Source = v.Source
  483. edbInfoMapping.SourceName = v.SourceName
  484. edbInfoMapping.MinValue = v.MinValue
  485. edbInfoMapping.MaxValue = v.MaxValue
  486. edbInfoMapping.LatestDate = v.LatestDate
  487. edbInfoMapping.LatestValue = v.LatestValue
  488. edbInfoMapping.DataList = dataList
  489. originalEdbList = append(originalEdbList, edbInfoMapping)
  490. }
  491. return originalEdbList, fullADataList, fullBDataList, nil
  492. }
  493. func ContrastPreview(indexCode string) (residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
  494. var condition string
  495. var pars []interface{}
  496. if indexCode != "" {
  497. condition += " and edb_code=?"
  498. pars = append(pars, indexCode)
  499. }
  500. edbInfo, err := data_manage.GetEdbInfoByCondition(condition, pars)
  501. if err != nil {
  502. return residual_analysis_model.ResidualAnalysisChartEdbInfoMapping{}, err
  503. }
  504. if edbInfo == nil {
  505. return residual_analysis_model.ResidualAnalysisChartEdbInfoMapping{}, fmt.Errorf("指标不存在")
  506. }
  507. dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, "", "")
  508. var resp residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
  509. resp.EdbInfoId = edbInfo.EdbInfoId
  510. resp.EdbCode = edbInfo.EdbCode
  511. resp.ChartColor = "#F00"
  512. resp.SourceName = edbInfo.SourceName
  513. resp.EdbName = edbInfo.EdbName
  514. resp.Unit = edbInfo.Unit
  515. resp.Frequency = edbInfo.Frequency
  516. resp.MinValue = edbInfo.MinValue
  517. resp.MaxValue = edbInfo.MaxValue
  518. resp.DataList = dataList
  519. return resp, nil
  520. }
  521. func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveReq, sysUser *system.Admin) error {
  522. // 验证分类是否存在
  523. classifyCount, err := data_manage.GetEdbClassifyCountById(req.ClassifyId)
  524. if err != nil {
  525. return err
  526. }
  527. if classifyCount <= 0 {
  528. return fmt.Errorf("分类不存在")
  529. }
  530. // 校验名称是否重复
  531. var condition string
  532. var pars []interface{}
  533. condition += " and source = ? AND edb_name=?"
  534. pars = append(pars, req.Source, req.EdbName)
  535. edbInfoByCondition, err := data_manage.GetEdbInfoByCondition(condition, pars)
  536. if err != nil && err.Error() != utils.ErrNoRow() {
  537. return err
  538. }
  539. // 获取指标数据最大值 最小值 最后更新时间 最后更新时间对应的值
  540. var indexMax, indexMin, indexLatestValue float64
  541. var indexLatestDate string
  542. if len(req.DataList) > 0 {
  543. latestTime, _ := time.Parse(utils.YearMonthDay, req.DataList[0].DataTime)
  544. for _, data := range req.DataList {
  545. // 比较最大值
  546. if data.Value > indexMax {
  547. indexMax = data.Value
  548. }
  549. // 比较最小值
  550. if data.Value < indexMin {
  551. indexMin = data.Value
  552. }
  553. // 比较最新时间和对应值
  554. currentTime, err := time.Parse(utils.YearMonthDay, data.DataTime)
  555. if err != nil {
  556. // 时间解析失败,跳过此项
  557. continue
  558. }
  559. // 如果当前时间更晚
  560. if currentTime.After(latestTime) {
  561. latestTime = currentTime
  562. indexLatestDate = data.DataTime
  563. indexLatestValue = data.Value
  564. }
  565. }
  566. }
  567. // 更新保存指标和配置的映射关系
  568. mappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(req.ConfigId)
  569. if err != nil {
  570. return err
  571. }
  572. // 判断是更新还是修改 看指标配置映射中,是否存在对应指标 存在 则更新 不存在 则新增
  573. var edbInfoMapping residual_analysis_model.CalculateResidualAnalysisConfigMapping
  574. for _, mapping := range mappingList {
  575. if req.IndexType == mapping.IndexType && req.IndexType != 0 {
  576. edbInfoMapping = mapping
  577. }
  578. }
  579. var edbInfoId int64
  580. var edbCode string
  581. // 更新or新增
  582. if edbInfoMapping.EdbInfoId > 0 {
  583. // 查询指标库指标
  584. edbInfo, err := data_manage.GetEdbInfoById(int(edbInfoMapping.EdbInfoId))
  585. if err != nil {
  586. return err
  587. }
  588. if edbInfo == nil {
  589. return fmt.Errorf("指标不存在")
  590. }
  591. edbInfoId = int64(edbInfo.EdbInfoId)
  592. edbCode = edbInfo.EdbCode
  593. if edbInfoByCondition != nil && edbInfoByCondition.EdbInfoId != edbInfo.EdbInfoId {
  594. return fmt.Errorf("指标名称重复")
  595. }
  596. // 须补充更新指标最大值,最小值,数据最新时间,数据最新值
  597. edbInfo.MaxValue = indexMax
  598. edbInfo.MinValue = indexMin
  599. edbInfo.LatestDate = indexLatestDate
  600. edbInfo.LatestValue = indexLatestValue
  601. edbInfo.Unit = req.Unit
  602. edbInfo.Frequency = req.Frequency
  603. edbInfo.ClassifyId = req.ClassifyId
  604. edbInfo.EdbName = req.EdbName
  605. err = edbInfo.Update([]string{"min_value", "max_value", "latest_date", "latest_value", "unit", "frequency", "classify_id", "edb_name"})
  606. if err != nil {
  607. return err
  608. }
  609. // 删除对应得指标数据
  610. err = residual_analysis_model.DeleteResidualAnalysisDataByEdbCode(edbInfo.EdbCode)
  611. if err != nil {
  612. return fmt.Errorf("删除指标数据失败")
  613. }
  614. } else {
  615. if edbInfoByCondition != nil {
  616. return fmt.Errorf("指标名称重复")
  617. }
  618. // 新增指标
  619. edbCode, err = utils.GenerateEdbCode(1, "")
  620. if err != nil {
  621. return err
  622. }
  623. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  624. edbInfoId, err = data_manage.AddEdbInfo(&data_manage.EdbInfo{
  625. EdbCode: edbCode,
  626. UniqueCode: utils.MD5(utils.CHART_PREFIX + "_" + timestamp),
  627. EdbName: req.EdbName,
  628. EdbNameEn: req.EdbNameEn,
  629. ClassifyId: req.ClassifyId,
  630. EdbType: req.EdbType,
  631. Unit: req.Unit,
  632. UnitEn: req.UnitEn,
  633. Frequency: req.Frequency,
  634. Source: req.Source,
  635. SourceName: "残差分析",
  636. Calendar: req.Calendar,
  637. SysUserRealName: sysUser.RealName,
  638. SysUserId: sysUser.AdminId,
  639. LatestDate: indexLatestDate,
  640. LatestValue: indexLatestValue,
  641. MinValue: indexMin,
  642. MaxValue: indexMax,
  643. CreateTime: time.Now(),
  644. ModifyTime: time.Now(),
  645. })
  646. if err != nil {
  647. return err
  648. }
  649. // 新增指标配置关系
  650. _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{
  651. CalculateResidualAnalysisConfigId: req.ConfigId,
  652. EdbInfoId: edbInfoId,
  653. ResidualType: req.ResidualType,
  654. IndexType: req.IndexType,
  655. CreateTime: time.Now(),
  656. ModifyTime: time.Now(),
  657. })
  658. if err != nil {
  659. return err
  660. }
  661. }
  662. // 新增数据
  663. for i := range req.DataList {
  664. req.DataList[i].EdbDataId = 0
  665. req.DataList[i].EdbInfoId = int(edbInfoId)
  666. req.DataList[i].EdbCode = edbCode
  667. }
  668. _, err = residual_analysis_model.AddResidualAnalysisData(req.DataList)
  669. if err != nil {
  670. return err
  671. }
  672. // 新增自变量 因变量与配置得关系 配置中不存在该指标,则新增
  673. var indexMap = make(map[int64]residual_analysis_model.CalculateResidualAnalysisConfigMapping)
  674. for _, mapping := range mappingList {
  675. indexMap[mapping.EdbInfoId] = mapping
  676. }
  677. if _, ok := indexMap[int64(req.EdbInfoIdA)]; !ok {
  678. _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{
  679. CalculateResidualAnalysisConfigId: req.ConfigId,
  680. EdbInfoId: int64(req.EdbInfoIdA),
  681. ResidualType: req.ResidualType,
  682. IndexType: 3,
  683. CreateTime: time.Now(),
  684. ModifyTime: time.Now(),
  685. })
  686. if err != nil {
  687. return err
  688. }
  689. }
  690. if _, ok := indexMap[int64(req.EdbInfoIdB)]; !ok {
  691. _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{
  692. CalculateResidualAnalysisConfigId: req.ConfigId,
  693. EdbInfoId: int64(req.EdbInfoIdB),
  694. ResidualType: req.ResidualType,
  695. IndexType: 4,
  696. CreateTime: time.Now(),
  697. ModifyTime: time.Now(),
  698. })
  699. if err != nil {
  700. return err
  701. }
  702. }
  703. return nil
  704. }
  705. func ResidualAnalysisDetail(edbInfoId int) (residual_analysis_model.ResidualAnalysisDetailResp, error) {
  706. // 通过指标配置映射表 拿到配置id,再获取关联的所有指标信息
  707. var condition string
  708. var pars []interface{}
  709. condition += " and edb_info_id=?"
  710. pars = append(pars, edbInfoId)
  711. mappingList, err := residual_analysis_model.GetConfigMappingListByCondition(condition, pars)
  712. if err != nil {
  713. return residual_analysis_model.ResidualAnalysisDetailResp{}, err
  714. }
  715. if len(mappingList) <= 0 {
  716. return residual_analysis_model.ResidualAnalysisDetailResp{}, fmt.Errorf("指标不存在")
  717. }
  718. mapping := mappingList[0]
  719. configMappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(mapping.CalculateResidualAnalysisConfigId)
  720. if err != nil {
  721. return residual_analysis_model.ResidualAnalysisDetailResp{}, err
  722. }
  723. var edbInfoIdList []int64
  724. var edbInfoMap = make(map[int64]residual_analysis_model.CalculateResidualAnalysisConfigMapping)
  725. var mappgingFlag = false
  726. var residualFlag = false
  727. for _, v := range configMappingList {
  728. edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
  729. edbInfoMap[v.EdbInfoId] = v
  730. if v.IndexType == 1 {
  731. mappgingFlag = true
  732. } else if v.IndexType == 2 {
  733. residualFlag = true
  734. }
  735. }
  736. condition = ""
  737. pars = []interface{}{}
  738. condition += ` and edb_info_id in(` + utils.GetOrmInReplace(len(edbInfoIdList)) + `)`
  739. for _, id := range edbInfoIdList {
  740. pars = append(pars, id)
  741. }
  742. edbInfoList, err := data_manage.GetEdbInfoListByCond(condition, pars)
  743. if err != nil {
  744. return residual_analysis_model.ResidualAnalysisDetailResp{}, err
  745. }
  746. // 获取配置
  747. configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(mapping.CalculateResidualAnalysisConfigId)
  748. if err != nil {
  749. return residual_analysis_model.ResidualAnalysisDetailResp{}, err
  750. }
  751. var edbInfoListResp []*residual_analysis_model.DetailEdbInfoList
  752. var dependentEdbInfo residual_analysis_model.DetailEdbInfoList
  753. var independentEdbInfo residual_analysis_model.DetailEdbInfoList
  754. for _, edbInfo := range edbInfoList {
  755. var indexType int
  756. if _, ok := edbInfoMap[int64(edbInfo.EdbInfoId)]; ok {
  757. indexType = edbInfoMap[int64(edbInfo.EdbInfoId)].IndexType
  758. }
  759. info := residual_analysis_model.DetailEdbInfoList{
  760. EdbInfoId: edbInfo.EdbInfoId,
  761. EdbInfoType: edbInfo.EdbInfoType,
  762. IndexType: indexType,
  763. SourceName: edbInfo.SourceName,
  764. Source: edbInfo.Source,
  765. EdbCode: edbInfo.EdbCode,
  766. EdbName: edbInfo.EdbName,
  767. EdbNameEn: edbInfo.EdbNameEn,
  768. Unit: edbInfo.Unit,
  769. UnitEn: edbInfo.UnitEn,
  770. Frequency: edbInfo.Frequency,
  771. FrequencyEn: edbInfo.FrequencyEn,
  772. ClassifyId: edbInfo.ClassifyId,
  773. }
  774. edbInfoListResp = append(edbInfoListResp, &info)
  775. if indexType == 3 {
  776. dependentEdbInfo = info
  777. } else if indexType == 4 {
  778. independentEdbInfo = info
  779. }
  780. }
  781. // 补充表格中 映射指标或者残差指标
  782. if mappgingFlag && !residualFlag {
  783. info := residual_analysis_model.DetailEdbInfoList{
  784. IndexType: 2,
  785. EdbName: independentEdbInfo.EdbName + "映射残差/" + dependentEdbInfo.EdbName,
  786. EdbNameEn: dependentEdbInfo.EdbNameEn,
  787. Unit: dependentEdbInfo.Unit,
  788. UnitEn: dependentEdbInfo.UnitEn,
  789. Frequency: dependentEdbInfo.Frequency,
  790. FrequencyEn: dependentEdbInfo.FrequencyEn,
  791. ClassifyId: dependentEdbInfo.ClassifyId,
  792. }
  793. edbInfoListResp = append(edbInfoListResp, &info)
  794. } else if !mappgingFlag && residualFlag {
  795. info := residual_analysis_model.DetailEdbInfoList{
  796. IndexType: 1,
  797. EdbName: dependentEdbInfo.EdbName + "映射" + independentEdbInfo.EdbName,
  798. EdbNameEn: dependentEdbInfo.EdbNameEn,
  799. Unit: dependentEdbInfo.Unit,
  800. UnitEn: dependentEdbInfo.UnitEn,
  801. Frequency: dependentEdbInfo.Frequency,
  802. FrequencyEn: dependentEdbInfo.FrequencyEn,
  803. ClassifyId: dependentEdbInfo.ClassifyId,
  804. }
  805. edbInfoListResp = append(edbInfoListResp, &info)
  806. }
  807. resp := residual_analysis_model.ResidualAnalysisDetailResp{
  808. ConfigInfo: &configInfo,
  809. EdbInfoList: edbInfoListResp,
  810. ResidualType: mapping.ResidualType,
  811. }
  812. return resp, nil
  813. }
  814. func SaveResidualAnalysisConfig(req residual_analysis_model.ResidualAnalysisReq, sysUser *system.Admin) (int64, error) {
  815. config := residual_analysis_model.ResidualAnalysisConfigVo{
  816. DateType: req.DateType,
  817. StartDate: req.StartDate,
  818. EndDate: req.EndDate,
  819. IsOrder: req.IsOrder,
  820. IndexType: req.IndexType,
  821. LeadValue: req.LeadValue,
  822. LeadFrequency: req.LeadFrequency,
  823. LeftIndexMin: req.LeftIndexMin,
  824. LeftIndexMax: req.LeftIndexMax,
  825. RightIndexMin: req.RightIndexMin,
  826. RightIndexMax: req.RightIndexMax,
  827. ResidualIndexMin: req.ResidualIndexMin,
  828. ResidualIndexMax: req.ResidualIndexMax,
  829. ContrastIndexMin: req.ContrastIndexMin,
  830. ContrastIndexMax: req.ContrastIndexMax,
  831. }
  832. // 转换为json格式
  833. configJson, err := json.Marshal(config)
  834. if err != nil {
  835. return 0, err
  836. }
  837. // 新增or更新
  838. /*
  839. var condition string
  840. var pars []interface{}
  841. if req.EdbInfoId > 0 {
  842. condition += " and edb_info_id=?"
  843. pars = append(pars, req.EdbInfoId)
  844. configMappings, err := residual_analysis_model.GetConfigMappingListByCondition(condition, pars)
  845. if err != nil {
  846. return 0, err
  847. }
  848. if len(configMappings) > 0 {
  849. mapping := configMappings[0]
  850. configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(mapping.CalculateResidualAnalysisConfigId)
  851. if err != nil {
  852. return 0, err
  853. }
  854. configInfo.Config = string(configJson)
  855. err = residual_analysis_model.UpdateResidualAnalysisConfig(configInfo)
  856. if err != nil {
  857. return 0, err
  858. }
  859. }
  860. }*/
  861. var configId int64
  862. if req.ConfigId > 0 {
  863. configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(req.ConfigId)
  864. if err != nil {
  865. return 0, err
  866. }
  867. if configInfo.CalculateResidualAnalysisConfigId == 0 {
  868. return 0, fmt.Errorf("未找到配置信息")
  869. }
  870. configId = int64(configInfo.CalculateResidualAnalysisConfigId)
  871. configInfo.Config = string(configJson)
  872. err = residual_analysis_model.UpdateResidualAnalysisConfig(configInfo)
  873. if err != nil {
  874. return 0, err
  875. }
  876. } else {
  877. analysisConfig := residual_analysis_model.CalculateResidualAnalysisConfig{
  878. Config: string(configJson),
  879. SysUserId: sysUser.AdminId,
  880. CreateTime: time.Now(),
  881. ModifyTime: time.Now(),
  882. }
  883. configId, err = residual_analysis_model.SaveResidualAnalysisConfig(analysisConfig)
  884. if err != nil {
  885. return 0, err
  886. }
  887. }
  888. return configId, nil
  889. }
  890. func CheckResidualAnalysisExist(configId int) (int64, error) {
  891. configMappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(configId)
  892. if err != nil {
  893. return 0, err
  894. }
  895. var configMapping residual_analysis_model.CalculateResidualAnalysisConfigMapping
  896. for _, mapping := range configMappingList {
  897. if mapping.IndexType == 2 {
  898. configMapping = mapping
  899. }
  900. }
  901. return configMapping.EdbInfoId, nil
  902. }