赞
踩
最近有一个需求是在小程序上开发一个在线聊天的功能,调研了一下觉得腾讯云的IM服务比较合适。
腾讯云IM与应用之间的交互逻辑如图所示
IM 提供全球接入、单聊、群聊、消息推送、资料关系链托管、账号鉴权等全方位解决方案,并提供完备的 App 接入、后台管理接口。
包括了社交沟通,互动直播,智能客服,物联网通信、企业通讯等等。
用的是Feign接入
@FeignClient(name = "tencentIMService", url = "${application.tencent-im.request-url}", configuration = OpenFeignConfig.class, fallback = TencentIMServiceFallback.class) public interface TencentIMService { /** * 查询账号 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param accounts 用户列表 * @return 响应体 */ @PostMapping("/v4/im_open_login_svc/account_check") JSONObject checkAccount(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMCheckAccountVM accounts); /** * 导入单个账号 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param account 用户 * @return 响应体 */ @PostMapping("/v4/im_open_login_svc/account_import") JSONObject importSingleAccount(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMSingleAccountVM account); /** * 设置账号资料 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param account 用户 * @return 响应体 */ @PostMapping("/v4/profile/portrait_set") JSONObject setAccount(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMProfileVM account); /** * 导入多个账号 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param accounts 用户账号列表 * @return 响应体 */ @PostMapping("/v4/im_open_login_svc/multiaccount_import") JSONObject importMultiAccount(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMMultiAccountVM accounts); /** * 创建群组 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param group 群组 * @return 响应体 */ @PostMapping("/v4/group_open_http_svc/create_group") JSONObject createGroup(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMGroupVM group); /** * 导入群组成员 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param group 群组 * @return 响应体 */ @PostMapping("/v4/group_open_http_svc/import_group_member") JSONObject importGroupMember(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMGroupVM group); /** * 在群组中发送普通消息 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param msg 普通消息 * @return 响应体 */ @PostMapping("/v4/group_open_http_svc/send_group_msg") JSONObject sendGroupMsg(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMGroupMsgVM msg); /** * 在群组中发送系统通知 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param msg 系统消息 * @return 响应体 */ @PostMapping("/v4/group_open_http_svc/send_group_system_notification") JSONObject sendGroupSystemNotification(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMGroupSystemMsgVM msg); /** * 创建机器人账号 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param msg 系统消息 * @return 响应体 */ @PostMapping("/v4/openim_robot_http_svc/create_robot") JSONObject createRobot(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMSingleAccountVM msg); /** * 在群组中发送普通消息 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param msg 普通消息 * @return 响应体 */ @PostMapping("/v4/openim/sendmsg") JSONObject sendMsg(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody IMMsgVM msg); /** * 在群组中发送普通消息 * * @param sdkappid sdkApId * @param identifier 必须为 App 管理员账号 * @param contenttype 请求格式固定值为json * @param usersig App 管理员账号生成的签名 * @param random 请输入随机的32位无符号整数,取值范围0 - 4294967295 * @param content 内容 * @return 响应体 */ @PostMapping("/v4/openim/admin_set_msg_read") JSONObject adminSetMsgRead(@RequestParam("sdkappid") Long sdkappid, @RequestParam("identifier") String identifier, @RequestParam("contenttype") String contenttype, @RequestParam("usersig") String usersig, @RequestParam("random") Integer random, @RequestBody String content); }
@Component @Slf4j @RequiredArgsConstructor public class IMUserSigService { private final ApplicationProperties applicationProperties; /** * 【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据 * <p> * 【参数说明】 * * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 * @param expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。 * @return usersig -生成的签名 */ /** * Function: Used to issue UserSig that is required by the TRTC and IM services. * <p> * Parameter description: * * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). * @param expire - UserSig expiration time, in seconds. For example, 86400 indicates that the generated UserSig will expire one day after being generated. * @return usersig - Generated signature. */ public String genUserSig(String userid, long expire) { return genUserSig(userid, expire, null); } /** * 【功能说明】 * 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。 * PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力: * - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。 * - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。 * 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。 * <p> * 【参数说明】 * * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 * @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。 * @param roomid - 房间号,用于指定该 userid 可以进入的房间号 * @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关: * - 第 1 位:0000 0001 = 1,创建房间的权限 * - 第 2 位:0000 0010 = 2,加入房间的权限 * - 第 3 位:0000 0100 = 4,发送语音的权限 * - 第 4 位:0000 1000 = 8,接收语音的权限 * - 第 5 位:0001 0000 = 16,发送视频的权限 * - 第 6 位:0010 0000 = 32,接收视频的权限 * - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限 * - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限 * - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。 * - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。 * @return usersig - 生成带userbuf的签名 */ /** * Function: * Used to issue PrivateMapKey that is optional for room entry. * PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities. * - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room. * - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room. * To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info. * <p> * Parameter description: * * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). * @param roomid - ID of the room to which the specified UserID can enter. * @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated. * @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features: * - Bit 1: 0000 0001 = 1, permission for room creation * - Bit 2: 0000 0010 = 2, permission for room entry * - Bit 3: 0000 0100 = 4, permission for audio sending * - Bit 4: 0000 1000 = 8, permission for audio receiving * - Bit 5: 0001 0000 = 16, permission for video sending * - Bit 6: 0010 0000 = 32, permission for video receiving * - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing) * - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing) * - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid. * - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data. * @return usersig - Generate signature with userbuf */ public String genPrivateMapKey(String userid, long expire, long roomid, long privilegeMap) { byte[] userbuf = genUserBuf(userid, roomid, expire, privilegeMap, 0, ""); //生成userbuf return genUserSig(userid, expire, userbuf); } /** * 【功能说明】 * 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。 * PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力: * - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。 * - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。 * 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。 * <p> * 【参数说明】 * * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 * @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。 * @param roomstr - 字符串房间号,用于指定该 userid 可以进入的房间号 * @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关: * - 第 1 位:0000 0001 = 1,创建房间的权限 * - 第 2 位:0000 0010 = 2,加入房间的权限 * - 第 3 位:0000 0100 = 4,发送语音的权限 * - 第 4 位:0000 1000 = 8,接收语音的权限 * - 第 5 位:0001 0000 = 16,发送视频的权限 * - 第 6 位:0010 0000 = 32,接收视频的权限 * - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限 * - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限 * - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。 * - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。 * @return usersig - 生成带userbuf的签名 */ /** * Function: * Used to issue PrivateMapKey that is optional for room entry. * PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities. * - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room. * - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room. * To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info. * <p> * Parameter description: * * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). * @param roomstr - ID of the room to which the specified UserID can enter. * @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated. * @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features: * - Bit 1: 0000 0001 = 1, permission for room creation * - Bit 2: 0000 0010 = 2, permission for room entry * - Bit 3: 0000 0100 = 4, permission for audio sending * - Bit 4: 0000 1000 = 8, permission for audio receiving * - Bit 5: 0001 0000 = 16, permission for video sending * - Bit 6: 0010 0000 = 32, permission for video receiving * - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing) * - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing) * - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid. * - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data. * @return usersig - Generate signature with userbuf */ public String genPrivateMapKeyWithStringRoomID(String userid, long expire, String roomstr, long privilegeMap) { byte[] userbuf = genUserBuf(userid, 0, expire, privilegeMap, 0, roomstr); //生成userbuf return genUserSig(userid, expire, userbuf); } private String hmacsha256(String identifier, long currTime, long expire, String base64Userbuf) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String contentToBeSigned = "TLS.identifier:" + identifier + "\n" + "TLS.sdkappid:" + tencentIm.getSdkAppId() + "\n" + "TLS.time:" + currTime + "\n" + "TLS.expire:" + expire + "\n"; if (null != base64Userbuf) { contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n"; } try { byte[] byteKey = tencentIm.getKey().getBytes(StandardCharsets.UTF_8); Mac hmac = Mac.getInstance("HmacSHA256"); SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256"); hmac.init(keySpec); byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes(StandardCharsets.UTF_8)); return (Base64.getEncoder().encodeToString(byteSig)).replaceAll("\\s*", ""); } catch (NoSuchAlgorithmException | InvalidKeyException e) { return ""; } } private String genUserSig(String userid, long expire, byte[] userbuf) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); long currTime = System.currentTimeMillis() / 1000; JSONObject sigDoc = new JSONObject(); sigDoc.put("TLS.ver", "2.0"); sigDoc.put("TLS.identifier", userid); sigDoc.put("TLS.sdkappid", tencentIm.getSdkAppId()); sigDoc.put("TLS.expire", expire); sigDoc.put("TLS.time", currTime); String base64UserBuf = null; if (null != userbuf) { base64UserBuf = Base64.getEncoder().encodeToString(userbuf).replaceAll("\\s*", ""); sigDoc.put("TLS.userbuf", base64UserBuf); } String sig = hmacsha256(userid, currTime, expire, base64UserBuf); if (sig.length() == 0) { return ""; } sigDoc.put("TLS.sig", sig); Deflater compressor = new Deflater(); compressor.setInput(sigDoc.toString().getBytes(StandardCharsets.UTF_8)); compressor.finish(); byte[] compressedBytes = new byte[2048]; int compressedBytesLength = compressor.deflate(compressedBytes); compressor.end(); return (new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)))).replaceAll("\\s*", ""); } public byte[] genUserBuf(String account, long dwAuthID, long dwExpTime, long dwPrivilegeMap, long dwAccountType, String RoomStr) { //视频校验位需要用到的字段,按照网络字节序放入buf中 /* cVer unsigned char/1 版本号,填0 wAccountLen unsigned short /2 第三方自己的帐号长度 account wAccountLen 第三方自己的帐号字符 dwSdkAppid unsigned int/4 sdkappid dwAuthID unsigned int/4 群组号码 dwExpTime unsigned int/4 过期时间 ,直接使用填入的值 dwPrivilegeMap unsigned int/4 权限位,主播0xff,观众0xab dwAccountType unsigned int/4 第三方帐号类型 */ //The fields required for the video check digit are placed in buf according to the network byte order. /* cVer unsigned char/1 Version number, fill in 0 wAccountLen unsigned short /2 Third party's own account length account wAccountLen Third party's own account characters dwSdkAppid unsigned int/4 sdkappid dwAuthID unsigned int/4 group number dwExpTime unsigned int/4 Expiration time , use the filled value directly dwPrivilegeMap unsigned int/4 Permission bits, host 0xff, audience 0xab dwAccountType unsigned int/4 Third-party account type */ long sdkAppId = applicationProperties.getTencentIm().getSdkAppId(); int accountLength = account.length(); int roomStrLength = RoomStr.length(); int offset = 0; int bufLength = 1 + 2 + accountLength + 20; if (roomStrLength > 0) { bufLength = bufLength + 2 + roomStrLength; } byte[] userbuf = new byte[bufLength]; //cVer if (roomStrLength > 0) { userbuf[offset++] = 1; } else { userbuf[offset++] = 0; } //wAccountLen userbuf[offset++] = (byte) ((accountLength & 0xFF00) >> 8); userbuf[offset++] = (byte) (accountLength & 0x00FF); //account for (; offset < 3 + accountLength; ++offset) { userbuf[offset] = (byte) account.charAt(offset - 3); } //dwSdkAppid userbuf[offset++] = (byte) ((sdkAppId & 0xFF000000) >> 24); userbuf[offset++] = (byte) ((sdkAppId & 0x00FF0000) >> 16); userbuf[offset++] = (byte) ((sdkAppId & 0x0000FF00) >> 8); userbuf[offset++] = (byte) (sdkAppId & 0x000000FF); //dwAuthId,房间号 //dwAuthId, room number userbuf[offset++] = (byte) ((dwAuthID & 0xFF000000) >> 24); userbuf[offset++] = (byte) ((dwAuthID & 0x00FF0000) >> 16); userbuf[offset++] = (byte) ((dwAuthID & 0x0000FF00) >> 8); userbuf[offset++] = (byte) (dwAuthID & 0x000000FF); //expire,过期时间,当前时间 + 有效期(单位:秒) //expire,Expiration time, current time + validity period (unit: seconds) long currTime = System.currentTimeMillis() / 1000; long expire = currTime + dwExpTime; userbuf[offset++] = (byte) ((expire & 0xFF000000) >> 24); userbuf[offset++] = (byte) ((expire & 0x00FF0000) >> 16); userbuf[offset++] = (byte) ((expire & 0x0000FF00) >> 8); userbuf[offset++] = (byte) (expire & 0x000000FF); //dwPrivilegeMap,权限位 //dwPrivilegeMap,Permission bits userbuf[offset++] = (byte) ((dwPrivilegeMap & 0xFF000000) >> 24); userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x00FF0000) >> 16); userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x0000FF00) >> 8); userbuf[offset++] = (byte) (dwPrivilegeMap & 0x000000FF); //dwAccountType,账户类型 //dwAccountType,account type userbuf[offset++] = (byte) ((dwAccountType & 0xFF000000) >> 24); userbuf[offset++] = (byte) ((dwAccountType & 0x00FF0000) >> 16); userbuf[offset++] = (byte) ((dwAccountType & 0x0000FF00) >> 8); userbuf[offset++] = (byte) (dwAccountType & 0x000000FF); if (roomStrLength > 0) { //roomStrLen userbuf[offset++] = (byte) ((roomStrLength & 0xFF00) >> 8); userbuf[offset++] = (byte) (roomStrLength & 0x00FF); //roomStr for (; offset < bufLength; ++offset) { userbuf[offset] = (byte) RoomStr.charAt(offset - (bufLength - roomStrLength)); } } return userbuf; } public static byte[] base64EncodeUrl(byte[] input) { byte[] base64 = Base64.getEncoder().encode(input); for (int i = 0; i < base64.length; ++i) switch (base64[i]) { case '+': base64[i] = '*'; break; case '/': base64[i] = '-'; break; case '=': base64[i] = '_'; break; default: break; } return base64; } }
@Component @Slf4j @RequiredArgsConstructor public class IMService { private final IMUserSigService userSigService; private final TencentIMService tencentIMService; private final ApplicationProperties applicationProperties; /** * 生成腾讯云用户的UserSig * * @param userid 腾讯IM用户ID * @return UserSig */ public String genUserSig(String userid) { return userSigService.genUserSig(userid, 30 * 24 * 3600); } /** * 查询用户 * * @param users 用户列表 * @return List 不存在的IM用户 */ public List<String> checkAccount(List<String> users) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); List<IMCheckAccountItemVM> accountItems = users.stream().map(u -> { IMCheckAccountItemVM single = new IMCheckAccountItemVM(); single.setUserId(u); return single; }).toList(); IMCheckAccountVM accounts = IMCheckAccountVM.builder().items(accountItems).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.checkAccount(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, accounts); log.info("im -> account-> import -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); if (!CharSequenceUtil.equals("OK", result.getStr("ActionStatus"))) { throw new SystemProblem(AppStatus.FAILURE, "查询IM账号出现错误!"); } List<String> noExistAccounts = new ArrayList<>(); JSONArray resultItem = result.getJSONArray("ResultItem"); resultItem.forEach(item -> { JSONObject itemObj = JSONUtil.parseObj(item); if (itemObj.getInt("ResultCode") == 0 && CharSequenceUtil.equals("NotImported", itemObj.getStr("AccountStatus"))) { noExistAccounts.add(itemObj.getStr("UserID")); } }); return noExistAccounts; } /** * 导入单个用户 * * @param userId 用户ID * @param nick 名称 * @param faceUrl 头像 * @return boolean */ public boolean importSingleAccount(String userId, String nick, String faceUrl) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); IMSingleAccountVM singleAccount = IMSingleAccountVM.builder().userId(userId).nick(nick).faceUrl(faceUrl).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.importSingleAccount(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, singleAccount); log.info("im -> account-> import -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 导入多个用户 * * @param userIds 用户ID列表 * @return boolean */ public boolean importMultiAccount(List<IMUserInfo> userIds) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); List<IMSingleAccountVM> accounts = userIds.stream().map(u -> { IMSingleAccountVM single = new IMSingleAccountVM(); single.setUserId(u.getUserId()); single.setNick(u.getNick()); single.setFaceUrl(u.getFaceUrl()); return single; }).toList(); IMMultiAccountVM multiAccount = IMMultiAccountVM.builder().accounts(accounts).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.importMultiAccount(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, multiAccount); log.info("im -> account-> import -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 创建群组 * * @param owner 群主 * @param groupName 群组名称 * @param type 群组类型:Private/Public/ChatRoom/Community(不支持AVChatRoom)(必填) * @param members 成员列表 * @return boolean */ public String createGroup(String owner, String groupName, String type, List<String> members) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); //群组信息 List<IMGroupMemberVM> memberList = members.stream().map( m -> { IMGroupMemberVM member = new IMGroupMemberVM(); member.setMemberAccount(m); return member; }).collect(Collectors.toList()); IMGroupVM group = IMGroupVM.builder().ownerAccount(owner).name(groupName).type(type).memberList(memberList).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.createGroup(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, group); String errorInfo = result.getStr("ErrorInfo"); log.info("im -> create-> group -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); if (!CharSequenceUtil.equals("OK", result.getStr("ActionStatus"))) { throw new SystemProblem(AppStatus.FAILURE, "create group occurs to exception : " + errorInfo); } return result.getStr("GroupId"); } /** * 导入群组成员 * * @param groupId 群组ID * @param members 群组成员 * @return boolean */ public String importGroupMember(String groupId, List<String> members) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); //群组信息 List<IMGroupMemberVM> memberList = members.stream().map( m -> { IMGroupMemberVM member = new IMGroupMemberVM(); member.setMemberAccount(m); return member; }).collect(Collectors.toList()); IMGroupVM group = IMGroupVM.builder().groupId(groupId).memberList(memberList).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.importGroupMember(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, group); String errorInfo = result.getStr("ErrorInfo"); log.info("im -> import-> group-member -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); if (!CharSequenceUtil.equals("OK", result.getStr("ActionStatus"))) { throw new SystemProblem(AppStatus.FAILURE, "create group occurs to exception : " + errorInfo); } return result.getStr("GroupId"); } /** * 向群组发送普通消息 * * @param groupId 群组ID * @param fromAccount 指定账号发送 * @param msgBodyList 消息体 * @return boolean */ public boolean sendGroupMsg(String groupId, String fromAccount, List<IMGroupMsgBodyVM> msgBodyList) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); //群组信息 IMGroupMsgVM groupMsg = IMGroupMsgVM.builder().groupId(groupId).fromAccount(fromAccount) .msgBody(msgBodyList).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.sendGroupMsg(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, groupMsg); log.info("im -> send-> group-msg -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 向群组发送系统消息 * * @param groupId 群组ID * @param content 系统消息 * @return boolean */ public boolean sendGroupSystemMsg(String groupId, String content) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); //系统信息 IMGroupSystemMsgVM groupSystemMsg = IMGroupSystemMsgVM.builder().groupId(groupId).content(content).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.sendGroupSystemNotification(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, groupSystemMsg); log.info("im -> send-> system-msg -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 创建机器人用户 * * @param userId 用户ID * @param nick 名称 * @param faceUrl 头像 * @return boolean */ public boolean createRobot(String userId, String nick, String faceUrl) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); IMSingleAccountVM singleAccount = IMSingleAccountVM.builder().userId(userId).nick(nick).faceUrl(faceUrl).build(); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); JSONObject result = tencentIMService.createRobot(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, singleAccount); log.info("im -> create-> robot -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 发送单聊消息 * * @param fromAccount 指定账号发送 * @param toAccount 指定账号接受 * @param msgBodyList 消息体 * @return boolean */ public boolean sendMsg(String fromAccount, String toAccount, List<IMGroupMsgBodyVM> msgBodyList) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); //群组信息 IMMsgVM msgVM = IMMsgVM.builder().fromAccount(fromAccount).toAccount(toAccount) .msgBody(msgBodyList).random(random).build(); JSONObject result = tencentIMService.sendMsg(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, msgVM); log.info("im -> send-> msg -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); return CharSequenceUtil.equals("OK", result.getStr("ActionStatus")); } /** * 设置用户资料 * * @param userId 用户ID * @param nick 昵称 * @param faceUrl 头像 */ public void setAccount(Long userId, String nick, String faceUrl) { List<String> accounts = new ArrayList<>(); accounts.add("PATIENT_" + userId); //不存在账号 List<String> noExists = checkAccount(accounts); accounts.removeAll(noExists); if (CollUtil.isNotEmpty(accounts)) { List<IMProfileItemVM> profileItems = new ArrayList<>(); profileItems.add(IMProfileItemVM.builder().tag("Tag_Profile_IM_Nick").value(nick).build()); // profileItems.add(IMProfileItemVM.builder().tag("Tag_Profile_IM_Image").value(faceUrl).build()); for (String account : accounts) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); IMProfileVM profileVM = IMProfileVM.builder().fromAccount(account).profileItem(profileItems).build(); JSONObject result = tencentIMService.setAccount(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, profileVM); log.info("im -> send-> msg -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); } } } /** * 管理员指定 reportAccount 将会话 peerAccount 的单聊未读计数清除。 * * @param reportAccount 进行会话未读计数清理的用户 UserId * @param peerAccount 单聊会话的另一方用户 UserId */ public void setMsgRead(String reportAccount, String peerAccount) { ApplicationProperties.TencentIm tencentIm = applicationProperties.getTencentIm(); String userSig = genUserSig(tencentIm.getIdentifier()); int random = RandomUtil.randomInt(0, Integer.MAX_VALUE); Map<String, String> content = new HashMap<>(); content.put("Report_Account", reportAccount); content.put("Peer_Account", peerAccount); JSONObject result = tencentIMService.adminSetMsgRead(tencentIm.getSdkAppId(), tencentIm.getIdentifier(), tencentIm.getContenttype(), userSig, random, JSONUtil.toJsonStr(content)); log.info("im -> send-> msg -> result:[{}],[{}]", result.getStr("ErrorCode"), result.getStr("ErrorInfo")); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。