复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:35:45 +08:00
parent 305d526110
commit b7f8db7d08
297 changed files with 81784 additions and 0 deletions

53
pkg/common/imapi/api.go Normal file
View File

@@ -0,0 +1,53 @@
// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package imapi
import (
"git.imall.cloud/openim/protocol/auth"
"git.imall.cloud/openim/protocol/group"
"git.imall.cloud/openim/protocol/relation"
"git.imall.cloud/openim/protocol/user"
)
// im caller.
var (
getAdminToken = NewApiCaller[auth.GetAdminTokenReq, auth.GetAdminTokenResp]("/auth/get_admin_token")
getuserToken = NewApiCaller[auth.GetUserTokenReq, auth.GetUserTokenResp]("/auth/get_user_token")
forceOffLine = NewApiCaller[auth.ForceLogoutReq, auth.ForceLogoutResp]("/auth/force_logout")
updateUserInfo = NewApiCaller[user.UpdateUserInfoReq, user.UpdateUserInfoResp]("/user/update_user_info")
updateUserInfoEx = NewApiCaller[user.UpdateUserInfoExReq, user.UpdateUserInfoExResp]("/user/update_user_info_ex")
registerUser = NewApiCaller[user.UserRegisterReq, user.UserRegisterResp]("/user/user_register")
getUserInfo = NewApiCaller[user.GetDesignateUsersReq, user.GetDesignateUsersResp]("/user/get_users_info")
accountCheck = NewApiCaller[user.AccountCheckReq, user.AccountCheckResp]("/user/account_check")
addNotificationAccount = NewApiCaller[user.AddNotificationAccountReq, user.AddNotificationAccountResp]("/user/add_notification_account")
updateNotificationAccount = NewApiCaller[user.UpdateNotificationAccountInfoReq, user.UpdateNotificationAccountInfoResp]("/user/update_notification_account")
getGroupsInfo = NewApiCaller[group.GetGroupsInfoReq, group.GetGroupsInfoResp]("/group/get_groups_info")
inviteToGroup = NewApiCaller[group.InviteUserToGroupReq, group.InviteUserToGroupResp]("/group/invite_user_to_group")
registerUserCount = NewApiCaller[user.UserRegisterCountReq, user.UserRegisterCountResp]("/statistics/user/register")
onlineUserCount = NewApiCaller[OnlineUserCountReq, OnlineUserCountResp]("/statistics/online_user_count")
onlineUserCountTrend = NewApiCaller[OnlineUserCountTrendReq, OnlineUserCountTrendResp]("/statistics/online_user_count_trend")
userSendMsgCount = NewApiCaller[UserSendMsgCountReq, UserSendMsgCountResp]("/statistics/user_send_msg_count")
userSendMsgCountTrend = NewApiCaller[UserSendMsgCountTrendReq, UserSendMsgCountTrendResp]("/statistics/user_send_msg_count_trend")
userSendMsgQuery = NewApiCaller[UserSendMsgQueryReq, UserSendMsgQueryResp]("/statistics/user_send_msg_query")
friendUserIDs = NewApiCaller[relation.GetFriendIDsReq, relation.GetFriendIDsResp]("/friend/get_friend_id")
importFriend = NewApiCaller[relation.ImportFriendReq, relation.ImportFriendResp]("/friend/import_friend")
sendSimpleMsg = NewApiCaller[SendSingleMsgReq, SendSingleMsgResp]("/msg/send_simple_msg")
)

160
pkg/common/imapi/call.go Normal file
View File

