feat(posts): add post reference/internal linking functionality
All checks were successful
Build Backend / build (push) Successful in 2m19s
Build Backend / build-docker (push) Successful in 1m18s

Add support for referencing other posts within messages through a new post_ref segment type. Includes new PostReference model to track relationships, PostRefService for processing references, and new endpoints for post suggestions, related posts, and reference click tracking.
This commit is contained in:
lafay
2026-04-26 00:37:20 +08:00
parent 898c0e6d9c
commit 23d7f1151e
11 changed files with 592 additions and 5 deletions

View File

@@ -250,8 +250,9 @@ const (
SegmentTypeAt SegmentType = "at"
SegmentTypeReply SegmentType = "reply"
SegmentTypeFace SegmentType = "face"
SegmentTypeLink SegmentType = "link"
SegmentTypeVote SegmentType = "vote"
SegmentTypeLink SegmentType = "link"
SegmentTypeVote SegmentType = "vote"
SegmentTypePostRef SegmentType = "post_ref"
)
// TextSegmentData 文本数据
@@ -325,6 +326,23 @@ type VoteOptionData struct {
Content string `json:"content"`
}
// PostRefSegmentData 帖子内链Segment数据
type PostRefSegmentData struct {
PostID string `json:"post_id"`
Title string `json:"title,omitempty"`
Author *UserResponse `json:"author,omitempty"`
Status string `json:"status,omitempty"`
Accessible bool `json:"accessible"`
ClickCount int `json:"click_count,omitzero"`
}
// PostRefBrief 帖子内链简要信息 (用于搜索联想)
type PostRefBrief struct {
ID string `json:"id"`
Title string `json:"title"`
Channel string `json:"channel,omitempty"`
}
// VoteSegmentData 投票Segment数据
type VoteSegmentData struct {
Options []VoteOptionData `json:"options"`

View File

@@ -145,6 +145,17 @@ func NewLinkSegment(url, title, description, image string) model.MessageSegment
}
}
// NewPostRefSegment 创建帖子内链Segment
func NewPostRefSegment(postID, title string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypePostRef),
Data: map[string]any{
"post_id": postID,
"title": title,
},
}
}
// ExtractVoteSegmentData 从消息链中提取投票Segment数据
func ExtractVoteSegmentData(segments model.MessageSegments) *VoteSegmentData {
for _, segment := range segments {
@@ -227,6 +238,12 @@ func ExtractTextContentFromModel(segments model.MessageSegments) string {
}
case "vote":
result += "[投票]"
case "post_ref":
if title, ok := segment.Data["title"].(string); ok && title != "" {
result += fmt.Sprintf("[帖子:%s]", title)
} else {
result += "[帖子引用]"
}
}
}
return result
@@ -256,6 +273,28 @@ func ExtractMentionedUsers(segments model.MessageSegments) []string {
return userIDs
}
// ExtractReferencedPosts 从消息链中提取所有被引用的帖子ID
func ExtractReferencedPosts(segments model.MessageSegments) []string {
var postIDs []string
seen := make(map[string]bool)
for _, segment := range segments {
if segment.Type == string(SegmentTypePostRef) {
postID, _ := segment.Data["post_id"].(string)
if postID != "" && !seen[postID] {
postIDs = append(postIDs, postID)
seen[postID] = true
}
}
}
return postIDs
}
// HasPostRefSegment 检查消息链中是否包含帖子引用
func HasPostRefSegment(segments model.MessageSegments) bool {
return HasSegmentType(segments, SegmentTypePostRef)
}
// IsAtAll 检查消息是否@了所有人
func IsAtAll(segments model.MessageSegments) bool {
return slices.ContainsFunc(segments, func(segment model.MessageSegment) bool {