# 红包消息结构文档 ## 客户端收到的推送消息结构 ### 完整消息结构 (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,只需在客户端添加红包消息的解析逻辑