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;
}
}