Files
nl-im-service/internal/api/user_handler.go
2026-08-24 15:29:53 +08:00

263 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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)
}