瀏覽代碼

Merge branch 'debug' of http://8.136.199.33:3000/eta_server/eta_index_lib into bzq1/stl_cf

zqbao 5 月之前
父節點
當前提交
f0342ef361
共有 55 個文件被更改,包括 3731 次插入389 次删除
  1. 33 17
      controllers/base_from_calculate.go
  2. 8 8
      controllers/base_from_mysteel_chemical.go
  3. 18 10
      controllers/base_from_predict_calculate.go
  4. 634 0
      controllers/base_from_rzd.go
  5. 2 0
      controllers/base_from_trade_analysis.go
  6. 174 0
      controllers/base_from_usda_fas.go
  7. 1 0
      controllers/base_from_wind.go
  8. 0 1
      controllers/base_from_wind_wsd.go
  9. 10 40
      controllers/future_good/future_good_edb_info.go
  10. 2 0
      go.sum
  11. 7 7
      logic/predict_edb.go
  12. 200 117
      logic/profit_chart_info.go
  13. 31 11
      models/base_from_adjust.go
  14. 12 7
      models/base_from_calculate.go
  15. 2 2
      models/base_from_mysteel_chemical.go
  16. 42 0
      models/base_from_rzd_classify.go
  17. 61 0
      models/base_from_rzd_data.go
  18. 54 0
      models/base_from_rzd_index.go
  19. 10 0
      models/base_from_ths.go
  20. 349 0
      models/base_from_usda_fas.go
  21. 9 0
      models/base_predict_from_calculate.go
  22. 7 4
      models/db.go
  23. 24 1
      models/edb_data_calculate_hbz.go
  24. 22 0
      models/edb_data_calculate_ljzzj.go
  25. 22 0
      models/edb_data_calculate_ljzzy.go
  26. 10 6
      models/edb_data_calculate_percentile.go
  27. 404 0
      models/edb_data_calculate_phase_shift.go
  28. 1 0
      models/edb_data_calculate_qjjs.go
  29. 22 0
      models/edb_data_calculate_rjz.go
  30. 21 0
      models/edb_data_calculate_tcz.go
  31. 223 0
      models/edb_data_rzd.go
  32. 16 17
      models/edb_info.go
  33. 1 1
      models/edb_info_record.go
  34. 3 3
      models/edb_info_relation.go
  35. 21 21
      models/edb_ths_hf.go
  36. 16 1
      models/future_good/future_good_edb_data.go
  37. 21 1
      models/predict_edb_data_calculate_hbz.go
  38. 23 1
      models/predict_edb_data_calculate_ljzzj.go
  39. 21 0
      models/predict_edb_data_calculate_ljzzy.go
  40. 325 0
      models/predict_edb_data_calculate_phase_shift.go
  41. 20 0
      models/predict_edb_data_calculate_tcz.go
  42. 90 12
      models/trade_analysis/trade_analysis.go
  43. 144 0
      routers/commentsRouter.go
  44. 42 12
      routers/router.go
  45. 2 2
      services/base_from_calculate.go
  46. 4 4
      services/base_from_mysteel_chemical.go
  47. 5 4
      services/base_from_python.go
  48. 309 0
      services/base_from_usda_fas.go
  49. 2 2
      services/edb_info_record.go
  50. 12 12
      services/edb_info_stat.go
  51. 3 3
      services/trade_analysis/trade_analysis_data.go
  52. 9 42
      static/pcsg_task.json
  53. 202 0
      utils/common.go
  54. 1 1
      utils/config.go
  55. 24 19
      utils/constants.go

+ 33 - 17
controllers/base_from_calculate.go

