跳到主要内容

红包场景实践

场景说明

用户在单聊/群聊会话中向指定好友发送红包(实际货币、虚拟货币、积分等),发送人和接收人可以明确感知红包是否被领取的状态(未领取、已领取)。

效果示例

红包消息流程

1. 准备工作

在开始之前,请确保已创建应用并完成客户端 SDK 集成

2. 定义红包消息

2.1 定义红包消息类型

您可通过 IMLib SDK 的自定义消息,创建红包类消息类型(如 RedPacketMessage),消息内容结构需根据业务需求自行定义,确保多端(Android / iOS / Web)一致。

Android / iOS 端自定义消息类参考:

Web 端示例代码:

javascript
// 1. RongIMLib.registerMessageType 必须在 connect 之前调用,否则可能造成收取消息的行为异常。
// 2. 请尽量把应用中所有涉及到的自定义消息统一一次注册,且同类型消息仅调用一次注册,便于管理。

const messageType = 'app:red_packet' // 消息类型
const isPersited = true // 是否存储
const isCounted = true // 是否计数
const searchProps = [] // 搜索字段,Web 端无需设置,搜索字段值设置为数字时取值范围为 (-Math.pow(2, 64), Math.pow(2, 64)) 且为整数
const isStatusMessage = false // 是否是状态消息。状态消息不存储、不计数,接收方在线时才能收到。
const PersonMessage = RongIMLib.registerMessageType(messageType, isPersited, isCounted, searchProps, isStatusMessage)

2.2 定义红包消息展示模板

如果您使用的是 IMKit SDK,必须创建对应的消息展示模板,否则 SDK 无法正常展示该类型消息。

Android / iOS 端自定义红包消息展示模板参考类:

Web IMKit 设置自定义消息样式示例代码

javascript
// 构造 IMKit 初始化参数
const customMessage = {
// 普通消息展示
userMessage: {
// key 为自定义消息的 messageType,return 的元素中暂不支持设置 class,如需设置样式可设置为行内样式。
'app:red_packet': (message) => {
const content = message.content;
return `<div style='padding: 0.5em 0.8333em;'>来自 ${content.name} 的红包</div>`;
}
},
// 通知类消息展示
notifyMessage: {
// key 为自定义消息的 messageType,return 的元素中暂不支持设置 class,如需设置样式可设置为行内样式。
'app:red_packet': (message) => {
const content = message.content
const string = `<div>来自 ${content.name} 的红包</div>`
return string;
}
},
// 会话最后一条消息展示
lastMessage:{
// key 为自定义消息的 messageType,return 的元素中暂不支持设置 class,如需设置样式可设置为行内样式。
'app:red_packet': (message) => {
const content = message.content;
return `[红包信息]`;
},
}
};

// 特别注意:此处 init 仅为展示自定义消息设置,应用内不需要多次进行初始化
imkit.init({
customMessage:customMessage
});

3. 注册并接收红包消息

Java
// 注册自定义消息类型,初始化 SDK 后注册
ArrayList<Class<? extends MessageContent>> myMessages = new ArrayList<>();
myMessages.add(CustomRedPacketMessage.class);
RongCoreClient.registerMessageType(myMessages);

// 接收消息示例:设置接收消息监听器,接收消息时会自动回调,如果您使用的 IMLib SDK,请调用 RongCoreClient 的 addOnReceiveMessageListener 方法
IMCenter.addOnReceiveMessageListener(
new io.rong.imlib.listener.OnReceiveMessageWrapperListener() {
@Override
public boolean onReceivedMessage(Message message, ReceivedProfile profile) {
int left = profile.getLeft();
boolean isOffline = profile.isOffline();
boolean hasPackage = profile.hasPackage();
}
});

// 返回的 `Message` 实体参考下面信息,可以使用 `objectName` 或者 `content` 来区分消息类型:

