SpringBoot集成腾讯IM流程

1.application.yaml中添加IM配置信息

#im模块
im:  
  identifier: admin  
  sdkappid: 1400888888  
  key: ccf2dc88c1ca232cfabbd24906d5091ab81ba0250224abc

2.封装IM工具类

Component
@Getter
@RefreshScope
public class ImAdminSignConfig {
    /**
     * 签名
     */
    private String usersig;

    /**
     * 管理员id
     */
    @Value("${im.identifier}")
    private String identifier;

    /**
     * im容器id
     */
    @Value("${im.sdkappid}")
    private long sdkappid;

    /**
     * im容器key
     */
    @Value("${im.key}")
    private String key;

    /**
     * im的api请求参数集合
     */
    private HashMap<String, String> uriParams;

    @PostConstruct
    void init() {
        usersig = TencentImUserSignUtil.genUserSig(identifier, 60 * 60 * 24 * 180, sdkappid, key);
        uriParams = new HashMap<>(16);
        // 半年签名
        uriParams.put("usersig", usersig);
        uriParams.put("identifier", identifier);
        uriParams.put("sdkappid", String.valueOf(sdkappid));
    }

    /**
     * 腾讯im签名
     *
     */
    public String genUserSig(String usersig, long expire) {
        return TencentImUserSignUtil.genUserSig(usersig, expire, sdkappid, key);
    }

    /**
     * 腾讯im签名
     *
     */
    public static class TencentImUserSignUtil {
        /**
         * 【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据
         * <p>
         * 【参数说明】
         *
         * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
         * @param expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。
         * @return usersig -生成的签名
         */
        public static String genUserSig(String userid, long expire, long sdkappid, String key) {
            return genUserSig(userid, expire, null, sdkappid, key);
        }

        private static String hmacsha256(String identifier, long currTime, long expire, String base64Userbuf, long sdkappid, String key) {
            String contentToBeSigned = "TLS.identifier:" + identifier + "\n"
                    + "TLS.sdkappid:" + sdkappid + "\n"
                    + "TLS.time:" + currTime + "\n"
                    + "TLS.expire:" + expire + "\n";
            if (null != base64Userbuf) {
                contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
            }
            try {
                byte[] byteKey = key.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 static String genUserSig(String userid, long expire, byte[] userbuf, long sdkappid, String key) {
            long currTime = System.currentTimeMillis() / 1000;
            JSONObject sigDoc = new JSONObject();
            sigDoc.put("TLS.ver", "2.0");
            sigDoc.put("TLS.identifier", userid);
            sigDoc.put("TLS.sdkappid", sdkappid);
            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, sdkappid, key);
            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(Base64Url.base64EncodeUrl(Arrays.copyOfRange(compressedBytes,
                    0, compressedBytesLength)))).replaceAll("\\s*", "");
        }
    }
}

3.封装IM API接口路径

/**
 * 腾讯im的api路径
 *
 */
