263 lines
6.5 KiB
Go
263 lines
6.5 KiB
Go
/**
|
||
* package api
|
||
* 作用:用户管理相关API处理器
|
||
*/
|
||
package api
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"math/rand"
|
||
"strconv"
|
||
"time"
|
||
"xk-websocket-v2/internal/model"
|
||
"xk-websocket-v2/internal/service"
|
||
"xk-websocket-v2/internal/utils"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
/**
|
||
* GetMyInfoHandler
|
||
* 功能:获取当前用户信息
|
||
* 路径:GET /api/user/my-info
|
||
* 需要:JWT认证
|
||
*/
|
||
func GetMyInfoHandler(c *gin.Context) {
|
||
// 从Context获取用户ID(由JWT中间件注入)
|
||
userID, exists := c.Get("user_id")
|
||
if !exists {
|
||
utils.Unauthorized(c, "未认证")
|
||
return
|
||
}
|
||
|
||
user, err := service.UserSvc.GetUserByID(userID.(string))
|
||
if err != nil {
|
||
utils.NotFound(c, "用户不存在")
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithData(c, user, "获取成功")
|
||
}
|
||
|
||
/**
|
||
* GetUserByIDHandler
|
||
* 功能:根据用户 ID 获取公开资料
|
||
* 路径:GET /api/user/:id
|
||
*/
|
||
func GetUserByIDHandler(c *gin.Context) {
|
||
targetID := c.Param("id")
|
||
if targetID == "" {
|
||
utils.BadRequest(c, "用户ID不能为空")
|
||
return
|
||
}
|
||
|
||
user, err := service.UserSvc.GetUserByID(targetID)
|
||
if err != nil {
|
||
utils.NotFound(c, "用户不存在")
|
||
return
|
||
}
|
||
user.Password = ""
|
||
utils.SuccessWithData(c, user, "获取成功")
|
||
}
|
||
|
||
/**
|
||
* GetUserListHandler
|
||
* 功能:获取用户列表
|
||
* 路径:GET /api/user/list
|
||
*/
|
||
func GetUserListHandler(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
users, total, err := service.UserSvc.GetUserList(page, pageSize)
|
||
if err != nil {
|
||
utils.InternalError(c, "查询失败")
|
||
return
|
||
}
|
||
|
||
// 确保返回空数组而不是null
|
||
if users == nil {
|
||
users = []model.User{}
|
||
}
|
||
|
||
utils.SuccessWithData(c, gin.H{
|
||
"data": users,
|
||
"total": total,
|
||
"page": page,
|
||
"size": pageSize,
|
||
}, "获取成功")
|
||
}
|
||
|
||
/**
|
||
* CreateUserHandler
|
||
* 功能:创建用户(管理员)
|
||
* 路径:POST /api/user/create
|
||
*/
|
||
func CreateUserHandler(c *gin.Context) {
|
||
// 该接口本质是管理员功能,但当前系统没有角色体系,任何登录用户都能创建任意账号(普通注册应走 /api/register)。
|
||
// 在缺少管理员鉴权的前提下,直接禁用以关闭越权创建用户的风险;待引入角色系统后再按 admin 放开。
|
||
utils.Forbidden(c, "无权限:创建用户需要管理员权限")
|
||
}
|
||
|
||
/**
|
||
* UpdateUserHandler
|
||
* 功能:更新用户信息
|
||
* 路径:POST /api/user/update
|
||
*/
|
||
func UpdateUserHandler(c *gin.Context) {
|
||
// 强制只能修改当前登录用户自己的资料:忽略请求体中的 id,防止越权修改他人
|
||
uid, ok := c.Get("user_id")
|
||
if !ok {
|
||
utils.Unauthorized(c, "未认证")
|
||
return
|
||
}
|
||
currentUserID := uid.(string)
|
||
|
||
var req struct {
|
||
ID string `json:"id"`
|
||
Updates map[string]interface{} `json:"updates" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
utils.BadRequest(c, "参数错误: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 字段白名单:仅允许更新以下资料字段。
|
||
// 过滤掉 password/id/email 等敏感或身份字段,防止通过此接口改密码或伪造身份。
|
||
// phone 保留:PC 端"修改手机号"功能走本接口更新自己的手机号(越权风险已由
|
||
// currentUserID 强制"只能改自己"消除;users.phone 有唯一索引,占用冲突由数据库兜底报错)
|
||
// moment_cover:朋友圈顶部封面图,用户可自行更换
|
||
allowed := map[string]bool{"name": true, "avatar": true, "desc": true, "region": true, "phone": true, "moment_cover": true}
|
||
updates := make(map[string]interface{})
|
||
for k, v := range req.Updates {
|
||
if allowed[k] {
|
||
updates[k] = v
|
||
}
|
||
}
|
||
if len(updates) == 0 {
|
||
utils.BadRequest(c, "没有可更新的字段")
|
||
return
|
||
}
|
||
|
||
if err := service.UserSvc.UpdateUser(currentUserID, updates); err != nil {
|
||
utils.BadRequest(c, "更新失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 如果更新了头像或名称,通知好友
|
||
if service.ContactSvc != nil && service.ChatSvc != nil {
|
||
if _, hasName := updates["name"]; hasName {
|
||
go notifyFriendsOfProfileUpdate(currentUserID, updates)
|
||
} else if _, hasAvatar := updates["avatar"]; hasAvatar {
|
||
go notifyFriendsOfProfileUpdate(currentUserID, updates)
|
||
}
|
||
}
|
||
|
||
utils.Success(c, "更新成功")
|
||
}
|
||
|
||
/**
|
||
* notifyFriendsOfProfileUpdate
|
||
* 功能:通知好友用户信息更新
|
||
*/
|
||
func notifyFriendsOfProfileUpdate(userID string, updates map[string]interface{}) {
|
||
if service.ContactSvc == nil || service.ChatSvc == nil || service.UserSvc == nil {
|
||
return
|
||
}
|
||
|
||
// 获取更新后的用户信息
|
||
user, err := service.UserSvc.GetUserByID(userID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
// 获取好友列表
|
||
friendIDs, err := service.ContactSvc.GetFriendUserIDs(userID)
|
||
if err != nil || len(friendIDs) == 0 {
|
||
return
|
||
}
|
||
|
||
// 构建通知消息
|
||
notifExtra := map[string]interface{}{
|
||
"type": "profile_update",
|
||
"user_id": userID,
|
||
"name": user.Name,
|
||
"avatar": user.Avatar,
|
||
"updates": updates,
|
||
}
|
||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||
|
||
// 发送给所有好友
|
||
for _, friendID := range friendIDs {
|
||
notifMsg := model.ChatMessage{
|
||
RoomID: userID,
|
||
SenderUserID: userID,
|
||
ReceiverUserID: friendID,
|
||
MessageType: model.MessageTypeFriendNotif,
|
||
Content: user.Name + " 更新了个人资料",
|
||
Extra: string(notifExtraJSON),
|
||
}
|
||
|
||
// 不持久化此类通知,直接推送
|
||
pushMsg := model.WsPayload{
|
||
RequestType: "receive_message",
|
||
Data: notifMsg,
|
||
}
|
||
msgBytes, _ := json.Marshal(pushMsg)
|
||
service.ChatSvc.DispatchMessage(friendID, msgBytes)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* DeleteUserHandler
|
||
* 功能:删除用户
|
||
* 路径:POST /api/user/delete
|
||
*/
|
||
func DeleteUserHandler(c *gin.Context) {
|
||
// 仅允许注销当前登录用户自己,禁止删除他人(原实现可传任意 id 越权删除)
|
||
uid, ok := c.Get("user_id")
|
||
if !ok {
|
||
utils.Unauthorized(c, "未认证")
|
||
return
|
||
}
|
||
currentUserID := uid.(string)
|
||
|
||
var req struct {
|
||
ID string `json:"id"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
utils.BadRequest(c, "参数错误")
|
||
return
|
||
}
|
||
|
||
if req.ID != "" && req.ID != currentUserID {
|
||
utils.Forbidden(c, "无权删除其他用户")
|
||
return
|
||
}
|
||
|
||
if err := service.UserSvc.DeleteUser(currentUserID); err != nil {
|
||
utils.BadRequest(c, "删除失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
utils.Success(c, "删除成功")
|
||
}
|
||
|
||
// 辅助函数:生成用户ID
|
||
func generateUserID() string {
|
||
// 使用时间戳+随机数生成用户ID
|
||
timestamp := time.Now().UnixNano()
|
||
random := rand.Intn(1000000)
|
||
return fmt.Sprintf("user_%d_%d", timestamp, random)
|
||
}
|