refactor: replace standard log with zap logger and optimize code patterns

- Migrate all log.Printf/log.Println calls to structured zap logging
- Use cmp.Or for cleaner default value handling
- Replace manual loops with slices.ContainsFunc/IndexFunc
- Add batch query methods (IsLikedBatch, IsFavoritedBatch) to solve N+1 problem
- Update JSON tags from omitempty to omitzero for numeric fields
- Use errgroup-style wg.Go for worker goroutines
- Remove duplicate casbin/v2 dependency (keeping v3)
- Add plans/ to gitignore
This commit is contained in:
lafay
2026-03-17 00:47:17 +08:00
parent b028f7e1d3
commit 5d6c982c9c
38 changed files with 2256 additions and 290 deletions

View File

@@ -3,6 +3,7 @@ package dto
import (
"encoding/json"
"fmt"
"slices"
"carrot_bbs/internal/model"
)
@@ -232,25 +233,27 @@ func ExtractMentionedUsers(segments model.MessageSegments) []string {
// IsAtAll 检查消息是否@了所有人
func IsAtAll(segments model.MessageSegments) bool {
for _, segment := range segments {
return slices.ContainsFunc(segments, func(segment model.MessageSegment) bool {
if segment.Type == string(SegmentTypeAt) {
if userID, ok := segment.Data["user_id"].(string); ok && userID == "all" {
return true
}
}
}
return false
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
}
}
idx := slices.IndexFunc(segments, func(segment model.MessageSegment) bool {
return segment.Type == string(SegmentTypeReply)
})
if idx == -1 {
return ""
}
if id, ok := segments[idx].Data["id"].(string); ok {
return id
}
return ""
}
@@ -291,12 +294,9 @@ func BuildSegmentsFromContent(contentType, content string, mediaURL *string) mod
// HasSegmentType 检查消息链中是否包含指定类型的segment
func HasSegmentType(segments model.MessageSegments, segmentType SegmentType) bool {
for _, segment := range segments {
if segment.Type == string(segmentType) {
return true
}
}
return false
return slices.ContainsFunc(segments, func(segment model.MessageSegment) bool {
return segment.Type == string(segmentType)
})
}
// GetSegmentsByType 获取消息链中所有指定类型的segment
@@ -313,12 +313,14 @@ func GetSegmentsByType(segments model.MessageSegments, segmentType SegmentType)
// 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
}
}
idx := slices.IndexFunc(segments, func(segment model.MessageSegment) bool {
return segment.Type == string(SegmentTypeImage)
})
if idx == -1 {
return ""
}
if url, ok := segments[idx].Data["url"].(string); ok {
return url
}
return ""
}
@@ -326,13 +328,18 @@ func GetFirstImageURL(segments model.MessageSegments) string {
// GetFirstMediaURL 获取消息链中第一个媒体文件的URL图片/视频/语音/文件)
// 用于兼容旧版本API
func GetFirstMediaURL(segments model.MessageSegments) string {
for _, segment := range segments {
idx := slices.IndexFunc(segments, func(segment model.MessageSegment) bool {
switch segment.Type {
case string(SegmentTypeImage), string(SegmentTypeVideo), string(SegmentTypeVoice), string(SegmentTypeFile):
if url, ok := segment.Data["url"].(string); ok {
return url
}
return true
}
return false
})
if idx == -1 {
return ""
}
if url, ok := segments[idx].Data["url"].(string); ok {
return url
}
return ""
}