package services import ( "encoding/json" "errors" "eta/eta_api/models" "eta/eta_api/models/ppt_english" "eta/eta_api/models/system" "eta/eta_api/services/alarm_msg" "eta/eta_api/services/ppt2img" "eta/eta_api/utils" "fmt" "sort" "time" ) const ( ElementsTypeText = "text" ElementsTypeImage = "image" ElementsTypeChart = "chart" ElementsTypeSheet = "sheet" ) type PPTContent struct { //Id int `json:"id" description:"此处因目录改版类型有int也有string且并没有使用到该字段所以注释掉"` Key int `json:"key"` ModelId int `json:"modelId"` Title string `json:"title"` Elements []PPTContentElements `json:"elements"` } type PPTContentElements struct { Type string `json:"type"` Position int `json:"position"` Content string `json:"content"` RichContent string `json:"richContent"` ChartId string `json:"chartId"` SheetId string `json:"sheetId"` SheetHeight string `json:"sheetHeight"` Src string `json:"src"` } // SavePPTReport 保存PPT报告 func SavePPTReport(pptId, classifyId int, title string, adminInfo *system.Admin) (reportId int, reportCode, errMsg string, err error) { defer func() { if err != nil { utils.FileLog.Info("%s", err.Error()) go alarm_msg.SendAlarmMsg("PPT转报告失败, SavePPTReport Msg: "+errMsg+", Err: "+err.Error(), 3) } }() if pptId == 0 { errMsg = "参数有误" err = errors.New("参数有误") return } item, e := models.GetPptV2ById(pptId) if e != nil { errMsg = "获取PPT失败" err = errors.New("获取PPT失败, Err: " + e.Error()) return } // PPT内容转HTML htm, e := pptContent2Html(item.Content, false) if e != nil { errMsg = "转换失败" err = e return } // 2023-02-21 PPT可多次转为报告, 不做关联 if classifyId == 0 { errMsg = "请选择报告类型" err = errors.New("请选择报告类型") return } if title == "" { errMsg = "标题不能为空" err = errors.New("标题不能为空") return } // 获取分类及父级分类 classifyList, e := models.GetAllClassify() if e != nil { errMsg = "转换失败" err = errors.New("获取分类列表失败, Err: " + e.Error()) return } classifyMap := make(map[int]*models.Classify, 0) for _, v := range classifyList { classifyMap[v.Id] = v } classifyIdFirst := 0 classifyIdSecond := 0 classifyIdThird := 0 classifyNameFirst := "" classifyNameSecond := "" classifyNameThird := "" // 最小单元分类,第二级别的分类 ,最大的分类 var baseClassify, twoClassify, threeClassify *models.Classify var hasTwo, hasThird bool baseClassify, ok := classifyMap[classifyId] if !ok { errMsg = "分类异常" err = errors.New("获取分类失败 ") return } twoClassify, hasTwo = classifyMap[baseClassify.ParentId] if hasTwo { threeClassify, hasThird = classifyMap[twoClassify.ParentId] } if hasThird { // 如果确实是有三级分类 classifyIdFirst = threeClassify.Id classifyNameFirst = threeClassify.ClassifyName classifyIdSecond = twoClassify.Id classifyNameSecond = twoClassify.ClassifyName classifyIdThird = baseClassify.Id classifyNameThird = baseClassify.ClassifyName } else if hasTwo { classifyIdFirst = twoClassify.Id classifyNameFirst = twoClassify.ClassifyName classifyIdSecond = baseClassify.Id classifyNameSecond = baseClassify.ClassifyName } else { classifyIdFirst = baseClassify.Id classifyNameFirst = baseClassify.ClassifyName } // 新增报告 nowTime := time.Now().Local() reportReq := &models.AddReq{ AddType: 1, ClassifyIdFirst: classifyIdFirst, ClassifyNameFirst: classifyNameFirst, ClassifyIdSecond: classifyIdSecond, ClassifyNameSecond: classifyNameSecond, ClassifyIdThird: classifyIdThird, ClassifyNameThird: classifyNameThird, Title: title, Abstract: "", Author: "FICC团队", Frequency: utils.ReportFrequencyDefault, State: 1, Content: htm, CreateTime: nowTime.Format(utils.FormatDateTime), ReportVersion: 2, CollaborateType: 1, // 协作方式,1:个人,2:多人协作。默认:1 ReportLayout: 1, // 报告布局,1:常规布局,2:智能布局。默认:1 IsPublicPublish: 2, // 是否公开发布,1:是,2:否 } // 如果PPT是公开的,则报告也公开发布 if item.IsShare == 1 { reportReq.IsPublicPublish = 1 } newReportId, newCode, _, e := CreateNewReport(*reportReq, adminInfo) if e != nil { errMsg = "转换失败" err = errors.New("新增报告失败, Err: " + e.Error()) return } reportId = int(newReportId) reportCode = newCode // 更新报告中的ppt图片 go saveReportPptImg(pptId, reportId, item.PptxUrl) return } // PPT2内容转HTML func pptContent2Html(content string, isEnglish bool) (htm string, err error) { contents := make([]PPTContent, 0) if e := json.Unmarshal([]byte(content), &contents); e != nil { err = errors.New("PPT内容转换失败") return } pageLen := len(contents) htmlContent := `` // iframe图表/表格域名 // 获取基础配置, 若未配置则直接返回 // 获取配置好的短信模版 smsCond := ` AND conf_key = ? ` smsPars := make([]interface{}, 0) smsPars = append(smsPars, "ChartViewUrl") conf := new(models.BusinessConf) conf, e := conf.GetItemByCondition(smsCond, smsPars) if e != nil { if e.Error() == utils.ErrNoRow() { err = fmt.Errorf("请先配置公共图库的地址") return } err = fmt.Errorf("获取聚合短信配置信息失败, Err: %s", e.Error()) return } if conf.ConfVal == "" { err = fmt.Errorf("请先配置公共图库的地址") return } chartRoot := conf.ConfVal if pageLen > 0 { htmlPrefix := `

` htmlSuffix := `

` htmlBr := `
` for i := 0; i < pageLen; i++ { // 每页标题加粗居中 title := contents[i].Title if title != "" { htmlContent += `

` htmlContent += title htmlContent += `

` } ele := contents[i].Elements // 每页元素按照Position升序排序 sort.Slice(ele, func(k, j int) bool { return ele[k].Position < ele[j].Position }) for _, v := range ele { // 根据不同的Type拼接不同的内容 htmlContent += htmlPrefix switch v.Type { case ElementsTypeText: htmlContent += v.RichContent case ElementsTypeImage: htmlContent += fmt.Sprint(``) case ElementsTypeChart: if isEnglish { // 英文研报图表src多加一个fromPage=en, 表格暂时没有区分 htmlContent += fmt.Sprintf(``, chartRoot, v.ChartId) break } htmlContent += fmt.Sprintf(``, chartRoot, v.ChartId) case ElementsTypeSheet: htmlContent += fmt.Sprintf(``, chartRoot, v.SheetId, v.SheetId, v.SheetHeight) } htmlContent += htmlSuffix } // 每页中间插入一个换行符, 最后一页不插入 currentPage := i + 1 if currentPage != pageLen { htmlContent += htmlPrefix + htmlBr + htmlSuffix } } } htm = htmlContent return } // ResetPPTReport 重置PPT关联的报告(如删除关联的报告后) func ResetPPTReport(reportId int, isEnglish bool) (err error) { defer func() { if err != nil { utils.FileLog.Info("%s", err.Error()) go alarm_msg.SendAlarmMsg("重置PPT关联报告失败, ResetPPTReport Err: "+err.Error(), 3) } }() // 英文报告 if isEnglish { en, e := ppt_english.GetPptEnglishByReportId(reportId) if e != nil && e.Error() != utils.ErrNoRow() { err = errors.New("获取英文PPT失败, Err: " + e.Error()) return } if en != nil { updateCols := []string{"ReportId", "ReportCode"} en.ReportId = 0 en.ReportCode = "" if e = en.Update(updateCols); e != nil { err = errors.New("更新英文PPT关联报告失败, Err: " + e.Error()) return } } return } // 中文报告 item, e := models.GetPptV2ByReportId(reportId) if e != nil && e.Error() != utils.ErrNoRow() { err = errors.New("获取PPT失败, Err: " + e.Error()) return } if item != nil { updateCols := []string{"ReportId", "ReportCode"} item.ReportId = 0 item.ReportCode = "" if e = item.Update(updateCols); e != nil { err = errors.New("更新PPT关联报告失败, Err: " + e.Error()) return } } return } // saveReportPptImg ppt转报告后,需要再次将ppt转图片 func saveReportPptImg(pptId, reportId int, pptUrl string) { var err error defer func() { if err != nil { utils.FileLog.Info(fmt.Sprintf("将ppt转图片失败, saveReportPptImg Err:%s", err.Error())) go alarm_msg.SendAlarmMsg("将ppt转图片失败, saveReportPptImg Err: "+err.Error(), 3) } }() // 更新报告内容 report, e := models.GetReportByReportId(reportId) if e != nil && e.Error() != utils.ErrNoRow() { err = errors.New("获取报告失败, Err: " + e.Error()) return } if report == nil { return } // 获取ppt转图片的结果 list, err := ppt2img.Ppt2Img(pptUrl) if err != nil { return } reportPptImgList := make([]*models.ReportPptImg, 0) for _, v := range list { reportPptImg := &models.ReportPptImg{ //ReportPptImgId: 0, PptId: pptId, ReportId: reportId, ReportChapterId: 0, ImgUrl: v, CreateTime: time.Now(), } reportPptImgList = append(reportPptImgList, reportPptImg) } //批量添加Ppt转报告的图片记录 err = models.AddAndEditMultiReportPptImg(pptId, reportPptImgList) return } // SaveEnglishPPTReport 保存英文PPT报告 func SaveEnglishPPTReport(pptId, classifyIdFirst, classifyIdSecond int, title, abstract string, adminInfo *system.Admin) (reportId int, reportCode, errMsg string, err error) { defer func() { if err != nil { utils.FileLog.Info("%s", err.Error()) go alarm_msg.SendAlarmMsg("PPT转报告失败, SavePPTReport Msg: "+errMsg+", Err: "+err.Error(), 3) } }() if pptId == 0 { errMsg = "参数有误" err = errors.New("参数有误") return } item, e := ppt_english.GetPptEnglishById(pptId) if e != nil { errMsg = "获取PPT失败" err = errors.New("获取PPT失败, Err: " + e.Error()) return } // PPT内容转HTML htm, e := pptContent2Html(item.Content, true) if e != nil { errMsg = "转换失败" err = e return } // 2023-02-21 PPT可多次转为报告, 不做关联 if title == "" { errMsg = "标题不能为空" err = errors.New("标题不能为空") return } if classifyIdFirst <= 0 { errMsg = "请选择报告分类" err = errors.New("报告分类不能为空") return } // 分类 classifyList, e := models.GetAllEnglishClassify() if e != nil { errMsg = "转换失败" err = errors.New("获取分类列表失败, Err: " + e.Error()) return } classifyMap := make(map[int]string, 0) for _, v := range classifyList { classifyMap[v.Id] = v.ClassifyName } classifyNameFirst := classifyMap[classifyIdFirst] classifyNameSecond := classifyMap[classifyIdSecond] // 新增报告 nowTime := time.Now().Local() reportReq := &models.AddEnglishReportReq{ AddType: 1, ClassifyIdFirst: classifyIdFirst, ClassifyNameFirst: classifyNameFirst, ClassifyIdSecond: classifyIdSecond, ClassifyNameSecond: classifyNameSecond, Title: title, Abstract: abstract, Author: "Horizon Insights FICC Team", Frequency: utils.ReportFrequencyDefault, State: 1, Content: htm, CreateTime: nowTime.Format(utils.FormatDateTime), } newReportId, newCode, e := CreateNewEnglishReport(*reportReq, adminInfo) if e != nil { errMsg = "转换失败" err = errors.New("新增报告失败, Err: " + e.Error()) return } reportId = int(newReportId) reportCode = newCode return } // UpdatePptEditing 更新PPT编辑状态 func UpdatePptEditing(pptId, status, userId int, userName string, isEn bool) (ret ppt_english.PPTEditingCache, err error) { if pptId <= 0 { return } cacheKey := "" if isEn { cacheKey = fmt.Sprint(utils.CACHE_EN_PPT_EDITING, pptId) } else { cacheKey = fmt.Sprint(utils.CACHE_PPT_EDITING, pptId) } // 完成编辑 if status == 2 { _ = utils.Rc.Delete(cacheKey) return } // 读取缓存中的结果 var editor ppt_english.PPTEditingCache strCache, _ := utils.Rc.RedisString(cacheKey) fmt.Println(strCache) if strCache != "" { e := json.Unmarshal([]byte(strCache), &editor) if e != nil { err = fmt.Errorf("解析缓存内容失败: %s", e.Error()) return } } // 标记编辑中 if status == 1 { // 无人编辑, 写入缓存 if !editor.IsEditing { ret.IsEditing = true ret.AdminId = userId ret.Editor = userName ret.Tips = fmt.Sprintf("当前%s正在编辑PPT", userName) b, _ := json.Marshal(ret) utils.Rc.SetNX(cacheKey, string(b), 3*time.Minute) return } // 有人编辑 if editor.IsEditing { // 编辑用户与当前用户不一致, 返回编辑用户, 一致则更新缓存 if userId == editor.AdminId { b, _ := json.Marshal(editor) utils.Rc.Do("SETEX", cacheKey, int64(180), string(b)) } ret = editor return } } else { // 默认查询 ret = editor } return }