package services import ( "encoding/json" "errors" "eta_gn/eta_api/models" "eta_gn/eta_api/models/company" "eta_gn/eta_api/models/report" "eta_gn/eta_api/models/system" "eta_gn/eta_api/services/alarm_msg" "eta_gn/eta_api/services/public_api" "eta_gn/eta_api/utils" "fmt" "github.com/PuerkitoBio/goquery" "html" "os" "regexp" "strconv" "strings" "time" ) func GetReportContentSub(content string) (contentSub string, err error) { content = html.UnescapeString(content) doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { fmt.Println("create doc err:", err.Error()) return } n := 0 doc.Find("p").Each(func(i int, s *goquery.Selection) { if n >= 5 { return } n++ phtml, err := s.Html() if err != nil { fmt.Println("get html err", err.Error()) return } if s.Text() != "" || strings.Contains(phtml, "src") { contentSub = contentSub + "

" + phtml + "

" } }) return } func UpdateChaptersVideo(ids []int) (err error) { defer func() { if err != nil { utils.FileLog.Error("UpdateChaptersVideo, chapterIds:%v, Err:%s", ids, err.Error()) go alarm_msg.SendAlarmMsg(fmt.Sprintf("更新章节音频失败, 章节ID: %v; Err: "+err.Error(), ids), 3) } }() if len(ids) <= 0 { return } chapterList, err := models.GetChapterListByChapterIds(ids) if err != nil { return } nowTime := time.Now() updateCols := make([]string, 0) updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds") for i := 0; i < len(chapterList); i++ { item := chapterList[i] if item.VideoUrl != "" && item.VideoName != "" && item.VideoSize != "" && item.VideoPlaySeconds != "" { continue } videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, html.UnescapeString(item.Content), nowTime.Format(utils.FormatDateTime)) if e != nil { err = e return } item.VideoUrl = videoUrl item.VideoName = videoName item.VideoSize = videoSize item.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds) if e = item.UpdateChapter(updateCols); e != nil { err = e } } return } func UpdateReportEs(reportId int, publishState int) (err error) { if reportId <= 0 { return } reportInfo, err := models.GetReportByReportId(reportId) if err != nil { return } categories := "" if reportInfo.HasChapter == 1 { chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id) if tmpErr != nil { return } if len(chapterList) > 0 { for _, chapterInfo := range chapterList { err = updateReportChapterEsByChapter(chapterInfo) if err != nil { return } } } } else { minClassifyId, _, tmpErr := getMinClassify(reportInfo) if tmpErr != nil { return } permissionList, tmpErr := models.GetChartPermissionNameFromMappingByKeyword("rddp", minClassifyId) if tmpErr != nil { return } categoryArr := make([]string, 0) for i := 0; i < len(permissionList); i++ { categoryArr = append(categoryArr, permissionList[i].PermissionName) } aliasArr, _ := addCategoryAliasToArr(categoryArr) categories = strings.Join(aliasArr, ",") } minClassifyId, minClassifyName, err := getMinClassify(reportInfo) if err != nil { return } esReport := &models.ElasticReportDetail{ ReportId: reportInfo.Id, ReportChapterId: 0, Title: reportInfo.Title, Abstract: reportInfo.Abstract, BodyContent: utils.TrimHtml(html.UnescapeString(reportInfo.Content)), PublishTime: reportInfo.PublishTime.Format(utils.FormatDateTime), PublishState: publishState, Author: reportInfo.Author, ClassifyIdFirst: reportInfo.ClassifyIdFirst, ClassifyNameFirst: reportInfo.ClassifyNameFirst, ClassifyIdSecond: reportInfo.ClassifyIdSecond, ClassifyNameSecond: reportInfo.ClassifyNameSecond, ClassifyId: minClassifyId, ClassifyName: minClassifyName, Categories: categories, StageStr: strconv.Itoa(reportInfo.Stage), } docId := fmt.Sprintf("%d-%d", reportInfo.Id, 0) if err = EsAddOrEditReport(utils.EsReportIndexName, docId, esReport); err != nil { return } return } func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error) { aliasArr = categoryArr if len(categoryArr) > 0 { for i := 0; i < len(categoryArr); i++ { if strings.Contains(categoryArr[i], "沥青") { aliasArr = append(aliasArr, "BU") } if strings.Contains(categoryArr[i], "MEG") { aliasArr = append(aliasArr, "EG", "乙二醇") } if strings.Contains(categoryArr[i], "聚酯") { aliasArr = append(aliasArr, "长丝", "短纤", "瓶片") } if strings.Contains(categoryArr[i], "纯苯+苯乙烯") { aliasArr = append(aliasArr, "EB") } if strings.Contains(categoryArr[i], "聚乙烯") { aliasArr = append(aliasArr, "PP", "PE") } if strings.Contains(categoryArr[i], "玻璃纯碱") { aliasArr = append(aliasArr, "玻璃", "纯碱", "FG", "SA") } if strings.Contains(categoryArr[i], "甲醇") { aliasArr = append(aliasArr, "甲醇", "MA") } if strings.Contains(categoryArr[i], "橡胶") { aliasArr = append(aliasArr, "橡胶", "RU") } } } return } func UpdateReportChapterEs(reportChapterId int) (err error) { if reportChapterId <= 0 { return } chapterInfo, err := models.GetReportChapterInfoById(reportChapterId) if err != nil { return } err = updateReportChapterEsByChapter(chapterInfo) if err != nil { return } return } func updateReportChapterEsByChapter(chapterInfo *models.ReportChapter) (err error) { obj := report.ReportChapterPermissionMapping{} permissionList, tmpErr := obj.GetPermissionItemListById(chapterInfo.ReportChapterId) if tmpErr != nil { return } categoryArr := make([]string, 0) if len(permissionList) > 0 { for ii := 0; ii < len(permissionList); ii++ { categoryArr = append(categoryArr, permissionList[ii].ChartPermissionName) } } aliasArr, _ := addCategoryAliasToArr(categoryArr) categories := strings.Join(aliasArr, ",") esChapter := &models.ElasticReportDetail{ ReportId: chapterInfo.ReportId, ReportChapterId: chapterInfo.ReportChapterId, Title: chapterInfo.Title, Abstract: chapterInfo.Abstract, BodyContent: utils.TrimHtml(html.UnescapeString(chapterInfo.Content)), PublishTime: chapterInfo.PublishTime.Format(utils.FormatDateTime), PublishState: chapterInfo.PublishState, Author: chapterInfo.Author, ClassifyIdFirst: chapterInfo.ClassifyIdFirst, ClassifyNameFirst: chapterInfo.ClassifyNameFirst, ClassifyIdSecond: 0, ClassifyNameSecond: "", ClassifyId: chapterInfo.ClassifyIdFirst, ClassifyName: chapterInfo.ClassifyNameFirst, Categories: categories, StageStr: strconv.Itoa(chapterInfo.Stage), } chapterDocId := fmt.Sprintf("%d-%d", chapterInfo.ReportId, chapterInfo.ReportChapterId) if err = EsAddOrEditReport(utils.EsReportIndexName, chapterDocId, esChapter); err != nil { return } return } func replaceReportBase64ToImg(content string) (newContent string, err error) { if content == "" { return } 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}==)?" re, _ := regexp.Compile(pattern) matcher := re.FindAllString(content, 999) if len(matcher) > 0 { for _, v := range matcher { imgUrl, tmpErr := reportBase64ToImg(v) if tmpErr != nil { err = tmpErr return } content = strings.ReplaceAll(content, v, imgUrl) } } newContent = content return } func reportBase64ToImg(imageBase64 string) (resourceUrl string, err error) { if imageBase64 == "" { err = errors.New("图片为空") return } ext := ".png" uploadDir := "./static" randStr := utils.GetRandStringNoSpecialChar(28) fileName := randStr + ext fpath := uploadDir + "/" + fileName b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imageBase64) if !b { err = errors.New("图片格式不正确") return } re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`) base64Str := re.ReplaceAllString(imageBase64, "") base64Str = strings.Replace(base64Str, " ", "", -1) err = utils.SaveBase64ToFile(base64Str, fpath) if err != nil { err = errors.New("图片保存失败" + err.Error()) return } defer os.Remove(fpath) hzUploadDir := utils.RESOURCE_DIR + "images/" savePath := hzUploadDir + time.Now().Format("200601/20060102/") savePath += fileName ossClient := NewOssClient() if ossClient == nil { err = fmt.Errorf("初始化OSS服务失败") return } resourceUrl, err = ossClient.UploadFile(fileName, fpath, savePath) if err != nil { err = fmt.Errorf("文件上传失败, Err: %s", err.Error()) return } item := new(models.Resource) item.ResourceUrl = resourceUrl item.ResourceType = 1 item.CreateTime = time.Now() _, err = models.AddResource(item) if err != nil { err = errors.New("资源上传失败" + err.Error()) return } return } func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) { if page == "" { err = errors.New("page不能为空") return } sceneMD5 := "a=1" if scene != "" { sceneMD5 = utils.MD5(scene) } picByte, err := GetSunCode(page, sceneMD5) if err != nil { return } localPath := "./static/imgs" fileName := utils.GetRandStringNoSpecialChar(28) + ".png" fpath := fmt.Sprint(localPath, "/", fileName) f, err := os.Create(fpath) if err != nil { fmt.Println("11111") return } if _, err = f.Write(picByte); err != nil { return } defer func() { f.Close() os.Remove(fpath) }() fileDir := "yb/suncode/" savePath := fileDir + time.Now().Format("200601/20060102/") + fileName ossClient := NewOssClient() if ossClient == nil { err = fmt.Errorf("初始化OSS服务失败") return } imgUrl, err = ossClient.UploadFile(fileName, fpath, savePath) if err != nil { err = fmt.Errorf("文件上传失败, Err: %s", err.Error()) return } if err != nil { return } if scene != "" { newSuncode := &models.YbPcSuncode{ Scene: scene, SceneMd5: sceneMD5, CodePage: page, SuncodeUrl: imgUrl, CreateTime: time.Now(), } err = models.AddYbPcSunCode(newSuncode) } if scene != "" { newPars := &models.YbSuncodePars{ Scene: scene, SceneKey: sceneMD5, CreateTime: time.Now(), } err = models.AddYbSuncodePars(newPars) } return } func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId int64, reportCode, errMsg string, err error) { contentSub := "" if req.Content != "" { e := utils.ContentXssCheck(req.Content) if e != nil { errMsg = "存在非法标签" err = errors.New("存在非法标签, Err: " + e.Error()) return } contentClean, e := FilterReportContentBr(req.Content) if e != nil { errMsg = "内容去除前后空格失败" err = errors.New("内容去除前后空格失败, Err: " + e.Error()) return } req.Content = contentClean sub, e := GetReportContentSub(req.Content) if e != nil { go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+e.Error(), 3) } contentSub = sub } maxStage, e := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird) if e != nil { errMsg = "期数获取失败!" err = errors.New("期数获取失败,Err:" + e.Error()) return } item := new(models.Report) item.AddType = req.AddType item.ClassifyIdFirst = req.ClassifyIdFirst item.ClassifyNameFirst = req.ClassifyNameFirst item.ClassifyIdSecond = req.ClassifyIdSecond item.ClassifyNameSecond = req.ClassifyNameSecond item.Title = req.Title item.Abstract = req.Abstract item.Author = req.Author item.Frequency = req.Frequency item.State = req.State item.Content = html.EscapeString(req.Content) item.Stage = maxStage + 1 item.ContentSub = html.EscapeString(contentSub) if req.CreateTime != "" { createTime, tmpErr := time.ParseInLocation(utils.FormatDateTime, req.CreateTime, time.Local) if tmpErr != nil { err = tmpErr return } item.CreateTime = createTime } item.ModifyTime = time.Now() item.ReportVersion = req.ReportVersion item.AdminId = adminInfo.AdminId item.AdminRealName = adminInfo.RealName item.ClassifyIdThird = req.ClassifyIdThird item.ClassifyNameThird = req.ClassifyNameThird if req.CollaborateType == 2 { item.HasChapter = 1 item.ChapterType = "" } item.LastModifyAdminId = adminInfo.AdminId item.LastModifyAdminName = adminInfo.RealName item.ContentModifyTime = time.Now() item.NeedSplice = 1 item.ContentStruct = html.EscapeString(req.ContentStruct) item.HeadImg = req.HeadImg item.EndImg = req.EndImg item.CanvasColor = req.CanvasColor item.HeadResourceId = req.HeadResourceId item.EndResourceId = req.EndResourceId item.CollaborateType = req.CollaborateType item.ReportLayout = req.ReportLayout item.IsPublicPublish = req.IsPublicPublish item.ReportCreateTime = time.Now() err, errMsg = AddReportAndChapter(item, 0, req.GrantAdminIdList) return } func FilterReportContentBr(content string) (res string, err error) { newContent := content content = html.UnescapeString(content) if content == "" { return } content = strings.Replace(content, "

