diff --git a/internal/api/router.go b/internal/api/router.go index b71be4ba6..7e3c6a669 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -159,6 +159,11 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co userRouterGroup.POST("/update_notification_account", u.UpdateNotificationAccountInfo) userRouterGroup.POST("/search_notification_account", u.SearchNotificationAccount) + // 手机号可见性设置(所有人/仅好友/隐藏) + userRouterGroup.POST("/set_phone_visibility", u.SetPhoneVisibility) + userRouterGroup.POST("/set_call_accept_setting", u.SetCallAcceptSetting) + userRouterGroup.POST("/set_msg_receive_setting", u.SetMsgReceiveSetting) + // 全局黑名单管理(仅管理员) userRouterGroup.POST("/add_global_blacklist", bl.AddGlobalBlacklist) userRouterGroup.POST("/remove_global_blacklist", bl.RemoveGlobalBlacklist) diff --git a/internal/api/user.go b/internal/api/user.go index a43766860..8482bf59f 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -305,3 +305,15 @@ func (u *UserApi) UpdateNotificationAccountInfo(c *gin.Context) { func (u *UserApi) SearchNotificationAccount(c *gin.Context) { a2r.Call(c, user.UserClient.SearchNotificationAccount, u.Client) } + +func (u *UserApi) SetPhoneVisibility(c *gin.Context) { + a2r.Call(c, user.UserClient.SetPhoneVisibility, u.Client) +} + +func (u *UserApi) SetCallAcceptSetting(c *gin.Context) { + a2r.Call(c, user.UserClient.SetCallAcceptSetting, u.Client) +} + +func (u *UserApi) SetMsgReceiveSetting(c *gin.Context) { + a2r.Call(c, user.UserClient.SetMsgReceiveSetting, u.Client) +} diff --git a/internal/rpc/group/convert.go b/internal/rpc/group/convert.go index 8026430c3..e1236bc0f 100644 --- a/internal/rpc/group/convert.go +++ b/internal/rpc/group/convert.go @@ -38,6 +38,10 @@ func (s *groupServer) groupDB2PB(group *model.Group, ownerUserID string, memberC ApplyMemberFriend: group.ApplyMemberFriend, NotificationUpdateTime: group.NotificationUpdateTime.UnixMilli(), NotificationUserID: group.NotificationUserID, + AllowSendMsg: group.AllowSendMsg, + AllowPinMsg: group.AllowPinMsg, + AllowAddMember: group.AllowAddMember, + AllowEditGroupInfo: group.AllowEditGroupInfo, } } diff --git a/internal/rpc/group/db_map.go b/internal/rpc/group/db_map.go index 7504bc851..36bb1de16 100644 --- a/internal/rpc/group/db_map.go +++ b/internal/rpc/group/db_map.go @@ -53,6 +53,18 @@ func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[s if group.Ex != nil { m["ex"] = group.Ex.Value } + if group.AllowSendMsg != nil { + m["allow_send_msg"] = group.AllowSendMsg.Value + } + if group.AllowPinMsg != nil { + m["allow_pin_msg"] = group.AllowPinMsg.Value + } + if group.AllowAddMember != nil { + m["allow_add_member"] = group.AllowAddMember.Value + } + if group.AllowEditGroupInfo != nil { + m["allow_edit_group_info"] = group.AllowEditGroupInfo.Value + } return m } @@ -100,6 +112,22 @@ func UpdateGroupInfoExMap(ctx context.Context, group *pbgroup.SetGroupInfoExReq) m["ex"] = group.Ex.Value normalFlag = true } + if group.AllowSendMsg != nil { + m["allow_send_msg"] = group.AllowSendMsg.Value + normalFlag = true + } + if group.AllowPinMsg != nil { + m["allow_pin_msg"] = group.AllowPinMsg.Value + normalFlag = true + } + if group.AllowAddMember != nil { + m["allow_add_member"] = group.AllowAddMember.Value + normalFlag = true + } + if group.AllowEditGroupInfo != nil { + m["allow_edit_group_info"] = group.AllowEditGroupInfo.Value + normalFlag = true + } return m, normalFlag, groupNameFlag, notificationFlag, nil } diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index c7880d360..acb816831 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -458,6 +458,11 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite if err := s.PopulateGroupMember(ctx, groupMember); err != nil { return nil, err } + // AllowAddMember == 1 时仅群主/管理员可拉人 + isOwnerOrAdmin := groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin + if group.AllowAddMember == model.GroupPermAdminOnly && !isOwnerOrAdmin { + return nil, errs.ErrNoPermission.WrapMsg("only owner or admin can add members to this group") + } } else { opUserID = mcontext.GetOpUserID(ctx) } @@ -1098,8 +1103,22 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf if err != nil { return nil, err } - if !(opMember.RoleLevel == constant.GroupOwner || opMember.RoleLevel == constant.GroupAdmin) { - return nil, errs.ErrNoPermission.WrapMsg("no group owner or admin") + isOwnerOrAdmin := opMember.RoleLevel == constant.GroupOwner || opMember.RoleLevel == constant.GroupAdmin + requestsPermField := req.GroupInfoForSet.AllowSendMsg != nil || + req.GroupInfoForSet.AllowPinMsg != nil || + req.GroupInfoForSet.AllowAddMember != nil || + req.GroupInfoForSet.AllowEditGroupInfo != nil + if requestsPermField && !isOwnerOrAdmin { + return nil, errs.ErrNoPermission.WrapMsg("only owner or admin can change group permission settings") + } + if !isOwnerOrAdmin { + grp, err := s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID) + if err != nil { + return nil, err + } + if grp.AllowEditGroupInfo == model.GroupPermAdminOnly { + return nil, errs.ErrNoPermission.WrapMsg("only owner or admin can edit group info") + } } if err := s.PopulateGroupMember(ctx, opMember); err != nil { return nil, err @@ -1193,9 +1212,24 @@ func (s *groupServer) SetGroupInfoEx(ctx context.Context, req *pbgroup.SetGroupI if err != nil { return nil, err } - - if !(opMember.RoleLevel == constant.GroupOwner || opMember.RoleLevel == constant.GroupAdmin) { - return nil, errs.ErrNoPermission.WrapMsg("no group owner or admin") + isOwnerOrAdmin := opMember.RoleLevel == constant.GroupOwner || opMember.RoleLevel == constant.GroupAdmin + // 4个群权限字段始终只有群主/管理员可修改 + requestsPermField := req.AllowSendMsg != nil || + req.AllowPinMsg != nil || + req.AllowAddMember != nil || + req.AllowEditGroupInfo != nil + if requestsPermField && !isOwnerOrAdmin { + return nil, errs.ErrNoPermission.WrapMsg("only owner or admin can change group permission settings") + } + // 其他字段:按 AllowEditGroupInfo 决定是否允许普通成员操作 + if !isOwnerOrAdmin { + grp, err := s.db.TakeGroup(ctx, req.GroupID) + if err != nil { + return nil, err + } + if grp.AllowEditGroupInfo == model.GroupPermAdminOnly { + return nil, errs.ErrNoPermission.WrapMsg("only owner or admin can edit group info") + } } if err := s.PopulateGroupMember(ctx, opMember); err != nil { diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index f6c3147ba..d6e1ea6a2 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -59,9 +59,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe data.MsgData.ContentType >= constant.NotificationBegin { return nil } - if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, data); err != nil { - return err - } + // 先做本地轻量级拦截(黑名单 + 消息接收权限),避免不必要的 webhook 触发 black, err := m.FriendLocalCache.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err @@ -69,6 +67,33 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if black { return servererrs.ErrBlockedByPeer.Wrap() } + // 校验接收方消息接收权限(MsgReceiveSetting) + // 0=所有人可发送,1=仅好友可发送,2=所有人不可发送 + recvUserInfo, err := m.UserLocalCache.GetUserInfo(ctx, data.MsgData.RecvID) + if err != nil { + return err + } + switch recvUserInfo.MsgReceiveSetting { + case 2: // MsgReceiveSettingNobody + return servererrs.ErrMsgReceiveNotAllowed.Wrap() + case 1: // MsgReceiveSettingFriends + isFriend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.RecvID, data.MsgData.SendID) + if err != nil { + return err + } + if !isFriend { + return servererrs.ErrMsgReceiveNotAllowed.Wrap() + } + // 已确认是好友,触发 webhook 后放行,不做 FriendVerify 冗余查询 + if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, data); err != nil { + return err + } + return nil + } + // MsgReceiveSetting==0(所有人可发),触发 webhook,再按全局 FriendVerify 兜底 + if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, data); err != nil { + return err + } if m.config.RpcConfig.FriendVerify { friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { @@ -77,7 +102,6 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if !friend { return servererrs.ErrNotPeersFriend.Wrap() } - return nil } return nil case constant.ReadGroupChatType: @@ -124,6 +148,10 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin { return servererrs.ErrMutedGroup.Wrap() } + // AllowSendMsg == 1 时仅群主/管理员可发消息 + if groupInfo.AllowSendMsg == 1 && groupMemberInfo.RoleLevel != constant.GroupAdmin { + return servererrs.ErrNoPermission.WrapMsg("only owner or admin can send messages in this group") + } } return nil default: diff --git a/internal/rpc/rtc/server.go b/internal/rpc/rtc/server.go index 3512afa4f..2877817d2 100644 --- a/internal/rpc/rtc/server.go +++ b/internal/rpc/rtc/server.go @@ -39,12 +39,13 @@ type Config struct { type rtcServer struct { rtc.UnimplementedRtcServiceServer - config *Config - db controller.RtcDatabase - roomClient *lksdk.RoomServiceClient - msgClient *rpcli.MsgClient - userClient *rpcli.UserClient - tokenExpiry time.Duration + config *Config + db controller.RtcDatabase + roomClient *lksdk.RoomServiceClient + msgClient *rpcli.MsgClient + userClient *rpcli.UserClient + relationClient *rpcli.RelationClient + tokenExpiry time.Duration } // Start initialises the RTC gRPC service and registers it with the gRPC server. @@ -69,6 +70,11 @@ func Start(ctx context.Context, cfg *Config, client discovery.SvcDiscoveryRegist return err } + friendConn, err := client.GetConn(ctx, cfg.Share.RpcRegisterName.Friend) + if err != nil { + return err + } + lk := cfg.RpcConfig.LiveKit roomClient := lksdk.NewRoomServiceClient(lk.InternalAddress, lk.APIKey, lk.APISecret) @@ -78,12 +84,13 @@ func Start(ctx context.Context, cfg *Config, client discovery.SvcDiscoveryRegist } s := &rtcServer{ - config: cfg, - db: controller.NewRtcDatabase(signalDB), - roomClient: roomClient, - msgClient: rpcli.NewMsgClient(msgConn), - userClient: rpcli.NewUserClient(userConn), - tokenExpiry: tokenExpiry, + config: cfg, + db: controller.NewRtcDatabase(signalDB), + roomClient: roomClient, + msgClient: rpcli.NewMsgClient(msgConn), + userClient: rpcli.NewUserClient(userConn), + relationClient: rpcli.NewRelationClient(friendConn), + tokenExpiry: tokenExpiry, } rtc.RegisterRtcServiceServer(server, s) diff --git a/internal/rpc/rtc/signal.go b/internal/rpc/rtc/signal.go index 02d7c077f..69551d57f 100644 --- a/internal/rpc/rtc/signal.go +++ b/internal/rpc/rtc/signal.go @@ -99,6 +99,18 @@ func (s *rtcServer) handleInvite(ctx context.Context, req *rtc.SignalInviteReq, inv.InviterUserID = req.UserID inv.InitiateTime = time.Now().UnixMilli() + // 校验每位被邀请者的通话接受权限,1-to-1 场景:有任一被邀请者拒绝则直接返错 + for _, inviteeID := range inv.InviteeUserIDList { + allowed, err := s.isCallAllowed(ctx, req.UserID, inviteeID) + if err != nil { + log.ZError(ctx, "handleInvite: isCallAllowed failed", err, "inviteeID", inviteeID) + return nil, err + } + if !allowed { + return nil, errs.ErrNoPermission.WrapMsg("the invitee does not accept calls from you", "inviteeID", inviteeID) + } + } + if _, err := s.roomClient.CreateRoom(ctx, &livekit.CreateRoomRequest{Name: inv.RoomID}); err != nil { log.ZError(ctx, "handleInvite", err, "r", err.Error()) return nil, errs.WrapMsg(err, "LiveKit CreateRoom failed", "roomID", inv.RoomID) @@ -157,6 +169,15 @@ func (s *rtcServer) handleInviteInGroup(ctx context.Context, req *rtc.SignalInvi content := marshalSignalReq(signalReq) for _, inviteeID := range inv.InviteeUserIDList { + allowed, err := s.isCallAllowed(ctx, req.UserID, inviteeID) + if err != nil { + log.ZWarn(ctx, "handleInviteInGroup: isCallAllowed failed, skipping invitee", err, "inviteeID", inviteeID) + continue + } + if !allowed { + log.ZInfo(ctx, "handleInviteInGroup: skipping invitee (call setting blocked)", "inviteeID", inviteeID) + continue + } if err := s.sendSignalingNotification(ctx, req.UserID, inviteeID, int32(constant.ReadGroupChatType), req.OfflinePushInfo, content); err != nil { log.ZWarn(ctx, "sendSignalingNotification to group invitee failed", err, "inviteeID", inviteeID) } @@ -169,6 +190,30 @@ func (s *rtcServer) handleInviteInGroup(ctx context.Context, req *rtc.SignalInvi }, nil } +// isCallAllowed 判断 inviterID 是否被允许向 inviteeID 发起音视频通话。 +// 规则: +// - CallAcceptSettingPublic(0) → 所有人均可 +// - CallAcceptSettingFriends(1) → 仅当 inviterID 在 inviteeID 好友列表中 +// - CallAcceptSettingNobody(2) → 任何人均不可 +func (s *rtcServer) isCallAllowed(ctx context.Context, inviterID, inviteeID string) (bool, error) { + userInfo, err := s.userClient.GetUserInfo(ctx, inviteeID) + if err != nil { + return false, err + } + switch userInfo.CallAcceptSetting { + case model.CallAcceptSettingNobody: + return false, nil + case model.CallAcceptSettingFriends: + isFriend, err := s.relationClient.IsFriend(ctx, inviteeID, inviterID) + if err != nil { + return false, err + } + return isFriend, nil + default: // CallAcceptSettingPublic + return true, nil + } +} + // handleAccept processes a call acceptance. func (s *rtcServer) handleAccept(ctx context.Context, req *rtc.SignalAcceptReq, signalReq *rtc.SignalReq) (*rtc.SignalAcceptResp, error) { inv := req.Invitation diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 777538925..d84b27d3a 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -35,6 +35,7 @@ import ( "github.com/openimsdk/protocol/group" friendpb "github.com/openimsdk/protocol/relation" "github.com/openimsdk/tools/db/redisutil" + "github.com/openimsdk/tools/mcontext" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" @@ -149,10 +150,50 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig return nil, servererrs.ErrUserBlocked.WrapMsg("user is banned", "userIDs", bannedIDs) } - resp.UsersInfo = convert.UsersDB2Pb(users) + pbUsers := convert.UsersDB2Pb(users) + viewerID := mcontext.GetOpUserID(ctx) + if err := s.applyPhoneVisibility(ctx, viewerID, pbUsers, users); err != nil { + return nil, err + } + resp.UsersInfo = pbUsers return resp, nil } +// applyPhoneVisibility 根据 phone_visibility 和好友关系决定是否下发明文手机号。 +// pbUsers 与 dbUsers 下标一一对应。 +func (s *userServer) applyPhoneVisibility(ctx context.Context, viewerID string, pbUsers []*sdkws.UserInfo, dbUsers []*tablerelation.User) error { + for i, db := range dbUsers { + pb := pbUsers[i] + if db.Phone == "" { + // 未设置手机号,直接跳过 + continue + } + switch db.PhoneVisibility { + case tablerelation.PhoneVisibilityPublic: + // 所有人可见,保留 phone 字段(已由 UserDB2Pb 填充) + case tablerelation.PhoneVisibilityHidden: + // 完全隐藏:即使本人也不通过此接口暴露,客户端自行从个人设置接口获取 + pb.Phone = "" + case tablerelation.PhoneVisibilityFriends: + // 仅好友可见 + if viewerID == db.UserID { + // 本人始终可见 + break + } + isFriend, err := s.relationClient.IsFriend(ctx, viewerID, db.UserID) + if err != nil { + return err + } + if !isFriend { + pb.Phone = "" + } + default: + pb.Phone = "" + } + } + return nil +} + // deprecated: // UpdateUserInfo func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) { @@ -237,6 +278,82 @@ func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Se return resp, nil } +// SetPhoneVisibility 设置手机号及其可见性(0=所有人,1=仅好友,2=隐藏)。 +// 只允许本人或管理员操作。 +func (s *userServer) SetPhoneVisibility(ctx context.Context, req *pbuser.SetPhoneVisibilityReq) (*pbuser.SetPhoneVisibilityResp, error) { + if req.UserID == "" { + return nil, errs.ErrArgs.WrapMsg("userID is required") + } + if req.PhoneVisibility < 0 || req.PhoneVisibility > 2 { + return nil, errs.ErrArgs.WrapMsg("phoneVisibility must be 0, 1 or 2") + } + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil { + return nil, err + } + if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil { + return nil, err + } + m := map[string]any{ + "phone_visibility": req.PhoneVisibility, + } + if req.Phone != "" { + m["phone"] = req.Phone + } + if err := s.db.UpdateByMap(ctx, req.UserID, m); err != nil { + return nil, err + } + s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID) + return &pbuser.SetPhoneVisibilityResp{}, nil +} + +// SetCallAcceptSetting 设置音视频通话接受权限(0=所有人,1=仅好友,2=不接受任何通话)。 +// 只允许本人或管理员操作。 +func (s *userServer) SetCallAcceptSetting(ctx context.Context, req *pbuser.SetCallAcceptSettingReq) (*pbuser.SetCallAcceptSettingResp, error) { + if req.UserID == "" { + return nil, errs.ErrArgs.WrapMsg("userID is required") + } + if req.CallAcceptSetting < 0 || req.CallAcceptSetting > 2 { + return nil, errs.ErrArgs.WrapMsg("callAcceptSetting must be 0, 1 or 2") + } + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil { + return nil, err + } + if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil { + return nil, err + } + if err := s.db.UpdateByMap(ctx, req.UserID, map[string]any{ + "call_accept_setting": req.CallAcceptSetting, + }); err != nil { + return nil, err + } + s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID) + return &pbuser.SetCallAcceptSettingResp{}, nil +} + +// SetMsgReceiveSetting 设置会话消息接收权限(0=所有人,1=仅好友,2=所有人不可发送)。 +// 只允许本人或管理员操作。 +func (s *userServer) SetMsgReceiveSetting(ctx context.Context, req *pbuser.SetMsgReceiveSettingReq) (*pbuser.SetMsgReceiveSettingResp, error) { + if req.UserID == "" { + return nil, errs.ErrArgs.WrapMsg("userID is required") + } + if req.MsgReceiveSetting < 0 || req.MsgReceiveSetting > 2 { + return nil, errs.ErrArgs.WrapMsg("msgReceiveSetting must be 0, 1 or 2") + } + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil { + return nil, err + } + if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil { + return nil, err + } + if err := s.db.UpdateByMap(ctx, req.UserID, map[string]any{ + "msg_receive_setting": req.MsgReceiveSetting, + }); err != nil { + return nil, err + } + s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID) + return &pbuser.SetMsgReceiveSettingResp{}, nil +} + func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckReq) (resp *pbuser.AccountCheckResp, err error) { resp = &pbuser.AccountCheckResp{} if datautil.Duplicate(req.CheckUserIDs) { diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go index d824fa68e..1130d81e2 100644 --- a/pkg/common/convert/user.go +++ b/pkg/common/convert/user.go @@ -31,6 +31,10 @@ func UserDB2Pb(user *relationtb.User) *sdkws.UserInfo { CreateTime: user.CreateTime.UnixMilli(), AppMangerLevel: user.AppMangerLevel, GlobalRecvMsgOpt: user.GlobalRecvMsgOpt, + Phone: user.Phone, + PhoneVisibility: user.PhoneVisibility, + CallAcceptSetting: user.CallAcceptSetting, + MsgReceiveSetting: user.MsgReceiveSetting, } } @@ -90,6 +94,18 @@ func UserPb2DBMapEx(user *sdkws.UserInfoWithEx) map[string]any { if user.GlobalRecvMsgOpt != nil { val["global_recv_msg_opt"] = user.GlobalRecvMsgOpt.Value } + if user.Phone != nil { + val["phone"] = user.Phone.Value + } + if user.PhoneVisibility != nil { + val["phone_visibility"] = user.PhoneVisibility.Value + } + if user.CallAcceptSetting != nil { + val["call_accept_setting"] = user.CallAcceptSetting.Value + } + if user.MsgReceiveSetting != nil { + val["msg_receive_setting"] = user.MsgReceiveSetting.Value + } return val } diff --git a/pkg/common/servererrs/code.go b/pkg/common/servererrs/code.go index 9e4fe9129..7c8fcf616 100644 --- a/pkg/common/servererrs/code.go +++ b/pkg/common/servererrs/code.go @@ -76,6 +76,7 @@ const ( MutedInGroup = 1402 // Member muted in the group MutedGroup = 1403 // Group is muted MsgAlreadyRevoke = 1404 // Message already revoked + MsgReceiveNotAllowed = 1405 // Recipient does not allow receiving messages from this sender // Token error codes. TokenExpiredError = 1501 diff --git a/pkg/common/servererrs/predefine.go b/pkg/common/servererrs/predefine.go index ed44817a2..08debfb86 100644 --- a/pkg/common/servererrs/predefine.go +++ b/pkg/common/servererrs/predefine.go @@ -59,6 +59,7 @@ var ( ErrMutedInGroup = errs.NewCodeError(MutedInGroup, "MutedInGroup") ErrMutedGroup = errs.NewCodeError(MutedGroup, "MutedGroup") ErrMsgAlreadyRevoke = errs.NewCodeError(MsgAlreadyRevoke, "MsgAlreadyRevoke") + ErrMsgReceiveNotAllowed = errs.NewCodeError(MsgReceiveNotAllowed, "MsgReceiveNotAllowed") ErrConnOverMaxNumLimit = errs.NewCodeError(ConnOverMaxNumLimit, "ConnOverMaxNumLimit") diff --git a/pkg/common/storage/model/group.go b/pkg/common/storage/model/group.go index 714fcc782..b34cabd83 100644 --- a/pkg/common/storage/model/group.go +++ b/pkg/common/storage/model/group.go @@ -18,6 +18,13 @@ import ( "time" ) +// GroupPermission 群组操作权限枚举。 +// 0=全员可操作(默认),1=仅群主/管理员可操作 +const ( + GroupPermAllMember = int32(0) // 全员均可 + GroupPermAdminOnly = int32(1) // 仅群主/管理员 +) + type Group struct { GroupID string `bson:"group_id"` GroupName string `bson:"group_name"` @@ -34,4 +41,12 @@ type Group struct { ApplyMemberFriend int32 `bson:"apply_member_friend"` NotificationUpdateTime time.Time `bson:"notification_update_time"` NotificationUserID string `bson:"notification_user_id"` + // AllowSendMsg 0=全员可发消息 1=仅群主/管理员可发消息 + AllowSendMsg int32 `bson:"allow_send_msg"` + // AllowPinMsg 0=全员可置顶消息 1=仅群主/管理员可置顶消息 + AllowPinMsg int32 `bson:"allow_pin_msg"` + // AllowAddMember 0=全员可拉人入群 1=仅群主/管理员可拉人入群 + AllowAddMember int32 `bson:"allow_add_member"` + // AllowEditGroupInfo 0=全员可编辑群资料 1=仅群主/管理员可编辑群资料 + AllowEditGroupInfo int32 `bson:"allow_edit_group_info"` } diff --git a/pkg/common/storage/model/user.go b/pkg/common/storage/model/user.go index f64d09e79..3903316e0 100644 --- a/pkg/common/storage/model/user.go +++ b/pkg/common/storage/model/user.go @@ -18,6 +18,30 @@ import ( "time" ) +// PhoneVisibility 手机号可见性枚举。 +// 0=所有人可见, 1=仅好友可见, 2=完全隐藏 +const ( + PhoneVisibilityPublic int32 = 0 + PhoneVisibilityFriends int32 = 1 + PhoneVisibilityHidden int32 = 2 +) + +// CallAcceptSetting 音视频通话接受权限枚举。 +// 0=所有人可发起, 1=仅好友可发起, 2=不接受任何通话 +const ( + CallAcceptSettingPublic int32 = 0 + CallAcceptSettingFriends int32 = 1 + CallAcceptSettingNobody int32 = 2 +) + +// MsgReceiveSetting 会话消息接收权限枚举。 +// 0=所有人可发送, 1=仅好友可发送, 2=所有人不可发送 +const ( + MsgReceiveSettingPublic int32 = 0 + MsgReceiveSettingFriends int32 = 1 + MsgReceiveSettingNobody int32 = 2 +) + type User struct { UserID string `bson:"user_id"` Nickname string `bson:"nickname"` @@ -26,6 +50,14 @@ type User struct { AppMangerLevel int32 `bson:"app_manger_level"` GlobalRecvMsgOpt int32 `bson:"global_recv_msg_opt"` CreateTime time.Time `bson:"create_time"` + // Phone 用户手机号(明文,仅服务端留存,下发时按 PhoneVisibility 过滤) + Phone string `bson:"phone"` + // PhoneVisibility 0=所有人可见 1=仅好友可见 2=隐藏 + PhoneVisibility int32 `bson:"phone_visibility"` + // CallAcceptSetting 0=所有人可发起 1=仅好友可发起 2=不接受任何通话 + CallAcceptSetting int32 `bson:"call_accept_setting"` + // MsgReceiveSetting 0=所有人可发送 1=仅好友可发送 2=所有人不可发送 + MsgReceiveSetting int32 `bson:"msg_receive_setting"` } func (u *User) GetNickname() string { diff --git a/pkg/rpcli/relation.go b/pkg/rpcli/relation.go index dce0e7165..ac4d60ef2 100644 --- a/pkg/rpcli/relation.go +++ b/pkg/rpcli/relation.go @@ -21,3 +21,15 @@ func (x *RelationClient) GetFriendsInfo(ctx context.Context, ownerUserID string, req := &relation.GetFriendInfoReq{OwnerUserID: ownerUserID, FriendUserIDs: friendUserIDs} return extractField(ctx, x.FriendClient.GetFriendInfo, req, (*relation.GetFriendInfoResp).GetFriendInfos) } + +// IsFriend checks whether userID2 is in userID1's friend list. +func (x *RelationClient) IsFriend(ctx context.Context, ownerUserID, friendUserID string) (bool, error) { + resp, err := x.FriendClient.IsFriend(ctx, &relation.IsFriendReq{ + UserID1: ownerUserID, + UserID2: friendUserID, + }) + if err != nil { + return false, err + } + return resp.InUser1Friends, nil +}