edb_info.go 16 KB

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