{
"conversationType": "PRIVATE",
"targetId": "userid3453",
"messageId": 70,
"channelId": "",
"messageDirection": "RECEIVE",
"senderUserId": "userid3453",
"receivedStatus": "io.rong.imlib.model.Message$ReceivedStatus @560f848",
"sentStatus": "SENT",
"receivedTime": 1739428279001,
"sentTime": 1739428279158,
"objectName": "app:red_packet",
"content": {
"content": "红包消息"
},
"extra": "",
"readReceiptInfo": "io.rong.imlib.model.ReadReceiptInfo @b8d3c06",
"messageConfig": {
"disablePushTitle": false,
"pushTitle": "",
"pushContent": "",
"pushData": "null",
"templateId": "",
"forceShowDetailContent": false,
"iOSConfig": null,
"androidConfig": null,
"harmonyConfig": null
},
"canIncludeExpansion": false,
"expansionDic": null,
"expansionDicEx": null,
"mayHasMoreMessagesBefore": false,
"UId": "CKVO-0J6T-GM26-D3E6",
"disableUpdateLastMessage": "false",
"directedUsers": "0"
}

4. 扩展区域展示(发送红包入口)

4.1 设置红包扩展面板插件

如果您使用的是 IMLib SDK,您需要自行实现发送红包入口。 如果您使用的是 IMKit SDK,可以在 IMKit 的扩展面板中添加自定义插件。

Android 端实现流程

自定义插件需要实现 IPluginModule 接口类,可以参考 IMKit 源码中的 IPluginModule.java,以及具体的实现类。 此处以实现自定义插件 RedPacketPlugin 示例类为例。

iOS 端实现流程

在聊天页面的 viewDidLoad 方法中插入对应的红包扩展插件图标

Objective C
[self.chatSessionInputBarControl.pluginBoardView insertItem:[UIImage imageNamed:@"redPacket"] highlightedImage:[UIImage imageNamed:@"redPacket"] title:@"发红包" tag:20080];

Web IMKit 没有扩展面板概念

4.2 配置扩展面板插件

示例代码

Java
// 1. 继承 `DefaultExtensionConfig`,创建自定义的扩展面板配置类 `MyExtensionConfig`,重写 `getPluginModules()` 方法。

public class MyExtensionConfig extends DefaultExtensionConfig {
@Override
public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType, String targetId) {
List<IPluginModule> pluginModules = super.getPluginModules(conversationType,targetId);
// 增加红包扩展项
pluginModules.add(new RedPacketPlugin());
return pluginModules;
}
}

// 2. SDK 初始化之后,调用 `setExtensionConfig` 方法设置自定义的输入配置,SDK 会根据此配置展示扩展面板。

RongExtensionManager.getInstance().setExtensionConfig(new MyExtensionConfig());

5. 付款成功发送红包消息

在用户付款成功后,需要调用融云的发送消息方法,发送红包消息并设置消息可扩展。根据业务侧逻辑选择从哪个端发送此类消息。

Java
// 构建红包消息
CustomRedPacketMessage redPacketMessage = CustomRedPacketMessage.obtain("0.01");
io.rong.imlib.model.Message message =
io.rong.imlib.model.Message.obtain(targetId, conversationType, redPacketMessage);
// 设置消息可扩展
message.setCanIncludeExpansion(true);
HashMap<String, String> redInfo = new HashMap<>();
redInfo.put("open","false");
redInfo.put("count","1");
redInfo.put("amount","0.01");
message.setExpansion(redInfo);
// 发送消息,如果您使用的 IMLib SDK,请使用 RongCoreClient 的 sendMessage 方法
IMCenter.getInstance().sendMessage(message, null, null, null);

服务端示例代码

