Files
EchoChat/backend/go-service/app/constants/meeting.go
2026-05-18 19:42:55 +08:00

165 lines
8.2 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 constants
// 会议相关常量Phase 2e-2
// 包含:会议类型、会议状态、参与者角色、结束原因、离会原因、默认配置
// 所有枚举值严禁直接用中文字符串,中文仅用于 *Map 展示
// 会议类型meeting_rooms.type
const (
MeetingTypeInstant = 1 // 即时会议MVP 仅此)
MeetingTypeScheduled = 2 // 预约会议Phase 2e-3
)
// MeetingTypeMap 会议类型中文映射
var MeetingTypeMap = map[int]string{
MeetingTypeInstant: "即时会议",
MeetingTypeScheduled: "预约会议",
}
// 会议状态meeting_rooms.status
const (
MeetingStatusPending = 0 // 未开始(仅预约会议使用)
MeetingStatusActive = 1 // 进行中
MeetingStatusEnded = 2 // 已结束
)
// MeetingStatusMap 会议状态中文映射
var MeetingStatusMap = map[int]string{
MeetingStatusPending: "未开始",
MeetingStatusActive: "进行中",
MeetingStatusEnded: "已结束",
}
// 会议参与者角色meeting_participants.role
// MVP 仅使用 Participant / Host 两档CoHost 保留至第二期
const (
MeetingRoleParticipant = 0 // 普通参会人
MeetingRoleHost = 1 // 主持人
MeetingRoleCoHost = 2 // 联合主持人保留MVP 不用)
)
// MeetingRoleMap 参与者角色中文映射
var MeetingRoleMap = map[int]string{
MeetingRoleParticipant: "参会人",
MeetingRoleHost: "主持人",
MeetingRoleCoHost: "联合主持人",
}
// 会议结束原因meeting_rooms.ended_reason
const (
MeetingEndedReasonHostEnded = "host_ended" // 主持人主动结束
MeetingEndedReasonEmptyTTL = "empty_ttl" // 空房超时销毁
MeetingEndedReasonAdminForce = "admin_force" // 管理员强制结束
MeetingEndedReasonSystemError = "system_error" // 系统异常(如 Node worker died
)
// MeetingEndedReasonMap 结束原因中文映射
var MeetingEndedReasonMap = map[string]string{
MeetingEndedReasonHostEnded: "主持人结束",
MeetingEndedReasonEmptyTTL: "空房超时",
MeetingEndedReasonAdminForce: "管理员强制结束",
MeetingEndedReasonSystemError: "系统异常",
}
// 离会原因meeting_participants.left_reason
const (
MeetingLeftReasonSelf = "self" // 主动离会
MeetingLeftReasonKicked = "kicked" // 被主持人移除
MeetingLeftReasonHostEnd = "host_end" // 主持人结束会议
MeetingLeftReasonEmptyTTL = "empty_ttl" // 空房 TTL 触发
MeetingLeftReasonDisconnect = "disconnect" // 网络断开(宽限期结束仍未重连)
)
// MeetingLeftReasonMap 离会原因中文映射
var MeetingLeftReasonMap = map[string]string{
MeetingLeftReasonSelf: "主动离开",
MeetingLeftReasonKicked: "被移除",
MeetingLeftReasonHostEnd: "会议结束",
MeetingLeftReasonEmptyTTL: "空房超时",
MeetingLeftReasonDisconnect: "网络断开",
}
// 会议默认配置(应用层强制约束)
const (
MeetingMVPMaxMembers = 8 // MVP 阶段单会议硬上限
MeetingRoomCodeLength = 9 // 会议号去连字符后的长度XXX-XXX-XXX
MeetingRoomCodeRetryMax = 3 // 生成会议号冲突重试上限
MeetingPasswordMaxAttempts = 5 // 密码连续错误锁定阈值
MeetingPasswordLockSeconds = 600 // 密码锁定时长10 分钟)
MeetingHostGraceSeconds = 120 // 主持人掉线宽限期2 分钟)
MeetingEmptyRoomTTLSeconds = 300 // 空房销毁 TTL5 分钟)
MeetingInviteTokenTTL = 600 // 邀请链接 Token TTL10 分钟)
MeetingChatRetentionHours = 24 // 会议聊天保留时长(会议结束后)
// Task 16 P2-7服务端聊天限流防止客户端校验被绕过
MeetingChatMaxContentLen = 500 // 单条消息最大字符数Unicode rune 计数)
MeetingChatRateLimitPerMin = 30 // 单用户单会议每分钟最大条数
MeetingChatRateLimitWindowS = 60 // 限流滑动窗口秒数(配合 Redis INCR + EXPIRE
// Task 16 Nit单个用户资源追踪集合 TTL
// 原硬编码在 meeting_signal_service.go::resourceTTL = time.Hour
// 中央化到 constants 便于统一修改 + 单元测试覆盖
MeetingResourceTrackTTLSeconds = 3600 // 1 小时
)
// 会议 WS 事件常量(设计文档 §6.3
// 命名格式meeting.{domain}.{action}
// 方向说明C→S客户端发往服务端需 ACKS→C服务端广播/定向推送,不需 ACK
const (
// ====== 房间组3 个)======
MeetingWSEventRoomJoin = "meeting.room.join" // C→S加入会议后的"宣告在线",绑定 WS 连接 ↔ roomCode
MeetingWSEventRoomLeave = "meeting.room.leave" // C→S主动离会等价 REST leave但保留信令触发入口
MeetingWSEventRoomEnded = "meeting.room.ended" // S→C会议已结束广播全员
// ====== 成员组5 个)======
MeetingWSEventMemberJoined = "meeting.member.joined" // S→C新成员加入
MeetingWSEventMemberLeft = "meeting.member.left" // S→C成员离开
MeetingWSEventMemberStateChange = "meeting.member.state.changed" // 双向:麦克风 / 摄像头状态变化host 可带 target_user_id 强制静音他人)
MeetingWSEventMemberKicked = "meeting.member.kicked" // S→C被移出会议定向发给被踢者
MeetingWSEventMemberProducerNew = "meeting.member.producer.new" // S→C有新 producer 可订阅produce.start 成功后广播)
MeetingWSEventHostChanged = "meeting.host.changed" // S→C主持人变更
// ====== 媒体组5 个mediasoup signaling 桥接)======
MeetingWSEventTransportCreate = "meeting.transport.create" // C→S→Node创建 Transport
MeetingWSEventTransportConnect = "meeting.transport.connect" // C→S→NodeTransport DTLS 握手
MeetingWSEventProduceStart = "meeting.produce.start" // C→S→Node创建 Producer广播 producer.new
MeetingWSEventConsumeStart = "meeting.consume.start" // C→S→Node订阅远端 Producer创建本地 Consumer
MeetingWSEventConsumeResume = "meeting.consume.resume" // C→S→NodeConsumer 订阅后由前端 track 挂载就绪再 resume防止首帧丢失Task 9 引入)
MeetingWSEventProducerClose = "meeting.producer.close" // C→S→Node关闭自己的 Producer推流停止
// ====== 补充事件(非 §6.3 核心 11 事件,但业务必须)======
MeetingWSEventChatMessage = "meeting.chat.message" // S→C会议内文字聊天广播REST SendChat 触发)
// ====== 屏幕共享Phase 3 引入2 个)======
// 屏幕流仍走普通的 sendTransport video Producer 路径appData.screen=true 区分),
// 这两个事件用于"会议内仅一份屏幕共享"的状态同步:
// - 启动时广播 producer_id + 共享者,前端将该流提升为大画面
// - 停止时广播,前端把屏幕 tile 撤掉,回到普通网格布局
// 后入者由 OnRoomJoin 内部 pushExistingRoomState 定向补推 started 事件,避免错过
MeetingWSEventScreenStarted = "meeting.screen.started" // S→C屏幕共享开始
MeetingWSEventScreenStopped = "meeting.screen.stopped" // S→C屏幕共享停止
// ====== 会议录制Phase B 引入2 个,仅 S→C======
// 启动 / 停止由 REST 接口POST /meetings/:code/recordings、DELETE …)触发,
// service 层落 DB + 调 media-server 后通过下面两个事件广播给会议内所有活跃成员:
// - started让所有客户端在 UI 上显示"正在录制"指示器
// - stopped附带最终状态ready / failed+ 文件 URLready 时),
// 便于前端立即跳转到回放或提示失败
MeetingWSEventRecordingStarted = "meeting.recording.started" // S→C录制开始
MeetingWSEventRecordingStopped = "meeting.recording.stopped" // S→C录制结束
)
// MeetingWSClientEvents 客户端可主动发起的 WS 事件C→S白名单
// 用于 WS 消息路由时快速校验事件名合法性
var MeetingWSClientEvents = []string{
MeetingWSEventRoomJoin,
MeetingWSEventRoomLeave,
MeetingWSEventMemberStateChange,
MeetingWSEventTransportCreate,
MeetingWSEventTransportConnect,
MeetingWSEventProduceStart,
MeetingWSEventConsumeStart,
MeetingWSEventConsumeResume,
MeetingWSEventProducerClose,
}