@Slf4j
@Component
public class ImApiServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImApiServer.class);

    @Resource
    private ImAdminSignConfig signConfig;

    /**
     * im的url
     */
    private final String BASE_URL = "https://console.tim.qq.com";

    /**
     * uri基本参数模板
     */
    private final String PARAMS_TEM = "?usersig=%s&identifier=%s&sdkappid=%s&contenttype=json";

    /**
     * 签名
     *
     * @param userId 用户ID
     * @param time   时间(单位:秒)
     * @return 签名
     */
    public String userSign(String userId, long time) {
        return signConfig.genUserSig(userId, time);
    }

    /**
     * 创建群组
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String createGroup(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/create_group";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 解散群组
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String destroyGroup(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/destroy_group";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 修改群组基本信息
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String modifyGroupBaseInfo(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/modify_group_base_info";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 增加群成员
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String addGroupMember(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/add_group_member";
        return postImApi(rawJsonStr, apiUrl);
    }


    /**
     * 删除群成员
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String deleteGroupMember(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/delete_group_member";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 导入单个账号
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String accountImport(String rawJsonStr) {
        String apiUrl = "/v4/im_open_login_svc/account_import";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 批量导入账号
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String accountImportBatch(String rawJsonStr) {
        String apiUrl = "/v4/im_open_login_svc/multiaccount_import";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 批量删除账号
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String accountDelete(String rawJsonStr) {
        String apiUrl = "/v4/im_open_login_svc/account_delete";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 设置资料
     * 支持 标配资料字段 和 自定义资料字段 的设置。
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String portraitSet(String rawJsonStr) {
        String apiUrl = "/v4/profile/portrait_set";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 群里发送消息
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String sendGroupMsg(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/send_group_msg";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 群里发送系统消息
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String sendGroupSystemMsg(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/send_group_system_notification";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * im请求基础api
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    private String postImApi(String rawJsonStr, String apiUrl) {
        String runningId = IdUtil.simpleUUID();
        String paramsUrl = String.format(PARAMS_TEM, signConfig.getUsersig(), signConfig.getIdentifier(), signConfig.getSdkappid());
        String url = BASE_URL + apiUrl + paramsUrl;
        LOGGER.info("请求im接口, 流水号[{}], url[{}], 入参[{}]", runningId, url, rawJsonStr);
        String res = this.httpPost(url, rawJsonStr);
        LOGGER.info("im接口响应, 流水号[{}], url[{}], 响应[{}]", runningId, url, rawJsonStr);
        return res;
    }

    /**
     * POST
     *
     * @param url  链接
     * @param data body
     * @return 响应
     */
    private String httpPost(String url, String data) {
        String result = null;
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            StringEntity stringEntity = new StringEntity(data, "UTF-8");
            stringEntity.setContentEncoding("UTF-8");
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Content-Type", "application/json");
            httpPost.setEntity(stringEntity);
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    result = EntityUtils.toString(resEntity, "utf-8");
                }
            }
        } catch (Exception e) {
            log.error("im请求异常", e);
        }
        return result;
    }


    /**
     * 发送自定义消息,用来消息计数
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String sendCustomMessageCount(String rawJsonStr) {
        String apiUrl = "/v4/openim/sendmsg";
        return postImApi(rawJsonStr, apiUrl);
    }

    /**
     * 群组批量禁言取消禁言
     *
     * @param rawJsonStr raw实体
     * @return 响应参数json
     */
    public String groupForbiddenWordBatch(String rawJsonStr) {
        String apiUrl = "/v4/group_open_http_svc/forbid_send_msg";
        return postImApi(rawJsonStr, apiUrl);
    }
}

4.封装IM Service接口

/**
 * im用户相关服务
 *
 */
public interface ImUserService {
    /**
     * 给user签名
     *
     * @param userId 用户ID(用户唯一键均可,可以为中文)
     * @return 签名
     */
    String userSign(String userId);

    /**
     * 注册User到im
     *
     * @param user 用户对象实体类
     * @return userId
     */
    String registerUser(ImRegisterUserReq user);

    /**
     * 批量删除im中的user
     *
     * @param userIds 用户ID集合
     * @return 是否成功
     */
    List<ImUserResp> removeUser(List<String> userIds);

    /**
     * 修改user基本信息
     *
     * @param user 用户对象实体类
     * @return 是否成功
     */
    boolean updateUser(ImUpdateUserReq user);

    /**
     * 发送自定义消息,用来消息计数
     *
     * @param request
     * @return 发送成功
     */
    boolean sendCustomMessageCount(ImCustomMsgReq request);

    /**
     * 批量注册IM
     *
     * @param imRegisterUserList
     */
    void registerUserBatch(List<ImRegisterUserReq> imRegisterUserList);
}

5.封装IM Service接口实现类

/**
 * im用户相关服务
 *
 */
