fix(phase2a): 代码审查修复 - 8项关键/重要问题

安全修复:
- WebSocket Token 增加 Redis 有效性校验(已登出用户无法建立 WS)

功能修复:
- GetRecommendFriends 改为批量查询,正确返回用户名/昵称/头像
- 上下线通知:OnlineService 通过接口注入获取好友列表推送状态变更
- 管理端在线用户 API 补充用户名信息

代码质量:
- 所有 json.Marshal/Redis 错误增加检查与日志
- ContactController 13 个 endpoint 统一走 handleError 业务错误映射
- 管理端 Controller 补全包注释、函数注释和结构化日志
- 前端 5 个联系人页面 avatar 工具函数抽取到 utils/avatar.js

Made-with: Cursor
This commit is contained in:
bujinyuan
2026-03-02 18:48:28 +08:00
parent 8ec0261427
commit 2c59500e27
26 changed files with 632 additions and 153 deletions

View File

@@ -1,41 +1,56 @@
// Package controller 提供 admin 模块的 HTTP 接口处理
package controller
import (
"strconv"
"github.com/echochat/backend/app/admin/service"
"github.com/echochat/backend/pkg/logs"
"github.com/echochat/backend/pkg/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ContactManageController 联系人关系管理控制器(管理端)
type ContactManageController struct {
contactManageService *service.ContactManageService
}
// NewContactManageController 创建 ContactManageController 实例
func NewContactManageController(contactManageService *service.ContactManageService) *ContactManageController {
return &ContactManageController{contactManageService: contactManageService}
}
// GetAllContacts GET /api/v1/admin/contacts
// GetAllContacts 获取所有好友关系列表(分页)
// GET /api/v1/admin/contacts
func (ctl *ContactManageController) GetAllContacts(c *gin.Context) {
funcName := "admin.contact_manage_controller.GetAllContacts"
ctx := c.Request.Context()
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
contacts, total, err := ctl.contactManageService.GetAllFriendships(ctx, page, pageSize)
if err != nil {
logs.Error(ctx, funcName, "获取好友关系列表失败", zap.Error(err))
utils.ResponseError(c, "获取好友关系列表失败")
return
}
logs.Info(ctx, funcName, "获取好友关系列表成功",
zap.Int64("total", total), zap.Int("page", page))
utils.ResponseOK(c, gin.H{
"list": contacts,
"total": total,
})
}
// DeleteContact DELETE /api/v1/admin/contacts/:id
// DeleteContact 删除好友关系
// DELETE /api/v1/admin/contacts/:id
func (ctl *ContactManageController) DeleteContact(c *gin.Context) {
funcName := "admin.contact_manage_controller.DeleteContact"
ctx := c.Request.Context()
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
utils.ResponseBadRequest(c, "ID 格式错误")
@@ -43,8 +58,12 @@ func (ctl *ContactManageController) DeleteContact(c *gin.Context) {
}
if err := ctl.contactManageService.DeleteFriendship(ctx, id); err != nil {
logs.Error(ctx, funcName, "删除好友关系失败",
zap.Int64("friendship_id", id), zap.Error(err))
utils.ResponseError(c, "删除好友关系失败")
return
}
logs.Info(ctx, funcName, "删除好友关系成功", zap.Int64("friendship_id", id))
utils.ResponseOK(c, nil)
}

View File

@@ -1,37 +1,54 @@
// Package controller 提供 admin 模块的 HTTP 接口处理
package controller
import (
"github.com/echochat/backend/app/admin/service"
"github.com/echochat/backend/pkg/logs"
"github.com/echochat/backend/pkg/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// OnlineController 在线用户管理控制器(管理端)
type OnlineController struct {
onlineManageService *service.OnlineManageService
}
// NewOnlineController 创建 OnlineController 实例
func NewOnlineController(onlineManageService *service.OnlineManageService) *OnlineController {
return &OnlineController{onlineManageService: onlineManageService}
}
// GetOnlineUsers GET /api/v1/admin/online/users
// GetOnlineUsers 获取在线用户列表
// GET /api/v1/admin/online/users
func (ctl *OnlineController) GetOnlineUsers(c *gin.Context) {
funcName := "admin.online_controller.GetOnlineUsers"
ctx := c.Request.Context()
userIDs, err := ctl.onlineManageService.GetOnlineUsers(ctx)
users, err := ctl.onlineManageService.GetOnlineUsers(ctx)
if err != nil {
logs.Error(ctx, funcName, "获取在线用户列表失败", zap.Error(err))
utils.ResponseError(c, "获取在线用户列表失败")
return
}
utils.ResponseOK(c, userIDs)
logs.Info(ctx, funcName, "获取在线用户列表成功", zap.Int("count", len(users)))
utils.ResponseOK(c, users)
}
// GetOnlineCount GET /api/v1/admin/online/count
// GetOnlineCount 获取在线用户总数
// GET /api/v1/admin/online/count
func (ctl *OnlineController) GetOnlineCount(c *gin.Context) {
funcName := "admin.online_controller.GetOnlineCount"
ctx := c.Request.Context()
count, err := ctl.onlineManageService.GetOnlineCount(ctx)
if err != nil {
logs.Error(ctx, funcName, "获取在线用户数失败", zap.Error(err))
utils.ResponseError(c, "获取在线用户数失败")
return
}
logs.Info(ctx, funcName, "获取在线用户数成功", zap.Int64("count", count))
utils.ResponseOK(c, gin.H{"count": count})
}

View File

@@ -33,7 +33,10 @@ func (s *ContactManageService) GetAllFriendships(ctx context.Context, page, page
logs.Debug(ctx, funcName, "管理端查询好友关系")
var total int64
s.db.WithContext(ctx).Model(&model.Friendship{}).Count(&total)
if err := s.db.WithContext(ctx).Model(&model.Friendship{}).Count(&total).Error; err != nil {
logs.Error(ctx, funcName, "统计好友关系总数失败", zap.Error(err))
return nil, 0, err
}
var results []AdminFriendship
offset := (page - 1) * pageSize
@@ -60,8 +63,11 @@ func (s *ContactManageService) DeleteFriendship(ctx context.Context, friendshipI
}
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
tx.Where("user_id = ? AND friend_id = ?", f.UserID, f.FriendID).Delete(&model.Friendship{})
tx.Where("user_id = ? AND friend_id = ?", f.FriendID, f.UserID).Delete(&model.Friendship{})
return nil
if err := tx.Where("user_id = ? AND friend_id = ?", f.UserID, f.FriendID).
Delete(&model.Friendship{}).Error; err != nil {
return err
}
return tx.Where("user_id = ? AND friend_id = ?", f.FriendID, f.UserID).
Delete(&model.Friendship{}).Error
})
}