Powered by Froala Editor

", "", -1) defer func() { if err != nil { go alarm_msg.SendAlarmMsg("过滤报告正文前后换行符及空格失败, ErrMsg: "+err.Error(), 3) } }() configKey := "report_filter_br" conf, e := company.GetConfigDetailByCode(configKey) if e != nil { err = errors.New("获取报告过滤配置失败, Err: " + e.Error()) return } if conf.ConfigValue != "1" { return content, nil } re := regexp.MustCompile(`(?is:)`) arr := re.FindAllString(content, -1) indexArr := re.FindAllIndex([]byte(content), -1) emptyRe := `]*>(
|
)+

` startIsBr := false countEmptyBr := 0 // 需要连续替换的空

总数 lastBrRange := 0 // 最后一个空

右侧index, 用来判断是否为连续的空

for i := range arr { byteRange := indexArr[i] if len(byteRange) == 2 { if i == 0 && byteRange[0] == 0 { startIsBr = true } if !startIsBr { break } if lastBrRange != 0 { if lastBrRange != byteRange[0] { break } } m, e := regexp.Match(emptyRe, []byte(arr[i])) if e != nil { err = e return } if m { countEmptyBr += 1 lastBrRange = byteRange[1] continue } break } } if countEmptyBr > 0 { reg, e := regexp.Compile(emptyRe) if e != nil { err = errors.New("正则解析失败, Err: " + e.Error()) return } counted := 0 // 已替换数 res = reg.ReplaceAllStringFunc(content, func(s string) string { counted += 1 if counted <= countEmptyBr { return "" } else { return s } }) if res == "" { res = newContent } } else { res = content if res == "" { res = newContent } } return } func GetEnglishReportOverview(content string) (res string, err error) { content = html.UnescapeString(content) doc, e := goquery.NewDocumentFromReader(strings.NewReader(content)) if e != nil { err = errors.New("Create Doc Err: " + e.Error()) return } target := "overview" label := "" start := -1 end := -1 doc.Find("p").Each(func(i int, s *goquery.Selection) { h, e := s.Html() if e != nil { err = errors.New("Get Html1 Err: " + e.Error()) return } h = strings.ToLower(h) t := s.Text() t = strings.ToLower(t) if strings.Contains(h, label) && t != "" && strings.Contains(t, target) { start = i } if start != -1 && end == -1 && i > start && strings.Contains(h, label) { end = i } }) if start != -1 && end != -1 { doc.Find("p").Each(func(i int, s *goquery.Selection) { if i > start && i < end { h, e := s.Html() if e != nil { err = errors.New("Get Html2 Err: " + e.Error()) return } if strings.Contains(h, "iframe") { return } res += `

