Files
EchoChat/backend/go-service/app/meeting/service/interfaces.go
bujinyuan e235e001b3 feat(phase2e-2): Go meeting 模块 12 个 REST 接口业务逻辑全量落地(Task 5)
主要产出:
- DTO 层:app/dto/meeting_dto.go 完整定义 13 个 DTO(请求/响应/基础共用)
- Service 层:MeetingService 12 业务方法 + 11 个 sentinel 错误 + 4 辅助
  - CreateRoom/JoinRoom/LeaveRoom/EndRoom 核心生命周期
  - KickMember/TransferHost/GetRoom/ListMyMeetings 会议管理
  - InviteUsers(+ NotifyPusher)/ RedeemInviteToken 邀请链路
  - SendChat/ListChats 会议内聊天
- Controller 层:12 handler + handleError 领域错误 → HTTP 映射
- 工具层:pkg/utils/meeting_code.go(XXX-XXX-XXX + invite token)
- Stub 接口:MediaOrchestrator(Task 7 替换)+ NoopMediaOrchestrator
- 路径修正:/rooms → /rooms/mine、/invites → /invite-tokens 对齐设计
- DAO 契约修复:GetByID/GetByCode/GetByRoomAndUser/FindActiveByUser
  将 gorm.ErrRecordNotFound 转为 (nil, nil),service 统一 nil 判定
- 安全强化:密码 bcrypt + 5 次错误锁 10 分钟;邀请 token 仅通过
  NotifyPusher.Extra 定向下发,响应不回传
- host 自动转让:host 离会时将最早加入者提升为 host
- 单点参会:用户同一时间仅能在一个活跃会议
- API 文档:docs/api/frontend/meeting.md 重写为 12 接口完整规范
- Wire 依赖注入:MediaOrchestrator + NoopMediaOrchestrator provider

验证:
- go build / go vet / wire 零告警
- 端到端 3 用户场景:12 happy path + 5 错误路径 PASS=19 / FAIL=0
  覆盖密码错/房间不存在/单点冲突/越权/邀请失效

文档同步:
- docs/progress/CURRENT_STATUS.md 增补 Task 5 章节
- .cursor/rules/project-context.mdc 更新阶段状态
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md 标记 T5 

下一步:Task 6(WS 信令协议 + BroadcastToMeeting 替换 PublishToUser 循环)

Made-with: Cursor
2026-04-21 16:39:59 +08:00

67 lines
2.9 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 service 提供 meeting 模块的业务逻辑
package service
import (
"context"
authModel "github.com/echochat/backend/app/auth/model"
notifyService "github.com/echochat/backend/app/notify/service"
)
// NotifyPusher 通知推送接口
// 由 notify.service.NotifyService 隐式实现,供 meeting 模块注入使用
// 会议邀请、主持人变更、踢出会议等事件通过此接口落地通知中心
type NotifyPusher interface {
Push(ctx context.Context, payload *notifyService.PushPayload)
PushBatch(ctx context.Context, payloads []*notifyService.PushPayload)
}
// UserInfoResolver 查询用户昵称 / 头像的接口
// 由 contact.FriendshipDAO 隐式实现(已有 GetUsersByIDs 方法)
// 用于拉取会议邀请中主持人信息、成员列表头像等
type UserInfoResolver interface {
GetUsersByIDs(ctx context.Context, userIDs []int64) ([]authModel.User, error)
}
// OnlineChecker 查询用户在线状态的接口
// 由 ws.OnlineService 隐式实现(签名对齐现有实现:不返回 error查询失败按离线处理
// 用于会议邀请时判断收件人是否在线(离线则降级为仅通知入库,上线时通过未读补偿推送)
type OnlineChecker interface {
IsOnline(ctx context.Context, userID int64) bool
}
// MediaOrchestrator 媒体服务器编排接口
// Task 7 落地 Go → Node media-server HTTP Client 后由 node_client.NodeClient 实现
// Task 5 阶段使用 NoopMediaOrchestrator 占位,仅返回假的 RouterID不产生真实媒体资源
// 语义:会议房间在 Go 侧落库后,通过该接口驱动 Node 端 mediasoup Router 创建与销毁
type MediaOrchestrator interface {
// CreateRouter 为会议房间创建 mediasoup Router
// 入参room_code 作为 Node 端聚合键;返回 Router ID 与推荐编解码参数Task 7 对接时补充)
CreateRouter(ctx context.Context, roomCode string) (routerID string, err error)
// CloseRouter 关闭会议房间对应的 mediasoup Router 及其下所有 transport/producer/consumer
// 幂等:重复关闭不返回错误
CloseRouter(ctx context.Context, roomCode string) error
}
// NoopMediaOrchestrator 占位实现Task 5 完成生命周期接口时使用
// 返回伪造的 RouterID所有操作仅写日志不调用 Node
// Task 7 完成后全局 wire 切换到真实 NodeClient 实现
type NoopMediaOrchestrator struct{}
// NewNoopMediaOrchestrator 构造占位的 MediaOrchestrator
func NewNoopMediaOrchestrator() *NoopMediaOrchestrator {
return &NoopMediaOrchestrator{}
}
// CreateRouter 返回以 "noop-router-" 为前缀的伪造 RouterID
// 调用方可据此区分真实 / 占位实现,便于调试与切换
func (n *NoopMediaOrchestrator) CreateRouter(_ context.Context, roomCode string) (string, error) {
return "noop-router-" + roomCode, nil
}
// CloseRouter 占位实现:直接返回 nil
func (n *NoopMediaOrchestrator) CloseRouter(_ context.Context, _ string) error {
return nil
}