290 lines
12 KiB
Markdown
290 lines
12 KiB
Markdown
# 红包消息结构文档
|
||
|
||
## 客户端收到的推送消息结构
|
||
|
||
### 完整消息结构 (MsgData)
|
||
|
||
客户端通过WebSocket或HTTP拉取收到的红包消息,消息结构如下:
|
||
|
||
```json
|
||
{
|
||
"clientMsgID": "client_1234567890abcdef", // 客户端消息ID(string)
|
||
"serverMsgID": "msg_1234567890abcdef", // 服务器消息ID(string)
|
||
"sendID": "user_owner123", // 发送者ID(群主ID)(string)
|
||
"recvID": "", // 接收者ID(群消息为空)(string)
|
||
"groupID": "group123", // 群ID(string)
|
||
"senderPlatformID": 0, // 发送者平台ID(int32,0表示系统)
|
||
"senderNickname": "群主昵称", // 发送者昵称(string)
|
||
"senderFaceURL": "https://...", // 发送者头像URL(string)
|
||
"sessionType": 3, // 会话类型(int32):3-群聊
|
||
"msgFrom": 200, // 消息来源(int32):200-系统消息
|
||
"contentType": 110, // 消息类型(int32):110-自定义消息
|
||
"content": "{\"data\":\"{\\\"redPacketID\\\":\\\"rp_123\\\",\\\"redPacketType\\\":1,\\\"blessing\\\":\\\"恭喜发财\\\"}\",\"description\":\"redpacket\",\"extension\":\"\"}", // 消息内容(JSON字符串,自定义消息格式)
|
||
"seq": 123456, // 消息序号(int64)
|
||
"sendTime": 1704067200000, // 发送时间戳(int64,毫秒)
|
||
"createTime": 1704067200000, // 创建时间戳(int64,毫秒)
|
||
"status": 2, // 消息状态(int32):2-发送成功
|
||
"isRead": false, // 是否已读(bool)
|
||
"options": { // 消息选项(map[string]bool)
|
||
"history": true, // 是否保存历史
|
||
"persistent": true, // 是否持久化
|
||
"offlinePush": true, // 是否离线推送
|
||
"unreadCount": true, // 是否计入未读数
|
||
"conversationUpdate": true, // 是否更新会话
|
||
"senderSync": true // 是否同步给发送者
|
||
},
|
||
"offlinePushInfo": { // 离线推送信息(可选)
|
||
"title": "[HONGBAO]", // 推送标题
|
||
"desc": "[HONGBAO]", // 推送描述
|
||
"ex": "", // 扩展字段
|
||
"iosPushSound": "", // iOS推送声音
|
||
"iosBadgeCount": false // iOS角标计数
|
||
},
|
||
"atUserIDList": [], // @用户列表(string[])
|
||
"attachedInfo": "", // 附加信息(string)
|
||
"ex": "" // 扩展字段(string)
|
||
}
|
||
```
|
||
|
||
### Content字段解析
|
||
|
||
`content` 字段是一个JSON字符串,需要先解析为 `CustomElem` 结构(自定义消息格式),然后从 `data` 字段中解析出 `RedPacketElem` 结构:
|
||
|
||
#### CustomElem 结构(自定义消息外层结构)
|
||
|
||
```json
|
||
{
|
||
"data": "{\"redPacketID\":\"rp_123\",\"redPacketType\":1,\"blessing\":\"恭喜发财\",\"isReceived\":false,\"receiveInfo\":null}", // 红包数据的JSON字符串(string,必填)
|
||
"description": "redpacket", // 二级类型标识(string):"redpacket"表示红包消息
|
||
"extension": "" // 扩展字段(string,可选)
|
||
}
|
||
```
|
||
|
||
#### RedPacketElem 结构(从data字段中解析)
|
||
|
||
```json
|
||
{
|
||
"redPacketID": "rp_1234567890abcdef", // 红包ID(string,必填)
|
||
"redPacketType": 1, // 红包类型(int32,必填):1-普通红包,2-拼手气红包
|
||
"blessing": "恭喜发财", // 祝福语(string,可选)
|
||
"isReceived": false, // 当前用户是否已领取(bool,服务器填充)
|
||
"receiveInfo": null // 领取信息(RedPacketReceiveInfo,仅拼手气红包且已领取时返回)
|
||
}
|
||
```
|
||
|
||
#### RedPacketReceiveInfo 结构(领取信息)
|
||
|
||
```json
|
||
{
|
||
"amount": 1000, // 领取金额(int64,单位:分)
|
||
"receiveTime": 1704067200000, // 领取时间戳(int64,毫秒)
|
||
"isLucky": false // 是否为手气最佳(bool,仅拼手气红包有效)
|
||
}
|
||
```
|
||
|
||
#### 字段说明
|
||
|
||
| 字段名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| `redPacketID` | string | 是 | 红包唯一标识,用于后续领取、查询等操作(总金额、总个数等信息可通过红包ID查询获取) |
|
||
| `redPacketType` | int32 | 是 | 红包类型:1-普通红包(平均分配),2-拼手气红包(随机分配) |
|
||
| `blessing` | string | 否 | 祝福语 |
|
||
| `isReceived` | bool | 是 | 当前用户是否已领取(服务器根据用户ID自动填充) |
|
||
| `receiveInfo` | RedPacketReceiveInfo | 否 | 领取信息,仅当 `isReceived=true` 且 `redPacketType=2`(拼手气红包)时返回 |
|
||
|
||
#### receiveInfo 字段说明
|
||
|
||
| 字段名 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| `amount` | int64 | 领取金额,单位:分 |
|
||
| `receiveTime` | int64 | 领取时间戳,毫秒 |
|
||
| `isLucky` | bool | 是否为手气最佳(仅拼手气红包有效) |
|
||
|
||
### 消息类型常量
|
||
|
||
- **ContentType**: `110` (`constant.Custom` - 自定义消息)
|
||
- **二级类型标识**: `"redpacket"` (存储在 `CustomElem.description` 字段中)
|
||
- **SessionType**: `3` (`constant.ReadGroupChatType` - 群聊)
|
||
- **MsgFrom**: `200` (`constant.SysMsgType` - 系统消息)
|
||
|
||
### 客户端解析示例
|
||
|
||
#### JavaScript/TypeScript
|
||
|
||
```typescript
|
||
interface RedPacketReceiveInfo {
|
||
amount: number; // 领取金额(分)
|
||
receiveTime: number; // 领取时间戳(毫秒)
|
||
isLucky: boolean; // 是否为手气最佳
|
||
}
|
||
|
||
interface RedPacketElem {
|
||
redPacketID: string;
|
||
redPacketType: number; // 1-普通红包,2-拼手气红包
|
||
blessing?: string;
|
||
isReceived: boolean; // 当前用户是否已领取
|
||
receiveInfo?: RedPacketReceiveInfo; // 领取信息(仅拼手气红包且已领取时返回)
|
||
}
|
||
|
||
interface CustomElem {
|
||
data: string; // 红包数据的JSON字符串
|
||
description: string; // 二级类型标识:"redpacket"
|
||
extension?: string; // 扩展字段
|
||
}
|
||
|
||
interface RedPacketMessage {
|
||
clientMsgID: string;
|
||
serverMsgID: string;
|
||
sendID: string;
|
||
groupID: string;
|
||
contentType: number; // 110 (自定义消息)
|
||
content: string; // CustomElem的JSON字符串
|
||
sendTime: number;
|
||
// ... 其他字段
|
||
}
|
||
|
||
// 解析消息
|
||
function parseRedPacketMessage(msg: RedPacketMessage): RedPacketElem | null {
|
||
// 检查是否为自定义消息类型
|
||
if (msg.contentType !== 110) {
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
// 先解析自定义消息结构
|
||
const customElem: CustomElem = JSON.parse(msg.content);
|
||
|
||
// 检查二级类型是否为红包
|
||
if (customElem.description !== "redpacket") {
|
||
return null;
|
||
}
|
||
|
||
// 从data字段中解析红包数据
|
||
const redPacketElem: RedPacketElem = JSON.parse(customElem.data);
|
||
return redPacketElem;
|
||
} catch (e) {
|
||
console.error('Failed to parse red packet content:', e);
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Go
|
||
|
||
```go
|
||
type RedPacketReceiveInfo struct {
|
||
Amount int64 `json:"amount"` // 领取金额(分)
|
||
ReceiveTime int64 `json:"receiveTime"` // 领取时间戳(毫秒)
|
||
IsLucky bool `json:"isLucky"` // 是否为手气最佳
|
||
}
|
||
|
||
type RedPacketElem struct {
|
||
RedPacketID string `json:"redPacketID"`
|
||
RedPacketType int32 `json:"redPacketType"`
|
||
TotalAmount int64 `json:"totalAmount"`
|
||
TotalCount int32 `json:"totalCount"`
|
||
Blessing string `json:"blessing"`
|
||
IsReceived bool `json:"isReceived"` // 当前用户是否已领取
|
||
ReceiveInfo *RedPacketReceiveInfo `json:"receiveInfo,omitempty"` // 领取信息(仅拼手气红包且已领取时返回)
|
||
}
|
||
|
||
type CustomElem struct {
|
||
Data string `json:"data"` // 红包数据的JSON字符串
|
||
Description string `json:"description"` // 二级类型标识:"redpacket"
|
||
Extension string `json:"extension"` // 扩展字段
|
||
}
|
||
|
||
func ParseRedPacketContent(content []byte) (*RedPacketElem, error) {
|
||
// 先解析自定义消息结构
|
||
var customElem CustomElem
|
||
if err := json.Unmarshal(content, &customElem); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 检查二级类型是否为红包
|
||
if customElem.Description != "redpacket" {
|
||
return nil, fmt.Errorf("not a red packet message")
|
||
}
|
||
|
||
// 从data字段中解析红包数据
|
||
var redPacketElem RedPacketElem
|
||
if err := json.Unmarshal([]byte(customElem.Data), &redPacketElem); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &redPacketElem, nil
|
||
}
|
||
```
|
||
|
||
### 完整消息示例
|
||
|
||
```json
|
||
{
|
||
"clientMsgID": "client_1704067200000_abc123",
|
||
"serverMsgID": "msg_1704067200000_def456",
|
||
"sendID": "user_owner123",
|
||
"recvID": "",
|
||
"groupID": "group123",
|
||
"senderPlatformID": 0,
|
||
"senderNickname": "群主",
|
||
"senderFaceURL": "https://example.com/avatar.jpg",
|
||
"sessionType": 3,
|
||
"msgFrom": 200,
|
||
"contentType": 110,
|
||
"content": "{\"data\":\"{\\\"redPacketID\\\":\\\"rp_1234567890abcdef\\\",\\\"redPacketType\\\":1,\\\"blessing\\\":\\\"恭喜发财\\\",\\\"isReceived\\\":false,\\\"receiveInfo\\\":null}\",\"description\":\"redpacket\",\"extension\":\"\"}",
|
||
"seq": 123456,
|
||
"sendTime": 1704067200000,
|
||
"createTime": 1704067200000,
|
||
"status": 2,
|
||
"isRead": false,
|
||
"options": {
|
||
"history": true,
|
||
"persistent": true,
|
||
"offlinePush": true,
|
||
"unreadCount": true,
|
||
"conversationUpdate": true,
|
||
"senderSync": true
|
||
},
|
||
"offlinePushInfo": {
|
||
"title": "[HONGBAO]",
|
||
"desc": "[HONGBAO]"
|
||
},
|
||
"atUserIDList": [],
|
||
"attachedInfo": "",
|
||
"ex": ""
|
||
}
|
||
```
|
||
|
||
### 消息选项说明
|
||
|
||
| 选项名 | 说明 |
|
||
|--------|------|
|
||
| `history` | 是否保存到历史记录 |
|
||
| `persistent` | 是否持久化存储 |
|
||
| `offlinePush` | 是否离线推送 |
|
||
| `unreadCount` | 是否计入未读数 |
|
||
| `conversationUpdate` | 是否更新会话 |
|
||
| `senderSync` | 是否同步给发送者 |
|
||
|
||
### 注意事项
|
||
|
||
1. **Content字段**: 是JSON字符串,需要先解析为 `CustomElem` 结构,然后从 `data` 字段中解析 `RedPacketElem`
|
||
2. **消息类型**: 使用自定义消息类型(`contentType = 110`),通过 `description` 字段标识二级类型为 `"redpacket"`
|
||
3. **二级类型扩展**: 未来可以扩展其他自定义消息类型,只需在 `description` 字段中使用不同的标识(如 `"wallet"`、`"coupon"` 等)
|
||
4. **金额单位**: `receiveInfo.amount` 单位是"分",不是"元"
|
||
5. **总金额和总个数**: 不在消息中传递,客户端可通过 `redPacketID` 调用查询接口获取详细信息
|
||
5. **红包类型**:
|
||
- `1` = 普通红包(平均分配)
|
||
- `2` = 拼手气红包(随机分配)
|
||
6. **发送者**: 固定为群主,`sendID` 为群主ID
|
||
7. **消息来源**: `msgFrom = 200` 表示系统消息
|
||
8. **会话类型**: `sessionType = 3` 表示群聊
|
||
9. **领取状态**: `isReceived` 字段由服务器根据当前用户ID自动填充,客户端无需设置
|
||
10. **领取信息**: `receiveInfo` 仅在以下情况返回:
|
||
- `isReceived = true`(用户已领取)
|
||
- `redPacketType = 2`(拼手气红包)
|
||
- 普通红包(`redPacketType = 1`)即使已领取也不会返回 `receiveInfo`
|
||
11. **客户端存储**: 客户端收到消息后,应将 `isReceived` 和 `receiveInfo` 存储到本地,用于UI展示
|
||
12. **兼容性**: 使用标准自定义消息类型,无需修改客户端SDK,只需在客户端添加红包消息的解析逻辑
|
||
|