stl.go 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477
  1. package stl
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "errors"
  6. "eta/eta_api/models/data_manage"
  7. "eta/eta_api/models/data_manage/stl"
  8. "eta/eta_api/models/data_manage/stl/request"
  9. "eta/eta_api/models/data_manage/stl/response"
  10. "eta/eta_api/services/data"
  11. "eta/eta_api/services/data/data_manage_permission"
  12. "eta/eta_api/services/elastic"
  13. "eta/eta_api/utils"
  14. "fmt"
  15. "os"
  16. "os/exec"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "github.com/rdlucklib/rdluck_tools/paging"
  22. "github.com/shopspring/decimal"
  23. "github.com/tealeg/xlsx"
  24. )
  25. const (
  26. ALL_DATE = iota + 1
  27. LAST_N_YEARS
  28. RANGE_DATE
  29. RANGE_DATE_TO_NOW
  30. )
  31. var EDB_DATA_CALCULATE_STL_TREND_CACHE = `eta:stl_decompose:trend:config_id:`
  32. var EDB_DATA_CALCULATE_STL_SEASONAL_CACHE = `eta:stl_decompose:seasonal:config_id:`
  33. var EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE = `eta:stl_decompose:residual:config_id:`
  34. var EDB_DATA_CALCULATE_STL_NonTrend_CACHE = `eta:stl_decompose:non_trend:config_id:`
  35. func GenerateStlEdbData(req *request.StlConfigReq, adminId int) (resp *response.StlPreviewResp, msg string, err error) {
  36. config, err := stl.GetCalculateStlConfigById(req.CalculateStlConfigId)
  37. if err != nil {
  38. if err.Error() == utils.ErrNoRow() {
  39. msg = "配置信息不存在,请重新计算"
  40. return
  41. }
  42. msg = "获取配置信息失败"
  43. return
  44. }
  45. var confReq request.StlConfigReq
  46. if err = json.Unmarshal([]byte(config.Config), &confReq); err != nil {
  47. msg = "预览失败"
  48. err = fmt.Errorf("配置信息解析失败, err:%s", err.Error())
  49. return
  50. }
  51. edbInfo, err := data_manage.GetEdbInfoById(confReq.EdbInfoId)
  52. if err != nil {
  53. if err.Error() == utils.ErrNoRow() {
  54. msg = "指标不存在"
  55. return
  56. }
  57. msg = "获取指标信息失败"
  58. return
  59. }
  60. var condition string
  61. var pars []interface{}
  62. switch confReq.DataRangeType {
  63. case ALL_DATE:
  64. case LAST_N_YEARS:
  65. condition += " AND data_time >=?"
  66. year := time.Now().Year()
  67. lastNyear, er := strconv.Atoi(req.LastNYear)
  68. if er != nil {
  69. msg = "最近N年输入不合法"
  70. err = er
  71. return
  72. }
  73. if lastNyear <= 0 {
  74. msg = "最近N年输入不合法"
  75. err = fmt.Errorf("最近N年输入不合法")
  76. return
  77. }
  78. lastNyear = lastNyear - 1
  79. lastDate := time.Date(year-lastNyear, 1, 1, 0, 0, 0, 0, time.Local)
  80. pars = append(pars, lastDate)
  81. case RANGE_DATE:
  82. condition = " AND data_time >=? AND data_time <=?"
  83. pars = append(pars, confReq.StartDate, confReq.EndDate)
  84. case RANGE_DATE_TO_NOW:
  85. condition = " AND data_time >=?"
  86. pars = append(pars, confReq.StartDate)
  87. }
  88. condition += " AND edb_code =?"
  89. pars = append(pars, edbInfo.EdbCode)
  90. edbData, err := data_manage.GetAllEdbDataListByCondition(condition, pars, edbInfo.Source, edbInfo.SubSource)
  91. if err != nil {
  92. msg = "获取指标数据失败"
  93. return
  94. }
  95. var condMsg string
  96. if confReq.Period < 2 || confReq.Period > len(edbData) {
  97. condMsg += "period必须是一个大于等于2的正整数,且必须小于时间序列的长度"
  98. }
  99. if confReq.Seasonal < 3 || confReq.Seasonal%2 == 0 || confReq.Seasonal <= confReq.Period {
  100. if condMsg != "" {
  101. condMsg += "\n"
  102. }
  103. condMsg += "seasonal必须是一个大于等于3的奇整数,且必须大于period"
  104. }
  105. if confReq.Trend < 3 || confReq.Trend%2 == 0 || confReq.Trend <= confReq.Period {
  106. if condMsg != "" {
  107. condMsg += "\n"
  108. }
  109. condMsg += "trend必须是一个大于等于3的奇整数,且必须大于period"
  110. }
  111. if confReq.Fraction < 0 || confReq.Fraction > 1 {
  112. if condMsg != "" {
  113. condMsg += "\n"
  114. }
  115. condMsg += "fraction必须是一个介于[0-1]之间"
  116. }
  117. if 1 > confReq.TrendDeg || confReq.TrendDeg > 5 {
  118. if condMsg != "" {
  119. condMsg += "\n"
  120. }
  121. condMsg += "trend_deg请设置成1-5的整数"
  122. }
  123. if 1 > confReq.SeasonalDeg || confReq.SeasonalDeg > 5 {
  124. if condMsg != "" {
  125. condMsg += "\n"
  126. }
  127. condMsg += "seasonal_deg请设置成1-5的整数"
  128. }
  129. if 1 > confReq.LowPassDeg || confReq.LowPassDeg > 5 {
  130. if condMsg != "" {
  131. condMsg += "\n"
  132. }
  133. condMsg += "low_pass_deg请设置成1-5的整数"
  134. }
  135. if condMsg != "" {
  136. msg = condMsg
  137. err = fmt.Errorf("参数错误")
  138. return
  139. }
  140. dir, _ := os.Executable()
  141. exPath := filepath.Dir(dir) + "/static/stl_tmp"
  142. err = CheckOsPathAndMake(exPath)
  143. if err != nil {
  144. msg = "计算失败"
  145. return
  146. }
  147. loadFilePath := exPath + "/" + strconv.Itoa(adminId) + "_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
  148. err = SaveToExcel(edbData, loadFilePath)
  149. if err != nil {
  150. msg = "保存数据到Excel失败"
  151. return
  152. }
  153. defer os.Remove(loadFilePath)
  154. saveFilePath := exPath + "/" + strconv.Itoa(adminId) + "_" + time.Now().Format(utils.FormatDateTimeUnSpace) + "_res" + ".xlsx"
  155. result, err := execStlPythonCode(loadFilePath, saveFilePath, confReq.Period, confReq.Seasonal, confReq.Trend, confReq.TrendDeg, confReq.SeasonalDeg, confReq.LowPassDeg, confReq.Fraction, confReq.Robust)
  156. if err != nil {
  157. msg = "计算失败,请重新选择指标和参数后计算"
  158. return
  159. }
  160. trendChart, seasonalChart, residualChart, nonTrendChartInfo, err := ParseStlExcel(saveFilePath)
  161. if err != nil {
  162. msg = "解析Excel失败"
  163. return
  164. }
  165. defer os.Remove(saveFilePath)
  166. resp = new(response.StlPreviewResp)
  167. resp.OriginEdbInfo.EdbInfoId = edbInfo.EdbInfoId
  168. resp.OriginEdbInfo.Title = edbInfo.EdbName
  169. resp.OriginEdbInfo.ClassifyId = edbInfo.ClassifyId
  170. resp.OriginEdbInfo.MaxData = edbInfo.MaxValue
  171. resp.OriginEdbInfo.MinData = edbInfo.MinValue
  172. resp.OriginEdbInfo.Frequency = edbInfo.Frequency
  173. resp.OriginEdbInfo.Unit = edbInfo.Unit
  174. resp.OriginEdbInfo.DataList = formatEdbData(edbData)
  175. resp.TrendChartInfo.DataList = trendChart.DataList
  176. resp.TrendChartInfo.MaxData = trendChart.MaxData
  177. resp.TrendChartInfo.MinData = trendChart.MinData
  178. resp.TrendChartInfo.Title = edbInfo.EdbName + "Trend"
  179. resp.TrendChartInfo.ClassifyId = edbInfo.ClassifyId
  180. resp.TrendChartInfo.Frequency = edbInfo.Frequency
  181. resp.TrendChartInfo.Unit = edbInfo.Unit
  182. resp.SeasonalChartInfo.DataList = seasonalChart.DataList
  183. resp.SeasonalChartInfo.MaxData = seasonalChart.MaxData
  184. resp.SeasonalChartInfo.MinData = seasonalChart.MinData
  185. resp.SeasonalChartInfo.ClassifyId = edbInfo.ClassifyId
  186. resp.SeasonalChartInfo.Title = edbInfo.EdbName + "Seasonal"
  187. resp.SeasonalChartInfo.Frequency = edbInfo.Frequency
  188. resp.SeasonalChartInfo.Unit = edbInfo.Unit
  189. resp.ResidualChartInfo.DataList = residualChart.DataList
  190. resp.ResidualChartInfo.MaxData = residualChart.MaxData
  191. resp.ResidualChartInfo.MinData = residualChart.MinData
  192. resp.ResidualChartInfo.ClassifyId = edbInfo.ClassifyId
  193. resp.ResidualChartInfo.Title = edbInfo.EdbName + "Residual"
  194. resp.ResidualChartInfo.Frequency = edbInfo.Frequency
  195. resp.ResidualChartInfo.Unit = edbInfo.Unit
  196. // 季节性项+残差项
  197. nonTrendName := fmt.Sprintf("%sNon-Trend/F%g", edbInfo.EdbName, confReq.Fraction)
  198. resp.NonTrendChartInfo.DataList = nonTrendChartInfo.DataList
  199. resp.NonTrendChartInfo.MaxData = nonTrendChartInfo.MaxData
  200. resp.NonTrendChartInfo.MinData = nonTrendChartInfo.MinData
  201. resp.NonTrendChartInfo.ClassifyId = edbInfo.ClassifyId
  202. resp.NonTrendChartInfo.Title = nonTrendName
  203. resp.NonTrendChartInfo.Frequency = edbInfo.Frequency
  204. resp.NonTrendChartInfo.Unit = edbInfo.Unit
  205. resp.EvaluationResult.Mean = strconv.FormatFloat(result.ResidualMean, 'f', 4, 64)
  206. resp.EvaluationResult.Std = strconv.FormatFloat(result.ResidualVar, 'f', 4, 64)
  207. resp.EvaluationResult.AdfPValue = strconv.FormatFloat(result.AdfPValue, 'f', -1, 64)
  208. resp.EvaluationResult.LjungBoxPValue = strconv.FormatFloat(result.LbTestPValue, 'f', -1, 64)
  209. confMapping, err := stl.GetCalculateStlConfigMappingByConfigId(req.CalculateStlConfigId)
  210. if err != nil {
  211. msg = "获取配置信息失败"
  212. return
  213. }
  214. var relationEdbInfoId []int
  215. for _, mapping := range confMapping {
  216. switch mapping.StlEdbType {
  217. case utils.StlTypeTrend:
  218. resp.TrendChartInfo.EdbInfoId = mapping.EdbInfoId
  219. relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
  220. case utils.StlTypeSeasonal:
  221. resp.SeasonalChartInfo.EdbInfoId = mapping.EdbInfoId
  222. relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
  223. case utils.StlTypeResidual:
  224. resp.ResidualChartInfo.EdbInfoId = mapping.EdbInfoId
  225. relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
  226. case utils.StlTypeNonTrend:
  227. resp.NonTrendChartInfo.EdbInfoId = mapping.EdbInfoId
  228. relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
  229. }
  230. }
  231. relationEdbInfo, err := data_manage.GetEdbInfoByIdList(relationEdbInfoId)
  232. if err != nil {
  233. msg = "获取关联指标信息失败"
  234. return
  235. }
  236. for _, info := range relationEdbInfo {
  237. switch info.EdbInfoId {
  238. case resp.TrendChartInfo.EdbInfoId:
  239. resp.TrendChartInfo.Title = info.EdbName
  240. resp.TrendChartInfo.ClassifyId = info.ClassifyId
  241. resp.TrendChartInfo.Frequency = info.Frequency
  242. resp.TrendChartInfo.Unit = info.Unit
  243. case resp.SeasonalChartInfo.EdbInfoId:
  244. resp.SeasonalChartInfo.Title = info.EdbName
  245. resp.SeasonalChartInfo.ClassifyId = info.ClassifyId
  246. resp.SeasonalChartInfo.Frequency = info.Frequency
  247. resp.SeasonalChartInfo.Unit = info.Unit
  248. case resp.ResidualChartInfo.EdbInfoId:
  249. resp.ResidualChartInfo.Title = info.EdbName
  250. resp.ResidualChartInfo.ClassifyId = info.ClassifyId
  251. resp.ResidualChartInfo.Frequency = info.Frequency
  252. resp.ResidualChartInfo.Unit = info.Unit
  253. case resp.NonTrendChartInfo.EdbInfoId:
  254. resp.NonTrendChartInfo.Title = info.EdbName
  255. resp.NonTrendChartInfo.ClassifyId = info.ClassifyId
  256. resp.NonTrendChartInfo.Frequency = info.Frequency
  257. resp.NonTrendChartInfo.Unit = info.Unit
  258. }
  259. }
  260. bTrend, _ := json.Marshal(trendChart.DataList)
  261. bSeasonal, _ := json.Marshal(seasonalChart.DataList)
  262. bResidual, _ := json.Marshal(residualChart.DataList)
  263. bNonTrend, _ := json.Marshal(nonTrendChartInfo.DataList)
  264. err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_TREND_CACHE+strconv.Itoa(config.CalculateStlConfigId), bTrend, time.Hour*2)
  265. if err != nil {
  266. msg = "计算失败,请重新计算"
  267. return
  268. }
  269. err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE+strconv.Itoa(config.CalculateStlConfigId), bSeasonal, time.Hour*2)
  270. if err != nil {
  271. msg = "计算失败,请重新计算"
  272. return
  273. }
  274. err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE+strconv.Itoa(config.CalculateStlConfigId), bResidual, time.Hour*2)
  275. if err != nil {
  276. msg = "计算失败,请重新计算"
  277. }
  278. err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_NonTrend_CACHE+strconv.Itoa(config.CalculateStlConfigId), bNonTrend, time.Hour*2)
  279. if err != nil {
  280. msg = "计算失败,请重新计算"
  281. }
  282. return
  283. }
  284. func formatEdbData(items []*data_manage.EdbData) []*response.EdbData {
  285. res := make([]*response.EdbData, 0, len(items))
  286. for _, item := range items {
  287. t, _ := time.Parse(utils.FormatDate, item.DataTime)
  288. res = append(res, &response.EdbData{
  289. DataTime: item.DataTime,
  290. Value: item.Value,
  291. DataTimestamp: t.UnixMilli(),
  292. })
  293. }
  294. return res
  295. }
  296. func CheckOsPathAndMake(path string) (err error) {
  297. if _, er := os.Stat(path); os.IsNotExist(er) {
  298. err = os.MkdirAll(path, os.ModePerm)
  299. }
  300. return
  301. }
  302. func ParseStlExcel(excelPath string) (TrendChart, SeasonalChart, ResidualChart, nonTrendChartInfo response.ChartEdbInfo, err error) {
  303. file, err := xlsx.OpenFile(excelPath)
  304. if err != nil {
  305. return
  306. }
  307. for _, sheet := range file.Sheets {
  308. switch sheet.Name {
  309. case "季节":
  310. var MinData, MaxData float64
  311. for i, row := range sheet.Rows {
  312. if i == 0 {
  313. continue
  314. }
  315. var date string
  316. var dataTimestamp int64
  317. if row.Cells[0].Type() == xlsx.CellTypeNumeric {
  318. dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
  319. tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
  320. date = tmpTime.Format(utils.FormatDate)
  321. dataTimestamp = tmpTime.UnixMilli()
  322. } else {
  323. timeDate, _ := time.Parse(utils.FormatDateTime, date)
  324. date = timeDate.Format(utils.FormatDate)
  325. dataTimestamp = timeDate.UnixMilli()
  326. }
  327. fv, _ := row.Cells[1].Float()
  328. if MinData == 0 || fv < MinData {
  329. MinData = fv
  330. }
  331. if MaxData == 0 || fv > MaxData {
  332. MaxData = fv
  333. }
  334. fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
  335. SeasonalChart.DataList = append(SeasonalChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
  336. }
  337. SeasonalChart.MinData = MinData
  338. SeasonalChart.MaxData = MaxData
  339. case "趋势":
  340. var MinData, MaxData float64
  341. for i, row := range sheet.Rows {
  342. if i == 0 {
  343. continue
  344. }
  345. var date string
  346. var dataTimestamp int64
  347. if row.Cells[0].Type() == xlsx.CellTypeNumeric {
  348. dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
  349. tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
  350. date = tmpTime.Format(utils.FormatDate)
  351. dataTimestamp = tmpTime.UnixMilli()
  352. } else {
  353. timeDate, _ := time.Parse(utils.FormatDateTime, date)
  354. date = timeDate.Format(utils.FormatDate)
  355. dataTimestamp = timeDate.UnixMilli()
  356. }
  357. fv, _ := row.Cells[1].Float()
  358. if MinData == 0 || fv < MinData {
  359. MinData = fv
  360. }
  361. if MaxData == 0 || fv > MaxData {
  362. MaxData = fv
  363. }
  364. fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
  365. TrendChart.DataList = append(TrendChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
  366. }
  367. TrendChart.MaxData = MaxData
  368. TrendChart.MinData = MinData
  369. case "残差":
  370. var MinData, MaxData float64
  371. for i, row := range sheet.Rows {
  372. if i == 0 {
  373. continue
  374. }
  375. var date string
  376. var dataTimestamp int64
  377. if row.Cells[0].Type() == xlsx.CellTypeNumeric {
  378. dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
  379. tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
  380. date = tmpTime.Format(utils.FormatDate)
  381. dataTimestamp = tmpTime.UnixMilli()
  382. } else {
  383. timeDate, _ := time.Parse(utils.FormatDateTime, date)
  384. date = timeDate.Format(utils.FormatDate)
  385. dataTimestamp = timeDate.UnixMilli()
  386. }
  387. fv, _ := row.Cells[1].Float()
  388. if MinData == 0 || fv < MinData {
  389. MinData = fv
  390. }
  391. if MaxData == 0 || fv > MaxData {
  392. MaxData = fv
  393. }
  394. fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
  395. ResidualChart.DataList = append(ResidualChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
  396. }
  397. ResidualChart.MaxData = MaxData
  398. ResidualChart.MinData = MinData
  399. }
  400. }
  401. // 数据处理
  402. dateList := make([]string, 0)
  403. residualDateMap := make(map[string]*response.EdbData)
  404. for _, item := range ResidualChart.DataList {
  405. if _, ok := residualDateMap[item.DataTime]; ok {
  406. continue
  407. }
  408. residualDateMap[item.DataTime] = item
  409. dateList = append(dateList, item.DataTime)
  410. }
  411. seasonalDateMap := make(map[string]*response.EdbData)
  412. for _, item := range SeasonalChart.DataList {
  413. if _, ok := seasonalDateMap[item.DataTime]; ok {
  414. continue
  415. }
  416. seasonalDateMap[item.DataTime] = item
  417. }
  418. // 季节性项+残差项
  419. {
  420. dataList := make([]*response.EdbData, 0)
  421. var minValue, maxValue sql.NullFloat64
  422. for _, date := range dateList {
  423. tmpResidual, ok := residualDateMap[date]
  424. if !ok {
  425. continue
  426. }
  427. tmpSeasonal, ok := seasonalDateMap[date]
  428. if !ok {
  429. continue
  430. }
  431. tmpValue := tmpResidual.Value + tmpSeasonal.Value
  432. tmpValue, _ = decimal.NewFromFloat(tmpValue).Round(4).Float64()
  433. dataList = append(dataList, &response.EdbData{
  434. DataTime: date,
  435. DataTimestamp: 0,
  436. Value: tmpValue,
  437. })
  438. // 如果没有设置最小值,或者设置的最小值比当前值还大,则需要更新最小值
  439. if !minValue.Valid || minValue.Float64 > tmpValue {
  440. err = minValue.Scan(tmpValue)
  441. if err != nil {
  442. return
  443. }
  444. }
  445. // 如果没有设置最大值,或者设置的最大值比当前值还小,则需要更新最大值
  446. if !maxValue.Valid || maxValue.Float64 < tmpValue {
  447. err = maxValue.Scan(tmpValue)
  448. if err != nil {
  449. return
  450. }
  451. }
  452. }
  453. nonTrendChartInfo.DataList = dataList
  454. nonTrendChartInfo.MinData = minValue.Float64
  455. nonTrendChartInfo.MaxData = maxValue.Float64
  456. }
  457. return
  458. }
  459. func SaveToExcel(data []*data_manage.EdbData, filePath string) (err error) {
  460. xlsxFile := xlsx.NewFile()
  461. sheetNew, err := xlsxFile.AddSheet("Tmp")
  462. if err != nil {
  463. return
  464. }
  465. titleRow := sheetNew.AddRow()
  466. titleRow.AddCell().SetString("日期")
  467. titleRow.AddCell().SetString("值")
  468. for i, d := range data {
  469. row := sheetNew.Row(i + 1)
  470. row.AddCell().SetString(d.DataTime)
  471. row.AddCell().SetFloat(d.Value)
  472. }
  473. err = xlsxFile.Save(filePath)
  474. if err != nil {
  475. return
  476. }
  477. return
  478. }
  479. type STLResult struct {
  480. ResidualMean float64 `json:"residual_mean"`
  481. ResidualVar float64 `json:"residual_var"`
  482. AdfPValue float64 `json:"adf_p_value"`
  483. LbTestPValue float64 `json:"lb_test_p_value"`
  484. LbTestStat float64 `json:"lb_test_stat"`
  485. }
  486. func execStlPythonCode(path, toPath string, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg int, fraction float64, robust bool) (stlResult *STLResult, err error) {
  487. pythonCode := `
  488. import json
  489. import warnings
  490. warnings.filterwarnings('ignore')
  491. import pandas as pd
  492. from statsmodels.tsa.seasonal import STL
  493. from statsmodels.nonparametric.smoothers_lowess import lowess
  494. from statsmodels.tsa.stattools import adfuller
  495. from statsmodels.stats.diagnostic import acorr_ljungbox
  496. import numpy as np
  497. file_path = r"%s"
  498. df = pd.read_excel(file_path, parse_dates=['日期'], engine='openpyxl')
  499. df.set_index('日期', inplace=True)
  500. df = df[df.index.notna()]
  501. period = %d
  502. seasonal = %d
  503. trend = %d
  504. fraction = %g
  505. seasonal_deg = %d
  506. trend_deg = %d
  507. low_pass_deg = %d
  508. robust = %s
  509. stl = STL(
  510. df['值'],
  511. period=period,
  512. seasonal=seasonal,
  513. trend=trend,
  514. low_pass=None,
  515. seasonal_deg=seasonal_deg,
  516. trend_deg=trend_deg,
  517. low_pass_deg=low_pass_deg,
  518. seasonal_jump=1,
  519. trend_jump=1,
  520. low_pass_jump=1,
  521. robust=robust
  522. )
  523. result = stl.fit()
  524. smoothed = lowess(df['值'], np.arange(len(df)), frac=fraction)
  525. trend_lowess = smoothed[:, 1]
  526. # 季节图
  527. seasonal_component = result.seasonal
  528. # 趋势图
  529. trend_lowess_series = pd.Series(trend_lowess, index=df.index)
  530. # 残差图
  531. residual_component = df['值'] - trend_lowess - seasonal_component
  532. # 计算打印残差的均值
  533. residual_mean = np.mean(residual_component)
  534. # 计算打印残差的方差
  535. residual_var = np.std(residual_component)
  536. # 计算打印残差的ADF检验结果, 输出p-value
  537. adf_result = adfuller(residual_component)
  538. # 根据p-value判断是否平稳
  539. lb_test = acorr_ljungbox(residual_component, lags=period, return_df=True)
  540. output_file = r"%s"
  541. with pd.ExcelWriter(output_file) as writer:
  542. # 保存季节图
  543. pd.Series(seasonal_component, index=df.index, name='值').to_frame().reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='季节', index=False)
  544. # 保存趋势图
  545. trend_lowess_series.to_frame(name='值').reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='趋势', index=False)
  546. # 保存残差图
  547. pd.Series(residual_component, index=df.index, name='值').to_frame().reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='残差', index=False)
  548. output = json.dumps({
  549. 'residual_mean': residual_mean,
  550. 'residual_var': residual_var,
  551. 'adf_p_value': adf_result[1],
  552. 'lb_test_p_value': lb_test['lb_pvalue'].values[0],
  553. 'lb_test_stat': lb_test['lb_stat'].values[0]
  554. })
  555. print(output)
  556. `
  557. robustStr := "True"
  558. if !robust {
  559. robustStr = "False"
  560. }
  561. pythonCode = fmt.Sprintf(pythonCode, path, period, seasonal, trend, fraction, seasonalDeg, trendDeg, lowPassDeg, robustStr, toPath)
  562. utils.FileLog.Info("stl exec python code:%s", pythonCode)
  563. cmd := exec.Command(`python3`, "-c", pythonCode)
  564. output, err := cmd.CombinedOutput()
  565. if err != nil {
  566. utils.FileLog.Info(`execStlPythonCode error:%s, input: path:%s, toPath:%s, period:%d, seasonal:%d, trend:%d, trendDeg:%d, seasonalDeg:%d, lowPassDeg:%d, fraction:%g, robust:%s, output:%s`, err.Error(), path, toPath, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg, fraction, robustStr, string(output))
  567. return
  568. }
  569. defer cmd.Process.Kill()
  570. if err = json.Unmarshal(output, &stlResult); err != nil {
  571. utils.FileLog.Info(`execStlPythonCode Unmarshal error:%s, input: path:%s, toPath:%s, period:%d, seasonal:%d, trend:%d, trendDeg:%d, seasonalDeg:%d, lowPassDeg:%d, fraction:%g, robust:%s, output:%s`, err.Error(), path, toPath, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg, fraction, robustStr, string(output))
  572. return
  573. }
  574. return
  575. }
  576. func SaveStlConfig(req *request.StlConfigReq, adminId int) (configId int64, msg string, err error) {
  577. edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
  578. if err != nil {
  579. if err.Error() == utils.ErrNoRow() {
  580. msg = "指标不存在"
  581. return
  582. }
  583. msg = "获取指标信息失败"
  584. return
  585. }
  586. var condition string
  587. var pars []interface{}
  588. switch req.DataRangeType {
  589. case ALL_DATE:
  590. case LAST_N_YEARS:
  591. condition += " AND data_time >=?"
  592. year := time.Now().Year()
  593. lastNyear, er := strconv.Atoi(req.LastNYear)
  594. if er != nil {
  595. msg = "最近N年输入不合法"
  596. err = er
  597. return
  598. }
  599. lastDate := time.Date(year-lastNyear, 1, 1, 0, 0, 0, 0, time.Local)
  600. pars = append(pars, lastDate)
  601. case RANGE_DATE:
  602. condition = " AND data_time >=? AND data_time <=?"
  603. pars = append(pars, req.StartDate, req.EndDate)
  604. case RANGE_DATE_TO_NOW:
  605. condition = " AND data_time >=?"
  606. pars = append(pars, req.StartDate)
  607. }
  608. condition += " AND edb_code =?"
  609. pars = append(pars, edbInfo.EdbCode)
  610. edbData, err := data_manage.GetAllEdbDataListByCondition(condition, pars, edbInfo.Source, edbInfo.SubSource)
  611. if err != nil {
  612. msg = "获取指标数据失败"
  613. return
  614. }
  615. var condMsg string
  616. if req.Period < 2 || req.Period > len(edbData) {
  617. condMsg += "period必须是一个大于等于2的正整数,且必须小于时间序列的长度"
  618. }
  619. if req.Seasonal < 3 || req.Seasonal%2 == 0 || req.Seasonal <= req.Period {
  620. if condMsg != "" {
  621. condMsg += "\n"
  622. }
  623. condMsg += "seasonal必须是一个大于等于3的奇整数,且必须大于period"
  624. }
  625. if req.Trend < 3 || req.Trend%2 == 0 || req.Trend <= req.Period {
  626. if condMsg != "" {
  627. condMsg += "\n"
  628. }
  629. condMsg += "trend必须是一个大于等于3的奇整数,且必须大于period"
  630. }
  631. if req.Fraction < 0 || req.Fraction > 1 {
  632. if condMsg != "" {
  633. condMsg += "\n"
  634. }
  635. condMsg += "fraction必须是一个介于[0-1]之间"
  636. }
  637. if 1 > req.TrendDeg || req.TrendDeg > 5 {
  638. if condMsg != "" {
  639. condMsg += "\n"
  640. }
  641. condMsg += "trend_deg请设置成1-5的整数"
  642. }
  643. if 1 > req.SeasonalDeg || req.SeasonalDeg > 5 {
  644. if condMsg != "" {
  645. condMsg += "\n"
  646. }
  647. condMsg += "seasonal_deg请设置成1-5的整数"
  648. }
  649. if 1 > req.LowPassDeg || req.LowPassDeg > 5 {
  650. if condMsg != "" {
  651. condMsg += "\n"
  652. }
  653. condMsg += "low_pass_deg请设置成1-5的整数"
  654. }
  655. if condMsg != "" {
  656. msg = condMsg
  657. err = fmt.Errorf("参数错误")
  658. return
  659. }
  660. b, err := json.Marshal(req)
  661. if err != nil {
  662. return
  663. }
  664. conf := new(stl.CalculateStlConfig)
  665. if req.CalculateStlConfigId > 0 {
  666. conf.CalculateStlConfigId = req.CalculateStlConfigId
  667. conf.Config = string(b)
  668. conf.ModifyTime = time.Now()
  669. err = conf.Update([]string{"Config", "ModifyTime"})
  670. configId = int64(req.CalculateStlConfigId)
  671. } else {
  672. conf.Config = string(b)
  673. conf.SysUserId = adminId
  674. conf.CreateTime = time.Now()
  675. conf.ModifyTime = time.Now()
  676. configId, err = conf.Insert()
  677. }
  678. return
  679. }
  680. func SearchEdbInfoWithStl(adminId int, keyWord string, currentIndex, pageSize int, lang string) (resp data_manage.EdbInfoFilterDataResp, msg string, err error) {
  681. var edbInfoList []*data_manage.EdbInfoList
  682. noPermissionEdbInfoIdList := make([]int, 0) //无权限指标
  683. // 获取当前账号的不可见指标
  684. {
  685. obj := data_manage.EdbInfoNoPermissionAdmin{}
  686. confList, er := obj.GetAllListByAdminId(adminId)
  687. if er != nil && er.Error() != utils.ErrNoRow() {
  688. msg = "获取失败"
  689. err = fmt.Errorf("获取不可见指标配置数据失败,Err:" + er.Error())
  690. return
  691. }
  692. for _, v := range confList {
  693. noPermissionEdbInfoIdList = append(noPermissionEdbInfoIdList, v.EdbInfoId)
  694. }
  695. }
  696. if currentIndex <= 0 {
  697. currentIndex = 1
  698. }
  699. startSize := utils.StartIndex(currentIndex, pageSize)
  700. // 是否走ES
  701. isEs := false
  702. var total int64
  703. if keyWord != "" {
  704. frequencyList := []string{"日度", "周度", "旬度", "月度", "季度"}
  705. // 普通的搜索
  706. total, edbInfoList, err = elastic.SearchEdbInfoDataByfrequency(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, 0, frequencyList, noPermissionEdbInfoIdList)
  707. isEs = true
  708. } else {
  709. var condition string
  710. var pars []interface{}
  711. // 普通指标
  712. condition += ` AND edb_info_type = ? `
  713. pars = append(pars, 0)
  714. // 无权限指标id
  715. lenNoPermissionEdbInfoIdList := len(noPermissionEdbInfoIdList)
  716. if lenNoPermissionEdbInfoIdList > 0 {
  717. condition += ` AND edb_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionEdbInfoIdList) + `) `
  718. pars = append(pars, noPermissionEdbInfoIdList)
  719. }
  720. //频度
  721. condition += ` AND frequency IN ('日度', '周度', '旬度', '月度', '季度') `
  722. total, edbInfoList, err = data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
  723. }
  724. if err != nil {
  725. edbInfoList = make([]*data_manage.EdbInfoList, 0)
  726. }
  727. page := paging.GetPaging(currentIndex, pageSize, int(total))
  728. edbInfoListLen := len(edbInfoList)
  729. classifyIdList := make([]int, 0)
  730. for i := 0; i < edbInfoListLen; i++ {
  731. edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
  732. classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
  733. // 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
  734. if keyWord == "" {
  735. if lang == utils.ZhLangVersion {
  736. edbInfoList[i].SearchText = edbInfoList[i].EdbName
  737. }
  738. if lang == utils.EnLangVersion {
  739. edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
  740. }
  741. }
  742. }
  743. // 当前列表中的分类map
  744. classifyMap := make(map[int]*data_manage.EdbClassify)
  745. if edbInfoListLen > 0 {
  746. classifyList, er := data_manage.GetEdbClassifyByIdList(classifyIdList)
  747. if er != nil {
  748. msg = "获取失败"
  749. err = fmt.Errorf("获取分类列表失败,Err:" + er.Error())
  750. return
  751. }
  752. for _, v := range classifyList {
  753. classifyMap[v.ClassifyId] = v
  754. }
  755. // 获取所有有权限的指标和分类
  756. permissionEdbIdList, permissionClassifyIdList, er := data_manage_permission.GetUserEdbAndClassifyPermissionList(adminId, 0, 0)
  757. if er != nil {
  758. msg = "获取失败"
  759. err = fmt.Errorf("获取所有有权限的指标和分类失败,Err:" + er.Error())
  760. return
  761. }
  762. // 如果是ES的话,需要重新查一下指标的信息,主要是为了把是否授权字段找出来
  763. if isEs {
  764. edbInfoIdList := make([]int, 0)
  765. for i := 0; i < edbInfoListLen; i++ {
  766. edbInfoIdList = append(edbInfoIdList, edbInfoList[i].EdbInfoId)
  767. tmpEdbInfo := edbInfoList[i]
  768. if currClassify, ok := classifyMap[tmpEdbInfo.ClassifyId]; ok {
  769. edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfo.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfo.EdbInfoId, tmpEdbInfo.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
  770. }
  771. }
  772. tmpEdbList, er := data_manage.GetEdbInfoByIdList(edbInfoIdList)
  773. if er != nil {
  774. msg = "获取失败"
  775. err = fmt.Errorf("获取所有有权限的指标失败,Err:" + er.Error())
  776. return
  777. }
  778. edbInfoMap := make(map[int]*data_manage.EdbInfo)
  779. for _, v := range tmpEdbList {
  780. edbInfoMap[v.EdbInfoId] = v
  781. }
  782. for i := 0; i < edbInfoListLen; i++ {
  783. tmpEdbInfo, ok := edbInfoMap[edbInfoList[i].EdbInfoId]
  784. if !ok {
  785. continue
  786. }
  787. edbInfoList[i].IsJoinPermission = tmpEdbInfo.IsJoinPermission
  788. }
  789. }
  790. // 权限校验
  791. for i := 0; i < edbInfoListLen; i++ {
  792. tmpEdbInfoItem := edbInfoList[i]
  793. if currClassify, ok := classifyMap[tmpEdbInfoItem.ClassifyId]; ok {
  794. edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfoItem.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfoItem.EdbInfoId, tmpEdbInfoItem.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
  795. }
  796. }
  797. }
  798. for i := 0; i < edbInfoListLen; i++ {
  799. for j := 0; j < edbInfoListLen; j++ {
  800. if (edbInfoList[i].EdbNameAlias == edbInfoList[j].EdbNameAlias) &&
  801. (edbInfoList[i].EdbInfoId != edbInfoList[j].EdbInfoId) &&
  802. !(strings.Contains(edbInfoList[i].EdbName, edbInfoList[i].SourceName)) {
  803. edbInfoList[i].EdbName = edbInfoList[i].EdbName + "(" + edbInfoList[i].SourceName + ")"
  804. }
  805. }
  806. }
  807. //新增搜索词记录
  808. {
  809. searchKeyword := new(data_manage.SearchKeyword)
  810. searchKeyword.KeyWord = keyWord
  811. searchKeyword.CreateTime = time.Now()
  812. go data_manage.AddSearchKeyword(searchKeyword)
  813. }
  814. resp = data_manage.EdbInfoFilterDataResp{
  815. Paging: page,
  816. List: edbInfoList,
  817. }
  818. return
  819. }
  820. func SaveStlEdbInfo(req *request.SaveStlEdbInfoReq, adminId int, adminRealName, lang string) (addEdbInfoId int, isSendEmail bool, msg string, err error) {
  821. if req.EdbName == "" {
  822. msg = "指标名称不能为空"
  823. return
  824. }
  825. if req.Unit == "" {
  826. msg = "指标单位不能为空"
  827. return
  828. }
  829. if req.ClassifyId <= 0 {
  830. msg = "请选择分类"
  831. return
  832. }
  833. if req.Frequency == "" {
  834. msg = "指标频度不能为空"
  835. return
  836. }
  837. conf, err := stl.GetCalculateStlConfigById(req.CalculateStlConfigId)
  838. if err != nil {
  839. if err.Error() == utils.ErrNoRow() {
  840. msg = "未找到配置,请先进行计算"
  841. err = fmt.Errorf("配置不存在")
  842. return
  843. }
  844. msg = "获取失败"
  845. return
  846. }
  847. var stlConfig request.StlConfigReq
  848. if err = json.Unmarshal([]byte(conf.Config), &stlConfig); err != nil {
  849. msg = "获取失败"
  850. return
  851. }
  852. var edbInfoData []*response.EdbData
  853. switch req.StlEdbType {
  854. case utils.StlTypeTrend:
  855. // 趋势指标
  856. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  857. msg = "计算已过期,请重新计算"
  858. err = fmt.Errorf("not found")
  859. return
  860. }
  861. trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  862. if er != nil {
  863. msg = "获取失败"
  864. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  865. return
  866. }
  867. if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
  868. msg = "获取失败"
  869. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  870. return
  871. }
  872. case utils.StlTypeSeasonal:
  873. // 季节性指标
  874. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  875. msg = "计算已过期,请重新计算"
  876. err = fmt.Errorf("not found")
  877. return
  878. }
  879. seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  880. if er != nil {
  881. msg = "获取失败"
  882. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  883. return
  884. }
  885. if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
  886. msg = "获取失败"
  887. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  888. return
  889. }
  890. case utils.StlTypeResidual:
  891. // 残差性指标
  892. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  893. msg = "计算已过期,请重新计算"
  894. err = fmt.Errorf("not found")
  895. return
  896. }
  897. residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  898. if er != nil {
  899. msg = "获取失败"
  900. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  901. return
  902. }
  903. if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
  904. msg = "获取失败"
  905. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  906. return
  907. }
  908. case utils.StlTypeNonTrend:
  909. // 残差性指标
  910. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  911. msg = "计算已过期,请重新计算"
  912. err = fmt.Errorf("not found")
  913. return
  914. }
  915. nonTrendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  916. if er != nil {
  917. msg = "获取失败"
  918. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  919. return
  920. }
  921. if er := json.Unmarshal(nonTrendData, &edbInfoData); er != nil {
  922. msg = "获取失败"
  923. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  924. return
  925. }
  926. default:
  927. msg = "获取失败"
  928. err = fmt.Errorf("未知的计算类型")
  929. return
  930. }
  931. var opEdbInfoId int
  932. if req.EdbInfoId > 0 {
  933. opEdbInfoId = req.EdbInfoId
  934. // 检查指标名称是否存在
  935. var condition string
  936. var pars []interface{}
  937. switch lang {
  938. case utils.EnLangVersion:
  939. condition += " AND edb_name_en = ? "
  940. default:
  941. condition += " AND edb_name=? "
  942. }
  943. pars = append(pars, req.EdbName)
  944. existEdbInfo, er := data_manage.GetEdbInfoByCondition(condition, pars)
  945. if er != nil && er.Error() != utils.ErrNoRow() {
  946. msg = "获取失败"
  947. return
  948. }
  949. switch lang {
  950. case utils.EnLangVersion:
  951. if existEdbInfo != nil && existEdbInfo.EdbNameEn == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
  952. msg = "指标名称已存在"
  953. err = fmt.Errorf("指标名称已存在")
  954. return
  955. }
  956. default:
  957. if existEdbInfo != nil && existEdbInfo.EdbName == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
  958. msg = "指标名称已存在"
  959. err = fmt.Errorf("指标名称已存在")
  960. return
  961. }
  962. }
  963. // 更新指标
  964. edbInfo, er := data_manage.GetEdbInfoById(req.EdbInfoId)
  965. if er != nil {
  966. if er.Error() == utils.ErrNoRow() {
  967. msg = "未找到指标,请刷新后重试"
  968. err = er
  969. return
  970. }
  971. msg = "获取失败"
  972. err = er
  973. return
  974. }
  975. var updateCols []string
  976. switch lang {
  977. case utils.EnLangVersion:
  978. if edbInfo.EdbName != req.EdbName {
  979. edbInfo.EdbNameEn = req.EdbName
  980. updateCols = append(updateCols, "edb_name_en")
  981. }
  982. default:
  983. if edbInfo.EdbName != req.EdbName {
  984. edbInfo.EdbName = req.EdbName
  985. updateCols = append(updateCols, "edb_name")
  986. }
  987. }
  988. if edbInfo.ClassifyId != req.ClassifyId {
  989. // 更新分类
  990. maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
  991. if er != nil {
  992. msg = "获取失败"
  993. err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
  994. return
  995. }
  996. edbInfo.ClassifyId = req.ClassifyId
  997. edbInfo.Sort = maxSort + 1
  998. updateCols = append(updateCols, "classify_id", "sort")
  999. }
  1000. if edbInfo.Frequency != req.Frequency {
  1001. edbInfo.Frequency = req.Frequency
  1002. updateCols = append(updateCols, "frequency")
  1003. }
  1004. if edbInfo.Unit != req.Unit {
  1005. edbInfo.Unit = req.Unit
  1006. updateCols = append(updateCols, "unit")
  1007. }
  1008. edbInfo.CalculateFormula = conf.Config
  1009. updateCols = append(updateCols, "calculate_formula")
  1010. if len(updateCols) > 0 {
  1011. edbInfo.ModifyTime = time.Now()
  1012. updateCols = append(updateCols, "modify_time")
  1013. err = edbInfo.Update(updateCols)
  1014. if err != nil {
  1015. msg = "保存失败"
  1016. return
  1017. }
  1018. }
  1019. var dataList []*stl.EdbDataCalculateStl
  1020. for _, v := range edbInfoData {
  1021. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  1022. dataList = append(dataList, &stl.EdbDataCalculateStl{
  1023. EdbInfoId: edbInfo.EdbInfoId,
  1024. EdbCode: edbInfo.EdbCode,
  1025. DataTime: dataTime,
  1026. Value: v.Value,
  1027. CreateTime: time.Now(),
  1028. ModifyTime: time.Now(),
  1029. DataTimestamp: dataTime.UnixMilli(),
  1030. })
  1031. }
  1032. err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
  1033. if err != nil {
  1034. msg = "保存失败"
  1035. return
  1036. }
  1037. data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1038. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1039. if maxAndMinItem != nil {
  1040. err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
  1041. if err != nil {
  1042. msg = "保存失败"
  1043. err = errors.New("保存失败,Err:" + err.Error())
  1044. return
  1045. }
  1046. }
  1047. } else {
  1048. indexObj := new(stl.EdbDataCalculateStl)
  1049. edbCode, er := utils.GenerateEdbCode(1, "stl")
  1050. if er != nil {
  1051. msg = "生成指标代码失败"
  1052. err = fmt.Errorf("生成指标代码失败,Err:" + er.Error())
  1053. return
  1054. }
  1055. //判断指标名称是否存在
  1056. ok, er := CheckDulplicateEdbInfoName(req.EdbName, lang)
  1057. if er != nil {
  1058. msg = "保存失败"
  1059. err = fmt.Errorf("检查指标名称是否存在失败,Err:" + er.Error())
  1060. return
  1061. }
  1062. if ok {
  1063. msg = "指标名称已存在"
  1064. err = fmt.Errorf("指标名称已存在")
  1065. return
  1066. }
  1067. source := utils.DATA_SOURCE_CALCULATE_STL
  1068. subSource := utils.DATA_SUB_SOURCE_EDB
  1069. edbInfo := new(data_manage.EdbInfo)
  1070. //获取该层级下最大的排序数
  1071. maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
  1072. if er != nil {
  1073. msg = "获取失败"
  1074. err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
  1075. return
  1076. }
  1077. edbInfo.EdbCode = edbCode
  1078. edbInfo.EdbName = req.EdbName
  1079. edbInfo.EdbNameEn = req.EdbName
  1080. edbInfo.EdbNameSource = req.EdbName
  1081. edbInfo.Frequency = req.Frequency
  1082. edbInfo.Unit = req.Unit
  1083. edbInfo.UnitEn = req.Unit
  1084. edbInfo.CalculateFormula = conf.Config
  1085. edbInfo.ClassifyId = req.ClassifyId
  1086. edbInfo.SysUserId = adminId
  1087. edbInfo.SysUserRealName = adminRealName
  1088. edbInfo.CreateTime = time.Now()
  1089. edbInfo.ModifyTime = time.Now()
  1090. edbInfo.Sort = maxSort + 1
  1091. edbInfo.DataDateType = `交易日`
  1092. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  1093. edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
  1094. itemVal, er := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
  1095. if itemVal != nil && er == nil {
  1096. edbInfo.MaxValue = itemVal.MaxValue
  1097. edbInfo.MinValue = itemVal.MinValue
  1098. }
  1099. edbInfo.EdbType = 2
  1100. edbInfo.Source = source
  1101. edbInfo.SubSource = subSource
  1102. edbInfo.SourceName = "STL趋势分解"
  1103. extra, _ := json.Marshal(req)
  1104. edbInfo.Extra = string(extra)
  1105. edbInfoId, er := data_manage.AddEdbInfo(edbInfo)
  1106. if er != nil {
  1107. msg = "保存失败"
  1108. err = errors.New("保存失败,Err:" + er.Error())
  1109. return
  1110. }
  1111. edbInfo.EdbInfoId = int(edbInfoId)
  1112. var dataList []*stl.EdbDataCalculateStl
  1113. for _, v := range edbInfoData {
  1114. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  1115. dataList = append(dataList, &stl.EdbDataCalculateStl{
  1116. EdbInfoId: int(edbInfoId),
  1117. EdbCode: edbCode,
  1118. DataTime: dataTime,
  1119. Value: v.Value,
  1120. CreateTime: time.Now(),
  1121. ModifyTime: time.Now(),
  1122. DataTimestamp: dataTime.UnixMilli(),
  1123. })
  1124. }
  1125. err = indexObj.BatchInsert(dataList)
  1126. if err != nil {
  1127. msg = "保存失败"
  1128. return
  1129. }
  1130. //保存数据
  1131. data_manage.ModifyEdbInfoDataStatus(edbInfoId, source, subSource, edbCode)
  1132. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
  1133. if maxAndMinItem != nil {
  1134. err = data_manage.ModifyEdbInfoMaxAndMinInfo(int(edbInfoId), maxAndMinItem)
  1135. if err != nil {
  1136. msg = "保存失败"
  1137. err = errors.New("保存失败,Err:" + err.Error())
  1138. return
  1139. }
  1140. }
  1141. // 保存配置映射
  1142. {
  1143. stlMapping := new(stl.CalculateStlConfigMapping)
  1144. stlMapping.EdbInfoId = int(edbInfoId)
  1145. stlMapping.CalculateStlConfigId = req.CalculateStlConfigId
  1146. stlMapping.StlEdbType = req.StlEdbType
  1147. stlMapping.CreateTime = time.Now()
  1148. stlMapping.ModifyTime = time.Now()
  1149. _, err = stlMapping.Insert()
  1150. if err != nil {
  1151. msg = "保存失败"
  1152. err = errors.New("保存配置映射失败,Err:" + err.Error())
  1153. return
  1154. }
  1155. }
  1156. // 保存溯源信息
  1157. {
  1158. fromEdbInfo, er := data_manage.GetEdbInfoById(stlConfig.EdbInfoId)
  1159. if er != nil {
  1160. if er.Error() == utils.ErrNoRow() {
  1161. msg = "未找到指标,请刷新后重试"
  1162. err = fmt.Errorf("指标不存在,err:" + er.Error())
  1163. return
  1164. }
  1165. msg = "获取失败"
  1166. err = er
  1167. return
  1168. }
  1169. edbCalculateMappingInfo := new(data_manage.EdbInfoCalculateMapping)
  1170. edbCalculateMappingInfo.EdbInfoId = int(edbInfoId)
  1171. edbCalculateMappingInfo.Source = source
  1172. edbCalculateMappingInfo.SourceName = "STL趋势分解"
  1173. edbCalculateMappingInfo.EdbCode = edbCode
  1174. edbCalculateMappingInfo.FromEdbInfoId = fromEdbInfo.EdbInfoId
  1175. edbCalculateMappingInfo.FromEdbCode = fromEdbInfo.EdbCode
  1176. edbCalculateMappingInfo.FromEdbName = fromEdbInfo.EdbName
  1177. edbCalculateMappingInfo.FromSource = fromEdbInfo.Source
  1178. edbCalculateMappingInfo.FromSourceName = fromEdbInfo.SourceName
  1179. edbCalculateMappingInfo.CreateTime = time.Now()
  1180. edbCalculateMappingInfo.ModifyTime = time.Now()
  1181. err = edbCalculateMappingInfo.Insert()
  1182. if err != nil {
  1183. msg = "保存失败"
  1184. err = errors.New("保存溯源信息失败,Err:" + err.Error())
  1185. return
  1186. }
  1187. }
  1188. //添加es
  1189. data.AddOrEditEdbInfoToEs(int(edbInfoId))
  1190. opEdbInfoId = int(edbInfoId)
  1191. }
  1192. // 更新关联的同配置的指标
  1193. err = SyncUpdateRelationEdbInfo(req.CalculateStlConfigId, opEdbInfoId)
  1194. if err != nil {
  1195. msg = "更新关联的同配置的指标失败"
  1196. return
  1197. }
  1198. addEdbInfoId = opEdbInfoId
  1199. return
  1200. }
  1201. func SyncUpdateRelationEdbInfo(configId int, excludeId int) (err error) {
  1202. mappingList, err := stl.GetCalculateStlConfigMappingByConfigId(configId)
  1203. if err != nil {
  1204. return
  1205. }
  1206. conf, err := stl.GetCalculateStlConfigById(configId)
  1207. if err != nil {
  1208. return
  1209. }
  1210. for _, v := range mappingList {
  1211. edbInfo, er := data_manage.GetEdbInfoById(v.EdbInfoId)
  1212. if er != nil {
  1213. continue
  1214. }
  1215. if v.EdbInfoId == excludeId {
  1216. continue
  1217. }
  1218. var edbInfoData []*response.EdbData
  1219. switch v.StlEdbType {
  1220. case utils.StlTypeTrend:
  1221. // 趋势指标
  1222. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1223. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1224. continue
  1225. }
  1226. trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1227. if er != nil {
  1228. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1229. continue
  1230. }
  1231. if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
  1232. utils.FileLog.Info("redis获取解析, body:%s,err:%s", string(trendData), er.Error())
  1233. continue
  1234. }
  1235. case utils.StlTypeSeasonal:
  1236. // 季节性指标
  1237. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1238. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1239. continue
  1240. }
  1241. seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1242. if er != nil {
  1243. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1244. continue
  1245. }
  1246. if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
  1247. utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(seasonalData), er.Error())
  1248. continue
  1249. }
  1250. case utils.StlTypeResidual:
  1251. // 残差性指标
  1252. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1253. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1254. continue
  1255. }
  1256. residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1257. if er != nil {
  1258. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1259. continue
  1260. }
  1261. if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
  1262. utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(residualData), er.Error())
  1263. continue
  1264. }
  1265. case utils.StlTypeNonTrend:
  1266. // 残差性指标
  1267. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1268. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1269. continue
  1270. }
  1271. nonTrendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1272. if er != nil {
  1273. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_NonTrend_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1274. continue
  1275. }
  1276. if er := json.Unmarshal(nonTrendData, &edbInfoData); er != nil {
  1277. utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(nonTrendData), er.Error())
  1278. continue
  1279. }
  1280. default:
  1281. utils.FileLog.Info("未知的stlEdbType类型, mapping:%v", v)
  1282. continue
  1283. }
  1284. var dataList []*stl.EdbDataCalculateStl
  1285. for _, v := range edbInfoData {
  1286. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  1287. dataList = append(dataList, &stl.EdbDataCalculateStl{
  1288. EdbInfoId: edbInfo.EdbInfoId,
  1289. EdbCode: edbInfo.EdbCode,
  1290. DataTime: dataTime,
  1291. Value: v.Value,
  1292. CreateTime: time.Now(),
  1293. ModifyTime: time.Now(),
  1294. DataTimestamp: dataTime.UnixMilli(),
  1295. })
  1296. }
  1297. err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
  1298. if err != nil {
  1299. return
  1300. }
  1301. data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1302. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1303. if maxAndMinItem != nil {
  1304. err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
  1305. if err != nil {
  1306. return
  1307. }
  1308. }
  1309. edbInfo.CalculateFormula = conf.Config
  1310. edbInfo.ModifyTime = time.Now()
  1311. err = edbInfo.Update([]string{"calculate_formula", "modify_time"})
  1312. if err != nil {
  1313. return
  1314. }
  1315. }
  1316. return
  1317. }
  1318. func GetStlConfig(edbInfoId int) (resp *response.StlConfigResp, msg string, err error) {
  1319. configId, err := stl.GetCalculateStlConfigMappingIdByEdbInfoId(edbInfoId)
  1320. if err != nil {
  1321. if err.Error() == utils.ErrNoRow() {
  1322. msg = "未找到指标信息, 请选择其他指标"
  1323. return
  1324. }
  1325. msg = "查询失败"
  1326. return
  1327. }
  1328. queryEdbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
  1329. if err != nil {
  1330. if err.Error() == utils.ErrNoRow() {
  1331. msg = "未找到指标,请刷新后重试"
  1332. return
  1333. }
  1334. msg = "获取失败"
  1335. return
  1336. }
  1337. var req request.StlConfigReq
  1338. if err = json.Unmarshal([]byte(queryEdbInfo.CalculateFormula), &req); err != nil {
  1339. msg = "获取失败"
  1340. return
  1341. }
  1342. edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
  1343. if err != nil {
  1344. if err.Error() == utils.ErrNoRow() {
  1345. msg = "未找到指标,请刷新后重试"
  1346. return
  1347. }
  1348. msg = "获取失败"
  1349. return
  1350. }
  1351. resp = &response.StlConfigResp{
  1352. CalculateStlConfigId: configId,
  1353. EdbInfoId: req.EdbInfoId,
  1354. EdbInfoName: edbInfo.EdbName,
  1355. DataRangeType: req.DataRangeType,
  1356. StartDate: req.StartDate,
  1357. EndDate: req.EndDate,
  1358. LastNYear: req.LastNYear,
  1359. Period: req.Period,
  1360. Seasonal: req.Seasonal,
  1361. Trend: req.Trend,
  1362. Fraction: req.Fraction,
  1363. Robust: req.Robust,
  1364. TrendDeg: req.TrendDeg,
  1365. SeasonalDeg: req.SeasonalDeg,
  1366. LowPassDeg: req.LowPassDeg,
  1367. }
  1368. return
  1369. }
  1370. func CheckDulplicateEdbInfoName(edbName, lang string) (ok bool, err error) {
  1371. var count int
  1372. var condition string
  1373. var pars []interface{}
  1374. switch lang {
  1375. case utils.EnLangVersion:
  1376. condition += " AND edb_name_en = ? "
  1377. default:
  1378. condition += " AND edb_name=? "
  1379. }
  1380. pars = append(pars, edbName)
  1381. count, err = data_manage.GetEdbInfoCountByCondition(condition, pars)
  1382. if err != nil {
  1383. return
  1384. }
  1385. if count > 0 {
  1386. ok = true
  1387. return
  1388. }
  1389. return
  1390. }