share_poster.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. package services
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/PuerkitoBio/goquery"
  7. "hongze/hongze_yb/global"
  8. "hongze/hongze_yb/models/tables/yb_poster_resource"
  9. "hongze/hongze_yb/models/tables/yb_suncode_pars"
  10. "hongze/hongze_yb/services/alarm_msg"
  11. "hongze/hongze_yb/services/wx_app"
  12. "hongze/hongze_yb/utils"
  13. "io/ioutil"
  14. "net/http"
  15. "os"
  16. "strings"
  17. "time"
  18. )
  19. var ServerUrl = "http://127.0.0.1:5008/"
  20. // SharePosterReq 分享海报请求体
  21. type SharePosterReq struct {
  22. CodePage string `json:"code_page" description:"太阳码page"`
  23. CodeScene string `json:"code_scene" description:"太阳码scene"`
  24. Source string `json:"source" description:"来源"`
  25. Version string `json:"version" description:"海报版本号" `
  26. Pars string `json:"pars" description:"海报动态信息"`
  27. }
  28. // CreatePosterFromSource 生成分享海报
  29. func CreatePosterFromSource(codePage, codeScene, source, version, pars string) (imgUrl string, err error) {
  30. var errMsg string
  31. defer func() {
  32. if err != nil {
  33. global.LOG.Critical(fmt.Sprintf("CreatePosterFromSource: source=%s, pars:%s, errMsg:%s", source, pars, errMsg))
  34. reqSlice := make([]string, 0)
  35. reqSlice = append(reqSlice, fmt.Sprint("CodePage:", codePage, "\n"))
  36. reqSlice = append(reqSlice, fmt.Sprint("CodeScene:", codeScene, "\n"))
  37. reqSlice = append(reqSlice, fmt.Sprint("Source:", source, "\n"))
  38. reqSlice = append(reqSlice, fmt.Sprint("Version:", version, "\n"))
  39. reqSlice = append(reqSlice, fmt.Sprint("Pars:", pars, "\n"))
  40. go alarm_msg.SendAlarmMsg("CreatePosterFromSource生成分享海报失败, Msg:"+errMsg+";Err:"+err.Error()+"\n;Req:\n"+strings.Join(reqSlice, ";"), 3)
  41. }
  42. }()
  43. if codePage == "" || source == "" || pars == "" {
  44. errMsg = "参数有误"
  45. err = errors.New(errMsg)
  46. return
  47. }
  48. path := fmt.Sprint(codePage, "?", codeScene)
  49. // 非列表来源获取历史图片,无则生成
  50. if !strings.Contains(source, "list") && source != "price_driven" {
  51. poster, tmpErr := yb_poster_resource.GetPosterByCondition(path, "poster", version)
  52. if tmpErr != nil && tmpErr != utils.ErrNoRow {
  53. err = tmpErr
  54. return
  55. }
  56. if poster != nil && poster.ImgURL != "" {
  57. imgUrl = poster.ImgURL
  58. return
  59. }
  60. }
  61. // 图片长宽
  62. heightMap := map[string]int{
  63. "activity_detail": 1210,
  64. "activity_list": 1948,
  65. "special_column_detail": 1480,
  66. "special_column_list": 1548,
  67. "chart_detail": 1536,
  68. "chart_list": 1352,
  69. "report_detail": 1420,
  70. "report_list": 1344,
  71. "price_driven": 1344,
  72. }
  73. widthMap := map[string]int{
  74. "activity_detail": 1280,
  75. "activity_list": 1280,
  76. "special_column_detail": 1176,
  77. "special_column_list": 1176,
  78. "chart_detail": 1176,
  79. "chart_list": 1176,
  80. "report_detail": 1176,
  81. "report_list": 1176,
  82. "price_driven": 1176,
  83. }
  84. width := widthMap[source]
  85. height := heightMap[source]
  86. if width == 0 || height == 0 {
  87. errMsg = "图片长度有误"
  88. err = errors.New(errMsg)
  89. return "", err
  90. }
  91. // 生成太阳码
  92. sunCodeUrl, err := CreateAndUploadSunCode(codePage, codeScene, version)
  93. if err != nil {
  94. return
  95. }
  96. // 填充html内容
  97. contentStr, newHeight, err := fillContent2Html(source, pars, sunCodeUrl, height)
  98. if err != nil {
  99. errMsg = "html内容有误"
  100. return
  101. }
  102. // 请求python服务htm2img
  103. htm2ImgReq := make(map[string]interface{})
  104. htm2ImgReq["html_content"] = contentStr
  105. htm2ImgReq["width"] = width
  106. htm2ImgReq["height"] = newHeight
  107. res, err := postHtml2Img(htm2ImgReq)
  108. if err != nil || res == nil {
  109. errMsg = "html转图片请求失败"
  110. return
  111. }
  112. if res.Code != 200 {
  113. errMsg = "html转图片请求失败"
  114. err = errors.New("html转图片失败: " + res.Msg)
  115. return
  116. }
  117. imgUrl = res.Data
  118. // 记录海报信息
  119. newPoster := &yb_poster_resource.YbPosterResource{
  120. Path: path,
  121. ImgURL: imgUrl,
  122. Type: "poster",
  123. Version: version,
  124. CreateTime: time.Now(),
  125. }
  126. err = newPoster.Create()
  127. return
  128. }
  129. type Html2ImgResp struct {
  130. Code int `json:"code"`
  131. Msg string `json:"msg"`
  132. Data string `json:"data"`
  133. }
  134. // postHtml2Img 请求htm2img接口
  135. func postHtml2Img(param map[string]interface{}) (resp *Html2ImgResp, err error) {
  136. // 目前仅此处调用该接口,暂不加授权、校验等
  137. postUrl := ServerUrl + "htm2img"
  138. postData, err := json.Marshal(param)
  139. if err != nil {
  140. return
  141. }
  142. result, err := Html2ImgHttpPost(postUrl, string(postData), "application/json")
  143. if err != nil {
  144. return
  145. }
  146. if err = json.Unmarshal(result, &resp); err != nil {
  147. return
  148. }
  149. return resp, nil
  150. }
  151. // Html2ImgHttpPost post请求
  152. func Html2ImgHttpPost(url, postData string, params ...string) ([]byte, error) {
  153. body := ioutil.NopCloser(strings.NewReader(postData))
  154. client := &http.Client{}
  155. req, err := http.NewRequest("POST", url, body)
  156. if err != nil {
  157. return nil, err
  158. }
  159. contentType := "application/x-www-form-urlencoded;charset=utf-8"
  160. if len(params) > 0 && params[0] != "" {
  161. contentType = params[0]
  162. }
  163. req.Header.Set("Content-Type", contentType)
  164. resp, err := client.Do(req)
  165. if err != nil {
  166. return nil, err
  167. }
  168. defer resp.Body.Close()
  169. b, err := ioutil.ReadAll(resp.Body)
  170. fmt.Println("HttpPost:" + string(b))
  171. return b, err
  172. }
  173. // CreateAndUploadSunCode 生成太阳码并上传OSS
  174. func CreateAndUploadSunCode(page, scene, version string) (imgUrl string, err error) {
  175. if page == "" {
  176. err = errors.New("page不能为空")
  177. return
  178. }
  179. path := fmt.Sprint(page, "?", scene)
  180. exist, err := yb_poster_resource.GetPosterByCondition(path, "qrcode", version)
  181. if err != nil && err != utils.ErrNoRow {
  182. return
  183. }
  184. if exist != nil && exist.ImgURL != "" {
  185. return exist.ImgURL, nil
  186. }
  187. // scene超过32位会生成失败,md5处理至32位
  188. sceneMD5 := "a=1"
  189. if scene != "" {
  190. sceneMD5 = utils.MD5(scene)
  191. }
  192. picByte, err := wx_app.GetSunCode(page, sceneMD5)
  193. if err != nil {
  194. return
  195. }
  196. // 生成图片
  197. localPath := "./static/img"
  198. fileName := utils.GetRandStringNoSpecialChar(28) + ".png"
  199. fpath := fmt.Sprint(localPath, "/", fileName)
  200. f, err := os.Create(fpath)
  201. if err != nil {
  202. return
  203. }
  204. if _, err = f.Write(picByte); err != nil {
  205. return
  206. }
  207. defer func() {
  208. f.Close()
  209. os.Remove(fpath)
  210. }()
  211. // 上传OSS
  212. fileDir := "images/yb/suncode/"
  213. imgUrl, err = UploadAliyunToDir(fileName, fpath, fileDir)
  214. if err != nil {
  215. return
  216. }
  217. // 记录二维码信息
  218. newPoster := &yb_poster_resource.YbPosterResource{
  219. Path: path,
  220. ImgURL: imgUrl,
  221. Type: "qrcode",
  222. Version: version,
  223. CreateTime: time.Now(),
  224. }
  225. err = newPoster.Create()
  226. if err != nil {
  227. return
  228. }
  229. // 记录参数md5
  230. if scene != "" {
  231. newPars := &yb_suncode_pars.YbSuncodePars{
  232. Scene: scene,
  233. SceneKey: sceneMD5,
  234. CreateTime: time.Now(),
  235. }
  236. err = newPars.Create()
  237. }
  238. return
  239. }
  240. type PosterParsReq struct {
  241. ActivityTitle string `json:"activity_title"`
  242. ActivityAvatar string `json:"activity_avatar"`
  243. ActivitySpeaker string `json:"activity_speaker"`
  244. ActivityTime string `json:"activity_time"`
  245. ChartName string `json:"chart_name"`
  246. ChartImage string `json:"chart_image"`
  247. ReportType string `json:"report_type"`
  248. ReportAvatar string `json:"report_avatar"`
  249. ReportTitle string `json:"report_title"`
  250. ReportAbstract string `json:"report_abstract"`
  251. Stage1 string `json:"stage_1"`
  252. Avatar1 string `json:"avatar_1"`
  253. Title1 string `json:"title_1"`
  254. Author1 string `json:"author_1"`
  255. Tag1 string `json:"tag_1"`
  256. Img1 string `json:"img_1"`
  257. Time1 string `json:"time_1"`
  258. Abstract1 string `json:"abstract_1"`
  259. Status1 string `json:"status_1"`
  260. Speaker1 string `json:"speaker_1"`
  261. Stage2 string `json:"stage_2"`
  262. Avatar2 string `json:"avatar_2"`
  263. Title2 string `json:"title_2"`
  264. Author2 string `json:"author_2"`
  265. Tag2 string `json:"tag_2"`
  266. Img2 string `json:"img_2"`
  267. Abstract2 string `json:"abstract_2"`
  268. Time2 string `json:"time_2"`
  269. Status2 string `json:"status_2"`
  270. Speaker2 string `json:"speaker_2"`
  271. ListTitle string `json:"list_title"`
  272. CoreDrivenType string `json:"core_driven_type"`
  273. CoreDrivenContent string `json:"core_driven_content"`
  274. MainVariable string `json:"main_variable"`
  275. UpdateTime string `json:"update_time"`
  276. }
  277. // fillContent2Html 填充HTML动态内容
  278. func fillContent2Html(source, pars, sunCodeUrl string, height int) (contentStr string, newHeight int, err error) {
  279. params := new(PosterParsReq)
  280. if err = json.Unmarshal([]byte(pars), &params); err != nil {
  281. return
  282. }
  283. template := fmt.Sprint("static/htm2img/", source, ".html")
  284. contentByte, err := ioutil.ReadFile(template)
  285. if err != nil {
  286. return
  287. }
  288. newHeight = height
  289. contentStr = string(contentByte)
  290. // 列表的动态内容不完整的用默认内容的填充
  291. var emptyTime1, emptyTime2 bool
  292. if strings.Contains(source, "list") {
  293. if params.Title1 == "" || params.Title2 == "" {
  294. defaultAvatar := "https://hzstatic.hzinsights.com/static/images/202112/20211210/wn6c3oYKTfT4NbTZgRGflRuIBZaa.png"
  295. switch source {
  296. case "activity_list":
  297. if params.Title1 == "" {
  298. params.ListTitle = "线下沙龙"
  299. params.Status1 = "未开始"
  300. params.Avatar1 = defaultAvatar
  301. params.Title1 = "周度报告"
  302. params.Speaker1 = "FICC研究员"
  303. params.Time1 = "2022-5-10"
  304. emptyTime1 = true
  305. }
  306. params.Status2 = params.Status1
  307. params.Avatar2 = defaultAvatar
  308. params.Title2 = "周度报告"
  309. params.Speaker2 = "FICC研究员"
  310. params.Time2 = "2022-5-10"
  311. emptyTime2 = true
  312. case "special_column_list":
  313. if params.Title1 == "" {
  314. params.Stage1 = "第1期"
  315. params.Avatar1 = defaultAvatar
  316. params.Title1 = "弘则FICC专栏"
  317. params.Author1 = "弘则研究"
  318. params.Tag1 = "FICC研究员"
  319. }
  320. params.Stage2 = "第2期"
  321. params.Avatar2 = defaultAvatar
  322. params.Title2 = "弘则FICC专栏"
  323. params.Author2 = "弘则研究"
  324. params.Tag2 = "FICC研究员"
  325. case "chart_list":
  326. if params.Title1 == "" {
  327. params.Title1 = "螺纹仓单-热卷仓单季节性"
  328. params.Img1 = "https://hzstatic.hzinsights.com/static/images/202112/20211227/8VBIH1l6VraYpLCoBS6qOIXA5Zoq.png"
  329. }
  330. params.Title2 = "卷螺期货现货价差"
  331. params.Img2 = "https://hzstatic.hzinsights.com/static/images/202204/20220427/d8GRfdR3Xfrvk397SnYudcwVs9pV.png"
  332. case "report_list":
  333. if params.Title1 == "" {
  334. params.Img1 = defaultAvatar
  335. params.Title1 = "弘则FICC研报"
  336. params.Time1 = "1期"
  337. }
  338. params.Img2 = defaultAvatar
  339. params.Title2 = "弘则FICC研报"
  340. params.Time2 = "2期"
  341. }
  342. }
  343. }
  344. // 填充指定内容
  345. switch source {
  346. case "activity_detail":
  347. contentStr = strings.Replace(contentStr, "{{ACTIVITY_TITLE}}", params.ActivityTitle, 1)
  348. contentStr = strings.Replace(contentStr, "{{ACTIVITY_AVATAR}}", params.ActivityAvatar, 1)
  349. contentStr = strings.Replace(contentStr, "{{ACTIVITY_SPEAKER}}", params.ActivitySpeaker, 1)
  350. contentStr = strings.Replace(contentStr, "{{ACTIVITY_TIME}}", params.ActivityTime, 1)
  351. case "special_column_detail":
  352. contentStr = strings.Replace(contentStr, "{{REPORT_AVATAR}}", params.ReportAvatar, 1)
  353. contentStr = strings.Replace(contentStr, "{{REPORT_TITLE}}", params.ReportTitle, 1)
  354. contentStr = strings.Replace(contentStr, "{{REPORT_ABSTRACT}}", params.ReportAbstract, 1)
  355. case "chart_detail":
  356. contentStr = strings.Replace(contentStr, "{{CHART_NAME}}", params.ChartName, 1)
  357. contentStr = strings.Replace(contentStr, "{{CHART_IMAGE}}", params.ChartImage, 1)
  358. case "report_detail":
  359. doc, tmpErr := goquery.NewDocumentFromReader(strings.NewReader(params.ReportAbstract))
  360. if tmpErr != nil {
  361. err = tmpErr
  362. return
  363. }
  364. abstract := ""
  365. doc.Find("p").Each(func(i int, s *goquery.Selection) {
  366. phtml, tmpErr := s.Html()
  367. if tmpErr != nil {
  368. err = tmpErr
  369. return
  370. }
  371. st := s.Text()
  372. if st != "" && st != "<br>" && st != "<br style=\"max-width: 100%;\">" && !strings.Contains(phtml, "iframe") {
  373. abstract = abstract + "<p>" + phtml + "</p>"
  374. }
  375. })
  376. contentStr = strings.Replace(contentStr, "{{REPORT_TYPE}}", params.ReportType, 1)
  377. contentStr = strings.Replace(contentStr, "{{REPORT_TITLE}}", params.ReportTitle, 1)
  378. contentStr = strings.Replace(contentStr, "{{REPORT_ABSTRACT}}", abstract, 1)
  379. case "special_column_list":
  380. contentStr = strings.Replace(contentStr, "{{LIST_TITLE}}", params.ListTitle, 1)
  381. contentStr = strings.Replace(contentStr, "{{STAGE_1}}", "第"+params.Stage1+"期", 1)
  382. contentStr = strings.Replace(contentStr, "{{AVATAR_1}}", params.Avatar1, 1)
  383. contentStr = strings.Replace(contentStr, "{{TITLE_1}}", params.Title1, 1)
  384. contentStr = strings.Replace(contentStr, "{{AUTHOR_1}}", params.Author1, 1)
  385. contentStr = strings.Replace(contentStr, "{{TAG_1}}", params.Tag1, 1)
  386. contentStr = strings.Replace(contentStr, "{{STAGE_2}}", "第"+params.Stage2+"期", 1)
  387. contentStr = strings.Replace(contentStr, "{{AVATAR_2}}", params.Avatar2, 1)
  388. contentStr = strings.Replace(contentStr, "{{TITLE_2}}", params.Title2, 1)
  389. contentStr = strings.Replace(contentStr, "{{AUTHOR_2}}", params.Author2, 1)
  390. contentStr = strings.Replace(contentStr, "{{TAG_2}}", params.Tag2, 1)
  391. case "chart_list":
  392. contentStr = strings.Replace(contentStr, "{{LIST_TITLE}}", params.ListTitle, 1)
  393. contentStr = strings.Replace(contentStr, "{{TITLE_1}}", params.Title1, 1)
  394. contentStr = strings.Replace(contentStr, "{{IMG_1}}", params.Img1, 1)
  395. contentStr = strings.Replace(contentStr, "{{TITLE_2}}", params.Title2, 1)
  396. contentStr = strings.Replace(contentStr, "{{IMG_2}}", params.Img2, 1)
  397. case "report_list":
  398. contentStr = strings.Replace(contentStr, "{{LIST_TITLE}}", params.ListTitle, 1)
  399. contentStr = strings.Replace(contentStr, "{{TITLE_1}}", params.Title1, 1)
  400. contentStr = strings.Replace(contentStr, "{{ABSTRACT_1}}", params.Abstract1, 1)
  401. contentStr = strings.Replace(contentStr, "{{IMG_1}}", params.Img1, 1)
  402. contentStr = strings.Replace(contentStr, "{{TIME_1}}", params.Time1, 1)
  403. contentStr = strings.Replace(contentStr, "{{TITLE_2}}", params.Title2, 1)
  404. contentStr = strings.Replace(contentStr, "{{ABSTRACT_2}}", params.Abstract2, 1)
  405. contentStr = strings.Replace(contentStr, "{{IMG_2}}", params.Img2, 1)
  406. contentStr = strings.Replace(contentStr, "{{TIME_2}}", params.Time2, 1)
  407. case "price_driven":
  408. contentStr = strings.Replace(contentStr, "{{LIST_TITLE}}", params.ListTitle, 1)
  409. contentStr = strings.Replace(contentStr, "{{CORE_DRIVEN_TYPE}}", params.CoreDrivenType, 1)
  410. contentStr = strings.Replace(contentStr, "{{CORE_DRIVEN_CONTENT}}", params.CoreDrivenContent, 1)
  411. contentStr = strings.Replace(contentStr, "{{MAIN_VARIABLE}}", params.MainVariable, 1)
  412. contentStr = strings.Replace(contentStr, "{{UPDATE_TIME}}", params.UpdateTime, 1)
  413. case "activity_list":
  414. bgColorMap := map[string]string{
  415. "未开始": "#E3B377",
  416. "进行中": "#3385FF",
  417. "已结束": "#A2A2A2",
  418. }
  419. statusItemMap := map[string]string{
  420. "未开始": "block",
  421. "进行中": "none",
  422. "已结束": "none",
  423. }
  424. offlineMap := map[string]string{
  425. "线上会议": "none",
  426. "线下沙龙": "block",
  427. }
  428. onlineMap := map[string]string{
  429. "线上会议": "block",
  430. "线下沙龙": "none",
  431. }
  432. if params.Status1 != "未开始" {
  433. newHeight = 1715
  434. }
  435. contentStr = strings.Replace(contentStr, "{{LIST_TITLE}}", "弘则FICC周度电话会安排", 1)
  436. contentStr = strings.Replace(contentStr, "{{BG_COLORE_1}}", bgColorMap[params.Status1], 1)
  437. contentStr = strings.Replace(contentStr, "{{STATUS_1}}", params.Status1, 1)
  438. contentStr = strings.Replace(contentStr, "{{AVATAR_1}}", params.Avatar1, 1)
  439. contentStr = strings.Replace(contentStr, "{{TITLE_1}}", params.Title1, 1)
  440. contentStr = strings.Replace(contentStr, "{{SPEAKER_1}}", params.Speaker1, 1)
  441. contentStr = strings.Replace(contentStr, "{{TIME_1}}", params.Time1, 1)
  442. contentStr = strings.Replace(contentStr, "{{SHOW_ITEM_1}}", statusItemMap[params.Status1], 1)
  443. contentStr = strings.Replace(contentStr, "{{SHOW_OFFLINE_1}}", offlineMap[params.ListTitle], 1)
  444. contentStr = strings.Replace(contentStr, "{{SHOW_ONLINE_1}}", onlineMap[params.ListTitle], 1)
  445. contentStr = strings.Replace(contentStr, "{{BG_COLORE_2}}", bgColorMap[params.Status2], 1)
  446. contentStr = strings.Replace(contentStr, "{{STATUS_2}}", params.Status2, 1)
  447. contentStr = strings.Replace(contentStr, "{{AVATAR_2}}", params.Avatar2, 1)
  448. contentStr = strings.Replace(contentStr, "{{TITLE_2}}", params.Title2, 1)
  449. contentStr = strings.Replace(contentStr, "{{SPEAKER_2}}", params.Speaker2, 1)
  450. contentStr = strings.Replace(contentStr, "{{TIME_2}}", params.Time2, 1)
  451. contentStr = strings.Replace(contentStr, "{{SHOW_ITEM_2}}", statusItemMap[params.Status2], 1)
  452. contentStr = strings.Replace(contentStr, "{{SHOW_OFFLINE_2}}", offlineMap[params.ListTitle], 1)
  453. contentStr = strings.Replace(contentStr, "{{SHOW_ONLINE_2}}", onlineMap[params.ListTitle], 1)
  454. // 用默认内容填充的活动时间字体颜色调至看不见
  455. color1 := "#999"
  456. color2 := "#999"
  457. if emptyTime1 {
  458. color1 = "#fff"
  459. }
  460. if emptyTime2 {
  461. color2 = "#fff"
  462. }
  463. contentStr = strings.Replace(contentStr, "{{TIME_COLOR_1}}", color1, 1)
  464. contentStr = strings.Replace(contentStr, "{{TIME_COLOR_2}}", color2, 1)
  465. }
  466. contentStr = strings.Replace(contentStr, "{{SUN_CODE}}", sunCodeUrl, 1)
  467. return
  468. }
  469. // GetRddpShareImg 获取日度点评分享图
  470. func GetRddpShareImg(title string) (imgUrl string, err error) {
  471. if title == "" {
  472. return
  473. }
  474. template := fmt.Sprint("static/htm2img/rddp-share.html")
  475. contentByte, e := ioutil.ReadFile(template)
  476. if e != nil {
  477. err = errors.New("读取模板失败, Err: " + e.Error())
  478. return
  479. }
  480. contentStr := string(contentByte)
  481. contentStr = strings.Replace(contentStr, "{{TITLE}}", title, 1)
  482. htm2ImgReq := make(map[string]interface{})
  483. htm2ImgReq["html_content"] = contentStr
  484. htm2ImgReq["width"] = 1000
  485. htm2ImgReq["height"] = 800
  486. res, e := postHtml2Img(htm2ImgReq)
  487. if e != nil || res == nil {
  488. err = errors.New("html转图片请求失败")
  489. return
  490. }
  491. if res.Code != 200 {
  492. err = errors.New("html转图片请求失败: " + res.Msg)
  493. return
  494. }
  495. imgUrl = res.Data
  496. return
  497. }