From 7901224f014a52dbd693c723eee7b20fac1e1def Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Sun, 7 Jul 2024 19:28:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5unipush?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-platform/pom.xml | 5 + .../bx/implatform/config/UniPushConfig.java | 44 +++++++ .../com/bx/implatform/contant/RedisKey.java | 6 + .../controller/LoginController.java | 13 +- .../java/com/bx/implatform/dto/LoginDTO.java | 3 + .../java/com/bx/implatform/dto/LogoutDTO.java | 19 +++ .../java/com/bx/implatform/entity/User.java | 5 + .../listener/PrivateMessageListener.java | 39 +++++- .../service/INotifyPrivateService.java | 98 ++++++++++++++ .../bx/implatform/service/IUserService.java | 9 ++ .../service/impl/GroupServiceImpl.java | 6 +- .../impl/PrivateMessageServiceImpl.java | 6 +- .../service/impl/UserServiceImpl.java | 24 ++++ .../service/thirdparty/UniPushService.java | 122 ++++++++++++++++++ .../bx/implatform/session/NotifySession.java | 43 ++++++ .../src/main/resources/application.yml | 10 ++ im-platform/src/main/resources/db/db.sql | 1 + im-uniapp/App.vue | 17 +++ im-uniapp/manifest.json | 14 +- im-uniapp/pages/login/login.vue | 8 +- 20 files changed, 477 insertions(+), 15 deletions(-) create mode 100644 im-platform/src/main/java/com/bx/implatform/config/UniPushConfig.java create mode 100644 im-platform/src/main/java/com/bx/implatform/dto/LogoutDTO.java create mode 100644 im-platform/src/main/java/com/bx/implatform/service/INotifyPrivateService.java create mode 100644 im-platform/src/main/java/com/bx/implatform/service/thirdparty/UniPushService.java create mode 100644 im-platform/src/main/java/com/bx/implatform/session/NotifySession.java diff --git a/im-platform/pom.xml b/im-platform/pom.xml index 16e987c..9e7502a 100644 --- a/im-platform/pom.xml +++ b/im-platform/pom.xml @@ -115,6 +115,11 @@ redisson 3.17.3 + + com.getui.push + restful-sdk + 1.0.3.0 + diff --git a/im-platform/src/main/java/com/bx/implatform/config/UniPushConfig.java b/im-platform/src/main/java/com/bx/implatform/config/UniPushConfig.java new file mode 100644 index 0000000..641c3f6 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/config/UniPushConfig.java @@ -0,0 +1,44 @@ +package com.bx.implatform.config; + +import com.getui.push.v2.sdk.ApiHelper; +import com.getui.push.v2.sdk.GtApiConfiguration; +import com.getui.push.v2.sdk.api.PushApi; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +/** + * @author: 谢绍许 + * @date: 2024-07-06 + * @version: 1.0 + */ +@Data +@Component +public class UniPushConfig { + + @Value("${notify.uniPush.appId}") + private String appId; + @Value("${notify.uniPush.appKey}") + private String appKey; + @Value("${notify.uniPush.masterSecret}") + private String masterSecret; + + @Bean + public GtApiConfiguration uniPushConfiguration(){ + GtApiConfiguration apiConfiguration = new GtApiConfiguration(); + apiConfiguration.setAppId(appId); + apiConfiguration.setAppKey(appKey); + apiConfiguration.setMasterSecret(masterSecret); + return apiConfiguration; + } + + @Bean + public PushApi uniPushApi(GtApiConfiguration configuration){ + // 实例化ApiHelper对象,用于创建接口对象 + ApiHelper apiHelper = ApiHelper.build(configuration); + // 创建对象,建议复用。目前有PushApi、StatisticApi、UserApi + PushApi pushApi = apiHelper.creatApi(PushApi.class); + return pushApi; + } +} diff --git a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java index 026e3c9..b2dd88b 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -10,6 +10,11 @@ public final class RedisKey { * 已读群聊消息位置(已读最大id) */ public static final String IM_GROUP_READED_POSITION = "im:readed:group:position"; + + /** + * 私聊离线通知 + */ + public static final String IM_OFFLINE_NOTIFY_PRIVATE = "im:notify:private"; /** * webrtc 单人通话 */ @@ -45,4 +50,5 @@ public final class RedisKey { */ public static final String IM_LOCK_RTC_GROUP = IM_LOCK + "rtc:group"; + } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index 4a5e2d4..dbf07c2 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -1,6 +1,7 @@ package com.bx.implatform.controller; import com.bx.implatform.dto.LoginDTO; +import com.bx.implatform.dto.LogoutDTO; import com.bx.implatform.dto.ModifyPwdDTO; import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.result.Result; @@ -21,13 +22,21 @@ public class LoginController { private final IUserService userService; + @PostMapping("/login") - @ApiOperation(value = "用户注册", notes = "用户注册") - public Result register(@Valid @RequestBody LoginDTO dto) { + @ApiOperation(value = "用户登陆", notes = "用户注册") + public Result login(@Valid @RequestBody LoginDTO dto) { LoginVO vo = userService.login(dto); return ResultUtils.success(vo); } + @PostMapping("/logout") + @ApiOperation(value = "用户退出登陆", notes = "用户退出登陆") + public Result logout(@Valid @RequestBody LogoutDTO dto) { + userService.logout(dto); + return ResultUtils.success(); + } + @PutMapping("/refreshToken") @ApiOperation(value = "刷新token", notes = "用refreshtoken换取新的token") diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java index 0cdede2..cea3110 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java @@ -27,4 +27,7 @@ public class LoginDTO { @ApiModelProperty(value = "用户密码") private String password; + @ApiModelProperty(value = "用户客户端id") + private String cid; + } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LogoutDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LogoutDTO.java new file mode 100644 index 0000000..4b252af --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/LogoutDTO.java @@ -0,0 +1,19 @@ +package com.bx.implatform.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Data +@ApiModel("用户登录DTO") +public class LogoutDTO { + + @ApiModelProperty(value = "用户客户端id") + private String cid; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/entity/User.java b/im-platform/src/main/java/com/bx/implatform/entity/User.java index 639b43b..67937fc 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/User.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/User.java @@ -79,6 +79,11 @@ public class User extends Model { @TableField("password") private String password; + /** + * 客户端id,用于uni-push推送 + */ + @TableField("cid") + private String cid; /** * 最后登录时间 */ diff --git a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java index 194f469..5843302 100644 --- a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java +++ b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java @@ -6,10 +6,12 @@ import com.bx.imclient.annotation.IMListener; import com.bx.imclient.listener.MessageListener; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMSendCode; +import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.model.IMSendResult; import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.enums.MessageStatus; import com.bx.implatform.service.IPrivateMessageService; +import com.bx.implatform.service.INotifyPrivateService; import com.bx.implatform.vo.PrivateMessageVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -25,24 +27,49 @@ public class PrivateMessageListener implements MessageListener @Lazy @Autowired private IPrivateMessageService privateMessageService; + + @Lazy + @Autowired + private INotifyPrivateService uniPushService; + @Override public void process(List> results) { + // 更新消息状态 + updateMessageStatus(results); + // 推送离线通知 + sendOfflineNotify(results); + } + + private void updateMessageStatus(List> results) { Set messageIds = new HashSet<>(); - for(IMSendResult result : results){ + for (IMSendResult result : results) { PrivateMessageVO messageInfo = result.getData(); // 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态 if (result.getCode().equals(IMSendCode.SUCCESS.code())) { messageIds.add(messageInfo.getId()); - log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); + log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), + result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); } } - // 批量修改状态 - if(CollUtil.isNotEmpty(messageIds)){ + // 对发送成功的消息修改状态 + if (CollUtil.isNotEmpty(messageIds)) { UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.lambda().in(PrivateMessage::getId, messageIds) - .eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code()) - .set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); + .eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code()) + .set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); privateMessageService.update(updateWrapper); } } + + private void sendOfflineNotify(List> results) { + for (IMSendResult result : results) { + PrivateMessageVO messageInfo = result.getData(); + if (result.getCode().equals(IMSendCode.SUCCESS.code()) && result.getReceiver().getTerminal() + .equals(IMTerminalType.APP.code())) { + uniPushService.sendMessage(messageInfo.getSendId(), messageInfo.getRecvId(), messageInfo.getContent()); + log.info("推送离线通知,消息id:{},发送者:{},接收者:{}", messageInfo.getId(), result.getSender().getId(), + result.getReceiver().getId()); + } + } + } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/INotifyPrivateService.java b/im-platform/src/main/java/com/bx/implatform/service/INotifyPrivateService.java new file mode 100644 index 0000000..6fa2f5a --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/service/INotifyPrivateService.java @@ -0,0 +1,98 @@ +package com.bx.implatform.service; + +import cn.hutool.core.util.StrUtil; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.entity.User; +import com.bx.implatform.session.NotifySession; +import com.bx.implatform.service.thirdparty.UniPushService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * 私聊离线通知服务 + * + * @author: blue + * @date: 2024-07-06 + * @version: 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class INotifyPrivateService { + private final UniPushService uniPushService; + private final IUserService userService; + private final RedisTemplate redisTemplate; + @Value("${notify.enable}") + private Boolean enable = false; + @Value("${notify.max.private}") + private Integer max = -1; + public void sendMessage(Long sendId, Long recvId, String content) { + if(!enable){ + return; + } + NotifySession session = findNotifySession(sendId, recvId); + if (Objects.isNull(session)) { + session = createNotifySession(sendId, recvId); + } + // 未上报cid,无法推送 + if (StrUtil.isEmpty(session.getCid())) { + log.info("用户'{}'未上报cid,无法推送离线通知", recvId); + return; + } + // 已达到最大数量 + if (max > 0 && session.getCount() >= max) { + log.info("用户'{}'已到达推送数量上线,不再推送离线通知", recvId); + return; + } + // 消息数量加1 + session.setCount(session.getCount()+1); + String body = String.format("%s:%s", session.getSendNickName(),content); + // 大于1条时需要展示数量 + if (session.getCount() > 1) { + body = String.format("[%d条] ", session.getCount()) + body; + } + uniPushService.pushByCid(session,body); + // 保存会话 + saveNotifySession(session,sendId,recvId); + } + + public void removeNotifySession(Long sendId, Long recvId){ + String key = StrUtil.join(":", RedisKey.IM_OFFLINE_NOTIFY_PRIVATE, "*", recvId); + Set keys = redisTemplate.keys(key); + redisTemplate.delete(keys); + } + + private NotifySession createNotifySession(Long sendId, Long recvId) { + String key = StrUtil.join(":", RedisKey.IM_OFFLINE_NOTIFY_PRIVATE, sendId, recvId); + User sendUser = userService.getById(sendId); + User recvUser = userService.getById(recvId); + NotifySession session = new NotifySession(); + session.setCount(0); + session.setCid(recvUser.getCid()); + session.setTitle(sendUser.getNickName()); + session.setSendNickName(sendUser.getNickName()); + session.setNotifyId(Math.abs(key.hashCode())); + session.setLogo(sendUser.getHeadImageThumb()); + redisTemplate.opsForValue().set(key, session, 30, TimeUnit.DAYS); + return session; + } + + private NotifySession findNotifySession(Long sendId, Long recvId) { + String key = StrUtil.join(":", RedisKey.IM_OFFLINE_NOTIFY_PRIVATE, sendId, recvId); + return (NotifySession)redisTemplate.opsForValue().get(key); + } + + private void saveNotifySession(NotifySession session, Long sendId, Long recvId) { + String key = StrUtil.join(":", RedisKey.IM_OFFLINE_NOTIFY_PRIVATE, sendId, recvId); + redisTemplate.opsForValue().set(key, session); + } + +} diff --git a/im-platform/src/main/java/com/bx/implatform/service/IUserService.java b/im-platform/src/main/java/com/bx/implatform/service/IUserService.java index a24e0c3..1d1c823 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IUserService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IUserService.java @@ -2,6 +2,7 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; import com.bx.implatform.dto.LoginDTO; +import com.bx.implatform.dto.LogoutDTO; import com.bx.implatform.dto.ModifyPwdDTO; import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.entity.User; @@ -21,6 +22,14 @@ public interface IUserService extends IService { */ LoginVO login(LoginDTO dto); + /** + * 用户退出登陆 + * + * @param dto 退出登陆dto + * @return + */ + void logout(LogoutDTO dto); + /** * 修改用户密码 * diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index feb13c8..c32de84 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -75,7 +75,7 @@ public class GroupServiceImpl extends ServiceImpl implements return vo; } - @CacheEvict(value = "#vo.getId()") + @CacheEvict(key = "#vo.getId()") @Transactional(rollbackFor = Exception.class) @Override public GroupVO modifyGroup(GroupVO vo) { @@ -100,7 +100,7 @@ public class GroupServiceImpl extends ServiceImpl implements } @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = "#groupId") + @CacheEvict(key = "#groupId") @Override public void deleteGroup(Long groupId) { UserSession session = SessionContext.getSession(); @@ -178,7 +178,7 @@ public class GroupServiceImpl extends ServiceImpl implements return vo; } - @Cacheable(value = "#groupId") + @Cacheable(key = "#groupId") @Override public Group getById(Long groupId) { Group group = super.getById(groupId); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java index 0e10922..3c0dc4d 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java @@ -21,6 +21,7 @@ import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.PrivateMessageMapper; import com.bx.implatform.service.IFriendService; import com.bx.implatform.service.IPrivateMessageService; +import com.bx.implatform.service.INotifyPrivateService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; import com.bx.implatform.util.BeanUtils; @@ -43,7 +44,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl implements IU private final JwtProperties jwtProperties; private final IMClient imClient; + @Override public LoginVO login(LoginDTO dto) { User user = this.findUserByUserName(dto.getUserName()); @@ -56,6 +61,12 @@ public class UserServiceImpl extends ServiceImpl implements IU if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { throw new GlobalException(ResultCode.PASSWOR_ERROR); } + // 更新用户登陆时间和cid + user.setLastLoginTime(new Date()); + if(StrUtil.isNotEmpty(dto.getCid())){ + user.setCid(dto.getCid()); + } + this.updateById(user); // 生成token UserSession session = BeanUtils.copyProperties(user, UserSession.class); session.setUserId(user.getId()); @@ -71,6 +82,19 @@ public class UserServiceImpl extends ServiceImpl implements IU return vo; } + @Override + public void logout(LogoutDTO dto) { + UserSession session = SessionContext.getSession(); + if(StrUtil.isNotEmpty(dto.getCid())){ + // 清除cid,不再推送离线通知 + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); + wrapper.eq(User::getId,session.getUserId()); + wrapper.eq(User::getCid,dto.getCid()); + wrapper.set(User::getCid, Strings.EMPTY); + this.update(wrapper); + } + } + @Override public LoginVO refreshToken(String refreshToken) { //验证 token diff --git a/im-platform/src/main/java/com/bx/implatform/service/thirdparty/UniPushService.java b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/UniPushService.java new file mode 100644 index 0000000..f0c367b --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/UniPushService.java @@ -0,0 +1,122 @@ +package com.bx.implatform.service.thirdparty; + +import com.bx.implatform.session.NotifySession; +import com.getui.push.v2.sdk.api.PushApi; +import com.getui.push.v2.sdk.common.ApiResult; +import com.getui.push.v2.sdk.dto.req.Audience; +import com.getui.push.v2.sdk.dto.req.Settings; +import com.getui.push.v2.sdk.dto.req.message.PushChannel; +import com.getui.push.v2.sdk.dto.req.message.PushDTO; +import com.getui.push.v2.sdk.dto.req.message.PushMessage; +import com.getui.push.v2.sdk.dto.req.message.android.AndroidDTO; +import com.getui.push.v2.sdk.dto.req.message.android.GTNotification; +import com.getui.push.v2.sdk.dto.req.message.android.ThirdNotification; +import com.getui.push.v2.sdk.dto.req.message.android.Ups; +import com.getui.push.v2.sdk.dto.req.message.ios.Alert; +import com.getui.push.v2.sdk.dto.req.message.ios.Aps; +import com.getui.push.v2.sdk.dto.req.message.ios.IosDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author: 谢绍许 + * @date: 2024-07-06 + * @version: 1.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class UniPushService { + + private final PushApi pushApi; + + public void pushByCid(NotifySession session, String body){ + //根据cid进行单推 + PushDTO pushDTO = new PushDTO(); + // 设置推送参数,requestid需要每次变化唯一 + pushDTO.setRequestId(System.currentTimeMillis()+""); + Settings settings = new Settings(); + pushDTO.setSettings(settings); + //消息有效期,走厂商消息必须设置该值 + settings.setTtl(3600000); + //在线走个推通道时推送的消息体 + PushMessage pushMessage = new PushMessage(); + pushDTO.setPushMessage(pushMessage); + //此格式的透传消息由 unipush 做了特殊处理,会自动展示通知栏。开发者也可自定义其它格式,在客户端自己处理。 + GTNotification gtNotification = new GTNotification(); + gtNotification.setTitle(session.getTitle()); + gtNotification.setBody(body); + gtNotification.setClickType("startapp"); + gtNotification.setNotifyId(session.getNotifyId().toString()); + gtNotification.setLogoUrl(session.getLogo()); + gtNotification.setBadgeAddNum("1"); + pushMessage.setNotification(gtNotification); + // 设置接收人信息 + Audience audience = new Audience(); + pushDTO.setAudience(audience); + audience.addCid(session.getCid()); + //设置离线推送时的消息体 + PushChannel pushChannel = new PushChannel(); + //安卓离线厂商通道推送的消息体 + AndroidDTO androidDTO = new AndroidDTO(); + Ups ups = new Ups(); + ThirdNotification thirdNotification = new ThirdNotification(); + ups.setNotification(thirdNotification); + ups.setOptions(buildOptions(session.getLogo())); + thirdNotification.setTitle(session.getTitle()); + thirdNotification.setBody(body); + thirdNotification.setNotifyId(session.getNotifyId().toString()); + // 打开首页 + thirdNotification.setClickType("startapp"); + androidDTO.setUps(ups); + pushChannel.setAndroid(androidDTO); + // ios离线apn通道推送的消息体 + Alert alert = new Alert(); + alert.setTitle(session.getTitle()); + alert.setBody(body); + Aps aps = new Aps(); + // 0:普通通知消息 1:静默推送(无通知栏消息),静默推送时不需要填写其他参数。苹果建议1小时最多推送3条静默消息 + aps.setContentAvailable(0); + // default: 系统铃声 不填:无声 + aps.setSound("default"); + aps.setAlert(alert); + + IosDTO iosDTO = new IosDTO(); + iosDTO.setAps(aps); + iosDTO.setType("notify"); + pushChannel.setIos(iosDTO); + pushDTO.setPushChannel(pushChannel); + // 进行cid单推 + ApiResult>> apiResult = pushApi.pushToSingleByCid(pushDTO); + if (apiResult.isSuccess()) { + log.info("推送成功,{}",apiResult.getData()); + } else { + log.info("推送失败,code:{},msg:{}",apiResult.getCode(),apiResult.getMsg()); + } + } + + + private Map> buildOptions(String logo){ + Map> options = new HashMap<>(); + // 小米 + Map xm = new HashMap<>(); + xm.put("/extra.notification_style_type",1); + xm.put("/extra.notification_large_icon_uri",logo); + options.put("XM",xm); + // 华为 + Map hw = new HashMap<>(); + hw.put("/message/android/notification/image",logo); + hw.put("/message/android/notification/style",1); + options.put("HW",hw); + // 荣耀 + Map ho = new HashMap<>(); + hw.put("/android/notification/badge/addNum",1); + hw.put("/android/notification/icon",logo); + options.put("HW",hw); + return options; + } +} diff --git a/im-platform/src/main/java/com/bx/implatform/session/NotifySession.java b/im-platform/src/main/java/com/bx/implatform/session/NotifySession.java new file mode 100644 index 0000000..1ab253c --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/session/NotifySession.java @@ -0,0 +1,43 @@ +package com.bx.implatform.session; + +import lombok.Data; + +/** + * 离线通知session + * @author: blue + * @date: 2024-07-06 + * @version: 1.0 + */ +@Data +public class NotifySession { + /** + * 接收方客户端id + */ + private String cid; + + /** + * 通知id + */ + private Integer notifyId; + + /** + * 发送方用户名 + */ + private String sendNickName; + + /** + * 标题 + */ + private String title; + + /** + * 显示图标 + */ + private String logo; + + /** + * 消息数量 + */ + private Integer count; + +} diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index 8e0cbe8..2c26381 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -43,6 +43,16 @@ webrtc: iceServers: - urls: stun:stun.l.google.com:19302 +notify: + enable: true + max: + private: -1 # 私聊单个会话最大通知数量,-1表示不显示 + group: 10 # 群聊单个会话最大通知数量,-1表示不显示 + uniPush: + appId: nyK71XQYUy9s7Vlzoutlp1 + appKey: XtG6NkUSL98evtpLSE0jYA + masterSecret: MxApXxsx057jcPCeC0cXk6 + jwt: accessToken: expireIn: 1800 #半个小时 diff --git a/im-platform/src/main/resources/db/db.sql b/im-platform/src/main/resources/db/db.sql index 1ea4b86..03a5486 100644 --- a/im-platform/src/main/resources/db/db.sql +++ b/im-platform/src/main/resources/db/db.sql @@ -9,6 +9,7 @@ create table `im_user`( `sex` tinyint(1) default 0 comment '性别 0:男 1:女', `type` smallint default 1 comment '用户类型 1:普通用户 2:审核账户', `signature` varchar(1024) default '' comment '个性签名', + `cid` varchar(255) default '' comment '客户端id,用于uni-push推送', `last_login_time` datetime DEFAULT null comment '最后登录时间', `created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间', unique key `idx_user_name`(user_name), diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index 549bf4e..77fe6c6 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -19,6 +19,7 @@ store.dispatch("load").then(() => { // 初始化websocket this.initWebSocket(); + this.initUniPush(); }).catch((e) => { console.log(e); this.exit(); @@ -312,6 +313,21 @@ url: '/user/self', method: 'GET' }) + }, + initUniPush(){ + // #ifdef APP-PLUS + plus.push.setAutoNotification(true); + const clientInfo = plus.push.getClientInfo(); + console.log("clientInfo",clientInfo); + plus.push.addEventListener('click', (message)=>{ + const messages = plus.push.getAllMessage(); + console.log("messages",messages) + console.log("click",message) + }); + plus.push.addEventListener('receive', (message)=>{ + console.log("receive",message) + }); + // #endif } }, onLaunch() { @@ -333,6 +349,7 @@ }) // #endif } + } } diff --git a/im-uniapp/manifest.json b/im-uniapp/manifest.json index d69c7ad..2c30905 100644 --- a/im-uniapp/manifest.json +++ b/im-uniapp/manifest.json @@ -20,7 +20,8 @@ "modules" : { "Camera" : {}, "Record" : {}, - "Bluetooth" : {} + "Bluetooth" : {}, + "Push" : {} }, /* 应用发布信息 */ "distribute" : { @@ -59,7 +60,16 @@ /* SDK配置 */ "sdkConfigs" : { "ad" : {}, - "speech" : {} + "speech" : {}, + "push" : { + "unipush" : { + "icons" : { + "small" : { + "ldpi" : "C:/Users/82565/Desktop/盒子/logo.png" + } + } + } + } }, "icons" : { "android" : { diff --git a/im-uniapp/pages/login/login.vue b/im-uniapp/pages/login/login.vue index e3146b0..07ae6cb 100644 --- a/im-uniapp/pages/login/login.vue +++ b/im-uniapp/pages/login/login.vue @@ -21,7 +21,8 @@ loginForm: { terminal: 1, // APP终端 userName: '', - password: '' + password: '', + cid: '' }, rules: { userName: { @@ -41,6 +42,11 @@ }, methods: { submit() { + // APP需要上报cid,用于离线推送 + // #ifdef APP-PLUS + const clientInfo = plus.push.getClientInfo(); + this.loginForm.cid = clientInfo.clientid; + // #endif this.$http({ url: '/login', data: this.loginForm,