stl.go 41 KB

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