stl.go 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347
  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 json
  409. import warnings
  410. warnings.filterwarnings('ignore')
  411. import pandas as pd
  412. from statsmodels.tsa.seasonal import STL
  413. from statsmodels.nonparametric.smoothers_lowess import lowess
  414. from statsmodels.tsa.stattools import adfuller
  415. from statsmodels.stats.diagnostic import acorr_ljungbox
  416. import numpy as np
  417. file_path = r"%s"
  418. df = pd.read_excel(file_path, parse_dates=['日期'], engine='openpyxl')
  419. df.set_index('日期', inplace=True)
  420. df = df[df.index.notna()]
  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. utils.FileLog.Info("stl exec python code:%s", pythonCode)
  483. cmd := exec.Command(`python3`, "-c", pythonCode)
  484. output, err := cmd.CombinedOutput()
  485. if err != nil {
  486. utils.FileLog.Info(`execStlPythonCode error:%s, input: path:%s, toPath:%s, period:%d, seasonal:%d, trend:%d, trendDeg:%d, seasonalDeg:%d, lowPassDeg:%d, fraction:%g, robust:%s, output:%s`, err.Error(), path, toPath, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg, fraction, robustStr, string(output))
  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, trend:%d, trendDeg:%d, seasonalDeg:%d, lowPassDeg:%d, fraction:%g, robust:%s, output:%s`, err.Error(), path, toPath, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg, fraction, robustStr, string(output))
  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. }