@Service
public class ImUserServiceImpl implements ImUserService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ImUserServiceImpl.class);

    /**
     * im api
     */
    @Resource
    private ImApiServer imApiServer;

    /**
     * 给user签名
     * 默认签名7天
     *
     * @param userId 用户ID(用户唯一键均可,可以为中文)
     * @return 签名
     */
    @Override
    public String userSign(String userId) {
        // 默认签名7天
        long time = 60 * 60 * 24 * 7;
        String sign = imApiServer.userSign(userId, time);
        LOGGER.info("给user签名,userId[{}], 签名[{}]", userId, sign);
        return sign;
    }

    /**
     * 注册User到im
     *
     * @param user 用户对象实体类
     * @return userId
     */
    @Override
    public String registerUser(ImRegisterUserReq user) {
        LOGGER.info("注册User到im,参数[{}]", JacksonUtil.toJsonString(user));
        ImApiUserModelReq rawJson = new ImApiUserModelReq()
                .setUserId(user.getUserId())
                .setNick(user.getNick())
                .setFaceUrl(user.getFaceUrl());
        String recordJson = imApiServer.accountImport(JacksonUtil.toJsonString(rawJson));
        //断言返回体是否存在
        if (recordJson == null) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_001.value,UserErrorEnum.IM_001.desc);
        }
        ImApiRecordReq imApiRecord = JacksonUtil.fromJson(recordJson, ImApiRecordReq.class);
        //断言接口业务是否成功
        if (imApiRecord.getErrorCode() != ImApiRecordReq.SUCCESS) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_002.value, UserErrorEnum.IM_002.desc + "-->" + recordJson);
        }
        //增加自定义参数,登录账号
        String account = user.getAttrAccount();
        if (account != null) {
            ImApiUserModelReq rawAttrJson = new ImApiUserModelReq()
                    .setFromAccount(user.getUserId())
                    .setProfileItem(Collections.singletonList(new ImApiUserModelReq.ProfileItem(ImApiUserModelReq.ProfileItem.TAG_ACCOUNT, account)));
            String recordAttrJson = imApiServer.portraitSet(JacksonUtil.toJsonString(rawAttrJson));
        }
        return user.getUserId();
    }

    /**
     * 批量删除im中的user
     *
     * @param userIds 用户ID集合
     * @return 是否成功
     */
    @Override
    public List<ImUserResp> removeUser(List<String> userIds) {
        LOGGER.info("批量删除im中的user,参数[{}]", JacksonUtil.toJsonString(userIds));
        ImApiUserModelReq rawJson = new ImApiUserModelReq()
                .setToDelUserList(userIds.stream().map(ImUserResp::new).collect(Collectors.toList()));
        String recordJson = imApiServer.accountDelete(JacksonUtil.toJsonString(rawJson));
        //断言返回体是否存在
        if (recordJson == null) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_001.value,UserErrorEnum.IM_001.desc);
        }
        ImApiRecordReq imApiRecord = JacksonUtil.fromJson(recordJson, ImApiRecordReq.class);
        //断言接口业务是否成功
        if (imApiRecord.getErrorCode() != ImApiRecordReq.SUCCESS) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_002.value, UserErrorEnum.IM_002.desc + "-->" + recordJson);
        }
        return imApiRecord.getDelUserInfoList();
    }

    /**
     * 修改user基本信息
     *
     * @param user 用户对象实体类
     * @return 是否成功
     */
    @Override
    public boolean updateUser(ImUpdateUserReq user) {
        LOGGER.info("修改user基本信息,参数[{}]", JacksonUtil.toJsonString(user));
        ImApiUserModelReq rawJson = new ImApiUserModelReq()
                .setUserId(user.getUserId())
                .setNick(user.getNick())
                .setFaceUrl(user.getFaceUrl());
        String recordJson = imApiServer.accountImport(JacksonUtil.toJsonString(rawJson));
        //断言返回体是否存在
        if (recordJson == null) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_001.value,UserErrorEnum.IM_001.desc);
        }
        ImApiRecordReq imApiRecord = JacksonUtil.fromJson(recordJson, ImApiRecordReq.class);
        //断言接口业务是否成功
        if (imApiRecord.getErrorCode() != ImApiRecordReq.SUCCESS) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_002.value, UserErrorEnum.IM_002.desc + "-->" + recordJson);
        }
        //增加自定义参数
        List<ImApiUserModelReq.ProfileItem> profileItem = new ArrayList<>();
        String attrAccount = user.getAttrAccount();
        if (attrAccount != null) {
            profileItem.add(new ImApiUserModelReq.ProfileItem(ImApiUserModelReq.ProfileItem.TAG_ACCOUNT, attrAccount));
        }
        ImApiUserModelReq rawAttrJson = new ImApiUserModelReq()
                .setFromAccount(user.getUserId())
                .setProfileItem(profileItem);
        String recordAttrJson = imApiServer.portraitSet(JacksonUtil.toJsonString(rawAttrJson));
        return true;
    }

    /**
     * 发送自定义消息,用来消息计数
     *
     * @param request
     * @return 发送成功
     */
    @Override
    public boolean sendCustomMessageCount(ImCustomMsgReq request) {
        ImMsgContentReq imMsgContentRequest = new ImMsgContentReq()
                .setDesc(request.getDesc())
                .setData(request.getData())
                .setExt(request.getExt())
                .setSound(request.getSound());
        ImMsgBodyReq imMsgBodyRequest = new ImMsgBodyReq()
                .setMsgContent(imMsgContentRequest)
                .setMsgType("TIMCustomElem");
        List<ImMsgBodyReq> imMsgBodyRequestList = new ArrayList<>();
        imMsgBodyRequestList.add(imMsgBodyRequest);
        ImCustomMsgReq rawJson = new ImCustomMsgReq()
                .setSyncOtherMachine(2)
                .setToAccount(request.getToAccount())
                .setMsgRandom(Integer.parseInt(getRandomNickname(8)))
                .setMsgTimeStamp(Integer.parseInt(String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")))))
                .setForbidCallbackControl(Arrays.asList("ForbidBeforeSendMsgCallback", "ForbidAfterSendMsgCallback").toArray())
                .setMsgBody(imMsgBodyRequestList);

        String recordJson = imApiServer.sendCustomMessageCount(JacksonUtil.toJsonString(rawJson));
        //断言返回体是否存在
        if (recordJson == null) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_001.value,UserErrorEnum.IM_001.desc);
        }
        ImApiRecordReq imApiRecord = JacksonUtil.fromJson(recordJson, ImApiRecordReq.class);
        //断言接口业务是否成功
        if (imApiRecord.getErrorCode() != ImApiRecordReq.SUCCESS) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_002.value, UserErrorEnum.IM_002.desc + "-->" + recordJson);
        }

        return Boolean.TRUE;
    }

    /**
     * 批量注册IM
     *
     * @param systemImRegisterUserList
     */
    @Override
    public void registerUserBatch(List<ImRegisterUserReq> systemImRegisterUserList) {
        LOGGER.info("批量注册User到im,参数[{}]", JacksonUtil.toJsonString(systemImRegisterUserList));
        List<String> userIdList = systemImRegisterUserList.stream().map(o -> o.getUserId()).collect(Collectors.toList());
        ImApiUserModelReq rawJson = new ImApiUserModelReq()
                .setAccounts(userIdList);
        String recordJson = imApiServer.accountImportBatch(JacksonUtil.toJsonString(rawJson));
        //断言返回体是否存在
        if (recordJson == null) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_001.value,UserErrorEnum.IM_001.desc);
        }
        ImApiRecordReq imApiRecord = JacksonUtil.fromJson(recordJson, ImApiRecordReq.class);
        //断言接口业务是否成功
        if (imApiRecord.getErrorCode() != ImApiRecordReq.SUCCESS) {
            throw BizExceptionFactory.newBizException(UserErrorEnum.IM_002.value, UserErrorEnum.IM_002.desc + "-->" + recordJson);
        }
        //增加自定义参数
        systemImRegisterUserList.forEach(user -> {
            String account = user.getAttrAccount();
            if (account != null) {
                ImApiUserModelReq rawAttrJson = new ImApiUserModelReq()
                        .setFromAccount(user.getUserId())
                        .setProfileItem(Collections.singletonList(new ImApiUserModelReq.ProfileItem(ImApiUserModelReq.ProfileItem.TAG_ACCOUNT, account)));
                String resp = imApiServer.portraitSet(JacksonUtil.toJsonString(rawAttrJson));
                LOGGER.info("批量注册User到im,返回信息[{}]", resp);
            }
        });
    }

    /**
     * java生成随机数字10位数
     *
     * @param length [生成随机数的长度]
     * @return
     */
    public static String getRandomNickname(int length) {
        StringBuilder val = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            val.append(random.nextInt(10));
        }
        return val.toString();
    }

}

