ppt.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. package services
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "hongze/hz_eta_api/models"
  7. "hongze/hz_eta_api/models/ppt_english"
  8. "hongze/hz_eta_api/models/system"
  9. "hongze/hz_eta_api/services/alarm_msg"
  10. "hongze/hz_eta_api/services/ppt2img"
  11. "hongze/hz_eta_api/utils"
  12. "sort"
  13. "time"
  14. )
  15. const (
  16. ElementsTypeText = "text"
  17. ElementsTypeImage = "image"
  18. ElementsTypeChart = "chart"
  19. ElementsTypeSheet = "sheet"
  20. )
  21. type PPTContent struct {
  22. //Id int `json:"id" description:"此处因目录改版类型有int也有string且并没有使用到该字段所以注释掉"`
  23. Key int `json:"key"`
  24. ModelId int `json:"modelId"`
  25. Title string `json:"title"`
  26. Elements []PPTContentElements `json:"elements"`
  27. }
  28. type PPTContentElements struct {
  29. Type string `json:"type"`
  30. Position int `json:"position"`
  31. Content string `json:"content"`
  32. RichContent string `json:"richContent"`
  33. ChartId string `json:"chartId"`
  34. SheetId string `json:"sheetId"`
  35. SheetHeight string `json:"sheetHeight"`
  36. Src string `json:"src"`
  37. }
  38. // SavePPTReport 保存PPT报告
  39. func SavePPTReport(pptId, classifyIdSecond int, title string, adminInfo *system.Admin) (reportId int, reportCode, errMsg string, err error) {
  40. defer func() {
  41. if err != nil {
  42. utils.FileLog.Info("%s", err.Error())
  43. go alarm_msg.SendAlarmMsg("PPT转报告失败, SavePPTReport Msg: "+errMsg+", Err: "+err.Error(), 3)
  44. }
  45. }()
  46. if pptId == 0 {
  47. errMsg = "参数有误"
  48. err = errors.New("参数有误")
  49. return
  50. }
  51. item, e := models.GetPptV2ById(pptId)
  52. if e != nil {
  53. errMsg = "获取PPT失败"
  54. err = errors.New("获取PPT失败, Err: " + e.Error())
  55. return
  56. }
  57. // PPT内容转HTML
  58. htm, e := pptContent2Html(item.Content, false)
  59. if e != nil {
  60. errMsg = "转换失败"
  61. err = e
  62. return
  63. }
  64. // 2023-02-21 PPT可多次转为报告, 不做关联
  65. if classifyIdSecond == 0 {
  66. errMsg = "请选择报告类型"
  67. err = errors.New("请选择报告类型")
  68. return
  69. }
  70. if title == "" {
  71. errMsg = "标题不能为空"
  72. err = errors.New("标题不能为空")
  73. return
  74. }
  75. // 获取分类及父级分类
  76. classifyList, e := models.GetAllClassify()
  77. if e != nil {
  78. errMsg = "转换失败"
  79. err = errors.New("获取分类列表失败, Err: " + e.Error())
  80. return
  81. }
  82. classifyMap := make(map[int]*models.Classify, 0)
  83. for _, v := range classifyList {
  84. classifyMap[v.Id] = v
  85. }
  86. classifyIdFirst := 0
  87. classifyNameFirst := ""
  88. classifyNameSecond := ""
  89. secondClassify := classifyMap[classifyIdSecond]
  90. if secondClassify != nil {
  91. classifyNameSecond = secondClassify.ClassifyName
  92. firstClassify := classifyMap[secondClassify.ParentId]
  93. if firstClassify != nil {
  94. classifyIdFirst = firstClassify.Id
  95. classifyNameFirst = firstClassify.ClassifyName
  96. }
  97. }
  98. // 新增报告
  99. nowTime := time.Now().Local()
  100. reportReq := &models.AddReq{
  101. AddType: 1,
  102. ClassifyIdFirst: classifyIdFirst,
  103. ClassifyNameFirst: classifyNameFirst,
  104. ClassifyIdSecond: classifyIdSecond,
  105. ClassifyNameSecond: classifyNameSecond,
  106. Title: title,
  107. Abstract: "",
  108. Author: "FICC团队",
  109. Frequency: utils.ReportFrequencyDefault,
  110. State: 1,
  111. Content: htm,
  112. CreateTime: nowTime.Format(utils.FormatDateTime),
  113. ReportVersion: 2,
  114. }
  115. newReportId, newCode, _, e := CreateNewReport(*reportReq, adminInfo)
  116. if e != nil {
  117. errMsg = "转换失败"
  118. err = errors.New("新增报告失败, Err: " + e.Error())
  119. return
  120. }
  121. reportId = int(newReportId)
  122. reportCode = newCode
  123. // 更新报告中的ppt图片
  124. go saveReportPptImg(pptId, reportId, item.PptxUrl)
  125. return
  126. }
  127. // PPT2内容转HTML
  128. func pptContent2Html(content string, isEnglish bool) (htm string, err error) {
  129. contents := make([]PPTContent, 0)
  130. if e := json.Unmarshal([]byte(content), &contents); e != nil {
  131. err = errors.New("PPT内容转换失败")
  132. return
  133. }
  134. pageLen := len(contents)
  135. htmlContent := ``
  136. // iframe图表/表格域名
  137. chartRoot := `https://chartlib.hzinsights.com`
  138. if utils.RunMode == "debug" {
  139. chartRoot = `https://charttest.hzinsights.com`
  140. }
  141. if pageLen > 0 {
  142. htmlPrefix := `<p style="text-align: left; margin-top: 10px; font-size: 16px;">`
  143. htmlSuffix := `</p>`
  144. htmlBr := `<br>`
  145. for i := 0; i < pageLen; i++ {
  146. // 每页标题加粗居中
  147. title := contents[i].Title
  148. if title != "" {
  149. htmlContent += `<p style="font-size: 16px; text-align: left;"><strong>`
  150. htmlContent += title
  151. htmlContent += `</strong></p>`
  152. }
  153. ele := contents[i].Elements
  154. // 每页元素按照Position升序排序
  155. sort.Slice(ele, func(k, j int) bool {
  156. return ele[k].Position < ele[j].Position
  157. })
  158. for _, v := range ele {
  159. // 根据不同的Type拼接不同的内容
  160. htmlContent += htmlPrefix
  161. switch v.Type {
  162. case ElementsTypeText:
  163. htmlContent += v.RichContent
  164. case ElementsTypeImage:
  165. htmlContent += fmt.Sprint(`<img src="`, v.Src, `" class="fr-fic fr-dib fr-draggable">`)
  166. case ElementsTypeChart:
  167. if isEnglish {
  168. // 英文研报图表src多加一个fromPage=en, 表格暂时没有区分
  169. htmlContent += fmt.Sprintf(`<iframe src="%s/chartshow?code=%s&fromPage=en" width="100%%" height="350" style="border-width:0px; min-height:350px;"></iframe>`, chartRoot, v.ChartId)
  170. break
  171. }
  172. htmlContent += fmt.Sprintf(`<iframe src="%s/chartshow?code=%s" width="100%%" height="350" style="border-width:0px; min-height:350px;"></iframe>`, chartRoot, v.ChartId)
  173. case ElementsTypeSheet:
  174. htmlContent += fmt.Sprintf(`<iframe src="%s/sheetshow?code=%s" class="iframe%s" width="100%%" height="%s" style="border-width:0px;"></iframe>`, chartRoot, v.SheetId, v.SheetId, v.SheetHeight)
  175. }
  176. htmlContent += htmlSuffix
  177. }
  178. // 每页中间插入一个换行符, 最后一页不插入
  179. currentPage := i + 1
  180. if currentPage != pageLen {
  181. htmlContent += htmlPrefix + htmlBr + htmlSuffix
  182. }
  183. }
  184. }
  185. htm = htmlContent
  186. return
  187. }
  188. // ResetPPTReport 重置PPT关联的报告(如删除关联的报告后)
  189. func ResetPPTReport(reportId int, isEnglish bool) (err error) {
  190. defer func() {
  191. if err != nil {
  192. utils.FileLog.Info("%s", err.Error())
  193. go alarm_msg.SendAlarmMsg("重置PPT关联报告失败, ResetPPTReport Err: "+err.Error(), 3)
  194. }
  195. }()
  196. // 英文报告
  197. if isEnglish {
  198. en, e := ppt_english.GetPptEnglishByReportId(reportId)
  199. if e != nil && e.Error() != utils.ErrNoRow() {
  200. err = errors.New("获取英文PPT失败, Err: " + e.Error())
  201. return
  202. }
  203. if en != nil {
  204. updateCols := []string{"ReportId", "ReportCode"}
  205. en.ReportId = 0
  206. en.ReportCode = ""
  207. if e = en.Update(updateCols); e != nil {
  208. err = errors.New("更新英文PPT关联报告失败, Err: " + e.Error())
  209. return
  210. }
  211. }
  212. return
  213. }
  214. // 中文报告
  215. item, e := models.GetPptV2ByReportId(reportId)
  216. if e != nil && e.Error() != utils.ErrNoRow() {
  217. err = errors.New("获取PPT失败, Err: " + e.Error())
  218. return
  219. }
  220. if item != nil {
  221. updateCols := []string{"ReportId", "ReportCode"}
  222. item.ReportId = 0
  223. item.ReportCode = ""
  224. if e = item.Update(updateCols); e != nil {
  225. err = errors.New("更新PPT关联报告失败, Err: " + e.Error())
  226. return
  227. }
  228. }
  229. return
  230. }
  231. // saveReportPptImg ppt转报告后,需要再次将ppt转图片
  232. func saveReportPptImg(pptId, reportId int, pptUrl string) {
  233. var err error
  234. defer func() {
  235. if err != nil {
  236. utils.FileLog.Info(fmt.Sprintf("将ppt转图片失败, saveReportPptImg Err:%s", err.Error()))
  237. go alarm_msg.SendAlarmMsg("将ppt转图片失败, saveReportPptImg Err: "+err.Error(), 3)
  238. }
  239. }()
  240. // 更新报告内容
  241. report, e := models.GetReportByReportId(reportId)
  242. if e != nil && e.Error() != utils.ErrNoRow() {
  243. err = errors.New("获取报告失败, Err: " + e.Error())
  244. return
  245. }
  246. if report == nil {
  247. return
  248. }
  249. // 获取ppt转图片的结果
  250. list, err := ppt2img.Ppt2Img(pptUrl)
  251. if err != nil {
  252. return
  253. }
  254. reportPptImgList := make([]*models.ReportPptImg, 0)
  255. for _, v := range list {
  256. reportPptImg := &models.ReportPptImg{
  257. //ReportPptImgId: 0,
  258. PptId: pptId,
  259. ReportId: reportId,
  260. ReportChapterId: 0,
  261. ImgUrl: v,
  262. CreateTime: time.Now(),
  263. }
  264. reportPptImgList = append(reportPptImgList, reportPptImg)
  265. }
  266. //批量添加Ppt转报告的图片记录
  267. err = models.AddAndEditMultiReportPptImg(pptId, reportPptImgList)
  268. return
  269. }
  270. // SaveEnglishPPTReport 保存英文PPT报告
  271. func SaveEnglishPPTReport(pptId, classifyIdFirst, classifyIdSecond int, title, abstract string, adminInfo *system.Admin) (reportId int, reportCode, errMsg string, err error) {
  272. defer func() {
  273. if err != nil {
  274. utils.FileLog.Info("%s", err.Error())
  275. go alarm_msg.SendAlarmMsg("PPT转报告失败, SavePPTReport Msg: "+errMsg+", Err: "+err.Error(), 3)
  276. }
  277. }()
  278. if pptId == 0 {
  279. errMsg = "参数有误"
  280. err = errors.New("参数有误")
  281. return
  282. }
  283. item, e := ppt_english.GetPptEnglishById(pptId)
  284. if e != nil {
  285. errMsg = "获取PPT失败"
  286. err = errors.New("获取PPT失败, Err: " + e.Error())
  287. return
  288. }
  289. // PPT内容转HTML
  290. htm, e := pptContent2Html(item.Content, true)
  291. if e != nil {
  292. errMsg = "转换失败"
  293. err = e
  294. return
  295. }
  296. // 2023-02-21 PPT可多次转为报告, 不做关联
  297. if title == "" {
  298. errMsg = "标题不能为空"
  299. err = errors.New("标题不能为空")
  300. return
  301. }
  302. if classifyIdFirst <= 0 {
  303. errMsg = "请选择报告分类"
  304. err = errors.New("报告分类不能为空")
  305. return
  306. }
  307. // 分类
  308. classifyList, e := models.GetAllEnglishClassify(0)
  309. if e != nil {
  310. errMsg = "转换失败"
  311. err = errors.New("获取分类列表失败, Err: " + e.Error())
  312. return
  313. }
  314. classifyMap := make(map[int]string, 0)
  315. for _, v := range classifyList {
  316. classifyMap[v.Id] = v.ClassifyName
  317. }
  318. classifyNameFirst := classifyMap[classifyIdFirst]
  319. classifyNameSecond := classifyMap[classifyIdSecond]
  320. // 新增报告
  321. nowTime := time.Now().Local()
  322. reportReq := &models.AddEnglishReportReq{
  323. AddType: 1,
  324. ClassifyIdFirst: classifyIdFirst,
  325. ClassifyNameFirst: classifyNameFirst,
  326. ClassifyIdSecond: classifyIdSecond,
  327. ClassifyNameSecond: classifyNameSecond,
  328. Title: title,
  329. Abstract: abstract,
  330. Author: "Horizon Insights FICC Team",
  331. Frequency: utils.ReportFrequencyDefault,
  332. State: 1,
  333. Content: htm,
  334. CreateTime: nowTime.Format(utils.FormatDateTime),
  335. }
  336. newReportId, newCode, e := CreateNewEnglishReport(*reportReq, adminInfo)
  337. if e != nil {
  338. errMsg = "转换失败"
  339. err = errors.New("新增报告失败, Err: " + e.Error())
  340. return
  341. }
  342. reportId = int(newReportId)
  343. reportCode = newCode
  344. return
  345. }