Browse Source

Merge branch 'aj_guagnzhouqihuo'

tuoling805 1 year ago
parent
commit
b197b14cad

+ 47 - 0
controllers/data_manage/edb_info.go

@@ -1623,6 +1623,53 @@ func (this *EdbInfoController) EdbInfoSearch() {
 				}
 				isAdd = true
 			}
+		} else if source == utils.DATA_SOURCE_GFEX { //广州期货交易所
+			dataItems, err := data_manage.GetEdbDataAllByEdbCode(edbCode, source, subSource, utils.EDB_DATA_LIMIT)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取上期所已存在信息失败,Err:" + err.Error()
+				return
+			}
+
+			if len(dataItems) > 0 {
+				searchItem.EdbCode = edbCode
+				minDate, maxDate, err := data_manage.GetEdbDataGzMaxOrMinDate(edbCode)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取上期所日期信息失败,Err:" + err.Error()
+					return
+				}
+				searchItem.DataList = dataItems
+				searchItem.StartDate = minDate
+				searchItem.EndDate = maxDate
+			} else {
+				respItem, err := data.AddEdbData(source, edbCode)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取失败,Err:" + err.Error()
+					return
+				}
+				if respItem.Ret != 200 {
+					br.Msg = "未搜索到该指标"
+					br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + edbCode
+					return
+				}
+				isAdd = true
+			}
+
+			//获取指标信息
+			indexInfo, err := data_manage.GetBaseInfoFromGzByIndexCode(edbCode)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取上期所指标详情失败,Err:" + err.Error()
+				return
+			}
+
+			if indexInfo != nil {
+				searchItem.Frequency = indexInfo.Frequency
+				searchItem.Unit = indexInfo.Unit
+				searchItem.EdbName = indexInfo.IndexName
+			}
 		} else {
 			// 代码中没有的来源那么从edb_source中找是否有对应的
 			sourceItem := data_manage.EdbSourceIdMap[source]

+ 416 - 0
controllers/data_source/guagnzhouqihuo.go

@@ -0,0 +1,416 @@
+package data_source
+
+import (
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_source"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strconv"
+	"time"
+)
+
+// 广州期货交易所
+type DataSourceController struct {
+	controllers.BaseAuthController
+}
+
+// ComTradeCountryList
+// @Title 获取广州期货交易所分类
+// @Description 获取广州期货交易所分类
+// @Success 200 {object} []data_manage.ComTradeCountryItem
+// @router /gfex/classify/list [get]
+func (this *DataSourceController) GfexClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	classifyList, err := gzqhObj.GetBaseFromTradeGuangzhouClassifyAll()
+	if err != nil {
+		br.Msg = "获取分类失败"
+		br.ErrMsg = "获取分类失败,Err:" + err.Error()
+		return
+	}
+
+	classifyMap := make(map[int][]*data_source.BaseFromTradeGuangzhouClassifyView)
+	for _, v := range classifyList {
+		if v.ParentId == 0 {
+			continue
+		}
+		if items, ok := classifyMap[v.ParentId]; !ok {
+			list := make([]*data_source.BaseFromTradeGuangzhouClassifyView, 0)
+			list = append(list, v)
+			classifyMap[v.ParentId] = list
+		} else {
+			items = append(items, v)
+			classifyMap[v.ParentId] = items
+		}
+	}
+
+	classifyItems := make([]*data_source.BaseFromTradeGuangzhouClassifyView, 0)
+	for _, v := range classifyList {
+		if v.ParentId == 0 {
+			v.Children = classifyMap[v.BaseFromTradeGuangzhouClassifyId]
+			classifyItems = append(classifyItems, v)
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = classifyItems
+}
+
+// ComTradeCountryList
+// @Title 获取广州期货交易所数据最大日期
+// @Description 获取广州期货交易所数据最大日期
+// @Success 200 {object} []data_source.ComTradeCountryItem
+// @router /gfex/max/date [get]
+func (this *DataSourceController) GfexMaxDate() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	maxDate, err := gzqhObj.GetBaseFromTradeGuangzhouMaxDate()
+	if err != nil {
+		br.ErrMsg = "获取最新日期失败,Err:" + err.Error()
+		br.Msg = "获取最新日期失败"
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = maxDate
+}
+
+// ComTradeCountryList
+// @Title 获取广州期货交易所-分类下合约
+// @Description 获取广州期货交易所-分类下合约
+// @Param   BaseFromTradeGuangzhouClassifyId   query   int  true       "分类id"
+// @Param   TradeDate   query   string  true       "日期"
+// @Success 200 {object} []data_source.BaseFromTradeGuangzhouContract
+// @router /gfex/contract [get]
+func (this *DataSourceController) GfexContract() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	baseFromTradeGuangzhouClassifyId, _ := this.GetInt("BaseFromTradeGuangzhouClassifyId", 0)
+	if baseFromTradeGuangzhouClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.Msg = "分类id错误"
+		return
+	}
+	tradeDate := this.GetString("TradeDate")
+	if tradeDate == "" {
+		br.Msg = "参数错误"
+		br.Msg = "交易日期不能为空"
+		return
+	}
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	list, err := gzqhObj.GetBaseFromTradeGuangzhouContract(baseFromTradeGuangzhouClassifyId, tradeDate)
+	if err != nil {
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		br.Msg = "获取数据失败"
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// ComTradeCountryList
+// @Title 获取广州期货交易所-分类下指标信息
+// @Description 获取广州期货交易所-分类下指标信息
+// @Param   BaseFromTradeGuangzhouClassifyId   query   int  true       "分类id"
+// @Param   BaseFromTradeGuangzhouContractId   query   int  true       "合约id"
+// @Param   TradeDate   query   string  true       "日期"
+// @Success 200 {object} []data_source.BaseFromTradeGuangzhouIndexView
+// @router /gfex/index_data [get]
+func (this *DataSourceController) GfexIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	baseFromTradeGuangzhouClassifyId, _ := this.GetInt("BaseFromTradeGuangzhouClassifyId", 0)
+	if baseFromTradeGuangzhouClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id错误"
+		return
+	}
+	tradeDate := this.GetString("TradeDate")
+	if tradeDate == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "交易日期不能为空"
+		return
+	}
+	baseFromTradeGuangzhouContractId, err := this.GetInt("BaseFromTradeGuangzhouContractId", 0)
+	fmt.Println(err)
+	fmt.Println(baseFromTradeGuangzhouContractId)
+
+	var condition string
+	var pars []interface{}
+
+	if baseFromTradeGuangzhouContractId > 0 {
+		condition += `  b.data_time=? `
+		pars = append(pars, tradeDate)
+		condition += ` AND a.base_from_trade_guangzhou_classify_id=? `
+		pars = append(pars, baseFromTradeGuangzhouClassifyId)
+	} else {
+		if baseFromTradeGuangzhouClassifyId == 11 || baseFromTradeGuangzhouClassifyId == 12 { //月度数据处理
+			td, err := time.Parse(utils.FormatDate, tradeDate)
+			if err != nil {
+				br.Msg = "日期格式错误"
+				br.ErrMsg = "日期格式错误,Err:" + err.Error()
+				return
+			}
+			_, monthEndDay := utils.GetMonthStartAndEnd(strconv.Itoa(td.Year()), strconv.Itoa(int(td.Month())))
+			condition += `  a.data_time=? `
+			pars = append(pars, monthEndDay)
+		} else {
+			condition += `  a.data_time=? `
+			pars = append(pars, tradeDate)
+		}
+
+		condition += ` AND b.base_from_trade_guangzhou_classify_id=? `
+		pars = append(pars, baseFromTradeGuangzhouClassifyId)
+	}
+
+	//if baseFromTradeGuangzhouContractId > 0 {
+	//	condition += ` AND c.base_from_trade_guangzhou_contract_id=? `
+	//	pars = append(pars, baseFromTradeGuangzhouContractId)
+	//}
+
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	list, err := gzqhObj.GetBaseFromTradeGuangzhouIndex(condition, pars, baseFromTradeGuangzhouContractId)
+	if err != nil {
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		br.Msg = "获取数据失败"
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// @Title 获取广州期货交易所-一次性交割卖方仓单明细
+// @Description 获取广州期货交易所-一次性交割卖方仓单明细
+// @Param   BaseFromTradeGuangzhouIndexId   query   int  true       "指标id"
+// @Param   TradeDate   query   string  true       "日期"
+// @Success 200 {object} []data_source.BaseFromTradeGuangzhouIndexView
+// @router /gfex/index/detail [get]
+func (this *DataSourceController) GfexIndexDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	baseFromTradeGuangzhouIndexId, _ := this.GetInt("BaseFromTradeGuangzhouIndexId", 0)
+	if baseFromTradeGuangzhouIndexId < 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	tradeDate := this.GetString("TradeDate")
+	if tradeDate == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "交易日期不能为空"
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+
+	condition += ` a.base_from_trade_guangzhou_classify_id = ? `
+	pars = append(pars, baseFromTradeGuangzhouIndexId)
+
+	td, err := time.Parse(utils.FormatDate, tradeDate)
+	if err != nil {
+		br.Msg = "日期格式错误"
+		br.ErrMsg = "日期格式错误,Err:" + err.Error()
+		return
+	}
+	_, monthEndDay := utils.GetMonthStartAndEnd(strconv.Itoa(td.Year()), strconv.Itoa(int(td.Month())))
+	condition += ` AND a.end_date = ? `
+	pars = append(pars, monthEndDay)
+
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	list, err := gzqhObj.GetBaseFromTradeGuangzhouIndexDetail(condition, pars)
+	if err != nil {
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		br.Msg = "获取数据失败"
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// @Title 导出广州期货交易所-一次性交割卖方仓单明细
+// @Description 导出广州期货交易所-一次性交割卖方仓单明细
+// @Param   BaseFromTradeGuangzhouIndexId   query   int  true       "指标id"
+// @Param   TradeDate   query   string  true       "日期"
+// @Success 200  导出成功
+// @router /gfex/index/detail/export [get]
+func (this *DataSourceController) ExportGfexIndexDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	baseFromTradeGuangzhouIndexId, _ := this.GetInt("BaseFromTradeGuangzhouIndexId", 0)
+	if baseFromTradeGuangzhouIndexId < 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	tradeDate := this.GetString("TradeDate")
+	if tradeDate == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "交易日期不能为空"
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+
+	condition += ` a.base_from_trade_guangzhou_classify_id = ? `
+	pars = append(pars, baseFromTradeGuangzhouIndexId)
+
+	td, err := time.Parse(utils.FormatDate, tradeDate)
+	if err != nil {
+		br.Msg = "日期格式错误"
+		br.ErrMsg = "日期格式错误,Err:" + err.Error()
+		return
+	}
+	_, monthEndDay := utils.GetMonthStartAndEnd(strconv.Itoa(td.Year()), strconv.Itoa(int(td.Month())))
+	condition += ` AND a.end_date = ? `
+	pars = append(pars, monthEndDay)
+
+	gzqhObj := new(data_source.BaseFromTradeGuangzhouIndex)
+	list, err := gzqhObj.GetBaseFromTradeGuangzhouIndexDetail(condition, pars)
+	if err != nil {
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		br.Msg = "获取数据失败"
+		return
+	}
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+
+	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+	sheetNew, err := xlsxFile.AddSheet("详情")
+	if err != nil {
+		br.ErrMsg = "导出失败,Err:" + err.Error()
+		br.Msg = "导出失败"
+		return
+	}
+	titleRow := sheetNew.AddRow()
+	titleRow.AddCell().SetValue("指标ID")
+	titleRow.AddCell().SetValue("指标名称")
+	titleRow.AddCell().SetValue("数值")
+	titleRow.AddCell().SetValue("单位")
+	titleRow.AddCell().SetValue("频度")
+	titleRow.AddCell().SetValue("起始日期")
+	titleRow.AddCell().SetValue("最新日期")
+
+	var indexCode string
+	for _, sv := range list {
+		dataRow := sheetNew.AddRow()
+		dataRow.AddCell().SetValue(sv.IndexCode)
+		dataRow.AddCell().SetValue(sv.IndexName)
+		dataRow.AddCell().SetValue(sv.Value)
+		dataRow.AddCell().SetValue(sv.Unit)
+		dataRow.AddCell().SetValue(sv.Frequency)
+		dataRow.AddCell().SetValue(sv.StartDate)
+		dataRow.AddCell().SetValue(sv.EndDate)
+
+		indexCode = sv.IndexCode
+	}
+	err = xlsxFile.Save(downLoadnFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		err = xlsxFile.Save(downLoadnFilePath)
+		if err != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+	fileName := `一次性交割卖方仓单详情`
+	fileName = indexCode[:6] + fileName + `.xlsx` //文件名称
+	this.Ctx.Output.Download(downLoadnFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadnFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+
+}

+ 4 - 2
models/data_manage/edb_data_base.go

@@ -165,6 +165,8 @@ func GetEdbDataTableName(source, subSource int) (tableName string) {
 		tableName = "edb_data_calculate_zdyfx" // 自定义分析->74
 	case utils.DATA_SOURCE_CALCULATE_RJZ: //日均值->75
 		tableName = "edb_data_calculate_rjz"
+	case utils.DATA_SOURCE_GFEX: //广州期货交易所->76
+		tableName = "edb_data_gz"
 	default:
 		edbSource := EdbSourceIdMap[source]
 		if edbSource != nil {
@@ -307,11 +309,11 @@ func GetEdbDataAllByEdbCodes(edbCodes []string, limit int) (items []*EdbInfoSear
 	pars = append(pars, edbCodes)
 	o := orm.NewOrmUsingDB("data")
 
-	sql := ` SELECT * FROM edb_data_ys WHERE edb_code IN (`+ utils.GetOrmInReplace(len(edbCodes)) +`) ORDER BY data_time DESC`
+	sql := ` SELECT * FROM edb_data_ys WHERE edb_code IN (` + utils.GetOrmInReplace(len(edbCodes)) + `) ORDER BY data_time DESC`
 	if limit > 0 {
 		sql += `  LIMIT ?  `
 		pars = append(pars, limit)
 	}
 	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
-}
+}

+ 39 - 0
models/data_manage/edb_data_gz.go

@@ -0,0 +1,39 @@
+package data_manage
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type GzData struct {
+	InputValue string `orm:"column(DATA_VALUE)" description:"日期"`
+	DataTime   string `orm:"column(DATA_DATE)" description:"值"`
+}
+
+func GetEdbDataGzMaxOrMinDate(edbCode string) (minDate, maxDate string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT MIN(data_time) AS minDate,MAX(data_time) AS maxDate FROM edb_data_gz WHERE edb_code=? `
+	err = o.Raw(sql, edbCode).QueryRow(&minDate, &maxDate)
+	return
+}
+
+type GzIndexView struct {
+	BaseFromTradeGuangzhouIndexId    int     `description:"指标id"`
+	BaseFromTradeGuangzhouClassifyId int     `description:"分类id"`
+	IndexCode                        string  `description:"指标编码"`
+	IndexName                        string  `description:"指标名称"`
+	Frequency                        string  `description:"频率"`
+	Unit                             string  `description:"单位"`
+	StartDate                        string  `description:"开始日期"`
+	EndDate                          string  `description:"结束日期"`
+	Value                            float64 `description:"数据"`
+}
+
+// GetBaseInfoFromShByIndexCode 获取指标信息
+func GetBaseInfoFromGzByIndexCode(indexCode string) (item *GzIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_trade_guangzhou_index WHERE index_code=? `
+	sql = fmt.Sprintf(sql)
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}

+ 134 - 0
models/data_source/guagnzhouqihuo.go

@@ -0,0 +1,134 @@
+package data_source
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BaseFromTradeGuangzhouClassify struct {
+	BaseFromTradeGuangzhouClassifyId int       `orm:"column(base_from_trade_guangzhou_classify_id);pk"`
+	ClassifyName                     string    `description:"分类名称"`
+	ClassifyCode                     string    `description:"分类编码"`
+	ParentId                         int       `description:"分类父级id"`
+	ModifyTime                       time.Time `description:"修改时间"`
+	CreateTime                       time.Time `description:"创建时间"`
+}
+
+type BaseFromTradeGuangzhouIndex struct {
+	BaseFromTradeGuangzhouIndexId    int       `orm:"column(base_from_trade_guangzhou_index_id);pk"`
+	BaseFromTradeGuangzhouClassifyId int       `description:"分类id"`
+	IndexCode                        string    `description:"指标编码"`
+	IndexName                        string    `description:"指标名称"`
+	Frequency                        string    `description:"频率"`
+	Unit                             string    `description:"单位"`
+	StartDate                        string    `description:"开始日期"`
+	EndDate                          string    `description:"结束日期"`
+	CreateTime                       time.Time `description:"创建日期"`
+	ModifyTime                       time.Time `description:"修改日期"`
+}
+
+type BaseFromTradeGuangzhouData struct {
+	BaseFromTradeGuangzhouDataId  int       `orm:"column(base_from_trade_guangzhou_data_id);pk"`
+	BaseFromTradeGuangzhouIndexId int       `description:"指标id"`
+	IndexCode                     string    `description:"指标编码"`
+	DataTime                      string    `description:"数据日期"`
+	Value                         float64   `description:"数据值"`
+	QtySub                        float64   `description:"增减"`
+	CreateTime                    time.Time `description:"创建日期"`
+	ModifyTime                    time.Time `description:"修改日期"`
+}
+
+type BaseFromTradeGuangzhouClassifyView struct {
+	BaseFromTradeGuangzhouClassifyId int    `orm:"column(base_from_trade_guangzhou_classify_id);pk"`
+	ClassifyName                     string `description:"分类名称"`
+	ClassifyCode                     string `description:"分类编码"`
+	ParentId                         int    `description:"分类父级id"`
+	Children                         []*BaseFromTradeGuangzhouClassifyView
+}
+
+type BaseFromTradeGuangzhouContract struct {
+	BaseFromTradeGuangzhouContractId int    `orm:"column(base_from_trade_guangzhou_contract_id);pk"`
+	BaseFromTradeGuangzhouClassifyId int    `description:"分类id"`
+	ClassifyCode                     string `description:"分类编码"`
+	Contract                         string `description:"合约编码"`
+	TradeDate                        string `description:"合约日期"`
+}
+
+func (obj *BaseFromTradeGuangzhouIndex) GetBaseFromTradeGuangzhouClassifyAll() (list []*BaseFromTradeGuangzhouClassifyView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_trade_guangzhou_classify`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return list, err
+}
+
+func (obj *BaseFromTradeGuangzhouIndex) GetBaseFromTradeGuangzhouMaxDate() (max_date string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT MAX(a.end_date) AS max_date FROM base_from_trade_guangzhou_index AS a `
+	err = o.Raw(sql).QueryRow(&max_date)
+	return max_date, err
+}
+
+func (obj *BaseFromTradeGuangzhouIndex) GetBaseFromTradeGuangzhouContract(classifyId int, tradeDate string) (list []*BaseFromTradeGuangzhouContract, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_trade_guangzhou_contract AS a WHERE base_from_trade_guangzhou_classify_id=? AND trade_date=? `
+	_, err = o.Raw(sql, classifyId, tradeDate).QueryRows(&list)
+	return
+}
+
+type BaseFromTradeGuangzhouIndexView struct {
+	BaseFromTradeGuangzhouIndexId    int     `description:"指标id"`
+	BaseFromTradeGuangzhouClassifyId int     `description:"分类id"`
+	IndexCode                        string  `description:"指标编码"`
+	IndexName                        string  `description:"指标名称"`
+	Frequency                        string  `description:"频率"`
+	Unit                             string  `description:"单位"`
+	StartDate                        string  `description:"开始日期"`
+	EndDate                          string  `description:"结束日期"`
+	Value                            float64 `description:"数据"`
+}
+
+func (obj *BaseFromTradeGuangzhouIndex) GetBaseFromTradeGuangzhouIndex(condition string, pars []interface{}, baseFromTradeGuangzhouContractId int) (list []*BaseFromTradeGuangzhouIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	if baseFromTradeGuangzhouContractId <= 0 {
+		sql := ` SELECT b.base_from_trade_guangzhou_index_id,b.index_code,b.index_name,b.unit,b.frequency,b.start_date,b.end_date,a.value,c.base_from_trade_guangzhou_contract_id 
+ FROM base_from_trade_guangzhou_data AS a
+INNER JOIN base_from_trade_guangzhou_index AS b ON a.base_from_trade_guangzhou_index_id=b.base_from_trade_guangzhou_index_id
+LEFT JOIN base_from_trade_guangzhou_contract AS c ON b.base_from_trade_guangzhou_classify_id=c.base_from_trade_guangzhou_classify_id
+WHERE `
+		if condition != "" {
+			sql += condition
+		}
+		sql += ` ORDER BY a.index_code ASC `
+		_, err = o.Raw(sql, pars).QueryRows(&list)
+		return
+	} else {
+		condition += ` AND a.base_from_trade_guangzhou_contract_id=? `
+		pars = append(pars, baseFromTradeGuangzhouContractId)
+
+		sql := ` SELECT a.*,b.value 
+ FROM base_from_trade_guangzhou_index AS a
+INNER JOIN base_from_trade_guangzhou_data AS b ON a.base_from_trade_guangzhou_index_id=b.base_from_trade_guangzhou_index_id
+WHERE `
+
+		if condition != "" {
+			sql += condition
+		}
+		sql += ` ORDER BY a.index_code ASC `
+		_, err = o.Raw(sql, pars).QueryRows(&list)
+		return
+	}
+}
+
+func (obj *BaseFromTradeGuangzhouIndex) GetBaseFromTradeGuangzhouIndexDetail(condition string, pars []interface{}) (list []*BaseFromTradeGuangzhouIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT a.*
+ FROM base_from_trade_guangzhou_index AS a
+WHERE `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY a.index_code ASC `
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}

+ 1 - 1
models/english_report_email.go

@@ -141,7 +141,7 @@ WHERE a.is_deleted = 0 `
 // GetEnglishReportEmailList 获取邮箱列表
 func GetEnglishReportEmailList(condition string, pars []interface{}, order string) (list []*EnglishReportEmail, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT * FROM english_report_email WHERE is_deleted = 0 `
+	sql := `SELECT * FROM english_report_email WHERE is_deleted = 0`
 	sql += condition
 	if order != "" {
 		sql += order

+ 54 - 0
routers/commentsRouter.go

@@ -4255,6 +4255,60 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "GfexClassifyList",
+            Router: `/gfex/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "GfexContract",
+            Router: `/gfex/contract`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "GfexIndexDetail",
+            Router: `/gfex/index/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "ExportGfexIndexDetail",
+            Router: `/gfex/index/detail/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "GfexIndexData",
+            Router: `/gfex/index_data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "GfexMaxDate",
+            Router: `/gfex/max/date`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_stat:EdbSourceStatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_stat:EdbSourceStatController"],
         beego.ControllerComments{
             Method: "EdbDeleteLog",

+ 6 - 0
routers/router.go

@@ -18,6 +18,7 @@ import (
 	"eta/eta_api/controllers/data_manage/line_equation"
 	"eta/eta_api/controllers/data_manage/line_feature"
 	"eta/eta_api/controllers/data_manage/supply_analysis"
+	"eta/eta_api/controllers/data_source"
 	"eta/eta_api/controllers/data_stat"
 	"eta/eta_api/controllers/english_report"
 	"eta/eta_api/controllers/eta_trial"
@@ -336,6 +337,11 @@ func init() {
 				&report_approve.ReportApproveFlowController{},
 			),
 		),
+		web.NSNamespace("/data_source",
+			web.NSInclude(
+				&data_source.DataSourceController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 4 - 0
services/data/base_edb_lib.go

@@ -67,6 +67,8 @@ func AddEdbData(source int, edbCode string) (resp *models.BaseResponse, err erro
 		urlStr = "national_statistics/add"
 	case utils.DATA_SOURCE_FUBAO:
 		urlStr = "fubao/add"
+	case utils.DATA_SOURCE_GFEX:
+		urlStr = "gz/add"
 	default:
 		edbSource := data_manage.EdbSourceIdMap[source]
 		if edbSource != nil {
@@ -246,6 +248,8 @@ func RefreshEdbData(edbInfoId, source, subSource int, edbCode, startDate string)
 		urlStr = "national_statistics/refresh"
 	case utils.DATA_SOURCE_FUBAO:
 		urlStr = "fubao/refresh"
+	case utils.DATA_SOURCE_GFEX:
+		urlStr = "gz/refresh"
 	default:
 		edbSource := data_manage.EdbSourceIdMap[source]
 		if edbSource != nil {

+ 1 - 0
services/data/edb_info.go

@@ -2307,6 +2307,7 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 		utils.DATA_SOURCE_STOCK_PLANT:         "存量装置",
 		utils.DATA_SOURCE_NATIONAL_STATISTICS: "国家统计局",
 		utils.DATA_SOURCE_FUBAO:               "富宝数据",
+		utils.DATA_SOURCE_GFEX:                "广期所",
 	}
 
 	sourceName, ok := sourceNameMap[source]

+ 225 - 0
services/email.go

@@ -0,0 +1,225 @@
+package services
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+)
+
+func SendEmailToCompany() {
+	// 获取收件人列表
+	//emailCond := " AND enabled = 1 "
+	emailCond := ""
+	emailPars := make([]interface{}, 0)
+	emails, e := models.GetEnglishReportEmailList(emailCond, emailPars, "")
+	if e != nil {
+		fmt.Println("获取收件人列表失败, Err: " + e.Error())
+		return
+	}
+	if len(emails) == 0 {
+		fmt.Println("收件人列表为空")
+		return
+	}
+
+	// TODO:这是HTML模板内容
+	template := `<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,viewport-fit=cover">
+    <title>Horizon Insights x Fastmarkets x FGE: 2024 Macro & Commodities Outlook - Registration Open!</title>
+</head>
+
+<body style="padding:0;margin:0;background-color:#fff">
+<div id="app" style="max-width:1280px;margin:0 auto;font-size:14px;min-height:100vh;">
+    <div class="main-box" style="padding:25px 20px;">
+        <div style="line-height:1.7;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;color:#000000;"
+            class=" __aliyun_node_has_color">
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:center;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="font-size:18.0px;font-weight:bold;font-style:normal;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;color:#000000;text-decoration:underline;"
+                    class=" __aliyun_node_has_color">2024 Macro &amp; Commodities Outlook Invite</span></div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;"
+                    class=" __aliyun_node_has_color"><br></span></div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;"
+                    class=" __aliyun_node_has_color">Dear Client,</span></div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;"
+                    class=" __aliyun_node_has_color"><br></span></div>
+            <div style="clear:both;">
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;"
+                        class=" __aliyun_node_has_color">Happy Year-End Holidays!</span></div>
+            </div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;"
+                    class=" __aliyun_node_has_color"><br></span></div>
+            <div style="clear:both;">
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;border:.0px;font-family:arial;margin:.0px;padding:.0px;outline:.0px;font-size:13.0px;"
+                        class=" __aliyun_node_has_color">As we usher in the new year, Horizon Insights, in collaboration
+                        with Fastmarkets and FGE, is proud to announce</span><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;font-family:arial;font-size:13.0px;"
+                        class=" __aliyun_node_has_color">&nbsp;our 2024 Macro &amp; Commodities Outlook which will be
+                        held&nbsp;</span><span
+                        style="font-weight:bold;color:#000000;font-style:normal;text-transform:none;font-family:arial;font-size:13.0px;text-decoration:none;"
+                        class=" __aliyun_node_has_color">virtually</span><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;font-family:arial;font-size:13.0px;text-decoration:none;"
+                        class=" __aliyun_node_has_color">&nbsp;on&nbsp;</span><span
+                        style="font-weight:bold;color:#000000;font-style:normal;text-transform:none;font-family:arial;font-size:13.0px;text-decoration:none;"
+                        class=" __aliyun_node_has_color">10 &amp; 11 Jan 2024</span><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;font-family:arial;font-size:13.0px;"
+                        class=" __aliyun_node_has_color">,&nbsp;</span><span
+                        style="color:#000000;font-family:arial;font-size:13.0px;font-style:normal;font-weight:bold;text-transform:none;"
+                        class=" __aliyun_node_has_color">5pm - 8pm SGT</span><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;font-family:arial;font-size:13.0px;"
+                        class=" __aliyun_node_has_color">&nbsp;(i.e.&nbsp;</span><span
+                        style="color:#000000;font-family:arial;font-size:13.0px;font-style:normal;font-weight:bold;text-transform:none;"
+                        class=" __aliyun_node_has_color">9am - 12pm BST</span><span
+                        style="color:#000000;font-style:normal;font-weight:400;text-transform:none;font-family:arial;font-size:13.0px;"
+                        class=" __aliyun_node_has_color">). Our experienced analysts from Horizon Insights together with
+                        guest speakers from our valued partners will present our views in the fields of Macro, Ferrous
+                        Metals, Base Metals, Energy, as well as Petrochemicals.</span></div>
+            </div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-size:13.0px;font-family:arial;"><br></span>
+            </div>
+            <div style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-size:13.0px;font-family:arial;">Please
+                    see below for our event schedule:</span></div>
+            <div
+                style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">
+                <br></div>
+            <div
+                style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">
+                <span
+                    style="color:#000000;font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-size:13.0px;font-family:arial;"
+                    class=" __aliyun_node_has_color">&nbsp;&nbsp;<img style="vertical-align: bottom; margin: 0px;"
+                        height="3301"
+                        src="https://hzstatic.hzinsights.com/static/images/202312/20231228/KeDTAxIZ3UrZR5DZKKIQLdoIaOVB.png"
+                        width="890"></span></div>
+            <div
+                style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">
+                <span
+                    style="color:#000000;font-style:normal;font-weight:400;text-transform:none;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-size:13.0px;font-family:arial;"
+                    class=" __aliyun_node_has_color">&nbsp; &nbsp;</span></div>
+            <div
+                style="clear:both;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-variant-ligatures:normal;font-variant-caps:normal;text-align:start;text-indent:.0px;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">
+                <br></div>
+            <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;"><br></span>
+            </div>
+            <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;">To
+                    register, simply scan the QR code in the posters above to indicate your interest. Alternatively, you
+                    may also&nbsp;</span><span
+                    style="font-weight:bold;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;"><a
+                        href="https://forms.gle/yhPRDcri43P2QyPt8" target="_blank">register here</a></span><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;">.</span>
+            </div>
+            <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                class=" __aliyun_node_has_color"><span
+                    style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;color:#000000;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline;"
+                    class=" __aliyun_node_has_color">Should you encounter any difficulties registering, please contact
+                    Stephanie (stephanie@hzinsights.com).</span></div>
+            <div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="font-weight:bold;margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;">The
+                        virtual meeting details will be disseminated to you a few days prior to the event.</span></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><br></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;">Lastly,
+                        our firm would like to wish all of you a prosperous Happy New Year! We look forward to seeing
+                        everyone on the 10th &amp; 11th.</span></div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;"><br></span>
+                </div>
+                <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;color:#000000;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;text-align:start;text-indent:.0px;text-transform:none;text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;clear:both;"
+                    class=" __aliyun_node_has_color"><span
+                        style="margin:.0px;padding:.0px;border:.0px;outline:.0px;font-family:arial;font-size:13.0px;">Cheers!</span>
+                </div>
+            </div>
+            <div><br></div>
+            <div>
+                <div style="clear:both;"><span style="font-family:arial;font-size:13.0px;color:#000000;"
+                        class=" __aliyun_node_has_color"><br></span></div>
+                <div style="clear:both;">
+                    <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;clear:both;font-variant-caps:normal;text-align:start;text-indent:.0px;"
+                        class="x___aliyun_node_has_color"><br></div>
+                    <div style="margin:.0px;padding:.0px;border:.0px;outline:.0px;clear:both;font-variant-caps:normal;text-align:start;text-indent:.0px;"
+                        class="x___aliyun_node_has_color"></div>
+                </div>
+            </div>
+            <div><br></div>
+            <div><br></div>
+            <div><br></div>
+            <div style="line-height:20.0px;clear:both;"><br></div>
+        </div>
+    </div>
+</div>
+</body>
+
+</html>`
+
+	// 推送信息
+	sendData := make([]*EnglishReportSendEmailRequest, 0)
+	for i := range emails {
+		r := new(EnglishReportSendEmailRequest)
+		r.EmailId = emails[i].Id
+		r.Email = strings.Replace(emails[i].Email, " ", "", -1)
+		r.Subject = "Horizon Insights x Fastmarkets x FGE: 2024 Macro & Commodities Outlook - Registration Open!" // TODO:这是主题
+		r.FromAlias = "Horizon FICC"                                                                              // TODO:这是推送人(中文)
+
+		r.HtmlBody = template
+		sendData = append(sendData, r)
+	}
+	if len(sendData) == 0 {
+		fmt.Println("无邮件可推送")
+		return
+	}
+
+	// 请求阿里云接口批量推送
+	aliEmail := new(AliyunEmail)
+	resultList, e := aliEmail.BatchSendEmail(sendData)
+	if e != nil {
+		fmt.Println("批量推送失败, Err: " + e.Error())
+		return
+	}
+	for _, r := range resultList {
+		utils.FileLog.Info("email: %s, ok: %v, res: %s", r.Email, r.Ok, r.ResultData)
+		if r.Ok {
+			fmt.Println("发送成功")
+		} else {
+			fmt.Println("发送失败:" + r.ResultData)
+		}
+	}
+}

+ 1 - 0
utils/constants.go

@@ -168,6 +168,7 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE_ZSXY                          // 预测指数修匀->73
 	DATA_SOURCE_CALCULATE_ZDYFX                                 // 自定义分析->74
 	DATA_SOURCE_CALCULATE_RJZ                                   // 日均值计算->75
+	DATA_SOURCE_GFEX                                 = 78       // 广州期货交易所->78
 )
 
 // 数据刷新频率