6.在业务代码中调用IM接口方法

 //发送系统消息
ImCustomMsgReq imCustomMsgReq = new ImCustomMsgReq();
imCustomMsgReq.setData(desc)
                    .setDesc(desc)
                    .setToAccount(userId.toString());
 imUserService.sendCustomMessageCount(imCustomMsgReq);

7.集成过程中用到的相关工具类

ImMsgBodyReq.java

@Getter
@Setter
@ToString
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class ImMsgBodyReq {
    /**
     * 标签名
     */
    @JsonProperty("MsgType")
    private String msgType;

    /**
     * 值
     */
    @JsonProperty("MsgContent")
    private ImMsgContentReq msgContent;
}

ImMsgContentReq.java

@Getter
@Setter
@ToString
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class ImMsgContentReq {
    /**
     * 自定义消息数据。 不作为 APNs 的 payload 字段下发,故从 payload 中无法获取 Data 字段
     */
    @JsonProperty("Data")
    private String data;
    /**
     * 说明
     * 当消息中只有一个 TIMCustomElem 自定义消息元素时,如果 Desc 字段和 OfflinePushInfo.Desc 字段都不填写,
     * 将收不到该条消息的离线推送,需要填写 OfflinePushInfo.Desc 字段才能收到该消息的离线推送。
     */
    @JsonProperty("Desc")
    private String desc;
    /**
     * 扩展字段。当接收方为 iOS 系统且应用处在后台时,此字段作为 APNs 请求包 Payloads 中的 Ext 键值下发,Ext 的协议格式由业务方确定,APNs 只做透传。
     */
    @JsonProperty("Ext")
    private String ext;
    /**
     * 自定义 APNs 推送铃音。
     */
    @JsonProperty("Sound")
    private String sound;
}

ImCustomMsgReq.java

/**
 * im 自定义单聊消息
 */
