Pārlūkot izejas kodu

Merge branch 'master' into feature/eta2.1.2_predit_edb

# Conflicts:
#	utils/constants.go
xyxie 6 mēneši atpakaļ
vecāks
revīzija
d0d9eee2bd
64 mainītis faili ar 6428 papildinājumiem un 549 dzēšanām
  1. 22 1
      controllers/base_from_bloomberg.go
  2. 35 0
      controllers/base_from_fenwei.go
  3. 168 0
      controllers/base_from_hisugar.go
  4. 765 0
      controllers/base_from_ly.go
  5. 152 0
      controllers/base_from_trade_analysis.go
  6. 22 8
      controllers/factor_edb_series/factor_edb_series.go
  7. 73 1
      controllers/shanghai_smm.go
  8. 1 1
      logic/profit_chart_info.go
  9. 46 7
      models/base_calculate.go
  10. 3 5
      models/base_from_bloomberg.go
  11. 81 2
      models/base_from_fenwei.go
  12. 276 0
      models/base_from_hisugar.go
  13. 32 0
      models/base_from_ly_classify.go
  14. 72 0
      models/base_from_ly_data.go
  15. 57 0
      models/base_from_ly_index.go
  16. 46 0
      models/base_from_ly_index_record.go
  17. 2 1
      models/base_from_mysteel_chemical.go
  18. 1 1
      models/base_from_yongyi.go
  19. 1 1
      models/base_predict_from_calculate.go
  20. 4 0
      models/common.go
  21. 9 4
      models/db.go
  22. 1 1
      models/edb_data_calculate_avg.go
  23. 1 1
      models/edb_data_calculate_cjjx.go
  24. 1 1
      models/edb_data_calculate_hbz.go
  25. 1 1
      models/edb_data_calculate_hcz.go
  26. 4 4
      models/edb_data_calculate_ljz.go
  27. 2 2
      models/edb_data_calculate_nhcc.go
  28. 1 1
      models/edb_data_calculate_nszydbpjjs.go
  29. 971 0
      models/edb_data_calculate_qjjs.go
  30. 1 1
      models/edb_data_calculate_sum.go
  31. 68 198
      models/edb_data_calculate_tbz.go
  32. 1 1
      models/edb_data_calculate_tcz.go
  33. 1 1
      models/edb_data_calculate_time_shift.go
  34. 232 0
      models/edb_data_ly.go
  35. 4 0
      models/edb_data_table.go
  36. 181 0
      models/edb_data_trade_analysis.go
  37. 19 0
      models/edb_info.go
  38. 160 0
      models/edb_trade_analysis.go
  39. 210 0
      models/factor_edb_series_calculate_data_qjjs.go
  40. 1 0
      models/factor_edb_series_chart_mapping.go
  41. 7 0
      models/factor_edb_series_mapping.go
  42. 2 2
      models/predict_edb.go
  43. 2 2
      models/predict_edb_data_calculate_nhcc.go
  44. 1 1
      models/predict_edb_data_calculate_nszydbpjjs.go
  45. 344 0
      models/predict_edb_data_calculate_qjjs.go
  46. 52 277
      models/predict_edb_data_calculate_tbz.go
  47. 1 1
      models/predict_edb_data_calculate_time_shift.go
  48. 17 17
      models/predict_edb_info_rule.go
  49. 533 0
      models/trade_analysis/trade_analysis.go
  50. 129 0
      models/trade_analysis/trade_futures_company.go
  51. 47 0
      models/trade_analysis/warehouse.go
  52. 189 0
      routers/commentsRouter.go
  53. 15 0
      routers/router.go
  54. 287 0
      services/base_from_fenwei.go
  55. 156 0
      services/base_from_hisugar.go
  56. 3 0
      services/base_from_mysteel_chemical.go
  57. 1 1
      services/base_from_predict.go
  58. 1 1
      services/base_from_smm.go
  59. 3 0
      services/base_from_yongyi.go
  60. 93 0
      services/factor_edb_series.go
  61. 468 0
      services/trade_analysis/trade_analysis_data.go
  62. 181 0
      services/trade_analysis/trade_analysis_interface.go
  63. 156 0
      utils/common.go
  64. 12 3
      utils/constants.go

+ 22 - 1
controllers/base_from_bloomberg.go

@@ -169,6 +169,27 @@ func (this *BloombergController) PCSGImportHistoryData() {
 		br.ErrMsg = "参数解析失败,Err:" + e.Error()
 		return
 	}
+	if req.TaskKey == "" {
+		br.Msg = "TaskKey不可为空"
+		return
+	}
+	tasks, e := services.LoadPCSGBloombergTask()
+	if e != nil {
+		br.Msg = "加载配置失败"
+		br.ErrMsg = fmt.Sprintf("加载配置失败, Err: %v", e)
+		return
+	}
+	conf := new(services.PCSGBloombergTask)
+	for _, v := range tasks {
+		if v.TaskKey == req.TaskKey {
+			conf = v
+			break
+		}
+	}
+	if conf.TaskKey == "" {
+		br.Msg = "TaskKey配置不存在"
+		return
+	}
 
 	var indexes []models.BaseFromBloombergApiIndexAndData
 	var index models.BaseFromBloombergApiIndexAndData
