From f37e2ad96627efee0c4b347b350b148dfb4cc45b Mon Sep 17 00:00:00 2001 From: Alex-larget <33240357+Alex-larget@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:54:14 +0800 Subject: [PATCH] Implement person name retrieval by token and enhance mention handling - Added a new function to retrieve a person's name from the database using their token, addressing issues with damaged mentions that display tokens instead of names. - Updated the content parsing logic to ensure that mentions include both data-id and data-label attributes, improving the display of names in the user interface. - Enhanced the handling of inner text to correctly replace tokens with real names when applicable, ensuring a more seamless user experience. --- soul-api/internal/handler/autolink.go | 55 +++++++++++++++++++++------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/soul-api/internal/handler/autolink.go b/soul-api/internal/handler/autolink.go index b856c931..268695f1 100644 --- a/soul-api/internal/handler/autolink.go +++ b/soul-api/internal/handler/autolink.go @@ -51,6 +51,18 @@ func ensurePersonByName(db *gorm.DB, name string) (token string, err error) { return created.Token, nil } +// getPersonNameByToken 按 token 查 Person 返回 name,用于修复已损坏的 mention(显示 token 而非名字) +func getPersonNameByToken(db *gorm.DB, token string) string { + if token == "" { + return "" + } + var p model.Person + if db.Select("name").Where("token = ?", token).First(&p).Error != nil { + return "" + } + return p.Name +} + // ensureLinkTagByLabel 按 label 确保 LinkTag 存在,不存在则创建 func ensureLinkTagByLabel(db *gorm.DB, label string) (tagID string, err error) { label = strings.TrimSpace(label) @@ -125,21 +137,14 @@ func ParseAutoLinkContent(content string) (string, error) { placeholders := []string{} content = mentionSpanRe.ReplaceAllStringFunc(content, func(m string) string { - // 回填 data-id:若为空则按昵称匹配 + // 回填 data-id、data-label:TipTap 用 data-label 显示名字,缺则显示 token idRe := regexp.MustCompile(`data-id="([^"]*)"`) labelRe := regexp.MustCompile(`data-label="([^"]*)"`) innerRe := regexp.MustCompile(`>([^<]+)<`) nickname := "" - if idRe.MatchString(m) { - sub := idRe.FindStringSubmatch(m) - if len(sub) >= 2 && strings.TrimSpace(sub[1]) != "" { - placeholders = append(placeholders, m) - return "\x00PLACEHOLDER\x00" - } - } if labelRe.MatchString(m) { sub := labelRe.FindStringSubmatch(m) - if len(sub) >= 2 { + if len(sub) >= 2 && strings.TrimSpace(sub[1]) != "" { nickname = sanitizeNameOrLabel(sub[1]) } } @@ -149,16 +154,42 @@ func ParseAutoLinkContent(content string) (string, error) { nickname = sanitizeNameOrLabel(strings.TrimPrefix(sub[1], "@")) } } + // 若 inner 是 token(如已损坏显示 @pZefXfWlon...),用 token 查 Person 取真实名字 + if idRe.MatchString(m) { + sub := idRe.FindStringSubmatch(m) + if len(sub) >= 2 && strings.TrimSpace(sub[1]) != "" { + tokenVal := sub[1] + innerSub := innerRe.FindStringSubmatch(m) + innerText := "" + if len(innerSub) >= 2 { + innerText = strings.TrimPrefix(innerSub[1], "@") + } + // 若 inner 看起来像 token(长串字母数字)且与 data-id 一致,用 DB 查真实名字 + if innerText == tokenVal && len(tokenVal) >= 20 { + if realName := getPersonNameByToken(db, tokenVal); realName != "" { + nickname = realName + } + } + } + } if nickname != "" { if token, ok := names[nickname]; ok && token != "" { - // 插入或替换 data-id if idRe.MatchString(m) { m = idRe.ReplaceAllString(m, `data-id="`+token+`"`) } else { - // 在 data-type 后插入 data-id m = strings.Replace(m, `data-type="mention"`, `data-type="mention" data-id="`+token+`"`, 1) } } + // 确保有 data-label,否则 TipTap 会显示 token 而非名字 + needLabel := !labelRe.MatchString(m) + if !needLabel { + if sub := labelRe.FindStringSubmatch(m); len(sub) >= 2 && strings.TrimSpace(sub[1]) == "" { + needLabel = true + } + } + if needLabel { + m = strings.Replace(m, `data-type="mention"`, `data-type="mention" data-label="`+nickname+`"`, 1) + } } placeholders = append(placeholders, m) return "\x00PLACEHOLDER\x00" @@ -180,7 +211,7 @@ func ParseAutoLinkContent(content string) (string, error) { if token == "" { return m } - return `@` + raw + `` + return `@` + raw + `` }) content = tagRe.ReplaceAllStringFunc(content, func(m string) string {