package service import ( "context" "html" "regexp" "strconv" "strings" "unicode" "unicode/utf16" "unicode/utf8" "go-common/app/interface/openplatform/article/dao" "go-common/app/interface/openplatform/article/model" "go-common/library/ecode" "go-common/library/log" strip "github.com/grokify/html-strip-tags-go" ) var ( _zeroWidthReg = regexp.MustCompile(`[\x{200b}]+`) _nocharReg = []*regexp.Regexp{ // regexp.MustCompile(`[\p{Hangul}]+`), // kr regexp.MustCompile(`[\p{Tibetan}]+`), // tibe regexp.MustCompile(`[\p{Arabic}]+`), // arabic } _chineseReg = regexp.MustCompile(`[\p{Han}]+`) // chinese ) func (s *Service) allowRepeat(c context.Context, mid int64, title string) (ok bool) { log.Info("allowRepeat check start | mid(%d) title(%s).", mid, title) exist, _ := s.dao.SubmitCache(c, mid, title) log.Info("allowRepeat from cache | mid(%d) title(%s) exist(%d).", mid, title, exist) if !exist { log.Info("allowRepeat not exist | mid(%d) title(%s)", mid, title) s.dao.AddSubmitCache(c, mid, title) log.Info("allowRepeat add cache | mid(%d) title(%s).", mid, title) ok = true return } dao.PromInfo("creation:禁止重复标题") return } func (s *Service) preMust(c context.Context, art *model.Article) (err error) { var ok bool if art.Title, ok = s.checkTitle(art.Title); !ok || art.Title == "" { log.Error("s.checkTitle mid(%d) art.Title(%s) title contains illegal char or is empty", art.Author.Mid, art.Title) err = ecode.CreativeArticleTitleErr return } if art.Content, ok = s.checkContent(art.Content); !ok { log.Error("s.checkContent mid(%d) content too long", art.Author.Mid) err = ecode.CreativeArticleContentErr return } if !s.allowCategory(art.Category.ID) { log.Error("s.allowCategory mid(%d) art.Category(%d) not exists", art.Author.Mid, art.Category) err = ecode.CreativeArticleCategoryErr return } if !s.allowReprints(int8(art.Reprint)) { log.Error("s.allowReprints mid(%d) art.Reprint(%d) illegal reprint", art.Author.Mid, art.Reprint) err = ecode.CreativeArticleReprintErr return } if !s.allowTID(int8(art.TemplateID)) { log.Error("s.allowTID mid(%d) art.TemplateID(%d) illegal reprint", art.Author.Mid, art.TemplateID) err = ecode.CreativeArticleTIDErr return } if !model.ValidTemplate(art.TemplateID, art.ImageURLs) { err = ecode.ArtCreationTplErr return } if !s.allowTag(art.Tags) { log.Error("s.allowTag mid(%d) art.Tags(%s) tag name or number too large", art.Author.Mid, art.Tags) err = ecode.CreativeArticleTagErr } if art.Dynamic, ok = s.allowDynamicIntro(art.Dynamic); !ok { log.Error("s.checkDynamicIntro mid(%d) art.DynamicIntro(%s) title contains illegal char", art.Author.Mid, art.Dynamic) err = ecode.CreativeDynamicIntroErr return } return } func (s *Service) checkTitle(title string) (ct string, ok bool) { title = strings.TrimSpace(title) allCount := utf8.RuneCountInString(title) enCount := utf8.RuneCountInString(_chineseReg.ReplaceAllString(title, "")) chineseCount := allCount - enCount if chineseCount*2+enCount > 80 { return } for _, reg := range _nocharReg { if reg.MatchString(title) { return } } ct = _zeroWidthReg.ReplaceAllString(title, "") if utf8.RuneCountInString(ct) <= 0 { return } ok = true return } func (s *Service) contentStripSize(content string) (count int) { stripped := strip.StripTags(content) stripped = html.UnescapeString(stripped) stripped = strings.Map(func(r rune) rune { if unicode.IsSpace(r) { return -1 } return r }, stripped) stripped = strings.Replace(stripped, "\u200B", "", -1) stripped = strings.Replace(stripped, "\u00a0", "", -1) // utf16 size for _, r := range stripped { count += len(utf16.Encode([]rune{rune(r)})) } // 图片计算为一个字 offset := strings.Count(content, " s.c.Article.MaxContentSize { return } size := s.contentStripSize(ct) if size < s.c.Article.MinContentLength || size > s.c.Article.MaxContentLength { return } ok = true return } func (s *Service) preArticleCheck(c context.Context, art *model.Article) (err error) { if !s.allowRepeat(c, art.Author.Mid, art.Title) { err = ecode.CreativeArticleCanNotRepeat return } if err = s.preMust(c, art); err != nil { return } return } func (s *Service) allowCategory(cid int64) (ok bool) { _, ok = s.categoriesMap[cid] return } func (s *Service) allowReprints(cp int8) (ok bool) { ok = model.InReprints(cp) return } func (s *Service) allowTID(tid int8) (ok bool) { ok = model.InTemplateID(tid) return } func (s *Service) allowTag(tags []*model.Tag) (ok bool) { if (len(tags) > 12) || (len(tags) == 0) { return } for _, tag := range tags { if _zeroWidthReg.MatchString(tag.Name) { return } if (utf8.RuneCountInString(tag.Name) > 20) || (tag.Name == "") { return } } return true } //allowDynamicIntro 移动端动态推荐语,选填,不能超过233字. func (s *Service) allowDynamicIntro(dynamicIntro string) (ct string, ok bool) { ct = strings.TrimSpace(dynamicIntro) if utf8.RuneCountInString(ct) > 233 { return } ok = true return } func (s *Service) preDraftCheck(c context.Context, art *model.Draft) (err error) { if art.Title == "" { art.Title = "无标题" } var ok bool if art.Title, ok = s.checkTitle(art.Title); !ok { log.Error("s.checkTitle mid(%d) art.Title(%s) title contains illegal char or is empty", art.Author.Mid, art.Title) err = ecode.CreativeArticleTitleErr } return } // ParseParam parse article param which type is int. func (s *Service) ParseParam(c context.Context, categoryStr, reprintStr, tidStr, imageURLs, originImageURLs string) (art *model.ArtParam, err error) { var ( category int64 tid, reprint int ) category, err = strconv.ParseInt(categoryStr, 10, 64) if err != nil || category <= 0 { //文章要求必须传大于0的分类 err = ecode.CreativeArticleCategoryErr return } tid, err = strconv.Atoi(tidStr) if err != nil || tid < 0 { err = ecode.CreativeArticleTIDErr return } reprint, err = strconv.Atoi(reprintStr) if err != nil || reprint < 0 { err = ecode.CreativeArticleReprintErr return } imgs, oimgs, err := ParseImageURLs(imageURLs, originImageURLs) if err != nil { return } art = &model.ArtParam{ Category: category, TemplateID: int32(tid), Reprint: int32(reprint), ImageURLs: imgs, OriginImageURLs: oimgs, } return } // ParseDraftParam parse draft param which type is int. func (s *Service) ParseDraftParam(c context.Context, categoryStr, reprintStr, tidStr, imageURLs, originImageURLs string) (art *model.ArtParam, err error) { var ( category int64 tid, reprint int ) if categoryStr != "" { category, err = strconv.ParseInt(categoryStr, 10, 64) if err != nil || category < 0 { err = ecode.CreativeArticleCategoryErr return } } if tidStr != "" { tid, err = strconv.Atoi(tidStr) if err != nil || tid < 0 { err = ecode.CreativeArticleTIDErr return } } if reprintStr != "" { reprint, err = strconv.Atoi(reprintStr) if err != nil || reprint < 0 { err = ecode.CreativeArticleReprintErr return } } imgs, oimgs, err := ParseImageURLs(imageURLs, originImageURLs) if err != nil { return } art = &model.ArtParam{ Category: category, TemplateID: int32(tid), Reprint: int32(reprint), ImageURLs: imgs, OriginImageURLs: oimgs, } return } //ParseImageURLs parse img urls to []string. func ParseImageURLs(imageURLs, originImageURLs string) (imgs, oimgs []string, err error) { if originImageURLs == "" { originImageURLs = imageURLs } imgs = strings.Split(imageURLs, ",") oimgs = strings.Split(originImageURLs, ",") if len(imgs) != len(oimgs) { err = ecode.CreativeArticleImageURLsErr } return }