@@ -184,7 +205,7 @@ func (this *BloombergController) PCSGImportHistoryData() {
 	indexes = append(indexes, index)
 
 	// 写入数据
-	if e := services.PCSGWrite2BaseBloomberg(indexes, req.IsVCode, req.ExtraLetter, "", ""); e != nil {
+	if e := services.PCSGWrite2BaseBloomberg(indexes, conf.VCode, conf.ExtraLetter, conf.IndexNamePrefix, conf.IndexCodeSuffix); e != nil {
 		br.Msg = "刷新失败"
 		br.ErrMsg = "PCSG-写入Bloomberg数据源失败, Err: " + e.Error()
 		return

+ 35 - 0
controllers/base_from_fenwei.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_index_lib/models"
 	"eta/eta_index_lib/services"
 	"eta/eta_index_lib/utils"
+	"github.com/beego/beego/v2/core/logs"
 	"strconv"
 	"time"
 )
@@ -253,3 +254,37 @@ func (this *FenweiController) BaseIndexList() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// NetDataHandle
+// @Title 汾渭网页数据落库
+// @Description 汾渭网页数据落库
+// @Success 200 string "操作成功"
+// @router /net/data/handle [post]
+func (this *FenweiController) NetDataHandle() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.HandleFenWeiNetDataReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	logs.Info("汾渭网页数据落库请求参数 List size:", len(req.List))
+	err = services.NetDataHandle(req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 168 - 0
controllers/base_from_hisugar.go

@@ -0,0 +1,168 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services"
+	"eta/eta_index_lib/utils"
+	"strconv"
+	"time"
+)
+
+// HisugarController 泛糖科技
+type HisugarController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增泛糖科技指标接口
+// @Description 新增泛糖科技指标接口
+// @Success 200 {object} models.AddEdbInfoReq
+// @router /add [post]
+func (this *HisugarController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+
+	source := utils.DATA_SOURCE_HISUGAR
+	cacheKey = utils.CACHE_EDB_DATA_ADD + strconv.Itoa(source) + "_" + req.EdbCode
+	if !utils.Rc.IsExist(cacheKey) {
+		utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+		err = models.AddEdbDataFromHisugar(req.EdbCode)
+		if err != nil {
+			br.Msg = "获取指标信息失败!"
+			br.ErrMsg = "获取指标信息失败 AddEdbDataFromHisugar,Err:" + err.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success =  true
+		br.Msg = "获取成功"
+	} else {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+	}
+}
+
+// Refresh
+// @Title 刷新泛糖科技指标接口
+// @Description 刷新泛糖科技指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *HisugarController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_HISUGAR
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+
+	err = models.RefreshEdbDataFromHisugar(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromHisugar,Err:" + err.Error()
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// HandleEdbData
+// @Title 处理泛糖科技指标的接口
+// @Description 处理泛糖科技指标的接口
+// @Success 200 string "操作成功"
+// @router /handle/edb_data [post]
+func (this *HisugarController) HandleEdbData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.HandleHisugarEdbDataReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if err = services.HandleHisugarIndex(req.List); err != nil {
+		br.Msg = "处理失败"
+		br.ErrMsg = "处理失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 765 - 0
controllers/base_from_ly.go

@@ -0,0 +1,765 @@
+// @Author gmy 2024/8/13 16:01:00
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"time"
+)
+
+type BaseFromLyController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增粮油商务网指标
+// @Description 新增粮油商务网指标
+// @router /add [post]
+func (this *BaseFromLyController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_LY
+	var req models.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_ADD + strconv.Itoa(source) + "_" + req.EdbCode
+	if !utils.Rc.IsExist(cacheKey) {
+		utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+		err = models.AddEdbDataFromLy(req.EdbCode)
+		if err != nil {
+			br.Msg = "获取指标信息失败!"
+			br.ErrMsg = "获取指标信息失败 AddEdbDataFromSci99,Err:" + err.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+	} else {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+	}
+}
+
+// Refresh
+// @Title 刷新粮油商务网指标接口
+// @Description 刷新粮油商务网指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *BaseFromLyController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	source := utils.DATA_SOURCE_LY
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	dataUpdateTime := time.Now().Format(utils.FormatDateTime)
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	err = models.RefreshEdbDataFromBloomberg(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromBloomberg,Err:" + err.Error()
+		return
+	}
+
+	// 更新指标最大最小值
+	erDataUpdateDate, err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfoDataUpdate(edbInfo, dataUpdateTime)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+	// 添加指标刷新成功日志
+	if erDataUpdateDate != "" {
+		_ = services.AddEdbInfoUpdateLog(edbInfo.EdbInfoId, 1, "", 1, "", 0, 0)
+	} else {
+		_ = services.AddEdbInfoUpdateLog(edbInfo.EdbInfoId, 1, "", 2, "未刷新到数据", 0, 0)
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// GetLyClassifyByName
+// @Title 获取分类
+// @Description 获取分类
+// @Success 200 {object} models.BaseFromLyClassify
+// @router /get/ly/classify/by/name [post]
+func (this *BaseFromLyController) GetLyClassifyByName() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		CategoryName string `json:"CategoryName"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+	categoryName := reqData.CategoryName
+	if categoryName == "" {
+		br.Msg = "请输入分类!"
+		br.ErrMsg = "请输入分类"
+		return
+	}
+
+	lyClassify, err := models.GetLyClassifyByName(categoryName)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyClassify
+	br.Msg = "获取成功"
+}
+
+// GetLyIndexRecordByUrl
+// @Title 根据url获取指标已读取记录
+// @Description 根据url获取指标已读取记录
+// @Success 200 {object} models.BaseFromLyIndexRecord
+// @router /get/ly/index/record/by/url [post]
+func (this *BaseFromLyController) GetLyIndexRecordByUrl() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		Url string `json:"Url"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+	url := reqData.Url
+	if url == "" {
+		br.Msg = "请输入地址链接!"
+		br.ErrMsg = "请输入地址链接"
+		return
+	}
+
+	lyIndexRecord, err := models.GetLyIndexRecordByUrl(url)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyIndexRecord
+	br.Msg = "获取成功"
+}
+
+// AddLyIndexRecord
+// @Title 维护指标数据读取进度到数据库
+// @Description 维护指标数据读取进度到数据库
+// @Success 200 string "处理成功"
+// @router /add/ly/index/record [post]
+func (this *BaseFromLyController) AddLyIndexRecord() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.BaseFromLyIndexRecord
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	id, err := models.AddLyIndexRecord(&req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = id
+	br.Msg = "处理成功"
+}
+
+// AddLyDataList
+// @Title 新增指标数据
+// @Description 新增指标数据列表
+// @Success 200 string "处理成功"
+// @router /add/ly/data/list [post]
+func (this *BaseFromLyController) AddLyDataList() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req []models.BaseFromLyData
+	fmt.Println(string(this.Ctx.Input.RequestBody))
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	err = models.AddLyDataList(req)
+	if err != nil {
+		br.Msg = "新增指标数据失败!"
+		br.ErrMsg = "新增指标数据失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// AddLyIndex
+// @Title 新增指标
+// @Description 新增指标
+// @Success 200 string "处理成功"
+// @router /add/ly/index [post]
+func (this *BaseFromLyController) AddLyIndex() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.BaseFromLyIndex
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	indexId, err := models.AddLyIndex(&req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = indexId
+	br.Msg = "处理成功"
+}
+
+// GetLyDataByIndexIdAndDataTime
+// @Title 根据指标id和时间获取指标数据
+// @Description 根据指标id和时间获取指标数据
+// @Success 200 {object} models.BaseFromLyData
+// @router /get/ly/data/by/index/id/and/data/time [post]
+func (this *BaseFromLyController) GetLyDataByIndexIdAndDataTime() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		IndexId  int    `json:"IndexId"`
+		DataTime string `json:"DataTime"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexId := reqData.IndexId
+	if indexId == 0 {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+
+	dataTime := reqData.DataTime
+	if dataTime == "" {
+		br.Msg = "请输入时间!"
+		br.ErrMsg = "请输入时间"
+		return
+	}
+
+	lyData, err := models.GetLyDataByIndexIdAndDataTime(indexId, dataTime)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyData
+	br.Msg = "获取成功"
+}
+
+// GetLyDataByIndexIdAndDataTimeYM
+// @Title 根据指标id和年月时间获取指标数据
+// @Description 根据指标id和年月时间获取指标数据
+// @Success 200 {object} models.BaseFromLyData
+// @router /get/ly/data/by/index/id/and/data/time/ym [post]
+func (this *BaseFromLyController) GetLyDataByIndexIdAndDataTimeYM() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		IndexId   int    `json:"IndexId"`
+		YearMonth string `json:"YearMonth"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexId := reqData.IndexId
+	if indexId == 0 {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+
+	yearMonth := reqData.YearMonth
+
+	if yearMonth == "" {
+		br.Msg = "请输入时间!"
+		br.ErrMsg = "请输入时间"
+		return
+	}
+
+	lyData, err := models.GetLyDataByIndexIdAndDataTimeYM(indexId, yearMonth)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyData
+	br.Msg = "获取成功"
+}
+
+// UpdateLyDataById
+// @Title 更新数据源指标数据
+// @Description 更新数据源指标数据
+// @Success 200 string "处理成功"
+// @router /update/ly/data/by/id [post]
+func (this *BaseFromLyController) UpdateLyDataById() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		Id    int     `json:"Id"`
+		Value float64 `json:"Value"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	id := reqData.Id
+	if id == 0 {
+		br.Msg = "请输入id!"
+		br.ErrMsg = "请输入id"
+		return
+	}
+
+	value := reqData.Value
+	if value == 0 {
+		br.Msg = "请输入值!"
+		br.ErrMsg = "请输入值"
+		return
+	}
+
+	err = models.UpdateLyDataById(id, value)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// GetLyEdbDataByIndexCodeAndDataTime
+// @Title 根据指标编码和模糊日期获取指标库数据
+// @Description 根据指标编码和模糊日期获取指标库数据
+// @Success 200 {object} []models.EdbDataLy
+// @router /get/ly/edb/data/by/index/code/and/data/time [post]
+func (this *BaseFromLyController) GetLyEdbDataByIndexCodeAndDataTime() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		IndexCode string `json:"IndexCode"`
+		DataTime  string `json:"DataTime"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexCode := reqData.IndexCode
+	if indexCode == "" {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+
+	dataTime := reqData.DataTime
+	if dataTime == "" {
+		br.Msg = "请输入时间!"
+		br.ErrMsg = "请输入时间"
+		return
+	}
+
+	lyEdbData, err := models.GetLyEdbDataByIndexCodeAndDataTime(indexCode, dataTime)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyEdbData
+	br.Msg = "获取成功"
+}
+
+// GetLyEdbDataByIndexCodeAndExactDataTime
+// @Title 根据指标编码和精确日期获取指标库数据
+// @Description 根据指标编码和精确日期获取指标库数据
+// @Success 200 {object} []models.EdbDataLy
+// @router /get/ly/edb/data/by/index/code/and/exact/data/time [post]
+func (this *BaseFromLyController) GetLyEdbDataByIndexCodeAndExactDataTime() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		IndexCode string `json:"IndexCode"`
+		DataTime  string `json:"DataTime"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexCode := reqData.IndexCode
+	if indexCode == "" {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+
+	dataTime := reqData.DataTime
+	if dataTime == "" {
+		br.Msg = "请输入时间!"
+		br.ErrMsg = "请输入时间"
+		return
+	}
+
+	lyEdbData, err := models.GetLyEdbDataByIndexCodeAndExactDataTime(indexCode, dataTime)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyEdbData
+	br.Msg = "获取成功"
+}
+
+// UpdateLyEdbDataById
+// @Title 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+// @Description 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+// @Success 200 string "处理成功"
+// @router /update/ly/edb/data/by/id [post]
+func (this *BaseFromLyController) UpdateLyEdbDataById() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		Id    int     `json:"Id"`
+		Value float64 `json:"Value"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	id := reqData.Id
+	if id == 0 {
+		br.Msg = "请输入id!"
+		br.ErrMsg = "请输入id"
+		return
+	}
+
+	value := reqData.Value
+	if value == 0 {
+		br.Msg = "请输入值!"
+		br.ErrMsg = "请输入值"
+		return
+	}
+
+	err = models.UpdateLyEdbDataById(id, value)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// GetLyIndexByCode
+// @Title 查询指标编码是否存在
+// @Description 查询指标编码是否存在
+// @Success 200 {object} models.BaseFromLyIndex
+// @router /get/ly/index/by/code [post]
+func (this *BaseFromLyController) GetLyIndexByCode() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var reqData struct {
+		IndexCode string `json:"IndexCode"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexCode := reqData.IndexCode
+	if indexCode == "" {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+
+	lyIndex, err := models.GetLyIndexByCode(indexCode)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyIndex
+	br.Msg = "获取成功"
+}
+
+// GetEdbInfoByIndexCode
+// @Title 根据指标code获取指标信息
+// @Description 根据指标code获取指标信息
+// @Success 200 {object} models.EdbInfo
+// @router /get/edb/info/by/index/code [post]
+func (this *BaseFromLyController) GetEdbInfoByIndexCode() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var reqData struct {
+		IndexCode string `json:"IndexCode"`
+		Source    int    `json:"Source"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+
+	indexCode := reqData.IndexCode
+	if indexCode == "" {
+		br.Msg = "请输入指标id!"
+		br.ErrMsg = "请输入指标id"
+		return
+	}
+	source := reqData.Source
+	if source == 0 {
+		br.Msg = "请输入来源!"
+		br.ErrMsg = "请输入来源"
+		return
+	}
+
+	lyEdbData, err := models.GetEdbInfoByEdbCode(source, indexCode)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyEdbData
+	br.Msg = "获取成功"
+}
+
+// AddBatchLyEdbData
+// @Title 批量增加粮油指标库数据
+// @Description 批量增加粮油指标库数据
+// @Success 200 string "处理成功"
+// @router /add/batch/ly/edb/data [post]
+func (this *BaseFromLyController) AddBatchLyEdbData() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		_ = utils.Rc.Delete(cacheKey)
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req []models.EdbDataLy
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	err = models.AddLyEdbDataList(req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 152 - 0
controllers/base_from_trade_analysis.go

@@ -0,0 +1,152 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	tradeAnalysisModel "eta/eta_index_lib/models/trade_analysis"
+	tradeAnalysisService "eta/eta_index_lib/services/trade_analysis"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"time"
+)
+
+// BaseFromTradeAnalysisController 持仓分析指标
+type BaseFromTradeAnalysisController struct {
+	BaseAuthController
+}
+
+// EdbRefresh
+// @Title 指标库刷新
+// @Description 指标库刷新
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /edb/refresh [post]
+func (this *BaseFromTradeAnalysisController) EdbRefresh() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId < 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+	edbOb := new(models.EdbTradeAnalysis)
+	source := edbOb.GetSource()
+
+	cacheKey := fmt.Sprintf("%s_%d_%s", utils.CACHE_EDB_DATA_REFRESH, source, req.EdbCode)
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+	}()
+
+	// 获取指标详情
+	edbInfo, e := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if e != nil {
+		br.Msg = "指标不存在"
+		br.ErrMsg = fmt.Sprintf("指标不存在, %v", e)
+		return
+	}
+	formula := edbInfo.CalculateFormula
+
+	// 校验计算公式
+	var extraConfig tradeAnalysisModel.WarehouseExtraConfig
+	if formula == `` {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式有误, conf: %s", formula)
+		return
+	}
+	if e := json.Unmarshal([]byte(formula), &extraConfig); e != nil {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式解析失败, err: %v; conf: %s", e, formula)
+		return
+	}
+	if extraConfig.Exchange == "" {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式交易所异常, conf: %s", formula)
+		return
+	}
+	if extraConfig.ClassifyName == "" {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式品种异常, conf: %s", formula)
+		return
+	}
+	if len(extraConfig.Contracts) == 0 {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式合约异常, conf: %s", formula)
+		return
+	}
+	if len(extraConfig.Companies) != 1 {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式期货公司异常, conf: %s", formula)
+		return
+	}
+	if extraConfig.PredictRatio < 0 || extraConfig.PredictRatio > 1 {
+		br.Msg = "指标计算公式有误"
+		br.ErrMsg = fmt.Sprintf("指标计算公式估计参数异常, conf: %s", formula)
+		return
+	}
+
+	// 获取指标数据, 该图表未用实际指标, 为了统一数据格式用ChartEdbInfoMapping
+	companyTradeData, e := tradeAnalysisService.GetOriginTradeData(extraConfig.Exchange, extraConfig.ClassifyName, extraConfig.Contracts, extraConfig.Companies, extraConfig.PredictRatio)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取期货公司持仓加总数据失败, %v", e)
+		return
+	}
+	if len(companyTradeData) == 0 {
+		br.Msg = "期货数据为空"
+		return
+	}
+
+	// 转换持仓数据为指标数据
+	convertData, e := tradeAnalysisService.FormatCompanyTradeData2EdbData(companyTradeData[0], extraConfig.WarehouseChartType)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("持仓数据转为指标数据失败, %v", e)
+		return
+	}
+
+	// 刷新指标
+	if e = edbOb.Refresh(edbInfo, convertData); e != nil {
+		br.Msg = "刷新指标失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标失败, %v", e)
+		return
+	}
+
+	// 更新指标最值
+	if e = edbOb.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo); e != nil {
+		br.Msg = "刷新指标失败"
+		br.ErrMsg = fmt.Sprintf("更新指标最值失败, %v", e)
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 22 - 8
controllers/factor_edb_series/factor_edb_series.go

@@ -198,7 +198,7 @@ func (this *FactorEdbSeriesController) ChartRecalculate() {
 	if utils.Rc.IsExist(cacheKey) {
 		br.Ret = 501
 		br.Success = true
-		br.Msg = fmt.Sprintf("系统处理中, 请稍后重试, 图表ID: %s", req.ChartInfoId)
+		br.Msg = fmt.Sprintf("系统处理中, 请稍后重试, 图表ID: %d", req.ChartInfoId)
 		return
 	}
 	utils.Rc.SetNX(cacheKey, 1, 10*time.Minute)
@@ -232,7 +232,9 @@ func (this *FactorEdbSeriesController) ChartRecalculate() {
 	edbInfoIds := make([]int, 0)
 	for _, v := range mappings {
 		seriesIds = append(seriesIds, v.FactorEdbSeriesId)
-		edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+		if v.EdbInfoId > 0 {
+			edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+		}
 	}
 
 	// 获取因子指标及系列信息
@@ -253,12 +255,17 @@ func (this *FactorEdbSeriesController) ChartRecalculate() {
 		}
 	}
 	edbIdItem := make(map[int]*models.EdbInfo)
-	edbItems, e := models.GetEdbInfoByIdList(edbInfoIds)
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取因子指标失败, Err: " + e.Error()
-		return
+	edbItems := make([]*models.EdbInfo, 0)
+	var err error
+	if len(edbInfoIds) > 0 {
+		edbItems, err = models.GetEdbInfoByIdList(edbInfoIds)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取因子指标失败, Err: " + err.Error()
+			return
+		}
 	}
+
 	for _, v := range edbItems {
 		edbIdItem[v.EdbInfoId] = v
 	}
@@ -269,9 +276,10 @@ func (this *FactorEdbSeriesController) ChartRecalculate() {
 	calculateDataOb := new(models.FactorEdbSeriesCalculateData)
 	for _, v := range mappings {
 		edbItem := edbIdItem[v.EdbInfoId]
-		if edbItem == nil {
+		if edbItem == nil && v.CalculateType == models.FactorEdbSeriesChartCalculateTypeCorrelation {
 			continue
 		}
+
 		seriesItem := seriesIdItem[v.FactorEdbSeriesId]
 		if seriesItem == nil {
 			continue
@@ -405,6 +413,12 @@ func (this *FactorEdbSeriesController) ChartRecalculate() {
 					utils.FileLog.Info(fmt.Sprintf("相关性-更新矩阵数据失败, err: %v", e))
 					return
 				}
+			} else if chartMapping.CalculateType == models.FactorEdbSeriesChartCalculateTypeRange {
+				err, errMsg := services.RangeAnalysisChartCalculate(seriesItem.FactorEdbSeriesId, req.ChartInfoId, chartMapping)
+				if err != nil {
+					utils.FileLog.Info(fmt.Sprintf("区间分析图表数据更新失败, err: %v,errMsg %s", err, errMsg))
+					return
+				}
 			}
 		}(v, edbItem, seriesItem)
 	}

+ 73 - 1
controllers/shanghai_smm.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"encoding/json"
+	"eta/eta_index_lib/logic"
 	"eta/eta_index_lib/models"
 	shanghaismm "eta/eta_index_lib/services/shanghai_smm"
 	"eta/eta_index_lib/utils"
@@ -28,13 +29,17 @@ var ShanghaiSmmNameToCodeMap = make(map[string]string)
 // @router /refresh/list [post]
 func (this *ShanghaiSmmController) RefreshData() {
 	br := new(models.BaseResponse).Init()
+	var err error
 	defer func() {
+		if err != nil {
+			utils.FileLog.Error("SMM-RefreshData err:", err)
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
 
 	var req []*shanghaismm.EdbInfoData
-	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+	if err = json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
 		br.Msg = "参数解析异常!"
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
@@ -56,6 +61,7 @@ func (this *ShanghaiSmmController) RefreshData() {
 	updateIndexList := make([]*models.BaseFromSmmIndex, 0)
 	addDateList := make([]*models.BaseFromSmmData, 0)
 	readyDateList := make([]*models.BaseFromSmmData, 0)
+	updateIndexCode := make([]string, 0)
 	for _, v := range req {
 		indexInfo := ShanghaiSmmNameToIndexMap[v.IndexName]
 		if indexInfo == nil {
@@ -121,6 +127,7 @@ func (this *ShanghaiSmmController) RefreshData() {
 				item.ModifyTime = time.Now()
 				item.DataTimestamp = time.Now().UnixMilli()
 				addDateList = append(addDateList, item)
+				updateIndexCode = append(updateIndexCode, indexInfo.IndexCode)
 			} else {
 				smmData, err := models.GetBaseFromSmmDataByCodeAndDate(indexInfo.IndexCode, v.RenewDate)
 				if err != nil {
@@ -143,6 +150,7 @@ func (this *ShanghaiSmmController) RefreshData() {
 					if err != nil {
 						utils.FileLog.Info("indexCode: %s,更新指标数据失败,Err:%s", v.IndexCode, err.Error())
 					}
+					updateIndexCode = append(updateIndexCode, indexInfo.IndexCode)
 				}
 
 			}
@@ -201,6 +209,38 @@ func (this *ShanghaiSmmController) RefreshData() {
 			return
 		}
 	}
+	var messages []string
+	defer func() {
+		if len(messages) > 0 {
+			errMsg := strings.Join(messages, ",")
+			utils.FileLog.Info("SMM RefreshData errMsg: %s", errMsg)
+		}
+	}()
+	for _, v := range updateIndexCode {
+		itemInfo, err := models.GetSmmIndexInfoMaxAndMinInfo(v)
+		if err == nil && itemInfo != nil {
+			e := models.ModifySmmIndexMaxAndMinInfo(v, itemInfo)
+			if e != nil {
+				messages = append(messages, fmt.Sprintf("更新指标最大最小值失败, indexCode: %s, err: %s", v, e.Error()))
+				return
+			}
+		}
+
+		// 同步刷新ETA图库有色的指标
+		{
+			// 获取指标详情
+			edbInfo, e := models.GetEdbInfoByEdbCode(utils.DATA_SOURCE_YS, v)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				messages = append(messages, fmt.Sprintf("刷新ETA指标异常, indexCode: %s, err: %s", v, e.Error()))
+			}
+
+			// 已经加入到指标库的话,那么就去更新ETA指标库吧
+			if edbInfo != nil {
+				go logic.RefreshBaseEdbInfo(edbInfo, ``)
+			}
+		}
+	}
+
 	br.Msg = "数据刷新成功"
 	br.Success = true
 	br.Ret = 200
@@ -345,6 +385,38 @@ func (this *ShanghaiSmmController) RefreshExcel() {
 	}
 	// 不修改
 	fmt.Println(len(updateDataList))
+	var messages []string
+	defer func() {
+		if len(messages) > 0 {
+			errMsg := strings.Join(messages, ",")
+			utils.FileLog.Info("SMM RefreshExcel errMsg: %s", errMsg)
+		}
+	}()
+	for _, v := range updateDataList {
+		itemInfo, err := models.GetSmmIndexInfoMaxAndMinInfo(v.IndexCode)
+		if err == nil && itemInfo != nil {
+			e := models.ModifySmmIndexMaxAndMinInfo(v.IndexCode, itemInfo)
+			if e != nil {
+				messages = append(messages, fmt.Sprintf("更新指标最大最小值失败, indexCode: %s, err: %s", v.IndexCode, e.Error()))
+				return
+			}
+		}
+
+		// 同步刷新ETA图库有色的指标
+		{
+			// 获取指标详情
+			edbInfo, e := models.GetEdbInfoByEdbCode(utils.DATA_SOURCE_YS, v.IndexCode)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				messages = append(messages, fmt.Sprintf("刷新ETA指标异常, indexCode: %s, err: %s", v.IndexCode, e.Error()))
+			}
+
+			// 已经加入到指标库的话,那么就去更新ETA指标库吧
+			if edbInfo != nil {
+				go logic.RefreshBaseEdbInfo(edbInfo, ``)
+			}
+		}
+
+	}
 	// if len(updateDataList) > 0 {
 	// 	err = models.UpdateBaseFromSmmDataListByIndexCode(updateDataList)
 	// 	if err != nil {

+ 1 - 1
logic/profit_chart_info.go

@@ -714,7 +714,7 @@ func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earl
 							newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)
 
 							// 赋值平均值
-							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
+							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).Round(4).Float64()
 							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
 						}
 					}

+ 46 - 7
models/base_calculate.go

@@ -303,14 +303,28 @@ func (obj BaseCalculate) Tbz() (dateDataMap map[time.Time]float64, err error, er
 
 	var dateArr []time.Time
 	dataMap := make(map[string]*EdbInfoData) // 避免因为时间戳导致的日期不对,还是用string来表示比较合适
+	var firstDataTime time.Time              // 第一条数据的日期
 	for _, v := range dataList {
 		v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location())
 		dateArr = append(dateArr, v.DataTime)
 		dataMap[v.DataTime.Format(utils.FormatDate)] = v
+		firstDataTime = v.DataTime
 	}
 
+	if firstDataTime.IsZero() {
+		errMsg = "无数据"
+		err = errors.New(errMsg)
+		return
+	}
+	firstDataTime = firstDataTime.AddDate(1, 0, 0)
+
 	// 开始计算
 	for _, currentDate := range dateArr {
+		// 如果当前日期的日期早于数据最早的日期,那么就不往下执行了
+		if currentDate.Before(firstDataTime) {
+			break
+		}
+
 		//当前日期
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		currentItem, ok := dataMap[currentDateStr]
@@ -394,7 +408,7 @@ func tbzDiv(a, b float64) float64 {
 	if b != 0 {
 		af := decimal.NewFromFloat(a)
 		bf := decimal.NewFromFloat(b)
-		val, _ = af.Div(bf).Sub(decimal.NewFromFloat(1)).RoundCeil(4).Float64()
+		val, _ = af.Div(bf).Sub(decimal.NewFromFloat(1)).Round(4).Float64()
 	}
 	return val
 }
@@ -494,7 +508,7 @@ func (obj BaseCalculate) Tcz() (dateDataMap map[time.Time]float64, err error, er
 func tczSub(a, b float64) float64 {
 	af := decimal.NewFromFloat(a)
 	bf := decimal.NewFromFloat(b)
-	val, _ := af.Sub(bf).RoundCeil(4).Float64()
+	val, _ := af.Sub(bf).Round(4).Float64()
 	return val
 }
 
@@ -559,7 +573,7 @@ func (obj BaseCalculate) Nszydpjjs() (dateDataMap map[time.Time]float64, err err
 		}
 		af := totalVal //decimal.NewFromFloat(totalVal)
 		bf := decimal.NewFromFloat(float64(valArrLen))
-		val, _ := af.Div(bf).RoundCeil(4).Float64()
+		val, _ := af.Div(bf).Round(4).Float64()
 
 		dateDataMap[currentDate] = val
 	}
@@ -636,7 +650,7 @@ func hbzDiv(current, pre float64) (val float64, ok bool) {
 	}
 	currentVal := decimal.NewFromFloat(current)
 	preVal := decimal.NewFromFloat(pre)
-	val, _ = currentVal.Sub(preVal).Div(preVal).RoundCeil(4).Float64()
+	val, _ = currentVal.Sub(preVal).Div(preVal).Round(4).Float64()
 	//valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4)
 	ok = true
 
@@ -703,7 +717,7 @@ func (obj BaseCalculate) Hcz() (dateDataMap map[time.Time]float64, err error, er
 func hczDiv(current, pre float64) float64 {
 	currentVal := decimal.NewFromFloat(current)
 	preVal := decimal.NewFromFloat(pre)
-	val, _ := currentVal.Sub(preVal).RoundCeil(4).Float64()
+	val, _ := currentVal.Sub(preVal).Round(4).Float64()
 	//valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4)
 
 	return val
@@ -1014,7 +1028,7 @@ func (obj BaseCalculate) TimeShift() (dateDataMap map[time.Time]float64, err err
 
 		newDate := currentDate.AddDate(0, 0, shiftDay)
 
-		val, _ := decimal.NewFromFloat(currentItem.Value).RoundCeil(4).Float64()
+		val, _ := decimal.NewFromFloat(currentItem.Value).Round(4).Float64()
 
 		dateDataMap[newDate] = val
 	}
@@ -1142,7 +1156,7 @@ func cjjxSub(currValue float64, pastValue []float64) (value float64, ok bool) {
 		tmpVal := decimal.NewFromFloat(pastValue[k])
 		bf = bf.Add(tmpVal)
 	}
-	value, _ = af.Sub(bf.Div(numDecimal)).RoundCeil(4).Float64()
+	value, _ = af.Sub(bf.Div(numDecimal)).Round(4).Float64()
 	ok = true
 
 	return
@@ -1883,3 +1897,28 @@ func TransDateData2EdbData(dateData map[time.Time]float64) (edbData []*EdbInfoDa
 	})
 	return
 }
+
+// EdbInfoSearchDataToEdbInfoData
+// @Description: 数据格式转换
+// @author: Roc
+// @datetime 2024-08-22 16:30:34
+// @param list []*EdbInfoSearchData
+// @return result []*EdbInfoData
+// @return err error
+func EdbInfoSearchDataToEdbInfoData(list []*EdbInfoSearchData) (result []*EdbInfoData, err error) {
+	result = make([]*EdbInfoData, 0)
+	for _, v := range list {
+		dataTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		result = append(result, &EdbInfoData{
+			EdbDataId: v.EdbDataId,
+			DataTime:  dataTime,
+			Value:     v.Value,
+		})
+	}
+
+	return
+}

+ 3 - 5
models/base_from_bloomberg.go

@@ -354,9 +354,7 @@ type BaseFromBloombergApiIndexData struct {
 
 // PCSGImportHistoryDataReq 导入历史数据
 type PCSGImportHistoryDataReq struct {
-	IndexCode       string                `description:"指标编码"`
-	DataMap         map[time.Time]float64 `description:"数据日期/值"`
-	IsVCode         bool                  `description:"是否指标编码中间加V"`
-	ExtraLetter     string                `description:"指标编码中间额外加的字母...比如V"`
-	IndexNamePrefix string                `description:"指标名称前缀"`
+	IndexCode string                `description:"指标编码"`
+	DataMap   map[time.Time]float64 `description:"数据日期/值"`
+	TaskKey   string                `description:"导入数据对应的任务Key"`
 }

+ 81 - 2
models/base_from_fenwei.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"errors"
 	"eta/eta_index_lib/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
@@ -305,6 +306,23 @@ type HandleFenweiExcelDataReq struct {
 	TerminalCode string `description:"编码"`
 }
 
+type FenWeiNetIndexInfo struct {
+	FenweiIndexId int64  `orm:"column(fenwei_index_id);pk"`
+	IndexName     string `description:"指标名称"`
+	//IndexCode    string  `description:"指标编码"`
+	Unit         string      `description:"单位"`
+	Frequency    string      `description:"频度"`
+	TerminalCode string      `description:"编码"`
+	ClassifyName string      `description:"分类名称"`
+	DataTime     string      `description:"数据时间"`
+	Value        interface{} `description:"数据值"`
+}
+
+type HandleFenWeiNetDataReq struct {
+	List         []*FenWeiNetIndexInfo
+	TerminalCode string `description:"编码"`
+}
+
 func (y *BaseFromFenweiData) GetMaxAndMinDateByIndexCode(indexCode string) (item *EdbInfoMaxAndMinInfo, err error) {
 	o := orm.NewOrm()
 	sql := ` SELECT MIN(data_time) AS min_date,MAX(data_time) AS max_date,MIN(value) AS min_value,MAX(value) AS max_value FROM base_from_fenwei_data WHERE index_code=? `
@@ -338,10 +356,15 @@ func (y *BaseFromFenweiClassify) Update(updateCols []string) (err error) {
 	return
 }
 
-func (y *BaseFromFenweiClassify) GetByClassifyName(classifyName string) (item *BaseFromFenweiClassify, err error) {
+func (y *BaseFromFenweiClassify) GetByClassifyName(classifyName string) (item []*BaseFromFenweiClassify, err error) {
 	o := orm.NewOrm()
 	sql := ` SELECT * FROM base_from_fenwei_classify WHERE classify_name=? `
-	err = o.Raw(sql, classifyName).QueryRow(&item)
+	_, err = o.Raw(sql, classifyName).QueryRows(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
 	return
 }
 
@@ -413,3 +436,59 @@ func MultiUpdateBaseFromFenweiDataValue(items []*BaseFromFenweiData) (err error)
 	}
 	return
 }
+
+// GetBaseFromFenWeiIndexByIndexName 根据指标名称查询指标
+func GetBaseFromFenWeiIndexByIndexName(indexName string) (item *BaseFromFenweiIndex, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_fenwei_index WHERE index_name=? `
+	err = o.Raw(sql, indexName).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// GetBaseFromFenweiDataByIndexCodeAndDataTime 根据指标编码和dataTime查询指标数据
+func GetBaseFromFenweiDataByIndexCodeAndDataTime(indexCode, dataTime string) (item *BaseFromFenweiData, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_fenwei_data WHERE index_code=? AND data_time=? `
+	err = o.Raw(sql, indexCode, dataTime).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
+	return
+}
+
+// BatchAddBaseFromFenWeiData 批量插入指标数据
+func BatchAddBaseFromFenWeiData(items []*BaseFromFenweiData) (err error) {
+	if len(items) == 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(1, items)
+	return
+}
+
+// AddBaseFromFenWeiClassify 新增汾渭分类
+func AddBaseFromFenWeiClassify(item *BaseFromFenweiClassify) (classifyId int64, err error) {
+	o := orm.NewOrm()
+	classifyId, err = o.Insert(item)
+	return
+}
+
+// GetBaseFromFenWeiIndexByIndexCode 跟据指标编码查询指标信息
+func GetBaseFromFenWeiIndexByIndexCode(indexCode string) (item *BaseFromFenweiIndex, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_fenwei_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
+	return
+}

+ 276 - 0
models/base_from_hisugar.go

@@ -0,0 +1,276 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type BaseFromHisugarIndex struct {
+	BaseFromHisugarIndexId int       // 主键ID
+	IndexCode              string    // 指标编码
+	IndexName              string    // 指标名称
+	ClassifyId             uint      // 分类ID
+	Unit                   string    // 单位
+	Frequency              string    // 频度
+	Describe               string    // 指标描述
+	Sort                   int       // 排序
+	CreateTime             time.Time // 创建时间
+	ModifyTime             time.Time // 修改时间
+}
+
+type BaseFromHisugarData struct {
+	BaseFromHisugarDataId  int    // 数据表ID
+	BaseFromHisugarIndexId int    // 指标ID
+	IndexCode              string // 指标编码
+	DataTime               string
+	Value                  string
+	CreateTime             time.Time // 创建时间
+	ModifyTime             time.Time // 修改时间
+}
+
+
+//添加数据
+func AddBaseFromHisugarIndexMuti(items []*BaseFromHisugarIndex) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(500, items)
+	return
+}
+func AddBaseFromHisugarIndex(item *BaseFromHisugarIndex) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func AddBaseFromHisugarData(item *BaseFromHisugarData) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func AddEdbDataFromHisugar(edbCode string) (err error) {
+	o := orm.NewOrm()
+
+	hisugarBaseDataAll, err := GetHisugarDataByCode(edbCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return
+	}
+
+	var isAdd bool
+	addSql := ` INSERT INTO edb_data_hisugar(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	existMap := make(map[string]string)
+	for _, sv := range hisugarBaseDataAll {
+		eDate := sv.DataTime
+		var timeStr string
+		var dataTime time.Time
+		var sDataTime string
+		var timestamp int64
+
+		sDataTime = eDate
+		dataTime, err = time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+		if err != nil {
+			fmt.Println("time.Parse Err:" + eDate)
+			return err
+		}
+		timestamp = dataTime.UnixNano() / 1e6
+		timeStr = fmt.Sprintf("%d", timestamp)
+
+		value := strings.Replace(sv.Value, "%", "", -1)
+		if _, ok := existMap[sDataTime]; !ok {
+			addSql += GetAddSql("0", edbCode, sDataTime, timeStr, value)
+			fmt.Println("edbCode:", edbCode)
+			fmt.Println("sDataTime:", sDataTime)
+			fmt.Println("timeStr:", timeStr)
+			fmt.Println("value:", value)
+			isAdd = true
+		}
+		existMap[eDate] = value
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		utils.FileLog.Info("addSql:" + addSql)
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+// GetHisugarDataByCode
+func GetHisugarDataByCode(indexCode string) (items []*BaseFromHisugarData, err error) {
+	o := orm.NewOrm()
+	sql := "SELECT * FROM base_from_hisugar_data WHERE index_code=? "
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+
+// RefreshEdbDataFromHisugar 刷新隆众资讯
+func RefreshEdbDataFromHisugar(edbInfoId int, edbCode, startDate string) (err error) {
+	source := utils.DATA_SOURCE_OILCHEM
+	subSource := utils.DATA_SUB_SOURCE_EDB
+
+	o := orm.NewOrm()
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+
+	if edbCode != "" {
+		condition += " AND index_code=? "
+		pars = append(pars, edbCode)
+	}
+
+	if startDate != "" {
+		condition += " AND data_time>=? "
+		pars = append(pars, startDate)
+	}
+
+	hisugarDataList, err := GetBaseFromHisugarDataByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+
+	// 真实数据的最大日期  , 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool //是否找到配置日期的实际数据的值
+	{
+		edbDataInsertConfig, err = GetEdbDataInsertConfigByEdbId(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		}
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += " AND edb_info_id=? "
+	existPars = append(existPars, edbInfoId)
+	if startDate != "" {
+		existCondition += " AND data_time>=? "
+		existPars = append(existPars, startDate)
+	}
+
+	existList, err := GetEdbDataByCondition(source, subSource, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+	addSql := ` INSERT INTO edb_data_hisugar(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, v := range hisugarDataList {
+		item := v
+
+		eDate := item.DataTime
+		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+		if err != nil {
+			return err
+		}
+		if findItem, ok := existMap[v.DataTime]; !ok {
+			sValue := item.Value
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			addSql += GetAddSql(edbInfoIdStr, edbCode, eDate, timeStr, sValue)
+			isAdd = true
+		} else {
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 30) != item.Value {
+				err = ModifyEdbDataById(source, subSource, findItem.EdbDataId, item.Value)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		// 下面代码主要目的是处理掉手动插入的数据判断
+		{
+			if realDataMaxDate.IsZero() || dataTime.After(realDataMaxDate) {
+				realDataMaxDate = dataTime
+			}
+			if edbDataInsertConfigDate.IsZero() || dataTime.Equal(edbDataInsertConfigDate) {
+				isFindConfigDateRealData = true
+			}
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfoId, source, subSource, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshEdbDataFromSci add Err", err.Error())
+			return
+		}
+	}
+	return
+}
+
+func GetBaseFromHisugarDataByCondition(condition string, pars []interface{}) (list []*BaseFromHisugarData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_hisugar_data WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}
+
+
+type HandleHisugarEdbDataReq struct {
+	List []*BaseFromHisugarIndexReq
+}
+
+func GetBaseFromHisugarIndex() (list []*BaseFromHisugarIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_hisugar_index group by index_name `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetBaseFromHisugarData(indexCode, dataTime string) (item *BaseFromHisugarData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_hisugar_data where index_code=? And data_time = ? `
+	err = o.Raw(sql, indexCode, dataTime).QueryRow(&item)
+	return
+}
+
+// UpdateBaseFromSci99Data
+func UpdateBaseFromHisugarData(value , indexCode, dataTime string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE base_from_sci99_data SET value=?,modify_time=NOW() WHERE index_code = ? AND data_time = ? `
+	_, err = o.Raw(sql, value, indexCode, dataTime).Exec()
+	return
+}
+
+type BaseFromHisugarIndexReq struct {
+	BaseFromHisugarIndexId int       // 主键ID
+	IndexCode              string    // 指标编码
+	IndexName              string    // 指标名称
+	ClassifyId             uint      // 分类ID
+	Unit                   string    // 单位
+	Frequency              string    // 频度
+	Describe               string    // 指标描述
+	DataTime               string    // 数据日期
+	Sort                   int       // 排序
+	CreateTime             time.Time // 创建时间
+	ModifyTime             time.Time // 修改时间
+	IndexNameStr           string    // 指标名称字符串
+	MarketName             string    // 市场名称
+	Value                  string    // 值
+}

+ 32 - 0
models/base_from_ly_classify.go

@@ -0,0 +1,32 @@
+// @Author gmy 2024/8/7 9:26:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromLyClassify struct {
+	BaseFromLyClassifyId int    `orm:"column(base_from_ly_classify_id);pk"` // 分类ID
+	CreateTime           string `orm:"column(create_time)"`                 // 创建时间
+	ModifyTime           string `orm:"column(modify_time)"`                 // 修改时间
+	ClassifyName         string `orm:"column(classify_name)"`               // 分类名称
+	ParentId             int    `orm:"column(parent_id)"`                   // 上级id
+	Sort                 int    `orm:"column(sort)"`                        // 排序字段,越小越靠前
+	ClassifyNameEn       string `orm:"column(classify_name_en)"`            // 英文分类名称
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromLyClassify))
+}
+
+// GetLyClassifyByName 根据分类名称查询
+func GetLyClassifyByName(classifyName string) (item *BaseFromLyClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_ly_classify WHERE classify_name=?`
+	err = o.Raw(sql, classifyName).QueryRow(&item)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}

+ 72 - 0
models/base_from_ly_data.go

@@ -0,0 +1,72 @@
+// @Author gmy 2024/8/7 9:50:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromLyData struct {
+	BaseFromLyDataId  int     `orm:"column(base_from_ly_data_id);pk"` // 数据ID
+	CreateTime        string  `orm:"column(create_time)"`             // 创建时间
+	ModifyTime        string  `orm:"column(modify_time)"`             // 修改时间
+	BaseFromLyIndexId int     `orm:"column(base_from_ly_index_id)"`   // 指标id
+	IndexCode         string  `orm:"column(index_code)"`              // 指标编码
+	DataTime          string  `orm:"column(data_time)"`               // 数据日期
+	Value             float64 `orm:"column(value)"`                   // 数据值
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromLyData))
+}
+
+// AddLyDataList 批量插入数据记录列表
+func AddLyDataList(items []BaseFromLyData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// GetLyDataByIndexIdAndDataTime 根据指标id和数据日期查询数据
+func GetLyDataByIndexIdAndDataTime(indexId int, dataTime string) (items []BaseFromLyData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_ly_data WHERE base_from_ly_index_id=? AND data_time=?`
+	_, err = o.Raw(sql, indexId, dataTime).QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// GetLyDataByIndexIdAndDataTimeYM 根据指标id和数据日期的年月查询数据
+func GetLyDataByIndexIdAndDataTimeYM(indexId int, dataTime string) (items []BaseFromLyData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_ly_data WHERE base_from_ly_index_id=? AND data_time like ?`
+	_, err = o.Raw(sql, indexId, dataTime+"%").QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// UpdateLyDataById 根据主键id更新数据
+func UpdateLyDataById(dataId int, value float64) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE base_from_ly_data SET value=? WHERE base_from_ly_data_id=?`
+	_, err = o.Raw(sql, value, dataId).Exec()
+	return
+}
+
+// GetBaseFromLyDataByIndexCode 根据指标编码查询
+func GetBaseFromLyDataByIndexCode(condition string, pars []interface{}) (items []BaseFromLyData, err error) {
+	sql := `SELECT * FROM base_from_ly_data WHERE 1=1 `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}

+ 57 - 0
models/base_from_ly_index.go

@@ -0,0 +1,57 @@
+// Package models
+// @Author gmy 2024/8/7 9:38:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BaseFromLyIndex struct {
+	BaseFromLyIndexId    int    `orm:"column(base_from_ly_index_id);pk"` // 指标ID
+	CreateTime           string `orm:"column(create_time)"`              // 创建时间
+	ModifyTime           string `orm:"column(modify_time)"`              // 修改时间
+	BaseFromLyClassifyId int    `orm:"column(base_from_ly_classify_id)"` // 原始数据指标分类id
+	IndexCode            string `orm:"column(index_code)"`               // 指标编码
+	IndexName            string `orm:"column(index_name)"`               // 指标名称
+	Frequency            string `orm:"column(frequency)"`                // 频度
+	Unit                 string `orm:"column(unit)"`                     // 单位
+	EdbExist             int    `orm:"column(edb_exist)"`                // 指标库是否已添加:0-否;1-是
+}
+
+// 在 init 函数中注册模型
+func init() {
+	orm.RegisterModel(new(BaseFromLyIndex))
+}
+
+// AddLyIndexList 批量插入指标记录列表
+func AddLyIndexList(items []*BaseFromLyIndex) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// AddLyIndex 添加指标
+func AddLyIndex(item *BaseFromLyIndex) (int64, error) {
+	item.CreateTime = time.Now().Format("2006-01-02 15:04:05")
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}
+
+// GetLyIndexByCode 查询指标编码是否存在
+func GetLyIndexByCode(indexCode string) (item *BaseFromLyIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_ly_index WHERE index_code=?`
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
+	return
+}

+ 46 - 0
models/base_from_ly_index_record.go

@@ -0,0 +1,46 @@
+// Package models
+// @Author gmy 2024/8/7 9:38:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromLyIndexRecord struct {
+	BaseFromLyIndexRecordId int    `orm:"column(base_from_ly_index_record_id);pk"` // 指标记录ID
+	CreateTime              string `orm:"column(create_time)"`                     // 创建时间
+	ModifyTime              string `orm:"column(modify_time)"`                     // 修改时间
+	Product                 string `orm:"column(product)"`                         // 产品
+	Category                string `orm:"column(category)"`                        // 分类
+	Url                     string `orm:"column(url)"`                             // 指标页面地址
+	DataTime                string `orm:"column(data_time)"`                       // 数据日期
+}
+
+// 在 init 函数中注册模型
+func init() {
+	orm.RegisterModel(new(BaseFromLyIndexRecord))
+}
+
+// AddLyIndexRecord 添加指标记录
+func AddLyIndexRecord(item *BaseFromLyIndexRecord) (int64, error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}
+
+// GetLyIndexRecordByUrl 查询指标记录是否存在
+func GetLyIndexRecordByUrl(url string) (item *BaseFromLyIndexRecord, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_ly_index_record WHERE url=?`
+	err = o.Raw(sql, url).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
+	return
+}

+ 2 - 1
models/base_from_mysteel_chemical.go

@@ -226,6 +226,7 @@ type BaseFromMysteelChemicalIndex struct {
 	ModifyTime                        time.Time `description:"修改时间"`
 	IsStop                            int       `description:"是否停更:1:停更,0:未停更"`
 	TerminalCode                      string    `description:"终端编码"`
+	IsSupplierStop                    int       `description:"是否供应商停更:1:停更,0:未停更"`
 }
 
 type MysteelChemicalAPiCheck struct {
@@ -461,7 +462,7 @@ func (r *BaseFromMysteelChemicalData) Update(updateCols []string) (err error) {
 // Add 新增
 func (r *BaseFromMysteelChemicalData) Add(list []BaseFromMysteelChemicalData) (err error) {
 	o := orm.NewOrm()
-	_, err = o.InsertMulti(len(list), list)
+	_, err = o.InsertMulti(500, list)
 	return
 }
 

+ 1 - 1
models/base_from_yongyi.go

@@ -333,7 +333,7 @@ func (y *BaseFromYongyiClassify) GetByClassifyName(classifyName string) (item *B
 
 func (y *BaseFromYongyiClassify) GetParentClassify() (items []*BaseFromYongyiClassify, err error) {
 	o := orm.NewOrm()
-	sql := ` SELECT * FROM base_from_yongyi_classify WHERE classify_name in ("日度", "月度", "周度", "年度", "旬度") and parent_id=0 `
+	sql := ` SELECT * FROM base_from_yongyi_classify WHERE classify_name in ("日度", "月度", "周度", "年度", "旬度", "半年度", "年度") and parent_id=0 `
 	_, err = o.Raw(sql).QueryRows(&items)
 	return
 }

+ 1 - 1
models/base_predict_from_calculate.go

@@ -492,7 +492,7 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 				removeDateList = append(removeDateList, sk)
 				continue
 			}
-			saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String() //utils.SubFloatToString(calVal, 4)
+			saveValue := decimal.NewFromFloat(calVal).Round(4).String() //utils.SubFloatToString(calVal, 4)
 			if existVal, ok := dataMap[sk]; !ok {
 				dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
 				timestamp := dataTime.UnixNano() / 1e6

+ 4 - 0
models/common.go

@@ -58,6 +58,8 @@ func GetBaseEdbInfoModel(source int) (baseEdbInfoModel BaseEdbInfoInterface) {
 		baseEdbInfoModel = CalculateSum{}
 	case utils.DATA_SOURCE_CALCULATE_AVG:
 		baseEdbInfoModel = CalculateAvg{}
+	case utils.DATA_SOURCE_CALCULATE_RANGEANLYSIS:
+		baseEdbInfoModel = CalculateRangeAnalysis{}
 	default:
 
 	}
@@ -101,6 +103,8 @@ func GetBasePredictEdbInfoModel(source int) (baseEdbInfoModel BasePredictEdbInfo
 		baseEdbInfoModel = PredictPercentile{}
 	case utils.DATA_SOURCE_PREDICT_CALCULATE_ZSXY:
 		baseEdbInfoModel = PredictExponentialSmoothing{}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_RANGEANLYSIS:
+		baseEdbInfoModel = PredictCalculateRangeAnalysis{}
 	default:
 
 	}

+ 9 - 4
models/db.go

@@ -168,6 +168,10 @@ func initBaseIndex() {
 		new(BaseFromBusinessData), // 数据源中自有数据的明细数据表
 		new(BaseFromOilchemIndex),
 		new(BaseFromOilchemData),
+		new(BaseFromFenweiClassify),
+		new(EdbDataTradeAnalysis),
+		new(BaseFromHisugarIndex),
+		new(BaseFromHisugarData),
 	)
 }
 
@@ -210,10 +214,11 @@ func initBusinessEdb() {
 // initFactorEdbSeries 因子指标系列数据表
 func initFactorEdbSeries() {
 	orm.RegisterModel(
-		new(FactorEdbSeries),              // 因子指标系列
-		new(FactorEdbSeriesChartMapping),  // 因子指标系列-图表关联
-		new(FactorEdbSeriesMapping),       // 因子指标系列-指标计算数据
-		new(FactorEdbSeriesCalculateData), // 因子指标系列-指标关联
+		new(FactorEdbSeries),                  // 因子指标系列
+		new(FactorEdbSeriesChartMapping),      // 因子指标系列-图表关联
+		new(FactorEdbSeriesMapping),           // 因子指标系列-指标计算数据
+		new(FactorEdbSeriesCalculateData),     // 因子指标系列-指标关联
+		new(FactorEdbSeriesCalculateDataQjjs), // 因子指标系列-区间计算
 	)
 }
 

+ 1 - 1
models/edb_data_calculate_avg.go

@@ -402,7 +402,7 @@ func (obj CalculateAvg) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 		calVal = calVal / float64(len(sv))
 		// 有计算出来值,那么就从待删除指标中移除
 		delete(removeDateMap, sk)
-		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String()
+		saveValue := decimal.NewFromFloat(calVal).Round(4).String()
 		if existVal, ok := dataMap[sk]; !ok {
 			dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
 			timestamp := dataTime.UnixNano() / 1e6

+ 1 - 1
models/edb_data_calculate_cjjx.go

@@ -462,6 +462,6 @@ func CjjxSub(currValue float64, pastValue []float64) (value string) {
 	}
 	val, _ := af.Sub(bf.Div(numDecimal)).Float64()
 	//valStr := utils.SubFloatToString(val, 4)
-	valStr := decimal.NewFromFloat(val).RoundCeil(4).String()
+	valStr := decimal.NewFromFloat(val).Round(4).String()
 	return valStr
 }

+ 1 - 1
models/edb_data_calculate_hbz.go

@@ -297,6 +297,6 @@ func HbzDiv(current, pre float64) string {
 	currentVal := decimal.NewFromFloat(current)
 	preVal := decimal.NewFromFloat(pre)
 	val, _ := currentVal.Sub(preVal).Div(preVal).Float64()
-	valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4)
+	valStr := decimal.NewFromFloat(val).Round(4).String() //utils.SubFloatToString(val, 4)
 	return valStr
 }

+ 1 - 1
models/edb_data_calculate_hcz.go

@@ -327,6 +327,6 @@ func HczDiv(current, pre float64) string {
 	currentVal := decimal.NewFromFloat(current)
 	preVal := decimal.NewFromFloat(pre)
 	val, _ := currentVal.Sub(preVal).Float64()
-	valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4)
+	valStr := decimal.NewFromFloat(val).Round(4).String() //utils.SubFloatToString(val, 4)
 	return valStr
 }

+ 4 - 4
models/edb_data_calculate_ljz.go

@@ -378,7 +378,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 
 			daysT := decimal.NewFromInt(int64(days))
 			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
-			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			val, _ := initValAndMonthT.Div(daysT).Round(4).Float64()
 			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
 			valueMap[lastNewDate] = val
 		}
@@ -440,7 +440,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 
 			daysT := decimal.NewFromInt(int64(days))
 			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
-			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			val, _ := initValAndMonthT.Div(daysT).Round(4).Float64()
 			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
 			valueMap[lastNewDate] = val
 		}
@@ -505,7 +505,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 
 			daysT := decimal.NewFromInt(int64(days))
 			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
-			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			val, _ := initValAndMonthT.Div(daysT).Round(4).Float64()
 			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
 
 			valueMap[lastNewDate] = val
@@ -563,7 +563,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 
 			daysT := decimal.NewFromInt(int64(days))
 			initValAndMonthT := decimal.NewFromFloat(initVal * float64(monthDays))
-			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			val, _ := initValAndMonthT.Div(daysT).Round(4).Float64()
 			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", monthDays, initVal, days, val)
 			valueMap[lastNewDate] = val
 		}

+ 2 - 2
models/edb_data_calculate_nhcc.go

@@ -561,7 +561,7 @@ func refreshAllCalculateNhcc(to orm.TxOrmer, edbInfo *EdbInfo, existItemA, exist
 		bDecimal := decimal.NewFromFloat(b)
 		for _, aData := range aDataList {
 			xDecimal := decimal.NewFromFloat(aData.Value)
-			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
 			newBDataMap[aData.DataTime] = val
 		}
 
@@ -583,7 +583,7 @@ func refreshAllCalculateNhcc(to orm.TxOrmer, edbInfo *EdbInfo, existItemA, exist
 		bDecimal := decimal.NewFromFloat(bData.Value)
 		b2Decimal := decimal.NewFromFloat(b2Val)
 
-		val, _ := bDecimal.Sub(b2Decimal).RoundCeil(4).Float64()
+		val, _ := bDecimal.Sub(b2Decimal).Round(4).Float64()
 
 		// 判断之前有没有该数据
 		existData, ok := dataMap[currDate]

+ 1 - 1
models/edb_data_calculate_nszydbpjjs.go

@@ -282,7 +282,7 @@ func refreshAllCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, subSource,
 		af := totalVal //decimal.NewFromFloat(totalVal)
 		bf := decimal.NewFromFloat(float64(valArrLen))
 		val, _ := af.Div(bf).Float64()
-		valStr := decimal.NewFromFloat(val).RoundCeil(4).String()
+		valStr := decimal.NewFromFloat(val).Round(4).String()
 
 		if existVal, existOk := existDataMap[av]; !existOk {
 			currentDate, err := time.ParseInLocation(utils.FormatDate, av, time.Local)

+ 971 - 0
models/edb_data_calculate_qjjs.go

@@ -0,0 +1,971 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"math"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type CalculateRangeAnalysis struct {
+}
+
+type RangeAnalysisCalculateFormula struct {
+	DateRangeType        int                                `description:"区间划分类型 0:智能划分,1:手工划分,2:跨年划分"`
+	AutoDateConf         ChartRangeAnalysisAutoDateConf     `description:"智能划分时间区间配置"`
+	ManualDateConf       []ChartRangeAnalysisManualDateConf `description:"手工划分时间区间配置"`
+	YearDateConf         ChartRangeAnalysisYearDateConf     `description:"跨年划分时间区间配置"`
+	CalculateType        int                                `description:"计算类型 0: 区间均值,1: 区间累计值,2:区间涨幅,3:区间年化增长率,4:区间最大值,5:区间最小值"`
+	UnNormalDataDealType int                                `description:"异常值处理配置 0:不处理,1:剔除,2替换"`
+	UnNormalDataConf     ChartRangeAnalysisDeleteDataConf
+	DataConvertType      int                               `description:"数据转换类型 0不转, 1乘 2除 3对数"`
+	DataConvertConf      ChartRangeAnalysisDataConvertConf `description:"数据转换详情"`
+}
+
+type ChartRangeAnalysisAutoDateConf struct { //智能划分
+	IsAutoStartDate int                                  `description:"起始日期是否是动态设置:0固定,1动态"`
+	StartDate       string                               `description:"起始日期"` //固定模式下,截止日期为指标的最新日期
+	EndDate         string                               `description:"固定模式下的截止日期"`
+	IsAutoEndDate   int                                  `description:"截止日期是否是动态设置:0固定,1动态"`
+	StartDateConf   ChartRangeAnalysisAutoDateChangeConf `description:"动态起始日期配置"`
+	EndDateConf     ChartRangeAnalysisAutoDateChangeConf `description:"动态截止日期配置"`
+}
+
+type ChartRangeAnalysisAutoDateChangeConf struct {
+	BaseDateType int `description:"基准日期类型:0指标日期,1系统日期,2固定日期"`
+	MoveForward  int `description:"前移的期数"`
+	DateChange   []*EdbDataDateChangeConf
+}
+
+type EdbDataDateChangeConf struct {
+	Year         int
+	Month        int
+	Day          int
+	Frequency    string `description:"频度变换"`
+	FrequencyDay string `description:"频度的固定日期"`
+	ChangeType   int    `description:"日期变换类型1日期位移,2指定频率"`
+}
+
+type ChartRangeAnalysisManualDateConf struct { //手工划分
+	StartDate string `description:"开始日期"`
+	EndDate   string `description:"结束日期"`
+}
+
+type ChartRangeAnalysisYearDateConf struct {
+	StartDay string `description:"开始日"`
+	EndDay   string `description:"结束日"`
+}
+
+type ChartRangeAnalysisDeleteDataConf struct {
+	Formula      string
+	Value        float64
+	ReplaceValue float64 `description:"替换的值"`
+}
+
+type ChartRangeAnalysisDataConvertConf struct {
+	Value  float64 `description:"数据转换值"`
+	Unit   string  `description:"数据转换单位"`
+	EnUnit string  `description:"数据转换单位"`
+}
+
+type ChartRangeAnalysisDateDataItem struct {
+	StartDate time.Time
+	EndDate   time.Time
+	DataList  []*EdbInfoSearchData
+}
+
+func (obj CalculateRangeAnalysis) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	edbCode := params.EdbCode
+	uniqueCode := params.UniqueCode
+	sysUserId := params.SysUserId
+	sysUserRealName := params.SysUserRealName
+	//req *EdbInfoCalculateBatchSaveReq, edbCode, uniqueCode string, sysUserId int, sysUserRealName string
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = sysUserId
+	edbInfo.SysUserRealName = sysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = uniqueCode
+	edbInfo.CalculateFormula = req.CalculateFormula
+	edbInfo.EdbNameEn = req.EdbName
+	edbInfo.UnitEn = req.Unit
+	edbInfo.EdbType = obj.GetEdbType()
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	fromEdbInfo, e := GetEdbInfoById(req.FromEdbInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取来源指标失败,Err:%s", e.Error())
+		return
+	}
+
+	calculateMappingItem := new(EdbInfoCalculateMapping)
+	calculateMappingItem.CreateTime = time.Now()
+	calculateMappingItem.ModifyTime = time.Now()
+	calculateMappingItem.Sort = 1
+	calculateMappingItem.EdbCode = edbCode
+	calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+	calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+	calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+	calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+	calculateMappingItem.FromSource = fromEdbInfo.Source
+	calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+	calculateMappingItem.FromTag = ""
+	calculateMappingItem.Source = edbInfo.Source
+	calculateMappingItem.SourceName = edbInfo.SourceName
+	calculateMappingItem.FromSubSource = edbInfo.SubSource
+
+	_, err = to.Insert(calculateMappingItem)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj CalculateRangeAnalysis) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	req := params.Req
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.CalculateFormula
+	edbInfo.EdbNameEn = req.EdbNameEn
+	edbInfo.UnitEn = req.UnitEn
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime", "EdbNameEn", "UnitEn")
+	if err != nil {
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	fromEdbInfo, e := GetEdbInfoById(req.FromEdbInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取来源指标失败,Err:%s", e.Error())
+		return
+	}
+
+	calculateMappingItem := new(EdbInfoCalculateMapping)
+	calculateMappingItem.CreateTime = time.Now()
+	calculateMappingItem.ModifyTime = time.Now()
+	calculateMappingItem.Sort = 1
+	calculateMappingItem.EdbCode = edbInfo.EdbCode
+	calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+	calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+	calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+	calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+	calculateMappingItem.FromSource = fromEdbInfo.Source
+	calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+	calculateMappingItem.FromTag = ""
+	calculateMappingItem.Source = edbInfo.Source
+	calculateMappingItem.SourceName = edbInfo.SourceName
+	calculateMappingItem.FromSubSource = edbInfo.SubSource
+
+	_, err = to.Insert(calculateMappingItem)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj CalculateRangeAnalysis) Refresh(params RefreshParams) (err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	var fromEdbInfo *EdbInfo
+	for _, v := range edbInfoCalculateDetailList {
+		fromEdbInfo, _ = GetEdbInfoById(v.FromEdbInfoId)
+		break
+	}
+
+	if fromEdbInfo == nil {
+		errMsg = "指标异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj CalculateRangeAnalysis) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, fromEdbInfo *EdbInfo, edbCode, calculateFormula string) (err error, errMsg string) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(obj.GetSource(), utils.DATA_SUB_SOURCE_EDB)
+
+	//获取扩散指数指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source, subSource)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+	var rangeAnalysisConf RangeAnalysisCalculateFormula
+	//fmt.Println("calculateFormula:", calculateFormula)
+	err = json.Unmarshal([]byte(calculateFormula), &rangeAnalysisConf)
+	if err != nil {
+		err = fmt.Errorf("解析区间计算公式失败 %s", err.Error())
+		return
+	}
+
+	rangeAnalysisChartData, err := GetRangeAnalysisChartDataByEdbInfo(fromEdbInfo, rangeAnalysisConf)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算数据失败 %s", err.Error())
+		return
+	}
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for _, item := range rangeAnalysisChartData {
+		currDateStr := item.DataTime
+		currVal := item.Value
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, subSource, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := item.DataTimestamp
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	// 数据入库
+	{
+
+		if isAdd {
+			addSql = strings.TrimRight(addSql, ",")
+			_, err = to.Raw(addSql).Exec()
+		}
+
+		// 移除不存在的日期数据
+		if len(removeDateMap) > 0 {
+			removeDateList := make([]string, 0) //需要移除的日期
+			for k := range removeDateMap {
+				removeDateList = append(removeDateList, k)
+			}
+			removeDateStr := strings.Join(removeDateList, `","`)
+			removeDateStr = `"` + removeDateStr + `"`
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+			_, err = to.Raw(sql, edbInfoId).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除扩散指数指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	return
+}
+
+// GetAutoCalculateDateDataList 获取当前时间相关的区间作为计算依据
+func GetAutoCalculateDateDataList(currentDate string, dataList []*EdbInfoSearchData, req RangeAnalysisCalculateFormula) (newDataList []*EdbInfoSearchData, err error) {
+	currentDateTime, _ := time.ParseInLocation(utils.FormatDate, currentDate, time.Local)
+	switch req.DateRangeType {
+	case 0:
+		// 智能划分得到一个开始日期,和结束日期
+		var startDateTime time.Time
+		if req.AutoDateConf.IsAutoStartDate == 0 { //固定设置
+			startDate := req.AutoDateConf.StartDate
+			if startDate == "" {
+				startDate = "2020-01-01"
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		} else {
+			startConf := req.AutoDateConf.StartDateConf
+			startDate := ""
+			if startConf.BaseDateType == 0 { //
+				startDate = currentDate
+			} else if startConf.BaseDateType == 1 {
+				startDate = time.Now().Format(utils.FormatDate)
+			}
+			if startConf.MoveForward > 0 {
+				startDate = GetEdbDateByMoveForward(startDate, startConf.MoveForward, dataList)
+			}
+			if len(startConf.DateChange) > 0 {
+				startDate, err = HandleEdbDateChange(startDate, startConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分开始日期处理失败:%s", err.Error())
+					return
+				}
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		}
+		var calStartTime, calEndTime time.Time
+		if currentDateTime.Before(startDateTime) {
+			calStartTime = currentDateTime
+			calEndTime = startDateTime
+		} else {
+			calStartTime = startDateTime
+			calEndTime = currentDateTime
+		}
+		// 根据日期,获取数据
+		for _, vv := range dataList {
+			dataTimeT, _ := time.ParseInLocation(utils.FormatDate, vv.DataTime, time.Local)
+			if dataTimeT.After(calStartTime) && dataTimeT.Before(calEndTime) ||
+				dataTimeT.Equal(calStartTime) ||
+				dataTimeT.Equal(calEndTime) {
+				newDataList = append(newDataList, vv)
+			}
+		}
+	}
+	return
+}
+
+// HandleDataByCalculateType 根据计算公式处理数据
+func HandleRangeAnalysisDataByCalculateType(originList []*ChartRangeAnalysisDateDataItem, originDataList []*EdbInfoSearchData, req RangeAnalysisCalculateFormula) (newList []*EdbInfoSearchData, err error) {
+	if len(originList) == 0 {
+		return
+	}
+	calculateType := req.CalculateType
+	switch calculateType {
+	case 0: //均值
+		var sum float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					sum = 0
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for _, vv := range calDataList {
+						sum += vv.Value
+					}
+					val := sum / float64(len(calDataList))
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				sum = 0
+				for k, v := range item.DataList {
+					sum += v.Value
+					val := sum / float64(k+1)
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+
+	case 1: //累计值
+		var sum float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				sum = 0
+				for _, v := range item.DataList {
+					sum = 0
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for _, vv := range calDataList {
+						sum += vv.Value
+					}
+					val := sum
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				sum = 0
+				for _, v := range item.DataList {
+					sum += v.Value
+					val := sum
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+
+	case 2: //涨幅
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					var baseVal float64
+					//计算的数据返回需要重新确定
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					if len(calDataList) == 0 {
+						continue
+					}
+					baseVal = calDataList[0].Value
+					baseDate := calDataList[0].DataTime
+					if baseVal == 0 {
+						continue
+					}
+					if v.DataTime == baseDate {
+						continue
+					}
+
+					val := (v.Value - baseVal) / baseVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				if len(item.DataList) == 0 {
+					break
+				}
+				baseVal := item.DataList[0].Value
+				baseDate := item.DataList[0].DataTime
+				if baseVal == 0 {
+					break
+				}
+				for _, v := range item.DataList {
+					if v.DataTime == baseDate {
+						continue
+					}
+					val := (v.Value - baseVal) / baseVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{
+						DataTime:      v.DataTime,
+						Value:         val,
+						DataTimestamp: v.DataTimestamp,
+					})
+				}
+			}
+		}
+	case 3: //复合增长率
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					var baseVal float64
+					var baseDate string
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					if len(calDataList) == 0 {
+						continue
+					}
+					baseVal = calDataList[0].Value
+					baseDate = calDataList[0].DataTime
+					if v.DataTime == baseDate {
+						continue
+					}
+					if baseVal == 0 {
+						continue
+					}
+
+					baseDateT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					tmpT, e := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					// 计算两个日期相差的天数
+					diff := tmpT.Sub(baseDateT).Hours() / 24 / 365
+					val := v.Value / baseVal
+					val = math.Pow(val, 1/diff) - 1
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				if len(item.DataList) == 0 {
+					break
+				}
+				baseVal := item.DataList[0].Value
+				baseDate := item.DataList[0].DataTime
+				if baseVal == 0 {
+					break
+				}
+				for _, v := range item.DataList {
+					if v.DataTime == baseDate {
+						continue
+					}
+					baseDateT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					tmpT, e := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+					if e != nil {
+						err = fmt.Errorf("time.ParseInLocation err: %v", e)
+						return
+					}
+					// 计算两个日期相差的天数
+					diff := tmpT.Sub(baseDateT).Hours() / 24 / 365
+					val := v.Value / baseVal
+					val = math.Pow(val, 1/diff) - 1
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	case 4: //最大值
+		var maxVal float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for kk, vv := range calDataList {
+						if kk == 0 {
+							maxVal = vv.Value
+						}
+						if vv.Value > maxVal {
+							maxVal = vv.Value
+						}
+					}
+
+					val := maxVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				for k, v := range item.DataList {
+					if k == 0 {
+						maxVal = v.Value
+					}
+					if v.Value > maxVal {
+						maxVal = v.Value
+					}
+					val := maxVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	case 5: //最小值
+		var minVal float64
+		if req.DateRangeType == 0 && req.AutoDateConf.IsAutoStartDate > 0 {
+			for _, item := range originList {
+				for _, v := range item.DataList {
+					calDataList, e := GetAutoCalculateDateDataList(v.DataTime, originDataList, req)
+					if e != nil {
+						err = fmt.Errorf("获取区间数据失败:%s", e.Error())
+						return
+					}
+					for kk, vv := range calDataList {
+						if kk == 0 {
+							minVal = vv.Value
+						}
+						if vv.Value < minVal {
+							minVal = vv.Value
+						}
+					}
+					val := minVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		} else {
+			for _, item := range originList {
+				for k, v := range item.DataList {
+					if k == 0 {
+						minVal = v.Value
+					}
+					if v.Value < minVal {
+						minVal = v.Value
+					}
+					val := minVal
+					val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+					newList = append(newList, &EdbInfoSearchData{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// GetRangeAnalysisChartDataByEdbInfo 区间计算
+func GetRangeAnalysisChartDataByEdbInfo(fromEdbInfo *EdbInfo, calculateFormula RangeAnalysisCalculateFormula) (newDataList []*EdbInfoSearchData, err error) {
+	// 指标的开始日期和结束日期
+	edbStartDateTime, _ := time.ParseInLocation(utils.FormatDate, fromEdbInfo.StartDate, time.Local)
+	//edbStartDate := edbStartDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+	edbEndDateTime, _ := time.ParseInLocation(utils.FormatDate, fromEdbInfo.EndDate, time.Local)
+	edbEndDate := edbEndDateTime.Format(utils.FormatDate)
+
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*EdbInfoSearchData, 0)
+	switch fromEdbInfo.EdbInfoType {
+	case 0:
+		//获取来源指标的数据
+		dataList, err = GetEdbDataListAll(fromEdbInfo.Source, fromEdbInfo.SubSource,
+			FindEdbDataListAllCond{
+				EdbInfoId: fromEdbInfo.EdbInfoId,
+			}, 1)
+	case 1:
+		dataList, err = GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, "")
+	default:
+		err = errors.New(fmt.Sprint("获取失败,指标base类型异常", fromEdbInfo.EdbInfoType))
+		return
+	}
+
+	if err != nil {
+		err = fmt.Errorf("获取时间基准指标在时间区间内的值失败,Err:" + err.Error())
+		return
+	}
+
+	dateList := make([]*ChartRangeAnalysisDateDataItem, 0)
+	switch calculateFormula.DateRangeType {
+	case 0:
+		// 智能划分得到一个开始日期,和结束日期
+		var startDateTime, endDateTime time.Time
+		startDateTime = edbStartDateTime
+		if calculateFormula.AutoDateConf.IsAutoStartDate == 0 { //固定设置
+			startDate := calculateFormula.AutoDateConf.StartDate
+			if startDate == "" {
+				startDate = "2020-01-01"
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		}
+		if calculateFormula.AutoDateConf.IsAutoEndDate == 0 { //固定设置
+			endDate := calculateFormula.AutoDateConf.EndDate
+			if endDate == "" {
+				err = fmt.Errorf("智能划分截止日期处理失败:请输入截止日期")
+				return
+			}
+			// todo 如果截止日期比指标日期还要大,则用指标的最新日期
+			endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		} else {
+			endConf := calculateFormula.AutoDateConf.EndDateConf
+			endDate := edbEndDate
+			if endConf.MoveForward > 0 {
+				endDate = GetEdbDateByMoveForward(endDate, endConf.MoveForward, dataList)
+			}
+			if len(endConf.DateChange) > 0 {
+				endDate, err = HandleEdbDateChange(endDate, endConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分结束日期处理失败:%s", err.Error())
+					return
+				}
+			}
+			endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		}
+
+		dateList = append(dateList, &ChartRangeAnalysisDateDataItem{
+			StartDate: startDateTime,
+			EndDate:   endDateTime})
+	case 1:
+		// 手工划分得到多个开始日期和结束日期(已排序)
+		for _, v := range calculateFormula.ManualDateConf {
+			startDateT, _ := time.ParseInLocation(utils.FormatDate, v.StartDate, time.Local)
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+			tmp := &ChartRangeAnalysisDateDataItem{
+				StartDate: startDateT,
+				EndDate:   endDateT,
+			}
+			dateList = append(dateList, tmp)
+		}
+	case 2:
+		// 跨年划分得到多个开始日期和结束日期
+		startYear := edbStartDateTime.Year()
+		endYear := edbEndDateTime.Year()
+		startDay := calculateFormula.YearDateConf.StartDay
+		endDay := calculateFormula.YearDateConf.EndDay
+		for year := startYear; year <= endYear; year++ {
+			startDate := fmt.Sprintf("%d-%s", year, startDay)
+			endDate := fmt.Sprintf("%d-%s", year+1, endDay)
+			startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+			endDateTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			if startDateTime.Before(edbStartDateTime) {
+				break
+			}
+
+			tmp := &ChartRangeAnalysisDateDataItem{
+				StartDate: startDateTime,
+				EndDate:   endDateTime,
+			}
+			dateList = append(dateList, tmp)
+		}
+	}
+	// 根据日期,获取数据
+	for _, v := range dateList {
+		for _, vv := range dataList {
+			dataTimeT, _ := time.ParseInLocation(utils.FormatDate, vv.DataTime, time.Local)
+			if dataTimeT.After(v.StartDate) && dataTimeT.Before(v.EndDate) ||
+				dataTimeT.Equal(v.StartDate) ||
+				dataTimeT.Equal(v.EndDate) {
+				v.DataList = append(v.DataList, vv)
+			}
+		}
+	}
+	// 根据时间区间类型来获取数据的计算窗口,然后再拼接成整段数据
+	newDataList, err = HandleRangeAnalysisDataByCalculateType(dateList, dataList, calculateFormula)
+	if err != nil {
+		return
+	}
+	if calculateFormula.UnNormalDataDealType > 0 {
+		switch calculateFormula.UnNormalDataDealType { //0:不处理,1:剔除,2替换
+		case 1:
+			dealDataList := make([]*EdbInfoSearchData, 0)
+			for _, v := range newDataList {
+				if !utils.CompareFloatByOpStrings(calculateFormula.UnNormalDataConf.Formula, v.Value, calculateFormula.UnNormalDataConf.Value) {
+					dealDataList = append(dealDataList, v)
+				}
+			}
+		case 2:
+			for i, v := range newDataList {
+				if utils.CompareFloatByOpStrings(calculateFormula.UnNormalDataConf.Formula, v.Value, calculateFormula.UnNormalDataConf.Value) {
+					newDataList[i].Value = calculateFormula.UnNormalDataConf.ReplaceValue
+				}
+			}
+		}
+	}
+
+	if calculateFormula.DataConvertType > 0 {
+		// 数据转换类型 0不转, 1乘 2除 3对数
+		switch calculateFormula.DataConvertType {
+		case 1:
+			for i, v := range newDataList {
+				val := v.Value * calculateFormula.DataConvertConf.Value
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = item.MaxData * v.ConvertValue
+			//item.MinData = item.MinData * v.ConvertValue
+		case 2:
+			for i, v := range newDataList {
+				val := v.Value / calculateFormula.DataConvertConf.Value
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = item.MaxData / v.ConvertValue
+			//item.MinData = item.MinData / v.ConvertValue
+		case 3:
+			for i, v := range newDataList {
+				if v.Value <= 0 {
+					err = errors.New("数据中含有负数或0,无法对数运算")
+					return
+				}
+				val := math.Log(v.Value) / math.Log(calculateFormula.DataConvertConf.Value)
+				val, _ = decimal.NewFromFloat(val).Round(4).Float64()
+				newDataList[i].Value = val
+			}
+			//item.MaxData = math.Log(item.MaxData) / math.Log(v.ConvertValue)
+			//item.MinData = math.Log(item.MinData) / math.Log(v.ConvertValue)
+		}
+	}
+
+	return
+}
+
+func GetEdbDateByMoveForward(startDate string, moveForward int, edbDataList []*EdbInfoSearchData) (date string) {
+	// 根据日期进行排序
+	index := 0
+	length := len(edbDataList)
+	for i := length - 1; i >= 0; i-- {
+		item := edbDataList[i]
+		if item.DataTime == startDate {
+			index += 1
+			continue
+		}
+		if index >= moveForward {
+			date = item.DataTime
+			break
+		}
+		if index > 0 {
+			index += 1
+			date = item.DataTime
+		}
+	}
+	return
+}
+
+// HandleEdbDateChange 处理日期变换
+func HandleEdbDateChange(date string, dateChange []*EdbDataDateChangeConf) (newDate string, err error) {
+	newDate = date
+	if newDate != "" {
+		if len(dateChange) > 0 {
+			var dateTime time.Time
+			dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+			if err != nil {
+				err = fmt.Errorf("日期解析失败: %s", err.Error())
+				return
+			}
+			for _, v := range dateChange {
+				if v.ChangeType == 1 {
+					dateTime = dateTime.AddDate(v.Year, v.Month, v.Day)
+					newDate = dateTime.Format(utils.FormatDate)
+				} else if v.ChangeType == 2 {
+					newDate, err, _ = utils.HandleSystemAppointDateT(dateTime, v.FrequencyDay, v.Frequency)
+					if err != nil {
+						return
+					}
+					dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+					if err != nil {
+						err = fmt.Errorf("日期解析失败: %s", err.Error())
+						return
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj CalculateRangeAnalysis) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_RANGEANLYSIS
+}
+
+// GetSourceName 获取来源名称
+func (obj CalculateRangeAnalysis) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_RANGEANLYSIS
+}
+
+// GetEdbType 获取指标类型
+func (obj CalculateRangeAnalysis) GetEdbType() int {
+	return utils.CALCULATE_EDB_TYPE
+}

+ 1 - 1
models/edb_data_calculate_sum.go

@@ -409,7 +409,7 @@ func (obj CalculateSum) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 
 		// 有计算出来值,那么就从待删除指标中移除
 		delete(removeDateMap, sk)
-		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String()
+		saveValue := decimal.NewFromFloat(calVal).Round(4).String()
 		if existVal, ok := dataMap[sk]; !ok {
 			dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
 			timestamp := dataTime.UnixNano() / 1e6

+ 68 - 198
models/edb_data_calculate_tbz.go

@@ -210,25 +210,35 @@ func refreshAllCalculateTbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 	//计算数据
 
+	// 获取来源指标数据
 	dataList, err := GetEdbDataListAll(fromEdbInfo.Source, fromEdbInfo.SubSource, FindEdbDataListAllCond{
-		EdbInfoId:         fromEdbInfo.EdbInfoId,
-		StartDataTime:     startDate,
-		StartDataTimeCond: ">=",
+		EdbInfoId: fromEdbInfo.EdbInfoId,
+		//StartDataTime:     startDate,
+		//StartDataTimeCond: ">=",
 	}, 0)
 	if err != nil {
 		return err
 	}
-	var dateArr []string
-	dataMap := make(map[string]*EdbInfoSearchData)
-	for _, v := range dataList {
-		dateArr = append(dateArr, v.DataTime)
-		dataMap[v.DataTime] = v
+
+	// 来源指标数据参与计算
+	calculateDataList, err := EdbInfoSearchDataToEdbInfoData(dataList)
+	if err != nil {
+		return err
+	}
+	baseCalculate := BaseCalculate{
+		DataList:  calculateDataList,
+		Frequency: fromEdbInfo.Frequency,
+		Source:    3,
+	}
+	dateDataMap, err, _ := baseCalculate.Tbz()
+	if err != nil {
+		return err
 	}
-	fmt.Println("source:", source)
-	//获取指标所有数据
+
+	//获取当前已经入库的指标所有数据
 	existDataList := make([]*EdbData, 0)
 	dataTableName := GetEdbDataTableName(source, subSource)
-	fmt.Println("dataTableName:", dataTableName)
+	//fmt.Println("dataTableName:", dataTableName)
 	sql := `SELECT * FROM %s WHERE edb_info_id=? `
 	sql = fmt.Sprintf(sql, dataTableName)
 	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
@@ -236,206 +246,65 @@ func refreshAllCalculateTbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 		return err
 	}
 	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]bool) //需要移除的日期数据
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = true
 	}
-	fmt.Println("existDataMap:", existDataMap)
+	//fmt.Println("existDataMap:", existDataMap)
+
 	addSql := ` INSERT INTO edb_data_calculate_tbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 	existAddDataMap := make(map[string]string)
-	for _, av := range dateArr {
-		currentItem := dataMap[av]
-		if currentItem != nil {
-			//当前日期
-			currentDate, err := time.ParseInLocation(utils.FormatDate, av, time.Local)
-			if err != nil {
-				return err
-			}
-			//上一年的日期
-			preDate := currentDate.AddDate(-1, 0, 0)
-			preDateStr := preDate.Format(utils.FormatDate)
-			if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
-				//dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
-				timestamp := currentDate.UnixNano() / 1e6
-				timestampStr := fmt.Sprintf("%d", timestamp)
-				val := TbzDiv(currentItem.Value, findItem.Value)
-
-				if existVal, ok := existDataMap[av]; !ok {
-					if _, existOk := existAddDataMap[av]; !existOk {
-						addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-						isAdd = true
-					}
-					existAddDataMap[av] = av
-				} else {
-					existValDecimal, err := decimal.NewFromString(existVal)
-					existStr := existValDecimal.String()
-					if existStr != val {
-						sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-						sql = fmt.Sprintf(sql, dataTableName)
-						_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-						if err != nil {
-							return err
-						}
-					}
-				}
-				continue
-			} else {
-				if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
-					for i := 0; i <= 35; i++ {
-						nextDateDay := preDate.AddDate(0, 0, i)
-						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
-						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-							timestamp := currentDate.UnixNano() / 1e6
-							timestampStr := fmt.Sprintf("%d", timestamp)
-							val := TbzDiv(currentItem.Value, findItem.Value)
-
-							if existVal, ok := existDataMap[av]; !ok {
-								if _, existOk := existAddDataMap[av]; !existOk {
-									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-									isAdd = true
-								}
-								existAddDataMap[av] = av
-							} else {
-								existValDecimal, err := decimal.NewFromString(existVal)
-								existStr := existValDecimal.String()
-								if existStr != val {
-									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-									sql = fmt.Sprintf(sql, dataTableName)
-									_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-									if err != nil {
-										return err
-									}
-								}
-							}
-							break
-						} else {
-							preDateDay := preDate.AddDate(0, 0, -i)
-							preDateDayStr := preDateDay.Format(utils.FormatDate)
-							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-								timestamp := currentDate.UnixNano() / 1e6
-								timestampStr := fmt.Sprintf("%d", timestamp)
-								val := TbzDiv(currentItem.Value, findItem.Value)
 
-								if existVal, ok := existDataMap[av]; !ok {
-									if _, existOk := existAddDataMap[av]; !existOk {
-										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-										isAdd = true
-									}
-									existAddDataMap[av] = av
-								} else {
-									existValDecimal, err := decimal.NewFromString(existVal)
-									existStr := existValDecimal.String()
-									if existStr != val {
-										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-										sql = fmt.Sprintf(sql, dataTableName)
-										_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-										if err != nil {
-											return err
-										}
-									}
-								}
-								break
-							}
-						}
-					}
-				} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
-					if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
-						timestamp := currentDate.UnixNano() / 1e6
-						timestampStr := fmt.Sprintf("%d", timestamp)
-						val := TbzDiv(currentItem.Value, findItem.Value)
+	for currentDate, valFloat := range dateDataMap {
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		timestamp := currentDate.UnixNano() / 1e6
+		timestampStr := fmt.Sprintf("%d", timestamp)
+		val := decimal.NewFromFloat(valFloat).String()
 
-						if existVal, ok := existDataMap[av]; !ok {
-							if _, existOk := existAddDataMap[av]; !existOk {
-								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-								isAdd = true
-							}
-							existAddDataMap[av] = av
-						} else {
-							existValDecimal, err := decimal.NewFromString(existVal)
-							existStr := existValDecimal.String()
-							if existStr != val {
-								sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-								sql = fmt.Sprintf(sql, dataTableName)
-								_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-								if err != nil {
-									return err
-								}
-							}
-						}
-						break
-					}
-				} else {
-					nextDateDay := preDate.AddDate(0, 0, 1)
-					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+		//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+		delete(removeDataTimeMap, currentDateStr)
 
-					preDateDay := preDate.AddDate(0, 0, -1)
-					preDateDayStr := preDateDay.Format(utils.FormatDate)
-
-					for i := 0; i < 35; i++ {
-						if i >= 1 {
-							nextDateDay = nextDateDay.AddDate(0, 0, i)
-							nextDateDayStr = nextDateDay.Format(utils.FormatDate)
-						}
-						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-							timestamp := currentDate.UnixNano() / 1e6
-							timestampStr := fmt.Sprintf("%d", timestamp)
-							val := TbzDiv(currentItem.Value, findItem.Value)
+		if existVal, ok := existDataMap[currentDateStr]; !ok {
+			if _, existOk := existAddDataMap[currentDateStr]; !existOk {
+				addSql += GetAddSql(edbInfoIdStr, edbCode, currentDateStr, timestampStr, val)
+				isAdd = true
+			}
+			existAddDataMap[currentDateStr] = currentDateStr
+		} else {
+			existValDecimal, err := decimal.NewFromString(existVal)
+			existStr := existValDecimal.String()
+			if existStr != val {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, val, edbInfoId, currentDateStr).Exec()
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
 
-							if existVal, ok := existDataMap[av]; !ok {
-								if _, existOk := existAddDataMap[av]; !existOk {
-									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-									isAdd = true
-								}
-								existAddDataMap[av] = av
-							} else {
-								existValDecimal, err := decimal.NewFromString(existVal)
-								existStr := existValDecimal.String()
-								if existStr != val {
-									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-									sql = fmt.Sprintf(sql, dataTableName)
-									_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-									if err != nil {
-										return err
-									}
-								}
-							}
-							break
-						} else {
-							if i >= 1 {
-								preDateDay = preDateDay.AddDate(0, 0, -i)
-								preDateDayStr = preDateDay.Format(utils.FormatDate)
-							}
-							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-								timestamp := currentDate.UnixNano() / 1e6
-								timestampStr := fmt.Sprintf("%d", timestamp)
-								val := TbzDiv(currentItem.Value, findItem.Value)
+	//删除已经不存在的指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			//如果拼接指标变更了,那么需要删除所有的指标数据
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, dataTableName)
 
-								if existVal, ok := existDataMap[av]; !ok {
-									if _, existOk := existAddDataMap[av]; !existOk {
-										addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-										isAdd = true
-									}
-									existAddDataMap[av] = av
-								} else {
-									existValDecimal, err := decimal.NewFromString(existVal)
-									existStr := existValDecimal.String()
-									if existStr != val {
-										sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-										sql = fmt.Sprintf(sql, dataTableName)
-										_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-										if err != nil {
-											return err
-										}
-									}
-								}
-								break
-							}
-						}
-					}
-				}
+			_, err = to.Raw(sql, edbInfoId, removeDateList).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除自有数据的明细数据失败,Err:" + err.Error())
+				return
 			}
 		}
 	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()
