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:
@@ -41,7 +41,7 @@ func (ctl *ContactController) GetFriendList(c *gin.Context) {
|
||||
|
||||
friends, err := ctl.contactService.GetFriendList(ctx, userID, groupID)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "获取好友列表失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, friends)
|
||||
@@ -131,7 +131,7 @@ func (ctl *ContactController) GetPendingRequests(c *gin.Context) {
|
||||
|
||||
requests, err := ctl.contactService.GetPendingRequests(ctx, userID)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "获取好友申请列表失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, requests)
|
||||
@@ -154,7 +154,7 @@ func (ctl *ContactController) DeleteFriend(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.DeleteFriend(ctx, userID, friendID); err != nil {
|
||||
utils.ResponseError(c, "删除好友失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
@@ -183,7 +183,7 @@ func (ctl *ContactController) UpdateRemark(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.UpdateRemark(ctx, userID, friendID, req.Remark); err != nil {
|
||||
utils.ResponseError(c, "更新备注失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
@@ -230,7 +230,7 @@ func (ctl *ContactController) UnblockUser(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.UnblockUser(ctx, userID, targetID); err != nil {
|
||||
utils.ResponseError(c, "取消拉黑失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
@@ -248,7 +248,7 @@ func (ctl *ContactController) GetBlockList(c *gin.Context) {
|
||||
|
||||
blocked, err := ctl.contactService.GetBlockList(ctx, userID)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "获取黑名单失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, blocked)
|
||||
@@ -275,7 +275,7 @@ func (ctl *ContactController) SearchUsers(c *gin.Context) {
|
||||
|
||||
users, total, err := ctl.contactService.SearchUsers(ctx, userID, keyword, page, pageSize)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "搜索用户失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, gin.H{
|
||||
@@ -296,7 +296,7 @@ func (ctl *ContactController) GetRecommendFriends(c *gin.Context) {
|
||||
|
||||
friends, err := ctl.contactService.GetRecommendFriends(ctx, userID)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "获取好友推荐失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, friends)
|
||||
@@ -314,7 +314,7 @@ func (ctl *ContactController) GetGroups(c *gin.Context) {
|
||||
|
||||
groups, err := ctl.contactService.GetGroups(ctx, userID)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "获取分组列表失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, groups)
|
||||
@@ -338,7 +338,7 @@ func (ctl *ContactController) CreateGroup(c *gin.Context) {
|
||||
|
||||
group, err := ctl.contactService.CreateGroup(ctx, userID, req.Name)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "创建分组失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseCreated(c, group)
|
||||
@@ -367,7 +367,7 @@ func (ctl *ContactController) UpdateGroup(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.UpdateGroup(ctx, userID, groupID, req.Name, req.SortOrder); err != nil {
|
||||
utils.ResponseError(c, "修改分组失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
@@ -390,7 +390,7 @@ func (ctl *ContactController) DeleteGroup(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.DeleteGroup(ctx, userID, groupID); err != nil {
|
||||
utils.ResponseError(c, "删除分组失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
@@ -419,7 +419,7 @@ func (ctl *ContactController) MoveToGroup(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctl.contactService.MoveToGroup(ctx, userID, friendID, req.GroupID); err != nil {
|
||||
utils.ResponseError(c, "移动好友到分组失败")
|
||||
ctl.handleError(c, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
|
||||
@@ -81,9 +81,11 @@ func (d *FriendGroupDAO) DeleteGroup(ctx context.Context, groupID int64, userID
|
||||
zap.Int64("group_id", groupID), zap.Int64("user_id", userID))
|
||||
|
||||
return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
tx.Model(&model.Friendship{}).
|
||||
if err := tx.Model(&model.Friendship{}).
|
||||
Where("user_id = ? AND group_id = ?", userID, groupID).
|
||||
Update("group_id", nil)
|
||||
Update("group_id", nil).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := tx.Where("id = ? AND user_id = ?", groupID, userID).
|
||||
Delete(&model.FriendGroup{})
|
||||
|
||||
@@ -196,9 +196,11 @@ func (d *FriendshipDAO) BlockUser(ctx context.Context, userID, targetID int64) e
|
||||
zap.Int64("user_id", userID), zap.Int64("target_id", targetID))
|
||||
|
||||
return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
tx.Where("(user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)",
|
||||
if err := tx.Where("(user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)",
|
||||
userID, targetID, targetID, userID).
|
||||
Delete(&model.Friendship{})
|
||||
Delete(&model.Friendship{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block := &model.Friendship{
|
||||
UserID: userID,
|
||||
@@ -324,6 +326,23 @@ func (d *FriendshipDAO) GetFriendIDs(ctx context.Context, userID int64) ([]int64
|
||||
return ids, err
|
||||
}
|
||||
|
||||
// GetUsersByIDs 批量查询用户信息(仅返回 id/username/nickname/avatar)
|
||||
func (d *FriendshipDAO) GetUsersByIDs(ctx context.Context, userIDs []int64) ([]authModel.User, error) {
|
||||
funcName := "dao.friendship_dao.GetUsersByIDs"
|
||||
logs.Debug(ctx, funcName, "批量查询用户信息", zap.Int("count", len(userIDs)))
|
||||
|
||||
var users []authModel.User
|
||||
err := d.db.WithContext(ctx).
|
||||
Model(&authModel.User{}).
|
||||
Select("id, username, nickname, avatar").
|
||||
Where("id IN ?", userIDs).
|
||||
Find(&users).Error
|
||||
if err != nil {
|
||||
logs.Error(ctx, funcName, "批量查询用户信息失败", zap.Error(err))
|
||||
}
|
||||
return users, err
|
||||
}
|
||||
|
||||
// CountFriendsByGroup 统计每个分组的好友数
|
||||
func (d *FriendshipDAO) CountFriendsByGroup(ctx context.Context, userID int64) (map[int64]int, error) {
|
||||
type GroupCount struct {
|
||||
|
||||
@@ -4,6 +4,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/echochat/backend/app/contact/dao"
|
||||
"github.com/echochat/backend/app/dto"
|
||||
@@ -287,7 +288,7 @@ func (s *ContactService) SearchUsers(ctx context.Context, userID int64, keyword
|
||||
return result, total, nil
|
||||
}
|
||||
|
||||
// GetRecommendFriends 好友推荐(基于共同好友)
|
||||
// GetRecommendFriends 好友推荐(基于共同好友数量排序)
|
||||
func (s *ContactService) GetRecommendFriends(ctx context.Context, userID int64) ([]dto.SearchUserInfo, error) {
|
||||
funcName := "service.contact_service.GetRecommendFriends"
|
||||
logs.Debug(ctx, funcName, "好友推荐", zap.Int64("user_id", userID))
|
||||
@@ -322,31 +323,47 @@ func (s *ContactService) GetRecommendFriends(ctx context.Context, userID int64)
|
||||
id int64
|
||||
count int
|
||||
}
|
||||
var candidates []candidate
|
||||
candidates := make([]candidate, 0, len(candidateCount))
|
||||
for id, count := range candidateCount {
|
||||
candidates = append(candidates, candidate{id: id, count: count})
|
||||
}
|
||||
|
||||
sort.Slice(candidates, func(i, j int) bool {
|
||||
return candidates[i].count > candidates[j].count
|
||||
})
|
||||
|
||||
limit := 10
|
||||
if len(candidates) > limit {
|
||||
for i := 0; i < limit; i++ {
|
||||
for j := i + 1; j < len(candidates); j++ {
|
||||
if candidates[j].count > candidates[i].count {
|
||||
candidates[i], candidates[j] = candidates[j], candidates[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
candidates = candidates[:limit]
|
||||
}
|
||||
|
||||
candidateIDs := make([]int64, len(candidates))
|
||||
for i, c := range candidates {
|
||||
candidateIDs[i] = c.id
|
||||
}
|
||||
|
||||
users, err := s.friendshipDAO.GetUsersByIDs(ctx, candidateIDs)
|
||||
if err != nil {
|
||||
logs.Error(ctx, funcName, "批量查询推荐用户信息失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userMap := make(map[int64]dto.SearchUserInfo, len(users))
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = dto.SearchUserInfo{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Nickname: u.Nickname,
|
||||
Avatar: u.Avatar,
|
||||
IsFriend: false,
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]dto.SearchUserInfo, 0, len(candidates))
|
||||
for _, c := range candidates {
|
||||
users, _, err := s.friendshipDAO.SearchUsers(ctx, "", 0, 1, 1)
|
||||
_ = users
|
||||
_ = err
|
||||
result = append(result, dto.SearchUserInfo{
|
||||
ID: c.id,
|
||||
})
|
||||
if info, ok := userMap[c.id]; ok {
|
||||
result = append(result, info)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
Reference in New Issue
Block a user