stl.go 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355
  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. var cmd *exec.Cmd
  487. if utils.RunMode == "debug" {
  488. cmd = exec.Command(`D:\conda\envs\py36\python`, "-c", pythonCode)
  489. } else {
  490. cmd = exec.Command(`python3`, "-c", pythonCode)
  491. }
  492. output, err := cmd.CombinedOutput()
  493. if err != nil {
  494. 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))
  495. return
  496. }
  497. defer cmd.Process.Kill()
  498. if err = json.Unmarshal(output, &stlResult); err != nil {
  499. 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))
  500. return
  501. }
  502. return
  503. }
  504. func SaveStlConfig(req *request.StlConfigReq, adminId int) (configId int64, msg string, err error) {
  505. edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
  506. if err != nil {
  507. if err.Error() == utils.ErrNoRow() {
  508. msg = "指标不存在"
  509. return
  510. }
  511. msg = "获取指标信息失败"
  512. return
  513. }
  514. var condition string
  515. var pars []interface{}
  516. switch req.DataRangeType {
  517. case ALL_DATE:
  518. case LAST_N_YEARS:
  519. condition += " AND data_time >=?"
  520. year := time.Now().Year()
  521. lastNyear, er := strconv.Atoi(req.LastNYear)
  522. if er != nil {
  523. msg = "最近N年输入不合法"
  524. err = er
  525. return
  526. }
  527. lastDate := time.Date(year-lastNyear, 1, 1, 0, 0, 0, 0, time.Local)
  528. pars = append(pars, lastDate)
  529. case RANGE_DATE:
  530. condition = " AND data_time >=? AND data_time <=?"
  531. pars = append(pars, req.StartDate, req.EndDate)
  532. case RANGE_DATE_TO_NOW:
  533. condition = " AND data_time >=?"
  534. pars = append(pars, req.StartDate)
  535. }
  536. condition += " AND edb_code =?"
  537. pars = append(pars, edbInfo.EdbCode)
  538. edbData, err := data_manage.GetAllEdbDataListByCondition(condition, pars, edbInfo.Source, edbInfo.SubSource)
  539. if err != nil {
  540. msg = "获取指标数据失败"
  541. return
  542. }
  543. var condMsg string
  544. if req.Period < 2 || req.Period > len(edbData) {
  545. condMsg += "period必须是一个大于等于2的正整数,且必须小于时间序列的长度"
  546. }
  547. if req.Seasonal < 3 || req.Seasonal%2 == 0 || req.Seasonal <= req.Period {
  548. if condMsg != "" {
  549. condMsg += "\n"
  550. }
  551. condMsg += "seasonal必须是一个大于等于3的奇整数,且必须大于period"
  552. }
  553. if req.Trend < 3 || req.Trend%2 == 0 || req.Trend <= req.Period {
  554. if condMsg != "" {
  555. condMsg += "\n"
  556. }
  557. condMsg += "trend必须是一个大于等于3的奇整数,且必须大于period"
  558. }
  559. if req.Fraction < 0 || req.Fraction > 1 {
  560. if condMsg != "" {
  561. condMsg += "\n"
  562. }
  563. condMsg += "fraction必须是一个介于[0-1]之间"
  564. }
  565. if 1 > req.TrendDeg || req.TrendDeg > 5 {
  566. if condMsg != "" {
  567. condMsg += "\n"
  568. }
  569. condMsg += "trend_deg请设置成1-5的整数"
  570. }
  571. if 1 > req.SeasonalDeg || req.SeasonalDeg > 5 {
  572. if condMsg != "" {
  573. condMsg += "\n"
  574. }
  575. condMsg += "seasonal_deg请设置成1-5的整数"
  576. }
  577. if 1 > req.LowPassDeg || req.LowPassDeg > 5 {
  578. if condMsg != "" {
  579. condMsg += "\n"
  580. }
  581. condMsg += "low_pass_deg请设置成1-5的整数"
  582. }
  583. if condMsg != "" {
  584. msg = condMsg
  585. err = fmt.Errorf("参数错误")
  586. return
  587. }
  588. b, err := json.Marshal(req)
  589. if err != nil {
  590. return
  591. }
  592. conf := new(stl.CalculateStlConfig)
  593. if req.CalculateStlConfigId > 0 {
  594. conf.CalculateStlConfigId = req.CalculateStlConfigId
  595. conf.Config = string(b)
  596. conf.ModifyTime = time.Now()
  597. err = conf.Update([]string{"Config", "ModifyTime"})
  598. configId = int64(req.CalculateStlConfigId)
  599. } else {
  600. conf.Config = string(b)
  601. conf.SysUserId = adminId
  602. conf.CreateTime = time.Now()
  603. conf.ModifyTime = time.Now()
  604. configId, err = conf.Insert()
  605. }
  606. return
  607. }
  608. func SearchEdbInfoWithStl(adminId int, keyWord string, currentIndex, pageSize int) (resp data_manage.EdbInfoFilterDataResp, msg string, err error) {
  609. var edbInfoList []*data_manage.EdbInfoList
  610. noPermissionEdbInfoIdList := make([]int, 0) //无权限指标
  611. // 获取当前账号的不可见指标
  612. {
  613. obj := data_manage.EdbInfoNoPermissionAdmin{}
  614. confList, er := obj.GetAllListByAdminId(adminId)
  615. if er != nil && er.Error() != utils.ErrNoRow() {
  616. msg = "获取失败"
  617. err = fmt.Errorf("获取不可见指标配置数据失败,Err:" + er.Error())
  618. return
  619. }
  620. for _, v := range confList {
  621. noPermissionEdbInfoIdList = append(noPermissionEdbInfoIdList, v.EdbInfoId)
  622. }
  623. }
  624. if currentIndex <= 0 {
  625. currentIndex = 1
  626. }
  627. startSize := utils.StartIndex(currentIndex, pageSize)
  628. // 是否走ES
  629. isEs := false
  630. var total int64
  631. if keyWord != "" {
  632. frequencyList := []string{"日度", "周度", "旬度", "月度", "季度"}
  633. // 普通的搜索
  634. total, edbInfoList, err = elastic.SearchEdbInfoDataByfrequency(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, 0, frequencyList, noPermissionEdbInfoIdList)
  635. isEs = true
  636. } else {
  637. var condition string
  638. var pars []interface{}
  639. // 普通指标
  640. condition += ` AND edb_info_type = ? `
  641. pars = append(pars, 0)
  642. // 无权限指标id
  643. lenNoPermissionEdbInfoIdList := len(noPermissionEdbInfoIdList)
  644. if lenNoPermissionEdbInfoIdList > 0 {
  645. condition += ` AND edb_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionEdbInfoIdList) + `) `
  646. pars = append(pars, noPermissionEdbInfoIdList)
  647. }
  648. //频度
  649. condition += ` AND frequency IN ('日度', '周度', '旬度', '月度', '季度') `
  650. total, edbInfoList, err = data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
  651. }
  652. if err != nil {
  653. edbInfoList = make([]*data_manage.EdbInfoList, 0)
  654. }
  655. page := paging.GetPaging(currentIndex, pageSize, int(total))
  656. edbInfoListLen := len(edbInfoList)
  657. classifyIdList := make([]int, 0)
  658. for i := 0; i < edbInfoListLen; i++ {
  659. edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
  660. classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
  661. }
  662. // 当前列表中的分类map
  663. classifyMap := make(map[int]*data_manage.EdbClassify)
  664. if edbInfoListLen > 0 {
  665. classifyList, er := data_manage.GetEdbClassifyByIdList(classifyIdList)
  666. if er != nil {
  667. msg = "获取失败"
  668. err = fmt.Errorf("获取分类列表失败,Err:" + er.Error())
  669. return
  670. }
  671. for _, v := range classifyList {
  672. classifyMap[v.ClassifyId] = v
  673. }
  674. // 获取所有有权限的指标和分类
  675. permissionEdbIdList, permissionClassifyIdList, er := data_manage_permission.GetUserEdbAndClassifyPermissionList(adminId, 0, 0)
  676. if er != nil {
  677. msg = "获取失败"
  678. err = fmt.Errorf("获取所有有权限的指标和分类失败,Err:" + er.Error())
  679. return
  680. }
  681. // 如果是ES的话,需要重新查一下指标的信息,主要是为了把是否授权字段找出来
  682. if isEs {
  683. edbInfoIdList := make([]int, 0)
  684. for i := 0; i < edbInfoListLen; i++ {
  685. edbInfoIdList = append(edbInfoIdList, edbInfoList[i].EdbInfoId)
  686. tmpEdbInfo := edbInfoList[i]
  687. if currClassify, ok := classifyMap[tmpEdbInfo.ClassifyId]; ok {
  688. edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfo.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfo.EdbInfoId, tmpEdbInfo.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
  689. }
  690. }
  691. tmpEdbList, er := data_manage.GetEdbInfoByIdList(edbInfoIdList)
  692. if er != nil {
  693. msg = "获取失败"
  694. err = fmt.Errorf("获取所有有权限的指标失败,Err:" + er.Error())
  695. return
  696. }
  697. edbInfoMap := make(map[int]*data_manage.EdbInfo)
  698. for _, v := range tmpEdbList {
  699. edbInfoMap[v.EdbInfoId] = v
  700. }
  701. for i := 0; i < edbInfoListLen; i++ {
  702. tmpEdbInfo, ok := edbInfoMap[edbInfoList[i].EdbInfoId]
  703. if !ok {
  704. continue
  705. }
  706. edbInfoList[i].IsJoinPermission = tmpEdbInfo.IsJoinPermission
  707. }
  708. }
  709. // 权限校验
  710. for i := 0; i < edbInfoListLen; i++ {
  711. tmpEdbInfoItem := edbInfoList[i]
  712. if currClassify, ok := classifyMap[tmpEdbInfoItem.ClassifyId]; ok {
  713. edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfoItem.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfoItem.EdbInfoId, tmpEdbInfoItem.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
  714. }
  715. }
  716. }
  717. for i := 0; i < edbInfoListLen; i++ {
  718. for j := 0; j < edbInfoListLen; j++ {
  719. if (edbInfoList[i].EdbNameAlias == edbInfoList[j].EdbNameAlias) &&
  720. (edbInfoList[i].EdbInfoId != edbInfoList[j].EdbInfoId) &&
  721. !(strings.Contains(edbInfoList[i].EdbName, edbInfoList[i].SourceName)) {
  722. edbInfoList[i].EdbName = edbInfoList[i].EdbName + "(" + edbInfoList[i].SourceName + ")"
  723. }
  724. }
  725. }
  726. //新增搜索词记录
  727. {
  728. searchKeyword := new(data_manage.SearchKeyword)
  729. searchKeyword.KeyWord = keyWord
  730. searchKeyword.CreateTime = time.Now()
  731. go data_manage.AddSearchKeyword(searchKeyword)
  732. }
  733. resp = data_manage.EdbInfoFilterDataResp{
  734. Paging: page,
  735. List: edbInfoList,
  736. }
  737. return
  738. }
  739. func SaveStlEdbInfo(req *request.SaveStlEdbInfoReq, adminId int, adminRealName, lang string) (addEdbInfoId int, isSendEmail bool, msg string, err error) {
  740. if req.EdbName == "" {
  741. msg = "指标名称不能为空"
  742. return
  743. }
  744. if req.Unit == "" {
  745. msg = "指标单位不能为空"
  746. return
  747. }
  748. if req.ClassifyId <= 0 {
  749. msg = "请选择分类"
  750. return
  751. }
  752. if req.Frequency == "" {
  753. msg = "指标频度不能为空"
  754. return
  755. }
  756. conf, err := stl.GetCalculateStlConfigById(req.CalculateStlConfigId)
  757. if err != nil {
  758. if err.Error() == utils.ErrNoRow() {
  759. msg = "未找到配置,请先进行计算"
  760. err = fmt.Errorf("配置不存在")
  761. return
  762. }
  763. msg = "获取失败"
  764. return
  765. }
  766. var stlConfig request.StlConfigReq
  767. if err = json.Unmarshal([]byte(conf.Config), &stlConfig); err != nil {
  768. msg = "获取失败"
  769. return
  770. }
  771. var edbInfoData []*response.EdbData
  772. switch req.StlEdbType {
  773. case 1:
  774. // 趋势指标
  775. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  776. msg = "计算已过期,请重新计算"
  777. err = fmt.Errorf("not found")
  778. return
  779. }
  780. trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  781. if er != nil {
  782. msg = "获取失败"
  783. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  784. return
  785. }
  786. if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
  787. msg = "获取失败"
  788. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  789. return
  790. }
  791. case 2:
  792. // 季节性指标
  793. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  794. msg = "计算已过期,请重新计算"
  795. err = fmt.Errorf("not found")
  796. return
  797. }
  798. seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  799. if er != nil {
  800. msg = "获取失败"
  801. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  802. return
  803. }
  804. if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
  805. msg = "获取失败"
  806. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  807. return
  808. }
  809. case 3:
  810. // 残差性指标
  811. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
  812. msg = "计算已过期,请重新计算"
  813. err = fmt.Errorf("not found")
  814. return
  815. }
  816. residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
  817. if er != nil {
  818. msg = "获取失败"
  819. err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
  820. return
  821. }
  822. if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
  823. msg = "获取失败"
  824. err = fmt.Errorf("json解析失败,Err:" + er.Error())
  825. return
  826. }
  827. default:
  828. msg = "获取失败"
  829. err = fmt.Errorf("未知的计算类型")
  830. return
  831. }
  832. var opEdbInfoId int
  833. if req.EdbInfoId > 0 {
  834. opEdbInfoId = req.EdbInfoId
  835. // 检查指标名称是否存在
  836. var condition string
  837. var pars []interface{}
  838. switch lang {
  839. case utils.EnLangVersion:
  840. condition += " AND edb_name_en = ? "
  841. default:
  842. condition += " AND edb_name=? "
  843. }
  844. pars = append(pars, req.EdbName)
  845. existEdbInfo, er := data_manage.GetEdbInfoByCondition(condition, pars)
  846. if er != nil && er.Error() != utils.ErrNoRow() {
  847. msg = "获取失败"
  848. return
  849. }
  850. switch lang {
  851. case utils.EnLangVersion:
  852. if existEdbInfo != nil && existEdbInfo.EdbNameEn == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
  853. msg = "指标名称已存在"
  854. err = fmt.Errorf("指标名称已存在")
  855. return
  856. }
  857. default:
  858. if existEdbInfo != nil && existEdbInfo.EdbName == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
  859. msg = "指标名称已存在"
  860. err = fmt.Errorf("指标名称已存在")
  861. return
  862. }
  863. }
  864. // 更新指标
  865. edbInfo, er := data_manage.GetEdbInfoById(req.EdbInfoId)
  866. if er != nil {
  867. if er.Error() == utils.ErrNoRow() {
  868. msg = "未找到指标,请刷新后重试"
  869. err = er
  870. return
  871. }
  872. msg = "获取失败"
  873. err = er
  874. return
  875. }
  876. var updateCols []string
  877. switch lang {
  878. case utils.EnLangVersion:
  879. if edbInfo.EdbName != req.EdbName {
  880. edbInfo.EdbNameEn = req.EdbName
  881. updateCols = append(updateCols, "edb_name_en")
  882. }
  883. default:
  884. if edbInfo.EdbName != req.EdbName {
  885. edbInfo.EdbName = req.EdbName
  886. updateCols = append(updateCols, "edb_name")
  887. }
  888. }
  889. if edbInfo.ClassifyId != req.ClassifyId {
  890. // 更新分类
  891. maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
  892. if er != nil {
  893. msg = "获取失败"
  894. err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
  895. return
  896. }
  897. edbInfo.ClassifyId = req.ClassifyId
  898. edbInfo.Sort = maxSort + 1
  899. updateCols = append(updateCols, "classify_id", "sort")
  900. }
  901. if edbInfo.Frequency != req.Frequency {
  902. edbInfo.Frequency = req.Frequency
  903. updateCols = append(updateCols, "frequency")
  904. }
  905. if edbInfo.Unit != req.Unit {
  906. edbInfo.Unit = req.Unit
  907. updateCols = append(updateCols, "unit")
  908. }
  909. edbInfo.CalculateFormula = conf.Config
  910. updateCols = append(updateCols, "calculate_formula")
  911. if len(updateCols) > 0 {
  912. edbInfo.ModifyTime = time.Now()
  913. updateCols = append(updateCols, "modify_time")
  914. err = edbInfo.Update(updateCols)
  915. if err != nil {
  916. msg = "保存失败"
  917. return
  918. }
  919. }
  920. var dataList []*stl.EdbDataCalculateStl
  921. for _, v := range edbInfoData {
  922. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  923. dataList = append(dataList, &stl.EdbDataCalculateStl{
  924. EdbInfoId: edbInfo.EdbInfoId,
  925. EdbCode: edbInfo.EdbCode,
  926. DataTime: dataTime,
  927. Value: v.Value,
  928. CreateTime: time.Now(),
  929. ModifyTime: time.Now(),
  930. DataTimestamp: dataTime.UnixMilli(),
  931. })
  932. }
  933. err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
  934. if err != nil {
  935. msg = "保存失败"
  936. return
  937. }
  938. data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  939. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  940. if maxAndMinItem != nil {
  941. err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
  942. if err != nil {
  943. msg = "保存失败"
  944. err = errors.New("保存失败,Err:" + err.Error())
  945. return
  946. }
  947. }
  948. } else {
  949. indexObj := new(stl.EdbDataCalculateStl)
  950. edbCode, er := utils.GenerateEdbCode(1, "stl")
  951. if er != nil {
  952. msg = "生成指标代码失败"
  953. err = fmt.Errorf("生成指标代码失败,Err:" + er.Error())
  954. return
  955. }
  956. //判断指标名称是否存在
  957. ok, er := CheckDulplicateEdbInfoName(req.EdbName, lang)
  958. if er != nil {
  959. msg = "保存失败"
  960. err = fmt.Errorf("检查指标名称是否存在失败,Err:" + er.Error())
  961. return
  962. }
  963. if ok {
  964. msg = "指标名称已存在"
  965. err = fmt.Errorf("指标名称已存在")
  966. return
  967. }
  968. source := utils.DATA_SOURCE_CALCULATE_STL
  969. subSource := utils.DATA_SUB_SOURCE_EDB
  970. edbInfo := new(data_manage.EdbInfo)
  971. //获取该层级下最大的排序数
  972. maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
  973. if er != nil {
  974. msg = "获取失败"
  975. err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
  976. return
  977. }
  978. edbInfo.EdbCode = edbCode
  979. edbInfo.EdbName = req.EdbName
  980. edbInfo.EdbNameEn = req.EdbName
  981. edbInfo.EdbNameSource = req.EdbName
  982. edbInfo.Frequency = req.Frequency
  983. edbInfo.Unit = req.Unit
  984. edbInfo.UnitEn = req.Unit
  985. edbInfo.CalculateFormula = conf.Config
  986. edbInfo.ClassifyId = req.ClassifyId
  987. edbInfo.SysUserId = adminId
  988. edbInfo.SysUserRealName = adminRealName
  989. edbInfo.CreateTime = time.Now()
  990. edbInfo.ModifyTime = time.Now()
  991. edbInfo.Sort = maxSort + 1
  992. edbInfo.DataDateType = `交易日`
  993. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  994. edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
  995. itemVal, er := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
  996. if itemVal != nil && er == nil {
  997. edbInfo.MaxValue = itemVal.MaxValue
  998. edbInfo.MinValue = itemVal.MinValue
  999. }
  1000. edbInfo.EdbType = 2
  1001. edbInfo.Source = source
  1002. edbInfo.SubSource = subSource
  1003. edbInfo.SourceName = "STL趋势分解"
  1004. extra, _ := json.Marshal(req)
  1005. edbInfo.Extra = string(extra)
  1006. edbInfoId, er := data_manage.AddEdbInfo(edbInfo)
  1007. if er != nil {
  1008. msg = "保存失败"
  1009. err = errors.New("保存失败,Err:" + er.Error())
  1010. return
  1011. }
  1012. edbInfo.EdbInfoId = int(edbInfoId)
  1013. var dataList []*stl.EdbDataCalculateStl
  1014. for _, v := range edbInfoData {
  1015. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  1016. dataList = append(dataList, &stl.EdbDataCalculateStl{
  1017. EdbInfoId: int(edbInfoId),
  1018. EdbCode: edbCode,
  1019. DataTime: dataTime,
  1020. Value: v.Value,
  1021. CreateTime: time.Now(),
  1022. ModifyTime: time.Now(),
  1023. DataTimestamp: dataTime.UnixMilli(),
  1024. })
  1025. }
  1026. err = indexObj.BatchInsert(dataList)
  1027. if err != nil {
  1028. msg = "保存失败"
  1029. return
  1030. }
  1031. //保存数据
  1032. data_manage.ModifyEdbInfoDataStatus(edbInfoId, source, subSource, edbCode)
  1033. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
  1034. if maxAndMinItem != nil {
  1035. err = data_manage.ModifyEdbInfoMaxAndMinInfo(int(edbInfoId), maxAndMinItem)
  1036. if err != nil {
  1037. msg = "保存失败"
  1038. err = errors.New("保存失败,Err:" + err.Error())
  1039. return
  1040. }
  1041. }
  1042. // 保存配置映射
  1043. {
  1044. stlMapping := new(stl.CalculateStlConfigMapping)
  1045. stlMapping.EdbInfoId = int(edbInfoId)
  1046. stlMapping.CalculateStlConfigId = req.CalculateStlConfigId
  1047. stlMapping.StlEdbType = req.StlEdbType
  1048. stlMapping.CreateTime = time.Now()
  1049. stlMapping.ModifyTime = time.Now()
  1050. _, err = stlMapping.Insert()
  1051. if err != nil {
  1052. msg = "保存失败"
  1053. err = errors.New("保存配置映射失败,Err:" + err.Error())
  1054. return
  1055. }
  1056. }
  1057. // 保存溯源信息
  1058. {
  1059. fromEdbInfo, er := data_manage.GetEdbInfoById(stlConfig.EdbInfoId)
  1060. if er != nil {
  1061. if er.Error() == utils.ErrNoRow() {
  1062. msg = "未找到指标,请刷新后重试"
  1063. err = fmt.Errorf("指标不存在,err:" + er.Error())
  1064. return
  1065. }
  1066. msg = "获取失败"
  1067. err = er
  1068. return
  1069. }
  1070. edbCalculateMappingInfo := new(data_manage.EdbInfoCalculateMapping)
  1071. edbCalculateMappingInfo.EdbInfoId = int(edbInfoId)
  1072. edbCalculateMappingInfo.Source = source
  1073. edbCalculateMappingInfo.SourceName = "STL趋势分解"
  1074. edbCalculateMappingInfo.EdbCode = edbCode
  1075. edbCalculateMappingInfo.FromEdbInfoId = fromEdbInfo.EdbInfoId
  1076. edbCalculateMappingInfo.FromEdbCode = fromEdbInfo.EdbCode
  1077. edbCalculateMappingInfo.FromEdbName = fromEdbInfo.EdbName
  1078. edbCalculateMappingInfo.FromSource = fromEdbInfo.Source
  1079. edbCalculateMappingInfo.FromSourceName = fromEdbInfo.SourceName
  1080. edbCalculateMappingInfo.CreateTime = time.Now()
  1081. edbCalculateMappingInfo.ModifyTime = time.Now()
  1082. err = edbCalculateMappingInfo.Insert()
  1083. if err != nil {
  1084. msg = "保存失败"
  1085. err = errors.New("保存溯源信息失败,Err:" + err.Error())
  1086. return
  1087. }
  1088. }
  1089. //添加es
  1090. data.AddOrEditEdbInfoToEs(int(edbInfoId))
  1091. opEdbInfoId = int(edbInfoId)
  1092. }
  1093. // 更新关联的同配置的指标
  1094. err = SyncUpdateRelationEdbInfo(req.CalculateStlConfigId, opEdbInfoId)
  1095. if err != nil {
  1096. msg = "更新关联的同配置的指标失败"
  1097. return
  1098. }
  1099. addEdbInfoId = opEdbInfoId
  1100. return
  1101. }
  1102. func SyncUpdateRelationEdbInfo(configId int, excludeId int) (err error) {
  1103. mappingList, err := stl.GetCalculateStlConfigMappingByConfigId(configId)
  1104. if err != nil {
  1105. return
  1106. }
  1107. conf, err := stl.GetCalculateStlConfigById(configId)
  1108. if err != nil {
  1109. return
  1110. }
  1111. for _, v := range mappingList {
  1112. edbInfo, er := data_manage.GetEdbInfoById(v.EdbInfoId)
  1113. if er != nil {
  1114. continue
  1115. }
  1116. if v.EdbInfoId == excludeId {
  1117. continue
  1118. }
  1119. var edbInfoData []*response.EdbData
  1120. switch v.StlEdbType {
  1121. case 1:
  1122. // 趋势指标
  1123. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1124. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1125. continue
  1126. }
  1127. trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1128. if er != nil {
  1129. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1130. continue
  1131. }
  1132. if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
  1133. utils.FileLog.Info("redis获取解析, body:%s,err:%s", string(trendData), er.Error())
  1134. continue
  1135. }
  1136. case 2:
  1137. // 季节性指标
  1138. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1139. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1140. continue
  1141. }
  1142. seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1143. if er != nil {
  1144. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1145. continue
  1146. }
  1147. if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
  1148. utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(seasonalData), er.Error())
  1149. continue
  1150. }
  1151. case 3:
  1152. // 残差性指标
  1153. if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
  1154. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
  1155. continue
  1156. }
  1157. residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
  1158. if er != nil {
  1159. utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
  1160. continue
  1161. }
  1162. if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
  1163. utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(residualData), er.Error())
  1164. continue
  1165. }
  1166. default:
  1167. utils.FileLog.Info("未知的stlEdbType类型, mapping:%v", v)
  1168. continue
  1169. }
  1170. var dataList []*stl.EdbDataCalculateStl
  1171. for _, v := range edbInfoData {
  1172. dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
  1173. dataList = append(dataList, &stl.EdbDataCalculateStl{
  1174. EdbInfoId: edbInfo.EdbInfoId,
  1175. EdbCode: edbInfo.EdbCode,
  1176. DataTime: dataTime,
  1177. Value: v.Value,
  1178. CreateTime: time.Now(),
  1179. ModifyTime: time.Now(),
  1180. DataTimestamp: dataTime.UnixMilli(),
  1181. })
  1182. }
  1183. err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
  1184. if err != nil {
  1185. return
  1186. }
  1187. data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1188. maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
  1189. if maxAndMinItem != nil {
  1190. err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
  1191. if err != nil {
  1192. return
  1193. }
  1194. }
  1195. edbInfo.CalculateFormula = conf.Config
  1196. edbInfo.ModifyTime = time.Now()
  1197. err = edbInfo.Update([]string{"calculate_formula", "modify_time"})
  1198. if err != nil {
  1199. return
  1200. }
  1201. }
  1202. return
  1203. }
  1204. func GetStlConfig(edbInfoId int) (resp *response.StlConfigResp, msg string, err error) {
  1205. configId, err := stl.GetCalculateStlConfigMappingIdByEdbInfoId(edbInfoId)
  1206. if err != nil {
  1207. if err.Error() == utils.ErrNoRow() {
  1208. msg = "未找到指标信息, 请选择其他指标"
  1209. return
  1210. }
  1211. msg = "查询失败"
  1212. return
  1213. }
  1214. queryEdbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
  1215. if err != nil {
  1216. if err.Error() == utils.ErrNoRow() {
  1217. msg = "未找到指标,请刷新后重试"
  1218. return
  1219. }
  1220. msg = "获取失败"
  1221. return
  1222. }
  1223. var req request.StlConfigReq
  1224. if err = json.Unmarshal([]byte(queryEdbInfo.CalculateFormula), &req); err != nil {
  1225. msg = "获取失败"
  1226. return
  1227. }
  1228. edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
  1229. if err != nil {
  1230. if err.Error() == utils.ErrNoRow() {
  1231. msg = "未找到指标,请刷新后重试"
  1232. return
  1233. }
  1234. msg = "获取失败"
  1235. return
  1236. }
  1237. resp = &response.StlConfigResp{
  1238. CalculateStlConfigId: configId,
  1239. EdbInfoId: req.EdbInfoId,
  1240. EdbInfoName: edbInfo.EdbName,
  1241. DataRangeType: req.DataRangeType,
  1242. StartDate: req.StartDate,
  1243. EndDate: req.EndDate,
  1244. LastNYear: req.LastNYear,
  1245. Period: req.Period,
  1246. Seasonal: req.Seasonal,
  1247. Trend: req.Trend,
  1248. Fraction: req.Fraction,
  1249. Robust: req.Robust,
  1250. TrendDeg: req.TrendDeg,
  1251. SeasonalDeg: req.SeasonalDeg,
  1252. LowPassDeg: req.LowPassDeg,
  1253. }
  1254. return
  1255. }
  1256. func CheckDulplicateEdbInfoName(edbName, lang string) (ok bool, err error) {
  1257. var count int
  1258. var condition string
  1259. var pars []interface{}
  1260. switch lang {
  1261. case utils.EnLangVersion:
  1262. condition += " AND edb_name_en = ? "
  1263. default:
  1264. condition += " AND edb_name=? "
  1265. }
  1266. pars = append(pars, edbName)
  1267. count, err = data_manage.GetEdbInfoCountByCondition(condition, pars)
  1268. if err != nil {
  1269. return
  1270. }
  1271. if count > 0 {
  1272. ok = true
  1273. return
  1274. }
  1275. return
  1276. }