12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361 |
- package stl
- import (
- "encoding/json"
- "errors"
- "eta/eta_api/models/data_manage"
- "eta/eta_api/models/data_manage/stl"
- "eta/eta_api/models/data_manage/stl/request"
- "eta/eta_api/models/data_manage/stl/response"
- "eta/eta_api/services/data"
- "eta/eta_api/services/data/data_manage_permission"
- "eta/eta_api/services/elastic"
- "eta/eta_api/utils"
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "time"
- "github.com/rdlucklib/rdluck_tools/paging"
- "github.com/shopspring/decimal"
- "github.com/tealeg/xlsx"
- )
- const (
- ALL_DATE = iota + 1
- LAST_N_YEARS
- RANGE_DATE
- RANGE_DATE_TO_NOW
- )
- var EDB_DATA_CALCULATE_STL_TREND_CACHE = `eta:stl_decompose:trend:config_id:`
- var EDB_DATA_CALCULATE_STL_SEASONAL_CACHE = `eta:stl_decompose:seasonal:config_id:`
- var EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE = `eta:stl_decompose:residual:config_id:`
- func GenerateStlEdbData(req *request.StlConfigReq, adminId int) (resp *response.StlPreviewResp, msg string, err error) {
- config, err := stl.GetCalculateStlConfigById(req.CalculateStlConfigId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "配置信息不存在,请重新计算"
- return
- }
- msg = "获取配置信息失败"
- return
- }
- var confReq request.StlConfigReq
- if err = json.Unmarshal([]byte(config.Config), &confReq); err != nil {
- msg = "预览失败"
- err = fmt.Errorf("配置信息解析失败, err:%s", err.Error())
- return
- }
- edbInfo, err := data_manage.GetEdbInfoById(confReq.EdbInfoId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "指标不存在"
- return
- }
- msg = "获取指标信息失败"
- return
- }
- var condition string
- var pars []interface{}
- switch confReq.DataRangeType {
- case ALL_DATE:
- case LAST_N_YEARS:
- condition += " AND data_time >=?"
- year := time.Now().Year()
- lastNyear, er := strconv.Atoi(req.LastNYear)
- if er != nil {
- msg = "最近N年输入不合法"
- err = er
- return
- }
- if lastNyear <= 0 {
- msg = "最近N年输入不合法"
- err = fmt.Errorf("最近N年输入不合法")
- return
- }
- lastNyear = lastNyear - 1
- lastDate := time.Date(year-lastNyear, 1, 1, 0, 0, 0, 0, time.Local)
- pars = append(pars, lastDate)
- case RANGE_DATE:
- condition = " AND data_time >=? AND data_time <=?"
- pars = append(pars, confReq.StartDate, confReq.EndDate)
- case RANGE_DATE_TO_NOW:
- condition = " AND data_time >=?"
- pars = append(pars, confReq.StartDate)
- }
- condition += " AND edb_code =?"
- pars = append(pars, edbInfo.EdbCode)
- edbData, err := data_manage.GetAllEdbDataListByCondition(condition, pars, edbInfo.Source, edbInfo.SubSource)
- if err != nil {
- msg = "获取指标数据失败"
- return
- }
- if len(edbData) <= 0 {
- msg = "所选指标时间序列不存在数据,请重新选择"
- return
- }
- var condMsg string
- if confReq.Period < 2 || confReq.Period > len(edbData) {
- condMsg += "period必须是一个大于等于2的正整数,且必须小于时间序列的长度"
- }
- if confReq.Seasonal < 3 || confReq.Seasonal%2 == 0 || confReq.Seasonal <= confReq.Period {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "seasonal必须是一个大于等于3的奇整数,且必须大于period"
- }
- if confReq.Trend < 3 || confReq.Trend%2 == 0 || confReq.Trend <= confReq.Period {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "trend必须是一个大于等于3的奇整数,且必须大于period"
- }
- if confReq.Fraction < 0 || confReq.Fraction > 1 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "fraction必须是一个介于[0-1]之间"
- }
- if 1 > confReq.TrendDeg || confReq.TrendDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "trend_deg请设置成1-5的整数"
- }
- if 1 > confReq.SeasonalDeg || confReq.SeasonalDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "seasonal_deg请设置成1-5的整数"
- }
- if 1 > confReq.LowPassDeg || confReq.LowPassDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "low_pass_deg请设置成1-5的整数"
- }
- if condMsg != "" {
- msg = condMsg
- err = fmt.Errorf("参数错误")
- return
- }
- dir, _ := os.Executable()
- exPath := filepath.Dir(dir) + "/static/stl_tmp"
- err = CheckOsPathAndMake(exPath)
- if err != nil {
- msg = "计算失败"
- return
- }
- loadFilePath := exPath + "/" + strconv.Itoa(adminId) + "_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
- err = SaveToExcel(edbData, loadFilePath)
- if err != nil {
- msg = "保存数据到Excel失败"
- return
- }
- defer os.Remove(loadFilePath)
- saveFilePath := exPath + "/" + strconv.Itoa(adminId) + "_" + time.Now().Format(utils.FormatDateTimeUnSpace) + "_res" + ".xlsx"
- result, err := execStlPythonCode(loadFilePath, saveFilePath, confReq.Period, confReq.Seasonal, confReq.Trend, confReq.TrendDeg, confReq.SeasonalDeg, confReq.LowPassDeg, confReq.Fraction, confReq.Robust)
- if err != nil {
- msg = "计算失败,请重新选择指标和参数后计算"
- return
- }
- trendChart, seasonalChart, residualChart, err := ParseStlExcel(saveFilePath)
- if err != nil {
- msg = "解析Excel失败"
- return
- }
- defer os.Remove(saveFilePath)
- resp = new(response.StlPreviewResp)
- resp.OriginEdbInfo.EdbInfoId = edbInfo.EdbInfoId
- resp.OriginEdbInfo.Title = edbInfo.EdbName
- resp.OriginEdbInfo.ClassifyId = edbInfo.ClassifyId
- resp.OriginEdbInfo.MaxData = edbInfo.MaxValue
- resp.OriginEdbInfo.MinData = edbInfo.MinValue
- resp.OriginEdbInfo.Frequency = edbInfo.Frequency
- resp.OriginEdbInfo.Unit = edbInfo.Unit
- resp.OriginEdbInfo.DataList = formatEdbData(edbData)
- resp.TrendChartInfo.DataList = trendChart.DataList
- resp.TrendChartInfo.MaxData = trendChart.MaxData
- resp.TrendChartInfo.MinData = trendChart.MinData
- resp.TrendChartInfo.Title = edbInfo.EdbName + "Trend"
- resp.TrendChartInfo.ClassifyId = edbInfo.ClassifyId
- resp.TrendChartInfo.Frequency = edbInfo.Frequency
- resp.TrendChartInfo.Unit = edbInfo.Unit
- resp.SeasonalChartInfo.DataList = seasonalChart.DataList
- resp.SeasonalChartInfo.MaxData = seasonalChart.MaxData
- resp.SeasonalChartInfo.MinData = seasonalChart.MinData
- resp.SeasonalChartInfo.ClassifyId = edbInfo.ClassifyId
- resp.SeasonalChartInfo.Title = edbInfo.EdbName + "Seasonal"
- resp.SeasonalChartInfo.Frequency = edbInfo.Frequency
- resp.SeasonalChartInfo.Unit = edbInfo.Unit
- resp.ResidualChartInfo.DataList = residualChart.DataList
- resp.ResidualChartInfo.MaxData = residualChart.MaxData
- resp.ResidualChartInfo.MinData = residualChart.MinData
- resp.ResidualChartInfo.ClassifyId = edbInfo.ClassifyId
- resp.ResidualChartInfo.Title = edbInfo.EdbName + "Residual"
- resp.ResidualChartInfo.Frequency = edbInfo.Frequency
- resp.ResidualChartInfo.Unit = edbInfo.Unit
- resp.EvaluationResult.Mean = strconv.FormatFloat(result.ResidualMean, 'f', 4, 64)
- resp.EvaluationResult.Std = strconv.FormatFloat(result.ResidualVar, 'f', 4, 64)
- resp.EvaluationResult.AdfPValue = strconv.FormatFloat(result.AdfPValue, 'f', -1, 64)
- resp.EvaluationResult.LjungBoxPValue = strconv.FormatFloat(result.LbTestPValue, 'f', -1, 64)
- confMapping, err := stl.GetCalculateStlConfigMappingByConfigId(req.CalculateStlConfigId)
- if err != nil {
- msg = "获取配置信息失败"
- return
- }
- var relationEdbInfoId []int
- for _, mapping := range confMapping {
- switch mapping.StlEdbType {
- case 1:
- resp.TrendChartInfo.EdbInfoId = mapping.EdbInfoId
- relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
- case 2:
- resp.SeasonalChartInfo.EdbInfoId = mapping.EdbInfoId
- relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
- case 3:
- resp.ResidualChartInfo.EdbInfoId = mapping.EdbInfoId
- relationEdbInfoId = append(relationEdbInfoId, mapping.EdbInfoId)
- }
- }
- relationEdbInfo, err := data_manage.GetEdbInfoByIdList(relationEdbInfoId)
- if err != nil {
- msg = "获取关联指标信息失败"
- return
- }
- for _, info := range relationEdbInfo {
- switch info.EdbInfoId {
- case resp.TrendChartInfo.EdbInfoId:
- resp.TrendChartInfo.Title = info.EdbName
- resp.TrendChartInfo.ClassifyId = info.ClassifyId
- resp.TrendChartInfo.Frequency = info.Frequency
- resp.TrendChartInfo.Unit = info.Unit
- case resp.SeasonalChartInfo.EdbInfoId:
- resp.SeasonalChartInfo.Title = info.EdbName
- resp.SeasonalChartInfo.ClassifyId = info.ClassifyId
- resp.SeasonalChartInfo.Frequency = info.Frequency
- resp.SeasonalChartInfo.Unit = info.Unit
- case resp.ResidualChartInfo.EdbInfoId:
- resp.ResidualChartInfo.Title = info.EdbName
- resp.ResidualChartInfo.ClassifyId = info.ClassifyId
- resp.ResidualChartInfo.Frequency = info.Frequency
- resp.ResidualChartInfo.Unit = info.Unit
- }
- }
- bTrend, _ := json.Marshal(trendChart.DataList)
- bSeasonal, _ := json.Marshal(seasonalChart.DataList)
- bResidual, _ := json.Marshal(residualChart.DataList)
- err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_TREND_CACHE+strconv.Itoa(config.CalculateStlConfigId), bTrend, time.Hour*2)
- if err != nil {
- msg = "计算失败,请重新计算"
- return
- }
- err = utils.Rc.Put(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE+strconv.Itoa(config.CalculateStlConfigId), bSeasonal, time.Hour*2)
- if err != nil {
- msg = "计算失败,请重新计算"
- return
- }
- utils.Rc.Put(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE+strconv.Itoa(config.CalculateStlConfigId), bResidual, time.Hour*2)
- if err != nil {
- msg = "计算失败,请重新计算"
- }
- return
- }
- func formatEdbData(items []*data_manage.EdbData) []*response.EdbData {
- res := make([]*response.EdbData, 0, len(items))
- for _, item := range items {
- t, _ := time.Parse(utils.FormatDate, item.DataTime)
- res = append(res, &response.EdbData{
- DataTime: item.DataTime,
- Value: item.Value,
- DataTimestamp: t.UnixMilli(),
- })
- }
- return res
- }
- func CheckOsPathAndMake(path string) (err error) {
- if _, er := os.Stat(path); os.IsNotExist(er) {
- err = os.MkdirAll(path, os.ModePerm)
- }
- return
- }
- func ParseStlExcel(excelPath string) (TrendChart, SeasonalChart, ResidualChart response.ChartEdbInfo, err error) {
- file, err := xlsx.OpenFile(excelPath)
- if err != nil {
- return
- }
- for _, sheet := range file.Sheets {
- switch sheet.Name {
- case "季节":
- var MinData, MaxData float64
- for i, row := range sheet.Rows {
- if i == 0 {
- continue
- }
- var date string
- var dataTimestamp int64
- if row.Cells[0].Type() == xlsx.CellTypeNumeric {
- dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
- tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
- date = tmpTime.Format(utils.FormatDate)
- dataTimestamp = tmpTime.UnixMilli()
- } else {
- timeDate, _ := time.Parse(utils.FormatDateTime, date)
- date = timeDate.Format(utils.FormatDate)
- dataTimestamp = timeDate.UnixMilli()
- }
- fv, _ := row.Cells[1].Float()
- if MinData == 0 || fv < MinData {
- MinData = fv
- }
- if MaxData == 0 || fv > MaxData {
- MaxData = fv
- }
- fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
- SeasonalChart.DataList = append(SeasonalChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
- }
- SeasonalChart.MinData = MinData
- SeasonalChart.MaxData = MaxData
- case "趋势":
- var MinData, MaxData float64
- for i, row := range sheet.Rows {
- if i == 0 {
- continue
- }
- var date string
- var dataTimestamp int64
- if row.Cells[0].Type() == xlsx.CellTypeNumeric {
- dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
- tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
- date = tmpTime.Format(utils.FormatDate)
- dataTimestamp = tmpTime.UnixMilli()
- } else {
- timeDate, _ := time.Parse(utils.FormatDateTime, date)
- date = timeDate.Format(utils.FormatDate)
- dataTimestamp = timeDate.UnixMilli()
- }
- fv, _ := row.Cells[1].Float()
- if MinData == 0 || fv < MinData {
- MinData = fv
- }
- if MaxData == 0 || fv > MaxData {
- MaxData = fv
- }
- fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
- TrendChart.DataList = append(TrendChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
- }
- TrendChart.MaxData = MaxData
- TrendChart.MinData = MinData
- case "残差":
- var MinData, MaxData float64
- for i, row := range sheet.Rows {
- if i == 0 {
- continue
- }
- var date string
- var dataTimestamp int64
- if row.Cells[0].Type() == xlsx.CellTypeNumeric {
- dataNum, _ := strconv.ParseFloat(row.Cells[0].Value, 64)
- tmpTime := xlsx.TimeFromExcelTime(dataNum, false)
- date = tmpTime.Format(utils.FormatDate)
- dataTimestamp = tmpTime.UnixMilli()
- } else {
- timeDate, _ := time.Parse(utils.FormatDateTime, date)
- date = timeDate.Format(utils.FormatDate)
- dataTimestamp = timeDate.UnixMilli()
- }
- fv, _ := row.Cells[1].Float()
- if MinData == 0 || fv < MinData {
- MinData = fv
- }
- if MaxData == 0 || fv > MaxData {
- MaxData = fv
- }
- fv, _ = decimal.NewFromFloat(fv).Round(4).Float64()
- ResidualChart.DataList = append(ResidualChart.DataList, &response.EdbData{DataTime: date, Value: fv, DataTimestamp: dataTimestamp})
- }
- ResidualChart.MaxData = MaxData
- ResidualChart.MinData = MinData
- }
- }
- return
- }
- func SaveToExcel(data []*data_manage.EdbData, filePath string) (err error) {
- xlsxFile := xlsx.NewFile()
- sheetNew, err := xlsxFile.AddSheet("Tmp")
- if err != nil {
- return
- }
- titleRow := sheetNew.AddRow()
- titleRow.AddCell().SetString("日期")
- titleRow.AddCell().SetString("值")
- for i, d := range data {
- row := sheetNew.Row(i + 1)
- row.AddCell().SetString(d.DataTime)
- row.AddCell().SetFloat(d.Value)
- }
- err = xlsxFile.Save(filePath)
- if err != nil {
- return
- }
- return
- }
- type STLResult struct {
- ResidualMean float64 `json:"residual_mean"`
- ResidualVar float64 `json:"residual_var"`
- AdfPValue float64 `json:"adf_p_value"`
- LbTestPValue float64 `json:"lb_test_p_value"`
- LbTestStat float64 `json:"lb_test_stat"`
- }
- func execStlPythonCode(path, toPath string, period, seasonal, trend, trendDeg, seasonalDeg, lowPassDeg int, fraction float64, robust bool) (stlResult *STLResult, err error) {
- pythonCode := `
- import json
- import warnings
- warnings.filterwarnings('ignore')
- import pandas as pd
- from statsmodels.tsa.seasonal import STL
- from statsmodels.nonparametric.smoothers_lowess import lowess
- from statsmodels.tsa.stattools import adfuller
- from statsmodels.stats.diagnostic import acorr_ljungbox
- import numpy as np
- file_path = r"%s"
- df = pd.read_excel(file_path, parse_dates=['日期'], engine='openpyxl')
- df.set_index('日期', inplace=True)
- df = df[df.index.notna()]
- period = %d
- seasonal = %d
- trend = %d
- fraction = %g
- seasonal_deg = %d
- trend_deg = %d
- low_pass_deg = %d
- robust = %s
- stl = STL(
- df['值'],
- period=period,
- seasonal=seasonal,
- trend=trend,
- low_pass=None,
- seasonal_deg=seasonal_deg,
- trend_deg=trend_deg,
- low_pass_deg=low_pass_deg,
- seasonal_jump=1,
- trend_jump=1,
- low_pass_jump=1,
- robust=robust
- )
- result = stl.fit()
- smoothed = lowess(df['值'], np.arange(len(df)), frac=fraction)
- trend_lowess = smoothed[:, 1]
- # 季节图
- seasonal_component = result.seasonal
- # 趋势图
- trend_lowess_series = pd.Series(trend_lowess, index=df.index)
- # 残差图
- residual_component = df['值'] - trend_lowess - seasonal_component
- # 计算打印残差的均值
- residual_mean = np.mean(residual_component)
- # 计算打印残差的方差
- residual_var = np.std(residual_component)
- # 计算打印残差的ADF检验结果, 输出p-value
- adf_result = adfuller(residual_component)
- # 根据p-value判断是否平稳
- lb_test = acorr_ljungbox(residual_component, lags=period, return_df=True)
- output_file = r"%s"
- with pd.ExcelWriter(output_file) as writer:
- # 保存季节图
- pd.Series(seasonal_component, index=df.index, name='值').to_frame().reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='季节', index=False)
- # 保存趋势图
- trend_lowess_series.to_frame(name='值').reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='趋势', index=False)
- # 保存残差图
- pd.Series(residual_component, index=df.index, name='值').to_frame().reset_index().rename(columns={'index': '日期'}).to_excel(writer, sheet_name='残差', index=False)
- output = json.dumps({
- 'residual_mean': residual_mean,
- 'residual_var': residual_var,
- 'adf_p_value': adf_result[1],
- 'lb_test_p_value': lb_test['lb_pvalue'].values[0],
- 'lb_test_stat': lb_test['lb_stat'].values[0]
- })
- print(output)
- `
- robustStr := "True"
- if !robust {
- robustStr = "False"
- }
- pythonCode = fmt.Sprintf(pythonCode, path, period, seasonal, trend, fraction, seasonalDeg, trendDeg, lowPassDeg, robustStr, toPath)
- utils.FileLog.Info("stl exec python code:%s", pythonCode)
- cmd := exec.Command(`python3`, "-c", pythonCode)
- output, err := cmd.CombinedOutput()
- if err != nil {
- 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))
- return
- }
- defer cmd.Process.Kill()
- if err = json.Unmarshal(output, &stlResult); err != nil {
- 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))
- return
- }
- return
- }
- func SaveStlConfig(req *request.StlConfigReq, adminId int) (configId int64, msg string, err error) {
- edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "指标不存在"
- return
- }
- msg = "获取指标信息失败"
- return
- }
- var condition string
- var pars []interface{}
- switch req.DataRangeType {
- case ALL_DATE:
- case LAST_N_YEARS:
- condition += " AND data_time >=?"
- year := time.Now().Year()
- lastNyear, er := strconv.Atoi(req.LastNYear)
- if er != nil {
- msg = "最近N年输入不合法"
- err = er
- return
- }
- lastDate := time.Date(year-lastNyear, 1, 1, 0, 0, 0, 0, time.Local)
- pars = append(pars, lastDate)
- case RANGE_DATE:
- condition = " AND data_time >=? AND data_time <=?"
- pars = append(pars, req.StartDate, req.EndDate)
- case RANGE_DATE_TO_NOW:
- condition = " AND data_time >=?"
- pars = append(pars, req.StartDate)
- }
- condition += " AND edb_code =?"
- pars = append(pars, edbInfo.EdbCode)
- edbData, err := data_manage.GetAllEdbDataListByCondition(condition, pars, edbInfo.Source, edbInfo.SubSource)
- if err != nil {
- msg = "获取指标数据失败"
- return
- }
- var condMsg string
- if req.Period < 2 || req.Period > len(edbData) {
- condMsg += "period必须是一个大于等于2的正整数,且必须小于时间序列的长度"
- }
- if req.Seasonal < 3 || req.Seasonal%2 == 0 || req.Seasonal <= req.Period {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "seasonal必须是一个大于等于3的奇整数,且必须大于period"
- }
- if req.Trend < 3 || req.Trend%2 == 0 || req.Trend <= req.Period {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "trend必须是一个大于等于3的奇整数,且必须大于period"
- }
- if req.Fraction < 0 || req.Fraction > 1 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "fraction必须是一个介于[0-1]之间"
- }
- if 1 > req.TrendDeg || req.TrendDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "trend_deg请设置成1-5的整数"
- }
- if 1 > req.SeasonalDeg || req.SeasonalDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "seasonal_deg请设置成1-5的整数"
- }
- if 1 > req.LowPassDeg || req.LowPassDeg > 5 {
- if condMsg != "" {
- condMsg += "\n"
- }
- condMsg += "low_pass_deg请设置成1-5的整数"
- }
- if condMsg != "" {
- msg = condMsg
- err = fmt.Errorf("参数错误")
- return
- }
- b, err := json.Marshal(req)
- if err != nil {
- return
- }
- conf := new(stl.CalculateStlConfig)
- if req.CalculateStlConfigId > 0 {
- conf.CalculateStlConfigId = req.CalculateStlConfigId
- conf.Config = string(b)
- conf.ModifyTime = time.Now()
- err = conf.Update([]string{"Config", "ModifyTime"})
- configId = int64(req.CalculateStlConfigId)
- } else {
- conf.Config = string(b)
- conf.SysUserId = adminId
- conf.CreateTime = time.Now()
- conf.ModifyTime = time.Now()
- configId, err = conf.Insert()
- }
- return
- }
- func SearchEdbInfoWithStl(adminId int, keyWord string, currentIndex, pageSize int, lang string) (resp data_manage.EdbInfoFilterDataResp, msg string, err error) {
- var edbInfoList []*data_manage.EdbInfoList
- noPermissionEdbInfoIdList := make([]int, 0) //无权限指标
- // 获取当前账号的不可见指标
- {
- obj := data_manage.EdbInfoNoPermissionAdmin{}
- confList, er := obj.GetAllListByAdminId(adminId)
- if er != nil && er.Error() != utils.ErrNoRow() {
- msg = "获取失败"
- err = fmt.Errorf("获取不可见指标配置数据失败,Err:" + er.Error())
- return
- }
- for _, v := range confList {
- noPermissionEdbInfoIdList = append(noPermissionEdbInfoIdList, v.EdbInfoId)
- }
- }
- if currentIndex <= 0 {
- currentIndex = 1
- }
- startSize := utils.StartIndex(currentIndex, pageSize)
- // 是否走ES
- isEs := false
- var total int64
- if keyWord != "" {
- frequencyList := []string{"日度", "周度", "旬度", "月度", "季度"}
- // 普通的搜索
- total, edbInfoList, err = elastic.SearchEdbInfoDataByfrequency(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, 0, frequencyList, noPermissionEdbInfoIdList)
- isEs = true
- } else {
- var condition string
- var pars []interface{}
- // 普通指标
- condition += ` AND edb_info_type = ? `
- pars = append(pars, 0)
- // 无权限指标id
- lenNoPermissionEdbInfoIdList := len(noPermissionEdbInfoIdList)
- if lenNoPermissionEdbInfoIdList > 0 {
- condition += ` AND edb_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionEdbInfoIdList) + `) `
- pars = append(pars, noPermissionEdbInfoIdList)
- }
- //频度
- condition += ` AND frequency IN ('日度', '周度', '旬度', '月度', '季度') `
- total, edbInfoList, err = data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
- }
- if err != nil {
- edbInfoList = make([]*data_manage.EdbInfoList, 0)
- }
- page := paging.GetPaging(currentIndex, pageSize, int(total))
- edbInfoListLen := len(edbInfoList)
- classifyIdList := make([]int, 0)
- for i := 0; i < edbInfoListLen; i++ {
- edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
- classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
- // 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
- if keyWord == "" {
- if lang == utils.ZhLangVersion {
- edbInfoList[i].SearchText = edbInfoList[i].EdbName
- }
- if lang == utils.EnLangVersion {
- edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
- }
- }
- }
- // 当前列表中的分类map
- classifyMap := make(map[int]*data_manage.EdbClassify)
- if edbInfoListLen > 0 {
- classifyList, er := data_manage.GetEdbClassifyByIdList(classifyIdList)
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取分类列表失败,Err:" + er.Error())
- return
- }
- for _, v := range classifyList {
- classifyMap[v.ClassifyId] = v
- }
- // 获取所有有权限的指标和分类
- permissionEdbIdList, permissionClassifyIdList, er := data_manage_permission.GetUserEdbAndClassifyPermissionList(adminId, 0, 0)
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取所有有权限的指标和分类失败,Err:" + er.Error())
- return
- }
- // 如果是ES的话,需要重新查一下指标的信息,主要是为了把是否授权字段找出来
- if isEs {
- edbInfoIdList := make([]int, 0)
- for i := 0; i < edbInfoListLen; i++ {
- edbInfoIdList = append(edbInfoIdList, edbInfoList[i].EdbInfoId)
- tmpEdbInfo := edbInfoList[i]
- if currClassify, ok := classifyMap[tmpEdbInfo.ClassifyId]; ok {
- edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfo.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfo.EdbInfoId, tmpEdbInfo.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
- }
- }
- tmpEdbList, er := data_manage.GetEdbInfoByIdList(edbInfoIdList)
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取所有有权限的指标失败,Err:" + er.Error())
- return
- }
- edbInfoMap := make(map[int]*data_manage.EdbInfo)
- for _, v := range tmpEdbList {
- edbInfoMap[v.EdbInfoId] = v
- }
- for i := 0; i < edbInfoListLen; i++ {
- tmpEdbInfo, ok := edbInfoMap[edbInfoList[i].EdbInfoId]
- if !ok {
- continue
- }
- edbInfoList[i].IsJoinPermission = tmpEdbInfo.IsJoinPermission
- }
- }
- // 权限校验
- for i := 0; i < edbInfoListLen; i++ {
- tmpEdbInfoItem := edbInfoList[i]
- if currClassify, ok := classifyMap[tmpEdbInfoItem.ClassifyId]; ok {
- edbInfoList[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(tmpEdbInfoItem.IsJoinPermission, currClassify.IsJoinPermission, tmpEdbInfoItem.EdbInfoId, tmpEdbInfoItem.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
- }
- }
- }
- for i := 0; i < edbInfoListLen; i++ {
- for j := 0; j < edbInfoListLen; j++ {
- if (edbInfoList[i].EdbNameAlias == edbInfoList[j].EdbNameAlias) &&
- (edbInfoList[i].EdbInfoId != edbInfoList[j].EdbInfoId) &&
- !(strings.Contains(edbInfoList[i].EdbName, edbInfoList[i].SourceName)) {
- edbInfoList[i].EdbName = edbInfoList[i].EdbName + "(" + edbInfoList[i].SourceName + ")"
- }
- }
- }
- //新增搜索词记录
- {
- searchKeyword := new(data_manage.SearchKeyword)
- searchKeyword.KeyWord = keyWord
- searchKeyword.CreateTime = time.Now()
- go data_manage.AddSearchKeyword(searchKeyword)
- }
- resp = data_manage.EdbInfoFilterDataResp{
- Paging: page,
- List: edbInfoList,
- }
- return
- }
- func SaveStlEdbInfo(req *request.SaveStlEdbInfoReq, adminId int, adminRealName, lang string) (addEdbInfoId int, isSendEmail bool, msg string, err error) {
- if req.EdbName == "" {
- msg = "指标名称不能为空"
- return
- }
- if req.Unit == "" {
- msg = "指标单位不能为空"
- return
- }
- if req.ClassifyId <= 0 {
- msg = "请选择分类"
- return
- }
- if req.Frequency == "" {
- msg = "指标频度不能为空"
- return
- }
- conf, err := stl.GetCalculateStlConfigById(req.CalculateStlConfigId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "未找到配置,请先进行计算"
- err = fmt.Errorf("配置不存在")
- return
- }
- msg = "获取失败"
- return
- }
- var stlConfig request.StlConfigReq
- if err = json.Unmarshal([]byte(conf.Config), &stlConfig); err != nil {
- msg = "获取失败"
- return
- }
- var edbInfoData []*response.EdbData
- switch req.StlEdbType {
- case 1:
- // 趋势指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
- msg = "计算已过期,请重新计算"
- err = fmt.Errorf("not found")
- return
- }
- trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(req.CalculateStlConfigId))
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
- return
- }
- if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
- msg = "获取失败"
- err = fmt.Errorf("json解析失败,Err:" + er.Error())
- return
- }
- case 2:
- // 季节性指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
- msg = "计算已过期,请重新计算"
- err = fmt.Errorf("not found")
- return
- }
- seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
- return
- }
- if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
- msg = "获取失败"
- err = fmt.Errorf("json解析失败,Err:" + er.Error())
- return
- }
- case 3:
- // 残差性指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId)); !ok {
- msg = "计算已过期,请重新计算"
- err = fmt.Errorf("not found")
- return
- }
- residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(req.CalculateStlConfigId))
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取redis数据失败,Err:" + er.Error())
- return
- }
- if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
- msg = "获取失败"
- err = fmt.Errorf("json解析失败,Err:" + er.Error())
- return
- }
- default:
- msg = "获取失败"
- err = fmt.Errorf("未知的计算类型")
- return
- }
- var opEdbInfoId int
- if req.EdbInfoId > 0 {
- opEdbInfoId = req.EdbInfoId
- // 检查指标名称是否存在
- var condition string
- var pars []interface{}
- switch lang {
- case utils.EnLangVersion:
- condition += " AND edb_name_en = ? "
- default:
- condition += " AND edb_name=? "
- }
- pars = append(pars, req.EdbName)
- existEdbInfo, er := data_manage.GetEdbInfoByCondition(condition, pars)
- if er != nil && er.Error() != utils.ErrNoRow() {
- msg = "获取失败"
- return
- }
- switch lang {
- case utils.EnLangVersion:
- if existEdbInfo != nil && existEdbInfo.EdbNameEn == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
- msg = "指标名称已存在"
- err = fmt.Errorf("指标名称已存在")
- return
- }
- default:
- if existEdbInfo != nil && existEdbInfo.EdbName == req.EdbName && req.EdbInfoId != existEdbInfo.EdbInfoId {
- msg = "指标名称已存在"
- err = fmt.Errorf("指标名称已存在")
- return
- }
- }
- // 更新指标
- edbInfo, er := data_manage.GetEdbInfoById(req.EdbInfoId)
- if er != nil {
- if er.Error() == utils.ErrNoRow() {
- msg = "未找到指标,请刷新后重试"
- err = er
- return
- }
- msg = "获取失败"
- err = er
- return
- }
- var updateCols []string
- switch lang {
- case utils.EnLangVersion:
- if edbInfo.EdbName != req.EdbName {
- edbInfo.EdbNameEn = req.EdbName
- updateCols = append(updateCols, "edb_name_en")
- }
- default:
- if edbInfo.EdbName != req.EdbName {
- edbInfo.EdbName = req.EdbName
- updateCols = append(updateCols, "edb_name")
- }
- }
- if edbInfo.ClassifyId != req.ClassifyId {
- // 更新分类
- maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
- return
- }
- edbInfo.ClassifyId = req.ClassifyId
- edbInfo.Sort = maxSort + 1
- updateCols = append(updateCols, "classify_id", "sort")
- }
- if edbInfo.Frequency != req.Frequency {
- edbInfo.Frequency = req.Frequency
- updateCols = append(updateCols, "frequency")
- }
- if edbInfo.Unit != req.Unit {
- edbInfo.Unit = req.Unit
- updateCols = append(updateCols, "unit")
- }
- edbInfo.CalculateFormula = conf.Config
- updateCols = append(updateCols, "calculate_formula")
- if len(updateCols) > 0 {
- edbInfo.ModifyTime = time.Now()
- updateCols = append(updateCols, "modify_time")
- err = edbInfo.Update(updateCols)
- if err != nil {
- msg = "保存失败"
- return
- }
- }
- var dataList []*stl.EdbDataCalculateStl
- for _, v := range edbInfoData {
- dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
- dataList = append(dataList, &stl.EdbDataCalculateStl{
- EdbInfoId: edbInfo.EdbInfoId,
- EdbCode: edbInfo.EdbCode,
- DataTime: dataTime,
- Value: v.Value,
- CreateTime: time.Now(),
- ModifyTime: time.Now(),
- DataTimestamp: dataTime.UnixMilli(),
- })
- }
- err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
- if err != nil {
- msg = "保存失败"
- return
- }
- data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
- maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
- if maxAndMinItem != nil {
- err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
- if err != nil {
- msg = "保存失败"
- err = errors.New("保存失败,Err:" + err.Error())
- return
- }
- }
- } else {
- indexObj := new(stl.EdbDataCalculateStl)
- edbCode, er := utils.GenerateEdbCode(1, "stl")
- if er != nil {
- msg = "生成指标代码失败"
- err = fmt.Errorf("生成指标代码失败,Err:" + er.Error())
- return
- }
- //判断指标名称是否存在
- ok, er := CheckDulplicateEdbInfoName(req.EdbName, lang)
- if er != nil {
- msg = "保存失败"
- err = fmt.Errorf("检查指标名称是否存在失败,Err:" + er.Error())
- return
- }
- if ok {
- msg = "指标名称已存在"
- err = fmt.Errorf("指标名称已存在")
- return
- }
- source := utils.DATA_SOURCE_CALCULATE_STL
- subSource := utils.DATA_SUB_SOURCE_EDB
- edbInfo := new(data_manage.EdbInfo)
- //获取该层级下最大的排序数
- maxSort, er := data.GetEdbClassifyMaxSort(req.ClassifyId, 0)
- if er != nil {
- msg = "获取失败"
- err = fmt.Errorf("获取最大排序失败,Err:" + er.Error())
- return
- }
- edbInfo.EdbCode = edbCode
- edbInfo.EdbName = req.EdbName
- edbInfo.EdbNameEn = req.EdbName
- edbInfo.EdbNameSource = req.EdbName
- edbInfo.Frequency = req.Frequency
- edbInfo.Unit = req.Unit
- edbInfo.UnitEn = req.Unit
- edbInfo.CalculateFormula = conf.Config
- edbInfo.ClassifyId = req.ClassifyId
- edbInfo.SysUserId = adminId
- edbInfo.SysUserRealName = adminRealName
- edbInfo.CreateTime = time.Now()
- edbInfo.ModifyTime = time.Now()
- edbInfo.Sort = maxSort + 1
- edbInfo.DataDateType = `交易日`
- timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
- edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
- itemVal, er := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
- if itemVal != nil && er == nil {
- edbInfo.MaxValue = itemVal.MaxValue
- edbInfo.MinValue = itemVal.MinValue
- }
- edbInfo.EdbType = 2
- edbInfo.Source = source
- edbInfo.SubSource = subSource
- edbInfo.SourceName = "STL趋势分解"
- extra, _ := json.Marshal(req)
- edbInfo.Extra = string(extra)
- edbInfoId, er := data_manage.AddEdbInfo(edbInfo)
- if er != nil {
- msg = "保存失败"
- err = errors.New("保存失败,Err:" + er.Error())
- return
- }
- edbInfo.EdbInfoId = int(edbInfoId)
- var dataList []*stl.EdbDataCalculateStl
- for _, v := range edbInfoData {
- dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
- dataList = append(dataList, &stl.EdbDataCalculateStl{
- EdbInfoId: int(edbInfoId),
- EdbCode: edbCode,
- DataTime: dataTime,
- Value: v.Value,
- CreateTime: time.Now(),
- ModifyTime: time.Now(),
- DataTimestamp: dataTime.UnixMilli(),
- })
- }
- err = indexObj.BatchInsert(dataList)
- if err != nil {
- msg = "保存失败"
- return
- }
- //保存数据
- data_manage.ModifyEdbInfoDataStatus(edbInfoId, source, subSource, edbCode)
- maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(source, subSource, edbCode)
- if maxAndMinItem != nil {
- err = data_manage.ModifyEdbInfoMaxAndMinInfo(int(edbInfoId), maxAndMinItem)
- if err != nil {
- msg = "保存失败"
- err = errors.New("保存失败,Err:" + err.Error())
- return
- }
- }
- // 保存配置映射
- {
- stlMapping := new(stl.CalculateStlConfigMapping)
- stlMapping.EdbInfoId = int(edbInfoId)
- stlMapping.CalculateStlConfigId = req.CalculateStlConfigId
- stlMapping.StlEdbType = req.StlEdbType
- stlMapping.CreateTime = time.Now()
- stlMapping.ModifyTime = time.Now()
- _, err = stlMapping.Insert()
- if err != nil {
- msg = "保存失败"
- err = errors.New("保存配置映射失败,Err:" + err.Error())
- return
- }
- }
- // 保存溯源信息
- {
- fromEdbInfo, er := data_manage.GetEdbInfoById(stlConfig.EdbInfoId)
- if er != nil {
- if er.Error() == utils.ErrNoRow() {
- msg = "未找到指标,请刷新后重试"
- err = fmt.Errorf("指标不存在,err:" + er.Error())
- return
- }
- msg = "获取失败"
- err = er
- return
- }
- edbCalculateMappingInfo := new(data_manage.EdbInfoCalculateMapping)
- edbCalculateMappingInfo.EdbInfoId = int(edbInfoId)
- edbCalculateMappingInfo.Source = source
- edbCalculateMappingInfo.SourceName = "STL趋势分解"
- edbCalculateMappingInfo.EdbCode = edbCode
- edbCalculateMappingInfo.FromEdbInfoId = fromEdbInfo.EdbInfoId
- edbCalculateMappingInfo.FromEdbCode = fromEdbInfo.EdbCode
- edbCalculateMappingInfo.FromEdbName = fromEdbInfo.EdbName
- edbCalculateMappingInfo.FromSource = fromEdbInfo.Source
- edbCalculateMappingInfo.FromSourceName = fromEdbInfo.SourceName
- edbCalculateMappingInfo.CreateTime = time.Now()
- edbCalculateMappingInfo.ModifyTime = time.Now()
- err = edbCalculateMappingInfo.Insert()
- if err != nil {
- msg = "保存失败"
- err = errors.New("保存溯源信息失败,Err:" + err.Error())
- return
- }
- }
- //添加es
- data.AddOrEditEdbInfoToEs(int(edbInfoId))
- opEdbInfoId = int(edbInfoId)
- }
- // 更新关联的同配置的指标
- err = SyncUpdateRelationEdbInfo(req.CalculateStlConfigId, opEdbInfoId)
- if err != nil {
- msg = "更新关联的同配置的指标失败"
- return
- }
- addEdbInfoId = opEdbInfoId
- return
- }
- func SyncUpdateRelationEdbInfo(configId int, excludeId int) (err error) {
- mappingList, err := stl.GetCalculateStlConfigMappingByConfigId(configId)
- if err != nil {
- return
- }
- conf, err := stl.GetCalculateStlConfigById(configId)
- if err != nil {
- return
- }
- for _, v := range mappingList {
- edbInfo, er := data_manage.GetEdbInfoById(v.EdbInfoId)
- if er != nil {
- continue
- }
- if v.EdbInfoId == excludeId {
- continue
- }
- var edbInfoData []*response.EdbData
- switch v.StlEdbType {
- case 1:
- // 趋势指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
- continue
- }
- trendData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId))
- if er != nil {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_TREND_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
- continue
- }
- if er := json.Unmarshal(trendData, &edbInfoData); er != nil {
- utils.FileLog.Info("redis获取解析, body:%s,err:%s", string(trendData), er.Error())
- continue
- }
- case 2:
- // 季节性指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
- continue
- }
- seasonalData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
- if er != nil {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_SEASONAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
- continue
- }
- if er := json.Unmarshal(seasonalData, &edbInfoData); er != nil {
- utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(seasonalData), er.Error())
- continue
- }
- case 3:
- // 残差性指标
- if ok := utils.Rc.IsExist(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId)); !ok {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "指标数据不存在")
- continue
- }
- residualData, er := utils.Rc.RedisBytes(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId))
- if er != nil {
- utils.FileLog.Info(EDB_DATA_CALCULATE_STL_RESIDUAL_CACHE + strconv.Itoa(v.CalculateStlConfigId) + "redis获取失败,err:" + er.Error())
- continue
- }
- if er := json.Unmarshal(residualData, &edbInfoData); er != nil {
- utils.FileLog.Info("redis数据解析失败, body:%s,err:%s", string(residualData), er.Error())
- continue
- }
- default:
- utils.FileLog.Info("未知的stlEdbType类型, mapping:%v", v)
- continue
- }
- var dataList []*stl.EdbDataCalculateStl
- for _, v := range edbInfoData {
- dataTime, _ := time.Parse(utils.FormatDate, v.DataTime)
- dataList = append(dataList, &stl.EdbDataCalculateStl{
- EdbInfoId: edbInfo.EdbInfoId,
- EdbCode: edbInfo.EdbCode,
- DataTime: dataTime,
- Value: v.Value,
- CreateTime: time.Now(),
- ModifyTime: time.Now(),
- DataTimestamp: dataTime.UnixMilli(),
- })
- }
- err = stl.DeleteAndInsertEdbDataCalculateStl(edbInfo.EdbCode, dataList)
- if err != nil {
- return
- }
- data_manage.ModifyEdbInfoDataStatus(int64(edbInfo.EdbInfoId), edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
- maxAndMinItem, _ := data_manage.GetEdbInfoMaxAndMinInfo(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode)
- if maxAndMinItem != nil {
- err = data_manage.ModifyEdbInfoMaxAndMinInfo(edbInfo.EdbInfoId, maxAndMinItem)
- if err != nil {
- return
- }
- }
- edbInfo.CalculateFormula = conf.Config
- edbInfo.ModifyTime = time.Now()
- err = edbInfo.Update([]string{"calculate_formula", "modify_time"})
- if err != nil {
- return
- }
- }
- return
- }
- func GetStlConfig(edbInfoId int) (resp *response.StlConfigResp, msg string, err error) {
- configId, err := stl.GetCalculateStlConfigMappingIdByEdbInfoId(edbInfoId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "未找到指标信息, 请选择其他指标"
- return
- }
- msg = "查询失败"
- return
- }
- queryEdbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "未找到指标,请刷新后重试"
- return
- }
- msg = "获取失败"
- return
- }
- var req request.StlConfigReq
- if err = json.Unmarshal([]byte(queryEdbInfo.CalculateFormula), &req); err != nil {
- msg = "获取失败"
- return
- }
- edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
- if err != nil {
- if err.Error() == utils.ErrNoRow() {
- msg = "未找到指标,请刷新后重试"
- return
- }
- msg = "获取失败"
- return
- }
- resp = &response.StlConfigResp{
- CalculateStlConfigId: configId,
- EdbInfoId: req.EdbInfoId,
- EdbInfoName: edbInfo.EdbName,
- DataRangeType: req.DataRangeType,
- StartDate: req.StartDate,
- EndDate: req.EndDate,
- LastNYear: req.LastNYear,
- Period: req.Period,
- Seasonal: req.Seasonal,
- Trend: req.Trend,
- Fraction: req.Fraction,
- Robust: req.Robust,
- TrendDeg: req.TrendDeg,
- SeasonalDeg: req.SeasonalDeg,
- LowPassDeg: req.LowPassDeg,
- }
- return
- }
- func CheckDulplicateEdbInfoName(edbName, lang string) (ok bool, err error) {
- var count int
- var condition string
- var pars []interface{}
- switch lang {
- case utils.EnLangVersion:
- condition += " AND edb_name_en = ? "
- default:
- condition += " AND edb_name=? "
- }
- pars = append(pars, edbName)
- count, err = data_manage.GetEdbInfoCountByCondition(condition, pars)
- if err != nil {
- return
- }
- if count > 0 {
- ok = true
- return
- }
- return
- }
|