edb_info.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. package services
  2. import (
  3. "errors"
  4. "eta/eta_forum_hub/models"
  5. "eta/eta_forum_hub/models/mgodb"
  6. "eta/eta_forum_hub/services/elastic"
  7. "eta/eta_forum_hub/utils"
  8. "fmt"
  9. "github.com/shopspring/decimal"
  10. "math"
  11. "sort"
  12. "strconv"
  13. "time"
  14. )
  15. // BatchAddOrUpdateEdbInfo 添加批量添加指标到指标库
  16. func BatchAddOrUpdateEdbInfo(edbList []*models.EdbInfo, edbMapping []*models.EdbInfoCalculateMapping, sysUserId int, sysUserRealName string) (newList []*models.EdbInfo, err error, errMsg string, isSendEmail bool) {
  17. isSendEmail = true
  18. edbCodeMap := make(map[string]*models.EdbInfo)
  19. for _, v := range edbList {
  20. tmp, e, msg, _ := AddOrUpdateEdbInfo(v, sysUserId, sysUserRealName)
  21. if e != nil {
  22. err = fmt.Errorf("添加指标失败:%s,%s", e.Error(), msg)
  23. errMsg = "添加指标失败"
  24. return
  25. }
  26. newList = append(newList, tmp)
  27. edbCodeMap[tmp.EdbCode] = tmp
  28. }
  29. //批量添加指标的mapping信息
  30. calculateMappingItemList := make([]*models.EdbInfoCalculateMapping, 0)
  31. for _, v := range edbMapping {
  32. edbInfo, ok := edbCodeMap[v.EdbCode]
  33. if !ok {
  34. continue
  35. }
  36. fromEdbInfo, ok := edbCodeMap[v.FromEdbCode]
  37. if !ok {
  38. continue
  39. }
  40. calculateMappingItem := new(models.EdbInfoCalculateMapping)
  41. calculateMappingItem.CreateTime = time.Now()
  42. calculateMappingItem.ModifyTime = time.Now()
  43. calculateMappingItem.Sort = v.Sort
  44. calculateMappingItem.EdbCode = edbInfo.EdbCode
  45. calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
  46. calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
  47. calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
  48. calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
  49. calculateMappingItem.FromSource = fromEdbInfo.Source
  50. calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
  51. calculateMappingItem.FromTag = v.FromTag
  52. calculateMappingItem.Source = edbInfo.Source
  53. calculateMappingItem.SourceName = edbInfo.SourceName
  54. calculateMappingItem.FromSubSource = edbInfo.SubSource
  55. calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
  56. }
  57. if len(calculateMappingItemList) > 0 {
  58. err = models.AddEdbInfoCalculateMappingMulti(calculateMappingItemList)
  59. if err != nil {
  60. errMsg = "指标映射关系添加失败"
  61. err = fmt.Errorf("指标映射关系添加失败, %s", err.Error())
  62. return
  63. }
  64. }
  65. return
  66. }
  67. // AddOrUpdateEdbInfo 添加指标到指标库
  68. func AddOrUpdateEdbInfo(edbItem *models.EdbInfo, sysUserId int, sysUserRealName string) (edbInfo *models.EdbInfo, err error, errMsg string, isSendEmail bool) {
  69. isSendEmail = true
  70. //判断指标是否存在
  71. //edbInfo, err = models.GetEdbInfoByEdbCode(edbItem.EdbCode)
  72. edbInfo, err = models.GetEdbInfoById(edbItem.EdbInfoId)
  73. if err != nil {
  74. if err.Error() == utils.ErrNoRow() {
  75. err = nil
  76. } else {
  77. errMsg = "判断指标名称是否存在失败"
  78. err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
  79. return
  80. }
  81. }
  82. var edbInfoId int64
  83. // 指标已存在
  84. if edbInfo != nil && edbInfo.EdbInfoId > 0 {
  85. edbInfoId = int64(edbInfo.EdbInfoId)
  86. // 更新指标信息
  87. edbInfo.Source = edbItem.Source
  88. edbInfo.SourceName = edbItem.SourceName
  89. edbInfo.EdbType = edbItem.EdbType
  90. edbInfo.EdbCode = edbItem.EdbCode
  91. edbInfo.EdbName = edbItem.EdbName
  92. edbInfo.EdbNameSource = edbItem.EdbNameSource
  93. edbInfo.Frequency = edbItem.Frequency
  94. edbInfo.Unit = edbItem.Unit
  95. edbInfo.StartDate = edbItem.StartDate
  96. edbInfo.EndDate = edbItem.EndDate
  97. edbInfo.ClassifyId = edbItem.ClassifyId
  98. edbInfo.SysUserId = sysUserId
  99. edbInfo.SysUserRealName = sysUserRealName
  100. edbInfo.ModifyTime = time.Now()
  101. edbInfo.ServerUrl = edbItem.ServerUrl
  102. edbInfo.Sort = edbItem.Sort
  103. edbInfo.DataDateType = edbItem.DataDateType
  104. edbInfo.TerminalCode = edbItem.TerminalCode
  105. edbInfo.SourceIndexName = edbItem.SourceIndexName
  106. edbInfo.MaxValue = edbItem.MaxValue
  107. edbInfo.MinValue = edbItem.MinValue
  108. edbInfo.EdbInfoType = edbItem.EdbInfoType
  109. edbInfo.LatestDate = edbItem.LatestDate
  110. edbInfo.LatestValue = edbItem.LatestValue
  111. edbInfo.CalculateFormula = edbItem.CalculateFormula
  112. edbInfo.ManualSave = edbItem.ManualSave
  113. edbInfo.MoveType = edbItem.MoveType
  114. edbInfo.MoveFrequency = edbItem.MoveFrequency
  115. edbInfo.NoUpdate = edbItem.NoUpdate
  116. edbInfo.EdbNameEn = edbItem.EdbNameEn
  117. edbInfo.UnitEn = edbItem.UnitEn
  118. edbInfo.ChartImage = edbItem.ChartImage
  119. edbInfo.Calendar = edbItem.Calendar
  120. edbInfo.EmptyType = edbItem.EmptyType
  121. edbInfo.MaxEmptyType = edbItem.MaxEmptyType
  122. edbInfo.DataUpdateTime = edbItem.DataUpdateTime
  123. edbInfo.ErDataUpdateDate = edbItem.ErDataUpdateDate
  124. edbInfo.EndValue = edbItem.EndValue
  125. edbInfo.SubSource = edbItem.SubSource
  126. edbInfo.SubSourceName = edbItem.SubSourceName
  127. edbInfo.IndicatorCode = edbItem.IndicatorCode
  128. edbInfo.StockCode = edbItem.StockCode
  129. edbInfo.Extra = edbItem.Extra
  130. err = edbInfo.Update([]string{})
  131. if err != nil {
  132. errMsg = "保存失败"
  133. err = errors.New("保存失败,Err:" + err.Error())
  134. return
  135. }
  136. //同时删除指标的映射信息
  137. err = models.DeleteEdbInfoMapping(edbInfoId)
  138. if err != nil {
  139. errMsg = "删除指标映射信息失败"
  140. err = errors.New("删除指标映射信息失败,Err:" + err.Error())
  141. return
  142. }
  143. } else {
  144. edbInfo = new(models.EdbInfo)
  145. edbInfo.EdbInfoId = edbItem.EdbInfoId
  146. edbInfo.Source = edbItem.Source
  147. edbInfo.SourceName = edbItem.SourceName
  148. edbInfo.EdbType = edbItem.EdbType
  149. edbInfo.EdbCode = edbItem.EdbCode
  150. edbInfo.EdbName = edbItem.EdbName
  151. edbInfo.EdbNameSource = edbItem.EdbNameSource
  152. edbInfo.Frequency = edbItem.Frequency
  153. edbInfo.Unit = edbItem.Unit
  154. edbInfo.StartDate = edbItem.StartDate
  155. edbInfo.EndDate = edbItem.EndDate
  156. edbInfo.ClassifyId = edbItem.ClassifyId
  157. edbInfo.SysUserId = sysUserId
  158. edbInfo.SysUserRealName = sysUserRealName
  159. edbInfo.CreateTime = time.Now()
  160. edbInfo.ModifyTime = time.Now()
  161. edbInfo.ServerUrl = edbItem.ServerUrl
  162. edbInfo.Sort = edbItem.Sort
  163. edbInfo.DataDateType = edbItem.DataDateType
  164. edbInfo.TerminalCode = edbItem.TerminalCode
  165. edbInfo.SourceIndexName = edbItem.SourceIndexName
  166. //timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  167. //edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
  168. edbInfo.UniqueCode = edbItem.UniqueCode
  169. edbInfo.MaxValue = edbItem.MaxValue
  170. edbInfo.MinValue = edbItem.MinValue
  171. edbInfo.EdbInfoType = edbItem.EdbInfoType
  172. edbInfo.LatestDate = edbItem.LatestDate
  173. edbInfo.LatestValue = edbItem.LatestValue
  174. edbInfo.CalculateFormula = edbItem.CalculateFormula
  175. edbInfo.ManualSave = edbItem.ManualSave
  176. edbInfo.MoveType = edbItem.MoveType
  177. edbInfo.MoveFrequency = edbItem.MoveFrequency
  178. edbInfo.NoUpdate = edbItem.NoUpdate
  179. edbInfo.EdbNameEn = edbItem.EdbNameEn
  180. edbInfo.UnitEn = edbItem.UnitEn
  181. edbInfo.ChartImage = edbItem.ChartImage
  182. edbInfo.Calendar = edbItem.Calendar
  183. edbInfo.EmptyType = edbItem.EmptyType
  184. edbInfo.MaxEmptyType = edbItem.MaxEmptyType
  185. edbInfo.DataUpdateTime = edbItem.DataUpdateTime
  186. edbInfo.ErDataUpdateDate = edbItem.ErDataUpdateDate
  187. edbInfo.EndValue = edbItem.EndValue
  188. edbInfo.SubSource = edbItem.SubSource
  189. edbInfo.SubSourceName = edbItem.SubSourceName
  190. edbInfo.IndicatorCode = edbItem.IndicatorCode
  191. edbInfo.StockCode = edbItem.StockCode
  192. edbInfo.Extra = edbItem.Extra
  193. edbInfoId, err = models.AddEdbInfo(edbInfo)
  194. if err != nil {
  195. errMsg = "保存失败"
  196. err = errors.New("保存失败,Err:" + err.Error())
  197. return
  198. }
  199. edbInfo.EdbInfoId = int(edbInfoId)
  200. }
  201. if edbInfo.EdbType == 1 {
  202. err = mgodb.ModifyEdbDataEdbInfoId(edbInfoId, edbInfo.EdbCode)
  203. if err != nil {
  204. errMsg = "保存失败"
  205. err = errors.New("更新指标数据失败,Err:" + err.Error())
  206. return
  207. }
  208. } else {
  209. err = mgodb.ModifyEdbCalculateDataEdbInfoId(edbInfoId, edbInfo.EdbCode)
  210. if err != nil {
  211. errMsg = "保存失败"
  212. err = errors.New("更新指标数据失败,Err:" + err.Error())
  213. return
  214. }
  215. }
  216. // 更新es
  217. go AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
  218. return
  219. }
  220. // 批量删除指标,
  221. func BatchDeleteEdbInfo(edbInfoIds []int) (err error, errMsg string) {
  222. for _, v := range edbInfoIds {
  223. //查询单个指标是否允许删除
  224. err, errMsg = DeleteEdbInfo(v)
  225. if err != nil {
  226. errMsg = "删除指标失败"
  227. return
  228. }
  229. }
  230. return
  231. }
  232. // 删除单个指标
  233. func DeleteEdbInfo(edbInfoId int) (err error, errMsg string) {
  234. //判断指标是否存在
  235. info, err := models.GetEdbInfoById(edbInfoId)
  236. if err != nil {
  237. if err.Error() == utils.ErrNoRow() {
  238. err = nil
  239. return
  240. } else {
  241. errMsg = "判断指标名称是否存在失败"
  242. err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
  243. return
  244. }
  245. }
  246. //判断指标是否用于作图,如果用于作图,则不可删除
  247. chartCount, tmpErr := models.GetChartEdbMappingCount(edbInfoId)
  248. if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
  249. errMsg = "删除失败"
  250. err = errors.New("判断指标是否被用于作图失败,Err:" + tmpErr.Error())
  251. return
  252. }
  253. if chartCount > 0 {
  254. errMsg = "当前指标已用作画图,不可删除"
  255. return
  256. }
  257. //判断指标是否用作其他指标的计算
  258. calculateCount, tmpErr := models.GetEdbInfoCalculateMappingCount(edbInfoId)
  259. if tmpErr != nil {
  260. errMsg = "删除失败"
  261. err = errors.New("判断指标是否被用于计算失败,GetEdbInfoCalculateCount Err:" + tmpErr.Error())
  262. return
  263. }
  264. if calculateCount > 0 {
  265. errMsg = "当前指标已用作,指标运算,不可删除"
  266. return
  267. }
  268. // 删除指标,删除映射关系,删除指标数据
  269. err = models.DeleteEdbInfo(edbInfoId)
  270. if err != nil {
  271. errMsg = "删除失败"
  272. err = errors.New("删除指标失败,Err:" + err.Error())
  273. return
  274. }
  275. if info.EdbType == 1 {
  276. err = mgodb.DeleteEdbInfoDataByEdbInfoId(edbInfoId)
  277. if err != nil {
  278. errMsg = "删除失败"
  279. err = errors.New("删除指标数据失败,Err:" + err.Error())
  280. return
  281. }
  282. } else {
  283. err = mgodb.DeleteEdbInfoCalculateDataByEdbInfoId(edbInfoId)
  284. if err != nil {
  285. errMsg = "删除失败"
  286. err = errors.New("删除指标数据失败,Err:" + err.Error())
  287. return
  288. }
  289. }
  290. // 删除es中的数据
  291. go DeleteEdbInfoToEs(edbInfoId)
  292. return
  293. }
  294. // AddOrEditEdbInfoToEs 添加/修改ES中的指标
  295. func AddOrEditEdbInfoToEs(edbInfoId int) {
  296. //添加es
  297. itemInfo, _ := models.GetEdbInfoByCondition("AND edb_info_id=?", []interface{}{edbInfoId})
  298. go elastic.EsAddOrEditEdbInfoData(utils.DATA_INDEX_NAME, strconv.Itoa(itemInfo.EdbInfoId), itemInfo)
  299. }
  300. // 获取频度的英文版
  301. func GetFrequencyEn(frequency string) (frequencyEn string) {
  302. switch frequency {
  303. case "日度":
  304. frequencyEn = "day"
  305. return
  306. case "周度":
  307. frequencyEn = "week"
  308. return
  309. case "旬度":
  310. frequencyEn = "ten days"
  311. return
  312. case "月度":
  313. frequencyEn = "month"
  314. return
  315. case "季度":
  316. frequencyEn = "quarter"
  317. return
  318. case "年度":
  319. frequencyEn = "year"
  320. return
  321. }
  322. return
  323. }
  324. func GetLeadUnitEn(unit string) (unitEn string) {
  325. switch unit {
  326. case "天":
  327. unitEn = "day"
  328. return
  329. case "周":
  330. unitEn = "week"
  331. return
  332. case "月":
  333. unitEn = "month"
  334. return
  335. case "季":
  336. unitEn = "quarter"
  337. return
  338. case "年":
  339. unitEn = "year"
  340. return
  341. }
  342. return
  343. }
  344. // HandleDataByLinearRegressionToList 插值法补充数据(线性方程式)
  345. func HandleDataByLinearRegressionToList(edbInfoDataList []*models.EdbDataList, handleDataMap map[string]float64) (dataTimeList []string, valueList []float64, err error) {
  346. if len(edbInfoDataList) < 2 {
  347. return
  348. }
  349. var startEdbInfoData *models.EdbDataList
  350. for _, v := range edbInfoDataList {
  351. handleDataMap[v.DataTime] = v.Value
  352. dataTimeList = append(dataTimeList, v.DataTime)
  353. // 第一个数据就给过滤了,给后面的试用
  354. if startEdbInfoData == nil {
  355. startEdbInfoData = v
  356. //startEdbInfoData.DataTime = startEdbInfoData.DataTime[:5]+ "01-01"
  357. continue
  358. }
  359. // 获取两条数据之间相差的天数
  360. startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
  361. currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  362. betweenHour := int(currDataTime.Sub(startDataTime).Hours())
  363. betweenDay := betweenHour / 24
  364. // 如果相差一天,那么过滤
  365. if betweenDay <= 1 {
  366. startEdbInfoData = v
  367. continue
  368. }
  369. // 生成线性方程式
  370. var a, b float64
  371. {
  372. coordinateData := make([]utils.Coordinate, 0)
  373. tmpCoordinate1 := utils.Coordinate{
  374. X: 1,
  375. Y: startEdbInfoData.Value,
  376. }
  377. coordinateData = append(coordinateData, tmpCoordinate1)
  378. tmpCoordinate2 := utils.Coordinate{
  379. X: float64(betweenDay) + 1,
  380. Y: v.Value,
  381. }
  382. coordinateData = append(coordinateData, tmpCoordinate2)
  383. a, b = utils.GetLinearResult(coordinateData)
  384. if math.IsNaN(a) || math.IsNaN(b) {
  385. err = errors.New("线性方程公式生成失败")
  386. return
  387. }
  388. }
  389. // 生成对应的值
  390. {
  391. for i := 1; i < betweenDay; i++ {
  392. tmpDataTime := startDataTime.AddDate(0, 0, i)
  393. aDecimal := decimal.NewFromFloat(a)
  394. xDecimal := decimal.NewFromInt(int64(i) + 1)
  395. bDecimal := decimal.NewFromFloat(b)
  396. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
  397. handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
  398. dataTimeList = append(dataTimeList, tmpDataTime.Format(utils.FormatDate))
  399. valueList = append(valueList, val)
  400. }
  401. }
  402. startEdbInfoData = v
  403. }
  404. return
  405. }
  406. // HandleDataByLinearRegressionToList 保证生成365个数据点的线性插值法
  407. func HandleDataByLinearRegressionToListV2(edbInfoDataList []*models.EdbDataList, handleDataMap map[string]float64) (dataTimeList []string, valueList []float64, err error) {
  408. if len(edbInfoDataList) < 2 {
  409. return
  410. }
  411. // 确保至少有两天数据来生成线性方程
  412. if len(edbInfoDataList) < 2 {
  413. err = errors.New("至少需要两天的数据来执行线性插值")
  414. return
  415. }
  416. // 对数据按日期排序,确保顺序正确
  417. sort.Slice(edbInfoDataList, func(i, j int) bool {
  418. t1, _ := time.ParseInLocation(utils.FormatDate, edbInfoDataList[i].DataTime, time.Local)
  419. t2, _ := time.ParseInLocation(utils.FormatDate, edbInfoDataList[j].DataTime, time.Local)
  420. return t1.Before(t2)
  421. })
  422. startEdbInfoData := edbInfoDataList[0]
  423. endEdbInfoData := edbInfoDataList[len(edbInfoDataList)-1]
  424. // 计算起始和结束日期间实际的天数
  425. startDate, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
  426. endDate, _ := time.ParseInLocation(utils.FormatDate, endEdbInfoData.DataTime, time.Local)
  427. actualDays := endDate.Sub(startDate).Hours() / 24
  428. // 生成365个数据点,首先处理已有数据
  429. for _, v := range edbInfoDataList {
  430. handleDataMap[v.DataTime] = v.Value
  431. dataTimeList = append(dataTimeList, v.DataTime)
  432. valueList = append(valueList, v.Value)
  433. }
  434. // 如果已有数据跨越天数不足365天,则对缺失的日期进行线性插值
  435. if actualDays < 365 {
  436. // 使用已有数据点生成线性方程(这里简化处理,实际可能需更细致处理边界情况)
  437. var a, b float64
  438. coordinateData := []utils.Coordinate{
  439. {X: 1, Y: startEdbInfoData.Value},
  440. {X: float64(len(edbInfoDataList)), Y: endEdbInfoData.Value},
  441. }
  442. a, b = utils.GetLinearResult(coordinateData)
  443. if math.IsNaN(a) || math.IsNaN(b) {
  444. err = errors.New("线性方程公式生成失败")
  445. return
  446. }
  447. // 对剩余日期进行插值
  448. for i := 1; i < 365; i++ {
  449. day := startDate.AddDate(0, 0, i)
  450. if _, exists := handleDataMap[day.Format(utils.FormatDate)]; !exists {
  451. aDecimal := decimal.NewFromFloat(a)
  452. xDecimal := decimal.NewFromInt(int64(i) + 1)
  453. bDecimal := decimal.NewFromFloat(b)
  454. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
  455. handleDataMap[day.Format(utils.FormatDate)] = val
  456. dataTimeList = append(dataTimeList, day.Format(utils.FormatDate))
  457. valueList = append(valueList, val)
  458. }
  459. }
  460. }
  461. return
  462. }
  463. func GetRefreshEdbInfoFromBase(edbInfoId, source int) (baseEdbInfoArr, calculateInfoArr []*models.EdbInfo, err error) {
  464. calculateList, err := models.GetEdbInfoCalculateMap(edbInfoId, source)
  465. if err != nil && err.Error() != utils.ErrNoRow() {
  466. return
  467. }
  468. for _, item := range calculateList {
  469. if item.EdbInfoId == edbInfoId { // 如果查出来关联的指标就是自己的话,那么就过滤
  470. continue
  471. }
  472. if item.EdbType == 1 {
  473. baseEdbInfoArr = append(baseEdbInfoArr, item)
  474. } else {
  475. calculateInfoArr = append(calculateInfoArr, item)
  476. newBaseEdbInfoArr, newCalculateInfoArr, _ := GetRefreshEdbInfoFromBase(item.EdbInfoId, item.Source)
  477. baseEdbInfoArr = append(baseEdbInfoArr, newBaseEdbInfoArr...)
  478. calculateInfoArr = append(calculateInfoArr, newCalculateInfoArr...)
  479. }
  480. }
  481. return
  482. }