@@ -0,0 +1,160 @@
// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package imapi
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"time"
"git.imall.cloud/openim/chat/pkg/common/constant"
constantpb "git.imall.cloud/openim/protocol/constant"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"gorm.io/gorm/utils"
)
type baseApiResponse[T any] struct {
ErrCode int `json:"errCode"`
ErrMsg string `json:"errMsg"`
ErrDlt string `json:"errDlt"`
Data *T `json:"data"`
}
var client = &http.Client{
Timeout: time.Second * 10,
}
type ApiCaller[Req, Resp any] interface {
Call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error)
CallWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error)
}
func NewApiCaller[Req, Resp any](api string) ApiCaller[Req, Resp] {
return &caller[Req, Resp]{
api: api,
}
}
type caller[Req, Resp any] struct {
api string
}
func (a caller[Req, Resp]) Call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error) {
start := time.Now()
resp, err := a.call(ctx, apiPrefix, req)
if err != nil {
log.ZError(ctx, "api caller failed", err, "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
return nil, err
}
log.ZInfo(ctx, "api caller success resp", "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
return resp, nil
}
func (a caller[Req, Resp]) call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error) {
url := apiPrefix + a.api
reqBody, err := json.Marshal(req)
if err != nil {
return nil, err
}
request, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
operationID := utils.ToString(ctx.Value(constantpb.OperationID))
request.Header.Set(constantpb.OperationID, operationID)
if token, _ := ctx.Value(constant.CtxApiToken).(string); token != "" {
request.Header.Set(constantpb.Token, token)
}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
data, err := io.ReadAll(response.Body)
if err != nil {
return nil, errs.WrapMsg(err, "read http response body", "url", url, "code", response.StatusCode)
}
var resp baseApiResponse[Resp]
if err := json.Unmarshal(data, &resp); err != nil {
return nil, errs.WrapMsg(err, string(data))
}
if resp.ErrCode != 0 {
return nil, errs.NewCodeError(resp.ErrCode, resp.ErrMsg).WithDetail(resp.ErrDlt).Wrap()
}
return resp.Data, nil
}
func (a caller[Req, Resp]) CallWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error) {
start := time.Now()
resp, err := a.callWithQuery(ctx, apiPrefix, req, queryParams)
if err != nil {
log.ZError(ctx, "api caller failed", err, "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
return nil, err
}
log.ZInfo(ctx, "api caller success resp", "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
return resp, nil
}
func (a caller[Req, Resp]) callWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error) {
fullURL := apiPrefix + a.api
parsedURL, err := url.Parse(fullURL)
if err != nil {
return nil, errs.WrapMsg(err, "failed to parse URL", fullURL)
}
query := parsedURL.Query()
for key, value := range queryParams {
query.Set(key, value)
}
parsedURL.RawQuery = query.Encode()
fullURL = parsedURL.String()
reqBody, err := json.Marshal(req)
if err != nil {
return nil, err
}
request, err := http.NewRequestWithContext(ctx, http.MethodPost, fullURL, bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
operationID := utils.ToString(ctx.Value(constantpb.OperationID))
request.Header.Set(constantpb.OperationID, operationID)
if token, _ := ctx.Value(constant.CtxApiToken).(string); token != "" {
request.Header.Set(constantpb.Token, token)
}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
data, err := io.ReadAll(response.Body)
if err != nil {
return nil, errs.WrapMsg(err, "read http response body", "fullUrl", fullURL, "code", response.StatusCode)
}
var resp baseApiResponse[Resp]
if err := json.Unmarshal(data, &resp); err != nil {
return nil, errs.WrapMsg(err, string(data))
}
if resp.ErrCode != 0 {
return nil, errs.NewCodeError(resp.ErrCode, resp.ErrMsg).WithDetail(resp.ErrDlt).Wrap()
}
return resp.Data, nil
}

281
pkg/common/imapi/caller.go Normal file
View File

@@ -0,0 +1,281 @@
package imapi
import (
"context"
"sync"
"time"
"git.imall.cloud/openim/chat/pkg/botstruct"
"git.imall.cloud/openim/chat/pkg/eerrs"
"git.imall.cloud/openim/protocol/auth"
"git.imall.cloud/openim/protocol/constant"
"git.imall.cloud/openim/protocol/group"
"git.imall.cloud/openim/protocol/relation"
"git.imall.cloud/openim/protocol/sdkws"
"git.imall.cloud/openim/protocol/user"
wrapperspb "git.imall.cloud/openim/protocol/wrapperspb"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
)
type CallerInterface interface {
ImAdminTokenWithDefaultAdmin(ctx context.Context) (string, error)
ImportFriend(ctx context.Context, ownerUserID string, friendUserID []string) error
GetUserToken(ctx context.Context, userID string, platform int32) (string, error)
GetAdminTokenCache(ctx context.Context, userID string) (string, error)
GetAdminTokenServer(ctx context.Context, userID string) (string, error)
InviteToGroup(ctx context.Context, userID string, groupIDs []string) error
UpdateUserInfo(ctx context.Context, userID string, nickName string, faceURL string, userType int32, userFlag string) error
UpdateUserInfoEx(ctx context.Context, userID string, ex string) error
GetUserInfo(ctx context.Context, userID string) (*sdkws.UserInfo, error)
GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error)
AddNotificationAccount(ctx context.Context, req *user.AddNotificationAccountReq) error
UpdateNotificationAccount(ctx context.Context, req *user.UpdateNotificationAccountInfoReq) error
ForceOffLine(ctx context.Context, userID string) error
RegisterUser(ctx context.Context, users []*sdkws.UserInfo) error
FindGroupInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error)
UserRegisterCount(ctx context.Context, start int64, end int64) (map[string]int64, int64, error)
OnlineUserCount(ctx context.Context) (*OnlineUserCountResp, error)
OnlineUserCountTrend(ctx context.Context, req *OnlineUserCountTrendReq) (*OnlineUserCountTrendResp, error)
UserSendMsgCount(ctx context.Context, req *UserSendMsgCountReq) (*UserSendMsgCountResp, error)
UserSendMsgCountTrend(ctx context.Context, req *UserSendMsgCountTrendReq) (*UserSendMsgCountTrendResp, error)
UserSendMsgQuery(ctx context.Context, req *UserSendMsgQueryReq) (*UserSendMsgQueryResp, error)
FriendUserIDs(ctx context.Context, userID string) ([]string, error)
AccountCheckSingle(ctx context.Context, userID string) (bool, error)
SendSimpleMsg(ctx context.Context, req *SendSingleMsgReq, key string) error
}
type authToken struct {
token string
expired time.Time
}
type Caller struct {
imApi string
imSecret string
defaultIMUserID string
tokenCache map[string]*authToken
lock sync.RWMutex
}
func New(imApi string, imSecret string, defaultIMUserID string) CallerInterface {
return &Caller{
imApi: imApi,
imSecret: imSecret,
defaultIMUserID: defaultIMUserID,
tokenCache: make(map[string]*authToken),
lock: sync.RWMutex{},
}
}
func (c *Caller) ImportFriend(ctx context.Context, ownerUserID string, friendUserIDs []string) error {
if len(friendUserIDs) == 0 {
return nil
}
_, err := importFriend.Call(ctx, c.imApi, &relation.ImportFriendReq{
OwnerUserID: ownerUserID,
FriendUserIDs: friendUserIDs,
})
return err
}
func (c *Caller) ImAdminTokenWithDefaultAdmin(ctx context.Context) (string, error) {
return c.GetAdminTokenCache(ctx, c.defaultIMUserID)
}
func (c *Caller) GetAdminTokenCache(ctx context.Context, userID string) (string, error) {
c.lock.RLock()
t, ok := c.tokenCache[userID]
c.lock.RUnlock()
if ok && t.expired.After(time.Now()) {
return t.token, nil
}
c.lock.Lock()
defer c.lock.Unlock()
t, ok = c.tokenCache[userID]
if ok && t.expired.After(time.Now()) {
return t.token, nil
}
token, err := c.GetAdminTokenServer(ctx, userID)
if err != nil {
return "", err
}
c.tokenCache[userID] = &authToken{token: token, expired: time.Now().Add(time.Minute * 4)}
return token, nil
}
func (c *Caller) GetAdminTokenServer(ctx context.Context, userID string) (string, error) {
resp, err := getAdminToken.Call(ctx, c.imApi, &auth.GetAdminTokenReq{
Secret: c.imSecret,
UserID: userID,
})
if err != nil {
return "", err
}
log.ZDebug(ctx, "get im admin token from server", "userID", userID, "token", resp.Token)
return resp.Token, nil
}
func (c *Caller) GetUserToken(ctx context.Context, userID string, platformID int32) (string, error) {
resp, err := getuserToken.Call(ctx, c.imApi, &auth.GetUserTokenReq{
PlatformID: platformID,
UserID: userID,
})
if err != nil {
return "", err
}
return resp.Token, nil
}
func (c *Caller) InviteToGroup(ctx context.Context, userID string, groupIDs []string) error {
for _, groupID := range groupIDs {
_, _ = inviteToGroup.Call(ctx, c.imApi, &group.InviteUserToGroupReq{
GroupID: groupID,
Reason: "",
InvitedUserIDs: []string{userID},
})
}
return nil
}
func (c *Caller) UpdateUserInfo(ctx context.Context, userID string, nickName string, faceURL string, userType int32, userFlag string) error {
_, err := updateUserInfo.Call(ctx, c.imApi, &user.UpdateUserInfoReq{UserInfo: &sdkws.UserInfo{
UserID: userID,
Nickname: nickName,
FaceURL: faceURL,
UserType: userType,
UserFlag: userFlag,
}})
return err
}
func (c *Caller) UpdateUserInfoEx(ctx context.Context, userID string, ex string) error {
_, err := updateUserInfoEx.Call(ctx, c.imApi, &user.UpdateUserInfoExReq{
UserInfo: &sdkws.UserInfoWithEx{
UserID: userID,
Ex: &wrapperspb.StringValue{Value: ex},
},
})
return err
}
func (c *Caller) GetUserInfo(ctx context.Context, userID string) (*sdkws.UserInfo, error) {
resp, err := c.GetUsersInfo(ctx, []string{userID})
if err != nil {
return nil, err
}
if len(resp) == 0 {
return nil, errs.ErrRecordNotFound.WrapMsg("record not found")
}
return resp[0], nil
}
func (c *Caller) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
resp, err := getUserInfo.Call(ctx, c.imApi, &user.GetDesignateUsersReq{
UserIDs: userIDs,
})
if err != nil {
return nil, err
}
return resp.UsersInfo, nil
}
func (c *Caller) RegisterUser(ctx context.Context, users []*sdkws.UserInfo) error {
_, err := registerUser.Call(ctx, c.imApi, &user.UserRegisterReq{
Users: users,
})
return err
}
func (c *Caller) ForceOffLine(ctx context.Context, userID string) error {
for id := range constant.PlatformID2Name {
_, _ = forceOffLine.Call(ctx, c.imApi, &auth.ForceLogoutReq{
PlatformID: int32(id),
UserID: userID,
})
}
return nil
}
func (c *Caller) FindGroupInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
resp, err := getGroupsInfo.Call(ctx, c.imApi, &group.GetGroupsInfoReq{
GroupIDs: groupIDs,
})
if err != nil {
return nil, err
}
return resp.GroupInfos, nil
}
func (c *Caller) UserRegisterCount(ctx context.Context, start int64, end int64) (map[string]int64, int64, error) {
resp, err := registerUserCount.Call(ctx, c.imApi, &user.UserRegisterCountReq{
Start: start,
End: end,
})
if err != nil {
return nil, 0, err
}
return resp.Count, resp.Total, nil
}
// OnlineUserCount 获取在线人数统计
func (c *Caller) OnlineUserCount(ctx context.Context) (*OnlineUserCountResp, error) {
return onlineUserCount.Call(ctx, c.imApi, &OnlineUserCountReq{})
}
// OnlineUserCountTrend 获取在线人数走势统计
func (c *Caller) OnlineUserCountTrend(ctx context.Context, req *OnlineUserCountTrendReq) (*OnlineUserCountTrendResp, error) {
return onlineUserCountTrend.Call(ctx, c.imApi, req)
}
// UserSendMsgCount 获取用户发送消息统计
func (c *Caller) UserSendMsgCount(ctx context.Context, req *UserSendMsgCountReq) (*UserSendMsgCountResp, error) {
return userSendMsgCount.Call(ctx, c.imApi, req)
}
// UserSendMsgCountTrend 获取用户发送消息走势统计
func (c *Caller) UserSendMsgCountTrend(ctx context.Context, req *UserSendMsgCountTrendReq) (*UserSendMsgCountTrendResp, error) {
return userSendMsgCountTrend.Call(ctx, c.imApi, req)
}
// UserSendMsgQuery 获取用户发送消息查询列表
func (c *Caller) UserSendMsgQuery(ctx context.Context, req *UserSendMsgQueryReq) (*UserSendMsgQueryResp, error) {
return userSendMsgQuery.Call(ctx, c.imApi, req)
}
func (c *Caller) FriendUserIDs(ctx context.Context, userID string) ([]string, error) {
resp, err := friendUserIDs.Call(ctx, c.imApi, &relation.GetFriendIDsReq{UserID: userID})
if err != nil {
return nil, err
}
return resp.FriendIDs, nil
}
// return true when isUserNotExist.
func (c *Caller) AccountCheckSingle(ctx context.Context, userID string) (bool, error) {
resp, err := accountCheck.Call(ctx, c.imApi, &user.AccountCheckReq{CheckUserIDs: []string{userID}})
if err != nil {
return false, err
}
if resp.Results[0].AccountStatus == constant.Registered {
return false, eerrs.ErrAccountAlreadyRegister.Wrap()
}
return true, nil
}
func (c *Caller) SendSimpleMsg(ctx context.Context, req *SendSingleMsgReq, key string) error {
_, err := sendSimpleMsg.CallWithQuery(ctx, c.imApi, req, map[string]string{botstruct.Key: key})
return err
}
func (c *Caller) AddNotificationAccount(ctx context.Context, req *user.AddNotificationAccountReq) error {
_, err := addNotificationAccount.Call(ctx, c.imApi, req)
return err
}
func (c *Caller) UpdateNotificationAccount(ctx context.Context, req *user.UpdateNotificationAccountInfoReq) error {
_, err := updateNotificationAccount.Call(ctx, c.imApi, req)
return err
}

