diff --git a/yudao-module-member/yudao-module-member-biz/pom.xml b/yudao-module-member/yudao-module-member-biz/pom.xml index cf95191..3a2f7a0 100644 --- a/yudao-module-member/yudao-module-member-biz/pom.xml +++ b/yudao-module-member/yudao-module-member-biz/pom.xml @@ -106,6 +106,13 @@ yudao-spring-boot-starter-biz-ip + + + cn.jpush.api + jpush-client + 3.7.7 + + com.aliyun diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/chat/AppChatController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/chat/AppChatController.java index eefd935..38f8952 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/chat/AppChatController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/chat/AppChatController.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.member.client.vo.chat.friend.ChatAddFriendReqVO; import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatPrivateMessageSendReqVO; import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatPrivateMessageSendResVO; import cn.iocoder.yudao.module.member.service.opentype.OpenTypeService; +import cn.iocoder.yudao.module.member.service.push.MemberPushService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.validation.annotation.Validated; @@ -27,6 +28,8 @@ public class AppChatController { @Resource private ChatApi chatApi; + @Resource + private MemberPushService memberPushService; @PostMapping("/send-private-msg") @Operation(summary = "发送私聊消息(有会员限制)") @@ -35,7 +38,11 @@ public class AppChatController { openTypeService.isUserCanSendMessage(sendId, body.getRecvId()); body.setSendId(sendId); body.setTerminal(1); - return success(chatApi.send(body)); + ChatPrivateMessageSendResVO result = chatApi.send(body); + if (result != null) { + memberPushService.pushPrivateMessage(body); + } + return success(result); } @GetMapping("/add-friend") diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/JPushProperties.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/JPushProperties.java new file mode 100644 index 0000000..9299192 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/JPushProperties.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.framework.web.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("jpush") +@Data +public class JPushProperties { + + private boolean enabled; + + private String appKey; + + private String masterSecret; + + private boolean apnsProduction; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushService.java new file mode 100644 index 0000000..c28a2a5 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushService.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.member.service.push; + +import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatPrivateMessageSendReqVO; + +public interface MemberPushService { + + /** + * 推送私聊消息通知 + */ + void pushPrivateMessage(ChatPrivateMessageSendReqVO message); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushServiceImpl.java new file mode 100644 index 0000000..1d3a3b1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/push/MemberPushServiceImpl.java @@ -0,0 +1,105 @@ +package cn.iocoder.yudao.module.member.service.push; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatPrivateMessageSendReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.push.MemberPushDeviceDO; +import cn.iocoder.yudao.module.member.framework.web.config.JPushProperties; +import cn.jpush.api.JPushClient; +import cn.jpush.api.push.PushResult; +import cn.jpush.api.push.model.Message; +import cn.jpush.api.push.model.Options; +import cn.jpush.api.push.model.Platform; +import cn.jpush.api.push.model.PushPayload; +import cn.jpush.api.push.model.audience.Audience; +import cn.jpush.api.push.model.notification.AndroidNotification; +import cn.jpush.api.push.model.notification.IosNotification; +import cn.jpush.api.push.model.notification.Notification; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class MemberPushServiceImpl implements MemberPushService { + + private static final int MAX_ALERT_LENGTH = 100; + + @Resource + private MemberPushDeviceService pushDeviceService; + @Resource + private MemberUserApi memberUserApi; + @Resource + private JPushProperties jPushProperties; + + @Override + public void pushPrivateMessage(ChatPrivateMessageSendReqVO message) { + if (!jPushProperties.isEnabled() || StrUtil.isBlank(jPushProperties.getAppKey()) + || StrUtil.isBlank(jPushProperties.getMasterSecret())) { + return; + } + List devices = pushDeviceService.getByMemberId(message.getRecvId()); + List registrationIds = devices.stream() + .filter(device -> StrUtil.equalsIgnoreCase(device.getProvider(), "jpush")) + .map(MemberPushDeviceDO::getRegistrationId) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (CollUtil.isEmpty(registrationIds)) { + return; + } + + MemberUserRespDTO sender = memberUserApi.getUser(message.getSendId()); + String title = sender != null && StrUtil.isNotBlank(sender.getNickname()) ? sender.getNickname() : "新消息"; + String alert = buildAlert(message); + PushPayload payload = PushPayload.newBuilder() + .setPlatform(Platform.android_ios()) + .setAudience(Audience.registrationId(registrationIds)) + .setNotification(Notification.newBuilder() + .setAlert(alert) + .addPlatformNotification(AndroidNotification.newBuilder().setTitle(title).build()) + .addPlatformNotification(IosNotification.newBuilder().incrBadge(1).build()) + .build()) + .setMessage(Message.newBuilder().setTitle(title).setMsgContent(alert) + .addExtra("type", "private_message") + .addExtra("senderId", String.valueOf(message.getSendId())) + .addExtra("receiverId", String.valueOf(message.getRecvId())) + .addExtra("messageType", String.valueOf(message.getType())) + .build()) + .setOptions(Options.newBuilder().setApnsProduction(jPushProperties.isApnsProduction()).build()) + .build(); + try { + JPushClient client = new JPushClient(jPushProperties.getMasterSecret(), jPushProperties.getAppKey()); + PushResult result = client.sendPush(payload); + log.info("[pushPrivateMessage][sendId({}) recvId({}) registrationIds({}) msgId({}) 推送成功]", + message.getSendId(), message.getRecvId(), registrationIds.size(), result.msg_id); + } catch (Exception e) { + log.error("[pushPrivateMessage][sendId({}) recvId({}) 极光推送失败]", + message.getSendId(), message.getRecvId(), e); + } + } + + private String buildAlert(ChatPrivateMessageSendReqVO message) { + if (message.getType() == null || message.getType() == 0) { + return StrUtil.maxLength(message.getContent(), MAX_ALERT_LENGTH); + } + switch (message.getType()) { + case 1: + return "[图片]"; + case 2: + return "[文件]"; + case 3: + return "[语音]"; + case 4: + return "[视频]"; + default: + return "您收到一条新消息"; + } + } + +} diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index c160025..422a064 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -366,6 +366,12 @@ yudao: key: pLXUGAwK5305 customer: E77DF18BE109F454A5CD319E44BF5177 +jpush: + enabled: ${JPUSH_ENABLED:true} + app-key: ${JPUSH_APP_KEY:d7773439aa8c579dc31a552f} + master-secret: ${JPUSH_MASTER_SECRET:a2ea6bb666a26ff1dd246ffa} + apns-production: ${JPUSH_APNS_PRODUCTION:true} + debug: false # 插件配置 TODO 芋艿:【IOT】需要处理下 pf4j: