diff --git a/internal/model/comment.go b/internal/model/comment.go
index 6beeebb..082bb47 100644
--- a/internal/model/comment.go
+++ b/internal/model/comment.go
@@ -25,6 +25,9 @@ type Comment struct {
ReplyToName string `gorm:"-:migration;<-:false" json:"reply_to_name,omitempty"`
// RepliesCount 回复数(非DB字段,查询填充)
RepliesCount int `gorm:"-:migration;<-:false" json:"replies_count,omitempty"`
+ // Mentions 有效@提及映射 username -> uid(非DB字段,批量查询填充)
+ // 前端据此判断 @username 是否为有效提及并生成链接
+ Mentions map[string]uint `gorm:"-" json:"mentions,omitempty"`
}
// CommentMention @提及映射(绑定UID,不是username,支持改名后自动更新显示名)
@@ -39,6 +42,7 @@ type UserSearchResult struct {
UID uint `json:"uid"`
Username string `json:"username"`
Level int `json:"level"`
+ Avatar string `json:"avatar"`
}
// AdminCommentRow 后台评论列表行
diff --git a/internal/repository/comment_mention_repo.go b/internal/repository/comment_mention_repo.go
index f8ee0ab..d7f63b5 100644
--- a/internal/repository/comment_mention_repo.go
+++ b/internal/repository/comment_mention_repo.go
@@ -17,15 +17,62 @@ func (r *CommentRepo) FindMentionsByComment(commentID uint) ([]model.CommentMent
return list, err
}
-// SearchUsersByLevel 搜索指定等级及以上用户(用于@艾特),返回 uid/username/exp
+// LoadMentionsForComments 批量加载多条评论的有效@提及映射(username -> uid)
+// 传入的 comments 会被原地修改,填充 Mentions 字段
+func (r *CommentRepo) LoadMentionsForComments(comments []model.Comment) error {
+ if len(comments) == 0 {
+ return nil
+ }
+
+ commentIDs := make([]uint, len(comments))
+ for i, c := range comments {
+ commentIDs[i] = c.ID
+ }
+
+ // 查询 comment_mentions JOIN users,拿到 username 和 uid
+ type mentionRow struct {
+ CommentID uint
+ UID uint
+ Username string
+ }
+ var rows []mentionRow
+ err := r.db.Table("comment_mentions").
+ Select("comment_mentions.comment_id, comment_mentions.uid, users.username").
+ Joins("LEFT JOIN users ON users.id = comment_mentions.uid").
+ Where("comment_mentions.comment_id IN ?", commentIDs).
+ Find(&rows).Error
+ if err != nil {
+ return err
+ }
+
+ // 按 comment_id 分组
+ mentionMap := make(map[uint]map[string]uint)
+ for _, row := range rows {
+ if mentionMap[row.CommentID] == nil {
+ mentionMap[row.CommentID] = make(map[string]uint)
+ }
+ mentionMap[row.CommentID][row.Username] = row.UID
+ }
+
+ for i := range comments {
+ if m, ok := mentionMap[comments[i].ID]; ok {
+ comments[i].Mentions = m
+ }
+ }
+ return nil
+}
+
+// SearchUsersByLevel 搜索指定等级及以上用户(用于@艾特),返回 uid/username/avatar/exp
func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int) ([]struct {
UID uint
Username string
+ Avatar string
Exp int
}, error) {
type result struct {
UID uint
Username string
+ Avatar string
Exp int
}
var list []result
@@ -33,7 +80,7 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
threshold := model.LevelThresholds[minLevel]
err := r.db.Table("users").
- Select("id, username, exp").
+ Select("id, username, avatar, exp").
Where("username LIKE ? AND exp >= ? AND deleted_at IS NULL", "%"+keyword+"%", threshold).
Order("exp DESC").
Limit(limit).
@@ -42,14 +89,16 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
var res []struct {
UID uint
Username string
+ Avatar string
Exp int
}
for _, u := range list {
res = append(res, struct {
UID uint
Username string
+ Avatar string
Exp int
- }{UID: u.UID, Username: u.Username, Exp: u.Exp})
+ }{UID: u.UID, Username: u.Username, Avatar: u.Avatar, Exp: u.Exp})
}
return res, err
}
diff --git a/internal/service/comment_service.go b/internal/service/comment_service.go
index 7006571..387b073 100644
--- a/internal/service/comment_service.go
+++ b/internal/service/comment_service.go
@@ -152,7 +152,15 @@ func (s *CommentService) Delete(commentID uint, requesterID uint, isAdmin bool)
func (s *CommentService) ListRootComments(postID uint, page, pageSize int) ([]model.Comment, int64, error) {
p := common.Pagination{Page: page, PageSize: pageSize}
p.DefaultPagination()
- return s.repo.FindRootComments(postID, p.Offset(), p.PageSize)
+ comments, total, err := s.repo.FindRootComments(postID, p.Offset(), p.PageSize)
+ if err != nil {
+ return nil, 0, err
+ }
+ // 批量加载有效@提及映射,前端据此决定是否为链接
+ if len(comments) > 0 {
+ _ = s.repo.LoadMentionsForComments(comments)
+ }
+ return comments, total, nil
}
// ListReplies 获取某条顶级评论的全部回复
@@ -164,6 +172,10 @@ func (s *CommentService) ListReplies(rootID uint) ([]model.Comment, error) {
if list == nil {
list = []model.Comment{}
}
+ // 批量加载有效@提及映射
+ if len(list) > 0 {
+ _ = s.repo.LoadMentionsForComments(list)
+ }
return list, nil
}
@@ -178,9 +190,9 @@ func (s *CommentService) ListAllComments(keyword string, showDeleted bool, page,
return rows, total, err
}
-// SearchUsers 搜索LV3+用户(用于@艾特)
+// SearchUsers 搜索LV2+用户(用于@艾特悬浮窗)
func (s *CommentService) SearchUsers(keyword string) ([]model.UserSearchResult, error) {
- rows, err := s.repo.SearchUsersByLevel(keyword, 3, 10)
+ rows, err := s.repo.SearchUsersByLevel(keyword, 2, 10)
if err != nil {
return nil, fmt.Errorf("搜索用户失败: %w", err)
}
@@ -191,6 +203,7 @@ func (s *CommentService) SearchUsers(keyword string) ([]model.UserSearchResult,
UID: row.UID,
Username: row.Username,
Level: model.GetLevelByExp(row.Exp),
+ Avatar: row.Avatar,
}
}
return users, nil
@@ -206,7 +219,7 @@ func (s *CommentService) parseMentions(commentID uint, body string) {
continue
}
seen[username] = true
- rows, _ := s.repo.SearchUsersByLevel(username, 0, 1)
+ rows, _ := s.repo.SearchUsersByLevel(username, 2, 1)
if len(rows) > 0 {
_ = s.repo.CreateMention(&model.CommentMention{
CommentID: commentID,
diff --git a/internal/service/repository.go b/internal/service/repository.go
index 90e98c8..f0314da 100644
--- a/internal/service/repository.go
+++ b/internal/service/repository.go
@@ -158,9 +158,11 @@ type commentStore interface {
SearchUsersByLevel(keyword string, minLevel, limit int) ([]struct {
UID uint
Username string
+ Avatar string
Exp int
}, error)
GetUsernameByID(userID uint) (string, error)
+ LoadMentionsForComments(comments []model.Comment) error
ListAllComments(keyword string, showDeleted bool, offset, limit int) ([]model.AdminCommentRow, int64, error)
}
diff --git a/templates/MetaLab-2026/html/home/index.html b/templates/MetaLab-2026/html/home/index.html
index 5ff5b69..65c113f 100644
--- a/templates/MetaLab-2026/html/home/index.html
+++ b/templates/MetaLab-2026/html/home/index.html
@@ -65,9 +65,8 @@
{{if .IsLoggedIn}}
{{if .Avatar}}
-
欢迎加入 MetaLab
diff --git a/templates/MetaLab-2026/html/layout/nav.html b/templates/MetaLab-2026/html/layout/nav.html index eed73db..b48dabe 100644 --- a/templates/MetaLab-2026/html/layout/nav.html +++ b/templates/MetaLab-2026/html/layout/nav.html @@ -21,9 +21,8 @@ {{if .Avatar}}