stl.go 42 KB

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