2026-05-29 11:59:57 +08:00

117 lines
3.4 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
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
}