117 lines
3.4 KiB
Go
117 lines
3.4 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
|
||
roomv1 "hyapp.local/api/proto/room/v1"
|
||
"hyapp/pkg/appcode"
|
||
)
|
||
|
||
// CheckSpeakPermission 给腾讯云 IM 回调或 gateway 提供同步发言守卫。
|
||
func (s *Service) CheckSpeakPermission(ctx context.Context, req *roomv1.CheckSpeakPermissionRequest) (*roomv1.CheckSpeakPermissionResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
closed, err := s.isRoomClosed(ctx, req.GetRoomId())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if closed {
|
||
return &roomv1.CheckSpeakPermissionResponse{
|
||
Allowed: false,
|
||
Reason: "room_closed",
|
||
}, nil
|
||
}
|
||
|
||
// 守卫查询优先读内存 Cell;没有 Cell 时从快照读取,避免外部 IM 入口越权自判。
|
||
snapshot, err := s.currentSnapshot(ctx, req.GetRoomId())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if snapshot == nil {
|
||
// 没有快照代表 room-service 不认识该房间。
|
||
return &roomv1.CheckSpeakPermissionResponse{
|
||
Allowed: false,
|
||
Reason: "room_not_found",
|
||
}, nil
|
||
}
|
||
|
||
nowMS := s.clock.Now().UnixMilli()
|
||
banned := snapshotUserBanned(snapshot, req.GetUserId(), nowMS)
|
||
// 允许发言必须同时满足未 ban、未 mute、房间开启聊天、用户在业务 presence 内。
|
||
if !banned && !containsID(snapshot.GetMuteUserIds(), req.GetUserId()) && snapshot.GetChatEnabled() && findProtoUser(snapshot, req.GetUserId()) != nil {
|
||
return &roomv1.CheckSpeakPermissionResponse{
|
||
Allowed: true,
|
||
RoomVersion: snapshot.GetVersion(),
|
||
}, nil
|
||
}
|
||
|
||
reason := "not_in_room"
|
||
switch {
|
||
case banned:
|
||
// ban 优先级最高,被踢用户即使仍在腾讯云 IM 群内也不能发言。
|
||
reason = "user_banned"
|
||
case containsID(snapshot.GetMuteUserIds(), req.GetUserId()):
|
||
reason = "user_muted"
|
||
case !snapshot.GetChatEnabled():
|
||
reason = "chat_disabled"
|
||
}
|
||
|
||
return &roomv1.CheckSpeakPermissionResponse{
|
||
Allowed: false,
|
||
Reason: reason,
|
||
RoomVersion: snapshot.GetVersion(),
|
||
}, nil
|
||
}
|
||
|
||
// VerifyRoomPresence 给外部 IM 入口提供进群前的同步 presence 守卫。
|
||
func (s *Service) VerifyRoomPresence(ctx context.Context, req *roomv1.VerifyRoomPresenceRequest) (*roomv1.VerifyRoomPresenceResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
// 腾讯云 IM 进群前必须通过该守卫,不能只依赖客户端先调用过 JoinRoom。
|
||
closed, err := s.isRoomClosed(ctx, req.GetRoomId())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if closed {
|
||
return &roomv1.VerifyRoomPresenceResponse{
|
||
Present: false,
|
||
Reason: "room_closed",
|
||
}, nil
|
||
}
|
||
|
||
snapshot, err := s.currentSnapshot(ctx, req.GetRoomId())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if snapshot == nil {
|
||
// 房间不存在时外部 IM 入口不应把用户加入房间频道。
|
||
return &roomv1.VerifyRoomPresenceResponse{
|
||
Present: false,
|
||
Reason: "room_not_found",
|
||
}, nil
|
||
}
|
||
|
||
if snapshotUserBanned(snapshot, req.GetUserId(), s.clock.Now().UnixMilli()) {
|
||
// ban 用户不能订阅,即使快照里还有旧 presence 也以 ban 为准。
|
||
return &roomv1.VerifyRoomPresenceResponse{
|
||
Present: false,
|
||
Reason: "user_banned",
|
||
RoomVersion: snapshot.GetVersion(),
|
||
}, nil
|
||
}
|
||
|
||
if findProtoUser(snapshot, req.GetUserId()) == nil {
|
||
// 没有 room-service presence 的用户不能偷收房间消息。
|
||
return &roomv1.VerifyRoomPresenceResponse{
|
||
Present: false,
|
||
Reason: "not_in_room",
|
||
RoomVersion: snapshot.GetVersion(),
|
||
}, nil
|
||
}
|
||
|
||
return &roomv1.VerifyRoomPresenceResponse{
|
||
Present: true,
|
||
RoomVersion: snapshot.GetVersion(),
|
||
}, nil
|
||
}
|