复制项目
This commit is contained in:
289
docs/redpacket-message-structure.md
Normal file
289
docs/redpacket-message-structure.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# 红包消息结构文档
|
||||
|
||||
## 客户端收到的推送消息结构
|
||||
|
||||
### 完整消息结构 (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,只需在客户端添加红包消息的解析逻辑
|
||||
|
||||
Reference in New Issue
Block a user