stl.go 42 KB

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