stl.go 47 KB

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