report.go 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. package services
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "eta/eta_api/models"
  6. "eta/eta_api/models/company"
  7. "eta/eta_api/models/system"
  8. "eta/eta_api/services/alarm_msg"
  9. "eta/eta_api/services/public_api"
  10. "eta/eta_api/utils"
  11. "fmt"
  12. "github.com/PuerkitoBio/goquery"
  13. "html"
  14. "os"
  15. "regexp"
  16. "strconv"
  17. "strings"
  18. "time"
  19. )
  20. func GetReportContentSub(content string) (contentSub string, err error) {
  21. content = html.UnescapeString(content)
  22. doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
  23. if err != nil {
  24. fmt.Println("create doc err:", err.Error())
  25. return
  26. }
  27. n := 0
  28. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  29. if n >= 5 {
  30. return
  31. }
  32. n++
  33. phtml, err := s.Html()
  34. if err != nil {
  35. fmt.Println("get html err", err.Error())
  36. return
  37. }
  38. if s.Text() != "" || strings.Contains(phtml, "src") {
  39. contentSub = contentSub + "<p>" + phtml + "</p>"
  40. }
  41. })
  42. return
  43. }
  44. // PublishDayWeekReport 发布晨周报
  45. func PublishDayWeekReport(reportId int) (tips string, err error) {
  46. report, err := models.GetReportByReportId(reportId)
  47. if err != nil {
  48. return
  49. }
  50. if report.State == 2 {
  51. return
  52. }
  53. chapters, err := models.GetChapterListByReportId(reportId)
  54. if err != nil {
  55. return
  56. }
  57. chapterLen := len(chapters)
  58. if chapterLen <= 0 {
  59. err = errors.New("报告章节为空,不可发布")
  60. return
  61. }
  62. reportType := chapters[0].ReportType
  63. // 校验章节
  64. publishReport, tips, publishIdArr, unPublishIdArr, err := checkDayWeekChapterWrite(chapters, reportType)
  65. if err != nil {
  66. return
  67. }
  68. publishLen := len(publishIdArr)
  69. if publishLen <= 0 {
  70. err = errors.New("报告章节均不可发布")
  71. return
  72. }
  73. // 需发布整期
  74. updateCols := make([]string, 0)
  75. if publishReport {
  76. updateCols = append(updateCols, "Title", "State", "ModifyTime")
  77. // 发布后标题调整
  78. title := report.Title
  79. title = strings.ReplaceAll(title, "【弘则FICC晨报】", "")
  80. title = strings.ReplaceAll(title, "【弘则FICC周报】", "")
  81. if title == "" {
  82. // 取第一个需发布章节的标题
  83. firstId := publishIdArr[0]
  84. firstTitle := ""
  85. for i := 0; i < chapterLen; i++ {
  86. if chapters[i].ReportChapterId == firstId {
  87. firstTitle = chapters[i].Title
  88. break
  89. }
  90. }
  91. title = firstTitle
  92. }
  93. report.Title = title
  94. report.State = 2
  95. // 研报后台4.4 只在没有发布过时更新发布时间,其余均按模版消息发送时间当作发布时间
  96. if report.MsgIsSend == 0 || report.PublishTime.IsZero() {
  97. report.PublishTime = time.Now().Local()
  98. updateCols = append(updateCols, "PublishTime")
  99. }
  100. report.ModifyTime = time.Now().Local()
  101. }
  102. publishIdStr := utils.IntArr2joinString(publishIdArr, ",")
  103. //unPublishIdStr := utils.IntArr2joinString(unPublishIdArr, ",")
  104. if e := models.PublishReportAndChapter(report, publishIdArr, unPublishIdArr, publishReport, updateCols); e != nil {
  105. err = errors.New("发布报告及章节失败")
  106. return
  107. }
  108. // 生成章节音频
  109. go func() {
  110. _ = UpdateChaptersVideo(publishIdStr)
  111. }()
  112. // 更新报告ES
  113. go func() {
  114. _ = UpdateReportEs(report.Id, 2)
  115. }()
  116. // 发布时备份内容
  117. go SaveReportLogs(report, chapters, report.AdminId, report.AdminRealName)
  118. return
  119. }
  120. // UpdateChaptersVideo 更新章节音频
  121. func UpdateChaptersVideo(chapterIds string) (err error) {
  122. defer func() {
  123. if err != nil {
  124. utils.FileLog.Error("UpdateChaptersVideo, chapterIds:%s, Err:%s", chapterIds, err.Error())
  125. go alarm_msg.SendAlarmMsg("更新章节音频失败, 章节ID: "+chapterIds+", Err: "+err.Error(), 3)
  126. }
  127. }()
  128. if chapterIds == "" {
  129. return
  130. }
  131. ids := make([]int, 0)
  132. chapterIdArr := strings.Split(chapterIds, ",")
  133. for _, v := range chapterIdArr {
  134. id, e := strconv.Atoi(v)
  135. if e != nil {
  136. return
  137. }
  138. ids = append(ids, id)
  139. }
  140. chapterList, err := models.GetChapterListByChapterIds(ids)
  141. if err != nil {
  142. return
  143. }
  144. // 生成video
  145. nowTime := time.Now()
  146. updateCols := make([]string, 0)
  147. updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds")
  148. for i := 0; i < len(chapterList); i++ {
  149. item := chapterList[i]
  150. // 忽略已有音频的章节
  151. if item.VideoUrl != "" && item.VideoName != "" && item.VideoSize != "" && item.VideoPlaySeconds != "" {
  152. continue
  153. }
  154. videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, html.UnescapeString(item.Content), nowTime.Format(utils.FormatDateTime))
  155. if e != nil {
  156. err = e
  157. return
  158. }
  159. item.VideoUrl = videoUrl
  160. item.VideoName = videoName
  161. item.VideoSize = videoSize
  162. item.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds)
  163. if e = item.UpdateChapter(updateCols); e != nil {
  164. err = e
  165. }
  166. }
  167. return
  168. }
  169. // PublishTodayDayReport 发布今日晨报
  170. func PublishTodayDayReport() (err error) {
  171. nowTime := time.Now()
  172. startTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
  173. endTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 23, 59, 59, 0, time.Local)
  174. todayReport, err := models.GetUnPublishDayReport(startTime, endTime)
  175. if err != nil {
  176. if err.Error() == utils.ErrNoRow() { //如果是找不到待发送的晨报,那么需要将err置空
  177. err = nil
  178. }
  179. return
  180. }
  181. if todayReport != nil {
  182. if _, tmpErr := PublishDayWeekReport(todayReport.Id); tmpErr != nil {
  183. err = tmpErr
  184. return
  185. }
  186. // 定时发布的晨报自动推送客群
  187. reportDetail, tmpErr := models.GetReportById(todayReport.Id)
  188. if tmpErr != nil {
  189. err = tmpErr
  190. return
  191. }
  192. // 推送模板消息
  193. if tmpErr = SendMiniProgramReportWxMsg(todayReport.Id); tmpErr != nil {
  194. err = tmpErr
  195. return
  196. }
  197. if tmpErr = models.ModifyReportThsMsgIsSend(reportDetail); tmpErr != nil {
  198. err = tmpErr
  199. return
  200. }
  201. }
  202. return
  203. }
  204. // UpdateReportEs 更新报告/章节Es
  205. func UpdateReportEs(reportId int, publishState int) (err error) {
  206. if reportId <= 0 {
  207. return
  208. }
  209. reportInfo, err := models.GetReportByReportId(reportId)
  210. if err != nil {
  211. return
  212. }
  213. categories := ""
  214. if reportInfo.HasChapter == 1 {
  215. // 晨周报
  216. chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
  217. if tmpErr != nil {
  218. return
  219. }
  220. if len(chapterList) > 0 {
  221. for i := 0; i < len(chapterList); i++ {
  222. // 章节对应的品种
  223. permissionList, tmpErr := models.GetChapterTypePermissionByReportChapterTypeId(chapterList[i].TypeId)
  224. if tmpErr != nil {
  225. return
  226. }
  227. categoryArr := make([]string, 0)
  228. if len(permissionList) > 0 {
  229. for ii := 0; ii < len(permissionList); ii++ {
  230. categoryArr = append(categoryArr, permissionList[ii].PermissionName)
  231. }
  232. }
  233. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  234. chapterCategories := strings.Join(aliasArr, ",")
  235. esChapter := &models.ElasticReportDetail{
  236. ReportId: chapterList[i].ReportId,
  237. ReportChapterId: chapterList[i].ReportChapterId,
  238. Title: chapterList[i].Title,
  239. Abstract: chapterList[i].Abstract,
  240. BodyContent: utils.TrimHtml(html.UnescapeString(chapterList[i].Content)),
  241. PublishTime: chapterList[i].PublishTime.Format(utils.FormatDateTime),
  242. PublishState: chapterList[i].PublishState,
  243. Author: chapterList[i].Author,
  244. ClassifyIdFirst: chapterList[i].ClassifyIdFirst,
  245. ClassifyNameFirst: chapterList[i].ClassifyNameFirst,
  246. ClassifyIdSecond: 0,
  247. ClassifyNameSecond: "",
  248. Categories: chapterCategories,
  249. StageStr: strconv.Itoa(chapterList[i].Stage),
  250. }
  251. chapterDocId := fmt.Sprintf("%d-%d", reportInfo.Id, chapterList[i].ReportChapterId)
  252. if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
  253. return
  254. }
  255. }
  256. }
  257. } else {
  258. //if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
  259. permissionList, tmpErr := models.GetChartPermissionNameFromMappingByKeyword(reportInfo.ClassifyNameSecond, "rddp")
  260. if tmpErr != nil {
  261. return
  262. }
  263. categoryArr := make([]string, 0)
  264. for i := 0; i < len(permissionList); i++ {
  265. categoryArr = append(categoryArr, permissionList[i].PermissionName)
  266. }
  267. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  268. categories = strings.Join(aliasArr, ",")
  269. //}
  270. }
  271. // 新增报告ES
  272. esReport := &models.ElasticReportDetail{
  273. ReportId: reportInfo.Id,
  274. ReportChapterId: 0,
  275. Title: reportInfo.Title,
  276. Abstract: reportInfo.Abstract,
  277. BodyContent: utils.TrimHtml(html.UnescapeString(reportInfo.Content)),
  278. PublishTime: reportInfo.PublishTime.Format(utils.FormatDateTime),
  279. PublishState: publishState,
  280. Author: reportInfo.Author,
  281. ClassifyIdFirst: reportInfo.ClassifyIdFirst,
  282. ClassifyNameFirst: reportInfo.ClassifyNameFirst,
  283. ClassifyIdSecond: reportInfo.ClassifyIdSecond,
  284. ClassifyNameSecond: reportInfo.ClassifyNameSecond,
  285. Categories: categories,
  286. StageStr: strconv.Itoa(reportInfo.Stage),
  287. }
  288. docId := fmt.Sprintf("%d-%d", reportInfo.Id, 0)
  289. if err = EsAddOrEditReport(utils.EsReportIndexName, docId, esReport); err != nil {
  290. return
  291. }
  292. return
  293. }
  294. // addCategoryAliasToArr 品种别名
  295. func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error) {
  296. aliasArr = categoryArr
  297. if len(categoryArr) > 0 {
  298. for i := 0; i < len(categoryArr); i++ {
  299. if strings.Contains(categoryArr[i], "沥青") {
  300. aliasArr = append(aliasArr, "BU")
  301. }
  302. if strings.Contains(categoryArr[i], "MEG") {
  303. aliasArr = append(aliasArr, "EG", "乙二醇")
  304. }
  305. if strings.Contains(categoryArr[i], "聚酯") {
  306. aliasArr = append(aliasArr, "长丝", "短纤", "瓶片")
  307. }
  308. if strings.Contains(categoryArr[i], "纯苯+苯乙烯") {
  309. aliasArr = append(aliasArr, "EB")
  310. }
  311. if strings.Contains(categoryArr[i], "聚乙烯") {
  312. aliasArr = append(aliasArr, "PP", "PE")
  313. }
  314. if strings.Contains(categoryArr[i], "玻璃纯碱") {
  315. aliasArr = append(aliasArr, "玻璃", "纯碱", "FG", "SA")
  316. }
  317. if strings.Contains(categoryArr[i], "甲醇") {
  318. aliasArr = append(aliasArr, "甲醇", "MA")
  319. }
  320. if strings.Contains(categoryArr[i], "橡胶") {
  321. aliasArr = append(aliasArr, "橡胶", "RU")
  322. }
  323. }
  324. }
  325. return
  326. }
  327. // UpdateReportChapterEs 更新报告章节ES
  328. func UpdateReportChapterEs(reportChapterId int) (err error) {
  329. if reportChapterId <= 0 {
  330. return
  331. }
  332. chapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
  333. if err != nil {
  334. return
  335. }
  336. // 章节对应的品种
  337. permissionList, tmpErr := models.GetChapterTypePermissionByReportChapterTypeId(chapterInfo.TypeId)
  338. if tmpErr != nil {
  339. return
  340. }
  341. categoryArr := make([]string, 0)
  342. if len(permissionList) > 0 {
  343. for ii := 0; ii < len(permissionList); ii++ {
  344. categoryArr = append(categoryArr, permissionList[ii].PermissionName)
  345. }
  346. }
  347. aliasArr, _ := addCategoryAliasToArr(categoryArr)
  348. categories := strings.Join(aliasArr, ",")
  349. // 新增/编辑ES
  350. esChapter := &models.ElasticReportDetail{
  351. ReportId: chapterInfo.ReportId,
  352. ReportChapterId: chapterInfo.ReportChapterId,
  353. Title: chapterInfo.Title,
  354. Abstract: chapterInfo.Abstract,
  355. BodyContent: utils.TrimHtml(html.EscapeString(chapterInfo.Content)),
  356. PublishTime: chapterInfo.PublishTime.Format(utils.FormatDateTime),
  357. PublishState: chapterInfo.PublishState,
  358. Author: chapterInfo.Author,
  359. ClassifyIdFirst: chapterInfo.ClassifyIdFirst,
  360. ClassifyNameFirst: chapterInfo.ClassifyNameFirst,
  361. ClassifyIdSecond: 0,
  362. ClassifyNameSecond: "",
  363. Categories: categories,
  364. StageStr: strconv.Itoa(chapterInfo.Stage),
  365. }
  366. chapterDocId := fmt.Sprintf("%d-%d", chapterInfo.ReportId, chapterInfo.ReportChapterId)
  367. if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil {
  368. return
  369. }
  370. return
  371. }
  372. // DeleteReportAndChapter 删除报告及章节
  373. func DeleteReportAndChapter(reportId int) (err error) {
  374. reportInfo, err := models.GetReportByReportId(reportId)
  375. if err != nil {
  376. err = errors.New("报告信息有误, Err: " + err.Error())
  377. return
  378. }
  379. if reportInfo.State == 2 {
  380. err = errors.New("报告已发布,不可删除")
  381. return
  382. }
  383. // 更新ES
  384. _ = UpdateReportEs(reportId, 1)
  385. // 删除
  386. if reportInfo.HasChapter == 1 && (reportInfo.ChapterType == utils.REPORT_TYPE_DAY || reportInfo.ChapterType == utils.REPORT_TYPE_WEEK) {
  387. err = models.DeleteDayWeekReportAndChapter(reportId)
  388. } else {
  389. err = models.DeleteReport(reportId)
  390. }
  391. if err != nil {
  392. err = errors.New("删除失败, Err: " + err.Error())
  393. return
  394. }
  395. // 重置PPT关联报告
  396. go func() {
  397. _ = ResetPPTReport(reportId, false)
  398. }()
  399. return
  400. }
  401. // 替换报告内容中的base64图片
  402. func replaceReportBase64ToImg(content string) (newContent string, err error) {
  403. if content == "" {
  404. return
  405. }
  406. 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}==)?"
  407. re, _ := regexp.Compile(pattern)
  408. matcher := re.FindAllString(content, 999)
  409. if len(matcher) > 0 {
  410. for _, v := range matcher {
  411. imgUrl, tmpErr := reportBase64ToImg(v)
  412. if tmpErr != nil {
  413. err = tmpErr
  414. return
  415. }
  416. content = strings.ReplaceAll(content, v, imgUrl)
  417. }
  418. }
  419. newContent = content
  420. return
  421. }
  422. // 转换base64图片为img并上传
  423. func reportBase64ToImg(imageBase64 string) (resourceUrl string, err error) {
  424. if imageBase64 == "" {
  425. err = errors.New("图片为空")
  426. return
  427. }
  428. ext := ".png"
  429. uploadDir := "./static"
  430. randStr := utils.GetRandStringNoSpecialChar(28)
  431. fileName := randStr + ext
  432. fpath := uploadDir + "/" + fileName
  433. b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imageBase64)
  434. if !b {
  435. err = errors.New("图片格式不正确")
  436. return
  437. }
  438. re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
  439. base64Str := re.ReplaceAllString(imageBase64, "")
  440. base64Str = strings.Replace(base64Str, " ", "", -1)
  441. err = utils.SaveBase64ToFile(base64Str, fpath)
  442. if err != nil {
  443. err = errors.New("图片保存失败" + err.Error())
  444. return
  445. }
  446. defer os.Remove(fpath)
  447. hzUploadDir := utils.RESOURCE_DIR + "images/"
  448. savePath := hzUploadDir + time.Now().Format("200601/20060102/")
  449. savePath += fileName
  450. //上传到阿里云 和 minio
  451. //if utils.ObjectStorageClient == "minio" {
  452. // err = UploadFileToMinIo(fileName, fpath, savePath)
  453. // if err != nil {
  454. // err = errors.New("文件上传失败" + err.Error())
  455. // return
  456. // }
  457. // resourceUrl = utils.MinIoImghost + savePath
  458. //} else {
  459. // err = UploadFileToAliyun(fileName, fpath, savePath)
  460. // if err != nil {
  461. // err = errors.New("文件上传失败" + err.Error())
  462. // return
  463. // }
  464. // resourceUrl = utils.Imghost + savePath
  465. //}
  466. ossClient := NewOssClient()
  467. if ossClient == nil {
  468. err = fmt.Errorf("初始化OSS服务失败")
  469. return
  470. }
  471. resourceUrl, err = ossClient.UploadFile(fileName, fpath, savePath)
  472. if err != nil {
  473. err = fmt.Errorf("文件上传失败, Err: %s", err.Error())
  474. return
  475. }
  476. item := new(models.Resource)
  477. item.ResourceUrl = resourceUrl
  478. item.ResourceType = 1
  479. item.CreateTime = time.Now()
  480. _, err = models.AddResource(item)
  481. if err != nil {
  482. err = errors.New("资源上传失败" + err.Error())
  483. return
  484. }
  485. return
  486. }
  487. // UpdateReportVideo 更新报告及其章节音频
  488. func UpdateReportVideo(reportId int) (err error) {
  489. defer func() {
  490. if err != nil {
  491. utils.FileLog.Error("UpdateReportVideo, reportId:%s, Err:%s", strconv.Itoa(reportId), err.Error())
  492. go alarm_msg.SendAlarmMsg("更新报告音频失败, 报告ID: "+strconv.Itoa(reportId)+", Err: "+err.Error(), 3)
  493. //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "更新报告音频失败, 报告ID: " + reportIdStr + ", Err: "+err.Error(), utils.EmailSendToUsers)
  494. }
  495. }()
  496. if reportId == 0 {
  497. return
  498. }
  499. reportInfo, err := models.GetReportByReportId(reportId)
  500. if err != nil {
  501. return
  502. }
  503. if reportInfo.HasChapter == 1 {
  504. // 更新章节音频
  505. chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
  506. if tmpErr != nil {
  507. err = tmpErr
  508. return
  509. }
  510. chapterIdArr := make([]string, 0)
  511. for i := 0; i < len(chapterList); i++ {
  512. chapterIdArr = append(chapterIdArr, strconv.Itoa(chapterList[i].ReportChapterId))
  513. }
  514. chapterIds := strings.Join(chapterIdArr, ",")
  515. //go UpdateChaptersVideo(chapterIds)
  516. err = UpdateChaptersVideo(chapterIds)
  517. } else {
  518. // 更新报告音频
  519. if reportInfo.VideoUrl != "" {
  520. return
  521. }
  522. nowTime := time.Now()
  523. updateCols := make([]string, 0)
  524. updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds")
  525. videoUrl, videoName, videoSize, videoPlaySeconds, tmpErr := CreateReportVideo(reportInfo.Title, html.UnescapeString(reportInfo.Content), nowTime.Format(utils.FormatDateTime))
  526. reportInfo.VideoUrl = videoUrl
  527. reportInfo.VideoName = videoName
  528. reportInfo.VideoSize = videoSize
  529. reportInfo.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds)
  530. tmpErr = reportInfo.UpdateReport(updateCols)
  531. if tmpErr != nil {
  532. err = tmpErr
  533. return
  534. }
  535. }
  536. return
  537. }
  538. func UpdateEmptyVideoReportVideo() (err error) {
  539. list, err := models.GetSyncEmptyVideoReport()
  540. if err != nil {
  541. return
  542. }
  543. listLen := len(list)
  544. if listLen <= 0 {
  545. fmt.Println("无报告需要更新音频")
  546. return
  547. }
  548. fmt.Println("Start 待更新报告音频数: ", listLen)
  549. for i := 0; i < listLen; i++ {
  550. if err = UpdateReportVideo(list[i].Id); err != nil {
  551. fmt.Printf("更新音频失败")
  552. fmt.Println(err.Error())
  553. return
  554. }
  555. }
  556. fmt.Println("End 报告音频更新完毕")
  557. return
  558. }
  559. // checkDayWeekChapterWrite 校验晨周报已写章节与本期应写章节
  560. func checkDayWeekChapterWrite(chapters []*models.ReportChapter, reportType string) (publishReport bool, tips string, publishIdArr, unPublishIdArr []int, err error) {
  561. nowTime := time.Now().Local()
  562. updateTypeArr := make([]int, 0) // 需更新的章节类型IDs
  563. publishIdArr = make([]int, 0) // 需发布的章节IDs
  564. unPublishIdArr = make([]int, 0) // 需取消发布/未发布的章节IDs
  565. // 校验章节内容
  566. if reportType == utils.REPORT_TYPE_DAY {
  567. // 晨报章节不能都为空
  568. isEmpty := true
  569. for i := 0; i < len(chapters); i++ {
  570. if chapters[i].Content != "" && chapters[i].Title != "" {
  571. isEmpty = false
  572. break
  573. }
  574. }
  575. if isEmpty {
  576. err = errors.New("报告章节内容均为空或标题为空,不可发布")
  577. return
  578. }
  579. } else {
  580. // 周报章节需至少有一篇已编辑且有标题
  581. editNum := 0
  582. for i := 0; i < len(chapters); i++ {
  583. if chapters[i].IsEdit == 1 && chapters[i].Title != "" {
  584. editNum += 1
  585. }
  586. }
  587. if editNum == 0 {
  588. err = errors.New("报告均未编辑或标题为空,不可发布")
  589. return
  590. }
  591. }
  592. // 章节类型列表
  593. types, e := models.GetReportChapterTypeListByResearchType(reportType)
  594. if e != nil {
  595. err = errors.New("获取章节类型列表失败")
  596. return
  597. }
  598. // 本期需更新的章节IDs
  599. typeLen := len(types)
  600. for i := 0; i < typeLen; i++ {
  601. if types[i].IsSet != 1 && types[i].Enabled != 0 {
  602. // 正常更新
  603. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  604. } else {
  605. // 被设置为零值的也算作正常更新
  606. if types[i].PauseStartTime == utils.EmptyDateStr && types[i].PauseEndTime == utils.EmptyDateStr {
  607. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  608. continue
  609. }
  610. // 暂停更新需校验时间
  611. startTime, _ := time.Parse(utils.FormatDate, types[i].PauseStartTime)
  612. endTime, _ := time.Parse(utils.FormatDate, types[i].PauseEndTime)
  613. if nowTime.Before(startTime) || nowTime.After(endTime.AddDate(0, 0, 1)) {
  614. updateTypeArr = append(updateTypeArr, types[i].ReportChapterTypeId)
  615. }
  616. }
  617. }
  618. // 校验本期需更新的章节是否都已编辑
  619. chapterLen := len(chapters)
  620. updateTypeLen := len(updateTypeArr)
  621. tipsArr := make([]string, 0)
  622. for i := 0; i < chapterLen; i++ {
  623. isWrite := false
  624. for ii := 0; ii < updateTypeLen; ii++ {
  625. // 本期应发布的章节
  626. if chapters[i].TypeId == updateTypeArr[ii] {
  627. // 标题或者内容为空的情况下, 记录tips提示信息且不发布该章节
  628. if chapters[i].Title == "" || chapters[i].Content == "" {
  629. tipsArr = append(tipsArr, chapters[i].TypeName)
  630. break
  631. }
  632. isWrite = true
  633. break
  634. }
  635. }
  636. if isWrite {
  637. publishIdArr = append(publishIdArr, chapters[i].ReportChapterId)
  638. } else {
  639. unPublishIdArr = append(unPublishIdArr, chapters[i].ReportChapterId)
  640. }
  641. }
  642. if len(tipsArr) > 0 {
  643. tips = "部分章节未发布:" + strings.Join(tipsArr, "、") + "未填写标题/内容"
  644. }
  645. // 周报需发布的章节与需更新的章节数相等则表示可发布整期, 晨报无限制
  646. if reportType == utils.REPORT_TYPE_DAY {
  647. publishReport = true
  648. } else {
  649. if len(publishIdArr) == updateTypeLen {
  650. publishReport = true
  651. }
  652. }
  653. return
  654. }
  655. // PcCreateAndUploadSunCode 生成太阳码并上传OSS
  656. func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) {
  657. if page == "" {
  658. err = errors.New("page不能为空")
  659. return
  660. }
  661. // scene超过32位会生成失败,md5处理至32位
  662. sceneMD5 := "a=1"
  663. if scene != "" {
  664. sceneMD5 = utils.MD5(scene)
  665. }
  666. picByte, err := GetSunCode(page, sceneMD5)
  667. if err != nil {
  668. return
  669. }
  670. // 生成图片
  671. localPath := "./static/imgs"
  672. fileName := utils.GetRandStringNoSpecialChar(28) + ".png"
  673. fpath := fmt.Sprint(localPath, "/", fileName)
  674. f, err := os.Create(fpath)
  675. if err != nil {
  676. fmt.Println("11111")
  677. return
  678. }
  679. if _, err = f.Write(picByte); err != nil {
  680. return
  681. }
  682. defer func() {
  683. f.Close()
  684. os.Remove(fpath)
  685. }()
  686. // 上传OSS
  687. fileDir := "yb/suncode/"
  688. //上传到阿里云 和 minio
  689. //if utils.ObjectStorageClient == "minio" {
  690. // imgUrl, err = UploadMinIoToDir(fileName, fpath, "", fileDir)
  691. // if err != nil {
  692. // return
  693. // }
  694. //} else {
  695. // imgUrl, err = UploadAliyunToDir(fileName, fpath, "", fileDir)
  696. // if err != nil {
  697. // return
  698. // }
  699. //}
  700. savePath := fileDir + time.Now().Format("200601/20060102/") + fileName
  701. ossClient := NewOssClient()
  702. if ossClient == nil {
  703. err = fmt.Errorf("初始化OSS服务失败")
  704. return
  705. }
  706. imgUrl, err = ossClient.UploadFile(fileName, fpath, savePath)
  707. if err != nil {
  708. err = fmt.Errorf("文件上传失败, Err: %s", err.Error())
  709. return
  710. }
  711. if err != nil {
  712. return
  713. }
  714. // 记录参数
  715. if scene != "" {
  716. newSuncode := &models.YbPcSuncode{
  717. Scene: scene,
  718. SceneMd5: sceneMD5,
  719. CodePage: page,
  720. SuncodeUrl: imgUrl,
  721. CreateTime: time.Now(),
  722. }
  723. err = models.AddYbPcSunCode(newSuncode)
  724. }
  725. // 记录参数md5
  726. if scene != "" {
  727. newPars := &models.YbSuncodePars{
  728. Scene: scene,
  729. SceneKey: sceneMD5,
  730. CreateTime: time.Now(),
  731. }
  732. err = models.AddYbSuncodePars(newPars)
  733. }
  734. return
  735. }
  736. // CreateNewReport 创建新报告
  737. func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId int64, reportCode, errMsg string, err error) {
  738. contentSub := ""
  739. if req.Content != "" {
  740. e := utils.ContentXssCheck(req.Content)
  741. if e != nil {
  742. errMsg = "存在非法标签"
  743. err = errors.New("存在非法标签, Err: " + e.Error())
  744. return
  745. }
  746. contentClean, e := FilterReportContentBr(req.Content)
  747. if e != nil {
  748. errMsg = "内容去除前后空格失败"
  749. err = errors.New("内容去除前后空格失败, Err: " + e.Error())
  750. return
  751. }
  752. req.Content = contentClean
  753. sub, e := GetReportContentSub(req.Content)
  754. if e != nil {
  755. go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+e.Error(), 3)
  756. }
  757. contentSub = sub
  758. }
  759. maxStage, e := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond)
  760. if e != nil {
  761. errMsg = "期数获取失败!"
  762. err = errors.New("期数获取失败,Err:" + e.Error())
  763. return
  764. }
  765. item := new(models.Report)
  766. item.AddType = req.AddType
  767. item.ClassifyIdFirst = req.ClassifyIdFirst
  768. item.ClassifyNameFirst = req.ClassifyNameFirst
  769. item.ClassifyIdSecond = req.ClassifyIdSecond
  770. item.ClassifyNameSecond = req.ClassifyNameSecond
  771. item.Title = req.Title
  772. item.Abstract = req.Abstract
  773. item.Author = req.Author
  774. item.Frequency = req.Frequency
  775. item.State = req.State
  776. item.Content = html.EscapeString(req.Content)
  777. item.Stage = maxStage + 1
  778. item.ContentSub = html.EscapeString(contentSub)
  779. item.CreateTime = req.CreateTime
  780. item.ModifyTime = time.Now()
  781. item.ReportVersion = req.ReportVersion
  782. item.AdminId = adminInfo.AdminId
  783. item.AdminRealName = adminInfo.RealName
  784. newReportId, e = models.AddReport(item)
  785. if e != nil {
  786. errMsg = "保存失败"
  787. err = errors.New("保存失败,Err:" + e.Error())
  788. return
  789. }
  790. // 处理权限
  791. //if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
  792. go func() {
  793. permissionItems, e := models.GetPermission(req.ClassifyNameSecond)
  794. if e != nil {
  795. alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
  796. }
  797. for _, v := range permissionItems {
  798. e = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
  799. if e != nil {
  800. alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
  801. }
  802. }
  803. // 同步crm权限
  804. _ = EditReportPermissionSync(newReportId, req.ClassifyNameSecond)
  805. }()
  806. //}
  807. reportCode = utils.MD5(strconv.Itoa(int(newReportId)))
  808. //修改唯一编码
  809. {
  810. go models.ModifyReportCode(newReportId, reportCode)
  811. }
  812. return
  813. }
  814. // FilterReportContentBr 过滤报告正文前后换行符
  815. func FilterReportContentBr(content string) (res string, err error) {
  816. newContent := content
  817. //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>`
  818. content = html.UnescapeString(content)
  819. if content == "" {
  820. return
  821. }
  822. // 过滤编辑器版权html
  823. 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)
  824. defer func() {
  825. if err != nil {
  826. go alarm_msg.SendAlarmMsg("过滤报告正文前后换行符及空格失败, ErrMsg: "+err.Error(), 3)
  827. }
  828. }()
  829. // 做一个配置,有问题的时候随时关闭
  830. configKey := "report_filter_br"
  831. conf, e := company.GetConfigDetailByCode(configKey)
  832. if e != nil {
  833. err = errors.New("获取报告过滤配置失败, Err: " + e.Error())
  834. return
  835. }
  836. if conf.ConfigValue != "1" {
  837. return content, nil
  838. }
  839. // 找出所有<p>标签, <p>标签的索引
  840. re := regexp.MustCompile(`(?is:<p(.*?)</p>)`)
  841. arr := re.FindAllString(content, -1)
  842. indexArr := re.FindAllIndex([]byte(content), -1)
  843. // 空<p>正则
  844. emptyRe := `<p[^>]*>(<br>|<br/>)+</p>`
  845. startIsBr := false
  846. countEmptyBr := 0 // 需要连续替换的空<p>总数
  847. lastBrRange := 0 // 最后一个空<p>右侧index, 用来判断是否为连续的空<p>
  848. // 注:以下逻辑只适用于去除前面的空行, 由于编辑器始终会在文章最后面跟上自己的html标签, 此处不再进行后面空行的去除=_=!
  849. for i := range arr {
  850. byteRange := indexArr[i]
  851. if len(byteRange) == 2 {
  852. // 内容开头不为<p>直接跳出遍历, 否则才进行空<p>的判断
  853. if i == 0 && byteRange[0] == 0 {
  854. startIsBr = true
  855. }
  856. if !startIsBr {
  857. break
  858. }
  859. if lastBrRange != 0 {
  860. // 说明不是连续的空<p>, 中间出现了其他标签, 那么结束遍历, 进行最终的文本替换
  861. if lastBrRange != byteRange[0] {
  862. break
  863. }
  864. }
  865. // 正则匹配为空<p>则计数, 记录该空<p>右侧index
  866. m, e := regexp.Match(emptyRe, []byte(arr[i]))
  867. if e != nil {
  868. err = e
  869. return
  870. }
  871. if m {
  872. countEmptyBr += 1
  873. lastBrRange = byteRange[1]
  874. continue
  875. }
  876. // 遍历到该<p>标签不为空了, 结束遍历
  877. break
  878. }
  879. }
  880. if countEmptyBr > 0 {
  881. reg, e := regexp.Compile(emptyRe)
  882. if e != nil {
  883. err = errors.New("正则解析失败, Err: " + e.Error())
  884. return
  885. }
  886. counted := 0 // 已替换数
  887. res = reg.ReplaceAllStringFunc(content, func(s string) string {
  888. counted += 1
  889. if counted <= countEmptyBr {
  890. return ""
  891. } else {
  892. return s
  893. }
  894. })
  895. if res == "" {
  896. res = newContent
  897. }
  898. } else {
  899. res = content
  900. if res == "" {
  901. res = newContent
  902. }
  903. }
  904. return
  905. }
  906. // GetEnglishReportOverview 获取英文研报overview部分
  907. func GetEnglishReportOverview(content string) (res string, err error) {
  908. content = html.UnescapeString(content)
  909. doc, e := goquery.NewDocumentFromReader(strings.NewReader(content))
  910. if e != nil {
  911. err = errors.New("Create Doc Err: " + e.Error())
  912. return
  913. }
  914. target := "overview"
  915. label := "</strong>"
  916. start := -1
  917. end := -1
  918. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  919. h, e := s.Html()
  920. if e != nil {
  921. err = errors.New("Get Html1 Err: " + e.Error())
  922. return
  923. }
  924. h = strings.ToLower(h)
  925. t := s.Text()
  926. t = strings.ToLower(t)
  927. if strings.Contains(h, label) && t != "" && strings.Contains(t, target) {
  928. start = i
  929. }
  930. if start != -1 && end == -1 && i > start && strings.Contains(h, label) {
  931. end = i
  932. }
  933. })
  934. if start != -1 && end != -1 {
  935. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  936. if i > start && i < end {
  937. h, e := s.Html()
  938. if e != nil {
  939. err = errors.New("Get Html2 Err: " + e.Error())
  940. return
  941. }
  942. // 包含iframe则过滤掉
  943. if strings.Contains(h, "iframe") {
  944. return
  945. }
  946. res += `<p>` + h + `</p>`
  947. }
  948. })
  949. }
  950. return
  951. }
  952. // GetReportContentSubWithoutIframe 获取报告正文前几段,过滤iframe
  953. func GetReportContentSubWithoutIframe(content string) (contentSub string, err error) {
  954. content = html.UnescapeString(content)
  955. doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
  956. if err != nil {
  957. fmt.Println("create doc err:", err.Error())
  958. return
  959. }
  960. label := "iframe"
  961. n := 0
  962. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  963. if n >= 5 {
  964. return
  965. }
  966. n++
  967. h, err := s.Html()
  968. if err != nil {
  969. fmt.Println("get html err", err.Error())
  970. return
  971. }
  972. // 包含iframe则过滤掉
  973. if strings.Contains(h, label) {
  974. return
  975. }
  976. if s.Text() != "" || strings.Contains(h, "src") {
  977. contentSub = contentSub + "<p>" + h + "</p>"
  978. }
  979. })
  980. return
  981. }
  982. // UpdateReportEditMark 更新研报当前更新状态
  983. // status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
  984. func UpdateReportEditMark(reportId, nowUserId, status int, nowUserName, lang string) (ret models.MarkReportResp, err error) {
  985. //更新标记key
  986. key := fmt.Sprint(`crm:report:edit:`, reportId)
  987. ret.Status = 0
  988. ret.Msg = "无人编辑"
  989. opUserId, e := utils.Rc.RedisInt(key)
  990. var opUser models.MarkReportItem
  991. var classifyNameFirst string
  992. if e != nil {
  993. opUserInfoStr, tErr := utils.Rc.RedisString(key)
  994. if tErr == nil {
  995. tErr = json.Unmarshal([]byte(opUserInfoStr), &opUser)
  996. if tErr == nil {
  997. opUserId = opUser.AdminId
  998. }
  999. }
  1000. }
  1001. //判断是否是晨报或者周报,如果是则跳过
  1002. var reportInfo *models.ReportDetail
  1003. classifyNameFirst = opUser.ReportClassifyNameFirst
  1004. if reportId > 0 && status != 2 && classifyNameFirst == "" {
  1005. //查询报告ID信息
  1006. reportInfo, err = models.GetReportById(reportId)
  1007. if err != nil {
  1008. err = fmt.Errorf("报告不存在")
  1009. return
  1010. }
  1011. classifyNameFirst = reportInfo.ClassifyNameFirst
  1012. }
  1013. if classifyNameFirst == "晨报" || classifyNameFirst == "周报" {
  1014. return
  1015. }
  1016. if opUserId > 0 && opUserId != nowUserId {
  1017. editor := opUser.Editor
  1018. if editor == "" {
  1019. //查询账号的用户姓名
  1020. otherInfo, e := system.GetSysAdminById(opUserId)
  1021. if e != nil {
  1022. err = fmt.Errorf("查询其他编辑者信息失败")
  1023. return
  1024. }
  1025. editor = otherInfo.RealName
  1026. }
  1027. ret.Status = 1
  1028. if lang == utils.EnLangVersion {
  1029. ret.Msg = fmt.Sprintf("%s is currently editing the report", editor)
  1030. } else {
  1031. ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
  1032. }
  1033. ret.Editor = editor
  1034. return
  1035. }
  1036. if status == 1 {
  1037. nowUser := &models.MarkReportItem{AdminId: nowUserId, Editor: nowUserName, ReportClassifyNameFirst: classifyNameFirst}
  1038. bt, e := json.Marshal(nowUser)
  1039. if e != nil {
  1040. err = fmt.Errorf("格式化编辑者信息失败")
  1041. return
  1042. }
  1043. if opUserId > 0 {
  1044. utils.Rc.Do("SETEX", key, int64(180), string(bt)) //3分钟缓存
  1045. } else {
  1046. utils.Rc.SetNX(key, string(bt), time.Second*60*3) //3分钟缓存
  1047. }
  1048. } else if status == 0 {
  1049. //清除编辑缓存
  1050. _ = utils.Rc.Delete(key)
  1051. }
  1052. return
  1053. }
  1054. // HandleVideoDecibel 处理报告中的音频文件
  1055. func HandleVideoDecibel(chapterInfo *models.ReportChapter) {
  1056. public_api.HandleVideoDecibel(chapterInfo.ReportChapterId)
  1057. return
  1058. }
  1059. // SaveReportLogs 记录报告日志
  1060. func SaveReportLogs(item *models.Report, chapters []*models.ReportChapter, adminId int, adminRealName string) {
  1061. if item == nil && len(chapters) == 0 {
  1062. return
  1063. }
  1064. var err error
  1065. defer func() {
  1066. if err != nil {
  1067. tips := fmt.Sprintf("报告日志记录, SaveReportLogs error: %s", err.Error())
  1068. go alarm_msg.SendAlarmMsg(tips, 2)
  1069. }
  1070. }()
  1071. if item != nil {
  1072. e := models.AddReportSaveLog(item.Id, item.AdminId, item.Content, item.ContentSub, item.AdminRealName)
  1073. if e != nil {
  1074. err = fmt.Errorf("AddReportSaveLog: %s", e.Error())
  1075. return
  1076. }
  1077. }
  1078. if len(chapters) > 0 {
  1079. e := models.MultiAddReportChaptersSaveLog(chapters, adminId, adminRealName)
  1080. if e != nil {
  1081. err = fmt.Errorf("MultiAddReportChaptersSaveLog: %s", e.Error())
  1082. return
  1083. }
  1084. }
  1085. return
  1086. }
  1087. func AddReportChapter(reportInfo *models.Report, inheritReportId int) (err error, errMsg string) {
  1088. // 获取最小分类id
  1089. minClassifyId := reportInfo.ClassifyIdThird
  1090. if minClassifyId <= 0 {
  1091. minClassifyId = reportInfo.ClassifyIdSecond
  1092. }
  1093. if minClassifyId <= 0 {
  1094. minClassifyId = reportInfo.ClassifyIdFirst
  1095. }
  1096. if minClassifyId <= 0 {
  1097. errMsg = "分类异常"
  1098. err = errors.New(errMsg)
  1099. return
  1100. }
  1101. errMsg = "生成报告章节失败"
  1102. // 章节类型列表
  1103. typeList, err := models.GetReportChapterTypeListByClassifyId(minClassifyId)
  1104. if err != nil {
  1105. err = errors.New(errMsg)
  1106. return
  1107. }
  1108. var chapterList []*models.ReportChapter
  1109. // 报告继承
  1110. skip := false
  1111. if inheritReportId > 0 {
  1112. inheritReport, tmpErr := models.GetReportByReportId(inheritReportId)
  1113. if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
  1114. errMsg = "获取待继承的报告失败"
  1115. err = tmpErr
  1116. return
  1117. }
  1118. if inheritReport != nil {
  1119. // 继承上一篇章节内容
  1120. inheritReportChapters, tmpErr := models.GetPublishedChapterListByReportId(inheritReport.Id)
  1121. if tmpErr != nil {
  1122. errMsg = "获取待继承的报告章节失败"
  1123. err = tmpErr
  1124. return
  1125. }
  1126. chapterMap := make(map[int]*models.ReportChapter)
  1127. for i := 0; i < len(inheritReportChapters); i++ {
  1128. chapterMap[inheritReportChapters[i].TypeId] = inheritReportChapters[i]
  1129. }
  1130. for _, typeItem := range typeList {
  1131. v := chapterMap[typeItem.ReportChapterTypeId]
  1132. chapterItem := new(models.ReportChapter)
  1133. if v != nil {
  1134. chapterItem.AddType = 2
  1135. chapterItem.Title = v.Title
  1136. chapterItem.ReportType = reportType
  1137. chapterItem.ClassifyIdFirst = classifyId
  1138. chapterItem.ClassifyNameFirst = classifyName
  1139. chapterItem.TypeId = typeItem.ReportChapterTypeId
  1140. chapterItem.TypeName = typeItem.ReportChapterTypeName
  1141. chapterItem.Content = v.Content
  1142. chapterItem.ContentSub = v.ContentSub
  1143. chapterItem.Stage = stage
  1144. chapterItem.PublishState = 1
  1145. chapterItem.Sort = typeItem.Sort
  1146. chapterItem.CreateTime = req.CreateTime
  1147. chapterItem.ModifyTime = time.Now()
  1148. } else {
  1149. chapterItem.AddType = 1
  1150. chapterItem.ReportType = reportType
  1151. chapterItem.ClassifyIdFirst = classifyId
  1152. chapterItem.ClassifyNameFirst = classifyName
  1153. chapterItem.TypeId = typeItem.ReportChapterTypeId
  1154. chapterItem.TypeName = typeItem.ReportChapterTypeName
  1155. chapterItem.Stage = stage
  1156. chapterItem.PublishState = 1
  1157. chapterItem.Sort = typeItem.Sort
  1158. chapterItem.CreateTime = req.CreateTime
  1159. chapterItem.ModifyTime = time.Now()
  1160. }
  1161. chapterList = append(chapterList, chapterItem)
  1162. }
  1163. skip = true
  1164. }
  1165. }
  1166. if !skip {
  1167. // 空白章节
  1168. for _, typeItem := range typeList {
  1169. chapterItem := new(models.ReportChapter)
  1170. chapterItem.AddType = 1
  1171. chapterItem.ReportType = reportType
  1172. chapterItem.ClassifyIdFirst = classifyId
  1173. chapterItem.ClassifyNameFirst = classifyName
  1174. chapterItem.TypeId = typeItem.ReportChapterTypeId
  1175. chapterItem.TypeName = typeItem.ReportChapterTypeName
  1176. chapterItem.Stage = stage
  1177. chapterItem.PublishState = 1
  1178. chapterItem.Sort = typeItem.Sort
  1179. chapterItem.CreateTime = req.CreateTime
  1180. chapterItem.ModifyTime = time.Now()
  1181. chapterList = append(chapterList, chapterItem)
  1182. }
  1183. }
  1184. // 新增报告及章节
  1185. var reportId int64
  1186. if reportId, err = models.AddReportAndChapter(item, chapterList); err != nil {
  1187. br.Msg = "保存失败"
  1188. br.ErrMsg = "新增报告及章节失败, Err: " + err.Error()
  1189. return
  1190. }
  1191. reportCode := utils.MD5(strconv.Itoa(int(reportId)))
  1192. // 修改唯一编码
  1193. {
  1194. go models.ModifyReportCode(reportId, reportCode)
  1195. }
  1196. }