refactor: upgrade to Go 1.26 and modernize code idioms
All checks were successful
Build Backend / build (push) Successful in 3m51s
Build Backend / build-docker (push) Successful in 1m0s

- Replace `interface{}` with `any` type alias across all packages
- Use built-in `min()`/`max()` for parameter clamping
- Use `slices.SortFunc`, `slices.Min`, `slices.Max` for cleaner code
- Use `strings.Cut()` for simpler string parsing in auth middleware
- Use `errors.Is()` for proper error comparison in handlers
- Update dependencies: golang.org/x/image 0.37.0 -> 0.38.0
- Add Wire code generation guidelines to ARCHITECTURE.md
- Disable Go cache in CI build workflow
This commit is contained in:
lafay
2026-03-30 04:49:35 +08:00
parent a69b2026f4
commit b640c9a249
47 changed files with 368 additions and 352 deletions

View File

@@ -173,12 +173,7 @@ func (s *adminDashboardServiceImpl) GetStats(ctx context.Context) (*dto.Dashboar
// GetUserActivityTrend 获取用户活跃度趋势
func (s *adminDashboardServiceImpl) GetUserActivityTrend(ctx context.Context, days int) ([]dto.UserActivityTrendResponse, error) {
if days <= 0 {
days = 7
}
if days > 30 {
days = 30
}
days = min(max(days, 1), 30)
now := time.Now()
results := make([]dto.UserActivityTrendResponse, days)
@@ -282,12 +277,7 @@ func (s *adminDashboardServiceImpl) GetContentStats(ctx context.Context) (*dto.C
// GetPendingContent 获取待审核内容
func (s *adminDashboardServiceImpl) GetPendingContent(ctx context.Context, limit int) ([]dto.PendingContentResponse, error) {
if limit <= 0 {
limit = 10
}
if limit > 50 {
limit = 50
}
limit = min(max(limit, 1), 50)
results := make([]dto.PendingContentResponse, 0)

View File

@@ -205,7 +205,7 @@ func (m *AsyncLogManager) flushDataChangeLogs(logs []*model.DataChangeLog, repo
}
// handleError 处理写入错误
func (m *AsyncLogManager) handleError(logType string, logs interface{}, err error) {
func (m *AsyncLogManager) handleError(logType string, logs any, err error) {
m.logger.Error("failed to batch write logs",
zap.String("log_type", logType),
zap.Error(err),
@@ -219,7 +219,7 @@ func (m *AsyncLogManager) handleError(logType string, logs interface{}, err erro
}
// writeToFallback 写入降级文件
func (m *AsyncLogManager) writeToFallback(logType string, logs interface{}) error {
func (m *AsyncLogManager) writeToFallback(logType string, logs any) error {
return nil
}

View File

@@ -35,7 +35,7 @@ type ActiveCall struct {
MediaType string // voice 或 video
CreatedAt time.Time
StartedAt *time.Time
Duration int64 // 通话时长(秒)
Duration int64 // 通话时长(秒)
// 参与者状态
Participants map[string]*ActiveParticipant
// ICE Servers
@@ -228,7 +228,7 @@ func (s *callService) Invite(ctx context.Context, callerID, calleeID, conversati
calleeOnline := s.hub.HasClients(calleeID)
// 发送来电通知
payload := map[string]interface{}{
payload := map[string]any{
"call_id": call.ID,
"conversation_id": conversationID,
"caller_id": callerID,
@@ -286,14 +286,14 @@ func (s *callService) Accept(ctx context.Context, callID, userID string) (*Activ
s.mu.Unlock()
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_accepted", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_accepted", map[string]any{
"call_id": callID,
"started_at": now.UnixMilli(),
"ice_servers": s.config.WebRTC.ICEServers,
})
// 通知被叫方其他设备
s.hub.PublishToUserOnline(userID, "call_answered_elsewhere", map[string]interface{}{
s.hub.PublishToUserOnline(userID, "call_answered_elsewhere", map[string]any{
"call_id": callID,
"reason": "answered_on_another_device",
})
@@ -328,7 +328,7 @@ func (s *callService) Reject(ctx context.Context, callID, userID string) error {
s.saveCallHistory(call, model.CallStatusRejected, 0)
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_rejected", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_rejected", map[string]any{
"call_id": callID,
"reason": "rejected",
})
@@ -362,7 +362,7 @@ func (s *callService) Busy(ctx context.Context, callID, userID string) error {
s.saveCallHistory(call, model.CallStatusMissed, 0)
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_busy", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_busy", map[string]any{
"call_id": callID,
})
@@ -420,7 +420,7 @@ func (s *callService) End(ctx context.Context, callID, userID string, reason str
// 通知其他参与者
for pUserID := range call.Participants {
if pUserID != userID {
s.hub.PublishToUserOnline(pUserID, "call_ended", map[string]interface{}{
s.hub.PublishToUserOnline(pUserID, "call_ended", map[string]any{
"call_id": callID,
"ended_by": userID,
"reason": reason,
@@ -452,7 +452,7 @@ func (s *callService) RelaySignal(ctx context.Context, callID, fromUserID string
for pUserID := range call.Participants {
if pUserID != fromUserID {
s.hub.PublishToUserOnlineReliable(pUserID, signalType, map[string]interface{}{
s.hub.PublishToUserOnlineReliable(pUserID, signalType, map[string]any{
"call_id": callID,
"from_id": fromUserID,
"payload": json.RawMessage(payload),
@@ -481,7 +481,7 @@ func (s *callService) SetMuted(ctx context.Context, callID, userID string, muted
for pUserID := range call.Participants {
if pUserID != userID {
s.hub.PublishToUserOnline(pUserID, "call_peer_muted", map[string]interface{}{
s.hub.PublishToUserOnline(pUserID, "call_peer_muted", map[string]any{
"call_id": callID,
"user_id": userID,
"muted": muted,

View File

@@ -97,7 +97,7 @@ func NewChatService(
}
}
func (s *chatServiceImpl) publishToUsers(userIDs []string, event string, payload interface{}) {
func (s *chatServiceImpl) publishToUsers(userIDs []string, event string, payload any) {
if s.wsHub == nil || len(userIDs) == 0 {
return
}
@@ -329,7 +329,7 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
if conv.Type == model.ConversationTypeGroup {
detailType = "group"
}
s.publishToUsers(targetIDs, "chat_message", map[string]interface{}{
s.publishToUsers(targetIDs, "chat_message", map[string]any{
"detail_type": detailType,
"message": dto.ConvertMessageToResponse(message),
})
@@ -342,7 +342,7 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
s.conversationCache.InvalidateUnreadCount(p.UserID, conversationID)
}
if totalUnread, uErr := s.repo.GetAllUnreadCount(p.UserID); uErr == nil {
s.publishToUsers([]string{p.UserID}, "conversation_unread", map[string]interface{}{
s.publishToUsers([]string{p.UserID}, "conversation_unread", map[string]any{
"conversation_id": conversationID,
"total_unread": totalUnread,
})
@@ -490,7 +490,7 @@ func (s *chatServiceImpl) MarkAsRead(ctx context.Context, conversationID string,
for _, p := range participants {
targetIDs = append(targetIDs, p.UserID)
}
s.publishToUsers(targetIDs, "message_read", map[string]interface{}{
s.publishToUsers(targetIDs, "message_read", map[string]any{
"detail_type": detailType,
"conversation_id": conversationID,
"group_id": groupID,
@@ -499,7 +499,7 @@ func (s *chatServiceImpl) MarkAsRead(ctx context.Context, conversationID string,
})
}
if totalUnread, uErr := s.repo.GetAllUnreadCount(userID); uErr == nil {
s.publishToUsers([]string{userID}, "conversation_unread", map[string]interface{}{
s.publishToUsers([]string{userID}, "conversation_unread", map[string]any{
"conversation_id": conversationID,
"total_unread": totalUnread,
})
@@ -579,7 +579,7 @@ func (s *chatServiceImpl) RecallMessage(ctx context.Context, messageID string, u
for _, p := range participants {
targetIDs = append(targetIDs, p.UserID)
}
s.publishToUsers(targetIDs, "message_recall", map[string]interface{}{
s.publishToUsers(targetIDs, "message_recall", map[string]any{
"detail_type": detailType,
"conversation_id": message.ConversationID,
"group_id": groupID,
@@ -652,7 +652,7 @@ func (s *chatServiceImpl) SendTyping(ctx context.Context, senderID string, conve
continue
}
if s.wsHub != nil {
s.wsHub.PublishToUser(p.UserID, "typing", map[string]interface{}{
s.wsHub.PublishToUser(p.UserID, "typing", map[string]any{
"detail_type": detailType,
"conversation_id": conversationID,
"user_id": senderID,

View File

@@ -135,7 +135,7 @@ func (s *CommentService) reviewCommentAsync(
Images: imageURLs,
AuthorID: authorID,
ParentID: parentID,
}, map[string]interface{}{
}, map[string]any{
"result": result,
})

View File

@@ -56,7 +56,7 @@ type GroupService interface {
// 群组管理
CreateGroup(ownerID string, name string, description string, memberIDs []string) (*model.Group, error)
GetGroupByID(id string) (*model.Group, error)
UpdateGroup(userID string, groupID string, updates map[string]interface{}) error
UpdateGroup(userID string, groupID string, updates map[string]any) error
DissolveGroup(userID string, groupID string) error
TransferOwner(userID string, groupID string, newOwnerID string) error
GetUserGroups(userID string, page, pageSize int) ([]model.Group, int64, error)
@@ -300,7 +300,7 @@ func (s *groupService) GetMemberCount(groupID string) (int, error) {
}
// UpdateGroup 更新群组信息
func (s *groupService) UpdateGroup(userID string, groupID string, updates map[string]interface{}) error {
func (s *groupService) UpdateGroup(userID string, groupID string, updates map[string]any) error {
// 检查群组是否存在
group, err := s.groupRepo.GetByID(groupID)
if err != nil {
@@ -448,7 +448,7 @@ func (s *groupService) broadcastMemberJoinNotice(groupID string, targetUserID st
ConversationID: conv.ID,
SenderID: model.SystemSenderIDStr,
Segments: model.MessageSegments{
{Type: "text", Data: map[string]interface{}{"text": noticeContent}},
{Type: "text", Data: map[string]any{"text": noticeContent}},
},
Status: model.MessageStatusNormal,
Category: model.CategoryNotification,
@@ -1360,7 +1360,7 @@ func (s *groupService) MuteMember(userID string, groupID string, targetUserID st
ConversationID: conv.ID,
SenderID: model.SystemSenderIDStr,
Segments: model.MessageSegments{
{Type: "text", Data: map[string]interface{}{"text": noticeContent}},
{Type: "text", Data: map[string]any{"text": noticeContent}},
},
Status: model.MessageStatusNormal,
Category: model.CategoryNotification,

View File

@@ -1,10 +1,11 @@
package service
import (
"cmp"
"context"
"errors"
"math"
"sort"
"slices"
"sync"
"time"
@@ -103,9 +104,7 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
if topN <= 0 {
topN = 100
}
if topN > 500 {
topN = 500
}
topN = min(topN, 500)
recentHours := w.cfg.HotRank.RecentWindowHours
if recentHours <= 0 {
recentHours = 168
@@ -176,15 +175,8 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
raw[i] = base / denom
}
minR, maxR := raw[0], raw[0]
for _, v := range raw[1:] {
if v < minR {
minR = v
}
if v > maxR {
maxR = v
}
}
minR := slices.Min(raw)
maxR := slices.Max(raw)
ranked = make([]scored, len(snapshots))
for i := range snapshots {
@@ -196,11 +188,16 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
}
ranked[i] = scored{idx: i, norm: norm}
}
sort.Slice(ranked, func(a, b int) bool {
if ranked[a].norm != ranked[b].norm {
return ranked[a].norm > ranked[b].norm
slices.SortFunc(ranked, func(a, b scored) int {
if a.norm != b.norm {
return cmp.Compare(b.norm, a.norm) // 降序
}
return snapshots[ranked[a].idx].CreatedAt.After(snapshots[ranked[b].idx].CreatedAt)
if snapshots[a.idx].CreatedAt.After(snapshots[b.idx].CreatedAt) {
return -1
} else if snapshots[a.idx].CreatedAt.Before(snapshots[b.idx].CreatedAt) {
return 1
}
return 0
})
}

View File

@@ -198,7 +198,7 @@ func NewLogService(
}
// GetLogCleanupServiceFromApp 从应用中获取日志清理服务
func GetLogCleanupServiceFromApp(app interface{}) LogCleanupService {
func GetLogCleanupServiceFromApp(app any) LogCleanupService {
// 这里需要根据实际的app结构来获取
// 暂时返回nil实际使用时需要实现
return nil

View File

@@ -66,8 +66,7 @@ type PostService interface {
IncrementViews(ctx context.Context, postID, userID string) error
// RecordShare 记录分享(仅已发布帖子计数 +1返回最新 shares_count
RecordShare(ctx context.Context, postID, userID string) (sharesCount int, err error)
}
}
// postServiceImpl 帖子服务实现
type postServiceImpl struct {
@@ -103,9 +102,9 @@ func (s *postServiceImpl) Create(ctx context.Context, userID, title, content str
post := &model.Post{
UserID: userID,
ChannelID: channelID,
Title: title,
Content: content,
Status: model.PostStatusPending,
Title: title,
Content: content,
Status: model.PostStatusPending,
}
err := s.postRepo.Create(post, images)
@@ -166,7 +165,7 @@ func (s *postServiceImpl) reviewPostAsync(postID, userID, title, content string,
Content: content,
Images: images,
AuthorID: authorID,
}, map[string]interface{}{
}, map[string]any{
"result": result,
})

View File

@@ -41,7 +41,7 @@ type PushService interface {
PushToUser(ctx context.Context, userID string, message *model.Message, priority int) error
// 系统消息推送
PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) error
PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]any) error
// 系统通知推送(新接口,使用独立的 SystemNotification 模型)
PushSystemNotification(ctx context.Context, userID string, notification *model.SystemNotification) error
@@ -148,17 +148,17 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
// 从 segments 中提取文本内容
content := dto.ExtractTextContentFromModel(message.Segments)
notification := map[string]interface{}{
notification := map[string]any{
"id": fmt.Sprintf("%s", message.ID),
"type": string(message.SystemType),
"content": content,
"extra": map[string]interface{}{},
"extra": map[string]any{},
"created_at": message.CreatedAt.UnixMilli(),
}
// 填充额外数据
if message.ExtraData != nil {
extra := notification["extra"].(map[string]interface{})
extra := notification["extra"].(map[string]any)
extra["actor_id"] = message.ExtraData.ActorID
extra["actor_name"] = message.ExtraData.ActorName
extra["avatar_url"] = message.ExtraData.AvatarURL
@@ -167,7 +167,7 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
extra["action_url"] = message.ExtraData.ActionURL
extra["action_time"] = message.ExtraData.ActionTime
if message.ExtraData.ActorID > 0 {
notification["trigger_user"] = map[string]interface{}{
notification["trigger_user"] = map[string]any{
"id": fmt.Sprintf("%d", message.ExtraData.ActorID),
"username": message.ExtraData.ActorName,
"avatar": message.ExtraData.AvatarURL,
@@ -200,7 +200,7 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
SenderID: message.SenderID,
}
s.wsHub.PublishToUser(userID, "chat_message", map[string]interface{}{
s.wsHub.PublishToUser(userID, "chat_message", map[string]any{
"detail_type": detailType,
"message": event,
})
@@ -432,7 +432,7 @@ func (s *pushServiceImpl) doRetry() {
}
// PushSystemMessage 推送系统消息
func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) error {
func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]any) error {
// 首先尝试WebSocket推送
if s.pushSystemViaWebSocket(ctx, userID, msgType, title, content, data) {
return nil
@@ -444,12 +444,12 @@ func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string,
}
// pushSystemViaWebSocket 通过WebSocket推送系统消息
func (s *pushServiceImpl) pushSystemViaWebSocket(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) bool {
func (s *pushServiceImpl) pushSystemViaWebSocket(ctx context.Context, userID string, msgType, title, content string, data map[string]any) bool {
if s.wsHub == nil || !s.wsHub.HasClients(userID) {
return false
}
sysMsg := map[string]interface{}{
sysMsg := map[string]any{
"type": msgType,
"title": title,
"content": content,
@@ -477,18 +477,18 @@ func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context
return false
}
wsNotification := map[string]interface{}{
wsNotification := map[string]any{
"id": fmt.Sprintf("%d", notification.ID),
"type": string(notification.Type),
"title": notification.Title,
"content": notification.Content,
"extra": map[string]interface{}{},
"extra": map[string]any{},
"created_at": notification.CreatedAt.UnixMilli(),
}
// 填充额外数据
if notification.ExtraData != nil {
extra := wsNotification["extra"].(map[string]interface{})
extra := wsNotification["extra"].(map[string]any)
extra["actor_id_str"] = notification.ExtraData.ActorIDStr
extra["actor_name"] = notification.ExtraData.ActorName
extra["avatar_url"] = notification.ExtraData.AvatarURL
@@ -499,7 +499,7 @@ func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context
// 设置触发用户信息
if notification.ExtraData.ActorIDStr != "" {
wsNotification["trigger_user"] = map[string]interface{}{
wsNotification["trigger_user"] = map[string]any{
"id": notification.ExtraData.ActorIDStr,
"username": notification.ExtraData.ActorName,
"avatar": notification.ExtraData.AvatarURL,

View File

@@ -54,7 +54,7 @@ type QRCodeSession struct {
// QRCodeLoginService 二维码登录服务
type QRCodeLoginService struct {
redis *redis.Client
wsHub *ws.Hub
wsHub *ws.Hub
jwtService *JWTService
userService UserService
activityService UserActivityService
@@ -65,7 +65,7 @@ type QRCodeLoginService struct {
func NewQRCodeLoginService(redis *redis.Client, wsHub *ws.Hub, jwtService *JWTService, userService UserService, activityService UserActivityService, logService *LogService) *QRCodeLoginService {
return &QRCodeLoginService{
redis: redis,
wsHub: wsHub,
wsHub: wsHub,
jwtService: jwtService,
userService: userService,
activityService: activityService,
@@ -87,7 +87,7 @@ func (s *QRCodeLoginService) CreateSession(ctx context.Context) (*QRCodeSession,
}
key := qrcodeSessionPrefix + sessionID
data := map[string]interface{}{
data := map[string]any{
"status": string(session.Status),
"user_id": "",
"created_at": session.CreatedAt,
@@ -275,7 +275,7 @@ func (s *QRCodeLoginService) Cancel(ctx context.Context, sessionID, userID strin
}
// 推送取消事件
s.wsHub.PublishToUser(sessionID, "cancelled", map[string]interface{}{})
s.wsHub.PublishToUser(sessionID, "cancelled", map[string]any{})
return nil
}

View File

@@ -389,7 +389,7 @@ func (s *sensitiveServiceImpl) AddWord(ctx context.Context, word string, categor
// 同步到 Redis
if s.redis != nil && s.config.RedisKeyPrefix != "" {
key := fmt.Sprintf("%s:%s", s.config.RedisKeyPrefix, word)
data := map[string]interface{}{
data := map[string]any{
"word": word,
"category": category,
"level": level,
@@ -498,7 +498,7 @@ func (s *sensitiveServiceImpl) loadFromRedis(ctx context.Context) error {
continue
}
var wordData map[string]interface{}
var wordData map[string]any
if err := json.Unmarshal([]byte(data), &wordData); err != nil {
continue
}