@@ -443,6 +312,7 @@ func refreshAllCalculateTbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 			return err
 		}
 	}
+
 	return
 }
 
@@ -453,7 +323,7 @@ func TbzDiv(a, b float64) string {
 		bf := decimal.NewFromFloat(b)
 		val, _ := af.Div(bf).Float64()
 		val = val - 1
-		valStr = decimal.NewFromFloat(val).RoundCeil(4).String()
+		valStr = decimal.NewFromFloat(val).Round(4).String()
 	} else {
 		valStr = "0"
 	}

+ 1 - 1
models/edb_data_calculate_tcz.go

@@ -445,6 +445,6 @@ func TczSub(a, b float64) string {
 	bf := decimal.NewFromFloat(b)
 	val, _ := af.Sub(bf).Float64()
 	//valStr := utils.SubFloatToString(val, 4)
-	valStr := decimal.NewFromFloat(val).RoundCeil(4).String()
+	valStr := decimal.NewFromFloat(val).Round(4).String()
 	return valStr
 }

+ 1 - 1
models/edb_data_calculate_time_shift.go

@@ -293,7 +293,7 @@ func refreshAllCalculateTimeShift(to orm.TxOrmer, edbInfoId, source, subSource,
 
 			timestamp := newDate.UnixNano() / 1e6
 			timestampStr := fmt.Sprintf("%d", timestamp)
-			valStr := decimal.NewFromFloat(currentItem.Value).RoundCeil(4).String()
+			valStr := decimal.NewFromFloat(currentItem.Value).Round(4).String()
 			if existVal, ok := existDataMap[newDate.Format(utils.FormatDate)]; !ok {
 				isAdd = true
 				addSql += GetAddSql(edbInfoIdStr, edbCode, newDate.Format(utils.FormatDate), timestampStr, valStr)

+ 232 - 0
models/edb_data_ly.go

@@ -0,0 +1,232 @@
+// @Author gmy 2024/9/14 16:13:00
+package models
+
+import (
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type EdbDataLy struct {
+	edbDataId     int     `orm:"column(edb_data_id);pk"` // 数据ID
+	CreateTime    string  `orm:"column(create_time)"`    // 创建时间
+	ModifyTime    string  `orm:"column(modify_time)"`    // 修改时间
+	EdbInfoId     int     `orm:"column(edb_info_id)"`    // 指标id
+	EdbCode       string  `orm:"column(edb_code)"`       // 指标编码
+	DataTime      string  `orm:"column(data_time)"`      // 数据日期
+	Value         float64 `orm:"column(value)"`          // 数据值
+	DataTimestamp uint64  `orm:"column(data_timestamp)"` // 数据日期时间戳
+}
+
+func init() {
+	orm.RegisterModel(new(EdbDataLy))
+}
+
+func GetLyEdbDataByIndexCodeAndDataTime(indexCode string, dataTime string) (items []EdbDataLy, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM edb_data_ly WHERE index_code=? AND data_time like ?`
+	_, err = o.Raw(sql, indexCode, dataTime+"%").QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+func GetLyEdbDataByIndexCodeAndExactDataTime(indexCode string, dataTime string) (items []EdbDataLy, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM edb_data_ly WHERE edb_code=? AND data_time=?`
+	_, err = o.Raw(sql, indexCode, dataTime).QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// UpdateLyEdbDataById 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+func UpdateLyEdbDataById(id int, value float64) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE edb_data_ly SET value=? WHERE edb_data_id=?`
+	_, err = o.Raw(sql, value, id).Exec()
+	return
+}
+
+// 新增指标库数据
+func AddLyEdbDataList(items []EdbDataLy) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// AddEdbDataFromLy 新增指标数据
+func AddEdbDataFromLy(edbCode string) (err error) {
+	o := orm.NewOrm()
+
+	var condition string
+	var pars []interface{}
+
+	if edbCode != "" {
+		condition += " AND index_code = ? "
+		pars = append(pars, edbCode)
+	}
+
+	dataAll, err := GetBaseFromLyDataByIndexCode(condition, pars)
+	if err != nil {
+		return err
+	}
+	dataLen := len(dataAll)
+
+	existMap := make(map[string]string)
+	if dataLen > 0 {
+		var isAdd bool
+		addSql := ` INSERT INTO edb_data_ly (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+		for i := 0; i < dataLen; i++ {
+			item := dataAll[i]
+			eDate := item.DataTime
+			sValue := utils.SubFloatToString(item.Value, 4)
+			if sValue != "" {
+				if _, ok := existMap[eDate]; !ok {
+					dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+					if err != nil {
+						return err
+					}
+					timestamp := dataTime.UnixNano() / 1e6
+					timeStr := fmt.Sprintf("%d", timestamp)
+					addSql += GetAddSql("0", edbCode, eDate, timeStr, sValue)
+					isAdd = true
+				}
+			}
+			existMap[eDate] = eDate
+		}
+		if isAdd {
+			addSql = strings.TrimRight(addSql, ",")
+			utils.FileLog.Info("addSql:" + addSql)
+			_, err = o.Raw(addSql).Exec()
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return
+}
+
+func RefreshEdbDataFromLy(edbInfoId int, edbCode, startDate string) (err error) {
+	source := utils.DATA_SOURCE_LY
+	subSource := utils.DATA_SUB_SOURCE_EDB
+
+	o := orm.NewOrm()
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+
+	if edbCode != "" {
+		condition += " AND index_code=? "
+		pars = append(pars, edbCode)
+	}
+
+	if startDate != "" {
+		condition += " AND data_time>=? "
+		pars = append(pars, startDate)
+	}
+
+	dataList, err := GetBaseFromLyDataByIndexCode(condition, pars)
+	if err != nil {
+		return
+	}
+
+	// 真实数据的最大日期  , 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool //是否找到配置日期的实际数据的值
+	{
+		edbDataInsertConfig, err = GetEdbDataInsertConfigByEdbId(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		}
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += " AND edb_info_id=? "
+	existPars = append(existPars, edbInfoId)
+	if startDate != "" {
+		existCondition += " AND data_time>=? "
+		existPars = append(existPars, startDate)
+	}
+	//获取指标所有数据
+	existList, err := GetEdbDataByCondition(source, subSource, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO ebd_data_ly(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	addMap := make(map[string]string)
+	for _, v := range dataList {
+		item := v
+		eDate := item.DataTime
+		sValue := utils.SubFloatToString(item.Value, 4)
+
+		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+		if err != nil {
+			return err
+		}
+		if findItem, ok := existMap[v.DataTime]; !ok {
+			if sValue != "" {
+				timestamp := dataTime.UnixNano() / 1e6
+				timeStr := fmt.Sprintf("%d", timestamp)
+				saveValue := sValue
+
+				if _, addOk := addMap[eDate]; !addOk {
+					addSql += GetAddSql(edbInfoIdStr, edbCode, eDate, timeStr, saveValue)
+					isAdd = true
+				}
+			}
+		} else {
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 4) != sValue {
+				err = ModifyEdbDataById(source, subSource, findItem.EdbDataId, sValue)
+				if err != nil {
+					return err
+				}
+			}
+		}
+		addMap[v.DataTime] = v.DataTime
+
+		// 下面代码主要目的是处理掉手动插入的数据判断
+		{
+			if realDataMaxDate.IsZero() || dataTime.After(realDataMaxDate) {
+				realDataMaxDate = dataTime
+			}
+			if edbDataInsertConfigDate.IsZero() || dataTime.Equal(edbDataInsertConfigDate) {
+				isFindConfigDateRealData = true
+			}
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfoId, source, subSource, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return
+}

+ 4 - 0
models/edb_data_table.go

@@ -168,6 +168,10 @@ func GetEdbDataTableName(source, subSource int) (tableName string) {
 		tableName = "edb_data_predict_base"
 	case utils.DATA_SOURCE_SCI_HQ: // 卓创红期
 		tableName = "edb_data_sci_hq"
+	case utils.DATA_SOURCE_TRADE_ANALYSIS: // 持仓分析->92
+		tableName = "edb_data_trade_analysis"
+	case utils.DATA_SOURCE_LY: // 粮油商务网->91
+		tableName = "edb_data_ly"
 	default:
 		edbSource := EdbSourceIdMap[source]
 		if edbSource != nil {

+ 181 - 0
models/edb_data_trade_analysis.go

@@ -0,0 +1,181 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EdbDataTradeAnalysis 持仓分析指标数据
+type EdbDataTradeAnalysis struct {
+	EdbDataId     int       `orm:"column(edb_data_id);pk"`
+	EdbInfoId     int       `description:"指标ID"`
+	EdbCode       string    `description:"指标编码"`
+	DataTime      time.Time `description:"数据日期"`
+	Value         float64   `description:"数据值"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"修改时间"`
+	DataTimestamp int64     `description:"数据日期时间戳"`
+}
+
+func (m *EdbDataTradeAnalysis) TableName() string {
+	return "edb_data_trade_analysis"
+}
+
+type EdbDataTradeAnalysisCols struct {
+	PrimaryId     string
+	EdbInfoId     string
+	EdbCode       string
+	DataTime      string
+	Value         string
+	CreateTime    string
+	ModifyTime    string
+	DataTimestamp string
+}
+
+func (m *EdbDataTradeAnalysis) Cols() EdbDataTradeAnalysisCols {
+	return EdbDataTradeAnalysisCols{
+		PrimaryId:     "edb_data_id",
+		EdbInfoId:     "edb_info_id",
+		EdbCode:       "edb_code",
+		DataTime:      "data_time",
+		Value:         "value",
+		CreateTime:    "create_time",
+		ModifyTime:    "modify_time",
+		DataTimestamp: "data_timestamp",
+	}
+}
+
+func (m *EdbDataTradeAnalysis) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EdbDataId = int(id)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) CreateMulti(items []*EdbDataTradeAnalysis) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(500, items)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) Remove() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.EdbDataId).Exec()
+	return
+}
+
+func (m *EdbDataTradeAnalysis) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *EdbDataTradeAnalysis) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *EdbDataTradeAnalysis) GetItemById(id int) (item *EdbDataTradeAnalysis, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *EdbDataTradeAnalysis, err error) {
+	o := orm.NewOrm()
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EdbDataTradeAnalysis, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EdbDataTradeAnalysis) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EdbDataTradeAnalysis, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// MultiUpdateValue 批量更新数据值
+func (m *EdbDataTradeAnalysis) MultiUpdateValue(items []*EdbDataTradeAnalysis) (err error) {
+	if len(items) == 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	sql := fmt.Sprintf("UPDATE %s SET %s = ?, %s = NOW() WHERE %s = ?", m.TableName(), m.Cols().Value, m.Cols().ModifyTime, m.Cols().PrimaryId)
+	p, e := o.Raw(sql).Prepare()
+	if e != nil {
+		err = fmt.Errorf("update sql prepare err: %v", e)
+		return
+	}
+	defer func() {
+		_ = p.Close()
+	}()
+	for _, v := range items {
+		_, err = p.Exec(v.Value, v.EdbDataId)
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 19 - 0
models/edb_info.go

@@ -540,6 +540,11 @@ func GetEdbInfoByEdbCode(source int, edbCode string) (item *EdbInfo, err error)
 	o := orm.NewOrm()
 	sql := ` SELECT * FROM edb_info WHERE source=? AND edb_code=? `
 	err = o.Raw(sql, source, edbCode).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		err = nil
+	}
+
 	return
 }
 
@@ -1593,3 +1598,17 @@ func TransEdbInfoDataList2SearchData(items []*EdbDataList) (list []*EdbInfoSearc
 	}
 	return
 }
+
+type SortEdbDataList []*EdbInfoSearchData
+
+func (m SortEdbDataList) Len() int {
+	return len(m)
+}
+
+func (m SortEdbDataList) Less(i, j int) bool {
+	return m[i].DataTime > m[j].DataTime
+}
+
+func (m SortEdbDataList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}

+ 160 - 0
models/edb_trade_analysis.go

@@ -0,0 +1,160 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"time"
+)
+
+// EdbTradeAnalysis 持仓分析指标
+type EdbTradeAnalysis struct{}
+
+// GetSource 获取来源编码id
+func (obj EdbTradeAnalysis) GetSource() int {
+	return utils.DATA_SOURCE_TRADE_ANALYSIS
+}
+
+// GetSourceName 获取来源名称
+func (obj EdbTradeAnalysis) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_TRADE_ANALYSIS
+}
+
+// GetEdbType 获取指标类型
+func (obj EdbTradeAnalysis) GetEdbType() int {
+	return utils.DEFAULT_EDB_TYPE
+}
+
+func (obj EdbTradeAnalysis) Refresh(edbInfo *EdbInfo, convertData []*EdbDataList) (err error) {
+	if edbInfo == nil {
+		err = fmt.Errorf("指标信息有误, EdbInfo: %v", edbInfo)
+		return
+	}
+
+	// 真实数据的最大日期, 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool
+	{
+		conf, e := GetEdbDataInsertConfigByEdbId(edbInfo.EdbInfoId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("GetEdbDataInsertConfigByEdbId err: %v", e)
+			return
+		}
+		edbDataInsertConfig = conf
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	// 获取已有数据
+	dataOb := new(EdbDataTradeAnalysis)
+	dataExists := make(map[string]*EdbDataTradeAnalysis)
+	searchExistMap := make(map[string]*EdbInfoSearchData)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", dataOb.Cols().EdbInfoId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, edbInfo.EdbInfoId)
+		//if queryDate != "" {
+		//	cond += fmt.Sprintf(" AND %s >= ?", dataOb.Cols().DataTime)
+		//	pars = append(pars, queryDate)
+		//}
+		list, e := dataOb.GetItemsByCondition(cond, pars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取指标数据失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			dataExists[v.DataTime.Format(utils.FormatDate)] = v
+			searchExistMap[v.DataTime.Format(utils.FormatDate)] = &EdbInfoSearchData{
+				EdbDataId:     v.EdbDataId,
+				EdbInfoId:     v.EdbInfoId,
+				DataTime:      v.DataTime.Format(utils.FormatDate),
+				Value:         v.Value,
+				EdbCode:       v.EdbCode,
+				DataTimestamp: v.DataTimestamp,
+			}
+		}
+	}
+
+	// 比对数据
+	insertExist := make(map[string]bool)
+	insertData := make([]*EdbDataTradeAnalysis, 0)
+	updateData := make([]*EdbDataTradeAnalysis, 0)
+	for _, v := range convertData {
+		dataDate, e := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("持仓分析-数据日期转换失败, EdbCode: %s, DataDate: %s", edbInfo.EdbCode, v.DataTime))
+			continue
+		}
+		strDate := v.DataTime
+
+		// 手动插入数据的判断
+		if realDataMaxDate.IsZero() || dataDate.After(realDataMaxDate) {
+			realDataMaxDate = dataDate
+		}
+		if edbDataInsertConfigDate.IsZero() || dataDate.Equal(edbDataInsertConfigDate) {
+			isFindConfigDateRealData = true
+		}
+
+		// 入库值
+		saveVal := decimal.NewFromFloat(v.Value).Round(4).String()
+		d, e := decimal.NewFromString(saveVal)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("EdbDataTradeAnalysis NewFromString err: %v", e))
+			continue
+		}
+		saveFloat, _ := d.Float64()
+
+		// 更新
+		exists := dataExists[strDate]
+		if exists != nil {
+			existVal := decimal.NewFromFloat(exists.Value).Round(4).String()
+			if saveVal != existVal {
+				exists.Value = saveFloat
+				updateData = append(updateData, exists)
+			}
+			continue
+		}
+
+		// 新增
+		if insertExist[strDate] {
+			continue
+		}
+		insertExist[strDate] = true
+
+		timestamp := dataDate.UnixNano() / 1e6
+		insertData = append(insertData, &EdbDataTradeAnalysis{
+			EdbInfoId:     edbInfo.EdbInfoId,
+			EdbCode:       edbInfo.EdbCode,
+			DataTime:      dataDate,
+			Value:         saveFloat,
+			CreateTime:    time.Now(),
+			ModifyTime:    time.Now(),
+			DataTimestamp: timestamp,
+		})
+	}
+
+	// 批量新增/更新
+	if len(insertData) > 0 {
+		if e := dataOb.CreateMulti(insertData); e != nil {
+			err = fmt.Errorf("批量新增指标数据失败, %v", e)
+			return
+		}
+	}
+	if len(updateData) > 0 {
+		if e := dataOb.MultiUpdateValue(updateData); e != nil {
+			err = fmt.Errorf("批量更新指标数据失败, %v", e)
+			return
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfo.EdbInfoId, obj.GetSource(), 0, searchExistMap, isFindConfigDateRealData)
+	return
+}
+
+func (obj EdbTradeAnalysis) UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo *EdbInfo) (err error) {
+	err, _ = UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	return
+}

+ 210 - 0
models/factor_edb_series_calculate_data_qjjs.go

@@ -0,0 +1,210 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// FactorEdbSeriesCalculateDataQjjs 因子指标系列-区间计算数据表
+type FactorEdbSeriesCalculateDataQjjs struct {
+	FactorEdbSeriesCalculateDataId int       `orm:"column(factor_edb_series_calculate_data_id);pk"`
+	FactorEdbSeriesId              int       `description:"因子指标系列ID"`
+	EdbInfoId                      int       `description:"指标ID"`
+	EdbCode                        string    `description:"指标编码"`
+	DataTime                       time.Time `description:"数据日期"`
+	Value                          float64   `description:"数据值"`
+	CreateTime                     time.Time `description:"创建时间"`
+	ModifyTime                     time.Time `description:"修改时间"`
+	DataTimestamp                  int64     `description:"数据日期时间戳"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) TableName() string {
+	return "factor_edb_series_calculate_data_qjjs"
+}
+
+type FactorEdbSeriesCalculateDataQjjsCols struct {
+	PrimaryId         string
+	FactorEdbSeriesId string
+	EdbInfoId         string
+	EdbCode           string
+	DataTime          string
+	Value             string
+	CreateTime        string
+	ModifyTime        string
+	DataTimestamp     string
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Cols() FactorEdbSeriesCalculateDataQjjsCols {
+	return FactorEdbSeriesCalculateDataQjjsCols{
+		PrimaryId:         "factor_edb_series_calculate_data_id",
+		FactorEdbSeriesId: "factor_edb_series_id",
+		EdbInfoId:         "edb_info_id",
+		EdbCode:           "edb_code",
+		DataTime:          "data_time",
+		Value:             "value",
+		CreateTime:        "create_time",
+		ModifyTime:        "modify_time",
+		DataTimestamp:     "data_timestamp",
+	}
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.FactorEdbSeriesCalculateDataId = int(id)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) CreateMulti(items []*FactorEdbSeriesCalculateDataQjjs) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(500, items)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Remove() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.FactorEdbSeriesCalculateDataId).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemById(id int) (item *FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrm()
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*FactorEdbSeriesCalculateDataQjjs, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// FactorEdbSeriesCalculateDataQjjsItem 因子指标系列-计算数据信息
+type FactorEdbSeriesCalculateDataQjjsItem struct {
+	DataId            int     `description:"数据ID"`
+	FactorEdbSeriesId int     `description:"因子指标系列ID"`
+	EdbInfoId         int     `description:"指标ID"`
+	EdbCode           string  `description:"指标编码"`
+	DataTime          string  `description:"数据日期"`
+	Value             float64 `description:"数据值"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Format2Item() (item *FactorEdbSeriesCalculateDataQjjsItem) {
+	item = new(FactorEdbSeriesCalculateDataQjjsItem)
+	item.DataId = m.FactorEdbSeriesCalculateDataId
+	item.FactorEdbSeriesId = m.FactorEdbSeriesId
+	item.EdbInfoId = m.EdbInfoId
+	item.EdbCode = m.EdbCode
+	return
+}
+
+// TransEdbSeriesCalculateDataQjjs2EdbDataList 转换数据格式
+func TransEdbSeriesCalculateDataQjjs2EdbDataList(items []*FactorEdbSeriesCalculateDataQjjs) (list []*EdbDataList) {
+	list = make([]*EdbDataList, 0)
+	for _, v := range items {
+		list = append(list, &EdbDataList{
+			DataTime: v.DataTime.Format(utils.FormatDate),
+			Value:    v.Value,
+		})
+	}
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetEdbDataList(seriesId int, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	var pars []interface{}
+	sql := `SELECT factor_edb_series_calculate_data_id as edb_data_id  ,edb_info_id,data_time,value,data_timestamp FROM %s WHERE edb_info_id=? and factor_edb_series_id=? `
+	pars = append(pars, edbInfoId, seriesId)
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, m.TableName())
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}

+ 1 - 0
models/factor_edb_series_chart_mapping.go

@@ -10,6 +10,7 @@ import (
 
 const (
 	FactorEdbSeriesChartCalculateTypeCorrelation = 1 // 相关性计算
+	FactorEdbSeriesChartCalculateTypeRange       = 2 // 	区间计算
 )
 
 // FactorEdbSeriesChartMapping 因子指标系列-图表关联

+ 7 - 0
models/factor_edb_series_mapping.go

@@ -165,3 +165,10 @@ func (m *FactorEdbSeriesMapping) Format2Item() (item *FactorEdbSeriesMappingItem
 	item.EdbCode = m.EdbCode
 	return
 }
+
+func (m *FactorEdbSeriesMapping) GetItemBySeriesId(seriesId int) (items []*FactorEdbSeriesMapping, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? `, m.TableName(), m.Cols().FactorEdbSeriesId)
+	_, err = o.Raw(sql, seriesId).QueryRows(&items)
+	return
+}

+ 2 - 2
models/predict_edb.go

@@ -228,7 +228,7 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 		// 移除不存在的日期
 		delete(removeDateMap, sk)
 
-		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String() //utils.SubFloatToString(calVal, 4)
+		saveValue := decimal.NewFromFloat(calVal).Round(4).String() //utils.SubFloatToString(calVal, 4)
 		existPredictEdbRuleData, ok := dataMap[sk]
 		if !ok {
 			dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
@@ -402,7 +402,7 @@ func CalculateByRuleByRuleLineNh(to orm.TxOrmer, predictEdbInfo EdbInfo, predict
 				continue
 			}
 
-			saveValue := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(calVal, 4)
+			saveValue := decimal.NewFromFloat(val).Round(4).String() //utils.SubFloatToString(calVal, 4)
 			existPredictEdbRuleData, ok := dataMap[currentDateStr]
 			if !ok {
 				timestamp := currentDate.UnixNano() / 1e6

+ 2 - 2
models/predict_edb_data_calculate_nhcc.go

@@ -467,7 +467,7 @@ func refreshAllPredictCalculateNhcc(to orm.TxOrmer, edbInfo, firstEdbInfo, secon
 		bDecimal := decimal.NewFromFloat(b)
 		for _, aData := range aDataList {
 			xDecimal := decimal.NewFromFloat(aData.Value)
-			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
 			newBDataMap[aData.DataTime] = val
 		}
 
@@ -489,7 +489,7 @@ func refreshAllPredictCalculateNhcc(to orm.TxOrmer, edbInfo, firstEdbInfo, secon
 		bDecimal := decimal.NewFromFloat(bData.Value)
 		b2Decimal := decimal.NewFromFloat(b2Val)
 
-		val, _ := bDecimal.Sub(b2Decimal).RoundCeil(4).Float64()
+		val, _ := bDecimal.Sub(b2Decimal).Round(4).Float64()
 		// 判断之前有没有该数据
 		existData, ok := dataMap[currDate]
 		if !ok { //不存在那么就添加吧

+ 1 - 1
models/predict_edb_data_calculate_nszydbpjjs.go

@@ -304,7 +304,7 @@ func refreshAllPredictCalculateNszydpjjs(to orm.TxOrmer, edbInfoId, source, subS
 		af := totalVal //decimal.NewFromFloat(totalVal)
 		bf := decimal.NewFromFloat(float64(valArrLen))
 		val, _ := af.Div(bf).Float64()
-		valStr := decimal.NewFromFloat(val).RoundCeil(4).String()
+		valStr := decimal.NewFromFloat(val).Round(4).String()
 
 		// 实际数据的值
 		if fromEdbInfo.LatestDate == av {

+ 344 - 0
models/predict_edb_data_calculate_qjjs.go

@@ -0,0 +1,344 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type PredictCalculateRangeAnalysis struct {
+}
+
+func (obj PredictCalculateRangeAnalysis) Add(params BatchSaveCalculateBatchParams) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	req := params.Req
+	edbCode := params.EdbCode
+	uniqueCode := params.UniqueCode
+	sysUserId := params.SysUserId
+	sysUserRealName := params.SysUserRealName
+	//req *EdbInfoCalculateBatchSaveReq, edbCode, uniqueCode string, sysUserId int, sysUserRealName string
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = sysUserId
+	edbInfo.SysUserRealName = sysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = uniqueCode
+	edbInfo.CalculateFormula = req.CalculateFormula
+	edbInfo.EdbNameEn = req.EdbName
+	edbInfo.UnitEn = req.Unit
+	edbInfo.EdbType = obj.GetEdbType()
+	edbInfo.EdbInfoType = 1
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	fromEdbInfo, e := GetEdbInfoById(req.FromEdbInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取来源指标失败,Err:%s", e.Error())
+		return
+	}
+
+	calculateMappingItem := new(EdbInfoCalculateMapping)
+	calculateMappingItem.CreateTime = time.Now()
+	calculateMappingItem.ModifyTime = time.Now()
+	calculateMappingItem.Sort = 1
+	calculateMappingItem.EdbCode = edbCode
+	calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+	calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+	calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+	calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+	calculateMappingItem.FromSource = fromEdbInfo.Source
+	calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+	calculateMappingItem.FromTag = ""
+	calculateMappingItem.Source = edbInfo.Source
+	calculateMappingItem.SourceName = edbInfo.SourceName
+	calculateMappingItem.FromSubSource = edbInfo.SubSource
+
+	_, err = to.Insert(calculateMappingItem)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj PredictCalculateRangeAnalysis) Edit(params BatchSaveCalculateBatchParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	req := params.Req
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.CalculateFormula
+	//修改指标信息
+	switch params.Lang {
+	case utils.EnLangVersion:
+		edbInfo.EdbNameEn = req.EdbName
+		edbInfo.UnitEn = req.Unit
+	default:
+		edbInfo.EdbName = req.EdbName
+		edbInfo.Unit = req.Unit
+		edbInfo.EdbNameSource = req.EdbName
+	}
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime", "EdbNameEn", "UnitEn")
+	if err != nil {
+		return
+	}
+
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	fromEdbInfo, e := GetEdbInfoById(req.FromEdbInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取来源指标失败,Err:%s", e.Error())
+		return
+	}
+
+	calculateMappingItem := new(EdbInfoCalculateMapping)
+	calculateMappingItem.CreateTime = time.Now()
+	calculateMappingItem.ModifyTime = time.Now()
+	calculateMappingItem.Sort = 1
+	calculateMappingItem.EdbCode = edbInfo.EdbCode
+	calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+	calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+	calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+	calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+	calculateMappingItem.FromSource = fromEdbInfo.Source
+	calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+	calculateMappingItem.FromTag = ""
+	calculateMappingItem.Source = edbInfo.Source
+	calculateMappingItem.SourceName = edbInfo.SourceName
+	calculateMappingItem.FromSubSource = edbInfo.SubSource
+
+	_, err = to.Insert(calculateMappingItem)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj PredictCalculateRangeAnalysis) Refresh(params RefreshParams) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	var fromEdbInfo *EdbInfo
+	for _, v := range edbInfoCalculateDetailList {
+		fromEdbInfo, _ = GetEdbInfoById(v.FromEdbInfoId)
+		break
+	}
+
+	if fromEdbInfo == nil {
+		errMsg = "指标异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateRangeAnalysis,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err, errMsg = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, fromEdbInfo, edbInfo.EdbCode, edbInfo.CalculateFormula)
+
+	return
+}
+
+func (obj PredictCalculateRangeAnalysis) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, fromEdbInfo *EdbInfo, edbCode, calculateFormula string) (latestDateStr string, latestValue float64, err error, errMsg string) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(obj.GetSource(), utils.DATA_SUB_SOURCE_EDB)
+
+	//获取扩散指数指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source, subSource)
+	if err != nil {
+		return
+	}
+	latestDateStr = fromEdbInfo.LatestDate
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+	var rangeAnalysisConf RangeAnalysisCalculateFormula
+	//fmt.Println("calculateFormula:", calculateFormula)
+	err = json.Unmarshal([]byte(calculateFormula), &rangeAnalysisConf)
+	if err != nil {
+		err = fmt.Errorf("解析区间计算公式失败 %s", err.Error())
+		return
+	}
+
+	rangeAnalysisChartData, err := GetRangeAnalysisChartDataByEdbInfo(fromEdbInfo, rangeAnalysisConf)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算数据失败 %s", err.Error())
+		return
+	}
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for _, item := range rangeAnalysisChartData {
+		currDateStr := item.DataTime
+		currVal := item.Value
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, subSource, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := item.DataTimestamp
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	// 数据入库
+	{
+
+		if isAdd {
+			addSql = strings.TrimRight(addSql, ",")
+			_, err = to.Raw(addSql).Exec()
+		}
+
+		// 移除不存在的日期数据
+		if len(removeDateMap) > 0 {
+			removeDateList := make([]string, 0) //需要移除的日期
+			for k := range removeDateMap {
+				removeDateList = append(removeDateList, k)
+			}
+			removeDateStr := strings.Join(removeDateList, `","`)
+			removeDateStr = `"` + removeDateStr + `"`
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+			_, err = to.Raw(sql, edbInfoId).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除扩散指数指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+	//确定实际数据的最终值
+	{
+		finalLast, tmpErr := GetFinalLastByTo(to, edbInfoId, obj.GetSource(), utils.DATA_SUB_SOURCE_EDB, fromEdbInfo.LatestDate)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			return
+		}
+		if tmpErr == nil {
+			latestDateStr = finalLast.DataTime
+			latestValue = finalLast.Value
+		}
+	}
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj PredictCalculateRangeAnalysis) GetSource() int {
+	return utils.DATA_SOURCE_PREDICT_CALCULATE_RANGEANLYSIS
+}
+
+// GetSourceName 获取来源名称
+func (obj PredictCalculateRangeAnalysis) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_RANGEANLYSIS
+}
+
+// GetEdbType 获取指标类型
+func (obj PredictCalculateRangeAnalysis) GetEdbType() int {
+	return utils.CALCULATE_EDB_TYPE
+}

+ 52 - 277
models/predict_edb_data_calculate_tbz.go

@@ -270,22 +270,32 @@ func (obj PredictTb) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbC
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 
 	// 获取关联指标数据
-	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 1, startDate)
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, "")
 	if err != nil {
 		return
 	}
 
-	latestDateStr = fromEdbInfo.LatestDate
-	//计算数据
-	var dateArr []string
-	dataMap := make(map[string]*EdbInfoSearchData)
-	for _, v := range dataList {
-		dateArr = append(dateArr, v.DataTime)
-		dataMap[v.DataTime] = v
+	// 来源指标数据参与计算
+	calculateDataList, err := EdbInfoSearchDataToEdbInfoData(dataList)
+	if err != nil {
+		return
+	}
+	baseCalculate := BaseCalculate{
+		DataList:  calculateDataList,
+		Frequency: fromEdbInfo.Frequency,
+		Source:    3,
+	}
+	dateDataMap, err, _ := baseCalculate.Tbz()
+	if err != nil {
+		return
 	}
+
+	// 真实数据的最后日期
+	latestDateStr = fromEdbInfo.LatestDate
+
 	//获取指标所有数据
 	existDataList := make([]*EdbData, 0)
-	fmt.Println("dataTableName:", dataTableName)
+	//fmt.Println("dataTableName:", dataTableName)
 
 	sql := `SELECT * FROM %s WHERE edb_info_id=? `
 	sql = fmt.Sprintf(sql, dataTableName)
@@ -300,285 +310,50 @@ func (obj PredictTb) refresh(to orm.TxOrmer, edbInfo, fromEdbInfo *EdbInfo, edbC
 		removeDataTimeMap[v.DataTime] = 1
 	}
 
-	fmt.Println("existDataMap:", existDataMap)
+	//fmt.Println("existDataMap:", existDataMap)
 	addSql := ` INSERT INTO edb_data_predict_calculate_tbz (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 	existAddDataMap := make(map[string]string)
-	for _, av := range dateArr {
-		//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
-		delete(removeDataTimeMap, av)
 
-		currentItem, ok := dataMap[av]
-		if !ok || currentItem == nil {
-			continue
-		}
+	for currentDate, valFloat := range dateDataMap {
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		timestamp := currentDate.UnixNano() / 1e6
+		timestampStr := fmt.Sprintf("%d", timestamp)
+		val := decimal.NewFromFloat(valFloat).String()
 
-		//当前日期
-		currentDate, tmpErr := time.ParseInLocation(utils.FormatDate, av, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		//上一年的日期
-		preDate := currentDate.AddDate(-1, 0, 0)
-		preDateStr := preDate.Format(utils.FormatDate)
-		if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
-			//dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
-			timestamp := currentDate.UnixNano() / 1e6
-			timestampStr := fmt.Sprintf("%d", timestamp)
-			val := TbzDiv(currentItem.Value, findItem.Value)
-
-			if existVal, ok := existDataMap[av]; !ok {
-				if _, existOk := existAddDataMap[av]; !existOk {
-					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-					isAdd = true
+		//校验待删除日期数据里面是否存在该元素,如果存在的话,那么移除该日期
+		delete(removeDataTimeMap, currentDateStr)
 
-					// 实际数据的值
-					if fromEdbInfo.LatestDate == av {
-						latestValueDecimal, tmpErr := decimal.NewFromString(val)
-						if tmpErr != nil {
-							return
-						}
-						latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-					}
-				}
-				existAddDataMap[av] = av
-			} else {
-				existValDecimal, tmpErr := decimal.NewFromString(existVal)
-				if tmpErr != nil {
-					err = tmpErr
-					return
-				}
-				existStr := existValDecimal.String()
-				if existStr != val {
-					sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-					sql = fmt.Sprintf(sql, dataTableName)
-					_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-					if err != nil {
-						return
-					}
-				}
+		if existVal, ok := existDataMap[currentDateStr]; !ok {
+			if _, existOk := existAddDataMap[currentDateStr]; !existOk {
+				addSql += GetAddSql(edbInfoIdStr, edbCode, currentDateStr, timestampStr, val)
+				isAdd = true
 			}
-			continue
+			existAddDataMap[currentDateStr] = currentDateStr
 		} else {
-			if fromEdbInfo.Frequency == "月度" { //向上和向下,各找一个月
-				for i := 0; i <= 35; i++ {
-					nextDateDay := preDate.AddDate(0, 0, i)
-					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
-					if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-						timestamp := currentDate.UnixNano() / 1e6
-						timestampStr := fmt.Sprintf("%d", timestamp)
-						val := TbzDiv(currentItem.Value, findItem.Value)
-
-						if existVal, ok := existDataMap[av]; !ok {
-							if _, existOk := existAddDataMap[av]; !existOk {
-								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-								isAdd = true
-
-								// 实际数据的值
-								if fromEdbInfo.LatestDate == av {
-									latestValueDecimal, tmpErr := decimal.NewFromString(val)
-									if tmpErr != nil {
-										return
-									}
-									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-								}
-							}
-							existAddDataMap[av] = av
-						} else {
-							existValDecimal, tmpErr := decimal.NewFromString(existVal)
-							if tmpErr != nil {
-								err = tmpErr
-								return
-							}
-							existStr := existValDecimal.String()
-							if existStr != val {
-								sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-								sql = fmt.Sprintf(sql, dataTableName)
-								_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-								if err != nil {
-									return
-								}
-							}
-						}
-						break
-					} else {
-						preDateDay := preDate.AddDate(0, 0, -i)
-						preDateDayStr := preDateDay.Format(utils.FormatDate)
-						if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-							timestamp := currentDate.UnixNano() / 1e6
-							timestampStr := fmt.Sprintf("%d", timestamp)
-							val := TbzDiv(currentItem.Value, findItem.Value)
-
-							if existVal, ok := existDataMap[av]; !ok {
-								if _, existOk := existAddDataMap[av]; !existOk {
-									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-									isAdd = true
-
-									// 实际数据的值
-									if fromEdbInfo.LatestDate == av {
-										latestValueDecimal, tmpErr := decimal.NewFromString(val)
-										if tmpErr != nil {
-											return
-										}
-										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-									}
-								}
-								existAddDataMap[av] = av
-							} else {
-								existValDecimal, tmpErr := decimal.NewFromString(existVal)
-								if tmpErr != nil {
-									err = tmpErr
-									return
-								}
-								existStr := existValDecimal.String()
-								if existStr != val {
-									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-									sql = fmt.Sprintf(sql, dataTableName)
-									_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-									if err != nil {
-										return
-									}
-								}
-							}
-							break
-						}
-					}
-				}
-			} else if fromEdbInfo.Frequency == "季度" || fromEdbInfo.Frequency == "年度" {
-				if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
-					timestamp := currentDate.UnixNano() / 1e6
-					timestampStr := fmt.Sprintf("%d", timestamp)
-					val := TbzDiv(currentItem.Value, findItem.Value)
-
-					if existVal, ok := existDataMap[av]; !ok {
-						if _, existOk := existAddDataMap[av]; !existOk {
-							addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-							isAdd = true
-
-							// 实际数据的值
-							if fromEdbInfo.LatestDate == av {
-								latestValueDecimal, tmpErr := decimal.NewFromString(val)
-								if tmpErr != nil {
-									return
-								}
-								latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-							}
-						}
-						existAddDataMap[av] = av
-					} else {
-						existValDecimal, tmpErr := decimal.NewFromString(existVal)
-						if tmpErr != nil {
-							err = tmpErr
-							return
-						}
-						existStr := existValDecimal.String()
-						if existStr != val {
-							sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-							sql = fmt.Sprintf(sql, dataTableName)
-							_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-							if err != nil {
-								return
-							}
-						}
-					}
-					break
+			existValDecimal, tmpErr := decimal.NewFromString(existVal)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existStr := existValDecimal.String()
+			if existStr != val {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, val, edbInfoId, currentDateStr).Exec()
+				if err != nil {
+					return
 				}
-			} else {
-				nextDateDay := preDate.AddDate(0, 0, 1)
-				nextDateDayStr := nextDateDay.Format(utils.FormatDate)
-
-				preDateDay := preDate.AddDate(0, 0, -1)
-				preDateDayStr := preDateDay.Format(utils.FormatDate)
-
-				for i := 0; i < 35; i++ {
-					if i >= 1 {
-						nextDateDay = nextDateDay.AddDate(0, 0, i)
-						nextDateDayStr = nextDateDay.Format(utils.FormatDate)
-					}
-					if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-						timestamp := currentDate.UnixNano() / 1e6
-						timestampStr := fmt.Sprintf("%d", timestamp)
-						val := TbzDiv(currentItem.Value, findItem.Value)
-
-						if existVal, ok := existDataMap[av]; !ok {
-							if _, existOk := existAddDataMap[av]; !existOk {
-								addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-								isAdd = true
-
-								// 实际数据的值
-								if fromEdbInfo.LatestDate == av {
-									latestValueDecimal, tmpErr := decimal.NewFromString(val)
-									if tmpErr != nil {
-										return
-									}
-									latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-								}
-							}
-							existAddDataMap[av] = av
-						} else {
-							existValDecimal, tmpErr := decimal.NewFromString(existVal)
-							if tmpErr != nil {
-								err = tmpErr
-								return
-							}
-							existStr := existValDecimal.String()
-							if existStr != val {
-								sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-								sql = fmt.Sprintf(sql, dataTableName)
-								_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-								if err != nil {
-									return
-								}
-							}
-						}
-						break
-					} else {
-						if i >= 1 {
-							preDateDay = preDateDay.AddDate(0, 0, -i)
-							preDateDayStr = preDateDay.Format(utils.FormatDate)
-						}
-						if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-							timestamp := currentDate.UnixNano() / 1e6
-							timestampStr := fmt.Sprintf("%d", timestamp)
-							val := TbzDiv(currentItem.Value, findItem.Value)
-
-							if existVal, ok := existDataMap[av]; !ok {
-								if _, existOk := existAddDataMap[av]; !existOk {
-									addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
-									isAdd = true
+			}
+		}
 
-									// 实际数据的值
-									if fromEdbInfo.LatestDate == av {
-										latestValueDecimal, tmpErr := decimal.NewFromString(val)
-										if tmpErr != nil {
-											return
-										}
-										latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
-									}
-								}
-								existAddDataMap[av] = av
-							} else {
-								existValDecimal, tmpErr := decimal.NewFromString(existVal)
-								if tmpErr != nil {
-									err = tmpErr
-									return
-								}
-								existStr := existValDecimal.String()
-								if existStr != val {
-									sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
-									sql = fmt.Sprintf(sql, dataTableName)
-									_, err = to.Raw(sql, val, edbInfoId, av).Exec()
-									if err != nil {
-										return
-									}
-								}
-							}
-							break
-						}
-					}
-				}
+		// 实际数据的值
+		if fromEdbInfo.LatestDate == currentDateStr {
+			latestValueDecimal, tmpErr := decimal.NewFromString(val)
+			if tmpErr != nil {
+				return
 			}
+			latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
 		}
 	}
 

+ 1 - 1
models/predict_edb_data_calculate_time_shift.go

@@ -264,7 +264,7 @@ func refreshAllPredictCalculateTimeShift(to orm.TxOrmer, edbInfoId, source, subS
 
 			timestamp := newDate.UnixNano() / 1e6
 			timestampStr := fmt.Sprintf("%d", timestamp)
-			valStr := decimal.NewFromFloat(currentItem.Value).RoundCeil(4).String()
+			valStr := decimal.NewFromFloat(currentItem.Value).Round(4).String()
 			if latestDateStr == newDate.Format(utils.FormatDate) {
 				latestValue = currentItem.Value
 			}

+ 17 - 17
models/predict_edb_info_rule.go

@@ -148,7 +148,7 @@ func PredictTbzDiv(a, b float64) (result float64) {
 	val := bf.Add(cf)
 
 	// 计算
-	result, _ = val.Mul(af).RoundCeil(4).Float64()
+	result, _ = val.Mul(af).Round(4).Float64()
 	return
 }
 
@@ -267,7 +267,7 @@ func PredictTczDiv(a, b float64) (result float64) {
 		bf := decimal.NewFromFloat(b)
 
 		// 计算
-		result, _ = af.Add(bf).RoundCeil(4).Float64()
+		result, _ = af.Add(bf).Round(4).Float64()
 	} else {
 		result = 0
 	}
@@ -330,7 +330,7 @@ func PredictHbzDiv(a, b float64) (result float64) {
 		val := bf.Add(cf)
 
 		// 计算
-		result, _ = val.Mul(af).RoundCeil(4).Float64()
+		result, _ = val.Mul(af).Round(4).Float64()
 	} else {
 		result = 0
 	}
@@ -387,7 +387,7 @@ func PredictHczDiv(a, b float64) (result float64) {
 		bf := decimal.NewFromFloat(b)
 
 		// 计算
-		result, _ = af.Add(bf).RoundCeil(4).Float64()
+		result, _ = af.Add(bf).Round(4).Float64()
 	} else {
 		result = 0
 	}
@@ -426,7 +426,7 @@ func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue in
 		}
 
 		// N期移动均值计算
-		val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
+		val, _ := tmpDecimalVal.Div(decimalN).Round(4).Float64()
 
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		tmpData := &EdbInfoSearchData{
@@ -499,7 +499,7 @@ func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue
 		tmpK := nValue + k + 1
 
 		xDecimal := decimal.NewFromInt(int64(tmpK))
-		val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+		val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
 
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		tmpData := &EdbInfoSearchData{
@@ -560,7 +560,7 @@ func GetChartPredictEdbInfoDataListByRuleTrendsHC(edbInfoId int, dayList []time.
 		lastValueDecimal := decimal.NewFromFloat(lastValue)
 		hcValDecimal := decimal.NewFromFloat(hcVal)
 
-		val, _ := lastValueDecimal.Add(hcValDecimal).RoundCeil(4).Float64()
+		val, _ := lastValueDecimal.Add(hcValDecimal).Round(4).Float64()
 
 		tmpData := &EdbInfoSearchData{
 			EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
@@ -810,7 +810,7 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, yearsList []int,
 			continue
 		}
 		lastDayValDec := decimal.NewFromFloat(lastDayVal)
-		val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).Add(lastDayValDec).RoundCeil(4).Float64()
+		val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).Add(lastDayValDec).Round(4).Float64()
 
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		tmpData := &EdbInfoSearchData{
@@ -974,7 +974,7 @@ func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, ye
 		tbVal := tmpAverageVal.Div(tmpHistoryAverageVal)
 
 		// 预测值结果 = 同比年份同期值(tmpHistoryCurrentVal的值)* 同比值(tbVal的值)
-		val, _ := decimal.NewFromFloat(tmpHistoryCurrentVal).Mul(tbVal).RoundCeil(4).Float64()
+		val, _ := decimal.NewFromFloat(tmpHistoryCurrentVal).Mul(tbVal).Round(4).Float64()
 
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		tmpData := &EdbInfoSearchData{
@@ -1111,7 +1111,7 @@ func GetChartPredictEdbInfoDataListByRuleTbzscz(edbInfoId int, tbEndValue float6
 		preDate := currentDate.AddDate(-1, 0, 0)
 		preDateStr := preDate.Format(utils.FormatDate)
 		if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
-			val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+			val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 			calculateStatus = true
 		} else {
 			switch frequency {
@@ -1122,13 +1122,13 @@ func GetChartPredictEdbInfoDataListByRuleTbzscz(edbInfoId int, tbEndValue float6
 				for i := 0; i <= 35; i++ {
 					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
 					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-						val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+						val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 						calculateStatus = true
 						break
 					} else {
 						preDateDayStr := preDateDay.Format(utils.FormatDate)
 						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-							val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+							val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 							calculateStatus = true
 							break
 						}
@@ -1139,7 +1139,7 @@ func GetChartPredictEdbInfoDataListByRuleTbzscz(edbInfoId int, tbEndValue float6
 
 			case "季度", "年度":
 				if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
-					val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+					val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 					calculateStatus = true
 					break
 				}
@@ -1150,13 +1150,13 @@ func GetChartPredictEdbInfoDataListByRuleTbzscz(edbInfoId int, tbEndValue float6
 				for i := 0; i < 35; i++ {
 					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
 					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
-						val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+						val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 						calculateStatus = true
 						break
 					} else {
 						preDateDayStr := preDateDay.Format(utils.FormatDate)
 						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
-							val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
+							val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).Round(4).Float64()
 							calculateStatus = true
 							break
 						} else {
@@ -1364,7 +1364,7 @@ func getCalculateNhccData(secondDataList []*EdbInfoSearchData, ruleConf RuleLine
 		bDecimal := decimal.NewFromFloat(b)
 		for _, aData := range aDataList {
 			xDecimal := decimal.NewFromFloat(aData.Value)
-			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+			val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
 			newBDataMap[aData.DataTime] = val
 		}
 
@@ -1419,7 +1419,7 @@ func GetChartPredictEdbInfoDataListByRuleNAnnualAverage(edbInfoId int, configVal
 		if tmpHistoryValNum != len(yearList) {
 			continue
 		}
-		val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).RoundCeil(4).Float64()
+		val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).Round(4).Float64()
 
 		currentDateStr := currentDate.Format(utils.FormatDate)
 		tmpData := &EdbInfoSearchData{

+ 533 - 0
models/trade_analysis/trade_analysis.go

@@ -0,0 +1,533 @@
+package trade_analysis
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// 上期能源持仓榜单表
+type TradePositionTop struct {
+	Id            uint64    `gorm:"primaryKey;column:id" json:"id"`
+	ClassifyName  string    `gorm:"column:classify_name" json:"classify_name"`     //分类名称
+	ClassifyType  string    `gorm:"column:classify_type" json:"classify_type"`     //分类名称下的类型
+	DealShortName string    `gorm:"column:deal_short_name" json:"deal_short_name"` //成交量公司简称
+	DealValue     int       `gorm:"column:deal_value" json:"deal_value"`           //成交量
+	DealChange    int       `gorm:"column:deal_change" json:"deal_change"`         //成交变化量
+	DataTime      time.Time `gorm:"column:data_time" json:"data_time"`             //数据日期
+	CreateTime    time.Time `gorm:"column:create_time" json:"create_time"`         //插入时间
+	ModifyTime    time.Time `gorm:"column:modify_time" json:"modify_time"`         //修改时间
+	DealType      int       `gorm:"column:deal_type" json:"deal_type"`             //交易类型:1多单,2空单,3净多单,4净空单
+	SourceType    int       `gorm:"column:source_type" json:"source_type"`         //数据来源,0是原始数据的值,1是由T+1日推算出的值,2是由T日的榜单数据推算出的值
+	Rank          int       `gorm:"column:rank" json:"rank"`                       //排名
+}
+
+type TradeClassifyNameList struct {
+	Exchange   string                      `description:"交易所"`
+	ExchangeEn string                      `description:"交易所英文"`
+	Sort       int                         `description:"排序字段" `
+	Num        int                         `description:"品种数量"`
+	DataTime   string                      `description:"最新更新时间"`
+	CurrDate   string                      `description:"当前日期"`
+	Items      []TradeClassifyNameListItem `description:"子类"`
+}
+type TradeClassifyNameListSort []TradeClassifyNameList
+
+func (v TradeClassifyNameListSort) Len() int {
+	return len(v)
+}
+
+func (v TradeClassifyNameListSort) Swap(i, j int) {
+	v[i], v[j] = v[j], v[i]
+}
+
+func (v TradeClassifyNameListSort) Less(i, j int) bool {
+	return v[i].Sort < v[j].Sort
+}
+
+type TradeClassifyNameListItemSort []TradeClassifyNameListItem
+
+func (v TradeClassifyNameListItemSort) Len() int {
+	return len(v)
+}
+
+func (v TradeClassifyNameListItemSort) Swap(i, j int) {
+	v[i], v[j] = v[j], v[i]
+}
+
+func (v TradeClassifyNameListItemSort) Less(i, j int) bool {
+	return v[i].ClassifyName < v[j].ClassifyName
+}
+
+type TradeClassifyNameListItem struct {
+	ClassifyName string                          `description:"交易分类"`
+	Items        []TradeClassifyNameListItemItem `description:"合约代码"`
+}
+
+type TradeClassifyNameListItemItemSort []TradeClassifyNameListItemItem
+
+func (v TradeClassifyNameListItemItemSort) Len() int {
+	return len(v)
+}
+
+func (v TradeClassifyNameListItemItemSort) Swap(i, j int) {
+	v[i], v[j] = v[j], v[i]
+}
+
+func (v TradeClassifyNameListItemItemSort) Less(i, j int) bool {
+	return v[i].ClassifyType < v[j].ClassifyType
+}
+
+type TradeClassifyNameListItemItem struct {
+	ClassifyType string `description:"分类名称下的类型"`
+}
+
+type TradeClassifyName struct {
+	ClassifyName string //分类名称
+	ClassifyType string //分类名称下的类型
+	LatestDate   string //分类下最晚日期
+}
+
+// GetExchangeClassify 获取交易所分类列表
+func GetExchangeClassify(exchange string) (list []TradeClassifyName, err error) {
+	tableName := "base_from_trade_" + exchange + "_index"
+	orderStr := "classify_name DESC, classify_type asc"
+	if exchange == "zhengzhou" {
+		orderStr = "classify_name asc"
+	}
+	sql := "SELECT classify_name, classify_type FROM " + tableName + " WHERE `rank` <=20 and `rank` > 0 GROUP BY classify_name, classify_type  "
+	sql += ` ORDER BY ` + orderStr
+
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+type LastTimeItem struct {
+	CreateTime time.Time
+}
+
+// GetExchangeLastTime 获取交易所数据最晚的时间
+func GetExchangeLastTime(exchange string) (item LastTimeItem, err error) {
+	tableName := "base_from_trade_" + exchange + "_index"
+	sql := `SELECT create_time FROM ` + tableName + ` ORDER BY create_time desc`
+	o := orm.NewOrm()
+	err = o.Raw(sql).QueryRow(&item)
+
+	return
+}
+
+type GetPositionTopReq struct {
+	Exchange     string `json:"exchange" form:"exchange"`           //交易所
+	ClassifyName string `json:"classify_name" form:"classify_name"` //分类名称
+	ClassifyType string `json:"classify_type" form:"classify_type"` //具体合约名称
+	DataTime     string `json:"data_time" form:"data_time"`         //请求日期,如果为空,则返回最新的榜单日期
+}
+
+type GetPositionTopResp struct {
+	BuyList       GetPositionTopList `description:"多单列表"`
+	SoldList      GetPositionTopList `description:"空单列表"`
+	CleanBuyList  GetPositionTopList `description:"净多单列表"`
+	CleanSoldList GetPositionTopList `description:"净空单列表"`
+	DataTime      string             `description:"最新日期或者请求日期"`
+	LastDataTime  string             `description:"最新日期"`
+}
+
+type GetPositionTopList struct {
+	TotalDealValue  int                      `description:"总计成交量"`
+	TotalDealChange int                      `description:"校昨日变化"`
+	List            []GetPositionTopListItem `description:"榜单详情列表"`
+}
+
+type GetPositionTopListItem struct {
+	DealShortName   string `description:"成交量公司简称"`
+	DealValue       int    `description:"成交量"`
+	DealChange      int    `description:"成交变化量"`
+	Rank            int    `description:"当前名次"`
+	Rate            string `description:"当前占比"`
+	BeforeAllRate   string `description:"排在前面的成交量总计占比(包含)"`
+	BeforeAllValue  int    `description:"排在前面的成交量总计"`
+	BeforeAllChange int    `description:"排在前面的成交量增减总计"`
+}
+
+func GetTradePositionTop(exchange string, classifyName, classifyType, dataTime string) (list []TradePositionTop, err error) {
+	tableName := "trade_position_" + exchange + "_top"
+	sql := `SELECT * FROM ` + tableName + " WHERE classify_name=? and classify_type=? and data_time=? and `rank` <=20 and `rank` > 0 ORDER BY deal_value desc"
+
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, classifyName, classifyType, dataTime).QueryRows(&list)
+
+	return
+}
+
+type OriginTradeData struct {
+	Rank         int       `description:"排名"`
+	CompanyName  string    `description:"期货公司名称"`
+	Val          int       `description:"持仓量"`
+	ValChange    int       `description:"持仓增减"`
+	DataTime     time.Time `description:"数据日期"`
+	ClassifyName string    `description:"品种名称"`
+	ClassifyType string    `description:"合约代码"`
+	ValType      int       `description:"数据类型: 1-多单; 2-空单"`
+}
+
+// GetTradeDataByClassifyAndCompany 根据品种和公司名称获取持仓数据
+func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, companies []string) (items []*OriginTradeData, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(contracts) == 0 || len(companies) == 0 {
+		return
+	}
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT
+			rank,
+			buy_short_name AS company_name,
+			buy_value AS val,
+			buy_change AS val_change,
+			classify_name,
+			classify_type,
+			data_time,
+			1 AS val_type 
+		FROM
+			%s 
+		WHERE
+			classify_name = ? AND classify_type IN (%s) AND buy_short_name IN (%s)
+		UNION ALL
+		(
+		SELECT
+			rank,
+			sold_short_name,
+			sold_value,
+			sold_change,
+			classify_name,
+			classify_type,
+			data_time,
+			2 AS val_type 
+		FROM
+			%s 
+		WHERE
+			classify_name = ? AND classify_type IN (%s) AND sold_short_name IN (%s)
+		)`
+	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, classifyName, contracts, companies, classifyName, contracts, companies).QueryRows(&items)
+	return
+}
+
+// GetTradeZhengzhouDataByClassifyAndCompany 郑商所-根据品种和公司名称获取持仓数据
+func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, companies []string) (items []*OriginTradeData, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(contracts) == 0 || len(companies) == 0 {
+		return
+	}
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT
+			rank,
+			buy_short_name AS company_name,
+			buy_value AS val,
+			buy_change AS val_change,
+			classify_name AS classify_type,
+			data_time,
+			1 AS val_type 
+		FROM
+			%s 
+		WHERE
+			classify_name IN (%s) AND buy_short_name IN (%s)
+		UNION ALL
+		(
+		SELECT
+			rank,
+			sold_short_name,
+			sold_value,
+			sold_change,
+			classify_name AS classify_type,
+			data_time,
+			2 AS val_type 
+		FROM
+			%s 
+		WHERE
+			classify_name IN (%s) AND sold_short_name IN (%s)
+		)`
+	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, contracts, companies, contracts, companies).QueryRows(&items)
+	return
+}
+
+// ContractCompanyTradeData [合约-期货公司]持仓数据
+type ContractCompanyTradeData struct {
+	CompanyName  string                          `description:"期货公司名称"`
+	ClassifyType string                          `description:"合约代码"`
+	StartDate    time.Time                       `description:"数据开始日期"`
+	EndDate      time.Time                       `description:"数据结束日期"`
+	DataList     []*ContractCompanyTradeDataList `description:"数据序列"`
+}
+
+const (
+	TradeDataTypeNull      = 0 // 无值
+	TradeDataTypeOrigin    = 1 // 原始值
+	TradeDataTypeCalculate = 2 // 推算值
+
+	WarehouseBuyChartType     = 1 // 多单图
+	WarehouseSoldChartType    = 2 // 空单图
+	WarehousePureBuyChartType = 3 // 净多单图
+
+	WarehouseDefaultUnit      = "手"
+	WarehouseDefaultFrequency = "日度"
+
+	GuangZhouTopCompanyAliasName = "日成交持仓排名" // 广期所TOP20对应的公司名称
+	GuangZhouSeatNameBuy         = "持买单量"       // 广期所指标名称中的多单名称
+	GuangZhouSeatNameSold        = "持卖单量"       // 广期所指标名称中的空单名称
+	GuangZhouTopSeatNameBuy      = "持买单量总计"   // 广期所指标名称中的TOP20多单名称
+	GuangZhouTopSeatNameSold     = "持卖单量总计"   // 广期所指标名称中的TOP20空单名称
+)
+
+const (
+	TradeExchangeZhengzhou = "zhengzhou"
+	TradeExchangeGuangzhou = "guangzhou"
+)
+
+var WarehouseTypeSuffixNames = map[int]string{
+	WarehouseBuyChartType:     "席位多单",
+	WarehouseSoldChartType:    "席位空单",
+	WarehousePureBuyChartType: "席位净多单",
+}
+
+// GuangzhouSeatNameValType 广期所数据名称对应的席位方向
+var GuangzhouSeatNameValType = map[string]int{
+	GuangZhouSeatNameBuy:     1,
+	GuangZhouSeatNameSold:    2,
+	GuangZhouTopSeatNameBuy:  1,
+	GuangZhouTopSeatNameSold: 2,
+}
+
+// ContractCompanyTradeDataList [合约-期货公司]持仓数据详情
+type ContractCompanyTradeDataList struct {
+	Date              time.Time `description:"数据日期"`
+	BuyVal            int       `description:"多单持仓量"`
+	BuyValType        int       `description:"多单数据类型: 0-无值; 1-原始值; 2-推算值"`
+	BuyChange         int       `description:"多单持仓增减"`
+	BuyChangeType     int       `description:"多单持仓增减类型: 0-无值; 1-原始值; 2-推算值"`
+	SoldVal           int       `description:"空单持仓量"`
+	SoldValType       int       `description:"空单数据类型: 0-无值; 1-原始值; 2-推算值"`
+	SoldChange        int       `description:"空单持仓增减"`
+	SoldChangeType    int       `description:"空单持仓增减类型: 0-无值; 1-原始值; 2-推算值"`
+	PureBuyVal        int       `description:"净多单持仓量"`
+	PureBuyValType    int       `description:"净多单数据类型: 0-无值; 1-原始值; 2-推算值"`
+	PureBuyChange     int       `description:"净多单持仓增减"`
+	PureBuyChangeType int       `description:"净多单持仓增减类型: 0-无值; 1-原始值; 2-推算值"`
+}
+
+// GetLastTradeDataByClassify 获取[合约]末位多空单数据
+func GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*OriginTradeData, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(contracts) == 0 {
+		return
+	}
+	contractReplacer := utils.GetOrmInReplace(len(contracts))
+
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT 
+			tpt.rank,
+			tpt.buy_short_name AS company_name,
+			tpt.buy_value AS val,
+			tpt.buy_change AS val_change,
+			tpt.classify_name,
+			tpt.classify_type,
+			tpt.data_time,
+			1 AS val_type
+		FROM 
+			%s tpt
+		JOIN 
+			(
+				SELECT
+					data_time, classify_type, MAX(rank) AS max_rank
+				FROM 
+					%s
+				WHERE 
+					classify_name = ? AND classify_type IN (%s) AND buy_short_name <> ''
+				GROUP BY 
+					data_time,
+					classify_type
+			) sub
+		ON
+			tpt.data_time = sub.data_time AND tpt.classify_type = sub.classify_type AND tpt.rank = sub.max_rank
+		WHERE 
+			tpt.classify_name = ? AND tpt.classify_type IN (%s)
+		UNION ALL
+		(
+		SELECT 
+			tpt.rank, tpt.sold_short_name, tpt.sold_value, tpt.sold_change, tpt.classify_name, tpt.classify_type, tpt.data_time, 2 AS val_type
+		FROM 
+			%s tpt
+		JOIN 
+			(
+				SELECT 
+					data_time, classify_type, MAX(rank) AS max_rank
+				FROM 
+					%s
+				WHERE 
+					classify_name = ? AND classify_type IN (%s) AND sold_short_name <> ''
+				GROUP BY 
+					data_time, classify_type
+			) sub
+		ON 
+			tpt.data_time = sub.data_time AND tpt.classify_type = sub.classify_type AND tpt.rank = sub.max_rank
+		WHERE 
+			tpt.classify_name = ? AND tpt.classify_type IN (%s)
+		)`
+	sql = fmt.Sprintf(sql, tableName, tableName, contractReplacer, contractReplacer, tableName, tableName, contractReplacer, contractReplacer)
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, classifyName, contracts, classifyName, contracts, classifyName, contracts, classifyName, contracts).QueryRows(&items)
+	return
+}
+
+// GetLastTradeZhengzhouDataByClassify 郑商所-获取[合约]末位多空单数据
+func GetLastTradeZhengzhouDataByClassify(exchange string, contracts []string) (items []*OriginTradeData, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(contracts) == 0 {
+		return
+	}
+	contractReplacer := utils.GetOrmInReplace(len(contracts))
+
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT 
+			tpt.rank,
+			tpt.buy_short_name AS company_name,
+			tpt.buy_value AS val,
+			tpt.buy_change AS val_change,
+  			tpt.classify_name AS classify_type,
+			tpt.data_time,
+			1 AS val_type
+		FROM 
+			%s tpt
+		JOIN 
+			(
+				SELECT
+					data_time, classify_name, MAX(rank) AS max_rank
+				FROM 
+					%s
+				WHERE 
+					classify_name IN (%s) AND buy_short_name <> ''
+				GROUP BY 
+					data_time,
+					classify_name
+			) sub
+		ON
+			tpt.data_time = sub.data_time AND tpt.classify_name = sub.classify_name AND tpt.rank = sub.max_rank
+		WHERE 
+			tpt.classify_name IN (%s)
+		UNION ALL
+		(
+		SELECT 
+			tpt.rank, tpt.sold_short_name, tpt.sold_value, tpt.sold_change, tpt.classify_name AS classify_type, tpt.data_time, 2 AS val_type
+		FROM 
+			%s tpt
+		JOIN 
+			(
+				SELECT 
+					data_time, classify_name, MAX(rank) AS max_rank
+				FROM 
+					%s
+				WHERE 
+					classify_name IN (%s) AND sold_short_name <> ''
+				GROUP BY 
+					data_time, classify_name
+			) sub
+		ON 
+			tpt.data_time = sub.data_time AND tpt.classify_name = sub.classify_name AND tpt.rank = sub.max_rank
+		WHERE 
+			tpt.classify_name IN (%s)
+		)`
+	sql = fmt.Sprintf(sql, tableName, tableName, contractReplacer, contractReplacer, tableName, tableName, contractReplacer, contractReplacer)
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, contracts, contracts, contracts, contracts).QueryRows(&items)
+	return
+}
+
+type BaseFromTradeGuangzhouIndex struct {
+	BaseFromTradeGuangzhouIndexId    int       `orm:"column(base_from_trade_guangzhou_index_id);pk"`
+	BaseFromTradeGuangzhouClassifyId int       `description:"分类id"`
+	IndexCode                        string    `description:"指标编码"`
+	IndexName                        string    `description:"指标名称"`
+	Frequency                        string    `description:"频率"`
+	Unit                             string    `description:"单位"`
+	StartDate                        string    `description:"开始日期"`
+	EndDate                          string    `description:"结束日期"`
+	CreateTime                       time.Time `description:"创建日期"`
+	ModifyTime                       time.Time `description:"修改日期"`
+}
+
+func GetBaseFromTradeGuangzhouIndexByClassifyId(classifyId int) (list []*BaseFromTradeGuangzhouIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_trade_guangzhou_index WHERE base_from_trade_guangzhou_classify_id = ?`
+	_, err = o.Raw(sql, classifyId).QueryRows(&list)
+	return
+}
+
+type BaseFromTradeGuangzhouData struct {
+	BaseFromTradeGuangzhouDataId  int       `orm:"column(base_from_trade_guangzhou_data_id);pk"`
+	BaseFromTradeGuangzhouIndexId int       `description:"指标id"`
+	IndexCode                     string    `description:"指标编码"`
+	DataTime                      time.Time `description:"数据日期"`
+	Value                         float64   `description:"数据值"`
+	QtySub                        float64   `description:"增减"`
+	CreateTime                    time.Time `description:"创建日期"`
+	ModifyTime                    time.Time `description:"修改日期"`
+}
+
+// GetBaseFromTradeGuangzhouDataByIndexIds 获取指标数据
+func GetBaseFromTradeGuangzhouDataByIndexIds(indexIds []int) (list []*BaseFromTradeGuangzhouData, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM base_from_trade_guangzhou_data WHERE base_from_trade_guangzhou_index_id IN (%s) ORDER BY base_from_trade_guangzhou_index_id`, utils.GetOrmInReplace(len(indexIds)))
+	_, err = o.Raw(sql, indexIds).QueryRows(&list)
+	return
+}
+
+// GetBaseFromTradeGuangzhouMinDataByIndexIds 获取指标中的末位数据
+func GetBaseFromTradeGuangzhouMinDataByIndexIds(indexIds []int) (list []*BaseFromTradeGuangzhouData, err error) {
+	indexLen := len(indexIds)
+	if indexLen == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT 
+			t1.data_time,
+			t1.min_value AS value
+		FROM 
+			(
+				SELECT 
+					data_time,
+					MIN(value) AS min_value
+				FROM 
+					base_from_trade_guangzhou_data
+				WHERE 
+					base_from_trade_guangzhou_index_id IN (%s)
+				GROUP BY 
+					data_time
+			) t1
+		JOIN 
+			base_from_trade_guangzhou_data t2
+		ON 
+			t1.data_time = t2.data_time AND t1.min_value = t2.value AND t2.base_from_trade_guangzhou_index_id IN (%s)
+		GROUP BY 
+			t1.data_time`, utils.GetOrmInReplace(indexLen), utils.GetOrmInReplace(indexLen))
+	_, err = o.Raw(sql, indexIds, indexIds).QueryRows(&list)
+	return
+}

+ 129 - 0
models/trade_analysis/trade_futures_company.go

@@ -0,0 +1,129 @@
+package trade_analysis
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+const TradeFuturesCompanyTop20 = "TOP20"
+
+// TradeFuturesCompany 期货公司表
+type TradeFuturesCompany struct {
+	TradeFuturesCompanyId int       `orm:"column(trade_futures_company_id);pk"`
+	CompanyName           string    `description:"标准公司名称"`
+	ZhengzhouName         string    `description:"郑商所下的名称"`
+	DalianName            string    `description:"大商所下的名称"`
+	ShanghaiName          string    `description:"上期所下的名称"`
+	IneName               string    `description:"上期能源下的名称"`
+	GuangzhouName         string    `description:"广期所下的名称"`
+	CffexName             string    `description:"中金所下的名称"`
+	Sort                  int       `description:"排序"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+func (m *TradeFuturesCompany) TableName() string {
+	return "trade_futures_company"
+}
+
+type TradeFuturesCompanyCols struct {
+	PrimaryId     string
+	CompanyName   string
+	ZhengzhouName string
+	DalianName    string
+	ShanghaiName  string
+	IneName       string
+	GuangzhouName string
+	CffexName     string
+	Sort          string
+	CreateTime    string
+	ModifyTime    string
+}
+
+func (m *TradeFuturesCompany) Cols() TradeFuturesCompanyCols {
+	return TradeFuturesCompanyCols{
+		PrimaryId:     "trade_futures_company_id",
+		CompanyName:   "company_name",
+		ZhengzhouName: "zhengzhou_name",
+		DalianName:    "dalian_name",
+		ShanghaiName:  "shanghai_name",
+		IneName:       "ine_name",
+		GuangzhouName: "guangzhou_name",
+		CffexName:     "cffex_name",
+		Sort:          "sort",
+		CreateTime:    "create_time",
+		ModifyTime:    "modify_time",
+	}
+}
+
+func (m *TradeFuturesCompany) GetItemById(id int) (item *TradeFuturesCompany, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *TradeFuturesCompany) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *TradeFuturesCompany, err error) {
+	o := orm.NewOrm()
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *TradeFuturesCompany) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *TradeFuturesCompany) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*TradeFuturesCompany, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *TradeFuturesCompany) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*TradeFuturesCompany, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// TradeFuturesCompanyItem 期货公司信息
+type TradeFuturesCompanyItem struct {
+	CompanyId   int    `description:"期货公司ID"`
+	CompanyName string `description:"标准公司名称"`
+	Sort        int    `description:"排序"`
+}
+
+func (m *TradeFuturesCompany) Format2Item() (item *TradeFuturesCompanyItem) {
+	item = new(TradeFuturesCompanyItem)
+	item.CompanyId = m.TradeFuturesCompanyId
+	item.CompanyName = m.CompanyName
+	item.Sort = m.Sort
+	return
+}

+ 47 - 0
models/trade_analysis/warehouse.go

@@ -0,0 +1,47 @@
+package trade_analysis
+
+// WarehouseExtraConfig 建仓图表配置
+type WarehouseExtraConfig struct {
+	MultipleGraphConfigId int      `description:"多图配置ID"`
+	WarehouseChartType    int      `description:"图表类型: 1-多单图; 2-空单图; 3-净多单图"`
+	Exchange              string   `description:"交易所标识"`
+	ClassifyName          string   `description:"品种名称"`
+	Contracts             []string `description:"合约代码"`
+	Companies             []string `description:"期货公司, 不超过5个"`
+	PredictRatio          float64  `description:"预估参数, 0-1之间"`
+}
+
+// WarehouseChartPars 建仓单表配置
+//type WarehouseChartPars struct {
+//	WarehouseChartType int    `description:"图表类型: 1-多单图; 2-空单图; 3-净多单图"`
+//	DateType           int    `description:"日期类型"`
+//	DateTypeNum        int    `description:"日期类型=25(N月)时的N值"`
+//	StartDate          string `description:"自定义开始日期"`
+//	EndDate            string `description:"自定义结束日期"`
+//	//ChartThemeId       int                               `description:"图表主题ID"`
+//	ChartEdbInfoList []*models.ChartSaveItem `description:"指标及配置信息"`
+//	//SourcesFrom        *data_manage.ChartInfoSourcesFrom `description:"图表来源"`
+//}
+
+// WarehouseChartDataResp 图表详情返回信息
+type WarehouseChartDataResp struct {
+	WarehouseExtraConfig
+	MultiEdbMappings []*WarehouseEdbSaveItem
+}
+
+// WarehouseEdbSaveItem 建仓指标保存
+type WarehouseEdbSaveItem struct {
+	EdbInfoId  int    `description:"指标ID"`
+	EdbName    string `description:"指标名称"`
+	Unit       string `description:"单位"`
+	Frequency  string `description:"频度"`
+	ClassifyId int    `description:"指标库分类ID"`
+	UniqueFlag string `description:"唯一标识"`
+	//ExtraConfig string `description:"配置信息-JSON"`
+}
+
+type WarehouseEdbSaveRespItem struct {
+	WarehouseEdbSaveItem
+	Tips   string `description:"提示信息"`
+	ErrMsg string `description:"错误信息"`
+}

+ 189 - 0
routers/commentsRouter.go

@@ -97,6 +97,159 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "AddBatchLyEdbData",
+            Router: `/add/batch/ly/edb/data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "AddLyDataList",
+            Router: `/add/ly/data/list`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "AddLyIndex",
+            Router: `/add/ly/index`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "AddLyIndexRecord",
+            Router: `/add/ly/index/record`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetEdbInfoByIndexCode",
+            Router: `/get/edb/info/by/index/code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyClassifyByName",
+            Router: `/get/ly/classify/by/name`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyDataByIndexIdAndDataTime",
+            Router: `/get/ly/data/by/index/id/and/data/time`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyDataByIndexIdAndDataTimeYM",
+            Router: `/get/ly/data/by/index/id/and/data/time/ym`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyEdbDataByIndexCodeAndDataTime",
+            Router: `/get/ly/edb/data/by/index/code/and/data/time`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyEdbDataByIndexCodeAndExactDataTime",
+            Router: `/get/ly/edb/data/by/index/code/and/exact/data/time`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyIndexByCode",
+            Router: `/get/ly/index/by/code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "GetLyIndexRecordByUrl",
+            Router: `/get/ly/index/record/by/url`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "UpdateLyDataById",
+            Router: `/update/ly/data/by/id`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromLyController"],
+        beego.ControllerComments{
+            Method: "UpdateLyEdbDataById",
+            Router: `/update/ly/edb/data/by/id`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromTradeAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromTradeAnalysisController"],
+        beego.ControllerComments{
+            Method: "EdbRefresh",
+            Router: `/edb/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BloombergController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BloombergController"],
         beego.ControllerComments{
             Method: "Add",
@@ -637,6 +790,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:FenweiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:FenweiController"],
+        beego.ControllerComments{
+            Method: "NetDataHandle",
+            Router: `/net/data/handle`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:FenweiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:FenweiController"],
         beego.ControllerComments{
             Method: "Refresh",
@@ -718,6 +880,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"],
+        beego.ControllerComments{
+            Method: "HandleEdbData",
+            Router: `/handle/edb_data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:HisugarController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:IcpiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:IcpiController"],
         beego.ControllerComments{
             Method: "Add",

+ 15 - 0
routers/router.go

@@ -287,11 +287,26 @@ func init() {
 				&factor_edb_series.FactorEdbSeriesController{},
 			),
 		),
+		beego.NSNamespace("/ly",
+			beego.NSInclude(
+				&controllers.BaseFromLyController{},
+			),
+		),
 		beego.NSNamespace("/oilchem",
 			beego.NSInclude(
 				&controllers.OilchemController{},
 			),
 		),
+		beego.NSNamespace("/trade_analysis",
+			beego.NSInclude(
+				&controllers.BaseFromTradeAnalysisController{},
+			),
+		),
+		beego.NSNamespace("/hisugar",
+			beego.NSInclude(
+				&controllers.HisugarController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 287 - 0
services/base_from_fenwei.go

@@ -6,8 +6,11 @@ import (
 	"eta/eta_index_lib/services/alarm_msg"
 	"eta/eta_index_lib/utils"
 	"fmt"
+	"github.com/beego/beego/v2/core/logs"
+	"github.com/mozillazg/go-pinyin"
 	"strings"
 	"time"
+	"unicode"
 )
 
 // HandleFenweiIndex 处理汾渭数据的excel数据
@@ -247,3 +250,287 @@ func handleFenweiIndex(req *models.HandleFenweiExcelData, terminalCode string) (
 	}
 	return
 }
+
+func NetDataHandle(req models.HandleFenWeiNetDataReq) error {
+	indexInfoList := req.List
+	classifyObj := new(models.BaseFromFenweiClassify)
+
+	// 因为传递过来得List是 指标数据 维度得,获取全量得指标数据比对,会导致分类和指标 频繁查询,会频繁访问数据库,做个简单优化吧
+	classifyMap := make(map[string]models.FenWeiNetIndexInfo)
+	indexMap := make(map[string]models.FenWeiNetIndexInfo)
+	for _, indexInfo := range indexInfoList {
+		classifyMap[indexInfo.ClassifyName] = *indexInfo
+		indexMap[indexInfo.IndexName] = *indexInfo
+	}
+
+	// 处理分类信息
+	classifyIdMap := make(map[string]int)
+	for classifyName, reqData := range classifyMap {
+		var classifyId int
+		classify, err := classifyObj.GetByClassifyName(classifyName)
+		if err != nil {
+			return err
+		}
+		if classify == nil {
+			logs.Info("NetDataHandle addClassify classifyName: ", classifyName)
+			classifyId, err = addFenWeiClassify(&reqData)
+			if err != nil {
+				return err
+			}
+		} else {
+			classifyId = classify[0].ClassifyId
+		}
+
+		// 保存分类ID
+		classifyIdMap[classifyName] = classifyId
+	}
+
+	// 处理指标信息
+	indexIdMap := make(map[string]int64)
+	indexCodeMap := make(map[string]string)
+	for indexName, reqData := range indexMap {
+		indexCode := GenerateIndexCode(indexName)
+		indexCodeMap[indexName] = indexCode
+		index, err := models.GetBaseFromFenWeiIndexByIndexName(indexName)
+		if err != nil {
+			return err
+		}
+		if index == nil {
+			logs.Info("NetDataHandle indexName: ", indexName)
+			classifyId := classifyIdMap[reqData.ClassifyName]
+			logs.Info("NetDataHandle addIndex indexName: ", indexName)
+			indexId, err := addIndex(&reqData, classifyId, indexCode)
+			if err != nil {
+				return err
+			}
+			indexIdMap[reqData.IndexName] = indexId
+		} else {
+			logs.Info("NetDataHandle exist indexName: ", indexName)
+			indexIdMap[reqData.IndexName] = index.FenweiIndexId
+		}
+	}
+
+	indexDataList := make([]*models.BaseFromFenweiData, 0)
+	for _, indexInfo := range indexInfoList {
+		// 处理指标数据
+		indexData, err := handleIndexData(indexInfo, indexIdMap, indexCodeMap)
+		if err != nil {
+			return err
+		}
+		if indexData != nil {
+			indexDataList = append(indexDataList, indexData)
+		}
+	}
+	// 批量插入指标数据
+	logs.Info("NetDataHandle addIndexData indexDataList.size: ", len(indexDataList))
+	if len(indexDataList) == 0 {
+		return nil
+	}
+	err := models.BatchAddBaseFromFenWeiData(indexDataList)
+	if err != nil {
+		return err
+	}
+	logs.Info("NetDataHandle addIndexData success")
+	return nil
+}
+
+func handleIndexData(indexInfo *models.FenWeiNetIndexInfo, indexIdMap map[string]int64, indexCodeMap map[string]string) (*models.BaseFromFenweiData, error) {
+	var format string
+	if isYearMonth(indexInfo.DataTime) {
+		format = convertYearMonthToLastDay(indexInfo.DataTime)
+	} else if indexInfo.Frequency == "月度" && isFirstDayOfMonth(indexInfo.DataTime) {
+		format = convertYearMonthDayToLastDay(indexInfo.DataTime)
+	} else {
+		format = indexInfo.DataTime
+	}
+
+	// 获取指标数据
+	indexCode := indexCodeMap[indexInfo.IndexName]
+	indexData, err := models.GetBaseFromFenweiDataByIndexCodeAndDataTime(indexCode, format)
+	if err != nil {
+		return nil, err
+	}
+	if indexData != nil {
+		// 汾渭不存在数据更新和预测值情况,所以此处未做更新逻辑
+		return nil, nil
+	}
+
+	timestamp := time.Now().UnixNano() / 1e6
+
+	indexId := indexIdMap[indexInfo.IndexName]
+
+	data := new(models.BaseFromFenweiData)
+	data.FenweiIndexId = int(indexId)
+	data.IndexCode = indexCode
+	data.DataTime = format
+	data.Value = fmt.Sprintf("%v", indexInfo.Value)
+	data.CreateTime = time.Now()
+	data.ModifyTime = time.Now()
+	data.DataTimestamp = timestamp
+
+	// 打印data对象日志
+
+	logs.Info("handleIndexData data : ", data)
+
+	return data, nil
+}
+
+func addIndex(info *models.FenWeiNetIndexInfo, classifyId int, indexCode string) (indexId int64, err error) {
+	byIndexCode, err := models.GetBaseFromFenWeiIndexByIndexCode(indexCode)
+	if err != nil {
+		return 0, err
+	}
+	if byIndexCode != nil {
+		return byIndexCode.FenweiIndexId, nil
+	}
+
+	index := new(models.BaseFromFenweiIndex)
+	index.IndexName = info.IndexName
+	index.IndexCode = indexCode
+	index.IndexCode = indexCode
+	index.Frequency = info.Frequency
+	index.ClassifyId = classifyId
+	index.Unit = info.Unit
+	index.ModifyTime = time.Now()
+	index.CreateTime = time.Now()
+	index.TerminalCode = info.TerminalCode
+	indexId, err = index.Add()
+	if err != nil {
+		return 0, err
+	}
+	return indexId, nil
+}
+
+// addFenWeiClassify 添加分类
+func addFenWeiClassify(info *models.FenWeiNetIndexInfo) (int, error) {
+	// 初始化分类结构
+	classify := new(models.BaseFromFenweiClassify)
+	classify.ClassifyName = info.ClassifyName
+
+	// 递归处理分类层级
+	return handleClassify(classify, 0, 1)
+}
+
+func handleClassify(classify *models.BaseFromFenweiClassify, parentId int, level int) (int, error) {
+	// 将分类名按 "-" 分割
+	classifyArr := strings.Split(classify.ClassifyName, "-")
+
+	// 当前分类名称
+	currentClassifyName := classifyArr[0]
+	classify.ClassifyName = currentClassifyName
+	classify.ParentId = parentId
+	classify.Level = level
+
+	// 检查当前分类是否已存在
+	existingClassifyList, err := classify.GetByClassifyName(currentClassifyName)
+	if err != nil {
+		return 0, err
+	}
+	var existingClassify *models.BaseFromFenweiClassify
+	for _, item := range existingClassifyList {
+		if item.ParentId == parentId {
+			existingClassify = item
+		}
+	}
+
+	// 如果分类存在,且父级ID相同,则递归处理下一级
+	if existingClassify != nil && existingClassify != nil {
+		// 如果存在且有下一级,则递归处理下一级
+		if len(classifyArr) > 1 {
+			nextClassifyName := strings.Join(classifyArr[1:], "-")
+			nextClassify := new(models.BaseFromFenweiClassify)
+			nextClassify.ClassifyName = nextClassifyName
+			return handleClassify(nextClassify, existingClassify.ClassifyId, existingClassify.Level+1)
+		}
+		return existingClassify.ClassifyId, nil
+	}
+
+	// 如果不存在,新增当前分类
+	classify.SysUserId = 0
+	classify.SysUserRealName = ""
+	classify.ModifyTime = time.Now()
+	classify.CreateTime = time.Now()
+	classifyId, err := models.AddBaseFromFenWeiClassify(classify)
+	if err != nil {
+		return 0, err
+	}
+
+	// 如果有下一级,递归处理下一级
+	if len(classifyArr) > 1 {
+		nextClassifyName := strings.Join(classifyArr[1:], "-")
+		nextClassify := new(models.BaseFromFenweiClassify)
+		nextClassify.ClassifyName = nextClassifyName
+		return handleClassify(nextClassify, int(classifyId), level+1)
+	}
+
+	return int(classifyId), nil
+}
+
+// 判断传入参数 dataText 是否是yyyy-MM格式,如果是则返回true,否则返回false
+func isYearMonth(dataText string) bool {
+	_, err := time.Parse("2006-01", dataText)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+// 判断传入参数 dataText yyyy-MM-dd格式,是否是该月第一天,如果是则返回true,否则返回false
+func isFirstDayOfMonth(dataText string) bool {
+	t, _ := time.Parse("2006-01-02", dataText)
+	if t.Day() == 1 {
+		return true
+	}
+	return false
+}
+
+// 转换时间 dataText yyyy-MM-dd格式,获取该月最后一天的日期
+func convertYearMonthDayToLastDay(dataText string) string {
+	t, _ := time.Parse("2006-01-02", dataText)
+	lastDay := t.AddDate(0, 1, -1)
+	return lastDay.Format("2006-01-02")
+}
+
+// 转换时间 dataText 2024-08 yyyy-MM 格式的时间字符串转换为该月最后一天的日期Date --> 2024-08-31
+func convertYearMonthToLastDay(dataText string) string {
+	t, _ := time.Parse("2006-01", dataText)
+	lastDay := t.AddDate(0, 1, -1)
+	return lastDay.Format("2006-01-02")
+}
+
+// GenerateIndexCode 指标编码规则:指标名称拼音首字母,数字、字母保留,特殊字符拿掉
+// 例:美湾:9月U:国际大豆进口成本价:期货收盘:张家港 -----> lyswwmw9yUgjddjkcbjqhspzjg
+func GenerateIndexCode(indexName string) string {
+
+	// 获取汉字的拼音首字母,保留数字和大写字母
+	indexCode := getFirstLetters(indexName)
+
+	return indexCode
+}
+
+// getFirstLetters 获取汉字的拼音首字母,并保留数字和大写字母
+func getFirstLetters(input string) string {
+	// 设置拼音转换选项,只获取首字母
+	args := pinyin.NewArgs()
+	args.Style = pinyin.FirstLetter
+
+	// 定义用于存储结果的字符串
+	var result strings.Builder
+
+	// 遍历输入字符串中的每个字符
+	for _, r := range input {
+		if unicode.IsDigit(r) || unicode.IsUpper(r) {
+			// 保留数字和大写字母
+			result.WriteRune(r)
+		} else if unicode.Is(unicode.Han, r) {
+			// 如果是汉字,则获取其拼音首字母
+			py := pinyin.Pinyin(string(r), args)
+			if len(py) > 0 && len(py[0]) > 0 {
+				result.WriteString(py[0][0])
+			}
+		}
+		// 对于其他字符,忽略处理
+	}
+
+	return result.String()
+}

+ 156 - 0
services/base_from_hisugar.go

@@ -0,0 +1,156 @@
+package services
+
+import (
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/mozillazg/go-pinyin"
+	"strings"
+)
+
+var HisugarIndexCodeMap = make(map[string]string)
+var HisugarIndexMap = make(map[string]*models.BaseFromHisugarIndex)
+func HandleHisugarIndex(list []*models.BaseFromHisugarIndexReq) (err error) {
+	allCode, e := models.GetBaseFromHisugarIndex()
+	if e != nil {
+		err = e
+		fmt.Println("select Code err:", err)
+		utils.FileLog.Info("GetBaseFromHisugarIndex err:", err)
+		return
+	}
+
+	for _, item := range allCode {
+		HisugarIndexCodeMap[item.IndexName] = item.IndexCode
+		HisugarIndexMap[item.IndexName] = item
+	}
+
+	for _, v := range list {
+		indexCode, needAdd := HisugarIndexCodeGenerator(v.IndexName, v.IndexNameStr, v.MarketName)
+		if needAdd {
+			item := models.BaseFromHisugarIndex{
+				IndexCode:              indexCode,
+				IndexName:              v.IndexName,
+				ClassifyId:             v.ClassifyId,
+				Unit:                   v.Unit,
+				Frequency:              v.Frequency,
+				Describe:               v.Describe,
+				Sort:                   v.Sort,
+				CreateTime:             v.CreateTime,
+				ModifyTime:             v.ModifyTime,
+			}
+			id, e := models.AddBaseFromHisugarIndex(&item)
+			if e != nil {
+				err = e
+				fmt.Println("AddBaseFromHisugarIndexMuti err:", err)
+				utils.FileLog.Info("AddBaseFromHisugarIndexMuti err:", err)
+				return
+			}
+			item.BaseFromHisugarIndexId = int(id)
+			HisugarIndexMap[item.IndexName] = &item
+		}
+
+
+		//获取指标数据信息
+		data, e := models.GetBaseFromHisugarData(indexCode, v.DataTime)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = e
+			fmt.Println("select err:", err)
+			utils.FileLog.Info("GetBaseFromTradeSci99IndexAll err:", err)
+		}
+
+		if data != nil {
+			if data.Value != v.Value {
+				// 更新
+				fmt.Println("更新指标:", indexCode+v.DataTime)
+				utils.FileLog.Info("更新指标:", indexCode+v.DataTime)
+				e = models.UpdateBaseFromHisugarData(v.Value, indexCode, v.DataTime)
+				if e != nil {
+					err = e
+					fmt.Println("Error update into database:", err)
+					utils.FileLog.Info("Error update into database:", err)
+					return
+				}
+			}
+		} else {
+			// 新增
+			dataItem := models.BaseFromHisugarData{
+				BaseFromHisugarIndexId: HisugarIndexMap[v.IndexName].BaseFromHisugarIndexId,
+				IndexCode:              indexCode,
+				DataTime:               v.DataTime,
+				Value:                  v.Value,
+				CreateTime:             v.CreateTime,
+				ModifyTime:             v.ModifyTime,
+			}
+			fmt.Println("新增数据:", indexCode+v.DataTime)
+			utils.FileLog.Info("新增数据:", indexCode+v.DataTime)
+
+			_,e = models.AddBaseFromHisugarData(&dataItem)
+			if e != nil {
+				err = e
+				fmt.Println("Error inserting into database:", err)
+				utils.FileLog.Info("Error inserting into database:", err)
+				return
+			}
+		}
+	}
+
+	return
+}
+
+
+func HisugarIndexCodeGenerator(indexName, indexCodeStr, marketSampleName string) (indexCode string, needAdd bool) {
+	strResult := ""
+	indexCodeStr = indexName
+	indexCode, _ = HisugarIndexCodeMap[indexName]
+	if indexCode == "" {
+		//首字母
+		a := pinyin.NewArgs()
+		a.Fallback = func(r rune, a pinyin.Args) []string {
+			return []string{string(r)}
+		}
+		indexCodeStr = strings.Replace(indexCodeStr, "(", "", -1)
+		indexCodeStr = strings.Replace(indexCodeStr, ")", "", -1)
+		rows := pinyin.Pinyin(indexCodeStr, a)
+		for i := 0; i < len(rows); i++ {
+			//strResult += rows[i][0]
+			if len(rows[i]) != 0 {
+				str := rows[i][0]
+				pi := str[0:1]
+				strResult += pi
+			}
+		}
+
+		// 处理括号内名称
+		if marketSampleName != "" {
+			if province, ok := ProvinceMap[marketSampleName]; ok {
+				strResult += province
+			} else {
+				a := pinyin.NewArgs()
+				rows := pinyin.LazyPinyin(marketSampleName, a)
+				for i := 0; i < len(rows); i++ {
+					strResult += rows[i]
+				}
+				if len(rows) == 0 {
+					strResult += marketSampleName
+				}
+			}
+		}
+
+
+		// 去除特殊符号
+		strResult = strings.Replace(strResult, " ", "", -1)
+		strResult = strings.Replace(strResult, "-", "", -1)
+		strResult = strings.Replace(strResult, "/", "", -1)
+		strResult = strings.Replace(strResult, "#", "", -1)
+		strResult = strings.Replace(strResult, ":", "", -1)
+		strResult = strings.Replace(strResult, "(", "", -1)
+		strResult = strings.Replace(strResult, ")", "", -1)
+
+
+		needAdd = true
+		strResult = "ftkj" + strResult
+		indexCode = strings.Replace(strResult, " ", "", -1)
+		HisugarIndexCodeMap[indexName] = indexCode
+	}
+	return
+}

+ 3 - 0
services/base_from_mysteel_chemical.go

@@ -353,10 +353,12 @@ func handleIndex(indexItem *models.HandleMysteelIndex) (err error) {
 		indexObj.Frequency = indexItem.Frequency
 		indexObj.ModifyTime = time.Now()
 		indexId = item.BaseFromMysteelChemicalIndexId
+		indexObj.IsSupplierStop = item.IsSupplierStop
 
 		var isStop int
 		if strings.Contains(indexItem.IndexName, "停") {
 			isStop = 1
+			indexObj.IsSupplierStop = 1
 		}
 		indexObj.IsStop = isStop
 
@@ -371,6 +373,7 @@ func handleIndex(indexItem *models.HandleMysteelIndex) (err error) {
 		updateColsArr = append(updateColsArr, "describe")
 		updateColsArr = append(updateColsArr, "end_date")
 		updateColsArr = append(updateColsArr, "is_stop")
+		updateColsArr = append(updateColsArr, "is_supplier_stop")
 		updateColsArr = append(updateColsArr, "modify_time")
 
 		e := indexObj.Update(updateColsArr)

+ 1 - 1
services/base_from_predict.go

@@ -246,7 +246,7 @@ func CalculateByRuleByNine(formulaStr string, edbInfoList []*models.EdbInfo, edb
 		//	return
 		//}
 
-		saveValue, _ := decimal.NewFromFloat(calVal).RoundCeil(4).Float64() //utils.SubFloatToString(calVal, 4)
+		saveValue, _ := decimal.NewFromFloat(calVal).Round(4).Float64() //utils.SubFloatToString(calVal, 4)
 		dataTime, _ := time.Parse(utils.FormatDate, date)
 		timestamp := dataTime.UnixNano() / 1e6
 

+ 1 - 1
services/base_from_smm.go

@@ -235,7 +235,7 @@ func SmmIndexHandle(baseFilePath, renameFilePath, indexName, indexCode, unit, fr
 }
 
 func GetEdbDataFromSmm(edbCode string) (smmBaseDataAll []models.BaseFromSmmDataList, err error) {
-	if utils.BusinessCode == "E2023110300" {
+	if utils.SmmDataMethod == "api" {
 		return GetSmmNewIndexFromBridge(edbCode)
 	}
 

+ 3 - 0
services/base_from_yongyi.go

@@ -94,6 +94,9 @@ func handleYongyiIndex(req *models.HandleYongyiExcelData, terminalCode string, c
 					ModifyTime:      now,
 					CreateTime:      now,
 				}
+				if classifyParentId > 0 {
+					classifyObj.Level = 2
+				}
 
 				classifyId, err = classifyObj.Add()
 				if err != nil {

+ 93 - 0
services/factor_edb_series.go

@@ -1,6 +1,8 @@
 package services
 
 import (
+	"encoding/json"
+	"errors"
 	"eta/eta_index_lib/models"
 	"eta/eta_index_lib/services/alarm_msg"
 	"eta/eta_index_lib/utils"
@@ -73,3 +75,94 @@ func FactorEdbStepCalculate(seriesId, edbInfoId int, edbCode string, edbData []*
 	}
 	return
 }
+
+func RangeAnalysisChartCalculate(seriesId, chartInfoId int, seriesMappingItem *models.FactorEdbSeriesChartMapping) (err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("RangeAnalysisChartCalculate 区间分析图表计算失败, 图表ID:%d, ErrMsg: %v", chartInfoId, err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+	chartInfo, err := models.GetChartInfoById(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "图表已被删除,请刷新页面"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取图表信息失败"
+		err = errors.New("获取图表信息失败,Err:" + err.Error())
+		return
+	}
+	// 区间计算图表配置校验
+	var extraConfig models.RangeAnalysisCalculateFormula
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg + ", Err: " + err.Error())
+		return
+	}
+	if seriesMappingItem.FactorEdbSeriesChartMappingId > 0 {
+		ob := new(models.FactorEdbSeriesMapping)
+		seriesEdbMappingList, e := ob.GetItemBySeriesId(seriesMappingItem.FactorEdbSeriesId)
+		if e != nil { // 没有数据,则不计算
+			return
+		}
+		edbInfoIds := make([]int, 0)
+		for _, item := range seriesEdbMappingList {
+			edbInfoIds = append(edbInfoIds, item.EdbInfoId)
+		}
+
+		edbInfoList, e := models.GetEdbInfoByIdList(edbInfoIds)
+		if e != nil {
+			errMsg = "获取指标信息失败"
+			err = errors.New(errMsg + ", Err: " + e.Error())
+			return
+		}
+		// 重新计算-先清除原数据
+		calculateDataOb := new(models.FactorEdbSeriesCalculateDataQjjs)
+
+		cond := fmt.Sprintf("%s = ?", calculateDataOb.Cols().FactorEdbSeriesId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, seriesId)
+		if e := calculateDataOb.RemoveByCondition(cond, pars); e != nil {
+			err = fmt.Errorf("清除原数据失败, err: %v", e)
+			return
+		}
+
+		// 计算成功的保存结果
+		dataArr := make([]*models.FactorEdbSeriesCalculateDataQjjs, 0)
+		for _, fromEdbInfo := range edbInfoList {
+			dataList, e := models.GetRangeAnalysisChartDataByEdbInfo(fromEdbInfo, extraConfig)
+			if e != nil {
+				errMsg = "获取区间分析图表数据失败"
+				err = errors.New(errMsg + ", Err: " + e.Error())
+				return
+			}
+			for _, dataItem := range dataList {
+				dataTime, _ := time.ParseInLocation(utils.FormatDate, dataItem.DataTime, time.Local)
+				dataArr = append(dataArr, &models.FactorEdbSeriesCalculateDataQjjs{
+					FactorEdbSeriesId: seriesId,
+					EdbInfoId:         fromEdbInfo.EdbInfoId,
+					EdbCode:           fromEdbInfo.EdbCode,
+					DataTime:          dataTime,
+					Value:             dataItem.Value,
+					CreateTime:        time.Now().Local(),
+					ModifyTime:        time.Now().Local(),
+					DataTimestamp:     dataItem.DataTimestamp,
+				})
+			}
+		}
+		if len(dataArr) == 0 {
+			err = fmt.Errorf("计算结果无数据, seriesId: %d", seriesId)
+			return
+		}
+		if e := calculateDataOb.CreateMulti(dataArr); e != nil {
+			err = fmt.Errorf("保存计算结果失败, seriesId: %d, err: %v, ", seriesId, e)
+			return
+		}
+	}
+	return
+}

+ 468 - 0
services/trade_analysis/trade_analysis_data.go

@@ -0,0 +1,468 @@
+package trade_analysis
+
+import (
+	"eta/eta_index_lib/models"
+	tradeAnalysisModel "eta/eta_index_lib/models/trade_analysis"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"sort"
+	"strings"
+	"time"
+)
+
+// FormatCompanyTradeData2EdbData [公司-合约加总]转为指标数据
+func FormatCompanyTradeData2EdbData(companyTradeData *tradeAnalysisModel.ContractCompanyTradeData, tradeType int) (edbData []*models.EdbDataList, err error) {
+	if companyTradeData == nil {
+		err = fmt.Errorf("持仓数据异常")
+		return
+	}
+	edbData = make([]*models.EdbDataList, 0)
+	var minData, maxData float64
+	for dk, dv := range companyTradeData.DataList {
+		// 交易方向
+		var val float64
+		if tradeType == tradeAnalysisModel.WarehouseBuyChartType {
+			if dv.BuyValType == tradeAnalysisModel.TradeDataTypeNull {
+				continue
+			}
+			val = float64(dv.BuyVal)
+		}
+		if tradeType == tradeAnalysisModel.WarehouseSoldChartType {
+			if dv.SoldValType == tradeAnalysisModel.TradeDataTypeNull {
+				continue
+			}
+			val = float64(dv.SoldVal)
+		}
+		if tradeType == tradeAnalysisModel.WarehousePureBuyChartType {
+			if dv.PureBuyValType == tradeAnalysisModel.TradeDataTypeNull {
+				continue
+			}
+			val = float64(dv.PureBuyVal)
+		}
+
+		if dk == 0 {
+			minData = val
+			maxData = val
+		}
+		if val < minData {
+			minData = val
+		}
+		if val > maxData {
+			maxData = val
+		}
+		edbData = append(edbData, &models.EdbDataList{
+			DataTime:      dv.Date.Format(utils.FormatDate),
+			DataTimestamp: dv.Date.UnixNano() / 1e6,
+			Value:         val,
+		})
+	}
+	return
+}
+
+// GetOriginTradeData 获取原始持仓数据
+func GetOriginTradeData(exchange, classifyName string, contracts, companies []string, predictRatio float64) (companyTradeData []*tradeAnalysisModel.ContractCompanyTradeData, err error) {
+	// 各原始数据表期货公司名称不一致
+	companyMap := make(map[string]string)
+	{
+		ob := new(tradeAnalysisModel.TradeFuturesCompany)
+		list, e := ob.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取期货公司名称失败: %v", e)
+			return
+		}
+		switch exchange {
+		case "zhengzhou":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.ZhengzhouName
+			}
+		case "dalian":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.DalianName
+			}
+		case "shanghai":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.ShanghaiName
+			}
+		case "cffex":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.CffexName
+			}
+		case "ine":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.IneName
+			}
+		case "guangzhou":
+			for _, v := range list {
+				companyMap[v.CompanyName] = v.GuangzhouName
+			}
+		}
+	}
+	var queryCompanies []string
+	for _, v := range companies {
+		// TOP20用空名称去查询
+		if v == tradeAnalysisModel.TradeFuturesCompanyTop20 {
+			queryCompanies = append(queryCompanies, "")
+			continue
+		}
+		companyName, ok := companyMap[v]
+		if !ok {
+			utils.FileLog.Info(fmt.Sprintf("交易所%s公司名称映射不存在: %s", exchange, v))
+			continue
+		}
+		queryCompanies = append(queryCompanies, companyName)
+	}
+
+	// 郑商所/广期所查询方式不一样
+	var tradeAnalysis TradeAnalysisInterface
+	switch exchange {
+	case tradeAnalysisModel.TradeExchangeZhengzhou:
+		tradeAnalysis = &ZhengzhouTradeAnalysis{}
+	case tradeAnalysisModel.TradeExchangeGuangzhou:
+		tradeAnalysis = &GuangzhouTradeAnalysis{}
+	default:
+		tradeAnalysis = &BaseTradeAnalysis{}
+	}
+
+	// 获取多单/空单原始数据
+	originList, e := tradeAnalysis.GetTradeDataByClassifyAndCompany(exchange, classifyName, contracts, queryCompanies)
+	if e != nil {
+		err = fmt.Errorf("获取多空单原始数据失败, %v", e)
+		return
+	}
+
+	keyItems := make(map[string]*tradeAnalysisModel.ContractCompanyTradeData)
+	keyDateData := make(map[string]*tradeAnalysisModel.ContractCompanyTradeDataList)
+	keyDateDataExist := make(map[string]bool)
+	for _, v := range originList {
+		// TOP20对应数据库中的空名称
+		companyName := v.CompanyName
+		if companyName == "" {
+			companyName = tradeAnalysisModel.TradeFuturesCompanyTop20
+		}
+
+		k := fmt.Sprintf("%s-%s", v.ClassifyType, companyName)
+		if keyItems[k] == nil {
+			keyItems[k] = new(tradeAnalysisModel.ContractCompanyTradeData)
+			keyItems[k].CompanyName = companyName
+			keyItems[k].ClassifyType = v.ClassifyType
+			keyItems[k].DataList = make([]*tradeAnalysisModel.ContractCompanyTradeDataList, 0)
+		}
+
+		kd := fmt.Sprintf("%s-%s", k, v.DataTime.Format(utils.FormatDate))
+		if keyDateData[kd] == nil {
+			keyDateData[kd] = new(tradeAnalysisModel.ContractCompanyTradeDataList)
+			keyDateData[kd].Date = v.DataTime
+		}
+		if v.ValType == 1 {
+			keyDateData[kd].BuyVal = v.Val
+			keyDateData[kd].BuyValType = tradeAnalysisModel.TradeDataTypeOrigin
+			keyDateData[kd].BuyChange = v.ValChange
+			keyDateData[kd].BuyChangeType = tradeAnalysisModel.TradeDataTypeOrigin
+		}
+		if v.ValType == 2 {
+			keyDateData[kd].SoldVal = v.Val
+			keyDateData[kd].SoldValType = tradeAnalysisModel.TradeDataTypeOrigin
+			keyDateData[kd].SoldChange = v.ValChange
+			keyDateData[kd].SoldChangeType = tradeAnalysisModel.TradeDataTypeOrigin
+		}
+		if !keyDateDataExist[kd] {
+			keyItems[k].DataList = append(keyItems[k].DataList, keyDateData[kd])
+			keyDateDataExist[kd] = true
+		}
+	}
+
+	// 获取[合约]每日的末位多空单
+	contractLastBuyDateVal := make(map[string]map[time.Time]int)
+	contractLastSoldDateVal := make(map[string]map[time.Time]int)
+	{
+		lastOriginList, e := tradeAnalysis.GetLastTradeDataByClassify(exchange, classifyName, contracts)
+		if e != nil {
+			err = fmt.Errorf("获取末位多空单原始数据失败, %v", e)
+			return
+		}
+		for _, v := range lastOriginList {
+			if v.ValType == 1 {
+				if contractLastBuyDateVal[v.ClassifyType] == nil {
+					contractLastBuyDateVal[v.ClassifyType] = make(map[time.Time]int)
+				}
+				contractLastBuyDateVal[v.ClassifyType][v.DataTime] = v.Val
+				continue
+			}
+			if contractLastSoldDateVal[v.ClassifyType] == nil {
+				contractLastSoldDateVal[v.ClassifyType] = make(map[time.Time]int)
+			}
+			contractLastSoldDateVal[v.ClassifyType][v.DataTime] = v.Val
+		}
+	}
+
+	// 填充[合约-公司]预估数据, 并根据[公司-多合约]分组, [公司]算作一个指标, 指标值为[多个合约]的计算加总
+	companyContracts := make(map[string][]*tradeAnalysisModel.ContractCompanyTradeData)
+	for _, v := range keyItems {
+		td, fd, ed, e := PredictingTradeData(v.DataList, contractLastBuyDateVal[v.ClassifyType], contractLastSoldDateVal[v.ClassifyType], predictRatio)
+		if e != nil {
+			err = fmt.Errorf("数据补全失败, %v", e)
+			return
+		}
+		v.DataList = td
+		v.StartDate = fd
+		v.EndDate = ed
+
+		if companyContracts[v.CompanyName] == nil {
+			companyContracts[v.CompanyName] = make([]*tradeAnalysisModel.ContractCompanyTradeData, 0)
+		}
+		companyContracts[v.CompanyName] = append(companyContracts[v.CompanyName], v)
+	}
+
+	// 以[公司]为组, 计算合约加总
+	companyTradeData = make([]*tradeAnalysisModel.ContractCompanyTradeData, 0)
+	for k, v := range companyContracts {
+		companyData := new(tradeAnalysisModel.ContractCompanyTradeData)
+		companyData.CompanyName = k
+		companyData.DataList = make([]*tradeAnalysisModel.ContractCompanyTradeDataList, 0)
+		contractArr := make([]string, 0)
+
+		// 合约加总
+		sumDateData := make(map[time.Time]*tradeAnalysisModel.ContractCompanyTradeDataList)
+		for _, vv := range v {
+			contractArr = append(contractArr, vv.ClassifyType)
+			for _, dv := range vv.DataList {
+				if sumDateData[dv.Date] == nil {
+					sumDateData[dv.Date] = new(tradeAnalysisModel.ContractCompanyTradeDataList)
+					sumDateData[dv.Date].Date = dv.Date
+				}
+				// 数据类型以第一个非零值为准, 只处理多空和净多, 变化就不管了
+				if sumDateData[dv.Date].BuyValType == tradeAnalysisModel.TradeDataTypeNull && dv.BuyValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].BuyValType = dv.BuyValType
+				}
+				if sumDateData[dv.Date].BuyValType == tradeAnalysisModel.TradeDataTypeOrigin && dv.BuyValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					sumDateData[dv.Date].BuyValType = dv.BuyValType
+				}
+				if dv.BuyValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].BuyVal += dv.BuyVal
+				}
+				// 空单
+				if sumDateData[dv.Date].SoldValType == tradeAnalysisModel.TradeDataTypeNull && dv.SoldValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].SoldValType = dv.SoldValType
+				}
+				if sumDateData[dv.Date].SoldValType == tradeAnalysisModel.TradeDataTypeOrigin && dv.SoldValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					sumDateData[dv.Date].SoldValType = dv.SoldValType
+				}
+				if dv.SoldValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].SoldVal += dv.SoldVal
+				}
+				// 净多单
+				if sumDateData[dv.Date].PureBuyValType == tradeAnalysisModel.TradeDataTypeNull && dv.PureBuyValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].PureBuyValType = dv.PureBuyValType
+				}
+				if sumDateData[dv.Date].PureBuyValType == tradeAnalysisModel.TradeDataTypeOrigin && dv.PureBuyValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					sumDateData[dv.Date].PureBuyValType = dv.PureBuyValType
+				}
+				if dv.PureBuyValType > tradeAnalysisModel.TradeDataTypeNull {
+					sumDateData[dv.Date].PureBuyVal += dv.PureBuyVal
+				}
+			}
+
+			// 多个合约比对开始结束时间
+			if companyData.StartDate.IsZero() {
+				companyData.StartDate = vv.StartDate
+			}
+			if vv.StartDate.Before(companyData.StartDate) {
+				companyData.StartDate = vv.StartDate
+			}
+			if companyData.EndDate.IsZero() {
+				companyData.EndDate = vv.EndDate
+			}
+			if vv.EndDate.Before(companyData.EndDate) {
+				companyData.EndDate = vv.EndDate
+			}
+		}
+		for _, sv := range sumDateData {
+			companyData.DataList = append(companyData.DataList, sv)
+		}
+		sort.Slice(companyData.DataList, func(i, j int) bool {
+			return companyData.DataList[i].Date.Before(companyData.DataList[j].Date)
+		})
+		companyData.ClassifyType = strings.Join(contractArr, ",")
+		companyTradeData = append(companyTradeData, companyData)
+	}
+	return
+}
+
+// PredictingTradeData 根据数据库中的多空数据填充预估数据
+func PredictingTradeData(originData []*tradeAnalysisModel.ContractCompanyTradeDataList, lastBuyDateVal, lastSoldDateVal map[time.Time]int, predictRatio float64) (newData []*tradeAnalysisModel.ContractCompanyTradeDataList, firstDate, endDate time.Time, err error) {
+	if len(originData) == 0 {
+		return
+	}
+	if predictRatio < 0 || predictRatio > 1 {
+		err = fmt.Errorf("估计参数不在0-1之间")
+		return
+	}
+	sort.Slice(originData, func(i, j int) bool {
+		return originData[i].Date.Before(originData[j].Date)
+	})
+	dateVal := make(map[time.Time]*tradeAnalysisModel.ContractCompanyTradeDataList)
+	for _, v := range originData {
+		dateVal[v.Date] = v
+	}
+
+	// 生成开始日期-1d(可能会往前面推算一天)至结束日期间的交易日, 以交易日为时间序列遍历
+	tradeDays := utils.GetTradingDays(originData[0].Date.AddDate(0, 0, -1), originData[len(originData)-1].Date)
+	for k, v := range tradeDays {
+		// T日多空均无的情况
+		//bothLast := false
+		if dateVal[v] == nil {
+			// T-1和T+1[原始数据]均无值, 那么T日无数据
+			hasPrev, hasNext := false, false
+			if k-1 >= 0 {
+				hasPrev = true
+			}
+			if k+1 <= len(tradeDays)-1 {
+				hasNext = true
+			}
+			if !hasPrev && !hasNext {
+				continue
+			}
+
+			// T+1有值, 优先从T+1推, 然后继续走下面计算净多单的逻辑
+			if hasNext {
+				nextDay := tradeDays[k+1]
+				if dateVal[nextDay] != nil {
+					// T+1有多/空及多空变化, 且是原始数据, 那么推出数据并在map中新加一日数据
+					if dateVal[nextDay].BuyValType == tradeAnalysisModel.TradeDataTypeOrigin && dateVal[nextDay].BuyChangeType == tradeAnalysisModel.TradeDataTypeOrigin {
+						if _, ok := dateVal[v]; !ok {
+							dateVal[v] = new(tradeAnalysisModel.ContractCompanyTradeDataList)
+							dateVal[v].Date = v
+						}
+						dateVal[v].BuyVal = dateVal[nextDay].BuyVal - dateVal[nextDay].BuyChange
+						dateVal[v].BuyValType = tradeAnalysisModel.TradeDataTypeOrigin
+					}
+					if dateVal[nextDay].SoldValType == tradeAnalysisModel.TradeDataTypeOrigin && dateVal[nextDay].SoldChangeType == tradeAnalysisModel.TradeDataTypeOrigin {
+						if _, ok := dateVal[v]; !ok {
+							dateVal[v] = new(tradeAnalysisModel.ContractCompanyTradeDataList)
+							dateVal[v].Date = v
+						}
+						dateVal[v].SoldVal = dateVal[nextDay].SoldVal - dateVal[nextDay].SoldChange
+						dateVal[v].SoldValType = tradeAnalysisModel.TradeDataTypeOrigin
+					}
+				}
+			}
+
+			// T+1没推出来而T-1有值, 那么T多空均取末位, 计算净多单
+			_, has := dateVal[v]
+			if hasPrev && !has {
+				sv, sok := lastSoldDateVal[v]
+				bv, bok := lastBuyDateVal[v]
+				if !sok && !bok {
+					continue
+				}
+				dateVal[v] = new(tradeAnalysisModel.ContractCompanyTradeDataList)
+				dateVal[v].Date = v
+				if sok {
+					dateVal[v].SoldVal = int(predictRatio*float64(sv) + 0.5)
+					dateVal[v].SoldValType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+				if bok {
+					dateVal[v].BuyVal = int(predictRatio*float64(bv) + 0.5)
+					dateVal[v].BuyValType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+				if dateVal[v].BuyValType > tradeAnalysisModel.TradeDataTypeNull && dateVal[v].SoldValType > tradeAnalysisModel.TradeDataTypeNull {
+					dateVal[v].PureBuyVal = dateVal[v].BuyVal - dateVal[v].SoldVal
+					dateVal[v].PureBuyValType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+				continue
+			}
+		}
+
+		// 多空均有的情况下计算净多单
+		if dateVal[v].BuyValType == tradeAnalysisModel.TradeDataTypeOrigin && dateVal[v].SoldValType == tradeAnalysisModel.TradeDataTypeOrigin {
+			dateVal[v].PureBuyVal = dateVal[v].BuyVal - dateVal[v].SoldVal
+			dateVal[v].PureBuyValType = tradeAnalysisModel.TradeDataTypeOrigin // 原始值算出来的也作原始值
+		}
+
+		// 仅有多单, 空单取末位, 计算净多单
+		if dateVal[v].BuyValType == tradeAnalysisModel.TradeDataTypeOrigin && dateVal[v].SoldValType == tradeAnalysisModel.TradeDataTypeNull {
+			if sv, ok := lastSoldDateVal[v]; ok {
+				dateVal[v].SoldVal = int(predictRatio*float64(sv) + 0.5) // 估计参数*末位值, 向上取整
+				dateVal[v].SoldValType = tradeAnalysisModel.TradeDataTypeCalculate
+				dateVal[v].PureBuyVal = dateVal[v].BuyVal - dateVal[v].SoldVal
+				dateVal[v].PureBuyValType = tradeAnalysisModel.TradeDataTypeCalculate
+			}
+		}
+
+		// 仅有空单, 多单取末位, 计算净多单
+		if dateVal[v].SoldValType == tradeAnalysisModel.TradeDataTypeOrigin && dateVal[v].BuyValType == tradeAnalysisModel.TradeDataTypeNull {
+			if sv, ok := lastBuyDateVal[v]; ok {
+				dateVal[v].BuyVal = int(predictRatio*float64(sv) + 0.5)
+				dateVal[v].BuyValType = tradeAnalysisModel.TradeDataTypeCalculate
+				dateVal[v].PureBuyVal = dateVal[v].BuyVal - dateVal[v].SoldVal
+				dateVal[v].PureBuyValType = tradeAnalysisModel.TradeDataTypeCalculate
+			}
+		}
+	}
+
+	// 二次遍历, 计算与T-1的变化值
+	for k, v := range tradeDays {
+		// 无T/T-1数据, 忽略
+		if dateVal[v] == nil {
+			continue
+		}
+		if k-1 < 0 {
+			continue
+		}
+		beforeDay := tradeDays[k-1]
+		if dateVal[beforeDay] == nil {
+			continue
+		}
+
+		// 多单变化
+		if dateVal[v].BuyChangeType == tradeAnalysisModel.TradeDataTypeNull {
+			if dateVal[v].BuyValType > tradeAnalysisModel.TradeDataTypeNull && dateVal[beforeDay].BuyValType > tradeAnalysisModel.TradeDataTypeNull {
+				dateVal[v].BuyChange = dateVal[v].BuyVal - dateVal[beforeDay].BuyVal
+				// 如果当日多单或者前日多单是估计值, 那么多单变化也为估计值
+				if dateVal[v].BuyValType == tradeAnalysisModel.TradeDataTypeCalculate || dateVal[beforeDay].BuyValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					dateVal[v].BuyChangeType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+			}
+		}
+
+		// 空单变化
+		if dateVal[v].SoldChangeType == tradeAnalysisModel.TradeDataTypeNull {
+			if dateVal[v].SoldValType > tradeAnalysisModel.TradeDataTypeNull && dateVal[beforeDay].SoldValType > tradeAnalysisModel.TradeDataTypeNull {
+				dateVal[v].SoldChange = dateVal[v].SoldVal - dateVal[beforeDay].SoldVal
+				// 如果当日空单或者前日空单是估计值, 那么空单变化也为估计值
+				if dateVal[v].SoldValType == tradeAnalysisModel.TradeDataTypeCalculate || dateVal[beforeDay].SoldValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					dateVal[v].SoldChangeType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+			}
+		}
+
+		// 净多变化
+		if dateVal[v].PureBuyChangeType == tradeAnalysisModel.TradeDataTypeNull {
+			if dateVal[v].PureBuyValType > tradeAnalysisModel.TradeDataTypeNull && dateVal[beforeDay].PureBuyValType > tradeAnalysisModel.TradeDataTypeNull {
+				dateVal[v].PureBuyChange = dateVal[v].PureBuyVal - dateVal[beforeDay].PureBuyVal
+				dateVal[v].PureBuyChangeType = tradeAnalysisModel.TradeDataTypeOrigin
+				// 如果当日净多单或者前日净多单是估计值, 那么净多单变化也为估计值
+				if dateVal[v].PureBuyValType == tradeAnalysisModel.TradeDataTypeCalculate || dateVal[beforeDay].PureBuyValType == tradeAnalysisModel.TradeDataTypeCalculate {
+					dateVal[v].PureBuyChangeType = tradeAnalysisModel.TradeDataTypeCalculate
+				}
+			}
+		}
+	}
+
+	// 重新遍历map, 生成数据序列并排序
+	newData = make([]*tradeAnalysisModel.ContractCompanyTradeDataList, 0)
+	for _, v := range dateVal {
+		if v.BuyValType == tradeAnalysisModel.TradeDataTypeNull && v.SoldValType == tradeAnalysisModel.TradeDataTypeNull {
+			continue
+		}
+		newData = append(newData, v)
+	}
+	sort.Slice(newData, func(i, j int) bool {
+		return newData[i].Date.Before(newData[j].Date)
+	})
+	if len(newData) > 0 {
+		firstDate = newData[0].Date
+		endDate = newData[len(newData)-1].Date
+	}
+	return
+}

+ 181 - 0
services/trade_analysis/trade_analysis_interface.go

@@ -0,0 +1,181 @@
+package trade_analysis
+
+import (
+	tradeAnalysisModel "eta/eta_index_lib/models/trade_analysis"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// TradeAnalysisInterface 持仓分析查询接口
+type TradeAnalysisInterface interface {
+	GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, queryCompanies []string) (items []*tradeAnalysisModel.OriginTradeData, err error) // 根据品种和公司获取原始数据
+	GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*tradeAnalysisModel.OriginTradeData, err error)                       // 获取品种末位数据
+}
+
+// BaseTradeAnalysis 通用交易所
+type BaseTradeAnalysis struct{}
+
+func (b *BaseTradeAnalysis) GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, queryCompanies []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	return tradeAnalysisModel.GetTradeDataByClassifyAndCompany(exchange, classifyName, contracts, queryCompanies)
+}
+
+func (b *BaseTradeAnalysis) GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	return tradeAnalysisModel.GetLastTradeDataByClassify(exchange, classifyName, contracts)
+}
+
+// ZhengzhouTradeAnalysis 郑商所
+type ZhengzhouTradeAnalysis struct{}
+
+func (z *ZhengzhouTradeAnalysis) GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, queryCompanies []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	return tradeAnalysisModel.GetTradeZhengzhouDataByClassifyAndCompany(exchange, contracts, queryCompanies)
+}
+
+func (z *ZhengzhouTradeAnalysis) GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	return tradeAnalysisModel.GetLastTradeZhengzhouDataByClassify(exchange, contracts)
+}
+
+// GuangzhouTradeAnalysis 广期所
+type GuangzhouTradeAnalysis struct{}
+
+func (g *GuangzhouTradeAnalysis) GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, queryCompanies []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	classifyIdMap := map[string]int{"si": 7, "lc": 8}
+	classifyId := classifyIdMap[classifyName]
+	if classifyId == 0 {
+		err = fmt.Errorf("品种有误")
+		return
+	}
+
+	// TOP20
+	seatNameArr := []string{tradeAnalysisModel.GuangZhouSeatNameBuy, tradeAnalysisModel.GuangZhouSeatNameSold}
+	if utils.InArrayByStr(queryCompanies, tradeAnalysisModel.TradeFuturesCompanyTop20) {
+		seatNameArr = append(seatNameArr, tradeAnalysisModel.GuangZhouTopSeatNameBuy, tradeAnalysisModel.GuangZhouTopSeatNameSold)
+	}
+
+	// 查询品种下所有指标
+	indexes, e := tradeAnalysisModel.GetBaseFromTradeGuangzhouIndexByClassifyId(classifyId)
+	if e != nil {
+		err = fmt.Errorf("获取广期所指标失败, %v", e)
+		return
+	}
+	var indexIds []int
+	indexInfo := make(map[int]*tradeAnalysisModel.OriginTradeData)
+	for _, v := range indexes {
+		// eg.永安期货_si2401_持买单量
+		nameArr := strings.Split(v.IndexName, "_")
+		if len(nameArr) != 3 {
+			continue
+		}
+		companyName := nameArr[0]
+		if nameArr[0] == tradeAnalysisModel.GuangZhouTopCompanyAliasName {
+			companyName = tradeAnalysisModel.TradeFuturesCompanyTop20
+		}
+		if !utils.InArrayByStr(seatNameArr, nameArr[2]) {
+			continue
+		}
+		if !utils.InArrayByStr(queryCompanies, companyName) {
+			continue
+		}
+		if !utils.InArrayByStr(contracts, nameArr[1]) {
+			continue
+		}
+		indexIds = append(indexIds, v.BaseFromTradeGuangzhouIndexId)
+		if indexInfo[v.BaseFromTradeGuangzhouIndexId] == nil {
+			if tradeAnalysisModel.GuangzhouSeatNameValType[nameArr[2]] == 0 {
+				continue
+			}
+			indexInfo[v.BaseFromTradeGuangzhouIndexId] = new(tradeAnalysisModel.OriginTradeData)
+			indexInfo[v.BaseFromTradeGuangzhouIndexId].CompanyName = companyName
+			indexInfo[v.BaseFromTradeGuangzhouIndexId].ClassifyName = classifyName
+			indexInfo[v.BaseFromTradeGuangzhouIndexId].ClassifyType = nameArr[1]
+			indexInfo[v.BaseFromTradeGuangzhouIndexId].ValType = tradeAnalysisModel.GuangzhouSeatNameValType[nameArr[2]]
+		}
+	}
+	if len(indexIds) == 0 {
+		return
+	}
+
+	// 查询指标数据
+	indexesData, e := tradeAnalysisModel.GetBaseFromTradeGuangzhouDataByIndexIds(indexIds)
+	if e != nil {
+		err = fmt.Errorf("获取广期所指标数据失败, %v", e)
+		return
+	}
+	items = make([]*tradeAnalysisModel.OriginTradeData, 0)
+	for _, v := range indexesData {
+		info, ok := indexInfo[v.BaseFromTradeGuangzhouIndexId]
+		if !ok {
+			continue
+		}
+		items = append(items, &tradeAnalysisModel.OriginTradeData{
+			CompanyName:  info.CompanyName,
+			Val:          int(v.Value),
+			ValChange:    int(v.QtySub),
+			DataTime:     v.DataTime,
+			ClassifyName: info.ClassifyName,
+			ClassifyType: info.ClassifyType,
+			ValType:      info.ValType,
+		})
+	}
+	return
+}
+
+func (g *GuangzhouTradeAnalysis) GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*tradeAnalysisModel.OriginTradeData, err error) {
+	classifyIdMap := map[string]int{"si": 7, "lc": 8}
+	classifyId := classifyIdMap[classifyName]
+	if classifyId == 0 {
+		err = fmt.Errorf("品种有误")
+		return
+	}
+	seatNameArr := []string{tradeAnalysisModel.GuangZhouSeatNameBuy, tradeAnalysisModel.GuangZhouSeatNameSold}
+
+	// 查询品种下所有指标
+	indexes, e := tradeAnalysisModel.GetBaseFromTradeGuangzhouIndexByClassifyId(classifyId)
+	if e != nil {
+		err = fmt.Errorf("获取广期所指标失败, %v", e)
+		return
+	}
+
+	// 获取各合约下的指标
+	contractIndexIds := make(map[string][]int)
+	for _, v := range indexes {
+		// eg.永安期货_si2401_持买单量
+		nameArr := strings.Split(v.IndexName, "_")
+		if len(nameArr) != 3 {
+			continue
+		}
+		if !utils.InArrayByStr(contracts, nameArr[1]) {
+			continue
+		}
+		if !utils.InArrayByStr(seatNameArr, nameArr[2]) {
+			continue
+		}
+		if tradeAnalysisModel.GuangzhouSeatNameValType[nameArr[2]] == 0 {
+			continue
+		}
+		k := fmt.Sprintf("%s-%d", nameArr[1], tradeAnalysisModel.GuangzhouSeatNameValType[nameArr[2]])
+		contractIndexIds[k] = append(contractIndexIds[k], v.BaseFromTradeGuangzhouIndexId)
+	}
+
+	// ps.如果后面如果有空可以优化一下这里, 把末位数据每天写进一张表里面
+	for k, v := range contractIndexIds {
+		keyArr := strings.Split(k, "-")
+		contract := keyArr[0]
+		valType, _ := strconv.Atoi(keyArr[1])
+		lastVales, e := tradeAnalysisModel.GetBaseFromTradeGuangzhouMinDataByIndexIds(v)
+		if e != nil {
+			err = fmt.Errorf("获取合约末位数据失败, %v", e)
+			return
+		}
+		for _, vv := range lastVales {
+			items = append(items, &tradeAnalysisModel.OriginTradeData{
+				Val:          int(vv.Value),
+				DataTime:     vv.DataTime,
+				ClassifyType: contract,
+				ValType:      valType,
+			})
+		}
+	}
+	return
+}

