commit 9ef8b1b3ce363a4273fe7f56812229d46b032a55 Author: DengBiao <2319963317@qq.com> Date: Wed Nov 16 18:58:50 2022 +0800 first commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..e401820 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +## 简要介绍 +### 什么是 grpc ? +```azure +gRPC是rpc框架中的一种,是一个高性能,开源和通用的RPC框架,基于Protobuf序列化协议开发,且支持众多开发语言。 + +gRPC可以通俗的理解为google对于RPC的一种实现形式。 +``` + +### grpc 特性 +```azure +grpc可以跨语言使用。支持多种语言 支持C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP等编程语言 +基于 IDL ( 接口定义语言(Interface Define Language))文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub; +通信协议基于标准的 HTTP/2 设计,支持·双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量; +序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。 +安装简单,扩展方便(用该框架每秒可达到百万个RPC) +``` + +### 什么是 Protocol Buffers ? +```azure +gRPC默认使用protocl buffers,protoc buffers 是谷歌成熟的开源的用于结构化数据序列化的机制。 + +``` + +### 使用Protocol Buffers + +- 第一步:使用 .proto文件(以.proto为后缀的二进制文本文件)定义待序列化数据的结构 +```azure +message Person { + string name = 1; + int32 id = 2; + bool has_ponycopter = 3; +} +``` +- 第二步:使用 protocol buffer 的编译器protoc从proto定义生成选择语言的数据接入层类。 +```azure +// greeter 服务定义. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// 客户端请求消息包含用户名. +message HelloRequest { + string name = 1; +} + +// 服务端响应包含一条greeting消息 +message HelloReply { + string message = 1; +} +``` + +gRPC使用带有特殊gRPC插件的protoc来生成proto文件中的代码。 但是,使用gRPC插件,您可以生成gRPC客户端和服务器代码,以及用于填充,序列化和检索消息类型的常规protocol buffer 代码。你可以从Protocol Buffers文档中获取到更多有关protocol buffer的信息,并能够获取到如何获取安装和你选定语言相关的protoc。 + +### Protocol buffer 版本 + +``` +虽然protocol buffer 被开源用户使用已经有一段时间,但我们的示例使用了一种新的protocol buffer,称为proto3,它具有略微简化的语法,一些有用的新功能,并支持更多语言。 +proto3目前已经支持Java,C++,Python,Objective-C,C#,Ruby和JavaScript,也实现了对Go语言的支持。 +通常,虽然也可以使用proto2(当前默认protocol buffer版本),但建议将proto3与gRPC一起使用,因为它允许您使用全系列gRPC支持的语言, +并避免使用proto2客户端与使用proto3服务端通信时的兼容性问题,反之亦然。 +``` + diff --git a/gim/business.ext.proto b/gim/business.ext.proto new file mode 100644 index 0000000..b2c672c --- /dev/null +++ b/gim/business.ext.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +message Empty{} + +service BusinessExt { + // 登录 + rpc SignIn (SignInReq) returns (SignInResp); + // 获取用户信息 + rpc GetUser (GetUserReq) returns (GetUserResp); + // 更新用户信息 + rpc UpdateUser (UpdateUserReq) returns (Empty); + // 搜索用户(这里简单数据库实现,生产环境建议使用ES) + rpc SearchUser (SearchUserReq) returns (SearchUserResp); + // 上传文件至云端 + rpc CloudUploadFile (CloudUploadFileReq) returns (CloudUploadFileResp); +} + +message SignInReq { + string phone_number = 1; // 手机号 + string code = 2; // 验证码 + int64 device_id = 3; // 设备id + int64 master_id = 4; // 站长id +} +message SignInResp { + bool is_new = 1; // 是否是新用户 + int64 user_id = 2; // 用户id + string token = 3; // token + int64 master_id = 4; // 站长id +} +message CloudUploadFileReq { + string dir = 1; // 目录名 + string file_name = 2; // 上传原文件名称 + string file_size = 3; // 文件大小 +} +message CloudUploadFileResp { + string method = 1; // 请求方式 + string host = 2; // 域名 + string key = 3; // key + string token = 4; // token +} + +message User { + int64 user_id = 1; // 用户id + string nickname = 2; // 昵称 + int32 sex = 3; // 性别 + string avatar_url = 4; // 头像地址 + string extra = 5; // 附加字段 + int64 create_time = 6; // 创建时间 + int64 update_time = 7; // 更新时间 + int64 master_id = 8; // 更新时间 +} + +message GetUserReq { + int64 user_id = 1; // 用户id +} +message GetUserResp { + User user = 1; // 用户信息 +} + +message UpdateUserReq { + string nickname = 1; // 昵称 + int32 sex = 2; // 性别 + string avatar_url = 3; // 头像地址 + string extra = 4; // 附加字段 +} + +message SearchUserReq{ + string key = 1; + int64 master_id = 2; +} +message SearchUserResp{ + repeated User users = 1; +} + diff --git a/gim/business.int.proto b/gim/business.int.proto new file mode 100644 index 0000000..95ac3e5 --- /dev/null +++ b/gim/business.int.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +import "common.ext.proto"; +import "business.ext.proto"; + +service BusinessInt { + // 权限校验 + rpc Auth (AuthReq) returns (Empty); + // 批量获取用户信息 + rpc GetUser (GetUserReq) returns (GetUserResp); + // 批量获取用户信息 + rpc GetUsers (GetUsersReq) returns (GetUsersResp); +} + +message AuthReq { + int64 user_id = 1; + int64 device_id = 2; + string token = 3; +} + +message GetUsersReq { + map user_ids = 1; // 用户id +} +message GetUsersResp { + map users = 1; // 用户信息 +} diff --git a/gim/common.ext.proto b/gim/common.ext.proto new file mode 100644 index 0000000..62448cb --- /dev/null +++ b/gim/common.ext.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +message Empty{} \ No newline at end of file diff --git a/gim/connect.ext.proto b/gim/connect.ext.proto new file mode 100644 index 0000000..92f01e6 --- /dev/null +++ b/gim/connect.ext.proto @@ -0,0 +1,174 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +enum PackageType { + PT_UNKNOWN = 0; // 未知 + PT_SIGN_IN = 1; // 设备登录请求 + PT_SYNC = 2; // 消息同步触发 + PT_HEARTBEAT = 3; // 心跳 + PT_MESSAGE = 4; // 消息投递 + PT_SUBSCRIBE_ROOM = 5; // 订阅房间 +} + +/************************************消息体定义开始************************************/ +// 单条消息投递内容(估算大约100个字节),todo 通知栏提醒 +message Message { + Sender sender = 1; // 发送者 + ReceiverType receiver_type = 2; // 接收者类型,1:user;2:group + int64 receiver_id = 3; // 用户id或者群组id + repeated int64 to_user_ids = 4; // 需要@的用户id列表 + MessageType message_type = 5; // 消息类型 + bytes message_content = 6; // 消息内容 + int64 seq = 7; // 用户消息发送序列号 + int64 send_time = 8; // 消息发送时间戳,精确到毫秒 + MessageStatus status = 9; // 消息状态 +} + +message Sender { + SenderType sender_type = 1; // 发送者类型,1:系统,2:用户,3:第三方业务系统 + int64 sender_id = 2; // 发送者id + int64 device_id = 3; // 发送者设备id + string avatar_url = 4; // 昵称 + string nickname = 5; // 头像 + string extra = 6; // 扩展字段 +} + +// 消息类型 +enum MessageType { + MT_UNKNOWN = 0; // 未知 + MT_TEXT = 1; // 文本 + MT_FACE = 2; // 表情 + MT_VOICE = 3; // 语音消息 + MT_IMAGE = 4; // 图片 + MT_FILE = 5; // 文件 + MT_LOCATION = 6; // 地理位置 + MT_COMMAND = 7; // 指令推送 + MT_CUSTOM = 8; // 自定义 +} + +// 文本消息 +message Text { + string text = 1; // 文本消息内容 +} + +// 表情消息 +message Face { + int64 face_id = 1; + string face_url = 2; +} + +// 语音消息 +message Voice { + string id = 1; // 语音包id + int32 size = 2; // 语音包大小 + int32 duration = 3; // 语音时长 + string url = 4; // 语音文件URL +} + +// 图片消息 +message Image { + string id = 1; // 图片id + int32 width = 2; // 图片宽度 + int32 height = 3; // 图片长度 + string url = 4; // 图片URL + string thumbnail_url = 5; // 图片缩略图url +} + +// 文件消息 +message File { + int64 id = 12; // 文件id + string name = 13; // 文件名 + int64 size = 14; // 文件大小 + string url = 15; // 文件url +} + +// 地理位置消息 +message Location { + string desc = 1; // 描述 + double latitude = 2; // 经度 + double longitude = 3; // 纬度 +} + +// Command 指令推送,1000以下,IM内部用,1000以上,留给业务用 +message Command { + int32 code = 1; // 指令码 + bytes data = 2; // 数据内容 +} + +// 自定义消息 +message Custom { + string data = 1; // 自定义数据 +} + +/************************************消息体定义结束************************************/ + +// 上行数据 +message Input { + PackageType type = 1; // 包的类型 + int64 request_id = 2; // 请求id + bytes data = 3; // 数据 +} + +// 下行数据 +message Output { + PackageType type = 1; // 包的类型 + int64 request_id = 2; // 请求id + int32 code = 3; // 错误码 + string message = 4; // 错误信息 + bytes data = 5; // 数据 +} + +// 设备登录,package_type:1 +message SignInInput { + int64 device_id = 1; // 设备id + int64 user_id = 2; // 用户id + string token = 3; // 秘钥 +} + +// 消息同步请求,package_type:2 +message SyncInput { + int64 seq = 1; // 客户端已经同步的序列号 +} +// 消息同步响应,package_type:2 +message SyncOutput { + repeated Message messages = 1; // 消息列表 + bool has_more = 2; // 是否有更多数据 +} + +// 订阅房间请求 +message SubscribeRoomInput { + int64 room_id = 1; // 房间ID,如果为0,取消房间订阅 + int64 seq = 2; // 消息消息序列号, +} + +enum ReceiverType { + RT_UNKNOWN = 0; // 未知 + RT_USER = 1; // 用户 + RT_GROUP = 2; // 群组 + RT_ROOM = 3; // 房间 +} + +// 消息投递,package_type:4 +message MessageSend { + Message message = 1; // 消息 +} + +enum SenderType { + ST_UNKNOWN = 0; // 未知的 + ST_SYSTEM = 1; // IM系统 + ST_USER = 2; // 用户 + ST_BUSINESS = 3; // 业务方 +} + +enum MessageStatus { + MS_UNKNOWN = 0; // 未知的 + MS_NORMAL = 1; // 正常的 + MS_RECALL = 2; // 撤回 +} + +// 投递消息回执,package_type:4 +message MessageACK { + int64 device_ack = 2; // 设备收到消息的确认号 + int64 receive_time = 3; // 消息接收时间戳,精确到毫秒 +} diff --git a/gim/connect.int.proto b/gim/connect.int.proto new file mode 100644 index 0000000..5a48b6e --- /dev/null +++ b/gim/connect.int.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +import "common.ext.proto"; +import "connect.ext.proto"; + +service ConnectInt { + // 消息投递 + rpc DeliverMessage (DeliverMessageReq) returns (Empty); +} + +message DeliverMessageReq { + int64 device_id = 1; // 设备id + MessageSend message_send = 2; // 数据 +} + +// 房间推送 +message PushRoomMsg{ + int64 room_id = 1; // 设备id + MessageSend message_send = 2; // 数据 +} + +// 房间推送 +message PushAllMsg{ + MessageSend message_send = 2; // 数据 +} + diff --git a/gim/logic.ext.proto b/gim/logic.ext.proto new file mode 100644 index 0000000..b35248c --- /dev/null +++ b/gim/logic.ext.proto @@ -0,0 +1,198 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +import "common.ext.proto"; +import "connect.ext.proto"; + +service LogicExt { + // 注册设备 + rpc RegisterDevice (RegisterDeviceReq) returns (RegisterDeviceResp); + + // 发送消息 + rpc SendMessage (SendMessageReq) returns (SendMessageResp); + // 推送消息到房间 + rpc PushRoom(PushRoomReq)returns(Empty); + + // 添加好友 + rpc AddFriend (AddFriendReq) returns (Empty); + // 同意添加好友 + rpc AgreeAddFriend (AgreeAddFriendReq) returns (Empty); + // 设置好友信息 + rpc SetFriend (SetFriendReq) returns (SetFriendResp); + // 获取好友列表 + rpc GetFriends (Empty) returns (GetFriendsResp); + + // 创建群组 + rpc CreateGroup (CreateGroupReq) returns (CreateGroupResp); + // 更新群组 + rpc UpdateGroup (UpdateGroupReq) returns (Empty); + // 获取群组信息 + rpc GetGroup (GetGroupReq) returns (GetGroupResp); + // 获取用户加入的所有群组 + rpc GetGroups (Empty) returns (GetGroupsResp); + + // 添加群组成员 + rpc AddGroupMembers (AddGroupMembersReq) returns (AddGroupMembersResp); + // 更新群组成员信息 + rpc UpdateGroupMember (UpdateGroupMemberReq) returns (Empty); + // 添加群组成员 + rpc DeleteGroupMember (DeleteGroupMemberReq) returns (Empty); + // 获取群组成员 + rpc GetGroupMembers (GetGroupMembersReq) returns (GetGroupMembersResp); +} + +message RegisterDeviceReq { + int32 type = 2; // 设备类型 + string brand = 3; // 厂商 + string model = 4; // 机型 + string system_version = 5; // 系统版本 + string sdk_version = 6; // sdk版本号 +} +message RegisterDeviceResp { + int64 device_id = 1; // 设备id +} + +message SendMessageReq { + ReceiverType receiver_type = 1; // 接收者类型,1:user;2:group + int64 receiver_id = 2; // 用户id或者群组id + repeated int64 to_user_ids = 3; // 需要@的用户id列表 + MessageType message_type = 4; // 消息类型 + bytes message_content = 5; // 消息内容 + int64 send_time = 6; // 消息发送时间戳,精确到毫秒 + bool is_persist = 7; // 是否将消息持久化到数据库 + string message_content_back = 8; +} +message SendMessageResp { + int64 seq = 1; // 消息序列号 +} + +message PushRoomReq{ + int64 room_id = 1; // 房间id + MessageType message_type = 2; // 消息类型 + bytes message_content = 3; // 消息内容 + int64 send_time = 4; // 消息发送时间戳,精确到毫秒 + bool is_persist = 5; // 是否将消息持久化 + bool is_priority = 6; // 是否优先推送 +} + +message AddFriendReq { + int64 friend_id = 1; // 用户id + string remarks = 2; // 备注 + string description = 3; // 描述 +} + +message AgreeAddFriendReq { + int64 user_id = 1; // 用户id + string remarks = 2; // 备注 +} + +message SetFriendReq { + int64 friend_id = 1; // 好友id + string remarks = 2; // 备注 + string extra = 8; // 附加字段 +} +message SetFriendResp { + int64 friend_id = 1; // 好友id + string remarks = 2; // 备注 + string extra = 8; // 附加字段 +} + +message Friend { + int64 user_id = 1; // 用户id + string phone_number = 2; // 电话号码 + string nickname = 3; // 昵称 + int32 sex = 4; // 性别 + string avatar_url = 5; // 头像地址 + string user_extra = 6; // 用户附加字段 + string remarks = 7; // 备注 + string extra = 8; // 附加字段 +} +message GetFriendsResp { + repeated Friend friends = 1; +} + +message CreateGroupReq { + string name = 1; // 名称 + string avatar_url = 2; // 头像 + string introduction = 3; // 简介 + string extra = 4; // 附加字段 + repeated int64 member_ids = 5; // 群组成员ID列表 +} +message CreateGroupResp { + int64 group_id = 1; // 群组id +} + +message UpdateGroupReq { + int64 group_id = 1; // 群组id + string avatar_url = 2; // 头像 + string name = 3; // 名称 + string introduction = 4; // 简介 + string extra = 5; // 附加字段 +} + +message GetGroupReq { + int64 group_id = 1; +} +message GetGroupResp { + Group group = 1; +} + +message Group { + int64 group_id = 1; // 群组id + string name = 2; // 名称 + string avatar_url = 3; // 头像 + string introduction = 4; // 简介 + int32 user_mum = 5; // 用户数 + string extra = 6; // 附加字段 + int64 create_time = 7; // 创建时间 + int64 update_time = 8; // 更新时间 +} + +message GetGroupsResp { + repeated Group groups = 1; +} + +message AddGroupMembersReq { + int64 group_id = 1; // 群组id + repeated int64 user_ids = 2; // 用户id列表 +} +message AddGroupMembersResp { + repeated int64 user_ids = 1; // 已经在群组的用户id列表 +} + +enum MemberType { + GMT_UNKNOWN = 0; // 未知 + GMT_ADMIN = 1; // 管理员 + GMT_MEMBER = 2; // 成员 +} + +message UpdateGroupMemberReq { + int64 group_id = 1; // 群组id + int64 user_id = 2; // 用户id + MemberType member_type = 3; // 成员类型 + string remarks = 4; // 备注 + string extra = 5; // 附加字段 +} + +message DeleteGroupMemberReq { + int64 group_id = 1; // 群组id + int64 user_id = 2; // 用户id +} + +message GetGroupMembersReq { + int64 group_id = 1; +} +message GetGroupMembersResp { + repeated GroupMember members = 1; +} +message GroupMember { + int64 user_id = 1; + string nickname = 2; // 昵称 + int32 sex = 3; // 性别 + string avatar_url = 4; // 头像地址 + string user_extra = 5; // 用户附加字段 + MemberType member_type = 6; // 成员类型 + string remarks = 7; // 备注 + string extra = 8; // 群组成员附加字段 +} \ No newline at end of file diff --git a/gim/logic.int.proto b/gim/logic.int.proto new file mode 100644 index 0000000..38b2b1e --- /dev/null +++ b/gim/logic.int.proto @@ -0,0 +1,102 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +import "common.ext.proto"; +import "connect.ext.proto"; +import "logic.ext.proto"; + +service LogicInt { + // 登录 + rpc ConnSignIn (ConnSignInReq) returns (Empty); + // 消息同步 + rpc Sync (SyncReq) returns (SyncResp); + // 设备收到消息回执 + rpc MessageACK (MessageACKReq) returns (Empty); + // 设备离线 + rpc Offline (OfflineReq) returns (Empty); + // 订阅房间 + rpc SubscribeRoom(SubscribeRoomReq)returns(Empty); + // 发送消息 + rpc SendMessage (SendMessageReq) returns (SendMessageResp); + // 推送消息到房间 + rpc PushRoom(PushRoomReq)returns(Empty); + // 全服推送 + rpc PushAll(PushAllReq)returns(Empty); + + // 获取设备信息 + rpc GetDevice (GetDeviceReq) returns (GetDeviceResp); + // 服务停止 + rpc ServerStop (ServerStopReq) returns (Empty); +} + +message ConnSignInReq { + int64 device_id = 1; // 设备id + int64 user_id = 2; // 用户id + string token = 3; // 秘钥 + string conn_addr = 4; // 服务器地址 + string client_addr = 5; // 客户端地址 +} + +message SyncReq { + int64 user_id = 1; // 用户id + int64 device_id = 2; // 设备id + int64 seq = 3; // 客户端已经同步的序列号 +} +message SyncResp { + repeated Message messages = 1; // 消息列表 + bool has_more = 2; // 是否有更多数据 +} + +message MessageACKReq { + int64 user_id = 1; // 用户id + int64 device_id = 2; // 设备id + int64 device_ack = 3; // 设备收到消息的确认号 + int64 receive_time = 4; // 消息接收时间戳,精确到毫秒 +} + +message OfflineReq { + int64 user_id = 1; // 用户id + int64 device_id = 2; // 设备id + string client_addr = 3; // 客户端地址 +} + +message SubscribeRoomReq{ + int64 user_id = 1; // 用户id + int64 device_id = 2; // 设备id + int64 room_id = 3; // 房间id + int64 seq = 4; // 消息序列号 + string conn_addr = 5; // 服务器地址 +} + +message PushAllReq{ + MessageType message_type = 1; // 消息类型 + bytes message_content = 2; // 消息内容 + int64 send_time = 3; // 消息发送时间戳,精确到毫秒 +} + +message GetDeviceReq { + int64 device_id = 1; +} +message GetDeviceResp { + Device device = 1; +} + +message Device { + int64 device_id = 1; // 设备id + int64 user_id = 2; // 用户id + int32 type = 3; // 设备类型,1:Android;2:IOS;3:Windows; 4:MacOS;5:Web + string brand = 4; // 手机厂商 + string model = 5; // 机型 + string system_version = 6; // 系统版本 + string sdk_version = 7; // SDK版本 + int32 status = 8; // 在线状态,0:不在线;1:在线 + string conn_addr = 9; // 服务端连接地址 + string client_addr = 10; // 客户端地址 + int64 create_time = 11; // 创建时间 + int64 update_time = 12; // 更新时间 +} + +message ServerStopReq { + string conn_addr = 1; +} diff --git a/gim/push.ext.proto b/gim/push.ext.proto new file mode 100644 index 0000000..50b592f --- /dev/null +++ b/gim/push.ext.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package pb; +option go_package = "gim/pkg/pb/"; + +import "logic.ext.proto"; + +enum PushCode { + PC_ADD_DEFAULT = 0; + + PC_ADD_FRIEND = 100; // 添加好友请求 + PC_AGREE_ADD_FRIEND = 101; // 同意添加好友 + + PC_UPDATE_GROUP = 110; // 更新群组 + + PC_ADD_GROUP_MEMBERS = 120; // 添加群组成员 + PC_REMOVE_GROUP_MEMBER = 121; // 移除群组成员 + +} + +// 推送码 PC_ADD_FRIEND = 100 +message AddFriendPush { + int64 friend_id = 1; // 好友id + string nickname = 2; // 昵称 + string avatar_url = 3; // 头像 + string description = 4; // 描述 +} + +// 推送码 PC_AGREE_ADD_FRIEND = 101 +message AgreeAddFriendPush { + int64 friend_id = 1; // 好友id + string nickname = 2; // 昵称 + string avatar_url = 3; // 头像 +} + +// 更新群组 PC_UPDATE_GROUP = 110 +message UpdateGroupPush { + int64 opt_id = 1; // 操作人用户id + string opt_name = 2; // 操作人昵称 + string name = 3; // 群组名称 + string avatar_url = 4; // 群组头像 + string introduction = 5; // 群组简介 + string extra = 6; // 附加字段 +} + +// 添加群组成员 PC_AGREE_ADD_GROUPS = 120 +message AddGroupMembersPush { + int64 opt_id = 1; // 操作人用户id + string opt_name = 2; // 操作人昵称 + repeated GroupMember members = 3; // 群组成员 +} + +// 删除群组成员 PC_REMOVE_GROUP_MEMBER = 121 +message RemoveGroupMemberPush { + int64 opt_id = 1; // 操作人用户id + string opt_name = 2; // 操作人昵称 + int64 deleted_user_id = 3; // 被删除的成员id +}