@Getter
@Setter
@ToString
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ImCustomMsgReq implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 1:把消息同步到 From_Account 在线终端和漫游上;
     * 2:消息不同步至 From_Account;
     * 若不填写默认情况下会将消息存 From_Account 漫游
     * 选填
     */
    @JsonProperty("SyncOtherMachine")
    private Integer syncOtherMachine;

    /**
     * 消息接收方 UserID
     * 必填
     */
    @JsonProperty("To_Account")
    private String toAccount;

    /**
     * 消息离线保存时长(单位:秒),最长为7天(604800秒)
     * 若设置该字段为0,则消息只发在线用户,不保存离线
     * 若设置该字段超过7天(604800秒),仍只保存7天
     * 若不设置该字段,则默认保存7天
     * 选填
     */
    @JsonProperty("MsgLifeTime")
    private Integer msgLifeTime;

    /**
     * 消息序列号,后台会根据该字段去重及进行同秒内消息的排序,详细规则请看本接口的功能说明。若不填该字段,则由后台填入随机数。
     * 选填
     */
    @JsonProperty("MsgSeq")
    private Integer msgSeq;

    /**
     * 消息随机数,后台用于同一秒内的消息去重。请确保该字段填的是随机数
     * 必填
     */
    @JsonProperty("MsgRandom")
    private Integer msgRandom;

    /**
     * 消息时间戳,UNIX 时间戳(单位:秒)
     * 选填
     */
    @JsonProperty("MsgTimeStamp")
    private Integer msgTimeStamp;

    /**
     * 消息回调禁止开关,只对本条消息有效,ForbidBeforeSendMsgCallback 表示禁止发消息前回调,
     * ForbidAfterSendMsgCallback 表示禁止发消息后回调
     * 选填
     */
    @JsonProperty("ForbidCallbackControl")
    private Object[] forbidCallbackControl;

    /**
     * 消息发送控制选项,是一个 String 数组,只对本条消息有效。"NoUnread"表示该条消息不计入未读数。
     * "NoLastMsg"表示该条消息不更新会话列表。"WithMuteNotifications"表示该条消息的接收方对发送方设置的免打扰选项生效(默认不生效)。
     * 示例:"SendMsgControl": ["NoUnread","NoLastMsg","WithMuteNotifications"]
     * 选填
     */
    @JsonProperty("SendMsgControl")
    private Array sendMsgControl;

    /**
     * 消息内容,具体格式请参考 消息格式描述(注意,一条消息可包括多种消息元素,MsgBody 为 Array 类型)
     * 必填
     */
    @JsonProperty("MsgBody")
    private List<ImMsgBodyReq> msgBody;

    /**
     * TIM 消息对象类型,目前支持的消息对象包括:
     * TIMTextElem(文本消息)
     * TIMLocationElem(位置消息)
     * TIMFaceElem(表情消息)
     * TIMCustomElem(自定义消息)
     * TIMSoundElem(语音消息)
     * TIMImageElem(图像消息)
     * TIMFileElem(文件消息)
     * TIMVideoFileElem(视频消息)
     * 必填
     */
    @JsonProperty("MsgType")
    private String msgType;

    /**
     * 对于每种 MsgType 用不同的 MsgContent 格式,具体可参考
     * 必填
     */
    @JsonProperty("MsgContent")
    private Object msgContent;

    /**
     * 消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到)
     * 选填
     */
    @JsonProperty("CloudCustomData")
    private String cloudCustomData;

    /**
     * 离线推送信息配置,具体可参考
     * 选填
     */
    @JsonProperty("OfflinePushInfo")
    private Object offlinePushInfo;

    /**
     * 自定义消息数据。 不作为 APNs 的 payload 字段下发,故从 payload 中无法获取 Data 字段
     */
    @JsonProperty("Data")
    private String data;
    /**
     * 说明
     * 当消息中只有一个 TIMCustomElem 自定义消息元素时,如果 Desc 字段和 OfflinePushInfo.Desc 字段都不填写,
     * 将收不到该条消息的离线推送,需要填写 OfflinePushInfo.Desc 字段才能收到该消息的离线推送。
     */
    @JsonProperty("Desc")
    private String desc;
    /**
     * 扩展字段。当接收方为 iOS 系统且应用处在后台时,此字段作为 APNs 请求包 Payloads 中的 Ext 键值下发,Ext 的协议格式由业务方确定,APNs 只做透传。
     */
    @JsonProperty("Ext")
    private String ext;
    /**
     * 自定义 APNs 推送铃音。
     */
    @JsonProperty("Sound")
    private String sound;

}
Base64Url.java
public class Base64Url {
    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;
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值