浏览代码

同花顺客群

ziwen 2 年之前
父节点
当前提交
9c65878023

+ 2 - 0
controller/voice_broadcast/voice_broadcast.go

@@ -172,6 +172,8 @@ func AddBroadcast(c *gin.Context) {
 	// 推送回复消息给用户
 	go wechat.SendVoiceBroadcastWxMsg(voiceBroadcast.BroadcastId, voiceBroadcast.SectionName, voiceBroadcast.BroadcastName)
 
+	//同花顺客群
+	go services.SendVoiceBroadcastToThs(voiceBroadcast)
 	response.Ok("发布成功", c)
 }
 

+ 1 - 1
go.mod

@@ -26,9 +26,9 @@ require (
 	github.com/swaggo/gin-swagger v1.3.3
 	github.com/swaggo/swag v1.7.4
 	github.com/tosone/minimp3 v1.0.1
+	github.com/wenzhenxi/gorsa v0.0.0-20220418014903-15feec0f05a6
 	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
 	golang.org/x/image v0.0.0-20190802002840-cff245a6509b
-	golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 	gorm.io/driver/mysql v1.1.3

+ 11 - 0
models/tables/report_send_ths_detail/create.go

@@ -0,0 +1,11 @@
+package models
+
+import "hongze/hongze_yb/global"
+
+//新增报告发送给同花顺的记录
+func AddReportSendThsDetail(item *ReportSendThsDetail) (lastId int, err error) {
+	err = global.MYSQL["rddp"].Create(item).Error
+	lastId = item.SendId
+	return
+}
+

+ 16 - 0
models/tables/report_send_ths_detail/query.go

@@ -0,0 +1,16 @@
+package models
+
+import "hongze/hongze_yb/global"
+
+//根据报告id获取发送记录
+func GetVoiceSendThsDetailById(voiceId int, reportType string) (item *ReportSendThsDetail, err error) {
+	err = global.DEFAULT_MYSQL.Model(ReportSendThsDetail{}).Where("report_id = ? AND report_type=?", voiceId,reportType).Order("send_id DESC").Scan(&item).Error
+	return
+}
+
+// GetLatelyReportSendThsDetail 获取发送中/发送成功的 距离现在最近的一条记录
+func GetLatelyReportSendThsDetail() (item *ReportSendThsDetail, err error) {
+	err = global.DEFAULT_MYSQL.Model(ReportSendThsDetail{}).Where(" status >=0").Order("push_time desc,send_id desc").Scan(&item).Error
+	return
+}
+

+ 16 - 0
models/tables/report_send_ths_detail/report_send_ths_detail.go

@@ -0,0 +1,16 @@
+package models
+
+import (
+	"time"
+)
+
+//报告推送给同花顺的表结构体
+type ReportSendThsDetail struct {
+	SendId     int       `orm:"column(send_id);pk" description:"发送给同花顺的Id"`
+	ReportId   int       `description:"报告id"`
+	ReportType string    `description:"报告类型"`
+	Status     int8      `description:"发送结果,0:待发送,-1发送失败,1发送成功"`
+	Remark     string    `description:"失败原因"`
+	PushTime   time.Time `description:"实际开始推送时间/预推送时间"`
+	CreateTime time.Time `description:"发送时间"`
+}

+ 14 - 0
models/tables/report_send_ths_detail/update.go

@@ -0,0 +1,14 @@
+package models
+
+import (
+	"hongze/hongze_yb/global"
+)
+
+//修改报告发送给同花顺的记录状态
+func ModifyReportSendThsDetailStatus(sendId int, status int8, remark string) (err error) {
+	err = global.MYSQL["rddp"].Model(ReportSendThsDetail{}).Where("send_id = ? ", sendId).Updates(ReportSendThsDetail{
+		Status:     status,
+		Remark:     remark,
+	}).Error
+	return
+}

+ 239 - 0
services/report_push.go

@@ -0,0 +1,239 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/wenzhenxi/gorsa"
+	"hongze/hongze_yb/global"
+	models "hongze/hongze_yb/models/tables/report_send_ths_detail"
+	"hongze/hongze_yb/models/tables/voice_broadcast"
+	"hongze/hongze_yb/services/alarm_msg"
+	"hongze/hongze_yb/services/wechat"
+	"hongze/hongze_yb/utils"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"time"
+)
+
+//func init() {
+//	report, _ := models.GetReportById(572)
+//	SendReportToThs(report)
+//}
+
+var permissionMap map[string]string = map[string]string{
+	"化里化外日评":    "原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱",
+	"股债日评":      "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"贵金属复盘":     "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"每日经济数据备忘录": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"宏观商品复盘":    "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"知白守黑日评":    "钢材,铁矿,双焦(焦煤、焦炭)",
+	"有声有色日度闲篇":  "有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"EIA原油库存点评": "原油",
+	"苯乙烯数据点评":   "苯乙烯",
+	"API原油库存点评": "原油",
+	"铁矿航运数据点评":  "铁矿",
+	"中观需求点评":    "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"聚酯数据点评":    "PTA,MEG",
+	"钢材周度数据点评":  "钢材",
+	"寻根知本":      "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"国际宏观":      "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"能化百家谈":     "原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱",
+	"有色百家谈":     "有色(铜、铝),有色(锌、铅),镍+不锈钢",
+	"黑色百家谈":     "钢材,铁矿,双焦(焦煤、焦炭)",
+}
+
+// TshResult 同花顺返回信息
+type TshResult struct {
+	ErrorCode int    `json:"error" description:"错误状态码"`
+	Message   string `json:"message" description:"提示信息"`
+}
+
+var (
+	THS_SendUrl string //同花顺地址url
+	THS_PubKey  string //同花顺公钥
+)
+
+func init() {
+	THS_PubKey = `-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqugglfCboOEfWtHlGBOW
+40a4Y3xOs0MPBwjTOzHgcaWzx5XCc20VftGVXkWlpjs8u4dza/Bp1SV7SJ5Y7U95
+jgUOP8Js9Qgp6UVqBJDJf3i1KpjHzlk3ma8zxAYUAdieEUE+SKSxSY+BD9A6lpf5
+n+igXLmzR5GeVGFeLzoMhB1+pXgGhW30ao9wPwuRF7DBl+FKa/ACi7iXLiwXVgqT
+FFi29TKeerEENu3EpMXvPml7tNUiVmVW6d83hlascfbAlkShwuHLSGpLqK7brtg6
+jRS9hreKFKb0BUQ4TB26e7IDCstbMRvUp4+OGezexzic5NYPQ8uLo5OTaS7f7PrW
+ZwIDAQAB
+-----END PUBLIC KEY-----`
+	if global.CONFIG.Serve.RunMode == "release" {
+		//同花顺正式地址
+		THS_SendUrl = `https://board.10jqka.com.cn/gateway/ps/syncNews`
+	} else {
+		//同花顺测试地址
+		THS_SendUrl = `https://mtest.10jqka.com.cn/gateway/ps/syncNews`
+	}
+}
+// SendThs 发送消息到同花顺
+func SendThs(title, labelStr, abstract, jumpBaseUrl, logoUrl, dataType string) (err error) {
+	defer func() {
+		if err != nil {
+			//fmt.Println(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送消息至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
+			go alarm_msg.SendAlarmMsg("发送消息至同花顺失败 ErrMsg:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送报告至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	pubKey := THS_PubKey
+	sendUrl := THS_SendUrl
+
+	//标题字符长度截取,最多50位字符
+	title = utils.SubStr(title, 50)
+	global.LOG.Info(fmt.Sprintf("title:%s", title))
+	title, err = gorsa.PublicEncrypt(title, pubKey)
+	if err != nil {
+		return
+	}
+
+	//简介字符长度截取,最多50位字符
+	abstract = utils.SubStr(abstract, 50)
+	global.LOG.Info(fmt.Sprintf("abstract:%s", abstract))
+	abstract, err = gorsa.PublicEncrypt(abstract, pubKey)
+	if err != nil {
+		return
+	}
+
+	global.LOG.Info(fmt.Sprintf("labelStr:%s", labelStr))
+	label, err := gorsa.PublicEncrypt(labelStr, pubKey)
+	if err != nil {
+		return
+	}
+
+	jumpUrl, err := gorsa.PublicEncrypt(jumpBaseUrl, pubKey)
+	if err != nil {
+		return
+	}
+
+	picUrl, err := gorsa.PublicEncrypt(logoUrl, pubKey)
+	if err != nil {
+		return
+	}
+
+	dataTypeEncript, err := gorsa.PublicEncrypt(dataType, pubKey)
+	if err != nil {
+		return
+	}
+
+	//开始发送
+	client := http.Client{}
+	form := url.Values{}
+	form.Add("title", title)
+	form.Add("description", abstract)
+	form.Add("label", label)
+	form.Add("url", jumpUrl)
+	form.Add("icon", picUrl)
+	form.Add("dataType", dataTypeEncript)
+
+	global.LOG.Info(fmt.Sprintf("SendThs parms:%s", form.Encode()))
+	resp, err := client.PostForm(sendUrl, form)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	body, _ := ioutil.ReadAll(resp.Body)
+
+	//fmt.Println(string(body))
+	global.LOG.Info(fmt.Sprintf("ThsResult parms:%s", string(body)))
+
+	//同花顺接口返回数据
+	var tshResult TshResult
+	err = json.Unmarshal(body, &tshResult)
+	if err != nil {
+		err = errors.New(fmt.Sprint("同花顺接口返回数据转换成结构体异常,Err:", err))
+		return
+	}
+	if tshResult.ErrorCode != 1 {
+		err = errors.New(fmt.Sprint("发送数据到同花顺接口异常,result:", string(body)))
+		return
+	}
+	return
+}
+
+// SendVoiceBroadcastToThs 发送语音播报到同花顺
+func SendVoiceBroadcastToThs(voice voice_broadcast.VoiceBroadcast) (err error) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("发送语音播报至同花顺失败 ErrMsg:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "SendReportMiniToThs发送报告至同花顺失败, ReportId:" + strconv.Itoa(report.Id) + ", ErrMsg:" + err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+
+	//小程序跳转地址
+	jumpBaseUrl := wechat.WxYbAppId + `/pages/voice/voice?voiceId=`
+
+	logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
+
+	sendDetail, err := models.GetVoiceSendThsDetailById(voice.BroadcastId, "语音播报")
+	if err != nil && err != utils.ErrNoRow {
+		return
+	} else if err == nil && sendDetail != nil {
+		if sendDetail.Status >= 0 {
+			fmt.Println("重复发送")
+			return
+		}
+	}
+
+	pushTime := time.Now()      //预发送时间
+	isPrePush := false          //是否预发布
+	addTime := time.Minute * 40 //短时间内多次发布报告,每篇报告的间隔时间(目前暂定40分钟,只有日度点评的报告需要限制)
+
+	//获取距离现在最近的一条发送成功失败记录
+	latelySendDetail, err := models.GetLatelyReportSendThsDetail()
+	//如果存在最近一条发送记录,那么去校验时间
+	if (err == nil && latelySendDetail != nil) || err == utils.ErrNoRow {
+		pushTime = latelySendDetail.PushTime.Add(addTime)
+		//如果最近一条的发送记录 的 (发送时间 + 每篇报告的间隔时间)  晚于 当前时间
+		if pushTime.After(time.Now()) {
+			isPrePush = true
+		} else {
+			pushTime = time.Now()
+		}
+	}
+
+	if isPrePush { //预发布,只添加预发布记录,不立马发送报告,等待定时任务推送消息给同花顺
+		newSendDetail := &models.ReportSendThsDetail{
+			ReportId:   voice.BroadcastId,
+			ReportType: "语音播报",
+			Status:     2,
+			PushTime:   pushTime,
+			CreateTime: time.Now(),
+		}
+		_, tmpErr := models.AddReportSendThsDetail(newSendDetail)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	} else {
+		newSendDetail := &models.ReportSendThsDetail{
+			ReportId:   voice.BroadcastId,
+			ReportType: "语音播报",
+			Status:     0,
+			PushTime:   pushTime,
+			CreateTime: time.Now(),
+		}
+		sendDetailId, tmpErr := models.AddReportSendThsDetail(newSendDetail)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		//及时发送
+		dataType := "2" //内容类型:1文字 2小程序
+		err = SendThs(voice.BroadcastName, voice.VarietyName, voice.BroadcastName, fmt.Sprint(jumpBaseUrl, voice.BroadcastId), logoUrl, dataType)
+		if err != nil {
+			_ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, err.Error())
+			return
+		}
+		_ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
+	}
+
+	return
+}

+ 11 - 0
utils/common.go

@@ -966,4 +966,15 @@ func Bit2MB(bitSize int64, prec int) (size float64) {
 	mb := float64(bitSize)/float64(1024*1024)
 	size, _ = strconv.ParseFloat(strconv.FormatFloat(mb,'f',prec,64), 64)
 	return
+}
+
+// SubStr 截取字符串(中文)
+func SubStr(str string, subLen int) string {
+	strRune := []rune(str)
+	bodyRuneLen := len(strRune)
+	if bodyRuneLen > subLen {
+		bodyRuneLen = subLen
+	}
+	str = string(strRune[:bodyRuneLen])
+	return str
 }