+ 156 - 0
utils/common.go

@@ -1326,6 +1326,151 @@ func FormatFloatPlaces(val float64, places int32) (newVal float64, err error) {
 	return
 }
 
+// handleSystemAppointDateT
+// @Description: 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+// @author: Roc
+// @datetime2023-10-27 09:31:35
+// @param Frequency string
+// @param Day string
+// @return date string
+// @return err error
+// @return errMsg string
+func HandleSystemAppointDateT(currDate time.Time, appointDay, frequency string) (date string, err error, errMsg string) {
+	//currDate := time.Now()
+	switch frequency {
+	case "本周":
+		day := int(currDate.Weekday())
+		if day == 0 { // 周日
+			day = 7
+		}
+		num := 0
+		switch appointDay {
+		case "周一":
+			num = 1
+		case "周二":
+			num = 2
+		case "周三":
+			num = 3
+		case "周四":
+			num = 4
+		case "周五":
+			num = 5
+		case "周六":
+			num = 6
+		case "周日":
+			num = 7
+		}
+		day = num - day
+		date = currDate.AddDate(0, 0, day).Format(FormatDate)
+	case "本旬":
+		day := currDate.Day()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 11, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 21, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本月":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本季":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 4, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 10, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本半年":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(FormatDate)
+	case "本年":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+		}
+		date = tmpDate.Format(FormatDate)
+	default:
+		errMsg = "错误的日期频度:" + frequency
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+func CompareFloatByOpStrings(op string, a, b float64) bool {
+	switch op {
+	case "=":
+		return a == b
+	case ">":
+		return a > b
+	case ">=":
+		return a >= b
+	case "<=":
+		return a <= b
+	case "<":
+		return a < b
+	}
+	return false
+}
+
 // IsDivideZero
 // @Description: 判断是否分母为0的bug
 // @author: Roc
@@ -1344,3 +1489,14 @@ func IsDivideZero(err error) bool {
 	}
 	return false
 }