@@ -352,7 +352,7 @@ func (this *CalculateController) Edit() {
 	newEdbInfoEditRecord.Unit = req.Unit
 	newEdbInfoEditRecord.OperateUserId = req.AdminId
 	newEdbInfoEditRecord.OperateUserRealName = req.AdminName
-	err = services.AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoEditRecord)
+	err = services.AddEditEdbInfoRecord(oldEdbInfo, newEdbInfoEditRecord)
 	if err != nil {
 		br.Msg = "记录基础信息变更日志失败"
 		br.ErrMsg = err.Error()
@@ -749,6 +749,9 @@ func (this *CalculateController) BatchSave() {
 		}
 		sourName = "日均值"
 		edbInfo, err = models.AddCalculateRjz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT:
+		sourName = "期数移位"
+		edbInfo, err = models.AddCalculatePhaseShift(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
 	default:
 		// 获取通用的数据源处理服务
 		baseEdbInfoModel = models.GetBaseEdbInfoModel(req.Source)
@@ -813,7 +816,7 @@ func (this *CalculateController) BatchSave() {
 	newEdbInfo.Unit = req.Unit
 	newEdbInfo.OperateUserId = req.AdminId
 	newEdbInfo.OperateUserRealName = req.AdminName
-	err = services.AddEditEdbInfoRcord(edbInfo, newEdbInfo)
+	err = services.AddEditEdbInfoRecord(edbInfo, newEdbInfo)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存失败,Err:" + err.Error()
@@ -1073,6 +1076,9 @@ func (this *CalculateController) BatchEdit() {
 	case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
 		sourName = "时间移位"
 		err = models.EditCalculateTimeShift(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT:
+		sourName = "期数移位"
+		err = models.EditCalculatePhaseShift(edbInfo, &req, fromEdbInfo)
 	case utils.DATA_SOURCE_CALCULATE_ZJPJ:
 		sourName = "直接拼接"
 
@@ -1266,7 +1272,7 @@ func (this *CalculateController) BatchEdit() {
 	newEdbInfoRecord.Unit = req.Unit
 	newEdbInfoRecord.OperateUserId = req.AdminId
 	newEdbInfoRecord.OperateUserRealName = req.AdminName
-	err = services.AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoRecord)
+	err = services.AddEditEdbInfoRecord(oldEdbInfo, newEdbInfoRecord)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "记录指标操作记录失败,Err:" + err.Error()
@@ -1531,6 +1537,25 @@ func (this *CalculateController) Refresh() {
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			errMsg = "RefreshAllCalculateTimeShift Err:" + err.Error()
 		}
+	case utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT: // 期数位移
+		calculate, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateMappingDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculate.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		//startDate = edbInfo.StartDate
+		startDate = `` //只要填写日期,就会出现问题,还是把日期给去掉吧
+		endDate = time.Now().Format(utils.FormatDate)
+		formulaInt, _ := strconv.Atoi(calculate.CalculateFormula)
+		err = models.RefreshAllCalculatePhaseShift(edbInfoId, source, subSource, formulaInt, calculate.MoveType, fromEdbInfo, calculate.EdbCode, startDate, endDate, calculate.MoveFrequency)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllCalculatePhaseShift Err:" + err.Error()
+		}
 	case utils.DATA_SOURCE_CALCULATE_ZJPJ: //刷新直接拼接
 		err = models.RefreshAllCalculateZjpj(edbInfo)
 		if err != nil && err.Error() != utils.ErrNoRow() {
@@ -1783,30 +1808,21 @@ func (this *CalculateController) SaveAdjust() {
 		}()
 	}
 
-	//获取指标数据
-	var condition string
-	var pars []interface{}
-	condition += " AND edb_name=? "
-	pars = append(pars, req.EdbName)
-
-	if req.EdbInfoId > 0 {
-		condition += " AND edb_info_id != ? "
-		pars = append(pars, req.EdbInfoId)
-	}
-
-	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	// 校验指标名称是否存在
+	existEdbName, err := logic.CheckExistByEdbNameAndEdbInfoId(0, req.EdbInfoId, req.EdbName, this.Lang)
 	if err != nil {
 		br.Msg = "判断指标名称是否存在失败"
 		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
 		return
 	}
-	if count > 0 {
+
+	if existEdbName {
 		br.Msg = "指标名称已存在,请重新填写"
 		br.ErrMsg = "指标名称已存在,请重新填写"
 		return
 	}
 
-	edbInfo, err, errMsg := models.SaveAdjustEdb(req)
+	edbInfo, err, errMsg := models.SaveAdjustEdb(req, this.Lang)
 	if err != nil {
 		br.Msg = errMsg
 		br.Msg = "添加指标失败,Err:" + err.Error()

+ 8 - 8
controllers/base_from_mysteel_chemical.go

@@ -11,14 +11,14 @@ import (
 	"time"
 )
 
-// MySteelChemicalController 钢联化工
+// MySteelChemicalController 上海钢联
 type MySteelChemicalController struct {
 	BaseAuthController
 }
 
 // Add
-// @Title 新增钢联化工指标接口
-// @Description 新增钢联化工指标接口
+// @Title 新增上海钢联指标接口
+// @Description 新增上海钢联指标接口
 // @Success 200 {object} models.AddEdbInfoReq
 // @router /add [post]
 func (this *MySteelChemicalController) Add() {
@@ -62,8 +62,8 @@ func (this *MySteelChemicalController) Add() {
 }
 
 // Refresh
-// @Title 刷新钢联化工指标接口
-// @Description 刷新钢联化工指标接口
+// @Title 刷新上海钢联指标接口
+// @Description 刷新上海钢联指标接口
 // @Success 200 {object} models.RefreshEdbInfoReq
 // @router /refresh [post]
 func (this *MySteelChemicalController) Refresh() {
@@ -183,8 +183,8 @@ func (this *MySteelChemicalController) QueryRefresh() {
 	br.Msg = "获取成功"
 }
 
-// @Title 检查钢联化工的api是否可用
-// @Description 检查钢联化工的api是否可用
+// @Title 检查上海钢联的api是否可用
+// @Description 检查上海钢联的api是否可用
 // @Success 200 {object} models.HandleMysteelIndexResp
 // @router /handle/mysteel/api/check [post]
 func (this *MySteelChemicalController) ApiHealthCheck() {
@@ -198,7 +198,7 @@ func (this *MySteelChemicalController) ApiHealthCheck() {
 	if err != nil {
 		br.Msg = "处理失败"
 		br.ErrMsg = "处理失败,Err:" + err.Error()
-		utils.FileLog.Info("钢联化工api接口不可用,Err:", err.Error())
+		utils.FileLog.Info("上海钢联api接口不可用,Err:", err.Error())
 		return
 	}
 

+ 18 - 10
controllers/base_from_predict_calculate.go

@@ -127,7 +127,7 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 	}
 
 	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
-	existEdbName, err := logic.CheckExistByEdbNameAndEdbInfoId(1, 0, req.EdbName, lang)
+	existEdbName, err := logic.CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, 0, req.EdbName, lang)
 	if err != nil {
 		br.Msg = "判断指标名称是否存在失败"
 		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
@@ -526,7 +526,7 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 	newEdbInfoEditRecord.Unit = req.Unit
 	newEdbInfoEditRecord.OperateUserId = req.AdminId
 	newEdbInfoEditRecord.OperateUserRealName = req.AdminName
-	err = services.AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoEditRecord)
+	err = services.AddEditEdbInfoRecord(oldEdbInfo, newEdbInfoEditRecord)
 	if err != nil {
 		br.Msg = "记录基础信息变更日志失败"
 		br.ErrMsg = err.Error()
@@ -610,7 +610,7 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 	//加入缓存机制,避免创建同一个名称的指标 end
 
 	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
-	existEdbName, err := logic.CheckExistByEdbNameAndEdbInfoId(1, req.EdbInfoId, req.EdbName, this.Lang)
+	existEdbName, err := logic.CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, req.EdbInfoId, req.EdbName, this.Lang)
 	if err != nil {
 		br.Msg = "判断指标名称是否存在失败"
 		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
@@ -714,6 +714,17 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		return
 	}
 
+	// 记录原始指标信息
+	var oldEdbInfo *models.EdbInfo
+	if req.EdbInfoId > 0 {
+		oldEdbInfo, err = models.GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			br.Msg = "获取指标信息失败"
+			br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+			return
+		}
+	}
+
 	// 来源预测指标信息
 	var fromEdbInfo *models.EdbInfo
 	if fromEdbInfoId > 0 {
@@ -802,6 +813,9 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT {
 		sourName = "预测时间移位"
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateTimeShift(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, this.Lang)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_PHASE_SHIFT {
+		sourName = "预测期数移位"
+		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculatePhaseShift(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, this.Lang)
 	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_CJJX {
 		sourName = "预测超季节性"
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateCjjx(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt, this.Lang)
@@ -903,12 +917,6 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 	}
 
 	if req.EdbInfoId > 0 {
-		oldEdbInfo, err := models.GetEdbInfoById(req.EdbInfoId)
-		if err != nil {
-			br.Msg = "获取指标信息失败"
-			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
-			return
-		}
 		// 记录操作变更记录
 		newEdbInfo := new(models.EdbInfoEditRecord)
 		newEdbInfo.EdbName = req.EdbName
@@ -916,7 +924,7 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		newEdbInfo.Unit = req.Unit
 		newEdbInfo.OperateUserId = req.AdminId
 		newEdbInfo.OperateUserRealName = req.AdminName
-		err = services.AddEditEdbInfoRcord(oldEdbInfo, newEdbInfo)
+		err = services.AddEditEdbInfoRecord(oldEdbInfo, newEdbInfo)
 		if err != nil {
 			br.Msg = "保存失败"
 			br.ErrMsg = "保存失败,Err:" + err.Error()

+ 634 - 0
controllers/base_from_rzd.go

@@ -0,0 +1,634 @@
+// @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 BaseFromRzdController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增睿姿得指标
+// @Description 新增粮油商务网指标
+// @router /add [post]
+func (this *BaseFromRzdController) 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_RZD
+	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.AddEdbDataFromRzd(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 *BaseFromRzdController) 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 = "获取成功"
+}
+
+// AddRzdClassify
+// @Title 新增分类
+// @Description 获取分类
+// @Success 200 {object} models.BaseFromRzdClassify
+// @router /add/rzd/classify [post]
+func (this *BaseFromRzdController) AddRzdClassify() {
+	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 {
+		ClassifyName string `json:"ClassifyName"`
+		ParentId     int    `json:"parentId"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+	categoryName := reqData.ClassifyName
+	if categoryName == "" {
+		br.Msg = "请输入分类!"
+		br.ErrMsg = "请输入分类"
+		return
+	}
+	rzdClassify := models.BaseFromRzdClassify{
+		ClassifyName: categoryName,
+		ParentId:     reqData.ParentId,
+		CreateTime:   utils.GetCurrentTime(),
+		ModifyTime:   utils.GetCurrentTime(),
+	}
+
+	lyClassify, err := models.AddRzdClassify(&rzdClassify)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyClassify
+	br.Msg = "获取成功"
+}
+
+// GetRzdClassifyByName
+// @Title 获取分类
+// @Description 获取分类
+// @Success 200 {object} models.BaseFromRzdClassify
+// @router /get/rzd/classify/by/name [post]
+func (this *BaseFromRzdController) GetRzdClassifyByName() {
+	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 {
+		ClassifyName string `json:"ClassifyName"`
+	}
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &reqData)
+	if err != nil {
+		br.ErrMsg = "无法解析请求体"
+		return
+	}
+	categoryName := reqData.ClassifyName
+	if categoryName == "" {
+		br.Msg = "请输入分类!"
+		br.ErrMsg = "请输入分类"
+		return
+	}
+
+	lyClassify, err := models.GetRzdClassifyByName(categoryName)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = lyClassify
+	br.Msg = "获取成功"
+}
+
+// AddBatchRzdData
+// @Title 新增数据源指标数据
+// @Description 新增数据源指标数据
+// @Success 200 string "处理成功"
+// @router /add/batch/rzd/data [post]
+func (this *BaseFromRzdController) AddBatchRzdData() {
+	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.BaseFromRzdData
+	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.AddRzdDataList(req)
+	if err != nil {
+		br.Msg = "新增指标数据失败!"
+		br.ErrMsg = "新增指标数据失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// AddRzdIndex
+// @Title 新增指标
+// @Description 新增指标
+// @Success 200 string "处理成功"
+// @router /add/rzd/index [post]
+func (this *BaseFromRzdController) AddRzdIndex() {
+	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.BaseFromRzdIndex
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	indexId, err := models.AddRzdIndex(&req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = indexId
+	br.Msg = "处理成功"
+}
+
+// GetRzdIndexDataByIndexIdAndDataTime
+// @Title 根据指标code和时间获取指标数据
+// @Description 根据指标code和时间获取指标数据
+// @Success 200 {object} models.BaseFromLyData
+// @router /get/rzd/index/data/by/code/and/time [post]
+func (this *BaseFromRzdController) GetRzdIndexDataByIndexIdAndDataTime() {
+	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 = "请输入指标code!"
+		br.ErrMsg = "请输入指标code"
+		return
+	}
+
+	dataTime := reqData.DataTime
+	if dataTime == "" {
+		br.Msg = "请输入时间!"
+		br.ErrMsg = "请输入时间"
+		return
+	}
+
+	rzdData, err := models.GetRzdDataByIndexCodeAndDataTime(indexCode, dataTime)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = rzdData
+	br.Msg = "获取成功"
+}
+
+// UpdateRzdDataById
+// @Title 更新数据源指标数据
+// @Description 更新数据源指标数据
+// @Success 200 string "处理成功"
+// @router /update/rzd/data/by/id [post]
+func (this *BaseFromRzdController) UpdateRzdDataById() {
+	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.UpdateRzdDataById(id, value)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// GetRzdEdbDataByIndexCodeAndDataTime
+// @Title 根据指标编码和精确日期获取指标库数据
+// @Description 根据指标编码和精确日期获取指标库数据
+// @Success 200 {object} []models.EdbDataRzd
+// @router /get/edb/rzd/data/by/code/and/time [post]
+func (this *BaseFromRzdController) GetRzdEdbDataByIndexCodeAndDataTime() {
+	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 = "获取成功"
+}
+
+// GetRzdEdbInfoByIndexCode
+// @Title 根据指标编码获取指标库指标
+// @Description 根据指标编码获取指标库指标
+// @Success 200 {object} []models.EdbDataRzd
+// @router /get/rzd/edb/info/by/code [post]
+func (this *BaseFromRzdController) GetRzdEdbInfoByIndexCode() {
+	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 = "获取成功"
+}
+
+// UpdateRzdEdbDataById
+// @Title 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+// @Description 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+// @Success 200 string "处理成功"
+// @router /update/rzd/edb/data/by/id [post]
+func (this *BaseFromRzdController) UpdateRzdEdbDataById() {
+	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.UpdateRzdEdbDataById(id, value)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}
+
+// GetRzdIndexByCode
+// @Title 查询指标编码是否存在
+// @Description 查询指标编码是否存在
+// @Success 200 {object} models.BaseFromLyIndex
+// @router /get/rzd/index/by/code [post]
+func (this *BaseFromRzdController) GetRzdIndexByCode() {
+	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
+	}
+
+	rzdIndex, err := models.GetRzdIndexByCode(indexCode)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = rzdIndex
+	br.Msg = "获取成功"
+}
+
+// AddBatchRzdEdbData
+// @Title 批量增加睿咨得指标库数据
+// @Description 批量增加睿咨得指标库数据
+// @Success 200 string "处理成功"
+// @router /add/batch/rzd/edb/data [post]
+func (this *BaseFromRzdController) AddBatchRzdEdbData() {
+	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.EdbDataRzd
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	err = models.AddRzdEdbDataList(req)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 2 - 0
controllers/base_from_trade_analysis.go

@@ -117,6 +117,8 @@ func (this *BaseFromTradeAnalysisController) EdbRefresh() {
 		return
 	}
 	if len(companyTradeData) == 0 {
+		br.Ret = 200
+		br.Success = true
 		br.Msg = "期货数据为空"
 		return
 	}

+ 174 - 0
controllers/base_from_usda_fas.go

@@ -0,0 +1,174 @@
+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"
+)
+
+// UsdaFasController 涌益咨询
+type UsdaFasController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增涌益咨询指标接口
+// @Description 新增涌益咨询指标接口
+// @Success 200 {object} models.AddEdbInfoReq
+// @router /add [post]
+func (this *UsdaFasController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	usdaFas := new(models.BaseFromUsdaFas)
+	source := usdaFas.GetSource()
+	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) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+	err = usdaFas.Add(req.EdbCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取指标信息失败 AddEdbDataFromUsdaFas,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	return
+}
+
+// Refresh
+// @Title 刷新涌益咨询指标接口
+// @Description 刷新涌益咨询指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *UsdaFasController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	usdaFas := new(models.BaseFromUsdaFas)
+	source := usdaFas.GetSource()
+	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
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+	if req.EdbInfoId <= 0 {
+		req.EdbInfoId = edbInfo.EdbInfoId
+	}
+	err = usdaFas.Refresh(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromusdaFas,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 = "获取成功"
+}
+
+// HandleExcelData
+// @Title 处理涌益咨询指标的接口
+// @Description 处理涌益咨询指标的接口
+// @Success 200 {object} models.HandleUsdaFasExcelDataReq
+// @router /handle/excel_data [post]
+func (this *UsdaFasController) HandleExcelData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.HandleUsdaFasExcelDataReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	// 处理excel表数据
+	err = services.HandleUsdaFasIndex(&req)
+	if err != nil {
+		fmt.Println("HandleMysteelIndex Err:" + err.Error())
+		br.Msg = "处理失败"
+		br.ErrMsg = "处理失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 1 - 0
controllers/base_from_wind.go

@@ -57,6 +57,7 @@ func (this *WindController) Add() {
 
 		// 试用平台的话,需要额外从弘则这边获取下地址
 		if utils.BusinessCode == utils.HZ_TRIAL_BUSSINESS_CODE {
+			// todo 从体验版中获取对应的终端信息
 			tmpWindUrl, tmpTerminalCode, err, errMsg := services.GetHzWindUrl(req.EdbCode, source)
 			if err != nil {
 				br.Msg = "添加失败!"

+ 0 - 1
controllers/base_from_wind_wsd.go

@@ -53,7 +53,6 @@ func (this *WindWsdController) Add() {
 
 		windUrl := ``
 		terminalCode := ""
-
 		// 试用平台的话,需要额外从弘则这边获取下地址
 		if utils.BusinessCode == utils.HZ_TRIAL_BUSSINESS_CODE {
 			tmpWindUrl, tmpTerminalCode, err, errMsg := services.GetHzWindUrl(req.EdbCode, source)

+ 10 - 40
controllers/future_good/future_good_edb_info.go

@@ -8,7 +8,7 @@ import (
 	"eta/eta_index_lib/models/future_good"
 	"eta/eta_index_lib/services"
 	"eta/eta_index_lib/utils"
-	"fmt"
+	"strconv"
 	"time"
 )
 
@@ -206,19 +206,19 @@ func (this *FutureGoodEdbInfoController) RefreshRelation() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-	var req future_good.RefreshFutureEdbEdbInfoReq
+	var req future_good.RefreshFutureChartInfoReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
 		br.Msg = "参数解析异常!"
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-	if req.FutureGoodEdbInfoId <= 0 {
-		br.Msg = "请输入指标ID!"
-		br.ErrMsg = "请输入指标ID"
+	if req.ChartInfoId <= 0 {
+		br.Msg = "请输入图表ID!"
+		br.ErrMsg = "请输入图表ID"
 		return
 	}
-	cacheKey = utils.CACHE_EDB_DATA_REFRESH + "_futuregood_relation_" + req.FutureGoodEdbCode
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + "_futuregood_relation_chart" + strconv.Itoa(req.ChartInfoId)
 
 	if utils.Rc.IsExist(cacheKey) {
 		br.Ret = 501
@@ -227,47 +227,17 @@ func (this *FutureGoodEdbInfoController) RefreshRelation() {
 		return
 	}
 
-	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	utils.Rc.SetNX(cacheKey, 1, 10*time.Minute)
 	defer func() {
 		utils.Rc.Delete(cacheKey)
 	}()
-
-	//获取指标信息
-	futureGoodEdbInfo, err := future_good.GetFutureGoodEdbInfo(req.FutureGoodEdbInfoId)
+	err, errMsg := logic.RefreshByChartId(req.ChartInfoId)
 	if err != nil {
-		if err.Error() != utils.ErrNoRow() {
-			br.Msg = "系统内找不到该指标"
-		} else {
-			br.Msg = "刷新失败"
-			br.ErrMsg = "添加失败,ERR:" + err.Error()
-		}
+		br.Msg = "利润曲线图表刷新失败"
+		br.ErrMsg = "利润曲线图表刷新失败,Err:" + errMsg
 		return
 	}
 
-	// 获取相关图表
-	list, err := models.GetGroupChartEdbMappingListByEdbInfoId(futureGoodEdbInfo.FutureGoodEdbInfoId, 2)
-	if err != nil {
-		br.Msg = "查找相关图表id失败"
-		br.ErrMsg = "添加失败,ERR:" + err.Error()
-		return
-	}
-
-	go func() {
-		errMsgList := make([]string, 0)
-		for _, v := range list {
-			err, errMsg := logic.RefreshByChartId(v.ChartInfoId)
-			if err != nil {
-				errMsgList = append(errMsgList, fmt.Sprint(v.ChartInfoId, "更新失败,"+errMsg))
-			}
-		}
-
-		if len(errMsgList) > 0 {
-			//br.Msg = "部分刷新失败"
-			//br.ErrMsg = "部分刷新失败,Err:" + strings.Join(errMsgList, ";")
-			return
-		}
-	}()
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "刷新成功"

+ 2 - 0
go.sum

@@ -8,6 +8,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
 github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
+github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211223021540-a6a1e1173105 h1:80Iu0Rujat7jabX6Egx/dU/ijgsMp4WtDr9LAv3dMmo=
+github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211223021540-a6a1e1173105/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
 github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
 github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=

+ 7 - 7
logic/predict_edb.go

@@ -66,7 +66,7 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 	edbCode := sourceEdbInfo.EdbCode + "_" + time.Now().Format(utils.FormatShortDateTimeUnSpace)
 
 	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
-	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(0, 0, edbName, lang)
+	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, 0, edbName, lang)
 	if err != nil {
 		errMsg = "判断指标名称是否存在失败"
 		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
@@ -244,8 +244,8 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 						calculateMappingItem := &models.EdbInfoCalculateMapping{
 							EdbInfoCalculateMappingId: 0,
 							EdbInfoId:                 0,
-							Source:                    utils.DATA_SOURCE_CALCULATE,
-							SourceName:                "指标运算",
+							Source:                    edbInfo.Source,
+							SourceName:                edbInfo.SourceName,
 							EdbCode:                   "",
 							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
 							FromEdbCode:               fromEdbInfo.EdbCode,
@@ -336,8 +336,8 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 						tmpCalculateMappingItem := &models.EdbInfoCalculateMapping{
 							EdbInfoCalculateMappingId: 0,
 							EdbInfoId:                 0,
-							Source:                    utils.DATA_SOURCE_CALCULATE,
-							SourceName:                "指标运算",
+							Source:                    edbInfo.Source,
+							SourceName:                edbInfo.SourceName,
 							EdbCode:                   "",
 							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
 							FromEdbCode:               fromEdbInfo.EdbCode,
@@ -443,7 +443,7 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string,
 	}
 
 	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
-	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(1, edbInfoId, edbName, lang)
+	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, edbInfoId, edbName, lang)
 	if err != nil {
 		errMsg = "判断指标名称是否存在失败"
 		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
@@ -993,7 +993,7 @@ func checkExistByEdbName(edbInfoType int, edbName, lang string) (has bool, err e
 	var pars []interface{}
 
 	condition += " AND edb_info_type=? "
-	pars = append(pars, 0)
+	pars = append(pars, edbInfoType)
 
 	switch lang {
 	case utils.EnLangVersion:

+ 200 - 117
logic/profit_chart_info.go

@@ -105,6 +105,8 @@ type ChartInfoReq struct {
 	BaseEdbInfoId           int                     `description:"基础的指标id"`
 	DateList                []ChartInfoDateReq      `description:"日期配置"`
 	ProfitNameEn            string                  `description:"利润英文名称"`
+	EdbInfoIdList           []int                   `description:"现货指标ID列表"`
+	XDataList               []XData                 `description:"横轴配置"`
 }
 
 // RefreshByChartId 根据图表id刷新图表
@@ -136,12 +138,34 @@ func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
 		}
 		return
 	}
+	if len(extraConf.EdbInfoIdList) == 0 {
+		extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId)
+	}
 
-	baseEdbInfo, err := models.GetEdbInfoById(extraConf.BaseEdbInfoId)
+	baseEdbInfo := new(models.EdbInfo)
+	// ETA指标
+	edbInfoListTmp, err := models.GetEdbInfoByIdList(extraConf.EdbInfoIdList)
 	if err != nil {
 		errMsg = "获取失败"
 		return
 	}
+	//按照请求顺序排序
+	edbInfoMap := make(map[int]*models.EdbInfo)
+	for _, v := range edbInfoListTmp {
+		edbInfoMap[v.EdbInfoId] = v
+	}
+	edbInfoList := make([]*models.EdbInfo, 0)
+	for _, v := range extraConf.EdbInfoIdList {
+		edbInfoList = append(edbInfoList, edbInfoMap[v])
+	}
+	edbInfoListMap := make(map[int]*models.EdbInfo)
+	for k, v := range edbInfoList {
+		edbInfoList[k].EdbNameSource = v.EdbName
+		edbInfoListMap[v.EdbInfoId] = v
+		if v.EdbInfoId == extraConf.BaseEdbInfoId {
+			baseEdbInfo = v
+		}
+	}
 	// 商品数据库指标
 	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
 	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
@@ -160,9 +184,7 @@ func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
 		futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo
 		zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo)
 	}
-
-	xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList)
-
+	xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList, extraConf.XDataList)
 	xDataListByte, err := json.Marshal(xDataList)
 	if err != nil {
 		errMsg = "保存失败"
@@ -179,7 +201,7 @@ func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
 	extraUpdateCol := make([]string, 0)
 	chartInfoFutureGoodProfit.XValue = string(xDataListByte)
 	chartInfoFutureGoodProfit.YValue = string(yDataListByte)
-	chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
+	//chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
 	chartInfoFutureGoodProfit.ModifyTime = time.Now()
 	extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"}
 	err = chartInfoFutureGoodProfit.Update(extraUpdateCol)
@@ -188,11 +210,15 @@ func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
 }
 
 // GetProfitChartEdbData 获取利润图表的指标数据
-func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag) (xDataList []XData, yDataList []YData, err error) {
+func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, edbInfoList []*models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag, reqXDataList []XData) (xDataList []XData, yDataList []YData, err error) {
+
 	if baseEdbInfo == nil {
 		err = errors.New("ETA指标未选取")
 		return
 	}
+	if len(edbInfoList) == 0 {
+		edbInfoList = append(edbInfoList, baseEdbInfo)
+	}
 	if len(zlFutureGoodEdbInfoList) <= 0 {
 		err = errors.New("商品指标未选取")
 		return
@@ -213,12 +239,17 @@ func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList
 			futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
 		}
 	}
-
+	// 指标对应的所有数据
+	//edbDataListMap := make(map[int][]*models.EdbDataList)
 	// 普通的指标数据
-	baseDataList := make([]*models.EdbDataList, 0)
-	baseDataList, err = models.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.SubSource, baseEdbInfo.EdbInfoId, "", "")
-	if err != nil {
-		return
+	baseDataListMap := make(map[int][]*models.EdbDataList)
+	for _, v := range edbInfoList {
+		baseDataList := make([]*models.EdbDataList, 0)
+		baseDataList, err = models.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
+		if err != nil {
+			return
+		}
+		baseDataListMap[v.EdbInfoId] = baseDataList
 	}
 
 	latestDate := zlFutureGoodEdbInfoList[0].EndDate
@@ -261,10 +292,10 @@ func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList
 	// 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约)
 	specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)
 
-	isAllChina := true // 是否都是国内的期货合约
+	hasChina := false // 是否包含国内的期货合约
 	for _, v := range zlFutureGoodEdbInfoList {
-		if v.RegionType != "国内" {
-			isAllChina = false
+		if v.RegionType == "国内" {
+			hasChina = true
 			break
 		}
 	}
@@ -283,7 +314,7 @@ func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList
 			return
 		}
 
-		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
+		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, monthNum)
 		if tmpErr != nil {
 			err = tmpErr
 			return
@@ -322,6 +353,11 @@ func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList
 		}
 	}
 
+	// 需求池604,只要包含了国内合约,最大必须是12期
+	if hasChina {
+		maxN = 12
+	}
+
 	// 找出所有的N值,并进行正序排列
 	dateList := make([]string, 0)
 	for _, n := range nMap {
@@ -330,36 +366,47 @@ func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList
 	sort.Slice(dateList, func(i, j int) bool {
 		return dateList[i] < dateList[j]
 	})
-
-	_, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN)
-	if err != nil {
-		return
+	var reqEdbInfoIds []int
+	for _, v := range edbInfoList {
+		reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId)
+		tmp := XData{
+			Name:   v.EdbName,
+			NameEn: v.EdbNameEn,
+		}
+		xDataList = append(xDataList, tmp)
 	}
+	var edbIdList []int
+	futureGoodNameMap := make(map[int]map[int]string)
+	edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds)
 
-	tmpXDataList, newYDataList, err := handleProfitResultData(baseEdbInfo, yDataList, earliestDateTime)
+	// todo 最后处理数据
+	tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, futureGoodNameMap, yDataList, earliestDateTime, edbIdList)
 	if err != nil {
 		return
 	}
-	xDataList = []XData{
-		{
-			Name:   "现货利润",
-			NameEn: "Spot Price",
-		},
+	if len(reqXDataList) == 0 {
+		xDataList = tmpXDataList
+	} else {
+		xDataList = reqXDataList
 	}
-	xDataList = append(xDataList, tmpXDataList...)
+
 	yDataList = newYDataList
 
 	return
 }
 
 // ProfitChartChartData 获取数据
-func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int) (edbIdList []int, yDataList []YData, err error) {
+func ProfitChartChartData(baseEdbInfo *models.EdbInfo, baseDataListMap map[int][]*models.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int, reqEdbInfoIds []int) (edbIdList []int, yDataList []YData, futureGoodNameMap map[int]map[int]string, err error) {
 	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
 	//earliestDateTime time.Time
 	// ETA指标数据
-	baseEdbDateData := make(map[string]float64)
-	for _, edbData := range baseDataList {
-		baseEdbDateData[edbData.DataTime] = edbData.Value
+	allBaseEdbDateDataMap := make(map[int]map[string]float64)
+	for edbInfoId, baseDataList := range baseDataListMap {
+		baseEdbDateData := make(map[string]float64)
+		for _, edbData := range baseDataList {
+			baseEdbDateData[edbData.DataTime] = edbData.Value
+		}
+		allBaseEdbDateDataMap[edbInfoId] = baseEdbDateData
 	}
 
 	// 商品指标数据
@@ -378,8 +425,8 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 
 	// 将计算公式中的字母转大写
 	formulaStr = strings.ToUpper(formulaStr)
-
-	for _, barChartInfoDate := range chartInfoDateList {
+	futureGoodNameMap = make(map[int]map[int]string)
+	for tmpk, barChartInfoDate := range chartInfoDateList {
 		yDataMap := make(map[int]float64)
 		var maxDate time.Time
 
@@ -406,26 +453,59 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 			return
 		}
 
-		findDataList := make([]float64, 0) // 当前日期的数据值
-		noDataIdList := make([]int, 0)     // 没有数据的指标id
-		xEdbInfoIdList := make([]int, 0)   // 当前数据的指标id列表
+		findDataList := make([]float64, 0)  // 当前日期的数据值
+		noDataIdList := make([]int, 0)      // 没有数据的指标id
+		noDataIdMap := make(map[int]int, 0) // 没有数据的指标map
+		xEdbInfoIdList := make([]int, 0)    // 当前数据的指标id列表
 
 		// 现货指标
-		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataList, baseEdbDateData, edbDataMap)
+		index := 0
+		var realDateTime time.Time
+		// 现货指标
+		baseEdbDateData, ok := allBaseEdbDateDataMap[baseEdbInfo.EdbInfoId]
+		if !ok {
+			err = fmt.Errorf("指标id: %d 没有数据", baseEdbInfo.EdbInfoId)
+			return
+		}
+		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataListMap[baseEdbInfo.EdbInfoId], baseEdbDateData, edbDataMap)
 		if tmpErr != nil {
 			err = tmpErr
 			return
 		}
-		findDataList = append(findDataList, findDataValue)
-		yDataMap[0] = findDataValue
 		if isFind {
 			maxDate = realDateTime
 		}
+		edbIdList = make([]int, 0) //普通指标ID
+		for _, edbInfoId := range reqEdbInfoIds {
+			if edbInfoId == baseEdbInfo.EdbInfoId {
+				findDataList = append(findDataList, findDataValue)
+				yDataMap[index] = findDataValue
+				xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
+				edbIdList = append(edbIdList, edbInfoId)
+				index += 1
+				continue
+			}
+			baseEdbDateDataTmp, ok := allBaseEdbDateDataMap[edbInfoId]
+			if !ok {
+				err = fmt.Errorf("指标id: %d 没有数据", edbInfoId)
+				return
+			}
+			findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)]
+			if !isFindTmp {
+				noDataIdList = append(noDataIdList, edbInfoId)
+				noDataIdMap[edbInfoId] = edbInfoId
+			}
+			findDataList = append(findDataList, findDataValueTmp)
+			yDataMap[index] = findDataValueTmp
 
-		xEdbInfoIdList = append(xEdbInfoIdList, 0)
+			xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
+			edbIdList = append(edbIdList, edbInfoId)
+			index += 1
+		}
 
 		mList := make([]int, 0) // 间隔月份
 
+		tmpNameMap := make(map[int]string)
 		// 最小开始的n值
 		//minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
 		for _, date := range dateList {
@@ -437,8 +517,6 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 			//findDateTime
 
 			// 获取当前日期相对开始日期的期数
-			//tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month())
-			// 用实际日期的月份作为基准,往前推12个月(2024-5-13 16:26:43修改)
 			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
 			if tmpN <= 0 {
 				continue
@@ -486,7 +564,17 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 			//}
 
 			newTagEdbIdMap := make(map[string]int)
+
 			for tag, zlEdbId := range tagEdbIdMap {
+				if tag == "A" {
+					nameTmp := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbName, "(")
+					nameTmpEn := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbNameEn, "(")
+					if len(nameTmp) > 1 && len(nameTmpEn) > 1 {
+						nameTmp[1] = strings.Trim(nameTmp[1], ")")
+						nameTmpEn[1] = strings.Trim(nameTmpEn[1], ")")
+						tmpNameMap[tmpN+1] = nameTmp[1] + "-" + nameTmpEn[1]
+					}
+				}
 				newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
 			}
 			//, formulaStr string, tagEdbIdMap map[string]int
@@ -494,52 +582,19 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 			//计算公式异常,那么就移除该指标
 			if formulaFormStr == `` {
 				//removeDateList = append(removeDateList, sk)
-				//fmt.Println("异常了")
+				fmt.Println("异常了")
 				continue
 			}
 
-			//expression := formula.NewExpression(formulaFormStr)
-			//calResult, evaluateErr := expression.Evaluate()
-			//if evaluateErr != nil {
-			//	// 分母为0的报错
-			//	if strings.Contains(evaluateErr.Error(), "divide by zero") {
-			//		//removeDateList = append(removeDateList, sk)
-			//		continue
-			//	}
-			//	err = errors.New("计算失败:Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
-			//	fmt.Println(err)
-			//	continue
-			//}
-			//// 如果计算结果是NAN,那么就退出当前循环
-			//if calResult.IsNan() {
-			//	continue
-			//}
-			//calVal, tmpErr := calResult.Float64()
-			//if tmpErr != nil {
-			//	err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
-			//	fmt.Println(err)
-			//	continue
-			//}
-			calVal, err := engine.ParseAndExec(formulaFormStr)
+			calVal, e := engine.ParseAndExec(formulaFormStr)
 			//calVal, err := calResult.Float64()
-			if err != nil {
-				// 分母为0的报错,忽略该循环
-				if utils.IsDivideZero(err) {
-					//removeDateList = append(removeDateList, sk)
-					continue
-				}
-				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+			if e != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + e.Error() + ";formulaStr:" + formulaFormStr)
 				fmt.Println(err)
-				return nil, nil, err
+				return
 			}
-			//nanCheck := fmt.Sprintf("%0.f", calVal)
 			//yDataMap[n] = calVal
 			//xEdbInfoIdList = append(xEdbInfoIdList, n)
-			nanCheck := fmt.Sprintf("%0.f", calVal)
-			// 分母为0.0的报错
-			if nanCheck == "NaN" || nanCheck == "+Inf" || nanCheck == "-Inf" {
-				continue
-			}
 			calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
 			yDataMap[tmpN] = calVal
 			xEdbInfoIdList = append(xEdbInfoIdList, tmpN)
@@ -565,6 +620,33 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 			yDate = maxDate.Format(utils.FormatDate)
 		}
 
+		{
+			hasDataIndexList := make([]int, 0)
+			for dataK, edbInfoId := range xEdbInfoIdList {
+				if _, ok := noDataIdMap[edbInfoId]; !ok { // 如果是没有数据的指标id
+					hasDataIndexList = append(hasDataIndexList, dataK)
+				}
+			}
+			lenHasDataIndex := len(hasDataIndexList)
+			if lenHasDataIndex > 0 {
+				for lenHasDataI := 1; lenHasDataI < lenHasDataIndex; lenHasDataI++ {
+					perK := hasDataIndexList[lenHasDataI-1] //上一个有数据的指标下标
+					currK := hasDataIndexList[lenHasDataI]  //当前有数据的指标下标
+					preVal := findDataList[perK]            //上一个有数据的坐标的值
+					currVal := findDataList[currK]          //当前有数据的指标的值
+
+					// 环差值
+					hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(currK - perK)))
+					var tmpI int64
+					// 将两个中间的数据做平均值补全
+					for hcI := perK + 1; hcI < currK; hcI++ {
+						tmpI++
+						findDataList[hcI], _ = decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(tmpI))).RoundCeil(4).Float64()
+					}
+				}
+			}
+		}
+		futureGoodNameMap[tmpk] = tmpNameMap
 		yDataList = append(yDataList, YData{
 			Date:           yDate,
 			ConfigDate:     realDateTime,
@@ -583,7 +665,7 @@ func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoM
 }
 
 // getFutureGoodEdbInfoList 获取适用的指标列表
-func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
+func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
 	maxN := 36 //最大36期合约
 	futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
 	futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo)
@@ -603,8 +685,6 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 		lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
 		//futureGoodEdbInfoList
 		//if isAllChina {
-		//	// 如果全是国内指标,那么只需要拼上多出的几期合约即可
-		//	maxN = lenFutureGoodEdbInfoList + monthNum
 		//}
 		// 如果全是国内指标,那么只需要拼上多出的几期合约即可
 		maxN = lenFutureGoodEdbInfoList + monthNum
@@ -612,9 +692,6 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 		for i := 1; i < maxN; i++ {
 			k := i % lenFutureGoodEdbInfoList
 			futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k]
-			if i > newMaxN {
-				newMaxN = i
-			}
 		}
 
 		//需求池604,只要是国内合约,最大必须是12期
@@ -626,7 +703,7 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 	for _, v := range tmpFutureGoodEdbInfoList {
 		//海外的连续日期,目前
 		if v.FutureGoodEdbType == 2 {
-			if v.Month <= newMaxN {
+			if v.Month <= maxN {
 				futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = v
 				if v.Month > newMaxN {
 					newMaxN = v.Month
@@ -647,7 +724,7 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 		subMonth := v.Month - int(earliestDateTime.Month())
 		// 如果(当前年-最新日期的年份) * 12个月 + (当前月-最新日期的月份) 小于总月份
 		tmpN := subYear*12 + subMonth
-		if tmpN < newMaxN {
+		if tmpN < maxN {
 			tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local)
 			futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = v
 			if tmpN > newMaxN {
@@ -661,29 +738,37 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 }
 
 // handleProfitResultData 处理成最终的结果数据
-func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earliestDateTime time.Time) (xDataList []XData, newYDataList []YData, err error) {
-	xDataList = make([]XData, 0)
+func handleProfitResultData(xDataListInit []XData, futureNameMap map[int]map[int]string, yDataList []YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []XData, newYDataList []YData, err error) {
 	newYDataList = yDataList
+	xDataList = xDataListInit
 
 	nMap := make(map[int]int)
-
+	nList := make([]int, 0)
+	nListEdbMap := make(map[int]struct{})
 	for _, v := range yDataList {
 		for _, n := range v.XEdbInfoIdList {
-			nMap[n] = n
+			if utils.InArrayByInt(allEdbInfoIds, n) {
+				if _, ok := nListEdbMap[n]; !ok {
+					nList = append(nList, n)
+					nListEdbMap[n] = struct{}{}
+				}
+			} else {
+				nMap[n] = n
+			}
 		}
 	}
 
 	// 找出所有的N值,并进行正序排列
-	nList := make([]int, 0)
+	nListTmp := make([]int, 0)
 	for _, n := range nMap {
-		nList = append(nList, n)
+		nListTmp = append(nListTmp, n)
 	}
-	sort.Slice(nList, func(i, j int) bool {
-		return nList[i] < nList[j]
+	sort.Slice(nListTmp, func(i, j int) bool {
+		return nListTmp[i] < nListTmp[j]
 	})
-
+	nList = append(nList, nListTmp...)
 	for _, n := range nList {
-		if n == 0 {
+		if utils.InArrayByInt(allEdbInfoIds, n) {
 			continue
 		}
 		xDataList = append(xDataList, XData{
@@ -704,7 +789,7 @@ func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earl
 			if len(xEdbInfoIdList) > 0 {
 				currN := xEdbInfoIdList[0]
 				// 当前距离最早的日期相差的N数
-				if n == currN {
+				if n == currN { // todo 改成所有的基础现货指标
 					if needNum > 0 {
 						currVal := yData.Value[valIndex]
 						preVal := yData.Value[valIndex-1]
@@ -714,12 +799,16 @@ 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)))).Round(4).Float64()
+							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
 							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
 						}
 					}
+					if utils.InArrayByInt(allEdbInfoIds, currN) {
+						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN)
+					} else {
+						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
+					}
 
-					newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
 					newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
 					valIndex++
 					needNum = 0
@@ -746,7 +835,7 @@ func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earl
 	}
 
 	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
-	xDataList = xDataList[0:maxI]
+	xDataList = xDataList[0 : maxI+1]
 	for yIndex, yData := range newYDataList {
 		if len(yData.XEdbInfoIdList) > maxI+1 {
 			newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
@@ -755,30 +844,23 @@ func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earl
 			newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
 		}
 
-		currDate, _ := time.ParseInLocation(utils.FormatDate, yData.Date, time.Local)
-
 		nameList := make([]string, 0)
 		enNameList := make([]string, 0)
-		for _, n := range newYDataList[yIndex].XEdbInfoIdList {
-			if n == 1 { // 现货价不处理
-				nameList = append(nameList, baseEdbInfo.EdbName)
-				enNameList = append(enNameList, baseEdbInfo.EdbNameEn)
+		for k1, n := range newYDataList[yIndex].XEdbInfoIdList {
+			if utils.InArrayByInt(allEdbInfoIds, n) { // 现货价不处理
+				tmpItem := xDataListInit[k1]
+				nameList = append(nameList, tmpItem.Name)
+				enNameList = append(enNameList, tmpItem.NameEn)
 				continue
 			}
 			if n <= 0 {
 				nameList = append(nameList, `无合约`)
 				enNameList = append(enNameList, `no contract`)
 			} else {
-				var date string
-
-				currDateTime := currDate.AddDate(0, n-1, 0)
-				month := int(currDateTime.Month())
-				date = fmt.Sprintf("%d%d", currDateTime.Year(), month)
-				if month < 10 {
-					date = fmt.Sprintf("%d0%d", currDateTime.Year(), month)
-				}
-				nameList = append(nameList, date)
-				enNameList = append(enNameList, date)
+				nameTmp := futureNameMap[yIndex][n]
+				nameTmpSlice := strings.Split(nameTmp, "-")
+				nameList = append(nameList, nameTmpSlice[0])
+				enNameList = append(enNameList, nameTmpSlice[1])
 			}
 		}
 		newYDataList[yIndex].NameList = nameList
@@ -827,6 +909,7 @@ func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaS
 	for k, v := range funMap {
 		formulaStr = strings.Replace(formulaStr, v, k, -1)
 	}
+	fmt.Println(formulaStr)
 	if replaceCount == len(tagEdbIdMap) {
 		return formulaStr
 	} else {

+ 31 - 11
models/base_from_adjust.go

@@ -30,7 +30,7 @@ type AddAdjustEdbData struct {
 }
 
 // SaveAdjustEdb 保存数据调整指标
-func SaveAdjustEdb(req SaveAdjustEdbReq) (edbInfo *EdbInfo, err error, errMsg string) {
+func SaveAdjustEdb(req SaveAdjustEdbReq, lang string) (edbInfo *EdbInfo, err error, errMsg string) {
 	errMsg = `添加指标失败`
 	o := orm.NewOrm()
 	to, err := o.Begin()
@@ -62,10 +62,12 @@ func SaveAdjustEdb(req SaveAdjustEdbReq) (edbInfo *EdbInfo, err error, errMsg st
 			Source:          utils.DATA_SOURCE_CALCULATE_ADJUST,
 			SourceName:      "数据调整",
 			EdbCode:         edbCode,
-			EdbName:         utils.TrimStr(req.EdbName),
-			EdbNameSource:   utils.TrimStr(req.EdbName),
-			Frequency:       utils.TrimStr(req.Frequency),
-			Unit:            utils.TrimStr(req.Unit),
+			EdbName:         strings.TrimSpace(req.EdbName),
+			EdbNameSource:   strings.TrimSpace(req.EdbName),
+			Frequency:       strings.TrimSpace(req.Frequency),
+			Unit:            strings.TrimSpace(req.Unit),
+			EdbNameEn:       strings.TrimSpace(req.EdbName),
+			UnitEn:          strings.TrimSpace(req.Unit),
 			ClassifyId:      req.ClassifyId,
 			SysUserId:       req.AdminId,
 			SysUserRealName: req.AdminName,
@@ -75,6 +77,7 @@ func SaveAdjustEdb(req SaveAdjustEdbReq) (edbInfo *EdbInfo, err error, errMsg st
 			EdbType:         2,
 			Sort:            GetAddEdbMaxSortByClassifyId(req.ClassifyId, utils.EDB_INFO_TYPE),
 		}
+
 		newEdbInfoId, tmpErr := to.Insert(edbInfo)
 		if tmpErr != nil {
 			err = tmpErr
@@ -133,14 +136,29 @@ func SaveAdjustEdb(req SaveAdjustEdbReq) (edbInfo *EdbInfo, err error, errMsg st
 			return
 		}
 
+		// 额外赋值
+		switch lang {
+		case utils.EnLangVersion:
+			req.EdbNameEn = req.EdbName
+			req.UnitEn = req.Unit
+
+			req.EdbName = edbInfo.EdbName
+			req.Unit = edbInfo.Unit
+		default:
+			req.EdbNameEn = edbInfo.EdbNameEn
+			req.UnitEn = edbInfo.UnitEn
+		}
+
 		// 更新指标信息
-		edbInfo.EdbName = utils.TrimStr(req.EdbName)
-		edbInfo.EdbNameSource = utils.TrimStr(req.EdbName)
-		edbInfo.Frequency = utils.TrimStr(req.Frequency)
-		edbInfo.Unit = utils.TrimStr(req.Unit)
+		edbInfo.EdbName = strings.TrimSpace(req.EdbName)
+		edbInfo.EdbNameSource = strings.TrimSpace(req.EdbName)
+		edbInfo.Frequency = strings.TrimSpace(req.Frequency)
+		edbInfo.Unit = strings.TrimSpace(req.Unit)
+		edbInfo.EdbNameEn = strings.TrimSpace(req.EdbNameEn)
+		edbInfo.UnitEn = strings.TrimSpace(req.UnitEn)
 		edbInfo.ClassifyId = req.ClassifyId
 		edbInfo.ModifyTime = time.Now()
-		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "EdbNameEn", "UnitEn", "ClassifyId", "ModifyTime")
 		if err != nil {
 			return
 		}
@@ -307,7 +325,7 @@ func RefreshAllAdjustEdb(edbInfo *EdbInfo, fromEdbInfo *EdbInfo) (err error) {
 		saveVal := utils.SubFloatToString(val, 20)
 		if existVal, ok := existDataMap[currDay]; !ok {
 			//格式化时间
-			currentDate, tmpErr := time.Parse(utils.FormatDate, item.DataTime)
+			currentDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
 			if tmpErr != nil {
 				err = tmpErr
 				return
@@ -363,6 +381,8 @@ type SaveAdjustEdbReq struct {
 	Unit          string                 `description:"单位"`
 	ClassifyId    int                    `description:"分类id"`
 	DataList      []SaveAdjustEdbDataReq `description:"指标对应的数据值"`
+	EdbNameEn     string                 `description:"英文指标名称"`
+	UnitEn        string                 `description:"英文单位"`
 }
 
 // SaveAdjustEdbDataReq 保存数据调整请求的数据的参数

+ 12 - 7
models/base_from_calculate.go

@@ -5,14 +5,12 @@ import (
 	"errors"
 	"eta/eta_index_lib/utils"
 	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/dengsgo/math-engine/engine"
+	"github.com/shopspring/decimal"
 	"strconv"
 	"strings"
 	"time"
-
-	"github.com/dengsgo/math-engine/engine"
-
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/shopspring/decimal"
 )
 
 // EdbInfoCalculateSaveReq 计算(运算)指标请求参数
@@ -231,7 +229,6 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSli
 				}
 			}
 		}
-
 		//计算数据
 		err = refreshAllCalculate(to, edbInfoList, edbInfoTag, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType, edbInfo.Extra)
 	}
@@ -687,7 +684,7 @@ type EdbInfoCalculateBatchSaveReq struct {
 	CalculateFormula string                         `description:"计算公式"`
 	EdbInfoIdArr     []EdbInfoCalculateEdbInfoIdReq `description:"关联指标列表"`
 	MoveType         int                            `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年"`
+	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年/交易日/自然日"`
 	Calendar         string                         `description:"公历/农历"`
 	Data             interface{}                    `description:"数据"`
 	EmptyType        int                            `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
@@ -727,6 +724,14 @@ type EdbInfoCalculateBatchEditReq struct {
 	CalculateFormula string                         `description:"计算公式"`
 }
 
+// DeleteCalculateData 删除计算数据
+func DeleteCalculateData(edbInfoId int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM edb_data_calculate WHERE edb_info_id=?`
+	_, err = o.Raw(sql, edbInfoId).Exec()
+	return
+}
+
 // CheckFormula2 校验公式是否正常(比如说除法的分母不能为0之类的,实际上就是用预设的字段数据做一次计算)
 func CheckFormula2(edbInfoArr []*EdbInfo, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string) (ok bool, err error) {
 	valArr := make(map[int]float64)

+ 2 - 2
models/base_from_mysteel_chemical.go

@@ -200,7 +200,7 @@ func RefreshEdbDataFromMysteelChemical(edbInfoId int, edbCode, startDate string)
 	return
 }
 
-// 钢联化工指标数据
+// 上海钢联指标数据
 type BaseFromMysteelChemicalIndex struct {
 	BaseFromMysteelChemicalIndexId    int64     `orm:"column(base_from_mysteel_chemical_index_id);pk"`
 	BaseFromMysteelChemicalClassifyId int       `description:"分类id"`
@@ -432,7 +432,7 @@ type HandleMysteelIndexResp struct {
 	List []*HandleMysteelIndex
 }
 
-// 钢联化工指标数据
+// 上海钢联指标数据
 type BaseFromMysteelChemicalData struct {
 	BaseFromMysteelChemicalDataId  int64 `orm:"column(base_from_mysteel_chemical_data_id);pk"`
 	BaseFromMysteelChemicalIndexId int64

+ 42 - 0
models/base_from_rzd_classify.go

@@ -0,0 +1,42 @@
+// @Author gmy 2024/8/7 9:26:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromRzdClassify struct {
+	BaseFromRzdClassifyId int    `orm:"column(base_from_rzd_classify_id);pk"`
+	CreateTime            string `orm:"column(create_time)"`
+	ModifyTime            string `orm:"column(modify_time)"`
+	ClassifyName          string `orm:"column(classify_name)"`
+	ParentId              int    `orm:"column(parent_id)"`
+	Sort                  int    `orm:"column(sort)"`
+	ClassifyNameEn        string `orm:"column(classify_name_en)"`
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromRzdClassify))
+}
+
+// GetRzdClassifyByName 根据分类名称查询
+func GetRzdClassifyByName(classifyName string) (item *BaseFromRzdClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_rzd_classify WHERE classify_name=?`
+	err = o.Raw(sql, classifyName).QueryRow(&item)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// AddRzdClassify 新增分类
+func AddRzdClassify(classify *BaseFromRzdClassify) (int64, error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(classify)
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}

+ 61 - 0
models/base_from_rzd_data.go

@@ -0,0 +1,61 @@
+// @Author gmy 2024/10/21 9:50:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromRzdData struct {
+	BaseFromRzdDataId  int     `orm:"column(base_from_rzd_data_id);pk"`
+	BaseFromRzdIndexId int     `orm:"column(base_from_rzd_index_id)"`
+	CreateTime         string  `orm:"column(create_time)"`
+	DataTime           string  `orm:"column(data_time)"`
+	IndexCode          string  `orm:"column(index_code)"`
+	ModifyTime         string  `orm:"column(modify_time)"`
+	Value              float64 `orm:"column(value)"`
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromRzdData))
+}
+
+// AddRzdDataList 批量插入数据记录列表
+func AddRzdDataList(items []BaseFromRzdData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// GetRzdDataByIndexCodeAndDataTime 根据指标id和数据日期查询数据
+func GetRzdDataByIndexCodeAndDataTime(indexCode string, dataTime string) (items []BaseFromRzdData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_rzd_data WHERE index_code=? AND data_time=?`
+	_, err = o.Raw(sql, indexCode, dataTime).QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// UpdateRzdDataById 根据主键id更新数据
+func UpdateRzdDataById(dataId int, value float64) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE base_from_rzd_data SET value=? WHERE base_from_rzd_data_id=?`
+	_, err = o.Raw(sql, value, dataId).Exec()
+	return
+}
+
+// GetBaseFromRzdDataByCondition 添加查询
+func GetBaseFromRzdDataByCondition(condition string, pars []interface{}) (items []BaseFromRzdData, err error) {
+	sql := `SELECT * FROM base_from_rzd_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
+}

+ 54 - 0
models/base_from_rzd_index.go

@@ -0,0 +1,54 @@
+// Package models
+// @Author gmy 2024/8/7 9:38:00
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromRzdIndex struct {
+	BaseFromRzdIndexId   int    `orm:"column(base_from_rzd_index_id);pk"`
+	CreateTime           string `orm:"column(create_time)"`
+	ModifyTime           string `orm:"column(modify_time)"`
+	BaseFromLyClassifyId int    `orm:"column(base_from_ly_classify_id)"`
+	IndexCode            string `orm:"column(index_code)"`
+	IndexName            string `orm:"column(index_name)"`
+	Frequency            string `orm:"column(frequency)"`
+	Unit                 string `orm:"column(unit)"`
+}
+
+// 在 init 函数中注册模型
+func init() {
+	orm.RegisterModel(new(BaseFromRzdIndex))
+}
+
+// AddRzdIndexList 批量插入指标记录列表
+func AddRzdIndexList(items []*BaseFromRzdIndex) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// AddRzdIndex 添加指标
+func AddRzdIndex(item *BaseFromRzdIndex) (int64, error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}
+
+// GetRzdIndexByCode 查询指标编码是否存在
+func GetRzdIndexByCode(indexCode string) (item *BaseFromRzdIndex, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_rzd_index WHERE index_code=?`
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+
+	return
+}

+ 10 - 0
models/base_from_ths.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_index_lib/services/alarm_msg"
 	"eta/eta_index_lib/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
@@ -26,7 +27,14 @@ type Tables struct {
 
 // 新增同花顺指标数据
 func AddEdbDataFromThs(edbCode string, item EdbDataFromThs) (err error) {
+	var errMsg string
 	o := orm.NewOrm()
+	defer func() {
+		if err != nil {
+			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 同花顺数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
+			go alarm_msg.SendAlarmMsg("同花顺数据获取失败:err:"+errMsg, 3)
+		}
+	}()
 
 	if len(item.Tables) > 0 {
 		table := item.Tables[0]
@@ -42,6 +50,7 @@ func AddEdbDataFromThs(edbCode string, item EdbDataFromThs) (err error) {
 			sValue := table.Value[i]
 			dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 			if err != nil {
+				errMsg = " time.Parse :" + err.Error()
 				return err
 			}
 			timestamp := dataTime.UnixNano() / 1e6
@@ -53,6 +62,7 @@ func AddEdbDataFromThs(edbCode string, item EdbDataFromThs) (err error) {
 			addSql = strings.TrimRight(addSql, ",")
 			_, err = o.Raw(addSql).Exec()
 			if err != nil {
+				errMsg = " tx.Exec Err :" + err.Error()
 				return
 			}
 		}

+ 349 - 0
models/base_from_usda_fas.go

@@ -0,0 +1,349 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BaseFromUsdaFas 美国农业部
+type BaseFromUsdaFas struct{}
+
+type BaseFromUsdaFasData struct {
+	BaseFromUsdaFasDataId  int `orm:"column(base_from_usda_fas_data_id);pk"`
+	BaseFromUsdaFasIndexId int
+	IndexCode              string
+	DataTime               string
+	Value                  string
+	CreateTime             time.Time
+	ModifyTime             time.Time
+	DataTimestamp          int64
+}
+
+func GetBaseFromUsdaFasDataByCondition(condition string, pars []interface{}) (list []*BaseFromUsdaFasData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_usda_fas_data WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}
+
+// Add 添加
+func (obj BaseFromUsdaFas) Add(edbCode string) (err error) {
+	o := orm.NewOrm()
+
+	var condition string
+	var pars []interface{}
+	if edbCode != "" {
+		condition += " AND index_code=? "
+		pars = append(pars, edbCode)
+	}
+	UsdaFasBaseDataAll, err := GetBaseFromUsdaFasDataByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return
+	}
+	var isAdd bool
+	addSql := ` INSERT INTO edb_data_usda_fas(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	existMap := make(map[string]string)
+	for _, sv := range UsdaFasBaseDataAll {
+		eDate := sv.DataTime
+		dataTime, err := time.Parse(utils.FormatDate, eDate)
+		if err != nil {
+			return err
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, ok := existMap[eDate]; !ok {
+			addSql += GetAddSql("0", edbCode, eDate, timeStr, sv.Value)
+			isAdd = true
+		}
+		existMap[eDate] = sv.Value
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		utils.FileLog.Info("addSql:" + addSql)
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+// Refresh 刷新涌益咨询指标数据
+func (obj BaseFromUsdaFas) Refresh(edbInfoId int, edbCode, startDate string) (err error) {
+	source := obj.GetSource()
+	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)
+	}
+
+	UsdaFasDataList, err := GetBaseFromUsdaFasDataByCondition(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, 0, 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_usda_fas(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, v := range UsdaFasDataList {
+		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, 0, 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, 0, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshEdbDataFromBaiinfo add Err", err.Error())
+			return
+		}
+	}
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj BaseFromUsdaFas) GetSource() int {
+	return utils.DATA_SOURCE_USDA_FAS
+}
+
+// GetSourceName 获取来源名称
+func (obj BaseFromUsdaFas) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_USDA_FAS
+}
+
+type BaseFromUsdaFasIndex struct {
+	BaseFromUsdaFasIndexId int64 `orm:"column(base_from_usda_fas_index_id);pk"`
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	StartDate              string
+	EndDate                string
+	ClassifyId             int64
+	Sort                   int
+	BaseFileName           string
+	RenameFileName         string
+	TerminalCode           string
+	CreateTime             time.Time
+	ModifyTime             time.Time
+}
+
+type BaseFromUsdaFasIndexList struct {
+	BaseFromUsdaFasIndexId int64 `orm:"column(base_from_usda_fas_index_id);pk"`
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	ClassifyId             int64
+	StartDate              string
+	EndDate                string
+	TerminalCode           string
+	CreateTime             string
+	ModifyTime             string
+}
+
+func (y *BaseFromUsdaFasData) GetByIndexCode(indexCode string) (list []*BaseFromUsdaFasData, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_usda_fas_data WHERE index_code=? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&list)
+	return
+}
+
+func (y *BaseFromUsdaFasData) AddMulti(item []*BaseFromUsdaFasData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(1, item)
+	return
+}
+
+// Update 修改
+func (y *BaseFromUsdaFasData) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(y, updateCols...)
+	return
+}
+
+// HandleUsdaFasExcelData 涌益咨询的excel数据
+type HandleUsdaFasExcelData struct {
+	ClassifyName       string `description:"指标目录"`
+	ParentClassifyName string `description:"父级指标目录"`
+	ClassifySort       int    `description:"指标目录排序号"`
+	IndexName          string `description:"指标名称"`
+	IndexCode          string `description:"指标编码"`
+	Unit               string `description:"单位"`
+	Sort               int    `description:"排序号"`
+	Frequency          string `description:"频度"`
+	ExcelDataMap       map[string]string
+}
+
+type HandleUsdaFasExcelDataReq struct {
+	List         []*HandleUsdaFasExcelData
+	TerminalCode string `description:"编码"`
+}
+
+func (y *BaseFromUsdaFasData) 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_usda_fas_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	var latest_value float64
+	sql = ` SELECT value AS latest_value FROM %s WHERE index_code=? ORDER BY data_time DESC LIMIT 1 `
+	sql = fmt.Sprintf(sql, "base_from_usda_fas_data")
+	err = o.Raw(sql, indexCode).QueryRow(&latest_value)
+	item.LatestValue = latest_value
+	return
+}
+
+func (y *BaseFromUsdaFasIndex) ModifyIndexMaxAndMinDate(indexCode string, item *EdbInfoMaxAndMinInfo) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE base_from_usda_fas_index SET start_date=?,end_date=?, end_value=?, modify_time=NOW() WHERE index_code=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.LatestValue, indexCode).Exec()
+	return
+}
+
+func (y *BaseFromUsdaFasIndex) GetByIndexCode(indexCode string) (item *BaseFromUsdaFasIndex, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_usda_fas_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}
+
+func (y *BaseFromUsdaFasIndex) Add() (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(y)
+	return
+}
+
+// Update 修改
+func (y *BaseFromUsdaFasIndex) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(y, updateCols...)
+
+	return
+}
+
+// BaseFromUsdaFasClassify UsdaFas原始数据分类表
+type BaseFromUsdaFasClassify struct {
+	ClassifyId      int64     `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ClassifyNameEn  string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+func (y *BaseFromUsdaFasClassify) Add() (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(y)
+	return
+}
+
+// Update 修改
+func (y *BaseFromUsdaFasClassify) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(y, updateCols...)
+
+	return
+}
+
+func (y *BaseFromUsdaFasClassify) GetByClassifyName(classifyName string) (item *BaseFromUsdaFasClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_usda_fas_classify WHERE classify_name=? `
+	err = o.Raw(sql, classifyName).QueryRow(&item)
+	return
+}
+
+func (y *BaseFromUsdaFasClassify) GetParentClassify() (items []*BaseFromUsdaFasClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_usda_fas_classify WHERE parent_id=0 `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 9 - 0
models/base_predict_from_calculate.go

@@ -303,6 +303,15 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 		}
 		if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
 			maxStartDate = tmpStartDate
+		}
+		tmpEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+		if tmpErr != nil {
+			err = errors.New(`最近的日期格式化失败;日期:` + v.EndDate + `;err:` + tmpErr.Error())
+			return
+		}
+		if minEndDate.IsZero() || tmpEndDate.Before(minEndDate) {
+			minEndDate = tmpEndDate
+		}
 		}*/
 
 		// 获取关联指标数据

+ 7 - 4
models/db.go

@@ -69,8 +69,8 @@ func init() {
 		new(EdbDataPredictCalculateLjztbpj),
 		new(EdbDataPredictCalculateNhcc),
 		new(EdbDataPredictCalculateZjpj),
-		new(EdbDataInsertConfig),
 		new(EdbAdjustConf), // 数据调整的配置
+		new(EdbDataInsertConfig),
 		new(BaseFromMysteelChemicalClassify),
 		new(EdbInfoRelation), //指标引用记录
 	)
@@ -147,10 +147,10 @@ func initBaseIndex() {
 		new(BaseFromCoalmineInlandIndex),
 		new(BaseFromCoalmineCompanyIndex),
 		new(BaseFromCoalmineFirmIndex),
-		new(BaseFromMtjhMapping),
-		new(BaseFromMtjhIndex),
 		new(BaseFromFenweiIndex),
 		new(BaseFromFenweiData),
+		new(BaseFromMtjhMapping),
+		new(BaseFromMtjhIndex),
 		new(BaseFromBloombergIndex),
 		new(BaseFromBloombergData),
 		new(BaseFromSci99Data),
@@ -161,11 +161,11 @@ func initBaseIndex() {
 		new(CCFStockExcel),
 		new(BaseFromSciHqData),
 		new(BaseFromSciHqIndex),
+		new(BaseFromBusinessData), // 数据源中自有数据的明细数据表
 		new(BaseFromThsHfIndex),
 		new(BaseFromThsHfData),
 		new(BaseFromEdbMapping),
 		new(EdbDataThsHf),
-		new(BaseFromBusinessData), // 数据源中自有数据的明细数据表
 		new(BaseFromOilchemIndex),
 		new(BaseFromOilchemData),
 		new(BaseFromFenweiClassify),
@@ -174,6 +174,9 @@ func initBaseIndex() {
 		new(BaseFromHisugarData),
 		new(EdbDataCalculateStl),
 		new(CalculateStlConfig),
+		new(BaseFromUsdaFasIndex),
+		new(BaseFromUsdaFasData),
+		new(BaseFromUsdaFasClassify),
 	)
 }
 

+ 24 - 1
models/edb_data_calculate_hbz.go

@@ -235,8 +235,10 @@ func refreshAllCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 		return err
 	}
 	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	addSql := ` INSERT INTO edb_data_calculate_hbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
@@ -251,13 +253,15 @@ func refreshAllCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 			preItem := dataList[j]
 			if currentItem != nil && preItem != nil {
 				existKey := edbCode + currentItem.DataTime
-
+				// 有计算出来值,那么就从待删除指标中移除
 				if _, ok := existMap[existKey]; !ok {
 					currentDate, _ := time.ParseInLocation(utils.FormatDate, currentItem.DataTime, time.Local)
 					timestamp := currentDate.UnixNano() / 1e6
 					timestampStr := fmt.Sprintf("%d", timestamp)
 					val := HbzDiv(currentItem.Value, preItem.Value)
 					if val != "" {
+						// 有计算出来值,那么就从待删除指标中移除
+						delete(removeDateMap, currentItem.DataTime)
 						if existVal, findOk := existDataMap[currentItem.DataTime]; !findOk {
 							addSql += GetAddSql(edbInfoIdStr, edbCode, currentItem.DataTime, timestampStr, val)
 							isAdd = true
@@ -279,6 +283,25 @@ func refreshAllCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 			}
 		}
 	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 22 - 0
models/edb_data_calculate_ljzzj.go

@@ -288,8 +288,10 @@ func (obj Ljzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, fromE
 		return err
 	}
 	dataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	existDataMap := make(map[string]string)
 	for yk, yv := range yearMap {
@@ -362,6 +364,7 @@ func (obj Ljzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, fromE
 				}
 			}
 			if date != "" {
+				delete(removeDateMap, date)
 				tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 				if !isAdd {
 					isAdd = newAdd
@@ -373,6 +376,25 @@ func (obj Ljzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, fromE
 			}
 		}
 	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 22 - 0
models/edb_data_calculate_ljzzy.go

@@ -257,8 +257,10 @@ func refreshAllCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSource int,
 		return err
 	}
 	dataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	existDataMap := make(map[string]string)
 	for yk, yv := range yearMap {
@@ -352,6 +354,7 @@ func refreshAllCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSource int,
 				//		}
 				//	}
 				//}
+				delete(removeDateMap, date)
 				tmpSql, newAdd, tmpErr := calculateLjzzy(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 				if !isAdd {
 					isAdd = newAdd
@@ -363,6 +366,25 @@ func refreshAllCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSource int,
 			}
 		}
 	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 10 - 6
models/edb_data_calculate_percentile.go

@@ -443,21 +443,25 @@ func (obj Percentile) getPercentileData(fromEdbInfo *EdbInfo, calculateValue int
 	// 百分位数据个数算法
 	// 数据区间第一个和最后一个数据点的时间和数据分别为(T1,S1)(T2,S2); N=T1到T2指标数据个数, n=小于等于S2的数据个数
 	// 个数百分位=(n-1)/(N-1)
-	maxDay := len(dataList) // 往前找数据的边界
+	var firstDate time.Time
+	if len(dataList) > 0 {
+		d, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+		firstDate = d
+	}
 	if percentType == utils.PercentCalculateTypeNum {
 		for i, d := range dataList {
 			// T2为当前日期
 			s2 := decimal.NewFromFloat(d.Value)
 			t2, _ := time.ParseInLocation(utils.FormatDate, d.DataTime, time.Local)
 
-			// 计算N和n
+			// 往前找(时间长度)个有数据的
 			var bigN, tinyN int
-			for k := 0; k < maxDay; k++ {
-				// 往前找(时间长度)个有数据的, N理论上只有最前面几个日期<calculateDay, 后面的N=calculateDay
-				if bigN >= calculateDay {
+			for k := 0; k < calculateDay; k++ {
+				tp := t2.AddDate(0, 0, -k)
+				if tp.Before(firstDate) {
 					break
 				}
-				preVal, preOk := dataMap[t2.AddDate(0, 0, -k)]
+				preVal, preOk := dataMap[tp]
 				if !preOk {
 					continue
 				}

+ 404 - 0
models/edb_data_calculate_phase_shift.go

@@ -0,0 +1,404 @@
+package models
+
+import (
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddCalculatePhaseShift 期数移位
+func AddCalculatePhaseShift(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculatePhaseShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 前端那边让后端处理。。。
+	if req.Frequency == "日度" && req.MoveFrequency == "" {
+		return nil, errors.New("日度指标,移动频率不能为空")
+	}
+	switch req.Frequency {
+	case "周度":
+		req.MoveFrequency = "周"
+	case "旬度":
+		req.MoveFrequency = "旬"
+	case "月度":
+		req.MoveFrequency = "月"
+	case "季度":
+		req.MoveFrequency = "季"
+	case "半年度":
+		req.MoveFrequency = "半年"
+	case "年度":
+		req.MoveFrequency = "年"
+	}
+
+	if req.EdbInfoId <= 0 {
+		edbInfo = new(EdbInfo)
+		edbInfo.Source = utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT
+		edbInfo.SourceName = "期数移位"
+		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.Formula
+		edbInfo.EdbNameEn = req.EdbName
+		edbInfo.UnitEn = req.Unit
+		edbInfo.EdbType = 2
+		edbInfo.Sort = GetAddEdbMaxSortByClassifyId(req.ClassifyId, utils.EDB_INFO_TYPE)
+		edbInfo.MoveType = req.MoveType
+		edbInfo.MoveFrequency = req.MoveFrequency
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			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
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT, utils.DATA_SUB_SOURCE_EDB)
+		fmt.Println("dataTableName:" + dataTableName)
+		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
+		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
+		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	formulaInt, _ := strconv.Atoi(req.Formula)
+
+	//计算数据
+	err = refreshAllCalculatePhaseShift(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, formulaInt, edbInfo.MoveType, fromEdbInfo, edbInfo.EdbCode, "", "", edbInfo.MoveFrequency)
+
+	return
+}
+
+// EditCalculatePhaseShift 修改期数移位
+func EditCalculatePhaseShift(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEdbInfo *EdbInfo) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculatePhaseShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	switch req.Frequency {
+	case "周度":
+		req.MoveFrequency = "周"
+	case "旬度":
+		req.MoveFrequency = "旬"
+	case "月度":
+		req.MoveFrequency = "月"
+	case "季度":
+		req.MoveFrequency = "季"
+	case "半年度":
+		req.MoveFrequency = "半年"
+	case "年度":
+		req.MoveFrequency = "年"
+	}
+
+	oldEdbInfo := *edbInfo //旧的指标信息
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.MoveType = req.MoveType
+	edbInfo.MoveFrequency = req.MoveFrequency
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.EdbNameEn = req.EdbNameEn
+	edbInfo.UnitEn = req.UnitEn
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "MoveType", "MoveFrequency", "CalculateFormula", "ModifyTime", "EdbNameEn", "UnitEn")
+	if err != nil {
+		return
+	}
+
+	//判断计算指标是否被更换
+	var existCondition string
+	var existPars []interface{}
+	existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+	existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+	count, err := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+	if err != nil {
+		err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+		return
+	}
+
+	if count <= 0 || req.MoveType != oldEdbInfo.MoveType || req.MoveFrequency != oldEdbInfo.MoveFrequency || req.Formula != oldEdbInfo.CalculateFormula {
+		//如果是依赖指标变更,那么需要删除对应的关联指标,并添加新的关系
+		if count <= 0 {
+			//删除,计算指标关联的,基础指标的关联关系
+			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
+			}
+			//关联关系
+			{
+				calculateMappingItem := &EdbInfoCalculateMapping{
+					EdbInfoCalculateMappingId: 0,
+					EdbInfoId:                 edbInfo.EdbInfoId,
+					Source:                    utils.DATA_SOURCE_CALCULATE_PHASE_SHIFT,
+					SourceName:                "期数移位",
+					EdbCode:                   edbInfo.EdbCode,
+					FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+					FromEdbCode:               fromEdbInfo.EdbCode,
+					FromEdbName:               fromEdbInfo.EdbName,
+					FromSource:                fromEdbInfo.Source,
+					FromSourceName:            fromEdbInfo.SourceName,
+					FromTag:                   "",
+					Sort:                      1,
+					CreateTime:                time.Now(),
+					ModifyTime:                time.Now(),
+					FromSubSource:             fromEdbInfo.SubSource,
+				}
+				_, err = to.Insert(calculateMappingItem)
+				if err != nil {
+					return
+				}
+			}
+		}
+
+		//清空原有数据
+		sql := ` DELETE FROM edb_data_calculate_phase_shift WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//计算数据
+		formulaInt, _ := strconv.Atoi(req.Formula)
+		err = refreshAllCalculatePhaseShift(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, formulaInt, edbInfo.MoveType, fromEdbInfo, edbInfo.EdbCode, "", "", edbInfo.MoveFrequency)
+	}
+	return
+}
+
+// RefreshAllCalculatePhaseShift 刷新所有时间移位数据
+func RefreshAllCalculatePhaseShift(edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculatePhaseShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//清空原有数据
+	sql := ` DELETE FROM edb_data_calculate_phase_shift WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = refreshAllCalculatePhaseShift(to, edbInfoId, source, subSource, formulaInt, moveType, fromEdbInfo, edbCode, startDate, endDate, moveFrequency)
+
+	return
+}
+
+// refreshAllCalculatePhaseShift 刷新所有时间移位数据
+func refreshAllCalculatePhaseShift(to orm.TxOrmer, edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+
+	//计算数据
+	dataList, err := GetEdbDataListAllByTo(to, fromEdbInfo.Source, fromEdbInfo.SubSource, FindEdbDataListAllCond{
+		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
+	}
+	fmt.Println("source:", source)
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source, subSource)
+	fmt.Println("dataTableName:", dataTableName)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return err
+	}
+
+	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = ``
+	}
+	fmt.Println("existDataMap:", existDataMap)
+	addSql := ` INSERT INTO edb_data_calculate_phase_shift(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	resultMap := make(map[string]float64)
+	dataLen := len(dataList)
+	var moveNum int
+	for i := 0; i < dataLen; i++ {
+		// step_1 如果 领先/滞后 之后时间key存在,将该key为目标key,填充
+		currentIndex := dataList[i]
+
+		// 领先
+		if moveType != 2 {
+			periods := dataLen - i + formulaInt - 1
+			if periods < dataLen {
+				newIndex := dataList[dataLen-periods-1]
+				resultMap[newIndex.DataTime] = currentIndex.Value
+			} else {
+				moveNum = formulaInt - i
+
+				// 新数据须根据频度补充key
+				currentDate, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+
+				shiftDay := CalculateIntervalDays(moveFrequency, moveNum, currentDate, resultMap, moveType)
+
+				var newDate time.Time
+				if moveFrequency == "年" {
+					newDate = currentDate.AddDate(moveNum, 0, 0)
+				} else {
+					newDate = currentDate.AddDate(0, 0, shiftDay)
+				}
+
+				format := newDate.Format(utils.FormatDate)
+				resultMap[format] = currentIndex.Value
+			}
+		} else {
+			// 滞后
+			periods := dataLen - i - formulaInt
+			if periods > 0 {
+				newIndex := dataList[dataLen-periods]
+				resultMap[newIndex.DataTime] = currentIndex.Value
+			} else {
+				moveNum = formulaInt + 1 - (dataLen - i)
+				// 新数据须根据频度补充key
+				currentDate, _ := time.ParseInLocation(utils.FormatDate, dataList[dataLen-1].DataTime, time.Local)
+
+				shiftDay := CalculateIntervalDays(moveFrequency, moveNum, currentDate, resultMap, moveType)
+
+				var newDate time.Time
+				if moveFrequency == "年" {
+					newDate = currentDate.AddDate(-moveNum, 0, 0)
+				} else {
+					newDate = currentDate.AddDate(0, 0, -shiftDay)
+				}
+
+				format := newDate.Format(utils.FormatDate)
+				resultMap[format] = currentIndex.Value
+			}
+		}
+	}
+
+	for key, value := range resultMap {
+		currentDate, _ := time.ParseInLocation(utils.FormatDate, key, time.Local)
+		timestamp := currentDate.UnixNano() / 1e6
+		timestampStr := fmt.Sprintf("%d", timestamp)
+		addSql += GetAddSql(edbInfoIdStr, edbCode, key, timestampStr, fmt.Sprintf("%f", value))
+		isAdd = true
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func CalculateIntervalDays(moveFrequency string, formulaInt int, baseDate time.Time, resultMap map[string]float64, moveType int) int {
+	var shiftDay int
+	switch moveFrequency {
+	case "自然日":
+		shiftDay = formulaInt
+	case "交易日":
+		shiftDay = utils.CalculateTradingDays(baseDate, formulaInt, resultMap, moveType)
+	case "周":
+		shiftDay = formulaInt * 7
+	case "旬":
+		shiftDay = utils.CalculateDekadTime(baseDate, formulaInt, moveType)
+	case "月":
+		shiftDay = utils.CalculateEndOfMonth(baseDate, formulaInt, moveType)
+	case "季":
+		shiftDay = utils.CalculateEndOfQuarter(baseDate, formulaInt, moveType)
+	case "半年":
+		shiftDay = utils.CalculateEndOfHalfYear(baseDate, formulaInt, moveType)
+	case "年":
+		shiftDay = utils.CalculateEndOfYear(baseDate, formulaInt, moveType)
+	default:
+		shiftDay = formulaInt
+	}
+	return shiftDay
+}

+ 1 - 0
models/edb_data_calculate_qjjs.go

@@ -854,6 +854,7 @@ func GetRangeAnalysisChartDataByEdbInfo(fromEdbInfo *EdbInfo, calculateFormula R
 					dealDataList = append(dealDataList, v)
 				}
 			}
+			newDataList = dealDataList
 		case 2:
 			for i, v := range newDataList {
 				if utils.CompareFloatByOpStrings(calculateFormula.UnNormalDataConf.Formula, v.Value, calculateFormula.UnNormalDataConf.Value) {

+ 22 - 0
models/edb_data_calculate_rjz.go

@@ -236,8 +236,10 @@ func refreshAllCalculateRjz(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *
 		return err
 	}
 	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	fmt.Println("existDataMap:", existDataMap)
 	addSql := ` INSERT INTO edb_data_calculate_rjz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
@@ -251,6 +253,7 @@ func refreshAllCalculateRjz(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *
 			if err != nil {
 				return err
 			}
+			delete(removeDateMap, av)
 			//根据频度计算需要均分的天数
 			days := GetRjzFrequencyDays(currentDate, fromEdbInfo.Frequency)
 			val := rjzDiv(currentItem.Value, days)
@@ -277,6 +280,25 @@ func refreshAllCalculateRjz(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *
 			}
 		}
 	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, utils.DATA_SUB_SOURCE_EDB)
+		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
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 21 - 0
models/edb_data_calculate_tcz.go

@@ -236,8 +236,10 @@ func refreshAllCalculateTcz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 		return err
 	}
 	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 
 	addSql := ` INSERT INTO edb_data_calculate_tcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
@@ -245,6 +247,7 @@ func refreshAllCalculateTcz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 	existAddDataMap := make(map[string]string)
 	for _, av := range dateArr {
 		currentItem := dataMap[av]
+		delete(removeDateMap, av)
 		if currentItem != nil {
 			//当前日期
 			currentDate, err := time.ParseInLocation(utils.FormatDate, av, time.Local)
@@ -428,6 +431,24 @@ func refreshAllCalculateTcz(to orm.TxOrmer, edbInfoId, source, subSource int, fr
 			}
 		}
 	}
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
+
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 223 - 0
models/edb_data_rzd.go

@@ -0,0 +1,223 @@
+// @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 EdbDataRzd struct {
+	edbDataId     int     `orm:"column(edb_data_id);pk"`
+	CreateTime    string  `orm:"column(create_time)"`
+	ModifyTime    string  `orm:"column(modify_time)"`
+	EdbInfoId     int     `orm:"column(edb_info_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(EdbDataRzd))
+}
+
+// GetRzdEdbDataByIndexCodeAndDataTime 根据指标编码和时间获取指标库数据
+func GetRzdEdbDataByIndexCodeAndDataTime(indexCode string, dataTime string) (items []EdbDataRzd, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM edb_data_rzd WHERE edb_code=? AND data_time=?`
+	_, err = o.Raw(sql, indexCode, dataTime).QueryRows(&items)
+	if errors.Is(err, orm.ErrNoRows) {
+		return nil, nil
+	}
+	return
+}
+
+// UpdateRzdEdbDataById 更新指标库数据 须根据指标编码和日期更新 仅适合月度数据
+func UpdateRzdEdbDataById(id int, value float64) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE edb_data_rzd SET value=? WHERE edb_data_id=?`
+	_, err = o.Raw(sql, value, id).Exec()
+	return
+}
+
+// AddRzdEdbDataList 新增指标库数据
+func AddRzdEdbDataList(items []EdbDataRzd) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// AddEdbDataFromRzd 新增指标数据
+func AddEdbDataFromRzd(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 := GetBaseFromRzdDataByCondition(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_rzd (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 RefreshEdbDataFromRzd(edbInfoId int, edbCode, startDate string) (err error) {
+	source := utils.DATA_SOURCE_RZD
+	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 := GetBaseFromRzdDataByCondition(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_rzd(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
+}

+ 16 - 17
models/edb_info.go

@@ -1341,7 +1341,7 @@ func EdbInfoAdd(req *AddEdbInfoParams, serverUrl string, sysUserId int, sysUserR
 		utils.DATA_SOURCE_MANUAL:              "手工数据",
 		utils.DATA_SOURCE_LZ:                  "隆众",
 		utils.DATA_SOURCE_YS:                  "SMM",
-		utils.DATA_SOURCE_GL:                  "钢联",
+		utils.DATA_SOURCE_GL:                  "上海钢联",
 		utils.DATA_SOURCE_ZZ:                  "郑商所",
 		utils.DATA_SOURCE_DL:                  "大商所",
 		utils.DATA_SOURCE_SH:                  "上期所",
@@ -1351,7 +1351,7 @@ func EdbInfoAdd(req *AddEdbInfoParams, serverUrl string, sysUserId int, sysUserR
 		utils.DATA_SOURCE_LT:                  "路透",
 		utils.DATA_SOURCE_COAL:                "中国煤炭市场网",
 		utils.DATA_SOURCE_GOOGLE_TRAVEL:       "our world in data",
-		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "钢联化工",
+		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "上海钢联",
 		utils.DATA_SOURCE_EIA_STEO:            "EIA STEO报告",
 		utils.DATA_SOURCE_COM_TRADE:           "UN",
 		utils.DATA_SOURCE_SCI:                 "SCI",
@@ -1381,7 +1381,7 @@ func EdbInfoAdd(req *AddEdbInfoParams, serverUrl string, sysUserId int, sysUserR
 
 	var sourceIndexName string
 
-	// 钢联化工需要校验下信息是否完整
+	// 上海钢联需要校验下信息是否完整
 	if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
 		indexObj := new(BaseFromMysteelChemicalIndex)
 		tmpItem, tmpErr := indexObj.GetIndexItem(req.EdbCode)
@@ -1534,20 +1534,6 @@ func GetEdbInfoByEdbCodeList(source int, edbCodeList []string) (items []*EdbInfo
 	return
 }
 
-// GetAddEdbMaxSortByClassifyId
-// @Description: 获取添加指标时,该分类下最大的排序(忽略错误信息)
-// @author: Roc
-// @datetime 2024-07-05 09:39:46
-// @param classifyId int
-// @param classifyType uint8
-// @return sort int
-func GetAddEdbMaxSortByClassifyId(classifyId int, classifyType uint8) (sort int) {
-	sort, _ = GetEdbAndClassifyMaxSort(classifyId, classifyType)
-	sort = sort + 1
-
-	return
-}
-
 // EdbInfoExtra 指标额外数据-extra字段
 type EdbInfoExtra struct {
 	ApiExtraPars string `description:"API-额外参数(如同花顺日期序列)"`
@@ -1599,6 +1585,19 @@ func TransEdbInfoDataList2SearchData(items []*EdbDataList) (list []*EdbInfoSearc
 	return
 }
 
+// GetAddEdbMaxSortByClassifyId
+// @Description: 获取添加指标时,该分类下最大的排序(忽略错误信息)
+// @author: Roc
+// @datetime 2024-07-05 09:39:46
+// @param classifyId int
+// @param classifyType uint8
+// @return sort int
+func GetAddEdbMaxSortByClassifyId(classifyId int, classifyType uint8) (sort int) {
+	sort, _ = GetEdbAndClassifyMaxSort(classifyId, classifyType)
+	sort = sort + 1
+	return
+}
+
 type SortEdbDataList []*EdbInfoSearchData
 
 func (m SortEdbDataList) Len() int {

+ 1 - 1
models/edb_info_record.go

@@ -21,7 +21,7 @@ type EdbInfoRecord struct {
 	Timestamp           int64     `description:"时间戳"`
 }
 
-func AddEditEdbInfoRcord(edbRecord *EdbInfoRecord) (e error) {
+func AddEditEdbInfoRecord(edbRecord *EdbInfoRecord) (e error) {
 	o := orm.NewOrm()
 	_, e = o.Insert(edbRecord)
 	return

+ 3 - 3
models/edb_info_relation.go

@@ -97,14 +97,14 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 
 	if len(refreshEdbInfoIds) > 0 {
 		// todo 更新指标的刷新状态
-		sql = ` UPDATE edb_info SET no_update = 0 WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
-		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		sql = ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
 	}
 
-	//更新数据源钢联化工指标
+	//更新数据源上海钢联指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
 		sql = ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`

+ 21 - 21
models/edb_ths_hf.go

@@ -417,11 +417,11 @@ func (obj EdbThsHf) refreshByMongo(edbInfo *EdbInfo, edbBaseMapping *BaseFromEdb
 	}
 
 	existDataMap := make(map[string]*mgo.EdbDataThsHf)
-	removeDataTimeMap := make(map[string]bool) //需要移除的日期数据
+	//removeDataTimeMap := make(map[string]bool) //需要移除的日期数据
 	for _, v := range existDataList {
 		tmpDate := v.DataTime.Format(utils.FormatDate)
 		existDataMap[tmpDate] = v
-		removeDataTimeMap[tmpDate] = true
+		//removeDataTimeMap[tmpDate] = true
 	}
 
 	// 待添加的数据集
@@ -483,25 +483,25 @@ func (obj EdbThsHf) refreshByMongo(edbInfo *EdbInfo, edbBaseMapping *BaseFromEdb
 		coll := mogDataObj.GetCollection()
 
 		//删除已经不存在的指标数据(由于该指标当日的数据删除了)
-		{
-			removeDateList := make([]time.Time, 0)
-			for dateTime := range removeDataTimeMap {
-				//获取已存在的所有数据
-				tmpDateTime, e := time.ParseInLocation(utils.FormatDate, dateTime, time.Local)
-				if e != nil {
-					err = fmt.Errorf("tmpDateTime parse err: %v", e)
-					return
-				}
-				removeDateList = append(removeDateList, tmpDateTime)
-			}
-			removeNum := len(removeDateList)
-			if removeNum > 0 {
-				if e = mogDataObj.RemoveManyByColl(coll, bson.M{"edb_code": edbInfo.EdbCode, "data_time": bson.M{"$in": removeDateList}}); e != nil {
-					err = fmt.Errorf("RemoveManyByColl, err: %v", e)
-					return
-				}
-			}
-		}
+		//{
+		//	removeDateList := make([]time.Time, 0)
+		//	for dateTime := range removeDataTimeMap {
+		//		//获取已存在的所有数据
+		//		tmpDateTime, e := time.ParseInLocation(utils.FormatDate, dateTime, time.Local)
+		//		if e != nil {
+		//			err = fmt.Errorf("tmpDateTime parse err: %v", e)
+		//			return
+		//		}
+		//		removeDateList = append(removeDateList, tmpDateTime)
+		//	}
+		//	removeNum := len(removeDateList)
+		//	if removeNum > 0 {
+		//		if e = mogDataObj.RemoveManyByColl(coll, bson.M{"edb_code": edbInfo.EdbCode, "data_time": bson.M{"$in": removeDateList}}); e != nil {
+		//			err = fmt.Errorf("RemoveManyByColl, err: %v", e)
+		//			return
+		//		}
+		//	}
+		//}
 
 		// 插入新数据
 		if len(addDataList) > 0 {

+ 16 - 1
models/future_good/future_good_edb_data.go

@@ -1,6 +1,7 @@
 package future_good
 
 import (
+	"eta/eta_index_lib/services/alarm_msg"
 	"eta/eta_index_lib/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
@@ -84,9 +85,16 @@ func GetFutureGoodEdbDataList(condition string, pars []interface{}) (list []*Fut
 	return
 }
 
-// AddEdbDataFromThs 添加Ths商品指标数据
+// AddEdbDataFromThs 添加同花顺商品指标数据
 func AddEdbDataFromThs(futureGoodEdbInfoId int, edbCode string, item FutureGoodDataFromThs) (err error) {
+	var errMsg string
 	o := orm.NewOrm()
+	defer func() {
+		if err != nil {
+			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", " 同花顺数据获取失败:err:"+errMsg, utils.EmailSendToUsers)
+			go alarm_msg.SendAlarmMsg("wind商品数据获取失败:err:"+errMsg, 3)
+		}
+	}()
 
 	var isAdd bool
 	addSql := ` INSERT INTO future_good_edb_data(future_good_edb_info_id,future_good_edb_code,data_time,trade_code,open,high,low,close,volume,amt,oi,settle,create_time,modify_time,data_timestamp) values `
@@ -97,6 +105,7 @@ func AddEdbDataFromThs(futureGoodEdbInfoId int, edbCode string, item FutureGoodD
 		eDate := table.Time[k]
 		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
 		if err != nil {
+			errMsg = " time.Parse :" + err.Error()
 			return err
 		}
 		timestamp := dataTime.UnixNano() / 1e6
@@ -121,6 +130,7 @@ func AddEdbDataFromThs(futureGoodEdbInfoId int, edbCode string, item FutureGoodD
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()
 		if err != nil {
+			errMsg = " tx.Exec Err :" + err.Error()
 			return
 		}
 	}
@@ -134,6 +144,11 @@ type RefreshFutureEdbEdbInfoReq struct {
 	StartDate           string `description:"开始日期"`
 }
 
+// RefreshFutureChartInfoReq 刷新商品指标请求
+type RefreshFutureChartInfoReq struct {
+	ChartInfoId int `description:"图表ID"`
+}
+
 // RefreshFutureGoodEdbDataFromThs 刷新wind期货指标数据
 func RefreshFutureGoodEdbDataFromThs(futureGoodEdbInfoId int, edbCode, startDate string, item FutureGoodDataFromThs) (err error) {
 	o := orm.NewOrm()

+ 21 - 1
models/predict_edb_data_calculate_hbz.go

@@ -211,8 +211,10 @@ func refreshAllPredictCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource
 		return
 	}
 	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	addSql := ` INSERT INTO edb_data_predict_calculate_hbz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
@@ -227,13 +229,14 @@ func refreshAllPredictCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource
 			preItem := dataList[j]
 			if currentItem != nil && preItem != nil {
 				existKey := edbCode + currentItem.DataTime
-
 				if _, ok := existMap[existKey]; !ok {
 					currentDate, _ := time.ParseInLocation(utils.FormatDate, currentItem.DataTime, time.Local)
 					timestamp := currentDate.UnixNano() / 1e6
 					timestampStr := fmt.Sprintf("%d", timestamp)
 					val := HbzDiv(currentItem.Value, preItem.Value)
 					if val != "" {
+						// 有计算出来值,那么就从待删除指标中移除
+						delete(removeDateMap, currentItem.DataTime)
 						if fromEdbInfo.LatestDate == currentItem.DataTime {
 							latestValueDecimal, tmpErr := decimal.NewFromString(val)
 							if tmpErr != nil {
@@ -267,6 +270,23 @@ func refreshAllPredictCalculateHbz(to orm.TxOrmer, edbInfoId, source, subSource
 			}
 		}
 	}
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 23 - 1
models/predict_edb_data_calculate_ljzzj.go

@@ -287,8 +287,10 @@ func (obj PredictLjzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 		return
 	}
 	dataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	existDataMap := make(map[string]string)
 	for yk, yv := range yearMap {
@@ -331,7 +333,8 @@ func (obj PredictLjzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 								a := decimal.NewFromFloat(dataTwoItem.Value)
 								b := decimal.NewFromFloat(2.0)
 								val, _ = a.Div(b).Float64()
-
+								// 有计算出来值,那么就从待删除指标中移除
+								delete(removeDateMap, date)
 								tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 								if !isAdd {
 									isAdd = newAdd
@@ -362,6 +365,8 @@ func (obj PredictLjzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 				}
 			}
 			if date != "" {
+				// 有计算出来值,那么就从待删除指标中移除
+				delete(removeDateMap, date)
 				tmpSql, newAdd, tmpErr := obj.calculate(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 				if !isAdd {
 					isAdd = newAdd
@@ -374,6 +379,23 @@ func (obj PredictLjzzj) refresh(to orm.TxOrmer, edbInfoId, source, subSource int
 			}
 		}
 	}
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 21 - 0
models/predict_edb_data_calculate_ljzzy.go

@@ -225,8 +225,10 @@ func refreshAllPredictCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSourc
 		return
 	}
 	dataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 	existDataMap := make(map[string]string)
 
@@ -270,6 +272,7 @@ func refreshAllPredictCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSourc
 								a := decimal.NewFromFloat(dataTwoItem.Value)
 								b := decimal.NewFromFloat(2.0)
 								val, _ = a.Div(b).Float64()
+								delete(removeDateMap, date)
 								tmpSql, newAdd, tmpErr := calculateLjzzy(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 								if !isAdd {
 									isAdd = newAdd
@@ -321,6 +324,7 @@ func refreshAllPredictCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSourc
 				//		}
 				//	}
 				//}
+				delete(removeDateMap, date)
 				tmpSql, newAdd, tmpErr := calculateLjzzy(edbInfoId, date, edbInfoIdStr, edbCode, dataTableName, addSql, val, dataMap, existDataMap, to)
 				if !isAdd {
 					isAdd = newAdd
@@ -333,6 +337,23 @@ func refreshAllPredictCalculateLjzzy(to orm.TxOrmer, edbInfoId, source, subSourc
 			}
 		}
 	}
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = to.Raw(addSql).Exec()

+ 325 - 0
models/predict_edb_data_calculate_phase_shift.go

@@ -0,0 +1,325 @@
+package models
+
+import (
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculatePhaseShift 期数移位
+func SavePredictCalculatePhaseShift(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName, lang string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			fmt.Println("SavePredictCalculateTimeShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 前端那边让后端处理。。。
+	if req.Frequency == "日度" && req.MoveFrequency == "" {
+		return nil, "", 0, errors.New("日度指标,移动频率不能为空")
+	}
+	switch req.Frequency {
+	case "周度":
+		req.MoveFrequency = "周"
+	case "旬度":
+		req.MoveFrequency = "旬"
+	case "月度":
+		req.MoveFrequency = "月"
+	case "季度":
+		req.MoveFrequency = "季"
+	case "半年度":
+		req.MoveFrequency = "半年"
+	case "年度":
+		req.MoveFrequency = "年"
+	}
+
+	if req.EdbInfoId <= 0 {
+		edbInfo = new(EdbInfo)
+		edbInfo.EdbInfoType = 1
+		edbInfo.Source = utils.DATA_SOURCE_PREDICT_CALCULATE_PHASE_SHIFT
+		edbInfo.SourceName = "预测期数位移"
+		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.Formula
+		edbInfo.EdbType = 2
+		edbInfo.MoveType = req.MoveType
+		edbInfo.MoveFrequency = req.MoveFrequency
+		edbInfo.EdbNameEn = req.EdbName
+		edbInfo.UnitEn = req.Unit
+		edbInfo.Sort = GetAddEdbMaxSortByClassifyId(req.ClassifyId, utils.PREDICT_EDB_INFO_TYPE)
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+		//关联关系
+		{
+			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
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+		latestDateStr = edbInfo.LatestDate
+		latestValue = edbInfo.LatestValue
+		oldEdbInfo := *edbInfo //旧的指标信息
+
+		//修改指标信息
+		switch 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.Frequency = req.Frequency
+		edbInfo.ClassifyId = req.ClassifyId
+		edbInfo.MoveType = req.MoveType
+		edbInfo.MoveFrequency = req.MoveFrequency
+		edbInfo.CalculateFormula = req.Formula
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "MoveType", "MoveFrequency", "CalculateFormula", "ModifyTime", "EdbNameEn", "UnitEn")
+		if err != nil {
+			return
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+
+		var count int
+		count, err = GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if err != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+			return
+		}
+
+		if count <= 0 || req.MoveType != oldEdbInfo.MoveType || req.MoveFrequency != oldEdbInfo.MoveFrequency || req.Formula != oldEdbInfo.CalculateFormula {
+			//如果是依赖指标变更,那么需要删除对应的关联指标,并添加新的关系
+			if count <= 0 {
+				//删除,计算指标关联的,基础指标的关联关系
+				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
+				}
+				//关联关系
+				{
+					calculateMappingItem := &EdbInfoCalculateMapping{
+						EdbInfoCalculateMappingId: 0,
+						EdbInfoId:                 edbInfo.EdbInfoId,
+						Source:                    utils.DATA_SOURCE_PREDICT_CALCULATE_PHASE_SHIFT,
+						SourceName:                "预测期数位移",
+						EdbCode:                   edbInfo.EdbCode,
+						FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+						FromEdbCode:               fromEdbInfo.EdbCode,
+						FromEdbName:               fromEdbInfo.EdbName,
+						FromSource:                fromEdbInfo.Source,
+						FromSourceName:            fromEdbInfo.SourceName,
+						FromTag:                   "",
+						Sort:                      1,
+						CreateTime:                time.Now(),
+						ModifyTime:                time.Now(),
+					}
+					_, err = to.Insert(calculateMappingItem)
+					if err != nil {
+						return
+					}
+				}
+			}
+
+			//清空原有数据
+			sql := ` DELETE FROM edb_data_predict_calculate_phase_shift WHERE edb_info_id = ? `
+			_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+			if err != nil {
+				return
+			}
+		} else {
+			return
+		}
+	}
+
+	formulaInt, _ := strconv.Atoi(req.Formula)
+
+	//计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculatePhaseShift(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, formulaInt, edbInfo.MoveType, fromEdbInfo, edbInfo.EdbCode, "", "", edbInfo.MoveFrequency)
+
+	return
+}
+
+// RefreshAllPredictCalculatePhaseShift 刷新所有时间移位数据
+func RefreshAllPredictCalculatePhaseShift(edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllPredictCalculatePhaseShift,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculatePhaseShift(to, edbInfoId, source, subSource, formulaInt, moveType, fromEdbInfo, edbCode, startDate, endDate, moveFrequency)
+	return
+}
+
+// refreshAllPredictCalculatePhaseShift 刷新所有时间移位数据
+func refreshAllPredictCalculatePhaseShift(to orm.TxOrmer, edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, startDate)
+	if err != nil {
+		return
+	}
+
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+
+	addSql := ` INSERT INTO edb_data_predict_calculate_phase_shift(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	resultMap := make(map[string]float64)
+	dataLen := len(dataList)
+	var moveNum int
+	for i := 0; i < dataLen; i++ {
+		// step_1 如果 领先/滞后 之后时间key存在,将该key为目标key,填充
+		currentIndex := dataList[i]
+
+		// 领先
+		if moveType != 2 {
+			periods := dataLen - i + formulaInt - 1
+			if periods < dataLen {
+				newIndex := dataList[dataLen-periods-1]
+				resultMap[newIndex.DataTime] = currentIndex.Value
+
+				if strings.Contains(currentIndex.DataTime, fromEdbInfo.LatestDate) {
+					latestDateStr = newIndex.DataTime
+				}
+			} else {
+				moveNum = formulaInt - i
+
+				// 新数据须根据频度补充key
+				currentDate, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+
+				shiftDay := CalculateIntervalDays(moveFrequency, moveNum, currentDate, resultMap, moveType)
+
+				var newDate time.Time
+				if moveFrequency == "年" {
+					newDate = currentDate.AddDate(moveNum, 0, 0)
+				} else {
+					newDate = currentDate.AddDate(0, 0, shiftDay)
+				}
+
+				format := newDate.Format(utils.FormatDate)
+
+				if strings.Contains(currentIndex.DataTime, fromEdbInfo.LatestDate) {
+					latestDateStr = format
+				}
+				resultMap[format] = currentIndex.Value
+			}
+		} else {
+			// 滞后
+			periods := dataLen - i - formulaInt
+			if periods > 0 {
+				newIndex := dataList[dataLen-periods]
+				resultMap[newIndex.DataTime] = currentIndex.Value
+
+				if strings.Contains(currentIndex.DataTime, fromEdbInfo.LatestDate) {
+					latestDateStr = newIndex.DataTime
+				}
+			} else {
+				moveNum = formulaInt + 1 - (dataLen - i)
+				// 新数据须根据频度补充key
+				currentDate, _ := time.ParseInLocation(utils.FormatDate, dataList[dataLen-1].DataTime, time.Local)
+
+				shiftDay := CalculateIntervalDays(moveFrequency, moveNum, currentDate, resultMap, moveType)
+
+				var newDate time.Time
+				if moveFrequency == "年" {
+					newDate = currentDate.AddDate(-moveNum, 0, 0)
+				} else {
+					newDate = currentDate.AddDate(0, 0, -shiftDay)
+				}
+
+				format := newDate.Format(utils.FormatDate)
+				resultMap[format] = currentIndex.Value
+
+				if strings.Contains(currentIndex.DataTime, fromEdbInfo.LatestDate) {
+					latestDateStr = format
+				}
+			}
+		}
+	}
+
+	for key, value := range resultMap {
+		currentDate, _ := time.ParseInLocation(utils.FormatDate, key, time.Local)
+		timestamp := currentDate.UnixNano() / 1e6
+		timestampStr := fmt.Sprintf("%d", timestamp)
+		addSql += GetAddSql(edbInfoIdStr, edbCode, key, timestampStr, fmt.Sprintf("%f", value))
+		isAdd = true
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 20 - 0
models/predict_edb_data_calculate_tcz.go

@@ -402,8 +402,10 @@ func RefreshAllPredictCalculateTcz(edbInfoId, source, subSource int, fromEdbInfo
 		return
 	}
 	existDataMap := make(map[string]string)
+	removeDateMap := make(map[string]bool) //需要移除的日期
 	for _, v := range existDataList {
 		existDataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = true
 	}
 
 	addSql := ` INSERT INTO edb_data_predict_calculate_tcz(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
@@ -412,6 +414,7 @@ func RefreshAllPredictCalculateTcz(edbInfoId, source, subSource int, fromEdbInfo
 	for _, av := range dateArr {
 		currentItem := dataMap[av]
 		if currentItem != nil {
+			delete(removeDateMap, av)
 			//当前日期
 			currentDate, tmpErr := time.ParseInLocation(utils.FormatDate, av, time.Local)
 			if tmpErr != nil {
@@ -674,6 +677,23 @@ func RefreshAllPredictCalculateTcz(edbInfoId, source, subSource int, fromEdbInfo
 			}
 		}
 	}
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		tableName := GetEdbDataTableName(source, subSource)
+		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
+		}
+	}
 	if isAdd {
 		addSql = strings.TrimRight(addSql, ",")
 		_, err = o.Raw(addSql).Exec()

+ 90 - 12
models/trade_analysis/trade_analysis.go

@@ -182,6 +182,45 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 	if len(contracts) == 0 || len(companies) == 0 {
 		return
 	}
+	condBuy := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsBuy := make([]interface{}, 0)
+	parsBuy = append(parsBuy, classifyName, contracts)
+
+	condSold := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsSold := make([]interface{}, 0)
+	parsSold = append(parsSold, classifyName, contracts)
+
+	// 是否含有TOP20
+	var hasTop bool
+	var condCompanies []string
+	for _, v := range companies {
+		if v == TradeFuturesCompanyTop20 {
+			hasTop = true
+			continue
+		}
+		condCompanies = append(condCompanies, v)
+	}
+	if !hasTop {
+		if len(condCompanies) == 0 {
+			err = fmt.Errorf("查询条件-期货公司异常")
+			return
+		}
+		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsBuy = append(parsBuy, condCompanies)
+		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsSold = append(parsSold, condCompanies)
+	} else {
+		if len(condCompanies) > 0 {
+			condBuy += fmt.Sprintf(` AND (rank = 999 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			condSold += fmt.Sprintf(` AND (rank = 999 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			parsBuy = append(parsBuy, condCompanies)
+			parsSold = append(parsSold, condCompanies)
+		} else {
+			condBuy += ` AND rank = 999`
+			condSold += ` AND rank = 999`
+		}
+	}
+
 	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
 	sql := `SELECT
 			rank,
@@ -195,7 +234,7 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 		FROM
 			%s 
 		WHERE
-			classify_name = ? AND classify_type IN (%s) AND buy_short_name IN (%s)
+			%s
 		UNION ALL
 		(
 		SELECT
@@ -210,11 +249,11 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 		FROM
 			%s 
 		WHERE
-			classify_name = ? AND classify_type IN (%s) AND sold_short_name IN (%s)
+			%s
 		)`
-	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
 	o := orm.NewOrm()
-	_, err = o.Raw(sql, classifyName, contracts, companies, classifyName, contracts, companies).QueryRows(&items)
+	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
 	return
 }
 
@@ -227,6 +266,45 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 	if len(contracts) == 0 || len(companies) == 0 {
 		return
 	}
+	condBuy := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsBuy := make([]interface{}, 0)
+	parsBuy = append(parsBuy, contracts)
+
+	condSold := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsSold := make([]interface{}, 0)
+	parsSold = append(parsSold, contracts)
+
+	// 是否含有TOP20
+	var hasTop bool
+	var condCompanies []string
+	for _, v := range companies {
+		if v == TradeFuturesCompanyTop20 {
+			hasTop = true
+			continue
+		}
+		condCompanies = append(condCompanies, v)
+	}
+	if !hasTop {
+		if len(condCompanies) == 0 {
+			err = fmt.Errorf("查询条件-期货公司异常")
+			return
+		}
+		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsBuy = append(parsBuy, condCompanies)
+		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsSold = append(parsSold, condCompanies)
+	} else {
+		if len(condCompanies) > 0 {
+			condBuy += fmt.Sprintf(` AND (rank = 999 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			condSold += fmt.Sprintf(` AND (rank = 999 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			parsBuy = append(parsBuy, condCompanies)
+			parsSold = append(parsSold, condCompanies)
+		} else {
+			condBuy += ` AND rank = 999`
+			condSold += ` AND rank = 999`
+		}
+	}
+
 	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
 	sql := `SELECT
 			rank,
@@ -239,7 +317,7 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 		FROM
 			%s 
 		WHERE
-			classify_name IN (%s) AND buy_short_name IN (%s)
+			%s
 		UNION ALL
 		(
 		SELECT
@@ -253,11 +331,11 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 		FROM
 			%s 
 		WHERE
-			classify_name IN (%s) AND sold_short_name IN (%s)
+			%s
 		)`
-	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
 	o := orm.NewOrm()
-	_, err = o.Raw(sql, contracts, companies, contracts, companies).QueryRows(&items)
+	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
 	return
 }
 
@@ -283,10 +361,10 @@ const (
 	WarehouseDefaultFrequency = "日度"
 
 	GuangZhouTopCompanyAliasName = "日成交持仓排名" // 广期所TOP20对应的公司名称
-	GuangZhouSeatNameBuy         = "持买单量"       // 广期所指标名称中的多单名称
-	GuangZhouSeatNameSold        = "持卖单量"       // 广期所指标名称中的空单名称
-	GuangZhouTopSeatNameBuy      = "持买单量总计"   // 广期所指标名称中的TOP20多单名称
-	GuangZhouTopSeatNameSold     = "持卖单量总计"   // 广期所指标名称中的TOP20空单名称
+	GuangZhouSeatNameBuy         = "持买单量"    // 广期所指标名称中的多单名称
+	GuangZhouSeatNameSold        = "持卖单量"    // 广期所指标名称中的空单名称
+	GuangZhouTopSeatNameBuy      = "持买单量总计"  // 广期所指标名称中的TOP20多单名称
+	GuangZhouTopSeatNameSold     = "持卖单量总计"  // 广期所指标名称中的TOP20空单名称
 )
 
 const (

+ 144 - 0
routers/commentsRouter.go

@@ -241,6 +241,123 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "AddBatchRzdData",
+            Router: `/add/batch/rzd/data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "AddBatchRzdEdbData",
+            Router: `/add/batch/rzd/edb/data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "AddRzdClassify",
+            Router: `/add/rzd/classify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "AddRzdIndex",
+            Router: `/add/rzd/index`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "GetRzdEdbDataByIndexCodeAndDataTime",
+            Router: `/get/edb/rzd/data/by/code/and/time`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "GetRzdClassifyByName",
+            Router: `/get/rzd/classify/by/name`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "GetRzdEdbInfoByIndexCode",
+            Router: `/get/rzd/edb/info/by/code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "GetRzdIndexByCode",
+            Router: `/get/rzd/index/by/code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "GetRzdIndexDataByIndexIdAndDataTime",
+            Router: `/get/rzd/index/data/by/code/and/time`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "UpdateRzdDataById",
+            Router: `/update/rzd/data/by/id`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:BaseFromRzdController"],
+        beego.ControllerComments{
+            Method: "UpdateRzdEdbDataById",
+            Router: `/update/rzd/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",
@@ -1726,6 +1843,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"],
+        beego.ControllerComments{
+            Method: "HandleExcelData",
+            Router: `/handle/excel_data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:UsdaFasController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:WindController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:WindController"],
         beego.ControllerComments{
             Method: "Add",

+ 42 - 12
routers/router.go

@@ -102,14 +102,14 @@ func init() {
 				&controllers.GieController{},
 			),
 		),
-		beego.NSNamespace("/coal",
+		beego.NSNamespace("/python",
 			beego.NSInclude(
-				&controllers.CoalController{},
+				&controllers.PythonController{},
 			),
 		),
-		beego.NSNamespace("/python",
+		beego.NSNamespace("/coal",
 			beego.NSInclude(
-				&controllers.PythonController{},
+				&controllers.CoalController{},
 			),
 		),
 		beego.NSNamespace("/google_travel",
@@ -117,6 +117,16 @@ func init() {
 				&controllers.GoogleTravelController{},
 			),
 		),
+		beego.NSNamespace("/predict_calculate",
+			beego.NSInclude(
+				&controllers.PredictCalculateController{},
+			),
+		),
+		beego.NSNamespace("/mysteel_chemical",
+			beego.NSInclude(
+				&controllers.MySteelChemicalController{},
+			),
+		),
 		beego.NSNamespace("/mysteel_chemical",
 			beego.NSInclude(
 				&controllers.MySteelChemicalController{},
@@ -222,24 +232,24 @@ func init() {
 				&controllers.CoalMineDataController{},
 			),
 		),
-		beego.NSNamespace("/fenwei",
+		beego.NSNamespace("/gz",
 			beego.NSInclude(
-				&controllers.FenweiController{},
+				&controllers.GzController{},
 			),
 		),
-		beego.NSNamespace("/gz",
+		beego.NSNamespace("/icpi",
 			beego.NSInclude(
-				&controllers.GzController{},
+				&controllers.IcpiController{},
 			),
 		),
-		beego.NSNamespace("/mtjh",
+		beego.NSNamespace("/fenwei",
 			beego.NSInclude(
-				&controllers.MtjhDataController{},
+				&controllers.FenweiController{},
 			),
 		),
-		beego.NSNamespace("/icpi",
+		beego.NSNamespace("/mtjh",
 			beego.NSInclude(
-				&controllers.IcpiController{},
+				&controllers.MtjhDataController{},
 			),
 		),
 		beego.NSNamespace("/edb_refresh",
@@ -292,11 +302,21 @@ func init() {
 				&controllers.BaseFromLyController{},
 			),
 		),
+		beego.NSNamespace("/rzd",
+			beego.NSInclude(
+				&controllers.BaseFromRzdController{},
+			),
+		),
 		beego.NSNamespace("/oilchem",
 			beego.NSInclude(
 				&controllers.OilchemController{},
 			),
 		),
+		beego.NSNamespace("/ly",
+			beego.NSInclude(
+				&controllers.BaseFromLyController{},
+			),
+		),
 		beego.NSNamespace("/trade_analysis",
 			beego.NSInclude(
 				&controllers.BaseFromTradeAnalysisController{},
@@ -307,6 +327,16 @@ func init() {
 				&controllers.HisugarController{},
 			),
 		),
+		beego.NSNamespace("/trade_analysis",
+			beego.NSInclude(
+				&controllers.BaseFromTradeAnalysisController{},
+			),
+		),
+		beego.NSNamespace("/usda_fas",
+			beego.NSInclude(
+				&controllers.UsdaFasController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 2 - 2
services/base_from_calculate.go

@@ -471,7 +471,7 @@ func EdbCalculateBatchSave(req models.EdbInfoCalculateBatchSaveReq, lang string)
 	newEdbInfoEditRecord.Unit = req.Unit
 	newEdbInfoEditRecord.OperateUserId = req.AdminId
 	newEdbInfoEditRecord.OperateUserRealName = req.AdminName
-	err = AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoEditRecord)
+	err = AddEditEdbInfoRecord(oldEdbInfo, newEdbInfoEditRecord)
 	if err != nil {
 		errMsg = "记录基础信息操作变更日志失败,Err:" + err.Error()
 		err = fmt.Errorf("操作记录保存失败")
@@ -897,7 +897,7 @@ func EdbCalculateBatchEdit(req models.EdbInfoCalculateBatchEditReq) (edbInfo *mo
 	newEdbInfoEditRecord.Unit = req.Unit
 	newEdbInfoEditRecord.OperateUserId = req.AdminId
 	newEdbInfoEditRecord.OperateUserRealName = req.AdminName
-	err = AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoEditRecord)
+	err = AddEditEdbInfoRecord(oldEdbInfo, newEdbInfoEditRecord)
 	if err != nil {
 		errMsg = "记录基础信息操作变更日志失败,Err:" + err.Error()
 		err = fmt.Errorf("操作记录保存失败")

+ 4 - 4
services/base_from_mysteel_chemical.go

@@ -188,7 +188,7 @@ func HandleApiIndex(indexCodes []string) (errMsg string, err error) {
 			e := dataObj.ModifyMysteelIndexMaxAndMinInfo(items[0].IndexCode, mysteelIndexMaxItem)
 			if e != nil {
 				fmt.Println("ModifyMysteelIndexMaxAndMinInfo Err:" + e.Error())
-				utils.FileLog.Info("修改钢联化工的最大最小日期失败,Err:" + e.Error())
+				utils.FileLog.Info("修改上海钢联的最大最小日期失败,Err:" + e.Error())
 			}
 		}
 
@@ -537,7 +537,7 @@ type MySteelChemicalApiInfoBody struct {
 	IncludeInfo bool `json:"includeInfo"`
 }
 
-// GetEdbDataFromMySteelChemical 批量获得钢联化工的指标数据
+// GetEdbDataFromMySteelChemical 批量获得上海钢联的指标数据
 func GetEdbDataFromMySteelChemical(indexCodes []string, startTime, endTime, order string) (item *models.MySteelChemicalApiResp, err error) {
 	if utils.MysteelChemicalApiToken == "" {
 		err = errors.New("钢联接口token未配置")
@@ -565,7 +565,7 @@ func GetEdbDataFromMySteelChemical(indexCodes []string, startTime, endTime, orde
 	return
 }
 
-// GetMySteelChemicalIndexNameMap 获取钢联化工的所有指标的信息
+// GetMySteelChemicalIndexNameMap 获取上海钢联的所有指标的信息
 func GetMySteelChemicalIndexNameMap() (indexNameMap map[string]*models.MySteelChemicalApiInfoItem, err error) {
 	if utils.MysteelChemicalApiToken == "" {
 		err = errors.New("钢联接口token未配置")
@@ -656,7 +656,7 @@ func RefreshDataFromMysteelChemical(edbCode, startDate, endDate string) (err err
 
 	terminal, err := GetTerminal(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, tmpIndex.TerminalCode)
 	if err != nil {
-		err = fmt.Errorf("获取钢联化工接口配置出错 Err: %s", err)
+		err = fmt.Errorf("获取上海钢联接口配置出错 Err: %s", err)
 		return
 	}
 

+ 5 - 4
services/base_from_python.go

@@ -87,7 +87,7 @@ func ExecPythonCode(edbCode, reqCode string) (dataMap models.EdbDataFromPython,
 		return
 	}
 	defer func() {
-		os.Remove(pythonFile)
+		//os.Remove(pythonFile)
 	}()
 	_, err = fileHandle.Write([]byte(pythonCodeStr))
 	if err != nil {
@@ -172,7 +172,8 @@ func getPythonFileAbsolutePath(edbCode string) (pythonFile string, err error) {
 // getPythonFrontStr 获取python前面的代码
 func getPythonFrontStr() string {
 	//return "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\nimport json\n\nimport pymysql\nimport pandas as pd\n\nsql_config = {\n    'host': 'rm-uf67kg347rhjfep5c1o.mysql.rds.aliyuncs.com',\n    'port': 3306,#主机号\n    'user': 'hz_technology',#账户名\n    'passwd': 'hongze@2021',#密码\n    'db': 'test_hz_data',\n    'charset': 'utf8mb4',\n    'cursorclass': pymysql.cursors.DictCursor\n}\n\ndb = pymysql.connect(**sql_config)\ndb.autocommit(1)\ncursor = db.cursor()\npandas_fetch_all = pd.read_sql\n\n# 返回数据\nresult = {}\n\n# 格式化返回数据\ndef format_data(data: pd.DataFrame,\n                index_str: str = \"data_time\",\n                value_str: str = \"value\"\n                ) -> pd.DataFrame:\n    \"\"\"\n        Parameters\n        ----------\n        data : pandas的DataFrame数据结构.\n        index_str : 对象下标字符串,在pandas的DataFrame中的列名.\n        value_str : 对象值字符串,在pandas的DataFrame中的列名\n\n        Returns\n        -------\n        DataFrame or Iterator[DataFrame]\n        例子:{'2007-01-09': 3220.0, '2007-01-10': 3230.0}\n\n        \"\"\"\n    index_list = []  # 空列表\n    value_list = []  # 空列表\n\n    for num in range(1, data.index.size):  # 迭代 所有的指标\n        index_list.append(data[index_str][num])\n        value_list.append(data[value_str][num])\n\n    tmp_data = {\n        \"date\": index_list,\n        \"value\": value_list\n    }\n    pd_data = pd.DataFrame(tmp_data)\n    # print(pd_data)\n    return pd_data\n"
-	str := fmt.Sprintf("#!/usr/bin/python\n# -*- coding: UTF-8 -*-\nimport json\n\nimport pymysql\nimport pandas as pd\nfrom pymongo import MongoClient\nimport pytz\n\nsql_config = {\n    'host': '%s',\n    'port': 3306,#主机号\n    'user': '%s',#账户名\n    'passwd': '%s',#密码\n    'db': '%s',\n    'charset': 'utf8mb4',\n    'cursorclass': pymysql.cursors.DictCursor\n}\n\ndb = pymysql.connect(**sql_config)\ndb.autocommit(1)\ncursor = db.cursor()\npandas_fetch_all = pd.read_sql\n\n", utils.PYTHON_MYSQL_HOST, utils.PYTHON_MYSQL_USER, utils.PYTHON_MYSQL_PASSWD, utils.PYTHON_MYSQL_DB)
+	//str := fmt.Sprintf("#!/usr/bin/python\n# -*- coding: UTF-8 -*-\nimport json\n\nimport pymysql\nimport pandas as pd\nfrom pymongo import MongoClient\nimport pytz\n\nsql_config = {\n    'host': '%s',\n    'port': 3306,#主机号\n    'user': '%s',#账户名\n    'passwd': '%s',#密码\n    'db': '%s',\n    'charset': 'utf8mb4',\n    'cursorclass': pymysql.cursors.DictCursor\n}\n\ndb = pymysql.connect(**sql_config)\ndb.autocommit(1)\ncursor = db.cursor()\npandas_fetch_all = pd.read_sql\n\n", utils.PYTHON_MYSQL_HOST, utils.PYTHON_MYSQL_USER, utils.PYTHON_MYSQL_PASSWD, utils.PYTHON_MYSQL_DB)
+	str := fmt.Sprintf("#!/usr/bin/python\n# -*- coding: UTF-8 -*-\nimport json\n\nimport pandas as pd\nfrom sqlalchemy import create_engine\nfrom urllib.parse import quote_plus\nimport pytz\n\nencoded_host = quote_plus(\"%s\")\nencoded_user = quote_plus(\"%s\")\nencoded_password = quote_plus(\"%s\")\nencoded_database = quote_plus(\"%s\")\n\ndb = create_engine(f'mysql+mysqlconnector://{encoded_user}:{encoded_password}@{encoded_host}:3306/{encoded_database}')\npandas_fetch_all = pd.read_sql_query\n\n", utils.PYTHON_MYSQL_HOST, utils.PYTHON_MYSQL_USER, utils.PYTHON_MYSQL_PASSWD, utils.PYTHON_MYSQL_DB)
 
 	// mongo部分
 	if utils.PYTHON_MONGO_HOST != `` {
@@ -191,7 +192,7 @@ func getPythonFrontStr() string {
 // @datetime 2024-05-07 18:38:03
 // @return string
 func getPythonFront2Str() string {
-	str := fmt.Sprintf("\nfrom pymongo import MongoClient\nfrom dateutil.tz import tzlocal\n# MongoDB 连接配置\nmongo_config = {\n    'host': '%s',  # 替换为你的 MongoDB 连接字符串\n    'database': '%s',  # 替换为你的数据库名\n    'collection': '%s',  # 替换为你的集合名\n    'auth_mechanism': '%s'  # 替换为你的认证机制\n}\n\n# 创建 MongoClient 并连接到数据库\nclient = MongoClient(mongo_config['host'], authMechanism=mongo_config['auth_mechanism'])\nmgo_db = client[mongo_config['database']]\ncollection = mgo_db[mongo_config['collection']]\n\n# 定义时区\nutc_tz = pytz.utc\nlocal_tz = tzlocal()  # 本地时区", utils.PYTHON_MONGO_HOST, utils.PYTHON_MONGO_DATABASE, "edb_data_business", utils.PYTHON_MONGO_AUTH_MECHANISM)
+	str := fmt.Sprintf("\nfrom pymongo import MongoClient\nimport pytz\nfrom dateutil.tz import tzlocal\n# MongoDB 连接配置\nmongo_config = {\n    'host': '%s',  # 替换为你的 MongoDB 连接字符串\n    'database': '%s',  # 替换为你的数据库名\n    'collection': '%s',  # 替换为你的集合名\n    'auth_mechanism': '%s'  # 替换为你的认证机制\n}\n\n# 创建 MongoClient 并连接到数据库\nclient = MongoClient(mongo_config['host'], authMechanism=mongo_config['auth_mechanism'])\nmgo_db = client[mongo_config['database']]\ncollection = mgo_db[mongo_config['collection']]\n\n# 定义时区\nutc_tz = pytz.utc\nlocal_tz = tzlocal()  # 本地时区", utils.PYTHON_MONGO_HOST, utils.PYTHON_MONGO_DATABASE, "edb_data_business", utils.PYTHON_MONGO_AUTH_MECHANISM)
 	return str
 }
 
@@ -207,5 +208,5 @@ func getPythonFront3Str() string {
 
 // getPythonLaterStr 获取python结尾的代码
 func getPythonLaterStr() string {
-	return "\n\nprint(\"result=\", result.to_json())\ndb.close()"
+	return "\n\nprint(\"result=\", result.to_json())\n"
 }

+ 309 - 0
services/base_from_usda_fas.go

@@ -0,0 +1,309 @@
+package services
+
+import (
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services/alarm_msg"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// HandleUsdaFasIndex 处理美国农业部的excel数据
+func HandleUsdaFasIndex(req *models.HandleUsdaFasExcelDataReq) (err error) {
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			msg := fmt.Sprint("数据源-美国农业部数据处理失败,err:", strings.Join(errMsgList, "\n"))
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	// 查询所有的一级分类
+	classifyObj := new(models.BaseFromUsdaFasClassify)
+	classifyList, err := classifyObj.GetParentClassify()
+	if err != nil {
+		err = fmt.Errorf("查询一级目录信息失败 Err:%s", err)
+		return
+	}
+	classifyMap := make(map[string]int, 0)
+	for _, v := range classifyList {
+		classifyMap[v.ClassifyName] = int(v.ClassifyId)
+	}
+
+	for _, v := range req.List {
+		if v.IndexName == "" || v.IndexCode == "" {
+			errMsgList = append(errMsgList, fmt.Sprintf("新增指标异常,指标编码%s或者指标ID%s为空:", v.IndexCode, v.IndexName))
+			continue
+		}
+		err = handleUsdaFasIndex(v, req.TerminalCode, classifyMap)
+		if err != nil {
+			errMsgList = append(errMsgList, fmt.Sprintf("新增指标异常,指标编码:%s, Err: %s", v.IndexCode, err))
+			return
+		}
+	}
+	return
+}
+
+func handleUsdaFasIndex(req *models.HandleUsdaFasExcelData, terminalCode string, classifyMap map[string]int) (err error) {
+	indexName := req.IndexName
+	indexCode := req.IndexCode
+	excelDataMap := req.ExcelDataMap
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			msg := fmt.Sprint("数据源-美国农业部数据处理失败,err:", strings.Join(errMsgList, "\n"))
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	indexObj := new(models.BaseFromUsdaFasIndex)
+	dataObj := new(models.BaseFromUsdaFasData)
+	classifyObj := new(models.BaseFromUsdaFasClassify)
+
+	var indexId int64
+
+	addDataList := make([]*models.BaseFromUsdaFasData, 0)
+
+	exitDataMap := make(map[string]*models.BaseFromUsdaFasData)
+
+	// 修改指标信息
+	if indexName == "" {
+		utils.FileLog.Info("未刷新到指标数据:indexName:" + indexName)
+		return
+	}
+	// 判断目录是否存在
+	var classifyId int64
+	now := time.Now()
+	if req.ClassifyName != "" {
+		classifyParentId := 0
+		level := 1
+		classifyParentId, _ = classifyMap[req.ParentClassifyName]
+		if classifyParentId > 0 {
+			level = 2
+		}
+		classifyObj, err = classifyObj.GetByClassifyName(req.ClassifyName)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				//新增分类
+				classifyObj = &models.BaseFromUsdaFasClassify{
+					ClassifyName:    req.ClassifyName,
+					ClassifyNameEn:  req.ClassifyName,
+					ParentId:        classifyParentId,
+					SysUserId:       0,
+					SysUserRealName: "",
+					Level:           level,
+					Sort:            req.ClassifySort,
+					ModifyTime:      now,
+					CreateTime:      now,
+				}
+
+				classifyId, err = classifyObj.Add()
+				if err != nil {
+					err = fmt.Errorf("新增分类失败 Err:%s", err)
+					return
+				}
+				classifyObj.ClassifyId = classifyId
+			} else {
+				return
+			}
+		} else {
+			classifyId = classifyObj.ClassifyId
+			classifyObj.ModifyTime = now
+			//classifyObj.Sort = req.ClassifySort
+			classifyObj.ParentId = classifyParentId
+			//e := classifyObj.Update([]string{"ParentId", "Sort", "ModifyTime"})
+			e := classifyObj.Update([]string{"ParentId", "ModifyTime"})
+			if e != nil {
+				fmt.Println("classifyObj Update Err:" + e.Error())
+				return
+			}
+		}
+	}
+
+	//判断指标是否存在
+	var isAdd int
+	item, err := indexObj.GetByIndexCode(indexCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			isAdd = 1
+			err = nil
+		} else {
+			isAdd = -1
+			err = fmt.Errorf("查询数据源指标库失败 GetByIndexCode Err:%s", err)
+			return
+		}
+	}
+	if item != nil && item.BaseFromUsdaFasIndexId > 0 {
+		fmt.Println("item:", item)
+		isAdd = 2
+	} else {
+		isAdd = 1
+	}
+
+	if isAdd == 1 {
+		indexObj.IndexCode = indexCode
+		indexObj.IndexName = indexName
+		indexObj.Frequency = req.Frequency
+		indexObj.ClassifyId = classifyId
+		indexObj.Unit = req.Unit
+		indexObj.Sort = req.Sort
+		indexObj.ModifyTime = time.Now()
+		indexObj.CreateTime = time.Now()
+		indexObj.TerminalCode = terminalCode
+		indexId, err = indexObj.Add()
+		if err != nil {
+			err = fmt.Errorf("数据源新增美国农业部指标失败 Err:%s", err)
+			return
+		}
+		indexObj.BaseFromUsdaFasIndexId = indexId
+	} else if isAdd == 2 {
+		indexId = item.BaseFromUsdaFasIndexId
+		if item.TerminalCode == `` && terminalCode != `` {
+			item.TerminalCode = terminalCode
+			err = item.Update([]string{"TerminalCode"})
+			if err != nil {
+				err = fmt.Errorf("数据源更新美国农业部指标失败 Err:%s", err)
+				return
+			}
+		}
+
+		indexObj.BaseFromUsdaFasIndexId = item.BaseFromUsdaFasIndexId
+		indexObj.IndexName = indexName
+		indexObj.Frequency = req.Frequency
+		indexObj.ClassifyId = classifyId
+		indexObj.Unit = req.Unit
+		indexObj.Sort = req.Sort
+		indexObj.ModifyTime = time.Now()
+
+		//修改数据
+		updateColsArr := make([]string, 0)
+		updateColsArr = append(updateColsArr, "index_name")
+		updateColsArr = append(updateColsArr, "classify_id")
+		updateColsArr = append(updateColsArr, "unit")
+		updateColsArr = append(updateColsArr, "frequency")
+		updateColsArr = append(updateColsArr, "sort")
+		updateColsArr = append(updateColsArr, "modify_time")
+
+		e := indexObj.Update(updateColsArr)
+		if e != nil {
+			fmt.Println("Index Update Err:" + e.Error())
+			return
+		}
+	}
+
+	//获取已存在的所有数据
+	var exitDataList []*models.BaseFromUsdaFasData
+	exitDataList, err = dataObj.GetByIndexCode(indexCode)
+	if err != nil {
+		err = fmt.Errorf("数据源查询美国农业部指标数据失败 Err:%s", err)
+		return
+	}
+	fmt.Println("exitDataListLen:", len(exitDataList))
+	for _, v := range exitDataList {
+		dateStr := v.DataTime
+		exitDataMap[dateStr] = v
+	}
+
+	// 遍历excel数据,然后跟现有的数据做校验,不存在则入库
+	for date, value := range excelDataMap {
+		if findData, ok := exitDataMap[date]; !ok {
+			_, err = time.ParseInLocation(utils.FormatDate, date, time.Local)
+			if err != nil {
+				err = fmt.Errorf("%s 转换日期格式失败 Err:%s", date, err)
+				return
+			}
+			//if !strings.Contains(value, "#N/A") {
+			var saveDataTime time.Time
+			if strings.Contains(date, "00:00:00") {
+				saveDataTime, err = time.Parse(utils.FormatDateTime, date)
+			} else {
+				saveDataTime, err = time.Parse(utils.FormatDate, date)
+			}
+			if err != nil {
+				err = fmt.Errorf("%s 转换日期格式失败 Err:%s", date, err)
+				continue
+			}
+			timestamp := saveDataTime.UnixNano() / 1e6
+
+			dataItem := new(models.BaseFromUsdaFasData)
+			dataItem.BaseFromUsdaFasIndexId = int(indexId)
+			dataItem.IndexCode = indexCode
+			dataItem.DataTime = date
+			dataItem.Value = value
+			dataItem.CreateTime = time.Now()
+			dataItem.ModifyTime = time.Now()
+			dataItem.DataTimestamp = timestamp
+			addDataList = append(addDataList, dataItem)
+			if len(addDataList) > 500 {
+				err = dataObj.AddMulti(addDataList)
+				if err != nil {
+					err = fmt.Errorf("批量新增指标失败 Err:%s", err)
+					return
+				}
+				addDataList = make([]*models.BaseFromUsdaFasData, 0)
+			}
+			//}
+		} else {
+			if findData != nil && findData.Value != value && !strings.Contains(value, "#N/A") { //修改数据
+				// 过滤0.50和0.5的比较
+				oldV, _ := strconv.ParseFloat(findData.Value, 64)
+				newV, _ := strconv.ParseFloat(value, 64)
+				if oldV == newV {
+					continue
+				}
+				dataObj.BaseFromUsdaFasIndexId = findData.BaseFromUsdaFasIndexId
+				dataObj.Value = value
+				dataObj.ModifyTime = time.Now()
+
+				updateDataColsArr := make([]string, 0)
+				updateDataColsArr = append(updateDataColsArr, "value")
+				updateDataColsArr = append(updateDataColsArr, "modify_time")
+				dataObj.Update(updateDataColsArr)
+			}
+		}
+	}
+
+	if len(addDataList) > 0 {
+		err = dataObj.AddMulti(addDataList)
+		if err != nil {
+			err = fmt.Errorf("批量新增指标失败 Err:%s", err)
+			return
+		}
+	}
+
+	var dateItem *models.EdbInfoMaxAndMinInfo
+	dateItem, err = dataObj.GetMaxAndMinDateByIndexCode(indexCode)
+	if err != nil {
+		err = fmt.Errorf("查询指标最新日期失败 Err:%s", err)
+		return
+	}
+
+	go func() {
+		indexObj.ModifyIndexMaxAndMinDate(indexCode, dateItem)
+	}()
+
+	// 同步刷新ETA指标库的指标
+	{
+		// 获取指标详情
+		baseObj := new(models.BaseFromUsdaFas)
+		var edbInfo *models.EdbInfo
+		edbInfo, err = models.GetEdbInfoByEdbCode(baseObj.GetSource(), indexCode)
+		if err != nil {
+			if err.Error() != utils.ErrNoRow() {
+				errMsgList = append(errMsgList, fmt.Sprint("刷新ETA指标异常,指标编码:", indexCode, err.Error()))
+				return
+			} else {
+				err = nil
+			}
+		}
+
+		// 已经加入到指标库的话,那么就去更新ETA指标库吧
+		if edbInfo != nil {
+			go logic.RefreshBaseEdbInfo(edbInfo, ``)
+		}
+	}
+	return
+}

+ 2 - 2
services/edb_info_record.go

@@ -5,7 +5,7 @@ import (
 	"time"
 )
 
-func AddEditEdbInfoRcord(oldEdbInfo *models.EdbInfo, newEdbInfo *models.EdbInfoEditRecord) (err error) {
+func AddEditEdbInfoRecord(oldEdbInfo *models.EdbInfo, newEdbInfo *models.EdbInfoEditRecord) (err error) {
 	if oldEdbInfo.EdbName != newEdbInfo.EdbName || oldEdbInfo.Frequency != newEdbInfo.Frequency || oldEdbInfo.Unit != newEdbInfo.Unit {
 		edbRecord := new(models.EdbInfoRecord)
 		edbRecord.EdbInfoId = oldEdbInfo.EdbInfoId
@@ -20,7 +20,7 @@ func AddEditEdbInfoRcord(oldEdbInfo *models.EdbInfo, newEdbInfo *models.EdbInfoE
 		ctime := time.Now()
 		edbRecord.CreateTime = ctime
 		edbRecord.Timestamp = ctime.Unix()
-		err = models.AddEditEdbInfoRcord(edbRecord)
+		err = models.AddEditEdbInfoRecord(edbRecord)
 		if err != nil {
 			return
 		}

+ 12 - 12
services/edb_info_stat.go

@@ -56,7 +56,7 @@ func AddEdbInfoUpdateLog(edbInfoId int, updateResult int, updateFailedReason str
 	return
 }
 
-// SetMysteelChemicalEdbInfoUpdateStat 定时统计钢联化工的数据源明细表
+// SetMysteelChemicalEdbInfoUpdateStat 定时统计上海钢联的数据源明细表
 func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 	defer func() {
 		if err != nil {
@@ -71,7 +71,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 	pars = append(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
 	edbList, err := models.GetEdbInfoByCondition(condition, pars, 0)
 	if err != nil {
-		err = fmt.Errorf("查询钢联化工指标信息出错,err: %s", err)
+		err = fmt.Errorf("查询上海钢联指标信息出错,err: %s", err)
 		return
 	}
 	nowTime := time.Now()
@@ -81,7 +81,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 	//查询当日所有钢联指标的终端更新记录
 	updateLogList, err := data_stat.GetEdbUpdateSourceLogByCreateDate(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, today, nextDay)
 	if err != nil {
-		err = fmt.Errorf("查询钢联化工指标终端更新日志报错,err: %s", err)
+		err = fmt.Errorf("查询上海钢联指标终端更新日志报错,err: %s", err)
 		return
 	}
 	fmt.Println(len(updateLogList))
@@ -102,7 +102,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 	//查询当日钢联所有的刷新记录
 	updateStatList, err := data_stat.GetEdbUpdateStatByCondition(statCond, statPars)
 	if err != nil {
-		err = fmt.Errorf("查询钢联化工数据源明细记录统计报错,err: %s", err)
+		err = fmt.Errorf("查询上海钢联数据源明细记录统计报错,err: %s", err)
 		return
 	}
 	updateStatMap := make(map[int]*data_stat.EdbInfoUpdateStat)
@@ -123,7 +123,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 		//查询所有需要当日刷新的周度指标
 		indexTotal, tErr := indexObj.GetIndexByCondition(cond, tmpPars)
 		if tErr != nil {
-			err = fmt.Errorf("查询钢联化工原始指标报错,err: %s", tErr)
+			err = fmt.Errorf("查询上海钢联原始指标报错,err: %s", tErr)
 			return
 		}
 		for _, v := range indexTotal {
@@ -136,7 +136,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 	//查询所有需要当日刷新的周度指标
 	indexStop, tErr := indexObj.GetIndexByCondition(tmpCond, []interface{}{})
 	if tErr != nil {
-		err = fmt.Errorf("查询钢联化工原始指标报错,err: %s", tErr)
+		err = fmt.Errorf("查询上海钢联原始指标报错,err: %s", tErr)
 		return
 	}
 	for _, v := range indexStop {
@@ -224,7 +224,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 			if len(addList) >= 500 {
 				err = logStat.Add(addList)
 				if err != nil {
-					err = fmt.Errorf("新增钢联化工明细记录报错,err: %s", err)
+					err = fmt.Errorf("新增上海钢联明细记录报错,err: %s", err)
 					return
 				}
 				addList = addList[:0]
@@ -233,7 +233,7 @@ func SetMysteelChemicalEdbInfoUpdateStat(needStat bool) (err error) {
 			if len(modifyList) >= 500 {
 				err = data_stat.UpdateEdbUpdateStatMulti(modifyList)
 				if err != nil {
-					err = fmt.Errorf("更新钢联化工明细记录报错,err: %s", err)
+					err = fmt.Errorf("更新上海钢联明细记录报错,err: %s", err)
 					return
 				}
 				modifyList = modifyList[:0]
@@ -299,7 +299,7 @@ func SetEdbSourceStat(needStat bool) (err error) {
 	//查询当日钢联所有的统计数据
 	updateStatList, err := data_stat.GetEdbUpdateStatByCondition(statCond, statPars)
 	if err != nil {
-		err = fmt.Errorf("查询钢联化工数据源明细记录统计报错,err: %s", err)
+		err = fmt.Errorf("查询上海钢联数据源明细记录统计报错,err: %s", err)
 		return
 	}
 	if !needStat && len(updateStatList) == 0 {
@@ -318,7 +318,7 @@ func SetEdbSourceStat(needStat bool) (err error) {
 	//查询当日钢联所有的统计数据
 	statList, err := data_stat.GetEdbSourceStatByCondition(cond, pars)
 	if err != nil {
-		err = fmt.Errorf("查询钢联化工数据源统计报错,err: %s", err)
+		err = fmt.Errorf("查询上海钢联数据源统计报错,err: %s", err)
 		return
 	}
 	statMap := make(map[string]*data_stat.EdbSourceStat)
@@ -395,7 +395,7 @@ func SetEdbSourceStat(needStat bool) (err error) {
 		if len(addList) >= 500 {
 			err = logStat.Add(addList)
 			if err != nil {
-				err = fmt.Errorf("新增钢联化工统计表报错,err: %s", err)
+				err = fmt.Errorf("新增上海钢联统计表报错,err: %s", err)
 				return
 			}
 			addList = addList[:0]
@@ -404,7 +404,7 @@ func SetEdbSourceStat(needStat bool) (err error) {
 		if len(modifyList) >= 500 {
 			err = data_stat.UpdateEdbSourceStatMulti(modifyList)
 			if err != nil {
-				err = fmt.Errorf("更新钢联化工统计表报错,err: %s", err)
+				err = fmt.Errorf("更新上海钢联统计表报错,err: %s", err)
 				return
 			}
 			modifyList = modifyList[:0]

+ 3 - 3
services/trade_analysis/trade_analysis_data.go

@@ -101,7 +101,7 @@ func GetOriginTradeData(exchange, classifyName string, contracts, companies []st
 	for _, v := range companies {
 		// TOP20用空名称去查询
 		if v == tradeAnalysisModel.TradeFuturesCompanyTop20 {
-			queryCompanies = append(queryCompanies, "")
+			queryCompanies = append(queryCompanies, tradeAnalysisModel.TradeFuturesCompanyTop20)
 			continue
 		}
 		companyName, ok := companyMap[v]
@@ -134,9 +134,9 @@ func GetOriginTradeData(exchange, classifyName string, contracts, companies []st
 	keyDateData := make(map[string]*tradeAnalysisModel.ContractCompanyTradeDataList)
 	keyDateDataExist := make(map[string]bool)
 	for _, v := range originList {
-		// TOP20对应数据库中的空名称
+		// Rank999-对应的是TOP20
 		companyName := v.CompanyName
-		if companyName == "" {
+		if v.Rank == 999 {
 			companyName = tradeAnalysisModel.TradeFuturesCompanyTop20
 		}
 

+ 9 - 42
static/pcsg_task.json

@@ -4,95 +4,62 @@
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRunHist4",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRunHist1",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRunHist2",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRunHistV1",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRun4",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRun6",
     "Frequency": "日度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   },
   {
     "TaskKey": "IDpcsgDailyRun7",
     "Frequency": "日度",
     "VCode": true,
     "ExtraLetter": "O",
-    "IndexNamePrefix": "Open Interest -",
-    "IndexCodeSuffix": ""
-  },
-  {
-    "TaskKey": "IDpcsgDailyRun8",
-    "Frequency": "日度",
-    "VCode": false,
-    "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
-  },
-  {
-    "TaskKey": "IDpcsgDailySnap0000",
-    "Frequency": "日度",
-    "VCode": false,
-    "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": "0000"
-  },
-  {
-    "TaskKey": "IDpcsgDailySnap0330",
-    "Frequency": "日度",
-    "VCode": false,
-    "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": "0330"
+    "IndexNamePrefix": "Open Interest -"
   },
   {
     "TaskKey": "IDpcsgMonthRun2",
     "Frequency": "月度",
     "VCode": false,
     "ExtraLetter": "",
-    "IndexNamePrefix": "",
-    "IndexCodeSuffix": ""
+    "IndexNamePrefix": ""
   }
 ]

+ 202 - 0
utils/common.go

@@ -1500,3 +1500,205 @@ func GetTradingDays(startDate, endDate time.Time) []time.Time {
 	}
 	return tradingDays
 }
+
+// CalculateTradingDays 计算天数 跳过周末
+func CalculateTradingDays(baseDate time.Time, tradingDays int, resultMap map[string]float64, moveType int) int {
+	oldDate := baseDate
+
+	// Move to the next day
+	var moveDays int
+	if moveType != 2 {
+		moveDays = tradingDays
+	} else {
+		moveDays = -tradingDays
+	}
+
+	daysMoved := 0 // 实际移动的工作日数
+	for daysMoved < tradingDays {
+		// 根据 moveType,决定是前进一天还是后退一天
+		if moveDays > 0 {
+			baseDate = baseDate.AddDate(0, 0, 1) // 向后移动一天
+		} else {
+			baseDate = baseDate.AddDate(0, 0, -1) // 向前移动一天
+		}
+
+		// 如果当前日期不是周六或周日,则计入交易日
+		weekday := baseDate.Weekday()
+		if weekday != time.Saturday && weekday != time.Sunday {
+			daysMoved++
+		}
+	}
+
+	// 计算实际天数差(包含跳过周末后的移动天数)
+	subDays := baseDate.Sub(oldDate)
+	days := int(math.Abs(subDays.Hours() / 24))
+
+	return days
+}
+
+// getLastDayOfMonth 获取某个月的最后一天
+func getLastDayOfMonth(t time.Time) time.Time {
+	// 移动到下个月的第一天,然后回退一天得到当前月的最后一天
+	return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()).AddDate(0, 1, -1)
+}
+
+// 获取某年某月的天数
+func daysInMonth(year int, month time.Month) int {
+	if month == time.February {
+		// 闰年处理
+		if (year%4 == 0 && year%100 != 0) || (year%400 == 0) {
+			return 29
+		}
+		return 28
+	}
+	if month == time.April || month == time.June || month == time.September || month == time.November {
+		return 30
+	}
+	return 31
+}
+
+// CalculateEndOfMonth 使用天数计算未来月末的天数差
+/*func CalculateEndOfMonth(baseDate time.Time, months, moveType int) int {
+	// 假设每个月28天,然后算到目标月的下个月
+	var daysToAdd int
+	// 计算目标月的下个月月初
+	if moveType == 2 {
+		daysToAdd = -(28 * months)
+	} else {
+		daysToAdd = 28 * (months + 2)
+	}
+	nextMonth := baseDate.AddDate(0, 0, daysToAdd)
+
+	// 获取目标月月初的第一天
+	firstDayOfNextMonth := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location())
+
+	// 获取目标月的最后一天(即月初减去1天)
+	lastDayOfTargetMonth := firstDayOfNextMonth.AddDate(0, 0, -1)
+
+	// 计算天数差
+	daysDifference := int(math.Abs(lastDayOfTargetMonth.Sub(baseDate).Hours() / 24))
+
+	return daysDifference
+}*/
+
+// CalculateEndOfMonth 计算从 baseDate 开始经过 months 后目标月的最后一天距离 baseDate 的天数差
+func CalculateEndOfMonth(baseDate time.Time, months, moveType int) int {
+	// 初始化目标日期为当前日期
+	targetDate := baseDate
+
+	// 如果 moveType == 2,表示倒退月份;否则为前进月份
+	if moveType == 2 {
+		months = -months
+	}
+
+	// 手动通过天数加减月份
+	for i := 0; i < int(math.Abs(float64(months))); i++ {
+		// 首先将日期调整到当前月份的第一天
+		targetDate = time.Date(targetDate.Year(), targetDate.Month(), 1, 0, 0, 0, 0, targetDate.Location())
+
+		// 根据 moveType 来前进或倒退到下一个月的第一天
+		if months > 0 {
+			// 前进到下一个月的第一天
+			targetDate = targetDate.AddDate(0, 1, 0)
+		} else {
+			// 倒退到上一个月的第一天
+			targetDate = targetDate.AddDate(0, -1, 0)
+		}
+
+		// 如果是倒退,调整为目标月的最后一天
+		if months < 0 {
+			daysInCurrentMonth := daysInMonth(targetDate.Year(), targetDate.Month())
+			targetDate = time.Date(targetDate.Year(), targetDate.Month(), daysInCurrentMonth, 0, 0, 0, 0, targetDate.Location())
+		}
+	}
+
+	// 获取目标月的下个月月初第一天
+	firstDayOfNextMonth := time.Date(targetDate.Year(), targetDate.Month()+1, 1, 0, 0, 0, 0, targetDate.Location())
+
+	// 获取目标月的最后一天(即下个月月初减去一天)
+	lastDayOfTargetMonth := firstDayOfNextMonth.AddDate(0, 0, -1)
+
+	// 计算天数差
+	daysDifference := int(math.Abs(lastDayOfTargetMonth.Sub(baseDate).Hours() / 24))
+
+	return daysDifference
+}
+
+// CalculateDekadTime 计算旬度时间
+func CalculateDekadTime(baseDate time.Time, tradingDays, moveType int) int {
+	// 记录原始日期
+	oldDate := baseDate
+
+	// 计算移动的旬数,1 旬为 10 天
+	var moveDekads int
+	if moveType != 2 {
+		moveDekads = tradingDays
+	} else {
+		moveDekads = -tradingDays
+	}
+
+	// 移动的天数为旬数 * 10,初步移动日期
+	baseDate = baseDate.AddDate(0, 0, moveDekads*10)
+
+	// 调整日期到最近的旬:10号、20号或月末
+	baseDate = adjustToNearestDekad(baseDate)
+
+	// 计算时间差
+	subDays := baseDate.Sub(oldDate)
+	days := int(math.Abs(subDays.Hours() / 24))
+
+	fmt.Printf("最终日期: %s, 总天数差: %d 天\n", baseDate.Format("2006-01-02"), days)
+
+	return days
+}
+
+// adjustToNearestDekad 调整日期到最近的旬
+func adjustToNearestDekad(date time.Time) time.Time {
+	day := date.Day()
+	lastDayOfMonth := getLastDayOfMonth(date).Day()
+
+	// 这里有些无可奈何了,暂时这么写吧。。。需要跟据润 平年根据每个月进行单独处理
+	if day < 5 {
+		dateOneMonthAgo := date.AddDate(0, -1, 0)
+		lastDayOfMonth2 := getLastDayOfMonth(dateOneMonthAgo).Day()
+		return time.Date(date.Year(), date.Month()-1, lastDayOfMonth2, 0, 0, 0, 0, date.Location())
+	} else if day > 5 && day <= 15 {
+		return time.Date(date.Year(), date.Month(), 10, 0, 0, 0, 0, date.Location())
+	} else if day > 11 && day <= 25 {
+		return time.Date(date.Year(), date.Month(), 20, 0, 0, 0, 0, date.Location())
+	} else {
+		return time.Date(date.Year(), date.Month(), lastDayOfMonth, 0, 0, 0, 0, date.Location())
+	}
+}
+
+/*// getLastDayOfMonth 返回指定日期所在月份的最后一天
+func getLastDayOfMonth(date time.Time) time.Time {
+	// 获取下个月的第一天
+	nextMonth := date.AddDate(0, 1, -date.Day()+1)
+	// 下个月第一天减去一天即为当前月的最后一天
+	lastDay := nextMonth.AddDate(0, 0, -1)
+	return lastDay
+}*/
+
+// CalculateEndOfQuarter 计算从当前到未来的季度末的天数差
+func CalculateEndOfQuarter(baseDate time.Time, quarters, moveType int) int {
+	// Move forward `quarters` quarters (3 months per quarter)
+	return CalculateEndOfMonth(baseDate, quarters*3, moveType)
+}
+
+// CalculateEndOfYear 计算从当前到未来的年末的天数差
+func CalculateEndOfYear(baseDate time.Time, years, moveType int) int {
+	// Move forward `years` years
+	return CalculateEndOfMonth(baseDate, years*12, moveType)
+}
+
+// CalculateEndOfHalfYear 计算从当前到未来的半年的天数差
+func CalculateEndOfHalfYear(baseDate time.Time, years, moveType int) int {
+	// Move forward `half years` years
+	return CalculateEndOfMonth(baseDate, years*6, moveType)
+}
+
+// GetCurrentTime 获取当前时间 格式为 2024-08-07 15:29:58
+func GetCurrentTime() string {
+	return time.Now().Format("2006-01-02 15:04:05")
+}

+ 1 - 1
utils/config.go

@@ -74,7 +74,7 @@ var (
 
 	ThsDataMethod           string //同花顺数据获取的方式,app是通过终端;api是通过接口
 	ThsRefreshToken         string // 同花顺的刷新token
-	MysteelChemicalApiToken string // 钢联化工的api数据token
+	MysteelChemicalApiToken string // 上海钢联的api数据token
 )
 
 type WindUrlMap struct {

+ 24 - 19
utils/constants.go

@@ -36,7 +36,7 @@ const (
 	DATA_SOURCE_YS                                              //有色
 	DATA_SOURCE_CALCULATE_HBZ                                   //环比值->12
 	DATA_SOURCE_CALCULATE_HCZ                                   //环差值->13
-	DATA_SOURCE_CALCULATE_BP                                    //变频->14
+	DATA_SOURCE_CALCULATE_BP                                    //变频,2023-2-10 13:56:01调整为"升频"->14
 	DATA_SOURCE_GL                                              //钢联->15
 	DATA_SOURCE_ZZ                                              //郑商所->16
 	DATA_SOURCE_DL                                              //大商所->17
@@ -56,7 +56,7 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE                               //预测指标运算->31
 	DATA_SOURCE_PREDICT_CALCULATE_TBZ                           //预测指标 - 同比值->32
 	DATA_SOURCE_PREDICT_CALCULATE_TCZ                           //预测指标 - 同差值->33
-	DATA_SOURCE_MYSTEEL_CHEMICAL                                //钢联化工->34
+	DATA_SOURCE_MYSTEEL_CHEMICAL                                //上海钢联->34
 	DATA_SOURCE_CALCULATE_CJJX                                  //超季节性->35
 	DATA_SOURCE_EIA_STEO                                        //eia steo报告->36
 	DATA_SOURCE_CALCULATE_NHCC                                  //计算指标(拟合残差)->37
@@ -115,7 +115,11 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE_RANGEANLYSIS       = 90 // 预测指标区间计算->90
 	DATA_SOURCE_LY                                   = 91 // 粮油商务网
 	DATA_SOURCE_TRADE_ANALYSIS                       = 92 // 持仓分析
-	DATA_SOURCE_HISUGAR                              = 93 // 泛糖科技 -> 93
+	DATA_SOURCE_HISUGAR                              = 93 //泛糖科技 -> 93
+	DATA_SOURCE_PREDICT_CALCULATE_PHASE_SHIFT        = 94 //预测指标 - 期数移位
+	DATA_SOURCE_CALCULATE_PHASE_SHIFT                = 95 // 期数移动
+	DATA_SOURCE_USDA_FAS                             = 93 // 泛糖科技 -> 93
+	DATA_SOURCE_RZD                                  = 97 // 睿咨得科技 -> 93
 	DATA_SOURCE_CALCULATE_STL                        = 98 // STL趋势分解 -> 98
 )
 
@@ -135,7 +139,7 @@ const (
 	DATA_SOURCE_NAME_CALCULATE_HBZ                        = `环比值`               //环比值->12
 	DATA_SOURCE_NAME_CALCULATE_HCZ                        = `环差值`               //环差值->13
 	DATA_SOURCE_NAME_CALCULATE_BP                         = `升频`                //变频,2023-2-10 13:56:01调整为"升频"->14
-	DATA_SOURCE_NAME_GL                                   = `钢联`                //钢联->15
+	DATA_SOURCE_NAME_GL                                   = `上海钢联`              //钢联->15
 	DATA_SOURCE_NAME_ZZ                                   = `郑商所`               //郑商所->16
 	DATA_SOURCE_NAME_DL                                   = `大商所`               //大商所->17
 	DATA_SOURCE_NAME_SH                                   = `上期所`               //上期所->18
@@ -154,7 +158,7 @@ const (
 	DATA_SOURCE_NAME_PREDICT_CALCULATE                    = `预测指标运算`            //预测指标运算->31
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_TBZ                = `预测同比`              //预测指标 - 同比值->32
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_TCZ                = `预测同差`              //预测指标 - 同差值->33
-	DATA_SOURCE_NAME_MYSTEEL_CHEMICAL                     = `钢联化工`              //钢联化工->34
+	DATA_SOURCE_NAME_MYSTEEL_CHEMICAL                     = `上海钢联`              //上海钢联->34
 	DATA_SOURCE_NAME_CALCULATE_CJJX                       = `超季节性`              //超季节性->35
 	DATA_SOURCE_NAME_EIA_STEO                             = `EIA STERO报告`       //eia stero报告->36
 	DATA_SOURCE_NAME_CALCULATE_NHCC                       = `拟合残差`              //计算指标(拟合残差)->37
@@ -202,13 +206,14 @@ const (
 	DATA_SOURCE_NAME_CALCULATE_SUM                        = `多指标求和`
 	DATA_SOURCE_NAME_CALCULATE_AVG                        = `多指标求平均`
 	DATA_SOURCE_NAME_BUSINESS                             = `自有数据`
-	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_CALCULATE_RANGEANLYSIS               = `区间计算`   //区间计算->87
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_RANGEANLYSIS       = `预测区间计算` //区间计算->90
 	DATA_SOURCE_NAME_HISUGAR                              = `泛糖科技`   // 泛糖科技 -> 93
+	DATA_SOURCE_NAME_TRADE_ANALYSIS                       = `持仓分析`   // 持仓分析
+	DATA_SOURCE_NAME_USDA_FAS                             = `泛糖科技`   // 泛糖科技 -> 93
 )
 
 // 基础数据初始化日期
@@ -312,17 +317,6 @@ const (
 	EnLangVersion = "en" // 英文语言版本
 )
 
-const (
-	PercentCalculateTypeRange = 0 // 百分位算法类型-数据区间
-	PercentCalculateTypeNum   = 1 // 百分位算法类型-数据个数
-)
-
-const (
-	WindDbWsd = "wsd"
-	ThsDs     = "thsds"
-	ThsHf     = "thshf"
-)
-
 // 指标计算方式
 const (
 	EdbBaseCalculateLjzzy                = 1  // 累计值转月->1
@@ -342,3 +336,14 @@ const (
 	EdbBaseCalculateExponentialSmoothing = 15 // 指数修匀->15
 	EdbBaseCalculateRjz                  = 16 // 日均值->16
 )
+
+const (
+	PercentCalculateTypeRange = 0 // 百分位算法类型-数据区间
+	PercentCalculateTypeNum   = 1 // 百分位算法类型-数据个数
+)
+
+const (
+	WindDbWsd = "wsd"
+	ThsDs     = "thsds"
+	ThsHf     = "thshf"
+)