View File

@@ -3,24 +3,72 @@ package service
import (
"context"
authModel "github.com/echochat/backend/app/auth/model"
wsApp "github.com/echochat/backend/app/ws"
"github.com/echochat/backend/pkg/logs"
"go.uber.org/zap"
"gorm.io/gorm"
)
// OnlineUserInfo 在线用户信息(包含用户名,用于管理端展示)
type OnlineUserInfo struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
}
// OnlineManageService 管理端在线状态查询服务
type OnlineManageService struct {
onlineService *wsApp.OnlineService
db *gorm.DB
}
func NewOnlineManageService(onlineService *wsApp.OnlineService) *OnlineManageService {
return &OnlineManageService{onlineService: onlineService}
// NewOnlineManageService 创建 OnlineManageService 实例
func NewOnlineManageService(onlineService *wsApp.OnlineService, db *gorm.DB) *OnlineManageService {
return &OnlineManageService{
onlineService: onlineService,
db: db,
}
}
func (s *OnlineManageService) GetOnlineUsers(ctx context.Context) ([]int64, error) {
// GetOnlineUsers 获取在线用户列表(含用户名)
func (s *OnlineManageService) GetOnlineUsers(ctx context.Context) ([]OnlineUserInfo, error) {
funcName := "service.online_manage_service.GetOnlineUsers"
logs.Debug(ctx, funcName, "获取在线用户列表")
return s.onlineService.GetOnlineUserIDs(ctx)
ids, err := s.onlineService.GetOnlineUserIDs(ctx)
if err != nil {
return nil, err
}
if len(ids) == 0 {
return []OnlineUserInfo{}, nil
}
var users []authModel.User
if err := s.db.WithContext(ctx).
Model(&authModel.User{}).
Select("id, username").
Where("id IN ?", ids).
Find(&users).Error; err != nil {
logs.Error(ctx, funcName, "查询在线用户信息失败", zap.Error(err))
return nil, err
}
userMap := make(map[int64]string, len(users))
for _, u := range users {
userMap[u.ID] = u.Username
}
result := make([]OnlineUserInfo, 0, len(ids))
for _, id := range ids {
result = append(result, OnlineUserInfo{
UserID: id,
Username: userMap[id],
})
}
return result, nil
}
// GetOnlineCount 获取在线用户总数
func (s *OnlineManageService) GetOnlineCount(ctx context.Context) (int64, error) {
return s.onlineService.GetOnlineCount(ctx)
}