+
+// GetTradingDays 获取开始时间至结束时间之间的交易日期(日度)
+func GetTradingDays(startDate, endDate time.Time) []time.Time {
+	var tradingDays []time.Time
+	for curr := startDate; !curr.After(endDate); curr = curr.AddDate(0, 0, 1) {
+		if curr.Weekday() >= time.Monday && curr.Weekday() <= time.Friday {
+			tradingDays = append(tradingDays, curr)
+		}
+	}
+	return tradingDays
+}

+ 12 - 3
utils/constants.go

@@ -109,8 +109,13 @@ const (
 	DATA_SOURCE_BUSINESS                             = 84 // 来源于自有数据
 	DATA_SOURCE_SCI99                                = 85 // 卓创资讯
 	DATA_SOURCE_CCF                                  = 86 // CCF化纤信息
+	DATA_SOURCE_CALCULATE_RANGEANLYSIS               = 87 //区间计算->87
 	DATA_SOURCE_SCI_HQ                               = 88 // 卓创红期->88
 	DATA_SOURCE_OILCHEM                              = 89 // 隆众资讯 -> 89
+	DATA_SOURCE_PREDICT_CALCULATE_RANGEANLYSIS       = 90 // 预测指标区间计算->90
+	DATA_SOURCE_LY                                   = 91 // 粮油商务网
+	DATA_SOURCE_TRADE_ANALYSIS                       = 92 // 持仓分析
+	DATA_SOURCE_HISUGAR                              = 93 // 泛糖科技 -> 93
 )
 
 // 指标来源的中文展示
