feat(P3): 私信 + #话题标签 + @提及 完成
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"feedsystem_video_go/internal/middleware/rabbitmq"
|
||||
rediscache "feedsystem_video_go/internal/middleware/redis"
|
||||
"feedsystem_video_go/internal/apierror"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -57,6 +58,7 @@ func (s *CommentService) Publish(ctx context.Context, comment *Comment) error {
|
||||
}
|
||||
}
|
||||
if mysqlEnqueued && redisEnqueued {
|
||||
s.notifyMentions(ctx, comment)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,6 +85,7 @@ func (s *CommentService) Publish(ctx context.Context, comment *Comment) error {
|
||||
if !redisEnqueued {
|
||||
UpdatePopularityCache(ctx, s.cache, comment.VideoID, 1)
|
||||
}
|
||||
s.notifyMentions(ctx, comment)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -115,3 +118,38 @@ func (s *CommentService) GetAll(ctx context.Context, videoID uint) ([]Comment, e
|
||||
}
|
||||
return s.repo.GetAllComments(ctx, videoID)
|
||||
}
|
||||
|
||||
var mentionRegex = regexp.MustCompile(`@(\w+)`)
|
||||
|
||||
func (s *CommentService) notifyMentions(ctx context.Context, comment *Comment) {
|
||||
matches := mentionRegex.FindAllStringSubmatch(comment.Content, -1)
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
seen := make(map[string]bool)
|
||||
for _, m := range matches {
|
||||
username := m[1]
|
||||
if seen[username] || username == comment.Username {
|
||||
continue
|
||||
}
|
||||
seen[username] = true
|
||||
var accID uint
|
||||
if err := s.repo.db.WithContext(ctx).Table("accounts").Where("username = ?", username).Select("id").Scan(&accID).Error; err != nil || accID == 0 {
|
||||
continue
|
||||
}
|
||||
notif := struct {
|
||||
RecipientID uint
|
||||
SenderID uint
|
||||
Type string
|
||||
TargetID uint
|
||||
Content string
|
||||
}{
|
||||
RecipientID: accID,
|
||||
SenderID: comment.AuthorID,
|
||||
Type: "mention",
|
||||
TargetID: comment.VideoID,
|
||||
Content: comment.Username + " 在评论中提到了你",
|
||||
}
|
||||
s.repo.db.WithContext(ctx).Table("notifications").Create(¬if)
|
||||
}
|
||||
}
|
||||
|
||||
30
backend/internal/video/tag_entity.go
Normal file
30
backend/internal/video/tag_entity.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package video
|
||||
|
||||
import "regexp"
|
||||
|
||||
type Tag struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"uniqueIndex;type:varchar(100);not null" json:"name"`
|
||||
}
|
||||
|
||||
type VideoTag struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
VideoID uint `gorm:"index;not null"`
|
||||
TagID uint `gorm:"index;not null"`
|
||||
}
|
||||
|
||||
var tagRegex = regexp.MustCompile(`#([\p{L}\p{N}_]+)`)
|
||||
|
||||
func ExtractTags(text string) []string {
|
||||
matches := tagRegex.FindAllStringSubmatch(text, -1)
|
||||
seen := make(map[string]bool)
|
||||
var tags []string
|
||||
for _, m := range matches {
|
||||
tag := m[1]
|
||||
if !seen[tag] {
|
||||
seen[tag] = true
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
@@ -60,8 +60,14 @@ func (vs *VideoService) Publish(ctx context.Context, video *Video) error {
|
||||
if err := tx.Create(&msg).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
tags := ExtractTags(video.Title + " " + video.Description)
|
||||
for _, tagName := range tags {
|
||||
var tag Tag
|
||||
tx.Where("name = ?", tagName).FirstOrCreate(&tag, Tag{Name: tagName})
|
||||
tx.Create(&VideoTag{VideoID: video.ID, TagID: tag.ID})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
|
||||
|
||||
Reference in New Issue
Block a user