Files
backend/internal/dto/segment.go

363 lines
9.5 KiB
Go
Raw Permalink Normal View History

package dto
import (
"encoding/json"
"fmt"
"carrot_bbs/internal/model"
)
// ParseSegmentData 解析Segment数据到目标结构体
func ParseSegmentData(segment model.MessageSegment, target interface{}) error {
dataBytes, err := json.Marshal(segment.Data)
if err != nil {
return err
}
return json.Unmarshal(dataBytes, target)
}
// NewTextSegment 创建文本Segment
func NewTextSegment(content string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeText),
Data: map[string]interface{}{"text": content},
}
}
// NewImageSegment 创建图片Segment
func NewImageSegment(url string, width, height int, thumbnailURL string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeImage),
Data: map[string]interface{}{
"url": url,
"width": width,
"height": height,
"thumbnail_url": thumbnailURL,
},
}
}
// NewImageSegmentWithSize 创建带文件大小的图片Segment
func NewImageSegmentWithSize(url string, width, height int, thumbnailURL string, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeImage),
Data: map[string]interface{}{
"url": url,
"width": width,
"height": height,
"thumbnail_url": thumbnailURL,
"file_size": fileSize,
},
}
}
// NewVoiceSegment 创建语音Segment
func NewVoiceSegment(url string, duration int, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeVoice),
Data: map[string]interface{}{
"url": url,
"duration": duration,
"file_size": fileSize,
},
}
}
// NewVideoSegment 创建视频Segment
func NewVideoSegment(url string, width, height, duration int, thumbnailURL string, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeVideo),
Data: map[string]interface{}{
"url": url,
"width": width,
"height": height,
"duration": duration,
"thumbnail_url": thumbnailURL,
"file_size": fileSize,
},
}
}
// NewFileSegment 创建文件Segment
func NewFileSegment(url, name string, size int64, mimeType string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeFile),
Data: map[string]interface{}{
"url": url,
"name": name,
"size": size,
"mime_type": mimeType,
},
}
}
// NewAtSegment 创建@Segment只存储 user_id昵称由前端根据群成员列表实时解析
func NewAtSegment(userID string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeAt),
Data: map[string]interface{}{
"user_id": userID,
},
}
}
// NewAtAllSegment 创建@所有人Segment
func NewAtAllSegment() model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeAt),
Data: map[string]interface{}{
"user_id": "all",
},
}
}
// NewReplySegment 创建回复Segment
func NewReplySegment(messageID string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeReply),
Data: map[string]interface{}{"id": messageID},
}
}
// NewFaceSegment 创建表情Segment
func NewFaceSegment(id int, name, url string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeFace),
Data: map[string]interface{}{
"id": id,
"name": name,
"url": url,
},
}
}
// NewLinkSegment 创建链接Segment
func NewLinkSegment(url, title, description, image string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeLink),
Data: map[string]interface{}{
"url": url,
"title": title,
"description": description,
"image": image,
},
}
}
// ExtractTextContentFromJSON 从JSON格式的segments中提取纯文本内容
// 用于从数据库读取的 []byte 格式的 segments
// 已废弃:现在数据库直接存储 model.MessageSegments 类型
func ExtractTextContentFromJSON(segmentsJSON []byte) string {
if len(segmentsJSON) == 0 {
return ""
}
var segments model.MessageSegments
if err := json.Unmarshal(segmentsJSON, &segments); err != nil {
return ""
}
return ExtractTextContentFromModel(segments)
}
// ExtractTextContentFromModel 从 model.MessageSegments 中提取纯文本内容
func ExtractTextContentFromModel(segments model.MessageSegments) string {
var result string
for _, segment := range segments {
switch segment.Type {
case "text":
if text, ok := segment.Data["text"].(string); ok {
result += text
}
case "at":
userID, _ := segment.Data["user_id"].(string)
if userID == "all" {
result += "@所有人 "
} else if userID != "" {
// 昵称由前端实时解析,后端文本提取仅用于推送通知兜底
result += "@某人 "
}
case "image":
result += "[图片]"
case "voice":
result += "[语音]"
case "video":
result += "[视频]"
case "file":
if name, ok := segment.Data["name"].(string); ok && name != "" {
result += fmt.Sprintf("[文件:%s]", name)
} else {
result += "[文件]"
}
case "face":
if name, ok := segment.Data["name"].(string); ok && name != "" {
result += fmt.Sprintf("[%s]", name)
} else {
result += "[表情]"
}
case "link":
if title, ok := segment.Data["title"].(string); ok && title != "" {
result += fmt.Sprintf("[链接:%s]", title)
} else {
result += "[链接]"
}
}
}
return result
}
// ExtractTextContent 从消息链中提取纯文本内容
// 用于搜索、通知展示等场景
func ExtractTextContent(segments model.MessageSegments) string {
return ExtractTextContentFromModel(segments)
}
// ExtractMentionedUsers 从消息链中提取被@的用户ID列表
// 不包括 "all"@所有人)
func ExtractMentionedUsers(segments model.MessageSegments) []string {
var userIDs []string
seen := make(map[string]bool)
for _, segment := range segments {
if segment.Type == string(SegmentTypeAt) {
userID, _ := segment.Data["user_id"].(string)
if userID != "all" && userID != "" && !seen[userID] {
userIDs = append(userIDs, userID)
seen[userID] = true
}
}
}
return userIDs
}
// IsAtAll 检查消息是否@了所有人
func IsAtAll(segments model.MessageSegments) bool {
for _, segment := range segments {
if segment.Type == string(SegmentTypeAt) {
if userID, ok := segment.Data["user_id"].(string); ok && userID == "all" {
return true
}
}
}
return false
}
// GetReplyMessageID 从消息链中获取被回复的消息ID
// 如果没有回复segment返回空字符串
func GetReplyMessageID(segments model.MessageSegments) string {
for _, segment := range segments {
if segment.Type == string(SegmentTypeReply) {
if id, ok := segment.Data["id"].(string); ok {
return id
}
}
}
return ""
}
// BuildSegmentsFromContent 从旧版content构建segments
// 用于兼容旧版本消息
func BuildSegmentsFromContent(contentType, content string, mediaURL *string) model.MessageSegments {
var segments model.MessageSegments
switch contentType {
case "text":
segments = append(segments, NewTextSegment(content))
case "image":
if mediaURL != nil {
segments = append(segments, NewImageSegment(*mediaURL, 0, 0, ""))
}
case "voice":
if mediaURL != nil {
segments = append(segments, NewVoiceSegment(*mediaURL, 0, 0))
}
case "video":
if mediaURL != nil {
segments = append(segments, NewVideoSegment(*mediaURL, 0, 0, 0, "", 0))
}
case "file":
if mediaURL != nil {
segments = append(segments, NewFileSegment(*mediaURL, content, 0, ""))
}
default:
// 默认当作文本处理
if content != "" {
segments = append(segments, NewTextSegment(content))
}
}
return segments
}
// HasSegmentType 检查消息链中是否包含指定类型的segment
func HasSegmentType(segments model.MessageSegments, segmentType SegmentType) bool {
for _, segment := range segments {
if segment.Type == string(segmentType) {
return true
}
}
return false
}
// GetSegmentsByType 获取消息链中所有指定类型的segment
func GetSegmentsByType(segments model.MessageSegments, segmentType SegmentType) []model.MessageSegment {
var result []model.MessageSegment
for _, segment := range segments {
if segment.Type == string(segmentType) {
result = append(result, segment)
}
}
return result
}
// GetFirstImageURL 获取消息链中第一张图片的URL
// 如果没有图片,返回空字符串
func GetFirstImageURL(segments model.MessageSegments) string {
for _, segment := range segments {
if segment.Type == string(SegmentTypeImage) {
if url, ok := segment.Data["url"].(string); ok {
return url
}
}
}
return ""
}
// GetFirstMediaURL 获取消息链中第一个媒体文件的URL图片/视频/语音/文件)
// 用于兼容旧版本API
func GetFirstMediaURL(segments model.MessageSegments) string {
for _, segment := range segments {
switch segment.Type {
case string(SegmentTypeImage), string(SegmentTypeVideo), string(SegmentTypeVoice), string(SegmentTypeFile):
if url, ok := segment.Data["url"].(string); ok {
return url
}
}
}
return ""
}
// DetermineContentType 从消息链推断消息类型(用于兼容旧版本)
func DetermineContentType(segments model.MessageSegments) string {
if len(segments) == 0 {
return "text"
}
// 优先检查媒体类型
for _, segment := range segments {
switch segment.Type {
case string(SegmentTypeImage):
return "image"
case string(SegmentTypeVideo):
return "video"
case string(SegmentTypeVoice):
return "voice"
case string(SegmentTypeFile):
return "file"
}
}
// 默认返回text
return "text"
}