stl.go 42 KB

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