report_push.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. package services
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/wenzhenxi/gorsa"
  7. "hongze/hz_eta_api/models"
  8. "hongze/hz_eta_api/services/alarm_msg"
  9. "hongze/hz_eta_api/utils"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. //func init() {
  18. // report, _ := models.GetReportById(572)
  19. // SendReportToThs(report)
  20. //}
  21. var permissionMap map[string]string = map[string]string{
  22. "化里化外日评": "原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱",
  23. "股债日评": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  24. "贵金属复盘": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  25. "每日经济数据备忘录": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  26. "宏观商品复盘": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  27. "知白守黑日评": "钢材,铁矿,双焦(焦煤、焦炭)",
  28. "有声有色日度闲篇": "有色(铜、铝),有色(锌、铅),镍+不锈钢",
  29. "EIA原油库存点评": "原油",
  30. "苯乙烯数据点评": "苯乙烯",
  31. "API原油库存点评": "原油",
  32. "铁矿航运数据点评": "铁矿",
  33. "中观需求点评": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  34. "聚酯数据点评": "PTA,MEG",
  35. "钢材周度数据点评": "钢材",
  36. "寻根知本": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  37. "国际宏观": "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢",
  38. "能化百家谈": "原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱",
  39. "有色百家谈": "有色(铜、铝),有色(锌、铅),镍+不锈钢",
  40. "黑色百家谈": "钢材,铁矿,双焦(焦煤、焦炭)",
  41. }
  42. // permissionLabelMap 品种与同花顺标签的映射关系
  43. var permissionLabelMap = map[string]string{
  44. "宏观经济": "宏观",
  45. "利率债": "利率债",
  46. "原油": "原油",
  47. "PTA": "PTA",
  48. "MEG": "MEG",
  49. "织造终端": "织造终端",
  50. "甲醇": "甲醇",
  51. "聚烯烃": "聚烯烃",
  52. "沥青": "沥青",
  53. "纯苯+苯乙烯": "纯苯+苯乙烯",
  54. "玻璃纯碱": "玻璃纯碱",
  55. "钢材": "钢材",
  56. "铁矿": "铁矿",
  57. "双焦(焦煤、焦炭)": "双焦(焦煤、焦炭)",
  58. "有色(锌)": "有色(锌)",
  59. "有色(铜、铝)": "有色(铜、铝)",
  60. "镍+不锈钢": "镍+不锈钢",
  61. "PVC": "PVC",
  62. "聚酯": "聚酯",
  63. "钴锂": "钴锂",
  64. "策略": "策略",
  65. "苯乙烯": "纯苯+苯乙烯",
  66. "锌": "有色(锌)",
  67. "双焦": "双焦(焦煤、焦炭)",
  68. "铜/铝": "有色(铜、铝)",
  69. "镍/不锈钢": "镍+不锈钢",
  70. "成品油": "成品油",
  71. "油品": "成品油",
  72. "纺服": "织造终端",
  73. }
  74. // TshResult 同花顺返回信息
  75. type TshResult struct {
  76. ErrorCode int `json:"error" description:"错误状态码"`
  77. Message string `json:"message" description:"提示信息"`
  78. }
  79. // SendReportToThs 发送报告到同花顺
  80. func SendReportToThs(report *models.ReportDetail) (err error) {
  81. defer func() {
  82. if err != nil {
  83. //fmt.Println(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送消息至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  84. go alarm_msg.SendAlarmMsg("发送报告至同花顺失败 ErrMsg:"+err.Error(), 3)
  85. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送报告至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  86. }
  87. }()
  88. jumpBaseUrl := `http://rddpweb.brilliantstart.cn/reportdtl?id=`
  89. //生产环境地址
  90. if utils.RunMode == "release" {
  91. jumpBaseUrl = `https://ficc.hzinsights.com/reportdtl?id=`
  92. }
  93. logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
  94. //获取分类信息(标签)
  95. permissionName := report.ClassifyNameSecond
  96. classifyItem, tmpErr := models.GetClassifyById(report.ClassifyIdSecond)
  97. if tmpErr != nil {
  98. err = errors.New(fmt.Sprint("获取分类失败:", tmpErr.Error()))
  99. return
  100. }
  101. //获取权限标签名称
  102. var permissionStr string
  103. if classifyItem != nil {
  104. permissionStr = classifyItem.ClassifyLabel
  105. if permissionStr == "" {
  106. var isOk bool
  107. permissionStr, isOk = permissionMap[permissionName]
  108. if !isOk {
  109. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  110. return
  111. }
  112. }
  113. } else {
  114. var isOk bool
  115. permissionStr, isOk = permissionMap[permissionName]
  116. if !isOk {
  117. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  118. return
  119. }
  120. }
  121. if permissionStr == "" {
  122. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  123. return
  124. }
  125. sendDetail, err := models.GetReportSendThsDetailByReportId(report.Id, "日度点评")
  126. if err != nil && err.Error() != utils.ErrNoRow() {
  127. return
  128. } else if err == nil && sendDetail != nil {
  129. if sendDetail.Status >= 0 {
  130. fmt.Println("重复发送")
  131. return
  132. }
  133. }
  134. pushTime := time.Now() //预发送时间
  135. isPrePush := false //是否预发布
  136. addTime := time.Minute * 40 //短时间内多次发布报告,每篇报告的间隔时间(目前暂定40分钟,只有日度点评的报告需要限制)
  137. //获取距离现在最近的一条发送成功失败记录
  138. latelySendDetail, err := models.GetLatelyReportSendThsDetail()
  139. //如果存在最近一条发送记录,那么去校验时间
  140. if (err == nil && latelySendDetail != nil) || err.Error() == utils.ErrNoRow() {
  141. pushTime = latelySendDetail.PushTime.Add(addTime)
  142. //如果最近一条的发送记录 的 (发送时间 + 每篇报告的间隔时间) 晚于 当前时间
  143. if pushTime.After(time.Now()) {
  144. isPrePush = true
  145. } else {
  146. pushTime = time.Now()
  147. }
  148. }
  149. //fmt.Println("sendDetailId:", sendDetailId)
  150. stageStr := fmt.Sprintf("%v", report.Stage)
  151. createDate, _ := time.Parse(utils.FormatDateTime, report.CreateTime)
  152. createDateFrom := createDate.Format("0102")
  153. title := `【第` + stageStr + `期|FICC】` + report.Title + `(` + createDateFrom + ")"
  154. if isPrePush { //预发布,只添加预发布记录,不立马发送报告,等待定时任务推送消息给同花顺
  155. newSendDetail := &models.ReportSendThsDetail{
  156. Title: title,
  157. LabelStr: permissionStr,
  158. ReportId: report.Id,
  159. ReportType: "日度点评",
  160. Status: 2,
  161. Level: 3,
  162. PushTime: pushTime,
  163. CreateTime: time.Now(),
  164. MsgType: 1,
  165. Content: report.Abstract,
  166. JumpUrl: fmt.Sprint(jumpBaseUrl, report.Id),
  167. Pic: logoUrl,
  168. }
  169. _, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  170. if tmpErr != nil {
  171. err = tmpErr
  172. return
  173. }
  174. } else {
  175. newSendDetail := &models.ReportSendThsDetail{
  176. Title: title,
  177. LabelStr: permissionStr,
  178. ReportId: report.Id,
  179. ReportType: "日度点评",
  180. Status: 0,
  181. Level: 1,
  182. PushTime: pushTime,
  183. CreateTime: time.Now(),
  184. MsgType: 1,
  185. Content: report.Abstract,
  186. JumpUrl: fmt.Sprint(jumpBaseUrl, report.Id),
  187. Pic: logoUrl,
  188. }
  189. sendDetailId, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  190. if tmpErr != nil {
  191. err = tmpErr
  192. return
  193. }
  194. dataType := "1" //内容类型:1文字 2小程序
  195. tmpErr = SendThs(title, permissionStr, report.Abstract, fmt.Sprint(jumpBaseUrl, report.Id), logoUrl, dataType)
  196. if tmpErr != nil {
  197. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, tmpErr.Error())
  198. return
  199. }
  200. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
  201. }
  202. go SendReportToEmail(report)
  203. return
  204. }
  205. // SendMsgToThs 发送群发消息到同花顺
  206. func SendMsgToThs(groupSendMsg *models.GroupSendMsg) (err error) {
  207. sendDetail, err := models.GetReportSendThsDetailByReportId(groupSendMsg.MsgId, "群发消息")
  208. if err != nil && err.Error() != utils.ErrNoRow() {
  209. return
  210. } else if err == nil && sendDetail != nil {
  211. if sendDetail.Status >= 0 {
  212. err = errors.New("重复发送")
  213. return
  214. }
  215. }
  216. newSendDetail := &models.ReportSendThsDetail{
  217. Title: groupSendMsg.Title,
  218. LabelStr: groupSendMsg.Label,
  219. ReportId: groupSendMsg.MsgId,
  220. ReportType: "群发消息",
  221. Status: 0,
  222. Level: 1,
  223. PushTime: time.Now(),
  224. CreateTime: time.Now(),
  225. MsgType: int(groupSendMsg.JumpType),
  226. Content: groupSendMsg.Description,
  227. JumpUrl: groupSendMsg.LinkUrl,
  228. Pic: groupSendMsg.ImgUrl,
  229. }
  230. sendDetailId, err := models.AddReportSendThsDetail(newSendDetail)
  231. if err != nil {
  232. return
  233. }
  234. dataType := fmt.Sprint(groupSendMsg.JumpType)
  235. //dataType := "1" //内容类型:1文字 2小程序
  236. err = SendThs(groupSendMsg.Title, groupSendMsg.Label, groupSendMsg.Description, groupSendMsg.LinkUrl, groupSendMsg.ImgUrl, dataType)
  237. if err != nil {
  238. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, err.Error())
  239. return
  240. }
  241. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
  242. return
  243. }
  244. // SendThs 发送消息到同花顺
  245. func SendThs(title, labelStr, abstract, jumpBaseUrl, logoUrl, dataType string) (err error) {
  246. defer func() {
  247. if err != nil {
  248. //fmt.Println(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送消息至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  249. go alarm_msg.SendAlarmMsg("发送消息至同花顺失败 ErrMsg:"+err.Error(), 3)
  250. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送报告至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  251. }
  252. }()
  253. pubKey := utils.THS_PubKey
  254. sendUrl := utils.THS_SendUrl
  255. //fmt.Println("sendUrl:", sendUrl)
  256. utils.FileLog.Info(fmt.Sprintf("jumpBaseUrl:%s", jumpBaseUrl))
  257. utils.FileLog.Info(fmt.Sprintf("dataType:%s", dataType))
  258. //标题字符长度截取,最多50位字符
  259. title = utils.SubStr(title, 50)
  260. utils.FileLog.Info(fmt.Sprintf("title:%s", title))
  261. title, err = gorsa.PublicEncrypt(title, pubKey)
  262. if err != nil {
  263. return
  264. }
  265. //简介字符长度截取,最多50位字符
  266. if dataType != `3` {
  267. abstract = utils.SubStr(abstract, 50)
  268. utils.FileLog.Info(fmt.Sprintf("abstract:%s", abstract))
  269. abstract, err = gorsa.PublicEncrypt(abstract, pubKey)
  270. if err != nil {
  271. return
  272. }
  273. } else {
  274. utils.FileLog.Info(fmt.Sprintf("abstract:%s", abstract))
  275. }
  276. // 关联后的标签数据
  277. newLabelList := make([]string, 0)
  278. labelList := strings.Split(labelStr, ",")
  279. for _, v := range labelList {
  280. tmpLabel, ok := permissionLabelMap[v] //判断是否在关联标签里面
  281. if !ok { //如果不在关联标签里面,那么就把原始的值赋值给
  282. tmpLabel = v
  283. }
  284. newLabelList = append(newLabelList, tmpLabel)
  285. }
  286. labelStr = strings.Join(newLabelList, ",")
  287. utils.FileLog.Info(fmt.Sprintf("labelStr:%s", labelStr))
  288. label, err := gorsa.PublicEncrypt(labelStr, pubKey)
  289. if err != nil {
  290. return
  291. }
  292. jumpUrl, err := gorsa.PublicEncrypt(jumpBaseUrl, pubKey)
  293. if err != nil {
  294. return
  295. }
  296. picUrl, err := gorsa.PublicEncrypt(logoUrl, pubKey)
  297. if err != nil {
  298. return
  299. }
  300. dataTypeEncript, err := gorsa.PublicEncrypt(dataType, pubKey)
  301. if err != nil {
  302. return
  303. }
  304. //开始发送
  305. client := http.Client{}
  306. form := url.Values{}
  307. form.Add("title", title)
  308. form.Add("description", abstract)
  309. form.Add("label", label)
  310. form.Add("url", jumpUrl)
  311. form.Add("icon", picUrl)
  312. form.Add("dataType", dataTypeEncript)
  313. utils.FileLog.Info(fmt.Sprintf("SendThs parms:%s", form.Encode()))
  314. resp, err := client.PostForm(sendUrl, form)
  315. if err != nil {
  316. return
  317. }
  318. defer resp.Body.Close()
  319. body, _ := ioutil.ReadAll(resp.Body)
  320. //fmt.Println(string(body))
  321. utils.FileLog.Info(fmt.Sprintf("ThsResult parms:%s", string(body)))
  322. //同花顺接口返回数据
  323. var tshResult TshResult
  324. err = json.Unmarshal(body, &tshResult)
  325. if err != nil {
  326. err = errors.New(fmt.Sprintf("同花顺接口返回数据转换成结构体异常;body:%s;Err:%s;", string(body), err.Error()))
  327. return
  328. }
  329. if tshResult.ErrorCode != 1 {
  330. err = errors.New(fmt.Sprint("发送数据到同花顺接口异常,result:", string(body)))
  331. return
  332. }
  333. return
  334. }
  335. // SendReportToEmail 发送报告邮件
  336. func SendReportToEmail(report *models.ReportDetail) (err error) {
  337. fmt.Println("SendReportToEmail")
  338. defer func() {
  339. if err != nil {
  340. //fmt.Println(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送消息至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  341. go alarm_msg.SendAlarmMsg("发送报告至邮件失败,SendReportToEmail ErrMsg:"+err.Error(), 3)
  342. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "SendReportToEmail ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  343. }
  344. }()
  345. toUser := ``
  346. jumpUrl := ``
  347. if utils.RunMode == "debug" {
  348. //toUser = `glji@hzinsights.com`
  349. toUser = `317699326@qq.com`
  350. jumpUrl = "http://rddpweb.brilliantstart.cn/reportdtl?id=1578" + strconv.Itoa(report.Id)
  351. } else {
  352. toUser = "lijun011112@gtjas.com"
  353. jumpUrl = "https://ficc.hzinsights.com/reportdtl?id=" + strconv.Itoa(report.Id)
  354. }
  355. createDate, err := time.Parse(utils.FormatDateTime, report.CreateTime)
  356. createDateFrom := createDate.Format("060102")
  357. emailBody :=
  358. `<!DOCTYPE html>
  359. <html lang="en">
  360. <head>
  361. <meta charset="UTF-8">
  362. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  363. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  364. <title>Email</title>
  365. </head>
  366. <body>
  367. <div>
  368. <h2 style="font-size: 16px;">弘则研报:<span>` + report.Title + "_" + createDateFrom + `</span><span></span></h2>
  369. <div style="font-size: 14px;margin-left: 40px;">
  370. <p>你好,</p>
  371. <p>今日报告已推送。</p>
  372. <p><span>报告类型:</span><span>` + report.ClassifyNameFirst + `</span></p>
  373. <p><span>报告标题:</span><span>` + report.Title + `</span></p>
  374. <p><span>报告链接:</span><a href="` + jumpUrl + `">弘则研究</a></p>
  375. </div>
  376. </div>
  377. </body>
  378. </html>`
  379. fmt.Println("start SendReportToEmail")
  380. result, err := utils.SendEmailByHz("弘则研报报告", emailBody, toUser)
  381. fmt.Println("send result:", result, err)
  382. return
  383. }
  384. // SendReportMiniToThs 发送报告-研报小程序到同花顺
  385. func SendReportMiniToThs(report *models.ReportDetail) (err error) {
  386. defer func() {
  387. if err != nil {
  388. go alarm_msg.SendAlarmMsg("SendReportMiniToThs 发送报告-研报小程序到同花顺失败, ReportId:"+strconv.Itoa(report.Id)+", ErrMsg:"+err.Error(), 3)
  389. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "SendReportMiniToThs发送报告至同花顺失败, ReportId:" + strconv.Itoa(report.Id) + ", ErrMsg:" + err.Error(), utils.EmailSendToUsers)
  390. }
  391. }()
  392. //小程序跳转地址
  393. jumpBaseUrl := utils.WxYbAppId + `/pages-report/reportDetail?reportId=`
  394. // 如果是晨报、周报,那么地址还不一样 (2023-4-7 09:24:25:添加晨报推送)
  395. if utils.InArrayByStr([]string{utils.REPORT_TYPE_DAY, utils.REPORT_TYPE_WEEK}, report.ChapterType) {
  396. jumpBaseUrl = utils.WxYbAppId + `/pages-report/chapterList?reportId=`
  397. }
  398. logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
  399. var permissionStr string
  400. if report.HasChapter == 0 {
  401. // 获取分类信息(标签)
  402. permissionName := report.ClassifyNameSecond
  403. classifyItem, tmpErr := models.GetClassifyById(report.ClassifyIdSecond)
  404. if tmpErr != nil {
  405. err = errors.New(fmt.Sprint("获取分类失败:", permissionName))
  406. return
  407. }
  408. // 获取权限标签名称
  409. if classifyItem != nil {
  410. permissionStr = classifyItem.ClassifyLabel
  411. if permissionStr == "" {
  412. var isOk bool
  413. permissionStr, isOk = permissionMap[permissionName]
  414. if !isOk {
  415. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  416. return
  417. }
  418. }
  419. } else {
  420. var isOk bool
  421. permissionStr, isOk = permissionMap[permissionName]
  422. if !isOk {
  423. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  424. return
  425. }
  426. }
  427. if permissionStr == "" {
  428. err = errors.New(fmt.Sprint("没有该权限的标签,权限名:", permissionName))
  429. return
  430. }
  431. } else {
  432. // 同php的
  433. permissionStr = "宏观,利率债,原油,PTA,MEG,织造终端,甲醇,聚烯烃,沥青,橡胶,苯乙烯,玻璃纯碱,钢材,铁矿,双焦(焦煤、焦炭),有色(铜、铝),有色(锌、铅),镍+不锈钢"
  434. }
  435. sendDetail, err := models.GetReportSendThsDetailByReportId(report.Id, "研报小程序")
  436. if err != nil {
  437. // 如果不是找不到数据,那么直接返回报错
  438. if err.Error() != utils.ErrNoRow() {
  439. return
  440. }
  441. //如果是找不到数据,那么就将err置空
  442. err = nil
  443. } else if err == nil && sendDetail != nil {
  444. if sendDetail.Status >= 0 {
  445. fmt.Println("重复发送")
  446. return
  447. }
  448. }
  449. //pushTime := time.Now() //预发送时间
  450. //isPrePush := false //是否预发布
  451. //addTime := time.Minute * 40 //短时间内多次发布报告,每篇报告的间隔时间(目前暂定40分钟,只有日度点评的报告需要限制)
  452. //level := 3 //默认是普通的推送等级
  453. classifyId := report.ClassifyIdSecond
  454. if classifyId <= 0 {
  455. classifyId = report.ClassifyIdFirst
  456. }
  457. level, pushTime, isPrePush, err := getPushTime("研报", classifyId)
  458. if isPrePush { //预发布,只添加预发布记录,不立马发送报告,等待定时任务推送消息给同花顺
  459. newSendDetail := &models.ReportSendThsDetail{
  460. Title: report.Title,
  461. LabelStr: permissionStr,
  462. ReportId: report.Id,
  463. ReportType: "研报",
  464. Status: 2,
  465. Level: level,
  466. PushTime: pushTime,
  467. CreateTime: time.Now(),
  468. MsgType: 2,
  469. Content: report.Abstract,
  470. JumpUrl: fmt.Sprint(jumpBaseUrl, report.Id),
  471. Pic: logoUrl,
  472. }
  473. _, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  474. if tmpErr != nil {
  475. err = tmpErr
  476. return
  477. }
  478. } else {
  479. newSendDetail := &models.ReportSendThsDetail{
  480. Title: report.Title,
  481. LabelStr: permissionStr,
  482. ReportId: report.Id,
  483. ReportType: "研报",
  484. Status: 0,
  485. Level: level,
  486. PushTime: pushTime,
  487. CreateTime: time.Now(),
  488. MsgType: 2,
  489. Content: report.Abstract,
  490. JumpUrl: fmt.Sprint(jumpBaseUrl, report.Id),
  491. Pic: logoUrl,
  492. }
  493. sendDetailId, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  494. if tmpErr != nil {
  495. err = tmpErr
  496. return
  497. }
  498. //及时发送
  499. dataType := "2" //内容类型:1文字 2小程序
  500. err = SendThs(report.Title, permissionStr, report.Abstract, fmt.Sprint(jumpBaseUrl, report.Id), logoUrl, dataType)
  501. if err != nil {
  502. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, err.Error())
  503. return
  504. }
  505. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
  506. }
  507. if report.HasChapter == 0 {
  508. go SendReportToEmail(report)
  509. }
  510. return
  511. }
  512. // SendYbPriceDrivenToThs 推送研报小程序价格驱动客群消息
  513. func SendYbPriceDrivenToThs(priceDrivenId, varietyTagId int) (err error) {
  514. defer func() {
  515. if err != nil {
  516. errMsg := "SendReportMiniToThs 推送研报小程序价格驱动到同花顺失败, PriceDrivenId:" + strconv.Itoa(priceDrivenId) + ", ErrMsg:" + err.Error()
  517. utils.FileLog.Info("%s", errMsg)
  518. go alarm_msg.SendAlarmMsg(errMsg, 3)
  519. }
  520. }()
  521. // 标签信息
  522. if varietyTagId <= 0 {
  523. return
  524. }
  525. varietyTag, err := models.GetVarietyTagById(varietyTagId)
  526. if err != nil {
  527. return
  528. }
  529. permissionName := varietyTag.ChartPermissionName
  530. if permissionName == "" {
  531. err = errors.New("客群标签为空, 不可推送")
  532. return
  533. }
  534. if permissionName == "宏观经济" {
  535. permissionName = "宏观"
  536. }
  537. title := fmt.Sprintf("%s价格驱动", permissionName)
  538. jumpUrl := fmt.Sprintf(`%s/pages/pricedriven/pricedriven?default_classify_first=%d&default_classify_sub=%d`, utils.WxYbAppId, varietyTag.VarietyClassifyId, varietyTag.VarietyTagId)
  539. logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
  540. // 校验发送类型: 0-不发送 1-即时发送 2-预发送
  541. reportType := "研报价格驱动"
  542. sendType, pushTime, err := checkPreparePush(priceDrivenId, reportType)
  543. if err != nil || sendType == 0 {
  544. return
  545. }
  546. thsDetail := &models.ReportSendThsDetail{
  547. Title: title,
  548. LabelStr: permissionName,
  549. ReportId: priceDrivenId,
  550. ReportType: reportType,
  551. Status: 0,
  552. Level: 1,
  553. PushTime: pushTime,
  554. CreateTime: time.Now(),
  555. MsgType: 2,
  556. Content: title,
  557. JumpUrl: jumpUrl,
  558. Pic: logoUrl,
  559. }
  560. // 延时发送
  561. if sendType == 2 {
  562. thsDetail.Status = 2
  563. thsDetail.Level = 3
  564. if _, e := models.AddReportSendThsDetail(thsDetail); e != nil {
  565. err = errors.New("AddReportSendThsDetail-新增发送记录失败, Err:" + e.Error())
  566. return
  567. }
  568. return
  569. }
  570. // 即时发送
  571. if sendType == 1 {
  572. lastId, e := models.AddReportSendThsDetail(thsDetail)
  573. if e != nil {
  574. err = errors.New("AddReportSendThsDetail-新增发送记录失败, Err:" + e.Error())
  575. return
  576. }
  577. dataType := "2" // 内容类型: 1-文字 2-小程序
  578. if e := SendThs(title, permissionName, title, jumpUrl, logoUrl, dataType); e != nil {
  579. err = errors.New("SendThs-推送失败, Err:" + e.Error())
  580. _ = models.ModifyReportSendThsDetailStatus(int(lastId), -1, e.Error())
  581. return
  582. }
  583. _ = models.ModifyReportSendThsDetailStatus(int(lastId), 1, "")
  584. }
  585. return
  586. }
  587. // checkPreparePush 校验发送类型
  588. func checkPreparePush(reportId int, reportType string) (sendType int, pushTime time.Time, err error) {
  589. // sendType 发送类型 0-不发送 1-即时发送 2-预发送
  590. sendDetail, e := models.GetReportSendThsDetailByReportId(reportId, reportType)
  591. if e != nil && e.Error() != utils.ErrNoRow() {
  592. err = errors.New("checkPreparePush-获取发送记录失败, Err:" + e.Error())
  593. return
  594. }
  595. // 重复发送
  596. if sendDetail != nil && sendDetail.Status >= 0 {
  597. return
  598. }
  599. // 最新一条(发送中/发送成功)的记录
  600. latelySendDetail, e := models.GetLatelyReportSendThsDetail()
  601. if e != nil && e.Error() != utils.ErrNoRow() {
  602. err = errors.New("checkPreparePush-获取最近一条发送记录失败, Err:" + e.Error())
  603. return
  604. }
  605. sendType = 1
  606. pushTime = time.Now()
  607. if latelySendDetail != nil {
  608. addTime := time.Minute * 40 // 报告间隔时间
  609. delayTime := latelySendDetail.PushTime.Add(addTime)
  610. // 最近一条发送记录的(发送时间+间隔时间)晚于当前时间, 则预发送
  611. if delayTime.After(time.Now()) {
  612. sendType = 2
  613. pushTime = delayTime
  614. }
  615. }
  616. return
  617. }
  618. // SendYbCommunityVideoThs 发送研报视频社区到同花顺
  619. func SendYbCommunityVideoThs(videoId int, title string) (err error) {
  620. defer func() {
  621. if err != nil {
  622. errMsg := "SendYbCommunityVideoThs 发送视频社区到同花顺失败, CommunityVideoId:" + strconv.Itoa(videoId) + ", ErrMsg:" + err.Error()
  623. utils.FileLog.Info("%s", errMsg)
  624. go alarm_msg.SendAlarmMsg(errMsg, 3)
  625. }
  626. }()
  627. permissionName := "宏观" //写死宏观,默认所有群都推
  628. jumpUrl := fmt.Sprint(utils.WxYbAppId+`/pages/video/videoList?videoId=`, videoId)
  629. logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
  630. reportType := "视频社区"
  631. sendDetail, err := models.GetReportSendThsDetailByReportId(videoId, reportType)
  632. if err != nil && err.Error() != utils.ErrNoRow() {
  633. return
  634. } else if err == nil && sendDetail != nil {
  635. if sendDetail.Status >= 0 {
  636. fmt.Println("重复发送")
  637. return
  638. }
  639. }
  640. //pushTime := time.Now() // 预发送时间
  641. //isPrePush := false // 是否预发布
  642. //addTime := time.Minute * 40 // 短时间内多次发布报告,每篇报告的间隔时间(目前暂定40分钟,只有日度点评的报告需要限制)
  643. //// 获取距离现在最近的一条发送成功失败记录
  644. level, pushTime, isPrePush, err := getPushTime("视频社区", 0)
  645. if isPrePush {
  646. // 预发布,只添加预发布记录,不立马发送报告,等待定时任务推送消息给同花顺
  647. newSendDetail := &models.ReportSendThsDetail{
  648. Title: title,
  649. LabelStr: permissionName,
  650. ReportId: videoId,
  651. ReportType: reportType,
  652. Status: 2,
  653. Level: level,
  654. PushTime: pushTime,
  655. CreateTime: time.Now(),
  656. MsgType: 2,
  657. Content: title,
  658. JumpUrl: jumpUrl,
  659. Pic: logoUrl,
  660. }
  661. _, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  662. if tmpErr != nil {
  663. err = tmpErr
  664. return
  665. }
  666. } else {
  667. // 即时发送
  668. newSendDetail := &models.ReportSendThsDetail{
  669. Title: title,
  670. LabelStr: permissionName,
  671. ReportId: videoId,
  672. ReportType: reportType,
  673. Status: 0,
  674. Level: level,
  675. PushTime: pushTime,
  676. CreateTime: time.Now(),
  677. MsgType: 2,
  678. Content: title,
  679. JumpUrl: jumpUrl,
  680. Pic: logoUrl,
  681. }
  682. sendDetailId, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  683. if tmpErr != nil {
  684. err = tmpErr
  685. return
  686. }
  687. dataType := "2" // 内容类型:1-文字 2-小程序
  688. err = SendThs(title, permissionName, title, jumpUrl, logoUrl, dataType)
  689. if err != nil {
  690. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, err.Error())
  691. return
  692. }
  693. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
  694. }
  695. return
  696. }
  697. // SendYbRoadVideoThs 发送研报线上路演到同花顺
  698. func SendYbRoadVideoThs(videoId int, title, chartPermissionIds string) (err error) {
  699. defer func() {
  700. if err != nil {
  701. errMsg := "SendYbRoadVideoThs 发送研报线上路演到同花顺, RoadVideoId:" + strconv.Itoa(videoId) + ", ErrMsg:" + err.Error()
  702. utils.FileLog.Info("%s", errMsg)
  703. go alarm_msg.SendAlarmMsg(errMsg, 3)
  704. }
  705. }()
  706. // 标签信息
  707. if chartPermissionIds == "" {
  708. return
  709. }
  710. chartPermissionIdSlice := strings.Split(chartPermissionIds, ",")
  711. chartList, e := models.GetChartPermissionByIds(chartPermissionIdSlice)
  712. if e != nil {
  713. err = errors.New("获取品种信息失败, Err:" + e.Error())
  714. return
  715. }
  716. permissionName := ""
  717. for _, v := range chartList {
  718. if v.PermissionName == "宏观经济" {
  719. v.PermissionName = "宏观"
  720. }
  721. permissionName += v.PermissionName + ","
  722. }
  723. if permissionName == "" {
  724. err = errors.New("客群标签为空, 不可推送")
  725. return
  726. }
  727. permissionName = strings.Trim(permissionName, ",")
  728. //permissionName := "宏观" //写死宏观,默认所有群都推
  729. jumpUrl := fmt.Sprint(utils.WxYbAppId+`/pages/roadShow/video/list?videoId=`, videoId)
  730. logoUrl := `https://hongze.oss-cn-shanghai.aliyuncs.com/hzyj.png`
  731. reportType := "线上路演"
  732. sendDetail, err := models.GetReportSendThsDetailByReportId(videoId, reportType)
  733. if err != nil && err.Error() != utils.ErrNoRow() {
  734. return
  735. } else if err == nil && sendDetail != nil {
  736. if sendDetail.Status >= 0 {
  737. fmt.Println("重复发送")
  738. return
  739. }
  740. }
  741. //pushTime := time.Now() // 预发送时间
  742. //isPrePush := false // 是否预发布
  743. //addTime := time.Minute * 40 // 短时间内多次发布报告,每篇报告的间隔时间(目前暂定40分钟,只有日度点评的报告需要限制)
  744. //// 获取距离现在最近的一条发送成功失败记录
  745. level, pushTime, isPrePush, err := getPushTime("线上路演", 0)
  746. if isPrePush {
  747. // 预发布,只添加预发布记录,不立马发送报告,等待定时任务推送消息给同花顺
  748. newSendDetail := &models.ReportSendThsDetail{
  749. Title: title,
  750. LabelStr: permissionName,
  751. ReportId: videoId,
  752. ReportType: reportType,
  753. Status: 2,
  754. Level: level,
  755. PushTime: pushTime,
  756. CreateTime: time.Now(),
  757. MsgType: 2,
  758. Content: title,
  759. JumpUrl: jumpUrl,
  760. Pic: logoUrl,
  761. }
  762. _, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  763. if tmpErr != nil {
  764. err = tmpErr
  765. return
  766. }
  767. } else {
  768. // 即时发送
  769. newSendDetail := &models.ReportSendThsDetail{
  770. Title: title,
  771. LabelStr: permissionName,
  772. ReportId: videoId,
  773. ReportType: reportType,
  774. Status: 0,
  775. Level: level,
  776. PushTime: pushTime,
  777. CreateTime: time.Now(),
  778. MsgType: 2,
  779. Content: title,
  780. JumpUrl: jumpUrl,
  781. Pic: logoUrl,
  782. }
  783. sendDetailId, tmpErr := models.AddReportSendThsDetail(newSendDetail)
  784. if tmpErr != nil {
  785. err = tmpErr
  786. return
  787. }
  788. dataType := "2" // 内容类型:1-文字 2-小程序
  789. err = SendThs(title, permissionName, title, jumpUrl, logoUrl, dataType)
  790. if err != nil {
  791. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), -1, err.Error())
  792. return
  793. }
  794. _ = models.ModifyReportSendThsDetailStatus(int(sendDetailId), 1, "")
  795. }
  796. return
  797. }
  798. // getPushTime 获取下次推送时间
  799. func getPushTime(configType string, classifyId int) (level int, pushTime time.Time, isPrePush bool, err error) {
  800. addTime := 40 //默认间隔40分钟
  801. level = 3 //默认是普通的推送等级
  802. pushTime = time.Now()
  803. isPrePush = true // 是否预发送
  804. // 查找对应的推送配置
  805. {
  806. findConfig, tmpErr := models.GetReportSendThsConfigByClassifyId(configType, classifyId)
  807. if tmpErr != nil {
  808. if tmpErr.Error() != utils.ErrNoRow() {
  809. err = tmpErr
  810. return
  811. }
  812. } else {
  813. addTime = findConfig.Time
  814. level = findConfig.Level
  815. }
  816. }
  817. // 如果是紧急的,那么立马推送
  818. if level == 1 {
  819. isPrePush = false
  820. return
  821. }
  822. updateMinuteStr := fmt.Sprintf(" DATE_ADD(push_time, INTERVAL %d minute) ", addTime)
  823. //获取相同推送等级level下待发送的 距离现在最近的一条等待发送的记录
  824. latelyLevelSendDetail, tmpErr := models.GetLatelyWaitLevelReportSendThs(level)
  825. //如果当前推送等级下,存在最近一条等待发送的记录,那么将该记录的推送时间后面的全部给往后推迟对应的时间
  826. if tmpErr == nil {
  827. err = models.UpdateReportSendThsPushTime(latelyLevelSendDetail.PushTime, updateMinuteStr)
  828. pushTime = latelyLevelSendDetail.PushTime.Add(time.Duration(addTime) * time.Minute)
  829. return
  830. }
  831. // 如果是查询sql报错,那么直接返回
  832. if tmpErr.Error() != utils.ErrNoRow() {
  833. err = tmpErr
  834. return
  835. }
  836. // 当前情况是:当前是同等级下没有待发送的消息
  837. var latelyPushTime time.Time
  838. //查询最近的发送中/已经成功/发送失败的记录
  839. latelyThsIsSendDetail, err := models.GetLatelyIsSendSendThsDetail()
  840. if err == nil {
  841. latelyPushTime = latelyThsIsSendDetail.PushTime
  842. } else {
  843. // 如果是查询sql报错,那么直接返回
  844. if err.Error() != utils.ErrNoRow() {
  845. return
  846. }
  847. }
  848. // 那么需要寻找下一个推送等级的第一条推送记录
  849. // 获取当前下一个推送等级下面的第一条数据
  850. earliestLevelSendDetail, tmpErr := models.GetEarliestWaitGtLevelReportSendThs(level)
  851. // 如果当前下一个推送等级下面存在数据数据,那么将该记录的推送时间以及后面时间的记录全部给往后推迟对应的时间
  852. if tmpErr == nil {
  853. if latelyPushTime.IsZero() {
  854. err = models.UpdateReportSendThsPushTimeByEqGtPushTime(earliestLevelSendDetail.PushTime, updateMinuteStr)
  855. } else {
  856. // 新记录需要发送的时间
  857. pushTime = latelyPushTime.Add(time.Duration(addTime) * time.Minute)
  858. // 如果当前时间晚于 新记录需要发送的时间 的话,那么发布时间就是现在
  859. if time.Now().After(pushTime) {
  860. pushTime = time.Now()
  861. isPrePush = false
  862. }
  863. // 下一篇报告 与 新报告的发布间隔时间
  864. differenceTime := earliestLevelSendDetail.PushTime.Sub(pushTime)
  865. updateMinuteStr := fmt.Sprintf(" DATE_ADD(push_time, INTERVAL %d minute) ", 40+int(differenceTime.Minutes()))
  866. err = models.UpdateReportSendThsPushTimeByEqGtPushTime(earliestLevelSendDetail.PushTime, updateMinuteStr)
  867. }
  868. return
  869. } else {
  870. // 如果是查询sql报错,那么直接返回
  871. if tmpErr.Error() != utils.ErrNoRow() {
  872. err = tmpErr
  873. return
  874. }
  875. // 后面没有报告了,那么就是直接往后面加就好了
  876. if !latelyPushTime.IsZero() {
  877. // 新记录需要发送的时间
  878. pushTime = latelyPushTime.Add(time.Duration(addTime) * time.Minute)
  879. // 如果当前时间晚于 新记录需要发送的时间 的话,那么发布时间就是现在
  880. if time.Now().After(pushTime) {
  881. pushTime = time.Now()
  882. isPrePush = false
  883. }
  884. return
  885. }
  886. }
  887. err = nil
  888. isPrePush = false
  889. return
  890. }
  891. // Demo 发送文字和图片
  892. func Demo() {
  893. description := `先说下玻璃吧,前面两三周还是比较看多05的,年报也是大方向上认为明年选择做多。但是,现在价格和成交已经不是很匹配了,短期盘面给出的升水以及绝对价格的角度来看,认为阶段性到目标位了,年前高位震荡看,策略上维持周报看法,前期多单可以适当止盈部分,轻仓观望。`
  894. description = `话不多说,到此结束。[翻白眼][翻白眼][旺柴][翻白眼]
  895. 这里是换行![翻白眼][捂脸]`
  896. urlStr := `https://hongze.oss-accelerate.aliyuncs.com/static/images/202301/20230103/CddIbfsYrcMCmhpmVgJyUU5Jyhjw.jpeg`
  897. urlStr = `https://hongze.oss-accelerate.aliyuncs.com/static/images/202301/20230103/siP2p9VjQMasEAZ52uFtl1CgNInS.png`
  898. //dataType, h5:1; 小程序:2;3:文字;4:图片
  899. SendThs("测试图片", "宏观", description, urlStr, "", "4")
  900. }