REQ-3201: push
This commit is contained in:
parent
4375d6478e
commit
de49f0ea09
@ -59,7 +59,7 @@ public class CustomMessageInfo {
|
||||
*/
|
||||
private Map<String, Object> personId2BizInfo = new HashMap<>();
|
||||
|
||||
private boolean isPush = false;
|
||||
private PushContent pushContent;
|
||||
|
||||
public void addBizInfo(String personId, Object bizInfo) {
|
||||
if (personId2BizInfo == null)
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class PushContent {
|
||||
|
||||
/**
|
||||
* 主标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 推送内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* android点击push的跳转链接
|
||||
*/
|
||||
private String androidPushUrl;
|
||||
|
||||
/**
|
||||
* ios点击push的跳转链接
|
||||
*/
|
||||
private String iosPushUrl;
|
||||
|
||||
/**
|
||||
* push消息类型. SYSTEM: 系统消息, OP: 运营消息
|
||||
*/
|
||||
private PushMessageTye messageTye;
|
||||
|
||||
/**
|
||||
* 自定义提示音
|
||||
*/
|
||||
private String customSoundFile;
|
||||
|
||||
private Map<String, String> intent = new HashMap<>();
|
||||
|
||||
public void addIntent(String key, String value) {
|
||||
intent.put(key, value);
|
||||
}
|
||||
|
||||
public String determineAndroidCategory() {
|
||||
return messageTye == PushMessageTye.OP
|
||||
? "MARKETING"
|
||||
: "IM";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package cn.axzo.im.center.api.vo.req;
|
||||
|
||||
import cn.axzo.basics.common.constant.enums.CodeDefinition;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public enum PushMessageTye implements CodeDefinition<String> {
|
||||
|
||||
SYSTEM("system", "系统消息"),
|
||||
OP("op", "运营消息"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
@ -78,10 +78,7 @@ public class SendTemplateMessageParam {
|
||||
return sender == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送消息
|
||||
*/
|
||||
private String payload;
|
||||
private PushContent pushContent;
|
||||
|
||||
private List<ExcludePushPayload> excludePushPayloads;
|
||||
|
||||
|
||||
@ -136,7 +136,6 @@ public class MessageController implements MessageApi {
|
||||
// 全员发送是不常用的场景,不应该由业务处理,所以把配置放在bizData里面
|
||||
.allPerson(sendMessageParam.isAllPerson())
|
||||
.appTypes(sendMessageParam.getAppTypes())
|
||||
.payload(sendMessageParam.getPayload())
|
||||
.build();
|
||||
Date now = new Date();
|
||||
MessageTask messageTask = messageTaskService.create(MessageTask.builder()
|
||||
@ -197,7 +196,7 @@ public class MessageController implements MessageApi {
|
||||
MessageTask.BizData bizData = MessageTask.BizData.builder()
|
||||
.msgTemplateContent(request.getMsgTemplateContent())
|
||||
.msgTemplateId(request.getMsgTemplateId())
|
||||
.payload(request.getPayload())
|
||||
.pushContent(request.getPushContent())
|
||||
.excludePushPayloads(request.getExcludePushPayloads())
|
||||
.templatedMsgType(request.getTemplatedMsgType())
|
||||
.isSenderRobot(request.isSendByRobot())
|
||||
|
||||
@ -2,6 +2,7 @@ package cn.axzo.im.entity;
|
||||
|
||||
import cn.axzo.im.center.api.vo.ApiChannel;
|
||||
import cn.axzo.im.center.api.vo.req.ExcludePushPayload;
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.center.common.enums.BizTypeEnum;
|
||||
@ -134,11 +135,7 @@ public class MessageTask {
|
||||
*/
|
||||
private BizTypeEnum bizType;
|
||||
|
||||
/**
|
||||
* 网易云信-自定义消息使用
|
||||
* 推送内容 - 业务数据,json格式
|
||||
*/
|
||||
private String payload;
|
||||
private PushContent pushContent;
|
||||
|
||||
/**
|
||||
* 跳转信息
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
package cn.axzo.im.gateway;
|
||||
|
||||
import cn.axzo.maokai.api.client.OrganizationalNodeApi;
|
||||
import cn.axzo.maokai.api.vo.request.OrganizationalNodeBatchQueryVO;
|
||||
import cn.axzo.maokai.api.vo.response.OrganizationalNodeVO;
|
||||
import cn.axzo.pokonyan.util.RpcUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service("organizationalNodeApiGateway")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class OrganizationalNodeApiGateway {
|
||||
|
||||
private final OrganizationalNodeApi organizationalNodeApi;
|
||||
|
||||
public List<OrganizationalNodeVO> fetchNodesByNodeIds(List<Long> nodeIdList) {
|
||||
OrganizationalNodeBatchQueryVO organizationalNodeBatchQueryVO = new OrganizationalNodeBatchQueryVO();
|
||||
organizationalNodeBatchQueryVO.setNodeIds(nodeIdList);
|
||||
organizationalNodeBatchQueryVO.setContainsDeleted(false);
|
||||
List<OrganizationalNodeVO> nodeVOList = organizationalNodeApi.listNode(organizationalNodeBatchQueryVO).getData();
|
||||
return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeApi.listNode(organizationalNodeBatchQueryVO), "fetchNodesByNodeIds", JSON.toJSONString(nodeVOList));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package cn.axzo.im.push;
|
||||
|
||||
import cn.axzo.im.center.api.vo.ApiChannel;
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.payload.PushPayloadBuilder;
|
||||
import cn.axzo.im.push.payload.intent.Intent;
|
||||
import cn.axzo.im.push.payload.intent.IntentValue;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Service
|
||||
public class NimPushService {
|
||||
|
||||
private final PushPayloadBuilder<?>[] payloadBuilders;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public NimPushService(ObjectProvider<PushPayloadBuilder[]> payloadBuilders) {
|
||||
this.payloadBuilders = payloadBuilders.getIfAvailable();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public String buildPayload(PushContent content, PushPeer peer) {
|
||||
Consumer<Intent<?>> intentPopulator = intent -> {
|
||||
intent.setValue(Intent.INTENT_TYPE,
|
||||
peer.getApiChannel() == ApiChannel.CUSTOM_MESSAGE
|
||||
? IntentValue.TYPE_SERVER
|
||||
: IntentValue.TYPE_IM);
|
||||
intent.setValue(Intent.INTENT_SESSION_TYPE, IntentValue.CONSTANT_SESSION_TYPE);
|
||||
intent.setValue(Intent.INTENT_SESSION_ID, IntentValue.create(peer.getSenderImAccount()));
|
||||
if (peer.getOuId() != null && peer.getOuId() != 0L)
|
||||
intent.setValue(Intent.INTENT_OU_ID, IntentValue.create(peer.getOuId()));
|
||||
if (peer.getWorkspaceId() != null && peer.getWorkspaceId() != 0L)
|
||||
intent.setValue(Intent.INTENT_WORKSPACE_ID, IntentValue.create(peer.getWorkspaceId()));
|
||||
if (StringUtils.isNotBlank(content.getCustomSoundFile()))
|
||||
intent.setValue(Intent.INTENT_SOUND, IntentValue.create(content.getCustomSoundFile()));
|
||||
for (Map.Entry<String, String> e : content.getIntent().entrySet()) {
|
||||
if (e.getValue() == null) continue;
|
||||
intent.setValue(e.getKey(), IntentValue.create(e.getValue()));
|
||||
}
|
||||
};
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("pushTitle", content.getTitle());
|
||||
for (PushPayloadBuilder builder : payloadBuilders) {
|
||||
Intent intent = builder.createIntent(content);
|
||||
BizAssertions.assertNotNull(intent, "intent can't be null");
|
||||
intentPopulator.accept(intent);
|
||||
builder.build(peer, content, intent, payload);
|
||||
}
|
||||
return payload.toJSONString();
|
||||
}
|
||||
|
||||
}
|
||||
33
im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java
Normal file
33
im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java
Normal file
@ -0,0 +1,33 @@
|
||||
package cn.axzo.im.push;
|
||||
|
||||
import cn.axzo.im.center.api.vo.ApiChannel;
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.center.api.vo.req.PushMessageTye;
|
||||
import cn.axzo.im.center.common.enums.AppTypeEnum;
|
||||
import cn.axzo.im.push.PushProps.ChannelConfig;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class PushPeer {
|
||||
private Long ouId;
|
||||
private Long workspaceId;
|
||||
private String senderImAccount;
|
||||
private AppTypeEnum appType;
|
||||
private ApiChannel apiChannel;
|
||||
|
||||
public String determineChannelId(ChannelConfig config,
|
||||
PushContent content) {
|
||||
PushProps.ChannelIds channelIds = appType == AppTypeEnum.CM
|
||||
? config.getWorkerIds()
|
||||
: config.getManagerIds();
|
||||
return content.getMessageTye() == PushMessageTye.OP
|
||||
? channelIds.getOpMessageChannelId()
|
||||
: channelIds.getWorkMessageChannelId();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package cn.axzo.im.push;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
@RefreshScope
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "upush.channels")
|
||||
public class PushProps {
|
||||
|
||||
/**
|
||||
* oppo渠道配置
|
||||
*/
|
||||
private ChannelConfig oppoChannelConfig = new ChannelConfig(
|
||||
"oppo渠道配置",
|
||||
// 管理端
|
||||
//new ChannelIds("cmp", "67a686e9", "38a2db69"),
|
||||
// 工人端
|
||||
//new ChannelIds("cm", "203be53b", "3163b193"),
|
||||
new ChannelIds("cm", "12a12b80", "12a12b80"),
|
||||
new ChannelIds("cm", "12a12b80", "12a12b80"));
|
||||
|
||||
/**
|
||||
* 小米渠道配置
|
||||
*/
|
||||
private ChannelConfig xiaomiChannelConfig = new ChannelConfig(
|
||||
"小米渠道配置",
|
||||
// 管理端
|
||||
new ChannelIds("cmp", "122345", "122346"),
|
||||
// 工人端
|
||||
new ChannelIds("cm", "122431", "122432"));
|
||||
|
||||
/**
|
||||
* 不同app的渠道配置
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ChannelConfig {
|
||||
|
||||
/**
|
||||
* 渠道名称
|
||||
*/
|
||||
private String channelName;
|
||||
|
||||
/**
|
||||
* 管理端配置
|
||||
*/
|
||||
private ChannelIds managerIds;
|
||||
|
||||
/**
|
||||
* 工人端配置
|
||||
*/
|
||||
private ChannelIds workerIds;
|
||||
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 不同渠道的channelId配置
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ChannelIds {
|
||||
|
||||
private String appType;
|
||||
|
||||
/**
|
||||
* 个人工作信息渠道
|
||||
*/
|
||||
private String workMessageChannelId;
|
||||
|
||||
/**
|
||||
* 推广信息、运营信息渠道
|
||||
*/
|
||||
private String opMessageChannelId;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.payload.intent.Intent;
|
||||
import cn.axzo.im.push.payload.intent.IntentValue;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
abstract class AndroidPushPayloadBuilder<T extends Intent<?>>
|
||||
implements PushPayloadBuilder<T> {
|
||||
|
||||
@Override
|
||||
public final T createIntent(PushContent content) {
|
||||
T intent = createIntent();
|
||||
BizAssertions.assertNotNull(intent, "intent can't be null");
|
||||
if (StringUtils.isNotBlank(content.getAndroidPushUrl()))
|
||||
Intent.setRouter(intent, IntentValue
|
||||
.create(content.getAndroidPushUrl())
|
||||
.urlEncode());
|
||||
return intent;
|
||||
}
|
||||
|
||||
abstract T createIntent();
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.payload.intent.UriIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 华为
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
class HWPushPayloadBuilder extends AndroidPushPayloadBuilder<UriIntent> {
|
||||
|
||||
@Override
|
||||
UriIntent createIntent() {
|
||||
return new UriIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
UriIntent intent, JSONObject payload) {
|
||||
// 点击事件的内容
|
||||
JSONObject clickAction = new JSONObject();
|
||||
clickAction.put("type", 1);
|
||||
clickAction.put("intent", intent.build());
|
||||
|
||||
// 通知的内容
|
||||
|
||||
JSONObject androidConfig = new JSONObject();
|
||||
androidConfig.put("category", content.determineAndroidCategory());
|
||||
|
||||
JSONObject hwField = new JSONObject();
|
||||
hwField.put("android", androidConfig);
|
||||
hwField.put("style", 1);
|
||||
hwField.put("big_title", content.getTitle());
|
||||
hwField.put("big_body", content.getContent());
|
||||
hwField.put("click_action", clickAction);
|
||||
|
||||
payload.put("hwField", hwField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.PushProps;
|
||||
import cn.axzo.im.push.payload.intent.JsonIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class OppoPushPayloadBuilder extends AndroidPushPayloadBuilder<JsonIntent> {
|
||||
|
||||
private final PushProps pushProps;
|
||||
|
||||
@Override
|
||||
public JsonIntent createIntent() {
|
||||
return new JsonIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
JsonIntent intent, JSONObject payload) {
|
||||
PushProps.ChannelConfig xmCfg = pushProps.getOppoChannelConfig();
|
||||
JSONObject oppoField = new JSONObject();
|
||||
oppoField.put("channel_id", peer.determineChannelId(xmCfg, content));
|
||||
oppoField.put("category", content.determineAndroidCategory());
|
||||
oppoField.put("notify_level", 2);
|
||||
oppoField.put("click_action_type", 1);
|
||||
oppoField.put("click_action_activity", "com.oppo.codelabpush.intent.action.test");
|
||||
oppoField.put("action_parameters", intent.build().toJSONString());
|
||||
|
||||
payload.put("oppoField", oppoField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.payload.intent.Intent;
|
||||
import cn.axzo.im.push.payload.intent.IntentValue;
|
||||
import cn.axzo.im.push.payload.intent.JsonIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 苹果
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
class PGPushPayloadBuilder implements PushPayloadBuilder<JsonIntent> {
|
||||
|
||||
@Override
|
||||
public JsonIntent createIntent(PushContent content) {
|
||||
JsonIntent intent = new JsonIntent();
|
||||
if (StringUtils.isNotBlank(content.getIosPushUrl()))
|
||||
Intent.setRouter(intent, IntentValue
|
||||
.create(content.getIosPushUrl()));
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
JsonIntent intent, JSONObject payload) {
|
||||
JSONObject userInfo = new JSONObject();
|
||||
userInfo.putAll(intent.build());
|
||||
|
||||
JSONObject alert = new JSONObject();
|
||||
alert.put("title", content.getTitle());
|
||||
alert.put("body", content.getContent());
|
||||
alert.put("userInfo", userInfo);
|
||||
|
||||
JSONObject apsField = new JSONObject();
|
||||
apsField.put("alert", alert);
|
||||
|
||||
payload.put("apsField", apsField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.payload.intent.Intent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* <a href="https://doc.yunxin.163.com/messaging/server-apis/DQyNjc5NjE?platform=server#apns%E6%8E%A8%E9%80%81%E6%B6%88%E6%81%AF">云信推送</a>
|
||||
* <a href="https://doc.yunxin.163.com/messaging/guide/TY4MzU5MDc?platform=android#%E8%AE%BE%E7%BD%AE%E6%8E%A8%E9%80%81%E9%80%9A%E7%9F%A5%E6%A0%8F%E8%B7%B3%E8%BD%AC%E6%96%B9%E5%BC%8F">各端推送配置</a>
|
||||
* <p/>
|
||||
* 文字, 链接、声音
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface PushPayloadBuilder<T extends Intent<?>> {
|
||||
|
||||
T createIntent(PushContent content);
|
||||
|
||||
void build(PushPeer peer, PushContent content,
|
||||
T intent, JSONObject payload);
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.center.api.vo.req.PushMessageTye;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.payload.intent.UriIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 荣耀
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
class RYPushPayloadBuilder extends AndroidPushPayloadBuilder<UriIntent> {
|
||||
|
||||
@Override
|
||||
public UriIntent createIntent() {
|
||||
return new UriIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
UriIntent intent, JSONObject payload) {
|
||||
// 点击事件的内容
|
||||
JSONObject clickAction = new JSONObject();
|
||||
clickAction.put("type", 1);
|
||||
clickAction.put("intent", intent.build());
|
||||
|
||||
// 通知的内容
|
||||
JSONObject notification = new JSONObject();
|
||||
notification.put("title", content.getTitle());
|
||||
notification.put("body", content.getContent());
|
||||
notification.put("clickAction", clickAction);
|
||||
notification.put("importance",
|
||||
content.getMessageTye() == PushMessageTye.OP ? "LOW" : "NORMAL");
|
||||
|
||||
JSONObject honorField = new JSONObject();
|
||||
honorField.put("notification", notification);
|
||||
|
||||
payload.put("honorField", honorField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.center.api.vo.req.PushMessageTye;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.payload.intent.UriIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* VIVO
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
class VivoPushPayloadBuilder extends AndroidPushPayloadBuilder<UriIntent> {
|
||||
|
||||
@Override
|
||||
public UriIntent createIntent() {
|
||||
return new UriIntent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 没找到自定义声音相关的字段
|
||||
*/
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
UriIntent intent, JSONObject payload) {
|
||||
JSONObject vivoField = new JSONObject();
|
||||
vivoField.put("content", content.getContent());
|
||||
vivoField.put("classification",
|
||||
content.getMessageTye() == PushMessageTye.OP ? 0 : 1);
|
||||
vivoField.put("skipType", 4);
|
||||
vivoField.put("networkType", -1);
|
||||
vivoField.put("category", content.determineAndroidCategory());
|
||||
vivoField.put("skipContent", intent.build());
|
||||
|
||||
payload.put("vivoField", vivoField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package cn.axzo.im.push.payload;
|
||||
|
||||
import cn.axzo.im.center.api.vo.req.PushContent;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.push.PushProps;
|
||||
import cn.axzo.im.push.payload.intent.UriIntent;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 小米
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class XMPushPayloadBuilder extends AndroidPushPayloadBuilder<UriIntent> {
|
||||
|
||||
private final PushProps pushProps;
|
||||
|
||||
@Override
|
||||
public UriIntent createIntent() {
|
||||
return new UriIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(PushPeer peer, PushContent content,
|
||||
UriIntent intent, JSONObject payload) {
|
||||
PushProps.ChannelConfig xmCfg = pushProps.getXiaomiChannelConfig();
|
||||
payload.put("channel_id", peer.determineChannelId(xmCfg, content));
|
||||
payload.put("notify_foreground", "1");
|
||||
payload.put("notify_effect", "2");
|
||||
payload.put("intent_uri", intent.build());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package cn.axzo.im.push.payload.intent;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public interface Intent<T> {
|
||||
|
||||
String INTENT_ROUTER = "router";
|
||||
String INTENT_SOUND = "sound";
|
||||
String INTENT_TYPE = "type";
|
||||
String INTENT_SESSION_TYPE = "sessionType";
|
||||
String INTENT_SESSION_ID = "sessionId";
|
||||
String INTENT_WORKSPACE_ID = "workspaceId";
|
||||
String INTENT_OU_ID = "ouId";
|
||||
|
||||
void setValue(String key, IntentValue value);
|
||||
|
||||
T build();
|
||||
|
||||
// !! helper
|
||||
|
||||
static void setRouter(Intent<?> intent, IntentValue router) {
|
||||
intent.setValue(INTENT_ROUTER, router);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package cn.axzo.im.push.payload.intent;
|
||||
|
||||
import cn.axzo.basics.common.exception.ServiceException;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class IntentValue {
|
||||
|
||||
private final Object value;
|
||||
|
||||
private static final IntentValue NULL = new IntentValue(null);
|
||||
public static final IntentValue CONSTANT_SESSION_TYPE = new IntentValue("0");
|
||||
public static final IntentValue TYPE_SERVER = new IntentValue("server");
|
||||
public static final IntentValue TYPE_IM = new IntentValue("im");
|
||||
|
||||
public static IntentValue create(Object value) {
|
||||
if (value == null) return NULL;
|
||||
return new IntentValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 和app约定好了, intent里面都使用string类型的值
|
||||
*/
|
||||
public void consume(Consumer<String> consumer) {
|
||||
if (value == null) return;
|
||||
consumer.accept(String.valueOf(value));
|
||||
}
|
||||
|
||||
public IntentValue urlEncode() {
|
||||
if (value == null) return NULL;
|
||||
try {
|
||||
if (value instanceof String)
|
||||
return create(URLEncoder.encode((String) value, "UTF-8"));
|
||||
else
|
||||
throw new ServiceException("only string can be url encoded");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("url encode failed, intent value: {}", value, e);
|
||||
throw new ServiceException(String.format("url encode failed, intent value: %s", value), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package cn.axzo.im.push.payload.intent;
|
||||
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public class JsonIntent implements Intent<JSONObject> {
|
||||
|
||||
private final JSONObject values = new JSONObject();
|
||||
|
||||
@Override
|
||||
public void setValue(String key, IntentValue value) {
|
||||
BizAssertions.assertNotBlank(key, "key is required");
|
||||
BizAssertions.assertNotNull(value, "value is required");
|
||||
value.consume(stringValue -> values.put(key, stringValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject build() {
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package cn.axzo.im.push.payload.intent;
|
||||
|
||||
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public class UriIntent implements Intent<String> {
|
||||
|
||||
private static final String INTENT_PREFIX =
|
||||
"intent://cn.axzo.codelabpush/deeplink?#Intent;scheme=pushschema;launchFlags=0x4000000;";
|
||||
|
||||
private final LinkedHashMap<String, IntentValue> values = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setValue(String key, IntentValue value) {
|
||||
BizAssertions.assertNotBlank(key, "key is required");
|
||||
BizAssertions.assertNotNull(value, "value is required");
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
StringBuilder buf = new StringBuilder(INTENT_PREFIX);
|
||||
for (Map.Entry<String, IntentValue> entry : values.entrySet()) {
|
||||
entry.getValue().consume(stringValue ->
|
||||
buf.append("S.")
|
||||
.append(entry.getKey())
|
||||
.append("=")
|
||||
.append(stringValue)
|
||||
.append(";"));
|
||||
}
|
||||
buf.append("end");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,6 +13,8 @@ import cn.axzo.im.dao.repository.MessageHistoryDao;
|
||||
import cn.axzo.im.entity.AccountRegister;
|
||||
import cn.axzo.im.entity.MessageHistory;
|
||||
import cn.axzo.im.enums.MessageHistoryStatus;
|
||||
import cn.axzo.im.push.NimPushService;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.utils.BizAssertions;
|
||||
import cn.axzo.im.utils.UUIDUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
@ -44,6 +46,7 @@ public class CustomMessageService {
|
||||
private final AccountService accountService;
|
||||
private final AccountRegisterDao accountRegisterDao;
|
||||
private final MessageHistoryDao messageHistoryDao;
|
||||
private final NimPushService nimPushService;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<MessageCustomResp> saveAsHistoryRecords(CustomMessageInfo request) {
|
||||
@ -126,10 +129,17 @@ public class CustomMessageService {
|
||||
history.setToAccount(account.getImAccount());
|
||||
history.setStatus(MessageHistoryStatus.PENDING);
|
||||
}
|
||||
if (request.isPush()) {
|
||||
history.setMessageBody(request.getPayload());
|
||||
history.getOrCreateRecordExt().setPayload(request.getPayload());
|
||||
history.getOrCreateRecordExt().setSound(request.getSound());
|
||||
if (request.getPushContent() != null) {
|
||||
PushPeer peer = new PushPeer();
|
||||
peer.setOuId(request.getOuId());
|
||||
peer.setWorkspaceId(null);
|
||||
peer.setSenderImAccount(customSendAccount.getImAccount());
|
||||
peer.setAppType(appType);
|
||||
peer.setApiChannel(ApiChannel.CUSTOM_MESSAGE);
|
||||
String payload = nimPushService.buildPayload(request.getPushContent(), peer);
|
||||
history.setMessageBody(payload);
|
||||
history.getOrCreateRecordExt().setPayload(payload);
|
||||
history.getOrCreateRecordExt().setSound(payload);
|
||||
} else {
|
||||
BizAssertions.assertNotNull(request.getBizType(), "业务类型不能为空");
|
||||
history.setMessageBody(JSON.toJSONString(messageBody));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package cn.axzo.im.service.impl;
|
||||
|
||||
import cn.axzo.im.center.api.vo.ApiChannel;
|
||||
import cn.axzo.im.center.api.vo.req.ExcludePushPayload;
|
||||
import cn.axzo.im.center.api.vo.req.SendMessageParam;
|
||||
import cn.axzo.im.center.common.enums.AccountTypeEnum;
|
||||
@ -12,6 +13,8 @@ import cn.axzo.im.entity.AccountRegister;
|
||||
import cn.axzo.im.entity.MessageHistory;
|
||||
import cn.axzo.im.entity.MessageTask;
|
||||
import cn.axzo.im.enums.MessageHistoryStatus;
|
||||
import cn.axzo.im.push.NimPushService;
|
||||
import cn.axzo.im.push.PushPeer;
|
||||
import cn.axzo.im.service.AccountRegisterService;
|
||||
import cn.axzo.im.service.AccountRegisterService.AccountRegisterDTO;
|
||||
import cn.axzo.im.service.AccountService;
|
||||
@ -67,6 +70,8 @@ public class MessageTaskServiceImpl extends ServiceImpl<MessageTaskMapper, Messa
|
||||
private OrganizationalTeamOuRelationApi organizationalTeamOuRelationApi;
|
||||
@Autowired
|
||||
private UpdatableMessageManager updatableMessageManager;
|
||||
@Autowired
|
||||
private NimPushService nimPushService;
|
||||
|
||||
private static final Integer DEFAULT_PAGE_SIZE = 500;
|
||||
|
||||
@ -261,9 +266,16 @@ public class MessageTaskServiceImpl extends ServiceImpl<MessageTaskMapper, Messa
|
||||
return personIdMatches && appTypeMatches;
|
||||
});
|
||||
messageHistory.getOrCreateRecordExt().setPayloadExcluded(excludePayload);
|
||||
if (!excludePayload)
|
||||
messageHistory.getOrCreateRecordExt().setPayload(
|
||||
bizData == null ? null : bizData.getPayload());
|
||||
if (bizData != null && !excludePayload) {
|
||||
PushPeer peer = new PushPeer();
|
||||
peer.setOuId(receivePerson.getOuId());
|
||||
peer.setWorkspaceId(receivePerson.getWorkspaceId());
|
||||
peer.setSenderImAccount(messageHistory.getFromAccount());
|
||||
peer.setAppType(AppTypeEnum.isValidAppType(messageHistory.getAppType()));
|
||||
peer.setApiChannel(ApiChannel.COMMON_MESSAGE);
|
||||
String payload = nimPushService.buildPayload(bizData.getPushContent(), peer);
|
||||
messageHistory.getOrCreateRecordExt().setPayload(payload);
|
||||
}
|
||||
messageHistory.setMessageBody(resolveBody(receivePerson, messageTask,
|
||||
messageHistory));
|
||||
return messageHistory;
|
||||
|
||||
@ -49,13 +49,6 @@ public class BizAssertions {
|
||||
AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言集合为空
|
||||
*/
|
||||
public static void assertEmpty(Collection<?> actual, String message, Object... args) {
|
||||
AssertUtil.isEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言值为真
|
||||
*/
|
||||
@ -88,18 +81,26 @@ public class BizAssertions {
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotBlank(String content, String message, Object... args) {
|
||||
if (StringUtils.isBlank(content)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertBlank(String content, String message, Object... args) {
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(CommonResponse<T> response) {
|
||||
return assertResponse(response, "error resp={}", JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(CommonResponse<T> response, String message, Object... args) {
|
||||
if (response.getCode() != HttpStatus.HTTP_OK) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
if (response == null || response.getCode() != HttpStatus.HTTP_OK) {
|
||||
ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
log.warn("remote call response with error", e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
@ -111,12 +112,8 @@ public class BizAssertions {
|
||||
|
||||
public static <T> T assertResponse(ApiResult<T> response, String message, Object... args) {
|
||||
if (!response.isSuccess()) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
log.warn("remote call response with error", e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
@ -128,12 +125,8 @@ public class BizAssertions {
|
||||
|
||||
public static <T> List<T> assertResponse(ApiListResult<T> response, String message, Object... args) {
|
||||
if (!response.isSuccess()) {
|
||||
String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage();
|
||||
if (StringUtils.isNotBlank(response.getMsg())) {
|
||||
finalMsg += ": " + response.getMsg();
|
||||
}
|
||||
ServiceException e = new ServiceException(finalMsg);
|
||||
log.warn("remote call response with error. response={}", JSON.toJSONString(response), e);
|
||||
ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
log.warn("remote call response with error", e);
|
||||
throw e;
|
||||
}
|
||||
return response.getData();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user