Files

1040 lines
27 KiB
Go
Raw Permalink Normal View History

2025-12-03 11:00:47 +08:00
/**
* package api
* 作用房间管理相关API处理器
*/
package api
import (
2025-12-05 15:23:04 +08:00
"encoding/json"
"fmt"
2025-12-05 13:12:48 +08:00
"log"
2025-12-05 09:25:24 +08:00
"strconv"
2025-12-05 15:48:31 +08:00
"strings"
2025-12-04 22:29:32 +08:00
"xk-websocket-v2/internal/model"
2025-12-03 11:00:47 +08:00
"xk-websocket-v2/internal/service"
"xk-websocket-v2/internal/utils"
"github.com/gin-gonic/gin"
2025-12-04 22:29:32 +08:00
"gorm.io/gorm"
2025-12-03 11:00:47 +08:00
)
/**
* CreateRoomHandler
* 功能创建房间
* 路径POST /api/rooms
*/
func CreateRoomHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
var req struct {
2025-12-04 22:29:32 +08:00
RoomType string `json:"room_type" binding:"required,oneof=p2p group"`
Members []string `json:"members" binding:"required"`
RoomName string `json:"room_name"`
RoomAvatar string `json:"room_avatar"`
2025-12-03 11:00:47 +08:00
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
room, err := service.RoomSvc.CreateRoom(req.RoomType, req.Members, userID.(string))
if err != nil {
utils.BadRequest(c, err.Error())
return
}
// 如果提供了房间名称和头像,更新
if req.RoomName != "" || req.RoomAvatar != "" {
updates := make(map[string]interface{})
if req.RoomName != "" {
updates["room_name"] = req.RoomName
}
if req.RoomAvatar != "" {
updates["room_avatar"] = req.RoomAvatar
}
service.RoomSvc.DB.Model(room).Updates(updates)
}
utils.SuccessWithData(c, room, "创建成功")
}
/**
* GetRoomHandler
* 功能获取房间信息
* 路径GET /api/rooms/:id
*/
func GetRoomHandler(c *gin.Context) {
roomID := c.Param("id")
2026-08-24 15:29:53 +08:00
// 仅房间成员可查看房间信息,防止非成员凭 room_id 窥探
if !requireRoomMember(c, roomID) {
return
}
2025-12-03 11:00:47 +08:00
room, err := service.RoomSvc.GetRoom(roomID)
if err != nil {
utils.NotFound(c, "房间不存在")
return
}
utils.SuccessWithData(c, room, "获取成功")
}
2026-08-24 15:29:53 +08:00
/**
* requireRoomMember
* 功能校验当前登录用户是否为该房间/群成员不是则写 403 并返回 false
* 为什么这样写群信息成员列表公告设置等读接口原先不校验调用者身份
* 任意登录用户凭 room_id 即可窥探任意群数据这里统一收口做成员校验
*/
func requireRoomMember(c *gin.Context, roomID string) bool {
uid, ok := c.Get("user_id")
if !ok {
utils.Unauthorized(c, "未认证")
return false
}
if !service.RoomSvc.IsRoomMember(roomID, uid.(string)) {
utils.Forbidden(c, "无权访问该群")
return false
}
return true
}
2025-12-04 22:29:32 +08:00
/**
* CreateChatGroupHandler
* 功能创建群聊
* 路径POST /api/groups
*/
func CreateChatGroupHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
var req struct {
Name string `json:"name" binding:"required"`
Avatar string `json:"avatar"`
MemberIDs []string `json:"member_ids" binding:"required"`
AdminIDs []string `json:"admin_ids"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
creatorID := userID.(string)
members := req.MemberIDs
// 确保创建者在成员列表中
found := false
for _, id := range members {
if id == creatorID {
found = true
break
}
}
if !found {
members = append(members, creatorID)
}
room, err := service.RoomSvc.CreateGroupRoom(creatorID, members, req.Name, req.Avatar, req.AdminIDs)
if err != nil {
utils.BadRequest(c, err.Error())
return
}
2025-12-05 15:23:04 +08:00
// 为所有成员创建会话记录
if service.ConversationSvc != nil {
if err := service.ConversationSvc.EnsureGroupConversations(room.RoomID); err != nil {
log.Printf("⚠️ 创建群聊会话失败 (roomID: %s): %v", room.RoomID, err)
}
}
// 发送系统消息通知所有成员
if service.ChatSvc != nil && service.UserSvc != nil {
// 获取创建者信息
creator, err := service.UserSvc.GetUserByID(creatorID)
if err == nil {
// 构建系统消息内容
content := fmt.Sprintf("%s 邀请你加入了群聊", creator.Name)
extraData := map[string]interface{}{
"type": "group_created",
"room_id": room.RoomID,
"creator": creatorID,
}
extraJSON, _ := json.Marshal(extraData)
// 为每个成员发送系统消息
for _, memberID := range members {
if memberID != creatorID {
// 创建系统消息
systemMsg := model.ChatMessage{
RoomID: room.RoomID,
SenderUserID: creatorID,
ReceiverUserID: room.RoomID, // 群聊时使用 roomID
MessageType: model.MessageTypeSystem,
Content: content,
Extra: string(extraJSON),
}
// 持久化消息
if err := service.ChatSvc.DB.Create(&systemMsg).Error; err != nil {
log.Printf("❌ 发送群聊系统消息失败 (memberID: %s): %v", memberID, err)
continue
}
// 更新会话
if service.ConversationSvc != nil {
_ = service.ConversationSvc.UpsertConversationOnMessage(memberID, room.RoomID, room.RoomID, systemMsg, false)
}
// 通过 WebSocket 推送消息
pushMsg := model.WsPayload{
RequestType: "receive_message",
Data: systemMsg,
}
msgBytes, _ := json.Marshal(pushMsg)
service.ChatSvc.DispatchMessage(memberID, msgBytes)
}
}
}
}
2025-12-04 22:29:32 +08:00
utils.SuccessWithData(c, room, "创建群聊成功")
}
2025-12-05 13:12:48 +08:00
/**
* ListUserGroupsHandler
* 功能获取用户所在的所有群聊列表带分组信息
* 路径GET /api/groups
*/
func ListUserGroupsHandler(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
utils.Unauthorized(c, "未授权")
return
}
groups, members, err := service.RoomSvc.ListUserGroups(userID.(string))
if err != nil {
utils.InternalError(c, "查询失败")
return
}
// 确保返回空数组而不是null
if groups == nil {
groups = []model.ChatRoom{}
}
if members == nil {
members = []model.RoomMember{}
}
// 为每个群的所有成员创建缺失的会话记录
if service.ConversationSvc != nil {
for _, group := range groups {
if err := service.ConversationSvc.EnsureGroupConversations(group.RoomID); err != nil {
// 记录错误但不中断流程
log.Printf("⚠️ 创建群聊会话失败 (roomID: %s): %v", group.RoomID, err)
}
}
}
// 创建成员映射表,方便快速查找
memberMap := make(map[string]model.RoomMember)
for _, member := range members {
memberMap[member.RoomID] = member
}
// 转换为前端期望的格式,包含 category 和 role
result := make([]map[string]interface{}, 0, len(groups))
for _, group := range groups {
// 获取用户在群中的角色
member, exists := memberMap[group.RoomID]
if !exists {
continue // 跳过无法获取成员信息的群
}
// 确定分类created/managed/joined
category := "joined"
if group.CreatorID == userID.(string) {
category = "created"
} else if member.Role == 1 || member.Role == 2 {
category = "managed"
}
// 格式化时间
var lastMessageTime *string
if group.LastMessageTime != nil {
timeStr := group.LastMessageTime.Format("2006-01-02T15:04:05Z07:00")
lastMessageTime = &timeStr
}
result = append(result, map[string]interface{}{
2025-12-08 14:18:00 +08:00
"room_id": group.RoomID,
"room_type": group.RoomType,
"room_name": group.RoomName,
"room_avatar": group.RoomAvatar,
"owner_id": group.OwnerID,
"creator_id": group.CreatorID,
"category": category,
"role": int(member.Role),
2025-12-05 13:12:48 +08:00
"last_message_time": lastMessageTime,
2025-12-08 14:18:00 +08:00
"last_message": group.LastMessage,
"created_at": group.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
"updated_at": group.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
2025-12-05 13:12:48 +08:00
})
}
utils.SuccessWithData(c, result, "获取成功")
}
2025-12-04 22:29:32 +08:00
/**
* GetGroupInfoHandler
* 功能获取群聊基础信息及成员数量
* 路径GET /api/groups/:room_id
*/
func GetGroupInfoHandler(c *gin.Context) {
roomID := c.Param("room_id")
2026-08-24 15:29:53 +08:00
// 仅群成员可查看群信息
if !requireRoomMember(c, roomID) {
return
}
2025-12-04 22:29:32 +08:00
room, err := service.RoomSvc.GetRoom(roomID)
if err != nil {
if err == gorm.ErrRecordNotFound {
utils.NotFound(c, "群聊不存在")
return
}
utils.InternalError(c, "查询失败")
return
}
members, err := service.RoomSvc.ListGroupMembers(roomID)
if err != nil {
utils.InternalError(c, "查询成员失败")
return
}
// 统计管理员ID
adminIDs := make([]string, 0)
for _, m := range members {
if m.Role == 1 {
adminIDs = append(adminIDs, m.UserID)
}
}
utils.SuccessWithData(c, gin.H{
"room": room,
"member_count": len(members),
"admin_ids": adminIDs,
"owner_id": room.OwnerID,
}, "获取成功")
}
/**
* ListGroupMembersHandler
* 功能获取群成员列表
* 路径GET /api/groups/:room_id/members
*/
func ListGroupMembersHandler(c *gin.Context) {
roomID := c.Param("room_id")
2025-12-05 15:23:04 +08:00
keyword := c.Query("keyword")
2026-08-24 15:29:53 +08:00
// 仅群成员可查看成员列表
if !requireRoomMember(c, roomID) {
return
}
2025-12-05 15:23:04 +08:00
var members []model.RoomMember
var err error
if keyword != "" {
// 如果有搜索关键词,使用搜索方法
members, err = service.RoomSvc.SearchGroupMembers(roomID, keyword)
} else {
// 否则返回所有成员
members, err = service.RoomSvc.ListGroupMembers(roomID)
}
2025-12-04 22:29:32 +08:00
if err != nil {
utils.InternalError(c, "查询失败")
return
}
utils.SuccessWithData(c, members, "获取成功")
}
/**
* AddGroupMembersHandler
* 功能邀请/拉人入群
* 路径POST /api/groups/:room_id/members
*/
func AddGroupMembersHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
var req struct {
MemberIDs []string `json:"member_ids" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
if err := service.RoomSvc.AddGroupMembers(roomID, userID.(string), req.MemberIDs); err != nil {
utils.BadRequest(c, err.Error())
return
}
2025-12-05 15:48:31 +08:00
// 发送群通知和系统消息
if service.ChatSvc != nil && service.UserSvc != nil {
// 获取操作者信息
operator, opErr := service.UserSvc.GetUserByID(userID.(string))
if opErr == nil {
// 获取被邀请成员信息
invitedNames := make([]string, 0)
for _, memberID := range req.MemberIDs {
member, memErr := service.UserSvc.GetUserByID(memberID)
if memErr == nil {
invitedNames = append(invitedNames, member.Name)
}
}
if len(invitedNames) > 0 {
// 获取群成员列表(包括新加入的成员)
memberIDs, mErr := service.RoomSvc.GetRoomMembers(roomID)
if mErr == nil {
// 构建群通知消息内容
var notifContent string
if len(invitedNames) == 1 {
notifContent = fmt.Sprintf("%s 邀请了 %s 加入群聊", operator.Name, invitedNames[0])
} else {
notifContent = fmt.Sprintf("%s 邀请了 %s 加入群聊", operator.Name, strings.Join(invitedNames, "、"))
}
notifExtra := map[string]interface{}{
2025-12-08 14:18:00 +08:00
"type": "member_invite",
"room_id": roomID,
"operator": userID.(string),
2025-12-05 15:48:31 +08:00
"operator_name": operator.Name,
2025-12-08 14:18:00 +08:00
"targets": req.MemberIDs,
"target_names": invitedNames,
2025-12-05 15:48:31 +08:00
}
notifExtraJSON, _ := json.Marshal(notifExtra)
2026-08-24 15:29:53 +08:00
// 为所有成员发送群通知(包括新加入的成员):房间级落库一次,按成员推送
service.ChatSvc.BroadcastGroupNotification(userID.(string), roomID, notifContent, string(notifExtraJSON), memberIDs)
2025-12-05 15:48:31 +08:00
// 为新加入的成员发送系统消息
for _, memberID := range req.MemberIDs {
systemContent := fmt.Sprintf("你被 %s 邀请加入群聊", operator.Name)
systemExtra := map[string]interface{}{
2025-12-08 14:18:00 +08:00
"type": "member_invited",
"room_id": roomID,
"operator": userID.(string),
2025-12-05 15:48:31 +08:00
"operator_name": operator.Name,
}
systemExtraJSON, _ := json.Marshal(systemExtra)
systemMsg := model.ChatMessage{
RoomID: roomID,
SenderUserID: userID.(string),
ReceiverUserID: roomID,
MessageType: model.MessageTypeSystem,
Content: systemContent,
Extra: string(systemExtraJSON),
}
if err := service.ChatSvc.DB.Create(&systemMsg).Error; err == nil {
// 更新会话
if service.ConversationSvc != nil {
_ = service.ConversationSvc.UpsertConversationOnMessage(memberID, roomID, roomID, systemMsg, false)
}
// 推送消息
pushMsg := model.WsPayload{
RequestType: "receive_message",
Data: systemMsg,
}
msgBytes, _ := json.Marshal(pushMsg)
service.ChatSvc.DispatchMessage(memberID, msgBytes)
}
}
}
}
}
}
2025-12-04 22:29:32 +08:00
utils.Success(c, "添加成员成功")
}
/**
* RemoveGroupMemberHandler
* 功能移除群成员或退群
2025-12-05 09:25:24 +08:00
* 路径POST /api/groups/:room_id/members/:user_id/remove
2025-12-04 22:29:32 +08:00
*/
func RemoveGroupMemberHandler(c *gin.Context) {
operatorID, _ := c.Get("user_id")
roomID := c.Param("room_id")
memberID := c.Param("user_id")
// 自己移除自己 = 退群
if operatorID.(string) == memberID {
if err := service.RoomSvc.QuitGroup(roomID, memberID); err != nil {
utils.BadRequest(c, err.Error())
return
}
utils.Success(c, "已退出群聊")
return
}
// 否则为管理员/群主踢人
if err := service.RoomSvc.RemoveGroupMember(roomID, operatorID.(string), memberID); err != nil {
utils.BadRequest(c, err.Error())
return
}
2025-12-05 15:23:04 +08:00
// 发送群通知和系统消息
if service.ChatSvc != nil && service.UserSvc != nil {
// 获取操作者和被移除成员信息
operator, opErr := service.UserSvc.GetUserByID(operatorID.(string))
member, memErr := service.UserSvc.GetUserByID(memberID)
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
if opErr == nil && memErr == nil {
// 获取群成员列表
memberIDs, mErr := service.RoomSvc.GetRoomMembers(roomID)
if mErr == nil {
// 构建群通知消息
notifContent := fmt.Sprintf("%s 将 %s 移出了群聊", operator.Name, member.Name)
notifExtra := map[string]interface{}{
2025-12-08 14:18:00 +08:00
"type": "member_remove",
"room_id": roomID,
"operator": operatorID.(string),
"target": memberID,
2025-12-05 15:23:04 +08:00
"operator_name": operator.Name,
"target_name": member.Name,
}
notifExtraJSON, _ := json.Marshal(notifExtra)
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
// 构建系统消息
systemContent := fmt.Sprintf("你被 %s 移出了群聊", operator.Name)
systemExtra := map[string]interface{}{
2025-12-08 14:18:00 +08:00
"type": "member_removed",
"room_id": roomID,
2025-12-05 15:23:04 +08:00
"operator": operatorID.(string),
}
systemExtraJSON, _ := json.Marshal(systemExtra)
2025-12-08 14:18:00 +08:00
2026-08-24 15:29:53 +08:00
// 为所有成员发送群通知(被移除的成员不接收):房间级落库一次,按成员推送
remainIDs := make([]string, 0, len(memberIDs))
2025-12-05 15:23:04 +08:00
for _, uid := range memberIDs {
2026-08-24 15:29:53 +08:00
if uid != memberID {
remainIDs = append(remainIDs, uid)
2025-12-05 15:23:04 +08:00
}
}
2026-08-24 15:29:53 +08:00
service.ChatSvc.BroadcastGroupNotification(operatorID.(string), roomID, notifContent, string(notifExtraJSON), remainIDs)
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
// 为被移除的成员发送系统消息
systemMsg := model.ChatMessage{
RoomID: roomID,
SenderUserID: operatorID.(string),
ReceiverUserID: roomID,
MessageType: model.MessageTypeSystem,
Content: systemContent,
Extra: string(systemExtraJSON),
}
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
if err := service.ChatSvc.DB.Create(&systemMsg).Error; err == nil {
// 更新会话
if service.ConversationSvc != nil {
_ = service.ConversationSvc.UpsertConversationOnMessage(memberID, roomID, roomID, systemMsg, false)
}
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
// 推送消息
pushMsg := model.WsPayload{
RequestType: "receive_message",
Data: systemMsg,
}
msgBytes, _ := json.Marshal(pushMsg)
service.ChatSvc.DispatchMessage(memberID, msgBytes)
}
}
}
}
2025-12-04 22:29:32 +08:00
utils.Success(c, "已移除成员")
}
/**
* UpdateGroupInfoHandler
* 功能修改群名称/头像
2025-12-05 09:25:24 +08:00
* 路径POST /api/groups/:room_id/update
2025-12-04 22:29:32 +08:00
*/
func UpdateGroupInfoHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
var req struct {
Name string `json:"name"`
Avatar string `json:"avatar"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
// 仅群主或管理员可修改群信息
ok, err := service.RoomSvc.IsGroupAdminOrOwner(roomID, userID.(string))
if err != nil {
utils.InternalError(c, "权限校验失败")
return
}
if !ok {
utils.Forbidden(c, "无权限修改群信息")
return
}
updates := make(map[string]interface{})
if req.Name != "" {
updates["room_name"] = req.Name
}
if req.Avatar != "" {
updates["room_avatar"] = req.Avatar
}
if len(updates) == 0 {
utils.BadRequest(c, "没有可更新的字段")
return
}
// 更新群基础信息
if err := service.RoomSvc.DB.Model(&model.ChatRoom{}).
Where("room_id = ?", roomID).
Updates(updates).Error; err != nil {
utils.InternalError(c, "更新失败")
return
}
utils.Success(c, "更新成功")
}
/**
* ChangeMemberRoleHandler
* 功能调整成员角色
2025-12-05 09:25:24 +08:00
* 路径POST /api/groups/:room_id/members/:user_id/role
2025-12-04 22:29:32 +08:00
*/
func ChangeMemberRoleHandler(c *gin.Context) {
operatorID, _ := c.Get("user_id")
roomID := c.Param("room_id")
memberID := c.Param("user_id")
var req struct {
2025-12-08 14:55:47 +08:00
Role *int8 `json:"role"` // 使用指针类型,允许 0 值(普通成员)
2025-12-04 22:29:32 +08:00
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
2025-12-08 14:55:47 +08:00
// 手动验证 role 是否提供
if req.Role == nil {
utils.BadRequest(c, "参数错误: role 字段必须提供")
return
}
if err := service.RoomSvc.ChangeMemberRole(roomID, operatorID.(string), memberID, *req.Role); err != nil {
2025-12-04 22:29:32 +08:00
utils.BadRequest(c, err.Error())
return
}
utils.Success(c, "角色已更新")
}
2025-12-05 09:25:24 +08:00
/**
* QuitGroupHandler
* 功能群成员主动退出群聊
* 路径POST /api/groups/:room_id/quit
*/
func QuitGroupHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
// 校验用户是否为该群的成员
members, err := service.RoomSvc.ListGroupMembers(roomID)
if err != nil {
utils.InternalError(c, "查询成员失败")
return
}
isMember := false
for _, m := range members {
if m.UserID == userID.(string) {
isMember = true
break
}
}
if !isMember {
utils.BadRequest(c, "您不是该群的成员")
return
}
// 调用服务层退出群聊
if err := service.RoomSvc.QuitGroup(roomID, userID.(string)); err != nil {
utils.BadRequest(c, err.Error())
return
}
2025-12-05 15:23:04 +08:00
// 发送群通知
if service.ChatSvc != nil && service.UserSvc != nil {
// 获取退出成员信息
member, memErr := service.UserSvc.GetUserByID(userID.(string))
2025-12-08 14:18:00 +08:00
2025-12-05 15:23:04 +08:00
if memErr == nil {
// 获取群成员列表(退出后剩余的成员)
memberIDs, mErr := service.RoomSvc.GetRoomMembers(roomID)
if mErr == nil {
// 构建群通知消息
notifContent := fmt.Sprintf("%s 退出了群聊", member.Name)
notifExtra := map[string]interface{}{
"type": "member_leave",
"room_id": roomID,
"target": userID.(string),
"target_name": member.Name,
}
notifExtraJSON, _ := json.Marshal(notifExtra)
2025-12-08 14:18:00 +08:00
2026-08-24 15:29:53 +08:00
// 为所有剩余成员发送群通知:房间级落库一次,按成员推送
service.ChatSvc.BroadcastGroupNotification(userID.(string), roomID, notifContent, string(notifExtraJSON), memberIDs)
2025-12-05 15:23:04 +08:00
}
}
}
2025-12-05 09:25:24 +08:00
utils.Success(c, "已退出群聊")
}
/**
* DissolveGroupHandler
* 功能群主解散群聊
* 路径POST /api/groups/:room_id/dissolve
*/
func DissolveGroupHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
// 调用服务层解散群聊
if err := service.RoomSvc.DissolveGroup(roomID, userID.(string)); err != nil {
utils.BadRequest(c, err.Error())
return
}
utils.Success(c, "群聊已解散")
}
/**
* GetGroupNotificationsHandler
* 功能获取群通知列表
* 路径GET /api/group-notifications
*/
func GetGroupNotificationsHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
// 分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
isReadStr := c.Query("is_read")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
// 构建查询条件
query := service.ChatSvc.DB.Model(&model.ChatMessage{}).
Where("message_type = ?", model.MessageTypeGroupNotif).
Where("room_id IN (SELECT room_id FROM room_members WHERE user_id = ?)", userID)
// 可选:按已读状态过滤
if isReadStr == "true" || isReadStr == "false" {
// 注意chat_messages 表可能没有 is_read 字段,这里先不实现
// 如果需要,可以在 extra 字段中存储或使用其他方式
}
// 获取总数
var total int64
query.Count(&total)
// 分页查询
offset := (page - 1) * pageSize
var messages []model.ChatMessage
if err := query.Order("created_at desc").
Offset(offset).
Limit(pageSize).
Find(&messages).Error; err != nil {
utils.InternalError(c, "查询失败")
return
}
// 确保返回空数组而不是null
if messages == nil {
messages = []model.ChatMessage{}
}
utils.SuccessWithData(c, gin.H{
2025-12-08 14:18:00 +08:00
"data": messages,
"total": total,
"page": page,
2025-12-05 09:25:24 +08:00
"page_size": pageSize,
}, "获取成功")
}
2025-12-05 13:12:48 +08:00
/**
* GetGroupAnnouncementHandler
* 功能获取群公告
* 路径GET /api/groups/:room_id/announcement
*/
func GetGroupAnnouncementHandler(c *gin.Context) {
roomID := c.Param("room_id")
2026-08-24 15:29:53 +08:00
// 仅群成员可查看群公告
if !requireRoomMember(c, roomID) {
return
}
2025-12-05 13:12:48 +08:00
announcement, err := service.RoomSvc.GetGroupAnnouncement(roomID)
if err != nil {
if err == gorm.ErrRecordNotFound {
utils.NotFound(c, "群聊不存在")
return
}
utils.InternalError(c, "查询失败")
return
}
utils.SuccessWithData(c, gin.H{
"announcement": announcement,
}, "获取成功")
}
/**
* UpdateGroupAnnouncementHandler
* 功能更新群公告
* 路径POST /api/groups/:room_id/announcement
*/
func UpdateGroupAnnouncementHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
var req struct {
Announcement string `json:"announcement" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
if err := service.RoomSvc.UpdateGroupAnnouncement(roomID, userID.(string), req.Announcement); err != nil {
utils.BadRequest(c, err.Error())
return
}
utils.Success(c, "更新成功")
}
2025-12-08 14:18:00 +08:00
2026-08-24 15:29:53 +08:00
/**
* UpdateMyNicknameHandler
* 功能更新"我在本群的昵称"群名片
* 路径POST /api/groups/:room_id/nickname
* 说明操作者从 JWT 只能改自己的群名片此前该接口缺失导致移动端改群昵称功能 100% 失败
*/
func UpdateMyNicknameHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
var req struct {
// 允许空串(清除群名片),因此不加 required
Nickname string `json:"nickname"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
// 群名片长度限制,与数据库 varchar(100) 对齐(按字符数收紧到 30避免界面溢出
if len([]rune(req.Nickname)) > 30 {
utils.BadRequest(c, "群昵称不能超过30个字符")
return
}
if err := service.RoomSvc.UpdateMemberNickname(roomID, userID.(string), req.Nickname); err != nil {
utils.BadRequest(c, err.Error())
return
}
utils.Success(c, "修改成功")
}
2025-12-08 14:18:00 +08:00
/**
* MuteGroupMemberHandler
* 功能禁言群成员
* 路径POST /api/groups/:room_id/members/:user_id/mute
*/
func MuteGroupMemberHandler(c *gin.Context) {
operatorID, _ := c.Get("user_id")
roomID := c.Param("room_id")
memberID := c.Param("user_id")
var req struct {
Duration int64 `json:"duration"` // 禁言时长0=永久,-1=解除禁言
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误: "+err.Error())
return
}
if err := service.RoomSvc.MuteGroupMember(roomID, operatorID.(string), memberID, req.Duration); err != nil {
utils.BadRequest(c, err.Error())
return
}
if req.Duration == -1 {
utils.Success(c, "已解除禁言")
} else if req.Duration == 0 {
utils.Success(c, "已永久禁言")
} else {
utils.Success(c, "禁言成功")
}
}
/**
* GetMemberMuteStatusHandler
* 功能获取成员禁言状态
* 路径GET /api/groups/:room_id/members/:user_id/mute
*/
func GetMemberMuteStatusHandler(c *gin.Context) {
roomID := c.Param("room_id")
memberID := c.Param("user_id")
2026-08-24 15:29:53 +08:00
// 鉴权:仅本群成员可查询,防止任意登录用户越权读取任意群成员的禁言状态
// (与同文件其他群接口的 requireRoomMember 校验保持一致)
if !requireRoomMember(c, roomID) {
return
}
2025-12-08 14:18:00 +08:00
isMuted, mutedUntil, err := service.RoomSvc.IsGroupMemberMuted(roomID, memberID)
if err != nil {
if err == gorm.ErrRecordNotFound {
utils.NotFound(c, "成员不存在")
return
}
utils.InternalError(c, "查询失败")
return
}
utils.SuccessWithData(c, gin.H{
"is_muted": isMuted,
"muted_until": mutedUntil,
}, "获取成功")
}
2026-07-08 08:18:58 +08:00
/**
* GetGroupSettingsHandler
* 功能获取群设置名称头像公告邀请权限等
* 路径GET /api/groups/:room_id/settings
*/
func GetGroupSettingsHandler(c *gin.Context) {
roomID := c.Param("room_id")
2026-08-24 15:29:53 +08:00
// 仅群成员可查看群设置
if !requireRoomMember(c, roomID) {
return
}
2026-07-08 08:18:58 +08:00
room, err := service.RoomSvc.GetRoom(roomID)
if err != nil {
utils.NotFound(c, "群聊不存在")
return
}
utils.SuccessWithData(c, gin.H{
"name": room.RoomName,
"avatar": room.RoomAvatar,
"description": room.Announcement,
"invite_permission": 0,
"created_at": room.CreatedAt,
"updated_at": room.UpdatedAt,
}, "获取成功")
}
/**
* UpdateGroupSettingsHandler
* 功能更新群设置
* 路径POST /api/groups/:room_id/settings
*/
func UpdateGroupSettingsHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
roomID := c.Param("room_id")
ok, err := service.RoomSvc.IsGroupAdminOrOwner(roomID, userID.(string))
if err != nil || !ok {
utils.Forbidden(c, "无权限修改群设置")
return
}
var req struct {
Name string `json:"name"`
Avatar string `json:"avatar"`
Description string `json:"description"`
InvitePermission *int `json:"invite_permission"`
}
if err := c.ShouldBindJSON(&req); err != nil {
utils.BadRequest(c, "参数错误")
return
}
updates := map[string]interface{}{}
if req.Name != "" {
updates["room_name"] = req.Name
}
if req.Avatar != "" {
updates["room_avatar"] = req.Avatar
}
if req.Description != "" {
updates["announcement"] = req.Description
}
if len(updates) == 0 {
utils.BadRequest(c, "没有可更新的字段")
return
}
if err := service.RoomSvc.DB.Model(&model.ChatRoom{}).Where("room_id = ?", roomID).Updates(updates).Error; err != nil {
utils.InternalError(c, "更新失败")
return
}
GetGroupSettingsHandler(c)
}
/**
* GetCallHistoryHandler
* 功能获取当前用户的通话记录基于 call_id 消息
* 路径GET /api/calls/history
*/
func GetCallHistoryHandler(c *gin.Context) {
userID, _ := c.Get("user_id")
var messages []model.ChatMessage
err := service.ChatSvc.DB.
Where("(sender_user_id = ? OR receiver_user_id = ?) AND call_id <> '' AND call_id IS NOT NULL", userID, userID).
Order("created_at DESC").
Limit(50).
Find(&messages).Error
if err != nil {
utils.InternalError(c, "查询失败")
return
}
utils.SuccessWithData(c, messages, "获取成功")
}