report.go 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
  1. package services
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/PuerkitoBio/goquery"
  7. "github.com/rdlucklib/rdluck_tools/http"
  8. "hongze/hz_eta_api/models"
  9. "hongze/hz_eta_api/models/company"
  10. "hongze/hz_eta_api/models/system"
  11. "hongze/hz_eta_api/services/alarm_msg"
  12. "hongze/hz_eta_api/services/public_api"
  13. "hongze/hz_eta_api/utils"
  14. "html"
  15. "os"
  16. "regexp"
  17. "strconv"
  18. "strings"
  19. "time"
  20. )
  21. func GetReportContentSub(content string) (contentSub string, err error) {
  22. content = html.UnescapeString(content)
  23. doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
  24. if err != nil {
  25. fmt.Println("create doc err:", err.Error())
  26. return
  27. }
  28. n := 0
  29. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  30. if n >= 5 {
  31. return
  32. }
  33. n++
  34. phtml, err := s.Html()
  35. if err != nil {
  36. fmt.Println("get html err", err.Error())
  37. return
  38. }
  39. if s.Text() != "" || strings.Contains(phtml, "src") {
  40. contentSub = contentSub + "<p>" + phtml + "</p>"
  41. }
  42. })
  43. return
  44. }
  45. type ZgParam struct {
  46. }
  47. // 找钢网
  48. func ZhaoGangSend(report *models.ReportDetail) (err error) {
  49. defer func() {
  50. if err != nil {
  51. go alarm_msg.SendAlarmMsg("发送报告至找刚网失败,Err"+err.Error(), 3)
  52. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送报告至找刚网失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
  53. }
  54. }()
  55. reportIdStr := strconv.Itoa(report.Id)
  56. articleId := utils.MD5(reportIdStr)
  57. var reportType int
  58. if report.ClassifyNameSecond == "知白守黑日评" {
  59. reportType = 1
  60. } else {
  61. reportType = 2
  62. }
  63. contentHtml := html.UnescapeString(report.Content)
  64. doc, err := goquery.NewDocumentFromReader(strings.NewReader(contentHtml))
  65. if err != nil {
  66. fmt.Println("Create Doc Err:" + err.Error())
  67. err = errors.New("Create Doc Err:" + err.Error())
  68. return
  69. }
  70. doc.Find("p").Each(func(i int, p *goquery.Selection) {
  71. phtml, err := p.Html()
  72. if err != nil {
  73. fmt.Println("Err:" + err.Error())
  74. return
  75. }
  76. if phtml == "<br/>" {
  77. if i == 0 {
  78. p.Remove()
  79. } else {
  80. p.ReplaceWithHtml("<br/>")
  81. }
  82. } else {
  83. p.SetAttr("style", "line-height:30px")
  84. }
  85. })
  86. doc.Find("img").Each(func(i int, img *goquery.Selection) {
  87. img.SetAttr("style", "width:100%;")
  88. })
  89. contentHtml, _ = doc.Find("body").Html()
  90. createDate, _ := time.Parse(utils.FormatDateTime, report.CreateTime)
  91. createDay := createDate.Format("0102")
  92. title := "【第 " + strconv.Itoa(report.Stage) + "期|FICC" + "】 " + report.Title + "(" + createDay + ")"
  93. contentSummary := report.Abstract
  94. postUrl := `http://appserver.index.zhaogang.com/double.index.appserver.service/api/v1/wechat/article/hongze/push`
  95. signStr := "zhaogang_data_vip" + "articleId" + articleId + "type" + strconv.Itoa(reportType) + "title" + title + "contentSummary" + contentSummary + "zhaogang_data_vip"
  96. fmt.Println("signStr:", signStr)
  97. sign := utils.MD5(signStr)
  98. disclaimers := `<p>1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。</p >         <p>2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。</p >         <p>3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。</p >         <p>4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。</p >`
  99. param := make(map[string]interface{})
  100. dataMap := make(map[string]interface{})
  101. contentMap := make(map[string]interface{})
  102. dataMap["articleId"] = articleId
  103. dataMap["type"] = reportType
  104. dataMap["title"] = title
  105. dataMap["contentSummary"] = contentSummary
  106. contentMap["contentHtml"] = contentHtml
  107. contentMap["articleAuthor"] = report.Author
  108. //contentMap["publishedTime"] = report.PublishTime.Format(utils.FormatDateTime)
  109. contentMap["publishedTime"] = report.PublishTime
  110. contentMap["audioName"] = report.VideoName
  111. contentMap["audioUrl"] = report.VideoUrl
  112. videoPlaySeconds := report.VideoPlaySeconds
  113. f, _ := strconv.ParseFloat(videoPlaySeconds, 64)
  114. contentMap["audioLength"] = f * 1000
  115. contentMap["disclaimers"] = disclaimers
  116. param["cardData"] = dataMap
  117. param["contentData"] = contentMap
  118. param["sign"] = strings.ToUpper(sign)
  119. paramJson, err := json.Marshal(param)
  120. if err != nil {
  121. fmt.Println("param json.Marshal Err:" + err.Error())
  122. err = errors.New("param json.Marshal Err:" + err.Error())
  123. return
  124. }
  125. utils.FileLog.Info("ZhaoGangSend parms:%s", string(paramJson))
  126. result, err := http.Post(postUrl, string(paramJson), "application/json")
  127. if err != nil {
  128. fmt.Println("post err:" + err.Error())
  129. err = errors.New("post Err:" + err.Error())
  130. return
  131. }
  132. utils.FileLog.Info("ZhaoGangSend Result:%s", string(result))
  133. //返回数据校验
  134. mapResult := make(map[string]interface{})
  135. err = json.Unmarshal(result, &mapResult)
  136. if err != nil {
  137. fmt.Println("找钢网返回数据转json失败: err:", err.Error(), ";返回数据:", string(result))
  138. err = errors.New(fmt.Sprint("找钢网返回数据转json失败: err:", err.Error(), ";返回数据:", string(result)))
  139. return
  140. }
  141. if resultCode, ok := mapResult["code"]; ok {
  142. tmpResultCode := resultCode.(float64)
  143. if tmpResultCode != 200 {
  144. fmt.Println("找钢网返回数据异常,code返回参异常;返回数据:", string(result))
  145. err = errors.New(fmt.Sprint("找钢网返回数据异常,code返回参异常;返回数据:", string(result)))
  146. return
  147. }
  148. } else {
  149. fmt.Println("找钢网返回数据异常,缺少code返回参;返回数据:", string(result))
  150. err = errors.New(fmt.Sprint("找钢网返回数据异常,缺少code返回参;返回数据:", string(result)))
  151. return
  152. }
  153. utils.FileLog.Info("%s", string(result))
  154. return
  155. }
  156. /*func init() {
  157. fmt.Println("start")
  158. vint := 845
  159. report, err := models.GetReportById(vint)
  160. if err != nil {
  161. fmt.Println("GetReportById Err", err.Error())
  162. return
  163. }
  164. ZhaoGangSend(report)
  165. fmt.Println("end")
  166. return
  167. }*/
  168. // PublishDayWeekReport 发布晨周报
  169. func PublishDayWeekReport(reportId int) (tips string, err error) {
  170. report, err := models.GetReportByReportId(reportId)
  171. if err != nil {
  172. return
  173. }
  174. if report.State == 2 {
  175. return
  176. }
  177. chapters, err := models.GetChapterListByReportId(reportId)
  178. if err != nil {
  179. return
  180. }
  181. chapterLen := len(chapters)
  182. if chapterLen <= 0 {
  183. err = errors.New("报告章节为空,不可发布")
  184. return
  185. }
  186. reportType := chapters[0].ReportType
  187. // 校验章节
  188. publishReport, tips, publishIdArr, unPublishIdArr, err := checkDayWeekChapterWrite(chapters, reportType)
  189. if err != nil {
  190. return
  191. }
  192. publishLen := len(publishIdArr)
  193. if publishLen <= 0 {
  194. err = errors.New("报告章节均不可发布")
  195. return
  196. }
  197. // 需发布整期
  198. updateCols := make([]string, 0)
  199. if publishReport {
  200. updateCols = append(updateCols, "Title", "State", "ModifyTime")
  201. // 发布后标题调整
  202. title := report.Title
  203. title = strings.ReplaceAll(title, "【弘则FICC晨报】", "")
  204. title = strings.ReplaceAll(title, "【弘则FICC周报】", "")
  205. if title == "" {
  206. // 取第一个需发布章节的标题
  207. firstId := publishIdArr[0]
  208. firstTitle := ""
  209. for i := 0; i < chapterLen; i++ {
  210. if chapters[i].ReportChapterId == firstId {
  211. firstTitle = chapters[i].Title
  212. break
  213. }
  214. }
  215. title = firstTitle
  216. }
  217. report.Title = title
  218. report.State = 2
  219. // 研报后台4.4 只在没有发布过时更新发布时间,其余均按模版消息发送时间当作发布时间
  220. if report.MsgIsSend == 0 || report.PublishTime.IsZero() {
  221. report.PublishTime = time.Now().Local()
  222. updateCols = append(updateCols, "PublishTime")
  223. }
  224. report.ModifyTime = time.Now().Local()
  225. }
  226. publishIdStr := utils.IntArr2joinString(publishIdArr, ",")
  227. //unPublishIdStr := utils.IntArr2joinString(unPublishIdArr, ",")
  228. if e := models.PublishReportAndChapter(report, publishIdArr, unPublishIdArr, publishReport, updateCols); e != nil {
  229. err = errors.New("发布报告及章节失败")
  230. return
  231. }
  232. // 生成章节音频
  233. go func() {
  234. _ = UpdateChaptersVideo(publishIdStr)
  235. }()
  236. // 更新报告ES
  237. go func() {
  238. _ = UpdateReportEs(report.Id, 2)
  239. }()
  240. return
  241. }
  242. // UpdateChaptersVideo 更新章节音频
  243. func UpdateChaptersVideo(chapterIds string) (err error) {
  244. defer func() {
  245. if err != nil {
  246. utils.FileLog.Error("UpdateChaptersVideo, chapterIds:%s, Err:%s", chapterIds, err.Error())
  247. go alarm_msg.SendAlarmMsg("更新章节音频失败, 章节ID: "+chapterIds+", Err: "+err.Error(), 3)
  248. }
  249. }()
  250. if chapterIds == "" {
  251. return
  252. }
  253. ids := make([]int, 0)
  254. chapterIdArr := strings.Split(chapterIds, ",")
  255. for _, v := range chapterIdArr {
  256. id, e := strconv.Atoi(v)
  257. if e != nil {
  258. return
  259. }
  260. ids = append(ids, id)
  261. }
  262. chapterList, err := models.GetChapterListByChapterIds(ids)
  263. if err != nil {
  264. return
  265. }
  266. // 生成video
  267. nowTime := time.Now()
  268. updateCols := make([]string, 0)
  269. updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds")
  270. for i := 0; i < len(chapterList); i++ {
  271. item := chapterList[i]
  272. // 忽略已有音频的章节
  273. if item.VideoUrl != "" && item.VideoName != "" && item.VideoSize != "" && item.VideoPlaySeconds != "" {
  274. continue
  275. }
  276. videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, html.UnescapeString(item.Content), nowTime.Format(utils.FormatDateTime))
  277. if e != nil {
  278. err = e
  279. return
  280. }
  281. item.VideoUrl = videoUrl
  282. item.VideoName = videoName
  283. item.VideoSize = videoSize
  284. item.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds)
  285. if e = item.UpdateChapter(updateCols); e != nil {
  286. err = e
  287. }
  288. }
  289. return
  290. }
  291. // PublishTodayDayReport 发布今日晨报
  292. func PublishTodayDayReport() (err error) {
  293. nowTime := time.Now()
  294. startTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
  295. endTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 23, 59, 59, 0, time.Local)
  296. todayReport, err := models.GetUnPublishDayReport(startTime, endTime)
  297. if err != nil {
  298. if err.Error() == utils.ErrNoRow() { //如果是找不到待发送的晨报,那么需要将err置空
  299. err = nil
  300. }
  301. return
  302. }
  303. if todayReport != nil {
  304. if _, tmpErr := PublishDayWeekReport(todayReport.Id); tmpErr != nil {
  305. err = tmpErr
  306. return
  307. }
  308. // 定时发布的晨报自动推送客群
  309. reportDetail, tmpErr := models.GetReportById(todayReport.Id)
  310. if tmpErr != nil {
  311. err = tmpErr
  312. return
  313. }
  314. // 推送模板消息
  315. if tmpErr = SendMiniProgramReportWxMsg(todayReport.Id); tmpErr != nil {
  316. err = tmpErr
  317. return
  318. }
  319. if tmpErr = models.ModifyReportThsMsgIsSend(reportDetail); tmpErr != nil {
  320. err = tmpErr
  321. return
  322. }
  323. }
  324. return
  325. }
  326. // UpdateReportEs 更新报告/章节Es
  327. func UpdateReportEs(reportId int, publishState int) (err error) {
  328. if reportId <= 0 {
  329. return
  330. }
  331. reportInfo, err := models.GetReportByReportId(reportId)
  332. if err != nil {
  333. return
  334. }
  335. categories := ""
  336. if reportInfo.HasChapter == 1 {
  337. // 晨周报
  338. chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
  339. if tmpErr != nil {
  340. return
  341. }
  342. if len(chapterList) > 0 {
  343. for i := 0; i < len(chapterList); i++ {
  344. // 章节对应的品种
  345. permissionList, tmpErr := models.GetChapterTypePermissionByTypeIdAndResearchType(chapterList[i].TypeId, chapterList[i].ReportType)
  346. if tmpErr != nil {
  347. return
  348. }
  349. categoryArr := make([]string, 0)
  350. if len(permissionList) > 0 {
  351. for ii := 0; ii < len(permissionList); ii++ {
  352. categoryArr = append(categoryArr, permissionList[ii].PermissionName)
  353. }
  354. }
  355. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  356. chapterCategories := strings.Join(aliasArr, ",")
  357. esChapter := &models.ElasticReportDetail{
  358. ReportId: chapterList[i].ReportId,
  359. ReportChapterId: chapterList[i].ReportChapterId,
  360. Title: chapterList[i].Title,
  361. Abstract: chapterList[i].Abstract,
  362. BodyContent: utils.TrimHtml(html.UnescapeString(chapterList[i].Content)),
  363. PublishTime: chapterList[i].PublishTime.Format(utils.FormatDateTime),
  364. PublishState: chapterList[i].PublishState,
  365. Author: chapterList[i].Author,
  366. ClassifyIdFirst: chapterList[i].ClassifyIdFirst,
  367. ClassifyNameFirst: chapterList[i].ClassifyNameFirst,
  368. ClassifyIdSecond: 0,
  369. ClassifyNameSecond: "",
  370. Categories: chapterCategories,
  371. StageStr: strconv.Itoa(chapterList[i].Stage),
  372. }
  373. chapterDocId := fmt.Sprintf("%d-%d", reportInfo.Id, chapterList[i].ReportChapterId)
  374. if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
  375. return
  376. }
  377. }
  378. }
  379. } else {
  380. permissionList, tmpErr := models.GetChartPermissionNameFromMappingByKeyword(reportInfo.ClassifyNameSecond, "rddp")
  381. if tmpErr != nil {
  382. return
  383. }
  384. categoryArr := make([]string, 0)
  385. for i := 0; i < len(permissionList); i++ {
  386. categoryArr = append(categoryArr, permissionList[i].PermissionName)
  387. }
  388. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  389. categories = strings.Join(aliasArr, ",")
  390. }
  391. // 新增报告ES
  392. esReport := &models.ElasticReportDetail{
  393. ReportId: reportInfo.Id,
  394. ReportChapterId: 0,
  395. Title: reportInfo.Title,
  396. Abstract: reportInfo.Abstract,
  397. BodyContent: utils.TrimHtml(html.UnescapeString(reportInfo.Content)),
  398. PublishTime: reportInfo.PublishTime.Format(utils.FormatDateTime),
  399. PublishState: publishState,
  400. Author: reportInfo.Author,
  401. ClassifyIdFirst: reportInfo.ClassifyIdFirst,
  402. ClassifyNameFirst: reportInfo.ClassifyNameFirst,
  403. ClassifyIdSecond: reportInfo.ClassifyIdSecond,
  404. ClassifyNameSecond: reportInfo.ClassifyNameSecond,
  405. Categories: categories,
  406. StageStr: strconv.Itoa(reportInfo.Stage),
  407. }
  408. docId := fmt.Sprintf("%d-%d", reportInfo.Id, 0)
  409. if err = EsAddOrEditReport(utils.EsReportIndexName, docId, esReport); err != nil {
  410. return
  411. }
  412. return
  413. }
  414. // addCategoryAliasToArr 品种别名
  415. func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error) {
  416. aliasArr = categoryArr
  417. if len(categoryArr) > 0 {
  418. for i := 0; i < len(categoryArr); i++ {
  419. if strings.Contains(categoryArr[i], "沥青") {
  420. aliasArr = append(aliasArr, "BU")
  421. }
  422. if strings.Contains(categoryArr[i], "MEG") {
  423. aliasArr = append(aliasArr, "EG", "乙二醇")
  424. }
  425. if strings.Contains(categoryArr[i], "聚酯") {
  426. aliasArr = append(aliasArr, "长丝", "短纤", "瓶片")
  427. }
  428. if strings.Contains(categoryArr[i], "纯苯+苯乙烯") {
  429. aliasArr = append(aliasArr, "EB")
  430. }
  431. if strings.Contains(categoryArr[i], "聚乙烯") {
  432. aliasArr = append(aliasArr, "PP", "PE")
  433. }
  434. if strings.Contains(categoryArr[i], "玻璃纯碱") {
  435. aliasArr = append(aliasArr, "玻璃", "纯碱", "FG", "SA")
  436. }
  437. if strings.Contains(categoryArr[i], "甲醇") {
  438. aliasArr = append(aliasArr, "甲醇", "MA")
  439. }
  440. if strings.Contains(categoryArr[i], "橡胶") {
  441. aliasArr = append(aliasArr, "橡胶", "RU")
  442. }
  443. }
  444. }
  445. return
  446. }
  447. // UpdateReportChapterEs 更新报告章节ES
  448. func UpdateReportChapterEs(reportChapterId int) (err error) {
  449. if reportChapterId <= 0 {
  450. return
  451. }
  452. chapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
  453. if err != nil {
  454. return
  455. }
  456. // 章节对应的品种
  457. permissionList, tmpErr := models.GetChapterTypePermissionByTypeIdAndResearchType(chapterInfo.TypeId, chapterInfo.ReportType)
  458. if tmpErr != nil {
  459. return
  460. }
  461. categoryArr := make([]string, 0)
  462. if len(permissionList) > 0 {
  463. for ii := 0; ii < len(permissionList); ii++ {
  464. categoryArr = append(categoryArr, permissionList[ii].PermissionName)
  465. }
  466. }
  467. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  468. categories := strings.Join(aliasArr, ",")
  469. // 新增/编辑ES
  470. esChapter := &models.ElasticReportDetail{
  471. ReportId: chapterInfo.ReportId,
  472. ReportChapterId: chapterInfo.ReportChapterId,
  473. Title: chapterInfo.Title,
  474. Abstract: chapterInfo.Abstract,
  475. BodyContent: utils.TrimHtml(html.EscapeString(chapterInfo.Content)),
  476. PublishTime: chapterInfo.PublishTime.Format(utils.FormatDateTime),
  477. PublishState: chapterInfo.PublishState,
  478. Author: chapterInfo.Author,
  479. ClassifyIdFirst: chapterInfo.ClassifyIdFirst,
  480. ClassifyNameFirst: chapterInfo.ClassifyNameFirst,
  481. ClassifyIdSecond: 0,
  482. ClassifyNameSecond: "",
  483. Categories: categories,
  484. StageStr: strconv.Itoa(chapterInfo.Stage),
  485. }
  486. chapterDocId := fmt.Sprintf("%d-%d", chapterInfo.ReportId, chapterInfo.ReportChapterId)
  487. if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
  488. return
  489. }
  490. return
  491. }
  492. // DeleteReportAndChapter 删除报告及章节
  493. func DeleteReportAndChapter(reportId int) (err error) {
  494. reportInfo, err := models.GetReportByReportId(reportId)
  495. if err != nil {
  496. err = errors.New("报告信息有误, Err: " + err.Error())
  497. return
  498. }
  499. if reportInfo.State == 2 {
  500. err = errors.New("报告已发布,不可删除")
  501. return
  502. }
  503. // 更新ES
  504. _ = UpdateReportEs(reportId, 1)
  505. // 删除
  506. if reportInfo.HasChapter == 1 && (reportInfo.ChapterType == utils.REPORT_TYPE_DAY || reportInfo.ChapterType == utils.REPORT_TYPE_WEEK) {
  507. err = models.DeleteDayWeekReportAndChapter(reportId)
  508. } else {
  509. err = models.DeleteReport(reportId)
  510. }
  511. if err != nil {
  512. err = errors.New("删除失败, Err: " + err.Error())
  513. return
  514. }
  515. // 重置PPT关联报告
  516. go func() {
  517. _ = ResetPPTReport(reportId, false)
  518. }()
  519. return
  520. }
  521. // UpdatePublishedReportToEs 更新已发布的报告ES
  522. func UpdatePublishedReportToEs() (err error) {
  523. // 获取所有已发布的报告
  524. var condition string
  525. var pars []interface{}
  526. condition = ` AND state = 2 `
  527. reportList, err := models.GetReportList(condition, pars, "ficc", 1, 5000)
  528. count := 0
  529. failCount := 0
  530. for i := 0; i < len(reportList); i++ {
  531. if err = UpdateReportEs(reportList[i].Id, 2); err != nil {
  532. fmt.Printf("更新失败, report_id: %d, Err: %s\n", reportList[i].Id, err.Error())
  533. failCount += 1
  534. } else {
  535. count += 1
  536. }
  537. }
  538. fmt.Printf("报告总数:%d, 更新成功数: %d, 更新失败数: %d", len(reportList), count, failCount)
  539. return
  540. }
  541. // MigrateOldReport 迁移今年旧数据
  542. func MigrateOldReport() (err error) {
  543. // 查询本年度报告
  544. // 20220616查询本年度周报
  545. reportList, err := models.GetMigrateReportList()
  546. if err != nil {
  547. fmt.Println("获取今年报告失败, Err: " + err.Error())
  548. return
  549. }
  550. lenReport := len(reportList)
  551. frequencyMap := map[string]string{
  552. "day": "日度",
  553. "week": "周度",
  554. "two_week": "双周度",
  555. "month": "月度",
  556. "other": "不定时",
  557. }
  558. classifyList, err := models.GetAllClassify()
  559. if err != nil {
  560. fmt.Println("获取分类失败, Err: " + err.Error())
  561. return
  562. }
  563. classifyIdName := make(map[int]string, 0)
  564. classifyNameId := make(map[string]int, 0)
  565. classifyIdParentId := make(map[int]int, 0)
  566. for i := 0; i < len(classifyList); i++ {
  567. classifyIdName[classifyList[i].Id] = classifyList[i].ClassifyName
  568. classifyNameId[classifyList[i].ClassifyName] = classifyList[i].Id
  569. classifyIdParentId[classifyList[i].Id] = classifyList[i].ParentId
  570. }
  571. typeList, err := models.GetReportChapterTypeList()
  572. if err != nil {
  573. fmt.Println("获取章节类型失败, Err: " + err.Error())
  574. return
  575. }
  576. typeIdName := make(map[int]string, 0)
  577. typeIdSort := make(map[int]int, 0)
  578. for i := 0; i < len(typeList); i++ {
  579. typeIdName[typeList[i].ReportChapterTypeId] = typeList[i].ReportChapterTypeName
  580. typeIdSort[typeList[i].ReportChapterTypeId] = typeList[i].Sort
  581. }
  582. // 报告ID集合
  583. idArr := make([]int, 0)
  584. for i := 0; i < lenReport; i++ {
  585. //idArr = append(idArr, strconv.Itoa(reportList[i].ResearchReportId))
  586. idArr = append(idArr, reportList[i].ResearchReportId)
  587. }
  588. //idStr := strings.Join(idArr, ",")
  589. // 获取报告章节
  590. reportTypeList, err := models.GetResearchReportTypeListByReportIds(idArr)
  591. if err != nil {
  592. fmt.Println("获取报告章节失败, Err: " + err.Error())
  593. return
  594. }
  595. // 获取非晨周报权限mapping
  596. reportPermissionList, err := models.GetChapterPermissionMappingByResearchReportIds(idArr)
  597. if err != nil {
  598. fmt.Println("获取报告权限失败, Err: " + err.Error())
  599. return
  600. }
  601. reportTypeListMap := make(map[int][]*models.ResearchReportType, 0)
  602. reportPermissionListMap := make(map[int][]*models.ChartPermissionChapterMapping, 0)
  603. for i := 0; i < lenReport; i++ {
  604. // 报告章节map
  605. rid := reportList[i].ResearchReportId
  606. reportTypeMap := make([]*models.ResearchReportType, 0)
  607. for ii := 0; ii < len(reportTypeList); ii++ {
  608. if rid == reportTypeList[ii].ResearchReportId {
  609. reportTypeMap = append(reportTypeMap, reportTypeList[ii])
  610. }
  611. }
  612. reportTypeListMap[rid] = reportTypeMap
  613. // 报告权限map
  614. reportPermissionMap := make([]*models.ChartPermissionChapterMapping, 0)
  615. for iii := 0; iii < len(reportPermissionList); iii++ {
  616. if rid == reportPermissionList[iii].ReportChapterTypeId {
  617. reportPermissionMap = append(reportPermissionMap, reportPermissionList[iii])
  618. }
  619. }
  620. reportPermissionListMap[rid] = reportPermissionMap
  621. }
  622. // 获取章节内容
  623. typeIdArr := make([]int, 0)
  624. for i := 0; i < len(reportTypeList); i++ {
  625. //typeIdArr = append(typeIdArr, strconv.Itoa(reportTypeList[i].ResearchReportTypeId))
  626. typeIdArr = append(typeIdArr, reportTypeList[i].ResearchReportTypeId)
  627. }
  628. //typeIdStr := strings.Join(typeIdArr, ",")
  629. reportContentList, err := models.GetResearchReportTypeContentListByReportTypeIds(typeIdArr)
  630. if err != nil {
  631. fmt.Println("获取报告内容失败, Err: " + err.Error())
  632. return
  633. }
  634. // 获取章节ticker
  635. reportTickerList, err := models.GetResearchReportTypeTickerListByReportTypeIds(typeIdArr)
  636. if err != nil {
  637. fmt.Println("获取报告ticker失败, Err: " + err.Error())
  638. return
  639. }
  640. reportContentListMap := make(map[int][]*models.ResearchReportTypeContent, 0)
  641. reportTickerListMap := make(map[int][]*models.ResearchReportTypeTicker, 0)
  642. for i := 0; i < len(reportTypeList); i++ {
  643. // 报告章节map
  644. rtid := reportTypeList[i].ResearchReportTypeId
  645. reportContentMap := make([]*models.ResearchReportTypeContent, 0)
  646. for ii := 0; ii < len(reportContentList); ii++ {
  647. if rtid == reportContentList[ii].ResearchReportTypeId {
  648. reportContentMap = append(reportContentMap, reportContentList[ii])
  649. }
  650. }
  651. reportContentListMap[rtid] = reportContentMap
  652. // 报告ticker
  653. reportTickerMap := make([]*models.ResearchReportTypeTicker, 0)
  654. for iii := 0; iii < len(reportTickerList); iii++ {
  655. if rtid == reportTickerList[iii].ResearchReportTypeId {
  656. reportTickerMap = append(reportTickerMap, reportTickerList[iii])
  657. }
  658. }
  659. reportTickerListMap[rtid] = reportTickerMap
  660. }
  661. fmt.Printf("开始写入, 总数:%d\n", lenReport)
  662. countSuccess := 0
  663. countIgnore := 0
  664. for i := 0; i < lenReport; i++ {
  665. fmt.Printf("正在写入报告:%d\n", reportList[i].ResearchReportId)
  666. // 报告去重
  667. exist, tmpErr := models.GetNewReportExist(reportList[i].ResearchReportId)
  668. if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
  669. fmt.Println("查询是否迁移过该报告失败: ", reportList[i].ResearchReportId)
  670. return
  671. }
  672. if exist != nil {
  673. countIgnore += 1
  674. fmt.Println("跳过报告: ", reportList[i].ResearchReportId)
  675. continue
  676. }
  677. newReportId := 0
  678. item := reportList[i]
  679. reportTypeList = reportTypeListMap[item.ResearchReportId]
  680. isIgnore := false
  681. // 报告内容、章节
  682. if len(reportTypeList) > 0 {
  683. newReport := new(models.Report)
  684. newReport.AddType = 1
  685. // 标题去掉【】
  686. re, _ := regexp.Compile("^【[^】]*】")
  687. newTitle := re.ReplaceAllString(item.ResearchReportName, "")
  688. prefix := ""
  689. newReport.Title = prefix + newTitle
  690. newReport.Author = item.Author
  691. newReport.Frequency = frequencyMap[item.Type]
  692. newReport.CreateTime = item.CreatedTime
  693. modifyTime, _ := time.ParseInLocation(utils.FormatDateTime, item.CreatedTime, time.Local)
  694. newReport.ModifyTime = modifyTime
  695. newReport.State = 2
  696. newReport.PublishTime = item.ResearchReportDate
  697. newReport.Stage = item.Periods
  698. newReport.MsgIsSend = 1
  699. newReport.ThsMsgIsSend = 1
  700. newReport.ReportVersion = 1
  701. newReport.OldReportId = item.ResearchReportId
  702. if item.Type == utils.REPORT_TYPE_DAY || item.Type == utils.REPORT_TYPE_WEEK {
  703. // 晨周报
  704. newReport.HasChapter = 1
  705. newReport.ChapterType = item.Type
  706. classifyIdFirst := 0
  707. classifyNameFirst := ""
  708. if item.Type == utils.REPORT_TYPE_DAY {
  709. newReport.ClassifyIdFirst = classifyNameId["晨报"]
  710. newReport.ClassifyNameFirst = "晨报"
  711. classifyIdFirst = classifyNameId["晨报"]
  712. classifyNameFirst = "晨报"
  713. } else {
  714. newReport.ClassifyIdFirst = classifyNameId["周报"]
  715. newReport.ClassifyNameFirst = "周报"
  716. classifyIdFirst = classifyNameId["周报"]
  717. classifyNameFirst = "周报"
  718. }
  719. newDayWeekReport := new(models.CreateDayWeekReport)
  720. newChapterList := make([]*models.CreateDayWeekReportChapter, 0)
  721. for _, chapter := range reportTypeList {
  722. chapterAndTicker := new(models.CreateDayWeekReportChapter)
  723. tmpTickerList := make([]*models.ReportChapterTicker, 0)
  724. chapterContents := ""
  725. // 章节内容列表
  726. contentList := reportContentListMap[chapter.ResearchReportTypeId]
  727. for _, contents := range contentList {
  728. chapterContents += contents.Content
  729. }
  730. chapterContents, tmpErr := replaceReportBase64ToImg(chapterContents)
  731. if tmpErr != nil {
  732. fmt.Println("转换章节base64图片失败, Err: " + tmpErr.Error())
  733. return
  734. }
  735. state := 2
  736. if chapterContents == "" {
  737. state = 1
  738. }
  739. contentSub, tmpErr := GetReportContentSub(chapterContents)
  740. if tmpErr != nil {
  741. fmt.Println("解析 ContentSub 失败, Err: " + tmpErr.Error())
  742. return
  743. }
  744. chapterContents = html.EscapeString(chapterContents)
  745. contentSub = html.EscapeString(contentSub)
  746. chapterAndTicker.Chapter = &models.ReportChapter{
  747. ReportType: item.Type,
  748. ClassifyIdFirst: classifyIdFirst,
  749. ClassifyNameFirst: classifyNameFirst,
  750. TypeId: chapter.TypeId,
  751. TypeName: typeIdName[chapter.TypeId],
  752. Sort: typeIdSort[chapter.TypeId],
  753. Title: chapter.ResearchReportTypeTitle,
  754. AddType: 1,
  755. Author: item.Author,
  756. Content: chapterContents,
  757. ContentSub: contentSub,
  758. Stage: item.Periods,
  759. Trend: chapter.Trend,
  760. IsEdit: 1,
  761. PublishState: state,
  762. PublishTime: chapter.LastUpdatedTime,
  763. CreateTime: chapter.CreatedTime,
  764. ModifyTime: chapter.LastUpdatedTime,
  765. }
  766. // 晨报数据指标
  767. if item.Type == utils.REPORT_TYPE_DAY {
  768. tickerList := reportTickerListMap[chapter.ResearchReportTypeId]
  769. for _, ticker := range tickerList {
  770. tmpTickerList = append(tmpTickerList, &models.ReportChapterTicker{
  771. Sort: ticker.Sort,
  772. Ticker: ticker.Ticker,
  773. CreateTime: ticker.CreatedTime,
  774. UpdateTime: ticker.LastUpdatedTime,
  775. })
  776. }
  777. }
  778. chapterAndTicker.TickerList = tmpTickerList
  779. newChapterList = append(newChapterList, chapterAndTicker)
  780. }
  781. newDayWeekReport.Report = newReport
  782. newDayWeekReport.ChapterList = newChapterList
  783. // 新增晨周报
  784. newId, newErr := models.CreateMigrateNewDayWeekReport(newDayWeekReport)
  785. if newErr != nil {
  786. fmt.Println("新增晨周报失败, Err: " + newErr.Error())
  787. return
  788. }
  789. newReportId = newId
  790. } else {
  791. // 非晨周报
  792. if item.ReportVariety == "铁矿库存点评" {
  793. item.ReportVariety = "铁矿库存数据点评"
  794. }
  795. //newReport.Abstract = newTitle
  796. newReport.ClassifyIdSecond = classifyNameId[item.ReportVariety]
  797. newReport.ClassifyNameSecond = item.ReportVariety
  798. newReport.ClassifyIdFirst = classifyIdParentId[newReport.ClassifyIdSecond]
  799. newReport.ClassifyNameFirst = classifyIdName[newReport.ClassifyIdFirst]
  800. // 忽略分类不完整的
  801. if newReport.ClassifyIdFirst == 0 || newReport.ClassifyIdSecond == 0 || newReport.ClassifyNameFirst == "" || newReport.ClassifyNameSecond == "" {
  802. isIgnore = true
  803. countIgnore += 1
  804. continue
  805. }
  806. reportContents := ""
  807. for _, chapter := range reportTypeList {
  808. // 章节内容列表
  809. contentList := reportContentListMap[chapter.ResearchReportTypeId]
  810. for _, contents := range contentList {
  811. reportContents += contents.Content
  812. }
  813. }
  814. reportContents, tmpErr := replaceReportBase64ToImg(reportContents)
  815. if tmpErr != nil {
  816. fmt.Println("转换报告base64图片失败, Err: " + tmpErr.Error())
  817. return
  818. }
  819. newReport.State = 2
  820. if reportContents == "" {
  821. newReport.State = 1
  822. }
  823. reportContentSub, tmpErr := GetReportContentSub(reportContents)
  824. if tmpErr != nil {
  825. fmt.Println("解析 ContentSub 失败, Err: " + tmpErr.Error())
  826. return
  827. }
  828. newReport.Content = html.EscapeString(reportContents)
  829. newReport.ContentSub = html.EscapeString(reportContentSub)
  830. // 报告权限列表
  831. oldMapping := reportPermissionListMap[item.ResearchReportId]
  832. // 新增非晨周报
  833. newId, newErr := models.CreateMigrateNewOtherReport(newReport, oldMapping)
  834. if newErr != nil {
  835. fmt.Println("新增非晨周报失败, Err: " + newErr.Error())
  836. return
  837. }
  838. newReportId = newId
  839. }
  840. }
  841. // 更新音频
  842. if !isIgnore {
  843. countSuccess += 1
  844. fmt.Printf("写入报告%d成功, 新ID%d\n", reportList[i].ResearchReportId, newReportId)
  845. //go UpdateReportVideo(newReportId)
  846. UpdateReportVideo(newReportId)
  847. }
  848. }
  849. fmt.Printf("结束写入, 总数:%d, 忽略:%d, 成功:%d\n", lenReport, countIgnore, countSuccess)
  850. return
  851. }
  852. // 替换报告内容中的base64图片
  853. func replaceReportBase64ToImg(content string) (newContent string, err error) {
  854. if content == "" {
  855. return
  856. }
  857. pattern := "data:([a-z]+\\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?"
  858. re, _ := regexp.Compile(pattern)
  859. matcher := re.FindAllString(content, 999)
  860. if len(matcher) > 0 {
  861. for _, v := range matcher {
  862. imgUrl, tmpErr := reportBase64ToImg(v)
  863. if tmpErr != nil {
  864. err = tmpErr
  865. return
  866. }
  867. content = strings.ReplaceAll(content, v, imgUrl)
  868. }
  869. }
  870. newContent = content
  871. return
  872. }
  873. // 转换base64图片为img并上传
  874. func reportBase64ToImg(imageBase64 string) (resourceUrl string, err error) {
  875. if imageBase64 == "" {
  876. err = errors.New("图片为空")
  877. return
  878. }
  879. ext := ".png"
  880. uploadDir := "./static"
  881. randStr := utils.GetRandStringNoSpecialChar(28)
  882. fileName := randStr + ext
  883. fpath := uploadDir + "/" + fileName
  884. b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imageBase64)
  885. if !b {
  886. err = errors.New("图片格式不正确")
  887. return
  888. }
  889. re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
  890. base64Str := re.ReplaceAllString(imageBase64, "")
  891. base64Str = strings.Replace(base64Str, " ", "", -1)
  892. err = utils.SaveBase64ToFile(base64Str, fpath)
  893. if err != nil {
  894. err = errors.New("图片保存失败" + err.Error())
  895. return
  896. }
  897. defer os.Remove(fpath)
  898. hzUploadDir := "static/images/"
  899. savePath := hzUploadDir + time.Now().Format("200601/20060102/")
  900. savePath += fileName
  901. //上传到阿里云
  902. err = UploadFileToAliyun(fileName, fpath, savePath)
  903. if err != nil {
  904. err = errors.New("文件上传失败" + err.Error())
  905. return
  906. }
  907. fileHost := "https://hzstatic.hzinsights.com/"
  908. resourceUrl = fileHost + savePath
  909. item := new(models.Resource)
  910. item.ResourceUrl = resourceUrl
  911. item.ResourceType = 1
  912. item.CreateTime = time.Now()
  913. _, err = models.AddResource(item)
  914. if err != nil {
  915. err = errors.New("资源上传失败" + err.Error())
  916. return
  917. }
  918. return
  919. }
  920. // UpdateReportVideo 更新报告及其章节音频
  921. func UpdateReportVideo(reportId int) (err error) {
  922. defer func() {
  923. if err != nil {
  924. utils.FileLog.Error("UpdateReportVideo, reportId:%s, Err:%s", strconv.Itoa(reportId), err.Error())
  925. go alarm_msg.SendAlarmMsg("更新报告音频失败, 报告ID: "+strconv.Itoa(reportId)+", Err: "+err.Error(), 3)
  926. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "更新报告音频失败, 报告ID: " + reportIdStr + ", Err: "+err.Error(), utils.EmailSendToUsers)
  927. }
  928. }()
  929. if reportId == 0 {
  930. return
  931. }
  932. reportInfo, err := models.GetReportByReportId(reportId)
  933. if err != nil {
  934. return
  935. }
  936. if reportInfo.HasChapter == 1 {
  937. // 更新章节音频
  938. chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
  939. if tmpErr != nil {
  940. err = tmpErr
  941. return
  942. }
  943. chapterIdArr := make([]string, 0)
  944. for i := 0; i < len(chapterList); i++ {
  945. chapterIdArr = append(chapterIdArr, strconv.Itoa(chapterList[i].ReportChapterId))
  946. }
  947. chapterIds := strings.Join(chapterIdArr, ",")
  948. //go UpdateChaptersVideo(chapterIds)
  949. err = UpdateChaptersVideo(chapterIds)
  950. } else {
  951. // 更新报告音频
  952. if reportInfo.VideoUrl != "" {
  953. return
  954. }
  955. nowTime := time.Now()
  956. updateCols := make([]string, 0)
  957. updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds")
  958. videoUrl, videoName, videoSize, videoPlaySeconds, tmpErr := CreateReportVideo(reportInfo.Title, html.UnescapeString(reportInfo.Content), nowTime.Format(utils.FormatDateTime))
  959. reportInfo.VideoUrl = videoUrl
  960. reportInfo.VideoName = videoName
  961. reportInfo.VideoSize = videoSize
  962. reportInfo.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds)
  963. tmpErr = reportInfo.UpdateReport(updateCols)
  964. if tmpErr != nil {
  965. err = tmpErr
  966. return
  967. }
  968. }
  969. return
  970. }
  971. func UpdateEmptyVideoReportVideo() (err error) {
  972. list, err := models.GetSyncEmptyVideoReport()
  973. if err != nil {
  974. return
  975. }
  976. listLen := len(list)
  977. if listLen <= 0 {
  978. fmt.Println("无报告需要更新音频")
  979. return
  980. }
  981. fmt.Println("Start 待更新报告音频数: ", listLen)
  982. for i := 0; i < listLen; i++ {
  983. if err = UpdateReportVideo(list[i].Id); err != nil {
  984. fmt.Printf("更新音频失败")
  985. fmt.Println(err.Error())
  986. return
  987. }
  988. }
  989. fmt.Println("End 报告音频更新完毕")
  990. return
  991. }
  992. // checkDayWeekChapterWrite 校验晨周报已写章节与本期应写章节
  993. func checkDayWeekChapterWrite(chapters []*models.ReportChapter, reportType string) (publishReport bool, tips string, publishIdArr, unPublishIdArr []int, err error) {
  994. nowTime := time.Now().Local()
  995. updateTypeArr := make([]int, 0) // 需更新的章节类型IDs
  996. publishIdArr = make([]int, 0) // 需发布的章节IDs
  997. unPublishIdArr = make([]int, 0) // 需取消发布/未发布的章节IDs
  998. // 校验章节内容
  999. if reportType == utils.REPORT_TYPE_DAY {
  1000. // 晨报章节不能都为空
  1001. isEmpty := true
  1002. for i := 0; i < len(chapters); i++ {
  1003. if chapters[i].Content != "" && chapters[i].Title != "" {
  1004. isEmpty = false
  1005. break
  1006. }
  1007. }
  1008. if isEmpty {
  1009. err = errors.New("报告章节内容均为空或标题为空,不可发布")
  1010. return
  1011. }
  1012. } else {
  1013. // 周报章节需至少有一篇已编辑且有标题
  1014. editNum := 0
  1015. for i := 0; i < len(chapters); i++ {
  1016. if chapters[i].IsEdit == 1 && chapters[i].Title != "" {
  1017. editNum += 1
  1018. }
  1019. }
  1020. if editNum == 0 {
  1021. err = errors.New("报告均未编辑或标题为空,不可发布")
  1022. return
  1023. }
  1024. }
  1025. // 章节类型列表
  1026. types, e := models.GetReportChapterTypeListByResearchType(reportType)
  1027. if e != nil {
  1028. err = errors.New("获取章节类型列表失败")
  1029. return
  1030. }
  1031. // 本期需更新的章节IDs
  1032. typeLen := len(types)
  1033. for i := 0; i < typeLen; i++ {
  1034. if types[i].IsSet != 1 && types[i].Enabled != 0 {
  1035. // 正常更新
  1036. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  1037. } else {
  1038. // 被设置为零值的也算作正常更新
  1039. if types[i].PauseStartTime == utils.EmptyDateStr && types[i].PauseEndTime == utils.EmptyDateStr {
  1040. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  1041. continue
  1042. }
  1043. // 暂停更新需校验时间
  1044. startTime, _ := time.Parse(utils.FormatDate, types[i].PauseStartTime)
  1045. endTime, _ := time.Parse(utils.FormatDate, types[i].PauseEndTime)
  1046. if nowTime.Before(startTime) || nowTime.After(endTime.AddDate(0, 0, 1)) {
  1047. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  1048. }
  1049. }
  1050. }
  1051. // 校验本期需更新的章节是否都已编辑
  1052. chapterLen := len(chapters)
  1053. updateTypeLen := len(updateTypeArr)
  1054. tipsArr := make([]string, 0)
  1055. for i := 0; i < chapterLen; i++ {
  1056. isWrite := false
  1057. for ii := 0; ii < updateTypeLen; ii++ {
  1058. // 本期应发布的章节
  1059. if chapters[i].TypeId == updateTypeArr[ii] {
  1060. // 标题或者内容为空的情况下, 记录tips提示信息且不发布该章节
  1061. if chapters[i].Title == "" || chapters[i].Content == "" {
  1062. tipsArr = append(tipsArr, chapters[i].TypeName)
  1063. break
  1064. }
  1065. isWrite = true
  1066. break
  1067. }
  1068. }
  1069. if isWrite {
  1070. publishIdArr = append(publishIdArr, chapters[i].ReportChapterId)
  1071. } else {
  1072. unPublishIdArr = append(unPublishIdArr, chapters[i].ReportChapterId)
  1073. }
  1074. }
  1075. if len(tipsArr) > 0 {
  1076. tips = "部分章节未发布:" + strings.Join(tipsArr, "、") + "未填写标题/内容"
  1077. }
  1078. // 周报需发布的章节与需更新的章节数相等则表示可发布整期, 晨报无限制
  1079. if reportType == utils.REPORT_TYPE_DAY {
  1080. publishReport = true
  1081. } else {
  1082. if len(publishIdArr) == updateTypeLen {
  1083. publishReport = true
  1084. }
  1085. }
  1086. return
  1087. }
  1088. // PcCreateAndUploadSunCode 生成太阳码并上传OSS
  1089. func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) {
  1090. if page == "" {
  1091. err = errors.New("page不能为空")
  1092. return
  1093. }
  1094. // scene超过32位会生成失败,md5处理至32位
  1095. sceneMD5 := "a=1"
  1096. if scene != "" {
  1097. sceneMD5 = utils.MD5(scene)
  1098. }
  1099. picByte, err := GetSunCode(page, sceneMD5)
  1100. if err != nil {
  1101. return
  1102. }
  1103. // 生成图片
  1104. localPath := "./static/imgs"
  1105. fileName := utils.GetRandStringNoSpecialChar(28) + ".png"
  1106. fpath := fmt.Sprint(localPath, "/", fileName)
  1107. f, err := os.Create(fpath)
  1108. if err != nil {
  1109. fmt.Println("11111")
  1110. return
  1111. }
  1112. if _, err = f.Write(picByte); err != nil {
  1113. return
  1114. }
  1115. defer func() {
  1116. f.Close()
  1117. os.Remove(fpath)
  1118. }()
  1119. // 上传OSS
  1120. fileDir := "yb/suncode/"
  1121. imgUrl, err = UploadAliyunToDir(fileName, fpath, "", fileDir)
  1122. if err != nil {
  1123. return
  1124. }
  1125. if err != nil {
  1126. return
  1127. }
  1128. // 记录参数
  1129. if scene != "" {
  1130. newSuncode := &models.YbPcSuncode{
  1131. Scene: scene,
  1132. SceneMd5: sceneMD5,
  1133. CodePage: page,
  1134. SuncodeUrl: imgUrl,
  1135. CreateTime: time.Now(),
  1136. }
  1137. err = models.AddYbPcSunCode(newSuncode)
  1138. }
  1139. // 记录参数md5
  1140. if scene != "" {
  1141. newPars := &models.YbSuncodePars{
  1142. Scene: scene,
  1143. SceneKey: sceneMD5,
  1144. CreateTime: time.Now(),
  1145. }
  1146. err = models.AddYbSuncodePars(newPars)
  1147. }
  1148. return
  1149. }
  1150. // CreateNewReport 创建新报告
  1151. func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId int64, reportCode, errMsg string, err error) {
  1152. contentSub := ""
  1153. if req.Content != "" {
  1154. contentClean, e := FilterReportContentBr(req.Content)
  1155. if e != nil {
  1156. errMsg = "内容去除前后空格失败"
  1157. err = errors.New("内容去除前后空格失败, Err: " + e.Error())
  1158. return
  1159. }
  1160. req.Content = contentClean
  1161. sub, e := GetReportContentSub(req.Content)
  1162. if e != nil {
  1163. go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+e.Error(), 3)
  1164. }
  1165. contentSub = sub
  1166. }
  1167. maxStage, e := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond)
  1168. if e != nil {
  1169. errMsg = "期数获取失败!"
  1170. err = errors.New("期数获取失败,Err:" + e.Error())
  1171. return
  1172. }
  1173. item := new(models.Report)
  1174. item.AddType = req.AddType
  1175. item.ClassifyIdFirst = req.ClassifyIdFirst
  1176. item.ClassifyNameFirst = req.ClassifyNameFirst
  1177. item.ClassifyIdSecond = req.ClassifyIdSecond
  1178. item.ClassifyNameSecond = req.ClassifyNameSecond
  1179. item.Title = req.Title
  1180. item.Abstract = req.Abstract
  1181. item.Author = req.Author
  1182. item.Frequency = req.Frequency
  1183. item.State = req.State
  1184. item.Content = html.EscapeString(req.Content)
  1185. item.Stage = maxStage + 1
  1186. item.ContentSub = html.EscapeString(contentSub)
  1187. item.CreateTime = req.CreateTime
  1188. item.ModifyTime = time.Now()
  1189. item.ReportVersion = req.ReportVersion
  1190. item.AdminId = adminInfo.AdminId
  1191. item.AdminRealName = adminInfo.RealName
  1192. newReportId, e = models.AddReport(item)
  1193. if e != nil {
  1194. errMsg = "保存失败"
  1195. err = errors.New("保存失败,Err:" + e.Error())
  1196. return
  1197. }
  1198. //处理权限
  1199. {
  1200. permissionItems, err := models.GetPermission(req.ClassifyNameSecond)
  1201. if err != nil {
  1202. go alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
  1203. //utils.SendEmail(utils.APPNAME+"失败提醒", "获取权限失败,Err:"+err.Error(), utils.EmailSendToUsers)
  1204. }
  1205. for _, v := range permissionItems {
  1206. err = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
  1207. if err != nil {
  1208. go alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
  1209. //utils.SendEmail(utils.APPNAME+"失败提醒", "新增权限失败,Err:"+err.Error()+strconv.FormatInt(newReportId, 10), utils.EmailSendToUsers)
  1210. }
  1211. }
  1212. }
  1213. reportCode = utils.MD5(strconv.Itoa(int(newReportId)))
  1214. //修改唯一编码
  1215. {
  1216. go models.ModifyReportCode(newReportId, reportCode)
  1217. }
  1218. return
  1219. }
  1220. // FilterReportContentBr 过滤报告正文前后换行符
  1221. func FilterReportContentBr(content string) (res string, err error) {
  1222. newContent := content
  1223. //content = `<p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><strong>四季度验证投产预期</strong></p><p style=\"font-size: 16px;\"><strong>&nbsp;</strong></p><p style=\"font-size: 16px;\"><strong>PTA:<strong><strong>近端短缺的格局出现缓解的迹象,表现在:PX进口回升;国内重整及常减压提负;PTA、PX投产在即</strong></strong><strong>。正套部分或全部止盈。</strong></strong></p><p style=\"font-size: 16px;\"><strong>&nbsp;</strong></p><p style=\"font-size: 16px;\"><strong>乙二醇:</strong><strong style=\"font-weight: 700; color: rgb(0, 0, 0); font-family: ;\">到港预报集中的情况下仍在去库,4200-4600区间震荡操作,节前没有明显方向,不建议在节前备货的时间段布局空单</strong><strong>。</strong></p><p style=\"font-size: 16px;\"><strong>&nbsp;</strong></p><p style=\"font-size: 16px;\"><strong>1、PX国内供应本周回落,PTA工厂负荷变动滞后于PX装置。</strong></p><p style=\"font-size: 16px;\">PX装置变动:</p><p style=\"font-size: 16px;\">海南炼化一期66万吨PX装置因故障停车检修,重启时间待跟踪,其二期100万吨PX装置预计在此装置重启后停车检修。</p><p style=\"font-size: 16px;\">天津石化一套100万吨重整已于20日重启中,其39万吨PX预计下周初出产品。</p><p style=\"font-size: 16px;\">韩国SK 位于仁川的130万吨PX装置按计划在23日停车检修,计划检修时长45天左右。</p><p style=\"font-size: 16px;\">截至周五,中国国内PX负荷小幅回落至73.4%(前值<span style=\"color: rgb(65, 65, 65); font-family: sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">76.8</span>%),亚洲PX负荷小幅回落至68.7%(<span style=\"color: rgb(65, 65, 65); font-family: sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">72</span>%)。</p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/4VcA2eUE1Sgkx0KQ0NjchM0ajIis.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/vi3laEb3qejWISetD0SjGTwuAxUg.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=760c86a78c35ab9286903e9ce52a6682\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=1e36d11158bf146313f3f65a65dbc5f3\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=7a69494b1a84520b2cdf1dccb3c78bda\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=0a2e374c0689d5fba4a9dc40474a33b0\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"font-size: 16px;\">PXN本周延续回落至380美金附近。</p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=ad1b869f60ab31060730e8bd58260d8c\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=d1c87cb3adff22aea0fc37dfb1870626\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=bac98256b0b86e92ed4084fbbd223f5b\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><span style=\"font-size: 16px; font-family: Arial, Helvetica, sans-serif;\">PX平衡表</span></p><p style=\"margin: 0px; padding: 0px; font-size: 16px; color: rgb(0, 0, 0); font-family: ;\">海关统计,国内8月PX进口总量在78.8万吨,环比增加17.3万吨,增幅28%;同比减少30.8万吨,降幅28.1%;8月PX出口量0.47万吨,环比下降5万吨。</p><p style=\"margin: 0px; padding: 0px; font-size: 16px; color: rgb(0, 0, 0); font-family: ;\"><span style=\"color: rgb(0, 0, 0); font-family: ;\">PX 8月进口量大幅回升至79万吨附近,其中8月从韩国进口的PX量达到33.65万吨。9月目前公布的1-20号从韩国进口PX的量已经接近8月全月的水平,即环比8月仍是大幅增加的情况:</span><span style=\"font-size: 16px;\">据悉,9月1-20日韩国PX出口总量在30.9万吨,其中出口至中国27.9万吨。</span></p><p style=\"margin: 0px; padding: 0px; font-size: 16px; color: rgb(0, 0, 0); font-family: ;\"><span style=\"font-size: 16px;\">预计9月进口量环比8月进一步增加。</span></p><p style=\"margin: 0px; padding: 0px; font-size: 16px; color: rgb(0, 0, 0); font-family: ;\"><span style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/TD59rptBDNwJ7J2qpxd8HXi4X5cQ.png\" class=\"fr-fic fr-dib fr-draggable\"></span></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><strong><span style=\"color: rgb(0, 0, 0); font-family: ;\">原油本周偏弱,欧美汽油利润本周均出现了明显回升。</span></strong></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><strong><span style=\"color: rgb(0, 0, 0); font-family: ;\"><span style=\"color: rgb(34, 34, 34); font-family: system-ui, -apple-system, BlinkMacSystemFont, ;\">美国飓风将在下周登陆墨西哥湾,届时或将影响海上钻机的运行以及炼厂开工。</span>&nbsp;</span></strong></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=ef172d65e20b986351ed50545f3d36d1\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=4930c0981fc33ab125e6ef346c9cbd5b\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=f0cae34883508273d482d4cf8c577410\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><strong>2、</strong><strong>乙烯连续跌价至900美金,PX投产压力临近PXN延续压缩。石脑油亏损本周延续修复。</strong></p><p style=\"font-size: 16px;\">PTA:按照PX11-12月上1054美金(石脑油672美金,Brent86.15美金)计算,醋酸3075元,目前含醋酸的原料成本在5670元附近。可以发现虽然Brent和PX环比上周都出现了明显的下跌,但石脑油相对上周环比上涨,石脑油亏损本周延续修复,目前石脑油-Brent价差回升至0以上。</p><p style=\"font-size: 16px;\">给到200-300的最低加工成本,PTA的估值在5870-5970元。周五日盘收盘后TA11合约5742,11月及之后的PTA合约均亏损。</p><p style=\"font-size: 16px;\">PTA基差再度回升至1000附近,周五小幅走弱至970。</p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=3e743a3907292d9ad2dce6033ae7d8a4\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/Csak38HlAbt8XsbBsPeSWlaUKJiO.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/LOPKDaDnRnbJauWqlvbhQ5VNxa03.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/Ocdq2m3bMDqZnM8PTXVyj8XILt7Z.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=268b3f7204aa21accb8d292154cf4e3c\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"font-size: 16px;\">乙二醇:静态来看,按照盘面(煤价按照900元),锚定石脑油672美金,乙烯900美金,甲醇2640元,北美乙烷价格38美分/加仑计算,乙二醇的综合成本仍在5050元附近。周五日盘收盘01合约按照综合成本亏损700元附近。</p><p style=\"font-size: 16px;\">国内外采乙烷制乙二醇目前扭亏为盈。</p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=f1e3ba1df230353571d00af2dfb7e92a\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=0d17c186dda211dab039f2ccd39a8d76\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=3a93cb16f02731bc5c74b560f9590456\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=e1a38d9058b719d8dd5bf746bc9739be\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/drjYJ468Tpnr3Bno52CQZXRuayow.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/bv4JvZN7OxhXFnx0xL0EW6yGFlX8.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220918/54TYCFo1ulvOhbJpk9jQPJcnDyan.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/fZayI9g8kS2ILeVu799dBnAdugNB.png\" class=\"fr-fic fr-dib fr-draggable\"></p><ol style=\"margin: 0px; padding: 0px; list-style: none; color: rgb(0, 0, 0); font-family: ;\"><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><strong>3、</strong><span style=\"font-size: 16px; color: rgb(0, 0, 0); font-family: ;\"><strong>需求端本周仍偏弱:继上周加弹负荷下滑后,本周织造负荷下滑。延续坯布库存回升同时原料库存下降,聚酯连续累库(瓶片低库存优势也明显减弱),两家大厂减产执行过程和节前备货中和,因此减产去库成效甚微</strong><strong>。</strong></span><span style=\"font-size: 16px;\">&nbsp;</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">终端:江浙加弹综合开工微幅回升至77%(前值</span><span style=\"font-size: 16px; color: rgb(0, 0, 0); font-family: Tahoma, ;\">76</span><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">%);江浙织机综合开工回落至69%(前值</span><span style=\"font-size: 16px; color: rgb(0, 0, 0); font-family: Tahoma, ;\">71</span><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">%);</span><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">江浙印染综合开工维持在77%</span><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">&nbsp;。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">本周,涤丝仅周三稍有放量,整体涤丝销售氛围偏弱为主。终端在新订单氛围走弱和成本端偏弱氛围下,原料不再进一步跟进,消化前期备货为主,综合原料备货有所下降。截至目前,原料备货集中在10-15天,偏高备货至10月底。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">订单情况:近期的织造端新单氛围整体走弱明显,前期较好的圆机和经编工厂尤为明显,出货量也有所放缓,部分工厂生产前期订单为主。</span><br><span style=\"font-size: 18px !important; color: rgb(0, 0, 0); font-family: Tahoma, ;\">直接需求:</span>近期装置轮动检修与重启,长丝大厂陆续执行减产动作,但也有几套切片装置恢复,整体而言聚酯负荷仍以区间波动为主。截至本周五,初步核算聚酯负荷在83.9%(前值84.3%)。</li></ol><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=9c163c32bf7fca689c0067e285eb1a30\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"font-size: 16px;\">轻纺城成交量节前震荡回升但仍在偏低水平。</p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/sHBC03ZwLXP1jUK1mDdd0hUjEtQo.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=5e2a381802d34c5cf33502d8a9649bd6\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><p style=\"text-align: left; margin-top: 10px; font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=e47bcc409b9b7e9178805ac6c4a00bc1\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br></p><p style=\"font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=12be062cbc0c61819f99648cef6e2740\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br></p><p style=\"font-size: 16px;\"><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=fea70f6d5bf7f1899394be0619cc2da1\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=8a50b210b7ebb4fbb890d8ad58a817f6\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=800bf3710951ca96c2e5a3693c94a20d\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=fd1bff8836af41d0c32d0ed168c8b748\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe><br><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=baf493ba32b3c39e070e24ac5546fd6b\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></p><ul style=\"margin: 0px; padding: 0px; list-style: none; color: rgb(0, 0, 0); font-family: ;\"><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><strong>4、PTA行情沙盘推演<strong>2022.9.23:</strong><strong>PTA近端短缺的格局出现缓解的迹象,表现在:PX进口回升;国内重整及常减压提负;PTA、PX投产在即</strong></strong><strong>。正套部分或全部止盈。</strong></li></ul><p style=\"font-size: 16px;\"><img style=\"width: 100%;\" src=\"https://hzstatic.hzinsights.com/static/images/202209/20220922/d1HYjIFoBG9L2bGuWrXq7sBPpnT7.png\" class=\"fr-fic fr-dii fr-draggable\"><br></p><p style=\"font-size: 16px;\">PTA库存结构变化跟踪:本期PTA库存大幅去化17.9万吨附近(本周仓单集中注销,仓单库存大幅下降至0附近),聚酯成品折算PTA库存大幅累库9.3万吨,叠加PTA库存在聚酯成品库存累库的情况下去库8.6万吨。</p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/qYivQYoLkxJrmWGI5kNOy8i05q8P.png\" class=\"fr-fic fr-dib fr-draggable\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/cvLkXlLwczkvzktkN4R2SKCoHFby.png\" class=\"fr-fic fr-dib fr-draggable\" style=\"width: 572px;\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/EzRc4yuCqVcbdw2lb5FS2MCWvHd1.png\" class=\"fr-fic fr-dib fr-draggable\" style=\"width: 595px;\"></p><p style=\"font-size: 16px;\">PTA平衡表——按照Q4有500万吨新装置投产(东营威联化学250万吨及嘉通能源250万吨分别在11、12月计入产能基数)计算,按照9-10月聚酯月均负荷84%(下调1%)、87%(下调1%)预估,8-12月出口预计25万吨附近。8-10月目前预估均为去库格局。</p><p style=\"font-size: 16px;\">PX折算PTA与PTA合计9月去库幅度修正后大幅收窄。</p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/6VzQkr5nkbeqlCakRzTvZgl1PeFE.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"margin: 0px; padding: 0px; font-size: 16px;\"><br></p><p style=\"margin: 0px; padding: 0px; font-size: 16px;\"><strong style=\"color: rgb(0, 0, 0); font-family: ;\">5、</strong><strong style=\"color: rgb(0, 0, 0); font-family: ;\">乙二醇行情沙盘推演2022.9.23:到港预报集中的情况下仍在去库,4200-4600区间震荡操作,节前没有明显方向,不建议在节前备货的时间段布局空单。</strong></p><p style=\"margin: 0px; padding: 0px; font-size: 16px;\"><strong style=\"color: rgb(0, 0, 0); font-family: ;\"><img style=\"width: 100%;\" src=\"https://hzstatic.hzinsights.com/static/images/202209/20220916/e5XTMMmx04X6wpKITZ2NgjUyz8sI.png\" class=\"fr-fic fr-dii fr-draggable\"></strong><br></p><p style=\"margin: 0px; padding: 0px; font-size: 16px;\">乙二醇国内供需:</p><ul style=\"margin: 0px; padding: 0px; list-style: none; color: rgb(0, 0, 0); font-family: ;\"><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"color: rgb(0, 0, 0); font-family: Arial, Helvetica, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 28px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;\">乙二醇负荷低位仍在下降:截至9月22日,中国大陆地区乙二醇整体开工负荷在43.98%(较上期下降2.44%),其中煤制乙二醇开工负荷在28.67%(较上期下降2.08%)。</span><span style=\"font-family: Arial, Helvetica, sans-serif;\">&nbsp;</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\">后续供应存增加预期:</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\">上周意外停车的大连大型装置预计本周末或下周初开车。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\">内蒙古40万吨装置装置周内正常出料,负荷回升中。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\">陕西30万吨装置将于近日重启,预计9月底前后出料;内蒙古26万吨装置计划本月底前后投料重启。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/Yy4lTK54zBA3ifN4DFdpAhE1TQ4w.png\" class=\"fr-fic fr-dib fr-draggable\"></span><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/eXC4GZApxmJQLQSZZ15HV6J1nGdK.png\" class=\"fr-fic fr-dib fr-draggable\"></li></ul><p style=\"font-size: 16px;\">乙二醇到港预报与实际到港:</p><p style=\"font-size: 16px;\"><span style=\"font-size: 16px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; font-family: Arial, Helvetica, sans-serif; text-indent: 32px; float: none; display: inline !important;\"><span style=\"color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 32px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;\">隆众口径:截至9月29日,国内乙二醇华东总到港量预计在19.21万吨,较上一期增加3.45万吨,提升21.93个百分点。</span>&nbsp;</span></p><p style=\"font-size: 16px;\"><span style=\"font-size: 16px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; font-family: Arial, Helvetica, sans-serif; text-indent: 32px; float: none; display: inline !important;\">港口发货节前回升。</span></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/WXVwl3NV0U68hnEjeNX5RFPGowq3.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/2FuGYgp5dpx6XbCNXLdSzdh4uzvp.png\" class=\"fr-fic fr-dib fr-draggable\"></p><p style=\"font-size: 16px;\">海外装置:</p><p style=\"font-size: 16px;\">印度IOC 32.5万吨装置将于近期停车技改,预计停车将持续至12月份。</p><p style=\"font-size: 16px;\">伊朗 Marun 44.5万吨装置目前处于停车状态,该装置此前货源供应印度市场为主。</p><p style=\"font-size: 16px;\">美国Sasol 28万吨装置计划于10月上旬停车检修,预计检修时长在一个月附近。</p><p style=\"font-size: 16px;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/CRzzaHscEcvhW8KXdajvLqABw11g.png\" class=\"fr-fic fr-dib fr-draggable\"></p><ul style=\"margin: 0px; padding: 0px; list-style: none; color: rgb(0, 0, 0); font-family: ;\"><li style=\"margin: 0px; padding: 0px; font-size: 16px;\">库存:</li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\">隆众口径:<span style=\"font-size: 16px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; font-family: Arial, Helvetica, sans-serif; text-indent: 32px; float: none; display: inline !important;\">截至9月22日,华东主港地区MEG港口库存总量81.6万吨,较上一统计周期减少3.64万吨,降低4.27%。受周末台风天气影响,主港本周到货延迟且整体出货尚可,本周港口库存延续去库走势。</span></li></ul><ul style=\"margin: 0px; padding: 0px; list-style: none;\"><li style=\"margin: 0px; padding: 0px; color: rgb(0, 0, 0); font-family: ;\">节前备货,聚酯工厂的乙二醇备货量本周小幅回升至84万吨附近。</li><li><br></li><li><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=c13c3e669ae5940893d1fd0e669754a5\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></li><li><iframe src=\"https://chartlib.hzinsights.com/chartshow?code=0fac897a91cbfcdb67958ae9e5d039a7\" width=\"100%\" height=\"350\" style=\"border-width:0px; min-height:350px;\"></iframe></li><li style=\"margin: 0px; padding: 0px;\"><span style=\"font-size: 16px; font-family: Arial, Helvetica, sans-serif;\">乙二醇平衡表:</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;\">2022年8月我国乙二醇当月进口量为598134.40吨,累计进口量为5189987.09吨,进口量环比升4.83%,进口量同比下跌23.97%,累计进口量比去年同期降9.28%。&nbsp;</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px;\"><span style=\"font-family: Arial, Helvetica, sans-serif;\">按照9-10月聚酯月均负荷84%、87%,8月进口量比预估略多,9月因受到台风影响到港持续延迟,压力预计在9月下旬至9月底显现,因此预计也只有55万吨附近,Q4进口预计仍有差别,10-12月目前预计进口60-65万吨(其中10月或偏多)。</span></li><li style=\"margin: 0px; padding: 0px; font-size: 16px; font-family: Arial, Helvetica, sans-serif;\">进口量调整后,8月去库收窄至16万吨偏下,9月目前预计去库14万吨附近,10月累库压力来自于到港集中及新装置开始贡献产量,11月累库压力来自于需求回落+新装置产量提升。</li><li style=\"margin: 0px; padding: 0px; font-size: 16px; font-family: Arial, Helvetica, sans-serif;\">11-12月警惕供应端的超预期缩量。</li><li style=\"margin: 0px; padding: 0px; font-size: 16px; font-family: Arial, Helvetica, sans-serif;\"><img src=\"https://hongze.oss-accelerate.aliyuncs.com/static/images/202209/20220925/VEW93DsD4vrzXuNUfTMCFiJSU8Y3.png\" class=\"fr-fic fr-dib fr-draggable\"></li></ul><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p><p style=\"font-size: 16px;\"><br></p>`
  1224. content = html.UnescapeString(content)
  1225. if content == "" {
  1226. return
  1227. }
  1228. // 过滤编辑器版权html
  1229. content = strings.Replace(content, "<p data-f-id=\"pbf\" style=\"text-align: center; font-size: 14px; margin-top: 30px; opacity: 0.65; font-family: sans-serif;\">Powered by <a href=\"https://www.froala.com/wysiwyg-editor?pb=1\" title=\"Froala Editor\">Froala Editor</a></p>", "", -1)
  1230. defer func() {
  1231. if err != nil {
  1232. go alarm_msg.SendAlarmMsg("过滤报告正文前后换行符及空格失败, ErrMsg: "+err.Error(), 3)
  1233. }
  1234. }()
  1235. // 做一个配置,有问题的时候随时关闭
  1236. configKey := "report_filter_br"
  1237. conf, e := company.GetConfigDetailByCode(configKey)
  1238. if e != nil {
  1239. err = errors.New("获取报告过滤配置失败, Err: " + e.Error())
  1240. return
  1241. }
  1242. if conf.ConfigValue != "1" {
  1243. return content, nil
  1244. }
  1245. // 找出所有<p>标签, <p>标签的索引
  1246. re := regexp.MustCompile(`(?is:<p(.*?)</p>)`)
  1247. arr := re.FindAllString(content, -1)
  1248. indexArr := re.FindAllIndex([]byte(content), -1)
  1249. // 空<p>正则
  1250. emptyRe := `<p[^>]*>(<br>|<br/>)+</p>`
  1251. startIsBr := false
  1252. countEmptyBr := 0 // 需要连续替换的空<p>总数
  1253. lastBrRange := 0 // 最后一个空<p>右侧index, 用来判断是否为连续的空<p>
  1254. // 注:以下逻辑只适用于去除前面的空行, 由于编辑器始终会在文章最后面跟上自己的html标签, 此处不再进行后面空行的去除=_=!
  1255. for i := range arr {
  1256. byteRange := indexArr[i]
  1257. if len(byteRange) == 2 {
  1258. // 内容开头不为<p>直接跳出遍历, 否则才进行空<p>的判断
  1259. if i == 0 && byteRange[0] == 0 {
  1260. startIsBr = true
  1261. }
  1262. if !startIsBr {
  1263. break
  1264. }
  1265. if lastBrRange != 0 {
  1266. // 说明不是连续的空<p>, 中间出现了其他标签, 那么结束遍历, 进行最终的文本替换
  1267. if lastBrRange != byteRange[0] {
  1268. break
  1269. }
  1270. }
  1271. // 正则匹配为空<p>则计数, 记录该空<p>右侧index
  1272. m, e := regexp.Match(emptyRe, []byte(arr[i]))
  1273. if e != nil {
  1274. err = e
  1275. return
  1276. }
  1277. if m {
  1278. countEmptyBr += 1
  1279. lastBrRange = byteRange[1]
  1280. continue
  1281. }
  1282. // 遍历到该<p>标签不为空了, 结束遍历
  1283. break
  1284. }
  1285. }
  1286. if countEmptyBr > 0 {
  1287. reg, e := regexp.Compile(emptyRe)
  1288. if e != nil {
  1289. err = errors.New("正则解析失败, Err: " + e.Error())
  1290. return
  1291. }
  1292. counted := 0 // 已替换数
  1293. res = reg.ReplaceAllStringFunc(content, func(s string) string {
  1294. counted += 1
  1295. if counted <= countEmptyBr {
  1296. return ""
  1297. } else {
  1298. return s
  1299. }
  1300. })
  1301. if res == "" {
  1302. res = newContent
  1303. }
  1304. } else {
  1305. res = content
  1306. if res == "" {
  1307. res = newContent
  1308. }
  1309. }
  1310. return
  1311. }
  1312. // GetEnglishReportOverview 获取英文研报overview部分
  1313. func GetEnglishReportOverview(content string) (res string, err error) {
  1314. content = html.UnescapeString(content)
  1315. doc, e := goquery.NewDocumentFromReader(strings.NewReader(content))
  1316. if e != nil {
  1317. err = errors.New("Create Doc Err: " + e.Error())
  1318. return
  1319. }
  1320. target := "overview"
  1321. label := "</strong>"
  1322. start := -1
  1323. end := -1
  1324. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  1325. h, e := s.Html()
  1326. if e != nil {
  1327. err = errors.New("Get Html1 Err: " + e.Error())
  1328. return
  1329. }
  1330. h = strings.ToLower(h)
  1331. t := s.Text()
  1332. t = strings.ToLower(t)
  1333. if strings.Contains(h, label) && t != "" && strings.Contains(t, target) {
  1334. start = i
  1335. }
  1336. if start != -1 && end == -1 && i > start && strings.Contains(h, label) {
  1337. end = i
  1338. }
  1339. })
  1340. if start != -1 && end != -1 {
  1341. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  1342. if i > start && i < end {
  1343. h, e := s.Html()
  1344. if e != nil {
  1345. err = errors.New("Get Html2 Err: " + e.Error())
  1346. return
  1347. }
  1348. // 包含iframe则过滤掉
  1349. if strings.Contains(h, "iframe") {
  1350. return
  1351. }
  1352. res += `<p>` + h + `</p>`
  1353. }
  1354. })
  1355. }
  1356. return
  1357. }
  1358. // GetReportContentSubWithoutIframe 获取报告正文前几段,过滤iframe
  1359. func GetReportContentSubWithoutIframe(content string) (contentSub string, err error) {
  1360. content = html.UnescapeString(content)
  1361. doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
  1362. if err != nil {
  1363. fmt.Println("create doc err:", err.Error())
  1364. return
  1365. }
  1366. label := "iframe"
  1367. n := 0
  1368. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  1369. if n >= 5 {
  1370. return
  1371. }
  1372. n++
  1373. h, err := s.Html()
  1374. if err != nil {
  1375. fmt.Println("get html err", err.Error())
  1376. return
  1377. }
  1378. // 包含iframe则过滤掉
  1379. if strings.Contains(h, label) {
  1380. return
  1381. }
  1382. if s.Text() != "" || strings.Contains(h, "src") {
  1383. contentSub = contentSub + "<p>" + h + "</p>"
  1384. }
  1385. })
  1386. return
  1387. }
  1388. // UpdateReportEditMark 更新研报当前更新状态
  1389. // status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
  1390. func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName string) (ret models.MarkReportResp, err error) {
  1391. //更新标记key
  1392. key := fmt.Sprint(`crm:report:edit:`, reportId)
  1393. ret.Status = 0
  1394. ret.Msg = "无人编辑"
  1395. opUserId, e := utils.Rc.RedisInt(key)
  1396. var opUser models.MarkReportItem
  1397. var classifyNameFirst string
  1398. if e != nil {
  1399. opUserInfoStr, tErr := utils.Rc.RedisString(key)
  1400. if tErr == nil {
  1401. tErr = json.Unmarshal([]byte(opUserInfoStr), &opUser)
  1402. if tErr == nil {
  1403. opUserId = opUser.AdminId
  1404. }
  1405. }
  1406. }
  1407. //判断是否是晨报或者周报,如果是则跳过
  1408. var reportInfo *models.ReportDetail
  1409. classifyNameFirst = opUser.ReportClassifyNameFirst
  1410. if reportId > 0 && status != 2 && classifyNameFirst == "" {
  1411. //查询报告ID信息
  1412. reportInfo, err = models.GetReportById(reportId)
  1413. if err != nil {
  1414. err = fmt.Errorf("报告不存在")
  1415. return
  1416. }
  1417. classifyNameFirst = reportInfo.ClassifyNameFirst
  1418. }
  1419. if classifyNameFirst == "晨报" || classifyNameFirst == "周报" {
  1420. return
  1421. }
  1422. if opUserId > 0 && opUserId != nowUserId {
  1423. editor := opUser.Editor
  1424. if editor == "" {
  1425. //查询账号的用户姓名
  1426. otherInfo, e := system.GetSysAdminById(opUserId)
  1427. if e != nil {
  1428. err = fmt.Errorf("查询其他编辑者信息失败")
  1429. return
  1430. }
  1431. editor = otherInfo.RealName
  1432. }
  1433. ret.Status = 1
  1434. ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
  1435. ret.Editor = editor
  1436. return
  1437. }
  1438. if status == 1 {
  1439. nowUser := &models.MarkReportItem{AdminId: nowUserId, Editor: nowUserName, ReportClassifyNameFirst: classifyNameFirst}
  1440. bt, e := json.Marshal(nowUser)
  1441. if e != nil {
  1442. err = fmt.Errorf("格式化编辑者信息失败")
  1443. return
  1444. }
  1445. if opUserId > 0 {
  1446. utils.Rc.Do("SETEX", key, int64(180), string(bt)) //3分钟缓存
  1447. } else {
  1448. utils.Rc.SetNX(key, string(bt), time.Second*60*3) //3分钟缓存
  1449. }
  1450. } else if status == 0 {
  1451. //清除编辑缓存
  1452. _ = utils.Rc.Delete(key)
  1453. }
  1454. return
  1455. }
  1456. // HandleVideoDecibel 处理报告中的音频文件
  1457. func HandleVideoDecibel(chapterInfo *models.ReportChapter) {
  1458. public_api.HandleVideoDecibel(chapterInfo.ReportChapterId)
  1459. return
  1460. }