@@ -196,9 +201,13 @@ const (
 	DATA_SOURCE_NAME_CALCULATE_SUM                        = `多指标求和`
 	DATA_SOURCE_NAME_CALCULATE_AVG                        = `多指标求平均`
 	DATA_SOURCE_NAME_BUSINESS                             = `自有数据`
-	DATA_SOURCE_NAME_CCF                                  = `CCF`  // CCF化纤信息
-	DATA_SOURCE_NAME_SCI_HQ                               = `卓创红期` // 卓创红期
-	DATA_SOURCE_NAME_OILCHEM                              = `隆众资讯` // 隆众资讯 -> 89
+	DATA_SOURCE_NAME_CALCULATE_RANGEANLYSIS               = `区间计算`   //区间计算->87
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_RANGEANLYSIS       = `预测区间计算` //区间计算->90
+	DATA_SOURCE_NAME_TRADE_ANALYSIS                       = `持仓分析`   // 持仓分析
+	DATA_SOURCE_NAME_CCF                                  = `CCF`    // CCF化纤信息
+	DATA_SOURCE_NAME_SCI_HQ                               = `卓创红期`   // 卓创红期
+	DATA_SOURCE_NAME_OILCHEM                              = `隆众资讯`   // 隆众资讯 -> 89
+	DATA_SOURCE_NAME_HISUGAR                              = `泛糖科技`   // 泛糖科技 -> 93
 )
 
 // 基础数据初始化日期