` + h + `

` } }) } return } func GetReportContentSubWithoutIframe(content string) (contentSub string, err error) { content = html.UnescapeString(content) doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { fmt.Println("create doc err:", err.Error()) return } label := "iframe" n := 0 doc.Find("p").Each(func(i int, s *goquery.Selection) { if n >= 5 { return } n++ h, err := s.Html() if err != nil { fmt.Println("get html err", err.Error()) return } if strings.Contains(h, label) { return } if s.Text() != "" || strings.Contains(h, "src") { contentSub = contentSub + "

" + h + "

" } }) return } func UpdateReportEditMark(reportId, reportChapterId, nowUserId, status int, nowUserName, lang string) (ret models.MarkReportResp, err error) { key := fmt.Sprint(`crm:report:edit:`, reportId) if reportChapterId > 0 { key = fmt.Sprint(key, ":", reportChapterId) } ret.Status = 0 ret.Msg = "无人编辑" opUserId, e := utils.Rc.RedisInt(key) var opUser models.MarkReportItem var classifyNameFirst string if e != nil { opUserInfoStr, tErr := utils.Rc.RedisString(key) if tErr == nil { tErr = json.Unmarshal([]byte(opUserInfoStr), &opUser) if tErr == nil { opUserId = opUser.AdminId } } } var reportInfo *models.ReportDetail classifyNameFirst = opUser.ReportClassifyNameFirst if reportId > 0 && status != 2 && classifyNameFirst == "" { reportInfo, err = models.GetReportById(reportId) if err != nil { err = fmt.Errorf("报告不存在") return } classifyNameFirst = reportInfo.ClassifyNameFirst } if opUserId > 0 && opUserId != nowUserId { editor := opUser.Editor if editor == "" { otherInfo, e := system.GetSysAdminById(opUserId) if e != nil { err = fmt.Errorf("查询其他编辑者信息失败") return } editor = otherInfo.RealName } ret.Status = 1 if lang == utils.EnLangVersion { ret.Msg = fmt.Sprintf("%s is currently editing the report", editor) } else { ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor) } ret.Editor = editor return } if status == 1 { nowUser := &models.MarkReportItem{AdminId: nowUserId, Editor: nowUserName, ReportClassifyNameFirst: classifyNameFirst} bt, e := json.Marshal(nowUser) if e != nil { err = fmt.Errorf("格式化编辑者信息失败") return } if opUserId > 0 { utils.Rc.Do("SETEX", key, int64(60), string(bt)) //3分钟缓存 } else { utils.Rc.SetNX(key, string(bt), time.Second*60*1) //3分钟缓存 } } else if status == 3 { _ = utils.Rc.Delete(key) } return } func HandleVideoDecibel(chapterInfo *models.ReportChapter) { public_api.HandleVideoDecibel(chapterInfo.ReportChapterId) return } func SaveReportLogs(item *models.Report, chapters []*models.ReportChapter, adminId int, adminRealName string) { if item == nil && len(chapters) == 0 { return } var err error defer func() { if err != nil { tips := fmt.Sprintf("报告日志记录, SaveReportLogs error: %s", err.Error()) go alarm_msg.SendAlarmMsg(tips, 2) } }() if item != nil { e := models.AddReportSaveLog(item.Id, item.AdminId, item.Content, item.ContentSub, item.ContentStruct, item.CanvasColor, item.AdminRealName, item.HeadResourceId, item.EndResourceId) if e != nil { err = fmt.Errorf("AddReportSaveLog: %s", e.Error()) return } } if len(chapters) > 0 { e := models.MultiAddReportChaptersSaveLog(chapters, adminId, adminRealName) if e != nil { err = fmt.Errorf("MultiAddReportChaptersSaveLog: %s", e.Error()) return } } return }