REQ-3201: 运营push

This commit is contained in:
yanglin 2024-12-16 15:07:00 +08:00
parent bd22efdebb
commit b22257d678
23 changed files with 24 additions and 932 deletions

View File

@ -2,10 +2,8 @@ package cn.axzo.msg.center.inside.notices.service.impl;
import cn.axzo.msg.center.api.MessagePushApi;
import cn.axzo.msg.center.api.request.BuildNimPayloadRequest;
import cn.axzo.msg.center.api.request.MsgBody4Guest;
import cn.axzo.msg.center.inside.notices.service.IYouMengMessageService;
import cn.axzo.msg.center.nimpush.NimPushService;
import cn.azxo.framework.common.model.CommonResponse;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
@ -22,8 +20,6 @@ public class MessagePushApiImpl implements MessagePushApi {
@Autowired
private IYouMengMessageService youMengMessageService;
@Autowired
private NimPushService nimPushService;
@Override
public CommonResponse<Void> sendPushMessage(MsgBody4Guest msgBody) {
@ -32,11 +28,4 @@ public class MessagePushApiImpl implements MessagePushApi {
return CommonResponse.success();
}
@Override
public CommonResponse<String> buildNimPayload(BuildNimPayloadRequest request) {
log.info("buildNimPayload, request={}", request);
String payload = nimPushService.buildPayloadString(request, request.getPushPeer());
return CommonResponse.success(payload);
}
}

View File

@ -1,260 +0,0 @@
package cn.axzo.msg.center.inside.notices.service.impl.v3.msg;
import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.im.center.api.feign.MessageApi;
import cn.axzo.im.center.api.vo.ApiChannel;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.api.vo.req.ExcludePushPayload;
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.msg.center.api.enums.MsgStateV3Enum;
import cn.axzo.msg.center.api.request.v4.MessageSendBasicInfoV4;
import cn.axzo.msg.center.api.request.v4.MessageSendRequestV4;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import cn.axzo.msg.center.domain.entity.MessageRecordV3;
import cn.axzo.msg.center.message.domain.dto.TemplateModelV3;
import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO;
import cn.axzo.msg.center.message.service.card.CardSupport;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser;
import cn.axzo.msg.center.message.service.impl.v3.UrlParser;
import cn.axzo.msg.center.nimpush.NimPushService;
import cn.axzo.msg.center.nimpush.PushChannel;
import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.service.domain.PushPeer;
import cn.axzo.msg.center.service.dto.PersonV3DTO;
import cn.axzo.msg.center.service.enums.Channel;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* @author yanglin
*/
@Slf4j
@RequiredArgsConstructor
public class TemplateMessage {
private final MessageSendRequestV4 req;
private final String batchNo;
@Getter
private final Channel channel;
@Getter
private final TemplateModelV3 templateModel;
private final ApplicationContext beanFactory;
private List<MessageRecordV3> records;
private String title;
private String content;
String getTemplateCode() {
return templateModel.getTemplateCode();
}
Collection<Long> getMessageIds() {
return getMessageRecords().stream()
.map(MessageRecordV3::getId)
.collect(toSet());
}
Collection<Long> getReceiverPersonIds() {
return getMessageRecords().stream()
.map(MessageRecordV3::getReceiverPersonId)
.collect(toList());
}
List<MessageRecordV3> getMessageRecords() {
if (records != null) {
return records;
}
records = new ArrayList<>();
MessageSendBasicInfoV4 sendBasicInfo = req.getSendBasicInfo();
for (PersonV3DTO receiver : sendBasicInfo.distinctReceivers()) {
PersonV3DTO sender = sendBasicInfo.getSender();
MessageRecordV3 message = new MessageRecordV3();
records.add(message);
message.setBatchNo(batchNo);
message.setIdentityCode(UUIDUtil.uuidString());
message.setSenderPersonId(sender == null ? null : sender.getId());
message.setSenderOuId(sendBasicInfo.getSenderOuId());
message.setSenderWorkspaceId(sendBasicInfo.getSenderWorkspaceId());
message.setReceiverPersonId(receiver.getId());
message.setReceiverOuId(sendBasicInfo.determineReceiversOuId());
message.setReceiverWorkspaceId(sendBasicInfo.determineReceiversWorkspaceId());
message.setBizEventMappingCode(req.getEventMappingCode());
message.setTemplateCode(getTemplateCode());
message.setTitle(parseTitle());
message.setContent(parseContent());
OrganizationTypeEnum orgType = sendBasicInfo.getReceiversOrgType();
if (orgType == null) {
orgType = OrganizationTypeEnum.UNKNOWN;
}
message.setReceiverOrgType(orgType.stringCode());
message.setSubtitle(sendBasicInfo.getSubtitle());
message.setState(MsgStateV3Enum.UNSENT);
message.setBizCode(sendBasicInfo.determineBizCode());
message.setRouterParams(sendBasicInfo.determineRouterParams());
message.setBizExtParams(sendBasicInfo.determineBizExtParams());
message.setMsgExtInfo(getMsgExtInfo());
message.setFailCause(null);
message.setCreateAt(new Date());
message.setUpdateAt(new Date());
message.setIsDelete(TableIsDeleteEnum.NORMAL.value);
// 这里不设置发送时间, 有发送结果的时候再设置, 比较准确
}
return records;
}
SendTemplateMessageParam buildImRequest(MessageTemplateParserV3 templateParser,
PushDeviceSnapshots deviceSnapshots,
AppTypeEnum appType) {
MessageRecordV3 sample = getMessageRecords().get(0);
GeneralMessagePushVO sendVo = templateParser.parse(sample, templateModel);
MessageSendBasicInfoV4 sendBasicInfo = req.getSendBasicInfo();
SendTemplateMessageParam imRequest = new SendTemplateMessageParam();
imRequest.setBizId(String.format("%s:%s", CardSupport.getBizIdPrefix(getTemplateCode()), sendBasicInfo.determineBizCode()));
imRequest.setSendPriority(templateModel.getTemplate().determineImSendPriority());
imRequest.setMsgHeader(parseTitle());
imRequest.setMsgContent(parseContent());
imRequest.setMsgTemplateId(getTemplateCode());
imRequest.setMsgTemplateContent(JSON.toJSONString(sendVo));
imRequest.setExcludePushPayloads(new ArrayList<>());
// 接收人
ArrayList<PersonAccountAttribute> receivers = new ArrayList<>();
Set<Long> cmUnique = new HashSet<>();
Set<OuAndPerson> cmpUnique = new HashSet<>();
PushData pushData = templateModel.getTemplate().parsePushData();
boolean pushable = pushData.determinePushable(log, getTemplateCode());
// 扩展信息
for (PersonV3DTO receiver : sendBasicInfo.receivers()) {
PersonV3DTO.ReceiveModel imReceiveModel = receiver.getImReceiveModel();
Long ouId = imReceiveModel == null ? sendBasicInfo.determineReceiversOuId() : imReceiveModel.getOuId();
if (appType == AppTypeEnum.CM && !cmUnique.add(receiver.getId())) {
continue;
}
if (appType == AppTypeEnum.CMP && !cmpUnique.add(new OuAndPerson(ouId, receiver.getId()))) {
continue;
}
boolean excludePayload = pushable &&
!deviceSnapshots.getDevice(receiver.getId())
.shouldPush(appType, PushChannel.NIM);
if (excludePayload) {
ExcludePushPayload excludePush = new ExcludePushPayload();
excludePush.setPersonId(receiver.getId() + "");
excludePush.setAppType(appType);
imRequest.getExcludePushPayloads().add(excludePush);
}
PersonAccountAttribute receiverAccount = new PersonAccountAttribute();
receiverAccount.setPersonId(receiver.getId() + "");
receiverAccount.setOuId(ouId);
receiverAccount.setAppType(appType);
receivers.add(receiverAccount);
if (appType == AppTypeEnum.CM)
cmUnique.add(receiver.getId());
else if (appType == AppTypeEnum.CMP)
cmpUnique.add(new OuAndPerson(ouId, receiver.getId()));
}
imRequest.setReceivePersons(receivers);
JSONObject ext = new JSONObject();
ext.put("minAppVersion", templateModel.getTemplate().getMinAppVersion());
if (sample.getReceiverWorkspaceId() != null) {
ext.put("workspaceId", String.valueOf(sample.getReceiverWorkspaceId()));
}
if (sample.getReceiverOuId() != null) {
ext.put("ouId", String.valueOf(sample.getReceiverOuId()));
}
imRequest.setExt(ext);
if (pushable) {
imRequest.setPayload(buildPayload(sample, appType));
if (StringUtils.isNotBlank(pushData.getVoiceFile()))
ext.put(Intent.INTENT_SOUND, pushData.getVoiceFile());
}
return imRequest;
}
private String buildPayload(MessageRecordV3 sample, AppTypeEnum appType) {
ModelV3Parser modelV3Parser = beanFactory.getBean(ModelV3Parser.class);
ParsedModelV3 parsedModelV3 = modelV3Parser.parseModel(templateModel,
sample.getBizExtParams(), new UrlParser(sample.getRouterParams()));
NimPushService nimPushService = beanFactory.getBean(NimPushService.class);
MessageApi messageApi = beanFactory.getBean(MessageApi.class);
String imSenderAccount = BizAssertions.assertResponse(
messageApi.findTemplateRobotImAccount(getTemplateCode()));
PushPeer peer = new PushPeer();
peer.setSenderImAccount(imSenderAccount);
peer.setOuId(sample.getReceiverOuId());
peer.setWorkspaceId(sample.getReceiverWorkspaceId());
peer.setAppType(appType);
peer.setApiChannel(ApiChannel.COMMON_MESSAGE);
return nimPushService.buildPayloadString(parsedModelV3, peer);
}
// ------------------------------- 辅助方法
String parseTitle() {
if (title == null) {
title = PlaceholderResolver.tryResolve(
templateModel.getTemplate().getTitle(), req.getSendBasicInfo().getBizExtParams());
}
return title;
}
String parseContent() {
if (content == null) {
content = PlaceholderResolver.tryResolve(
templateModel.getTemplate().getContent(), req.getSendBasicInfo().getBizExtParams());
}
return content;
}
/**
* 预留
*/
JSONObject getMsgExtInfo() {
return new JSONObject();
}
@Data
private static class OuAndPerson {
private final Long ouId;
private final Long personId;
}
@Override
public String toString() {
HashMap<String, Object> values = new HashMap<>();
values.put("batchNo", batchNo);
values.put("bizEventMappingCode", req.getEventMappingCode());
values.put("bizCode", req.getSendBasicInfo().getBizCode());
values.put("templateCode", templateModel.getTemplate().getCode());
values.put("messageIds", getMessageIds());
values.put("receiverPersonIds", getReceiverPersonIds());
return JSON.toJSONString(values);
}
}

View File

@ -1,9 +1,6 @@
package cn.axzo.msg.center.message.migrate;
import cn.axzo.apollo.api.ApolloWorkerTaskOrderApi;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.maokai.api.client.CooperateShipQueryApi;
import cn.axzo.maokai.api.client.OrganizationalNodeUserApi;
import cn.axzo.maokai.api.client.OrganizationalTeamOuRelationApi;
import cn.axzo.maokai.api.vo.response.OrganizationalTeamOuRelationResp;
import cn.axzo.msg.center.common.utils.BizAssertions;
@ -65,9 +62,6 @@ public class OuIdMigrateService {
private final PendingMessageRecordDao pendingMessageRecordDao;
private final OrganizationalTeamOuRelationApi organizationalTeamOuRelationApi;
private final TeamServiceApi teamServiceApi;
private final CooperateShipQueryApi cooperateShipQueryApi;
private final ApolloWorkerTaskOrderApi apolloWorkerTaskOrderApi;
private final OrganizationalNodeUserApi organizationalNodeUserApi;
@Value("${message.pending.ouMigrateBatchSize:100}")
private final Long migrateBatchSize;

View File

@ -1,7 +1,6 @@
package cn.axzo.msg.center.message.service.card;
import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.im.center.api.vo.ApiChannel;
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.im.center.common.enums.YesOrNo;
@ -17,9 +16,7 @@ import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service;
import cn.axzo.msg.center.message.service.impl.v3.UrlParser;
import cn.axzo.msg.center.nimpush.NimPushService;
import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.service.domain.PushPeer;
import cn.axzo.msg.center.service.domain.card.AppVersionConfig;
import cn.axzo.msg.center.service.dto.PeerPerson;
import cn.axzo.msg.center.service.enums.MessageChannel;
@ -104,15 +101,6 @@ public class CardSupport {
PushDeviceSnapshots deviceSnapshots,
String imSenderAccount) {
ParsedTemplateV3 template = sendModel.getTemplate();
Supplier<String> payloadBuilder = () -> {
PushPeer peer = new PushPeer();
peer.setSenderImAccount(imSenderAccount);
peer.setOuId(group.getGroupKey().getOuId());
peer.setWorkspaceId(group.getGroupKey().getWorkspaceId());
peer.setAppType(group.getGroupKey().getAppType());
peer.setApiChannel(ApiChannel.COMMON_MESSAGE);
return nimPushService.buildPayloadString(sendModel.getParsedModel(), peer);
};
Supplier<JSONObject> extBuilder = () -> {
JSONObject ext = new JSONObject();
ext.put("minAppVersion", determineMinAppVersion(
@ -137,10 +125,10 @@ public class CardSupport {
imRequest.setExt(extBuilder.get());
imRequest.setUpdatable(sendModel.getCardTemplate().isUpdatable());
if (pushData.determinePushable(log, template.getCode())) {
imRequest.setPayload(payloadBuilder.get());
imRequest.setPushContent(nimPushService.buildPushContent(sendModel.getParsedModel(), false));
imRequest.setExcludePushPayloads(group.buildNimPushExcludes(deviceSnapshots));
if (StringUtils.isNotBlank(pushData.getVoiceFile()))
imRequest.getExt().put(Intent.INTENT_SOUND, pushData.getVoiceFile());
imRequest.getExt().put("sound", pushData.getVoiceFile());
}
return imRequest;
}

View File

@ -9,7 +9,6 @@ import cn.axzo.msg.center.dal.MessageGroupNodeDao;
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -43,7 +42,6 @@ public class PendingMessageNewServiceImpl {
private final OrganizationalTeamOuRelationApi organizationalTeamOuRelationApi;
private final PendingMessageBizConfig pendingMessageBizConfig;
private final MessageGroupNodeDao messageGroupNodeDao;
private final ModelV3Parser modelV3Parser;
public Map<String, Object> getTerminalConfigInfo() {
Map<AppTerminalTypeEnum, List<Long>> appType2NodeIds = pendingMessageBizConfig.getMsgGroupConfig();

View File

@ -2,11 +2,10 @@ package cn.axzo.msg.center.message.service.todo.manage.event;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.im.center.api.feign.MessageApi;
import cn.axzo.im.center.api.vo.ApiChannel;
import cn.axzo.im.center.api.vo.req.CustomMessageInfo;
import cn.axzo.im.center.api.vo.req.PushContent;
import cn.axzo.im.center.api.vo.resp.MessageCustomResp;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.domain.entity.MessageEntity;
import cn.axzo.msg.center.domain.entity.MessageTemplateV3;
import cn.axzo.msg.center.domain.entity.Todo;
@ -21,7 +20,6 @@ import cn.axzo.msg.center.nimpush.device.PushDevice;
import cn.axzo.msg.center.nimpush.device.PushDeviceService;
import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.service.domain.PushPeer;
import cn.axzo.msg.center.service.enums.PushTerminalEnum;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import com.alibaba.fastjson.JSON;
@ -69,7 +67,6 @@ public class TodoPushSenderNim implements ApplicationListener<NewTodoEvent> {
if (pushData != null && !pushData.determinePushable(log, parsedModelV3.getTemplateCode())) {
return;
}
String imSenderAccount = BizAssertions.assertResponse(messageApi.getCustomMessageSendImAccount());
PushDeviceSnapshots deviceSnapshots = pushDeviceService.createDeviceSnapshots();
for (Todo todo : event.getTodos()) {
executor.submit(() -> {
@ -78,21 +75,15 @@ public class TodoPushSenderNim implements ApplicationListener<NewTodoEvent> {
if (!pushDevice.shouldPush(appType, PushChannel.NIM))
continue;
PushPeer peer = new PushPeer();
peer.setSenderImAccount(imSenderAccount);
peer.setOuId(todo.getOuId());
peer.setWorkspaceId(todo.getOrgId());
peer.setAppType(appType);
peer.setApiChannel(ApiChannel.CUSTOM_MESSAGE);
String payload = nimPushService.buildPayloadString(parsedModelV3, peer);
if (StringUtils.isBlank(payload))
PushContent pushContent = nimPushService.buildPushContent(parsedModelV3, true);
if (pushContent == null)
continue;
CustomMessageInfo pushRequest = new CustomMessageInfo();
pushRequest.setAppTypeList(Collections.singletonList(appType));
pushRequest.setToPersonId(todo.getExecutorPersonId() + "");
pushRequest.setOuId(todo.getOuId());
pushRequest.setPayload(payload);
pushRequest.setPush(true);
pushRequest.setPayload("{}");
pushRequest.setPushContent(pushContent);
if (pushData != null && StringUtils.isNotBlank(pushData.getVoiceFile())) {
pushRequest.setSound(pushData.getVoiceFile());
}

View File

@ -1,115 +1,62 @@
package cn.axzo.msg.center.nimpush;
import cn.axzo.im.center.api.vo.ApiChannel;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.nimpush.payload.PushPayloadBuilder;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.nimpush.payload.intent.IntentValue;
import cn.axzo.basics.common.constant.enums.CodeDefinition;
import cn.axzo.im.center.api.vo.req.PushContent;
import cn.axzo.im.center.api.vo.req.PushMessageTye;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.push.PushMessage;
import cn.axzo.msg.center.service.domain.PushPeer;
import cn.axzo.msg.center.service.domain.UrlConfig;
import cn.axzo.msg.center.service.domain.UrlConfigVisitor;
import cn.axzo.msg.center.service.domain.UrlConfigWalker;
import cn.axzo.msg.center.service.domain.url.AppUrl;
import cn.axzo.msg.center.service.enums.CodeDefinition;
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.function.Consumer;
/**
* @author yanglin
*/
@Service
public class NimPushService {
private final PushPayloadBuilder<?>[] payloadBuilders;
@SuppressWarnings("rawtypes")
public NimPushService(ObjectProvider<PushPayloadBuilder[]> payloadBuilders) {
this.payloadBuilders = payloadBuilders.getIfAvailable();
}
public String buildPayloadString(PushMessage message, PushPeer peer) {
JSONObject payload = buildPayload(message, peer);
return payload == null ? null : payload.toJSONString();
}
private JSONObject buildPayload(PushMessage message, PushPeer peer) {
public PushContent buildPushContent(PushMessage message, boolean buildPushUrl) {
PushData pushData = message.getPushData();
if (pushData == null || !pushData.isSwitchOn()) return null;
PushContent content = new PushContent();
content.setTitle(message.getPushTitle());
content.setContent(message.getPushContent());
content.setCustomSoundFile(pushData.getVoiceFile());
content.setAppType(peer.getAppType());
content.setMessageTye(CodeDefinition
.findByCode(PushMessageTye.class, pushData.getType())
.orElse(null));
content.setCustomSoundFile(pushData.getVoiceFile());
if (peer.getApiChannel() == ApiChannel.CUSTOM_MESSAGE)
populateUrl(content, message.getPushUrl(), peer.getAppType());
return buildPayload(content, peer);
if (buildPushUrl)
populateUrl(content, message.getPushUrl());
return content;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private JSONObject 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()));
};
JSONObject payload = new JSONObject();
payload.put("pushTitle", content.getTitle());
for (PushPayloadBuilder builder : payloadBuilders) {
Intent intent = builder.createIntent(content, peer);
BizAssertions.assertNotNull(intent, "intent can't be null");
intentPopulator.accept(intent);
builder.build(content, intent, payload);
}
return payload;
}
private void populateUrl(PushContent content,
UrlConfig url,
AppTypeEnum appType) {
if (url == null || appType == null) return;
private void populateUrl(PushContent content, UrlConfig url) {
if (url == null) return;
UrlConfigWalker.walkDown(url, new UrlConfigVisitor() {
@Override
public void visitAppManagerAndroid(AppUrl android) {
if (android.hasUrl() && appType == AppTypeEnum.CMP)
content.setAndroidPushUrl(android.getUrl());
if (android.hasUrl())
content.setMangerAndroidPushUrl(android.getUrl());
}
@Override
public void visitAppManagerIos(AppUrl ios) {
if (ios.hasUrl() && appType == AppTypeEnum.CMP)
content.setIosPushUrl(ios.getUrl());
if (ios.hasUrl())
content.setManagerIosPushUrl(ios.getUrl());
}
@Override
public void visitAppWorkerAndroid(AppUrl android) {
if (android.hasUrl() && appType == AppTypeEnum.CM)
content.setAndroidPushUrl(android.getUrl());
if (android.hasUrl())
content.setWorkerAndroidPushUrl(android.getUrl());
}
@Override
public void visitAppWorkerIos(AppUrl ios) {
if (ios.hasUrl() && appType == AppTypeEnum.CM)
content.setIosPushUrl(ios.getUrl());
if (ios.hasUrl())
content.setWorkerIosPushUrl(ios.getUrl());
}
});
}

View File

@ -1,65 +0,0 @@
package cn.axzo.msg.center.nimpush;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.msg.center.inside.notices.config.push.PushProps.ChannelConfig;
import cn.axzo.msg.center.inside.notices.config.push.PushProps.ChannelIds;
import lombok.Getter;
import lombok.Setter;
/**
* @author yanglin
*/
@Setter
@Getter
public class PushContent {
/**
* 主标题
*/
private String title;
/**
* 推送内容
*/
private String content;
/**
* android点击push的跳转链接
*/
private String androidPushUrl;
/**
* ios点击push的跳转链接
*/
private String iosPushUrl;
/**
* 端信息
*/
private AppTypeEnum appType;
/**
* push消息类型. SYSTEM: 系统消息, OP: 运营消息
*/
private PushMessageTye messageTye;
/**
* 自定义提示音
*/
private String customSoundFile;
public String determineAndroidCategory() {
return messageTye == PushMessageTye.OP
? "MARKETING"
: "IM";
}
public String determineChannelId(ChannelConfig channelConfig) {
ChannelIds channelIds = appType == AppTypeEnum.CM
? channelConfig.getWorkerIds()
: channelConfig.getManagerIds();
return messageTye == PushMessageTye.OP
? channelIds.getOpMessageChannelId()
: channelIds.getWorkMessageChannelId();
}
}

View File

@ -1,24 +0,0 @@
package cn.axzo.msg.center.nimpush;
import cn.axzo.msg.center.service.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;
}
}

View File

@ -1,29 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.nimpush.payload.intent.IntentValue;
import cn.axzo.msg.center.service.domain.PushPeer;
import org.apache.commons.lang3.StringUtils;
/**
* @author yanglin
*/
abstract class AndroidPushPayloadBuilder<T extends Intent<?>>
implements PushPayloadBuilder<T> {
@Override
public final T createIntent(PushContent content, PushPeer peer) {
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();
}

View File

@ -1,43 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.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(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);
}
}

View File

@ -1,38 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.inside.notices.config.push.PushProps;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.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(PushContent content, JsonIntent intent, JSONObject payload) {
PushProps.ChannelConfig xmCfg = pushProps.getOppoChannelConfig();
JSONObject oppoField = new JSONObject();
oppoField.put("channel_id", content.determineChannelId(xmCfg));
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);
}
}

View File

@ -1,45 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.nimpush.payload.intent.IntentValue;
import cn.axzo.msg.center.nimpush.payload.intent.JsonIntent;
import cn.axzo.msg.center.service.domain.PushPeer;
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, PushPeer peer) {
JsonIntent intent = new JsonIntent();
if (StringUtils.isNotBlank(content.getIosPushUrl()))
Intent.setRouter(intent, IntentValue
.create(content.getIosPushUrl()));
return intent;
}
@Override
public void build(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);
}
}

View File

@ -1,22 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.service.domain.PushPeer;
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, PushPeer peer);
void build(PushContent content, T intent, JSONObject payload);
}

View File

@ -1,43 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.PushMessageTye;
import cn.axzo.msg.center.nimpush.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(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);
}
}

View File

@ -1,39 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.PushMessageTye;
import cn.axzo.msg.center.nimpush.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(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);
}
}