Java
 /**
* Send group gray bar message - targeted users (up to 1000 users per request)
*/
String[] targetIds = {"groupId"};
RedPacketMessage redPacketMessage =new RedPacketMessage("红包消息");
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("open", "false");
hashMap.put("count", "1");
hashMap.put("amount", "2");
GroupMessage groupMessage = new GroupMessage()
.setSenderId("fromId")
.setIsIncludeSender(1)
.setTargetId(targetIds) // Group ID
.setContent(redPacketMessage)
.setObjectName(txtMessage.getType())
.setExpansion(true)
.setExtraContent(hashMap);

ResponseResult redPackeReslut = group.send(groupMessage);
System.out.println("group info Notify message result: " + redPackeReslut.toString());

6. 开启红包更新红包扩展

用户点击开启红包后,红包消息状态需要变更为「已开启」。此时,您可通过设置消息扩展监听 updateMessageExpansion 更新此条消息的扩展信息,扩展设置开启用户 id 以及标识为已开启状态,同时更改本地显示的消息样式。

Java
// messageUid 为原红包消息唯一 Id。

RongIMClient.getInstance()
.updateMessageExpansion(
redInfo,
messageUid,
new RongIMClient.OperationCallback() {
@Override
public void onSuccess() {
// 更新发起者在这里处理更新扩展后的 UI 数据刷新
IMCenter.getInstance().refreshMessage(currentMessage);
}

@Override
public void onError(RongIMClient.ErrorCode errorCode) {
Toast.makeText(
getApplicationContext(),
"设置失败,ErrorCode : " + errorCode.getValue(),
Toast.LENGTH_LONG)
.show();
}
});

服务端示例代码

Java
/**
*
* Set message extension
*
*/
ExpansionModel msg = new ExpansionModel();
msg.setMsgUID("BS45-NPH4-HV87-10LM");
msg.setUserId("WNYZbMqpH");
msg.setTargetId("tjw3zbMrU");
msg.setConversationType(1);
HashMap<String, String> kv = new HashMap<String, String>();
kv.put("type1", "1");
kv.put("type2", "2");
kv.put("type3", "3");
kv.put("type4", "4");
msg.setExtraKeyVal(kv);
msg.setIsSyncSender(1);
ResponseResult result = expansion.set(msg);
System.out.println("set expansion: " + result.toString());

7. 发送方更新红包消息扩展

红包消息发送方可全局设置消息扩展,收到消息扩展更新回调时做对应的处理。建议调用客户端服务 API 获取最新的红包领取信息。

Java
// 接收方 Android 端示例代码
RongIMClient.getInstance().setMessageExpansionListener(new RongIMClient.MessageExpansionListener() {
@Override
public void onMessageExpansionUpdate(Map<String, String> expansion, Message message) {
if (message.getContent() instanceof CustomRedPacketMessage){
IMCenter.getInstance().refreshMessage(message); // 刷新原消息
// 其他自定义处理
}
}
});

8. 红包领取后操作

开启红包后,如需展示" XX 领取了红包"。可由服务端发送一条群定向消息或者红包发送者和领取者,或者监听消息扩展信息,展示对应的用户信息及领取状态。红包发送者可以监听消息扩展信息,收到监听后,主动到业务客户端获取消息领取的最新状态。

Java
// 插入小灰条消息
InformationNotificationMessage informationNotificationMessage = InformationNotificationMessage.obtain("你领取了xxx 发送的红包");

ConversationType conversationType = ConversationType.PRIVATE;
String targetId = "user1";
String senderUserId = "模拟发送方的 ID";
ReceivedStatus receivedStatus = new ReceivedStatus(0x1);
String sentTime = System.currentTimeMillis();

IMCenter.getInstance().insertIncomingMessage(conversationType, targetId, senderUserId, receivedStatus, informationNotificationMessage, sentTime, new RongIMClient.ResultCallback<Message>() {
/**
* 成功回调
* @param message 插入的消息
*/
@Override
public void onSuccess(Message message) {

}

/**
* 失败回调
* @param errorCode 错误码
*/
@Override
public void onError(RongIMClient.ErrorCode errorCode) {

}
});