- frontend/src/api/meeting.js:12 个 REST 接口封装,统一 unwrap envelope.data - frontend/src/services/websocket.js:新增 sendWithAck(Promise 化 + 超时 + 序列号) - frontend/src/utils/mediasoup-client.js:MediaEngine 包装 Device/Transport/Producer/Consumer - frontend/src/store/meeting.js:Pinia 会议状态机,桥接 14 个 WS 事件 + cleanupStaleMeetings - frontend/src/constants/meeting.js:状态枚举 + 事件名集中管理 - frontend/src/pages/meeting/debug.vue:临时调试页(H5 原生 video/audio DOM 绕过 uni 组件限制) - backend:meeting.consume.resume WS 事件 + create/join 响应透传 router_id + rtp_capabilities - 文档:frontend/meeting.md、websocket.md、CURRENT_STATUS、plan 全部同步 Task 9 落地 Made-with: Cursor
175 lines
6.6 KiB
Go
175 lines
6.6 KiB
Go
package dto
|
||
|
||
import "encoding/json"
|
||
|
||
// ====== 会议模块基础 DTO(Phase 2e-2)======
|
||
|
||
// MeetingRoomDTO 会议房间传输对象
|
||
// 用于 CreateRoom / GetRoom / JoinRoom / ListMyMeetings 的响应体
|
||
// 字段与 model.MeetingRoom 对齐,敏感字段(password_hash)不对外暴露
|
||
type MeetingRoomDTO struct {
|
||
ID int64 `json:"id"`
|
||
RoomCode string `json:"room_code"`
|
||
Title string `json:"title"`
|
||
HostID int64 `json:"host_id"`
|
||
HostName string `json:"host_name,omitempty"`
|
||
HostAvatar string `json:"host_avatar,omitempty"`
|
||
Type int `json:"type"`
|
||
HasPassword bool `json:"has_password"`
|
||
MaxMembers int `json:"max_members"`
|
||
Status int `json:"status"`
|
||
StatusLabel string `json:"status_label,omitempty"`
|
||
ScheduledAt string `json:"scheduled_at,omitempty"`
|
||
StartedAt string `json:"started_at,omitempty"`
|
||
EndedAt string `json:"ended_at,omitempty"`
|
||
EndedReason *string `json:"ended_reason,omitempty"`
|
||
Settings string `json:"settings"`
|
||
CreatedAt string `json:"created_at"`
|
||
OnlineCount int `json:"online_count"`
|
||
}
|
||
|
||
// MeetingParticipantDTO 会议参与者传输对象
|
||
// 用于 GetRoom 的成员列表响应;活跃用户(left_at=NULL)在前
|
||
type MeetingParticipantDTO struct {
|
||
ID int64 `json:"id"`
|
||
RoomID int64 `json:"room_id"`
|
||
UserID int64 `json:"user_id"`
|
||
UserName string `json:"user_name,omitempty"`
|
||
UserAvatar string `json:"user_avatar,omitempty"`
|
||
Role int `json:"role"`
|
||
RoleLabel string `json:"role_label,omitempty"`
|
||
JoinedAt string `json:"joined_at"`
|
||
LeftAt string `json:"left_at,omitempty"`
|
||
LeftReason *string `json:"left_reason,omitempty"`
|
||
Duration int `json:"duration"`
|
||
IsActive bool `json:"is_active"`
|
||
}
|
||
|
||
// MeetingChatDTO 会议内聊天消息传输对象
|
||
// 用于 SendChat 的响应 + ListChats 的列表项
|
||
type MeetingChatDTO struct {
|
||
ID int64 `json:"id"`
|
||
RoomID int64 `json:"room_id"`
|
||
UserID int64 `json:"user_id"`
|
||
UserName string `json:"user_name,omitempty"`
|
||
UserAvatar string `json:"user_avatar,omitempty"`
|
||
Content string `json:"content"`
|
||
CreatedAt string `json:"created_at"`
|
||
}
|
||
|
||
// ====== REST API 请求 / 响应 ======
|
||
|
||
// CreateMeetingRoomRequest 创建即时会议请求
|
||
// POST /api/v1/meeting/rooms
|
||
type CreateMeetingRoomRequest struct {
|
||
Title string `json:"title" binding:"required,min=1,max=200"` // 会议标题
|
||
Password string `json:"password" binding:"omitempty,min=4,max=20"` // 可选入会密码(纯文本入参,服务端 bcrypt 哈希存储)
|
||
MaxMembers int `json:"max_members" binding:"omitempty,min=2,max=8"` // 可选上限(MVP 硬上限 8,留参数为 Phase 2e-3 扩展)
|
||
}
|
||
|
||
// CreateMeetingRoomResponse 创建会议响应
|
||
// Task 9 起返回 router_id + rtp_capabilities,便于前端创建会议后立即 Device.load
|
||
type CreateMeetingRoomResponse struct {
|
||
Room MeetingRoomDTO `json:"room"`
|
||
RouterID string `json:"router_id,omitempty"`
|
||
RtpCapabilities json.RawMessage `json:"rtp_capabilities,omitempty"`
|
||
}
|
||
|
||
// JoinMeetingRoomRequest 加入会议请求
|
||
// POST /api/v1/meeting/rooms/:code/join
|
||
type JoinMeetingRoomRequest struct {
|
||
Password string `json:"password" binding:"omitempty,max=20"` // 入会密码(如房间设密则必传)
|
||
}
|
||
|
||
// JoinMeetingRoomResponse 加入会议响应
|
||
type JoinMeetingRoomResponse struct {
|
||
Room MeetingRoomDTO `json:"room"`
|
||
Participant MeetingParticipantDTO `json:"participant"`
|
||
RouterID string `json:"router_id,omitempty"` // mediasoup Router ID(Task 7 接入 Node 后填充)
|
||
RtpCapabilities json.RawMessage `json:"rtp_capabilities,omitempty"` // Router RTP 能力(Task 9);前端 mediasoup-client Device.load 直接使用
|
||
}
|
||
|
||
// LeaveMeetingRoomResponse 离开会议响应
|
||
type LeaveMeetingRoomResponse struct {
|
||
Duration int `json:"duration"` // 本次参会时长(秒)
|
||
}
|
||
|
||
// GetMeetingRoomResponse 会议详情响应
|
||
// GET /api/v1/meeting/rooms/:code
|
||
type GetMeetingRoomResponse struct {
|
||
Room MeetingRoomDTO `json:"room"`
|
||
Participants []MeetingParticipantDTO `json:"participants"`
|
||
OnlineCount int `json:"online_count"`
|
||
}
|
||
|
||
// ListMyMeetingsRequest 我的会议列表查询参数
|
||
// GET /api/v1/meeting/rooms/mine
|
||
type ListMyMeetingsRequest struct {
|
||
Status *int `form:"status"` // 状态过滤:nil=全部,0/1/2
|
||
BeforeID int64 `form:"before_id"` // 游标分页
|
||
Limit int `form:"limit"` // 页大小,默认 20,最大 50
|
||
}
|
||
|
||
// ListMyMeetingsResponse 我的会议列表响应
|
||
type ListMyMeetingsResponse struct {
|
||
List []MeetingRoomDTO `json:"list"`
|
||
HasMore bool `json:"has_more"`
|
||
}
|
||
|
||
// InviteUsersRequest 邀请用户请求
|
||
// POST /api/v1/meeting/rooms/:code/invite
|
||
type InviteUsersRequest struct {
|
||
InviteeIDs []int64 `json:"invitee_ids" binding:"required,min=1,max=50,dive,gt=0"` // 被邀请用户 ID 数组(去重后)
|
||
}
|
||
|
||
// InviteUsersResponse 邀请结果响应
|
||
type InviteUsersResponse struct {
|
||
Pushed int `json:"pushed"` // 成功发送邀请通知的数量(在线+离线都算)
|
||
Skipped int `json:"skipped"` // 跳过的数量(已在会中 / 重复 ID 等)
|
||
}
|
||
|
||
// KickMemberRequest 踢人请求
|
||
// POST /api/v1/meeting/rooms/:code/kick
|
||
type KickMemberRequest struct {
|
||
UserID int64 `json:"user_id" binding:"required,gt=0"`
|
||
}
|
||
|
||
// TransferHostRequest 主持人转让请求
|
||
// POST /api/v1/meeting/rooms/:code/transfer-host
|
||
type TransferHostRequest struct {
|
||
TargetUserID int64 `json:"target_user_id" binding:"required,gt=0"`
|
||
}
|
||
|
||
// SendMeetingChatRequest 发送会议内聊天请求
|
||
// POST /api/v1/meeting/rooms/:code/chats
|
||
type SendMeetingChatRequest struct {
|
||
Content string `json:"content" binding:"required,min=1,max=500"`
|
||
}
|
||
|
||
// SendMeetingChatResponse 发送聊天响应
|
||
type SendMeetingChatResponse struct {
|
||
Message MeetingChatDTO `json:"message"`
|
||
}
|
||
|
||
// ListMeetingChatsRequest 会议内聊天列表查询参数
|
||
// GET /api/v1/meeting/rooms/:code/chats
|
||
type ListMeetingChatsRequest struct {
|
||
BeforeID int64 `form:"before_id"` // 游标分页
|
||
Limit int `form:"limit"` // 页大小,默认 30,最大 100
|
||
}
|
||
|
||
// ListMeetingChatsResponse 会议内聊天列表响应
|
||
type ListMeetingChatsResponse struct {
|
||
List []MeetingChatDTO `json:"list"`
|
||
HasMore bool `json:"has_more"`
|
||
}
|
||
|
||
// RedeemInviteTokenResponse 邀请 Token 兑换响应
|
||
// POST /api/v1/meeting/invite-tokens/:token/redeem
|
||
// 兑换成功仅返回会议号 + 邀请人,前端自行调 JoinRoom 入会
|
||
type RedeemInviteTokenResponse struct {
|
||
RoomCode string `json:"room_code"`
|
||
InviterID int64 `json:"inviter_id"`
|
||
HasPassword bool `json:"has_password"`
|
||
}
|