139
pkg/common/imapi/model.go Normal file
View File

@@ -0,0 +1,139 @@
package imapi
import "git.imall.cloud/openim/protocol/sdkws"
// SendSingleMsgReq defines the structure for sending a message to multiple recipients.
type SendSingleMsgReq struct {
// groupMsg should appoint sendID
SendID string `json:"sendID"`
Content string `json:"content" binding:"required"`
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
Ex string `json:"ex"`
}
type SendSingleMsgResp struct{}
// OnlineUserCountReq 在线人数统计请求
type OnlineUserCountReq struct{}
// OnlineUserCountResp 在线人数统计返回
type OnlineUserCountResp struct {
OnlineCount int64 `json:"onlineCount"`
}
// OnlineUserCountTrendReq 在线人数走势统计请求
type OnlineUserCountTrendReq struct {
// StartTime 统计开始时间毫秒时间戳为空时默认最近24小时
StartTime int64 `json:"startTime"`
// EndTime 统计结束时间(毫秒时间戳),为空时默认当前时间
EndTime int64 `json:"endTime"`
// IntervalMinutes 统计间隔分钟仅支持15/30/60
IntervalMinutes int32 `json:"intervalMinutes"`
}
// OnlineUserCountTrendItem 在线人数走势数据点
type OnlineUserCountTrendItem struct {
// Timestamp 区间起始时间(毫秒时间戳)
Timestamp int64 `json:"timestamp"`
// OnlineCount 区间内平均在线人数
OnlineCount int64 `json:"onlineCount"`
}
// OnlineUserCountTrendResp 在线人数走势统计返回
type OnlineUserCountTrendResp struct {
// IntervalMinutes 统计间隔(分钟)
IntervalMinutes int32 `json:"intervalMinutes"`
// Points 走势数据点
Points []*OnlineUserCountTrendItem `json:"points"`
}
// UserSendMsgCountReq 用户发送消息总数统计请求
type UserSendMsgCountReq struct{}
// UserSendMsgCountResp 用户发送消息总数统计返回
type UserSendMsgCountResp struct {
// Count24h 最近24小时发送消息总数
Count24h int64 `json:"count24h"`
// Count7d 最近7天发送消息总数
Count7d int64 `json:"count7d"`
// Count30d 最近30天发送消息总数
Count30d int64 `json:"count30d"`
}
// UserSendMsgCountTrendReq 用户发送消息走势统计请求
type UserSendMsgCountTrendReq struct {
// UserID 发送者用户ID
UserID string `json:"userID"`
// ChatType 聊天类型1-单聊2-群聊
ChatType int32 `json:"chatType"`
// StartTime 统计开始时间毫秒时间戳为空时默认最近24小时
StartTime int64 `json:"startTime"`
// EndTime 统计结束时间(毫秒时间戳),为空时默认当前时间
EndTime int64 `json:"endTime"`
// IntervalMinutes 统计间隔分钟仅支持15/30/60
IntervalMinutes int32 `json:"intervalMinutes"`
}
// UserSendMsgCountTrendItem 用户发送消息走势数据点
type UserSendMsgCountTrendItem struct {
// Timestamp 区间起始时间(毫秒时间戳)
Timestamp int64 `json:"timestamp"`
// Count 区间内发送消息数
Count int64 `json:"count"`
}
// UserSendMsgCountTrendResp 用户发送消息走势统计返回
type UserSendMsgCountTrendResp struct {
// UserID 发送者用户ID
UserID string `json:"userID"`
// ChatType 聊天类型1-单聊2-群聊
ChatType int32 `json:"chatType"`
// IntervalMinutes 统计间隔(分钟)
IntervalMinutes int32 `json:"intervalMinutes"`
// Points 走势数据点
Points []*UserSendMsgCountTrendItem `json:"points"`
}
// UserSendMsgQueryReq 用户发送消息查询请求
type UserSendMsgQueryReq struct {
UserID string `json:"userID"`
StartTime int64 `json:"startTime"`
EndTime int64 `json:"endTime"`
Content string `json:"content"`
PageNumber int32 `json:"pageNumber"`
// ShowNumber 每页条数 默认50 最大200
ShowNumber int32 `json:"showNumber"`
}
// UserSendMsgQueryRecord 用户发送消息查询记录
type UserSendMsgQueryRecord struct {
// MsgID 使用服务端消息ID
MsgID string `json:"msgID"`
// SendID 发送者ID
SendID string `json:"sendID"`
// SenderName 发送者昵称或名称
SenderName string `json:"senderName"`
// RecvID 接收者ID 群聊为群ID
RecvID string `json:"recvID"`
// RecvName 接收者昵称或名称 群聊为群名称
RecvName string `json:"recvName"`
// ContentType 消息类型编号
ContentType int32 `json:"contentType"`
// ContentTypeName 消息类型名称
ContentTypeName string `json:"contentTypeName"`
// SessionType 聊天类型编号
SessionType int32 `json:"sessionType"`
// ChatTypeName 聊天类型名称
ChatTypeName string `json:"chatTypeName"`
// Content 消息内容
Content string `json:"content"`
// SendTime 消息发送时间
SendTime int64 `json:"sendTime"`
}
// UserSendMsgQueryResp 用户发送消息查询返回
type UserSendMsgQueryResp struct {
Count int64 `json:"count"`
PageNumber int32 `json:"pageNumber"`
ShowNumber int32 `json:"showNumber"`
Records []*UserSendMsgQueryRecord `json:"records"`
}