package services import ( "bytes" "encoding/json" "eta/eta_report/models" "eta/eta_report/services/alarm_msg" "eta/eta_report/utils" "fmt" "io/ioutil" "net/http" "sync" "time" ) const ( PythonReportHtml2ImgApi = "/api/report/html2img" // 图片生成接口 PythonReportHtml2ImgClearLocalApi = "/api/report/clear_local_file" // 清除本地图片文件接口 ) // CreateReportImgAndPdf 报告详情生成长图及PDF func CreateReportImgAndPdf(req Report2ImgQueueReq) { // 研报以后大概率也需要, 暂只处理智能研报的 if req.ReportType != 2 { return } if req.ReportCode == "" { return } var err error defer func() { if err != nil { tips := fmt.Sprintf("报告详情转长图, ErrMsg: %s", err.Error()) fmt.Println(tips) utils.FileLog.Info(tips) go alarm_msg.SendAlarmMsg(tips, 3) } }() // 先取报告转长图的host配置, 如果没有再取原有的报告分享配置(兼容只配了其中一个的客户) conf, e := models.GetBusinessConf() if e != nil { err = fmt.Errorf("获取商家配置失败, Err: %s", e.Error()) return } reportViewHost := conf[models.BusinessConfReport2ImgUrl] if reportViewHost == "" { reportViewHost = conf[models.BusinessConfReportViewUrl] } if reportViewHost == "" { err = fmt.Errorf("报告分享域名未配置") return } // 校验报告是否被删 reportOb := new(models.SmartReport) cond := ` AND report_code = ?` pars := make([]interface{}, 0) pars = append(pars, req.ReportCode) item, e := reportOb.GetItemByCondition(cond, pars) if e != nil { if e.Error() == utils.ErrNoRow() { return } err = fmt.Errorf("获取报告失败, Err: %s", e.Error()) return } // 报告平均生成时间基本在3min以下, 设定一个6min的超时以免后续阻塞 apiDone := make(chan bool, 1) apiError := make(chan error, 1) fileName := utils.GetRandStringNoSpecialChar(28) apiResult := make([]string, 0) go func() { var apiReq ReportHtml2ImgApiReq apiReq.ReportUrl = fmt.Sprintf("%s/%s?code=%s", reportViewHost, "smart_report_getImg", req.ReportCode) //if utils.RunMode == "debug" { // apiReq.ReportUrl = "https://ficc.hzinsights.com/reportshare_crm_report?code=4e38d30e656da5ae9d3a425109ce9e04" //} //fmt.Println(apiReq.ReportUrl) apiReq.FileName = fileName //fmt.Println("curl start") res, e := CurlReportHtml2ImgApi(apiReq) if e != nil { apiError <- fmt.Errorf("CurlReportHtml2ImgApi err: %s", e.Error()) return } //fmt.Println("curl end") apiResult = res apiDone <- true }() select { case <-time.After(6 * time.Minute): err = fmt.Errorf("报告生成长图超时") return case e = <-apiError: err = e return case <-apiDone: fmt.Println("api done") } if len(apiResult) != 2 { err = fmt.Errorf("文件生成有误") return } // 上传IMG, PDF var imgUrl, pdfUrl string var errImg, errPdf error //uploadDir := "static/report_images/" //fileDir := "" wg := sync.WaitGroup{} wg.Add(2) go func() { defer func() { wg.Done() }() imgFileName := fileName + ".png" ossClient := NewOssClient() if ossClient == nil { errImg = fmt.Errorf("初始化OSS服务失败") return } fmt.Println("start UploadFile") si, e := ossClient.UploadFile(imgFileName, apiResult[0], "") if e != nil { fmt.Println("UploadFile,Err:" + e.Error()) errImg = fmt.Errorf("文件上传失败, Err: %s", e.Error()) return } fmt.Println("end UploadFile:" + imgFileName) //if utils.ObjectStorageClient == "minio" { // si, e := UploadMinIoToDir(imgFileName, apiResult[0], uploadDir, fileDir) // if e != nil { // errImg = e // return // } // imgUrl = si // return //} //// 默认OSS //si, e := UploadAliyunToDir(imgFileName, apiResult[0], uploadDir, fileDir) //if e != nil { // errImg = e // return //} imgUrl = si var clearReq ReportHtml2ImgApiClearLocalReq clearReq.FileName = imgFileName e = CurlReportHtml2ImgApiClearLocal(clearReq) if e != nil { errImg = e return } }() go func() { defer func() { wg.Done() }() pdfFileName := fileName + ".pdf" ossClient := NewOssClient() if ossClient == nil { errImg = fmt.Errorf("初始化OSS服务失败") return } sp, e := ossClient.UploadFile(pdfFileName, apiResult[1], "") if e != nil { errImg = fmt.Errorf("文件上传失败, Err: %s", e.Error()) return } //if utils.ObjectStorageClient == "minio" { // sp, e := UploadMinIoToDir(pdfFileName, apiResult[1], uploadDir, fileDir) // if e != nil { // errPdf = e // return // } // pdfUrl = sp // return //} //sp, e := UploadAliyunToDir(pdfFileName, apiResult[1], uploadDir, fileDir) //if e != nil { // errPdf = e // return //} pdfUrl = sp var clearReq ReportHtml2ImgApiClearLocalReq clearReq.FileName = pdfFileName e = CurlReportHtml2ImgApiClearLocal(clearReq) if e != nil { errImg = e return } }() wg.Wait() if errImg != nil { err = fmt.Errorf("upload img err: %s", e.Error()) return } if errPdf != nil { err = fmt.Errorf("upload pdf err: %s", e.Error()) return } fmt.Println("imgUrl:" + imgUrl + ";pdfUrl:" + pdfUrl) // 更新报告链接 item.DetailImgUrl = imgUrl item.DetailPdfUrl = pdfUrl item.ModifyTime = time.Now().Local() updateCols := []string{"DetailImgUrl", "DetailPdfUrl", "ModifyTime"} if e = item.Update(updateCols); e != nil { err = fmt.Errorf("更新报告链接失败, Err: %s", e.Error()) return } return } // ReportHtml2ImgApiReq 报告生成图片接口请求体 type ReportHtml2ImgApiReq struct { ReportUrl string `json:"report_url" description:"报告详情分享地址"` FileName string `json:"file_name" description:"生成的文件名"` OutputType string `json:"output_type" description:"生成类型: img/pdf, 为空则两种均生成"` } // ReportHtml2ImgApiResp 报告生成图片接口响应体 type ReportHtml2ImgApiResp struct { Code int `json:"code" description:"状态码"` Msg string `json:"error" description:"提示信息"` Data []string `json:"data" description:"返回数据"` } // CurlReportHtml2ImgApi 请求报告生成图片接口 func CurlReportHtml2ImgApi(params ReportHtml2ImgApiReq) (apiResp []string, err error) { if utils.Report2ImgServerUrl == "" { err = fmt.Errorf("服务地址为空") return } url := fmt.Sprint(utils.Report2ImgServerUrl, PythonReportHtml2ImgApi) //fmt.Println("url: ", url) jsonData, e := json.Marshal(params) if e != nil { err = fmt.Errorf("data json marshal err: %s", e.Error()) return } //fmt.Println(string(jsonData)) //fmt.Println("http post start") resp, e := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if e != nil { err = fmt.Errorf("http post err: %s", e.Error()) return } defer resp.Body.Close() //fmt.Println("http post end") b, e := ioutil.ReadAll(resp.Body) if e != nil { err = fmt.Errorf("resp body read err: %s", e.Error()) return } if len(b) == 0 { err = fmt.Errorf("resp body is empty") return } result := new(ReportHtml2ImgApiResp) if e = json.Unmarshal(b, &result); e != nil { err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b)) return } if result.Code != 200 { err = fmt.Errorf("result: %s", string(b)) return } apiResp = result.Data return } // ReportHtml2ImgApiClearLocalReq 清除本地文件接口请求体 type ReportHtml2ImgApiClearLocalReq struct { FileName string `json:"file_name" description:"生成的文件名"` } // ReportHtml2ImgApiClearLocalResp 清除本地文件接口响应体 type ReportHtml2ImgApiClearLocalResp struct { Code int `json:"code" description:"状态码"` Msg string `json:"error" description:"提示信息"` Data string `json:"data" description:"返回数据"` } // CurlReportHtml2ImgApiClearLocal 请求清除本地文件 func CurlReportHtml2ImgApiClearLocal(params ReportHtml2ImgApiClearLocalReq) (err error) { if utils.Report2ImgServerUrl == "" { err = fmt.Errorf("服务地址为空") return } url := fmt.Sprint(utils.Report2ImgServerUrl, PythonReportHtml2ImgClearLocalApi) jsonData, e := json.Marshal(params) if e != nil { err = fmt.Errorf("data json marshal err: %s", e.Error()) return } //fmt.Println(string(jsonData)) resp, e := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if e != nil { err = fmt.Errorf("http post err: %s", e.Error()) return } defer resp.Body.Close() b, e := ioutil.ReadAll(resp.Body) if e != nil { err = fmt.Errorf("resp body read err: %s", e.Error()) return } if len(b) == 0 { err = fmt.Errorf("resp body is empty") return } result := new(ReportHtml2ImgApiClearLocalResp) if e = json.Unmarshal(b, &result); e != nil { err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b)) return } if result.Code != 200 { err = fmt.Errorf("result: %s", string(b)) return } return }