View File

@ -1,35 +0,0 @@
package cn.axzo.msg.center.nimpush.payload;
import cn.axzo.msg.center.inside.notices.config.push.PushProps;
import cn.axzo.msg.center.nimpush.PushContent;
import cn.axzo.msg.center.nimpush.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(PushContent content, UriIntent intent, JSONObject payload) {
PushProps.ChannelConfig xmCfg = pushProps.getXiaomiChannelConfig();
payload.put("channel_id", content.determineChannelId(xmCfg));
payload.put("notify_foreground", "1");
payload.put("notify_effect", "2");
payload.put("intent_uri", intent.build());
}
}

View File

@ -1,26 +0,0 @@
package cn.axzo.msg.center.nimpush.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);
}
}

View File

@ -1,52 +0,0 @@
package cn.axzo.msg.center.nimpush.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);
}
}
}

View File

@ -1,25 +0,0 @@
package cn.axzo.msg.center.nimpush.payload.intent;
import cn.axzo.msg.center.common.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;
}
}

View File

@ -1,40 +0,0 @@
package cn.axzo.msg.center.nimpush.payload.intent;
import cn.axzo.msg.center.common.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();
}
}

View File

@ -1,6 +1,5 @@
package cn.axzo.msg.center.api;
import cn.axzo.msg.center.api.request.BuildNimPayloadRequest;
import cn.axzo.msg.center.api.request.MsgBody4Guest;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
@ -24,10 +23,4 @@ public interface MessagePushApi {
@PostMapping("umeng/pushMsg")
CommonResponse<Void> sendPushMessage(@RequestBody MsgBody4Guest msgBody);
/**
* 构建云信push payload
*/
@PostMapping("push/nim/buildNimPayload")
CommonResponse<String> buildNimPayload(@RequestBody BuildNimPayloadRequest request);
}

View File

@ -1,22 +0,0 @@
package cn.axzo.msg.center.nimpush;
import cn.axzo.msg.center.MsgCenterApplication;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author yanglin
*/
@SpringBootTest(classes = MsgCenterApplication.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
class NimPushServiceTest {
private final NimPushService nimPushService;
@Test
void exec() {
}
}