diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChannelMsgTypeEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChannelMsgTypeEnum.java new file mode 100644 index 0000000..cd2db28 --- /dev/null +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ChannelMsgTypeEnum.java @@ -0,0 +1,28 @@ +package cn.axzo.im.center.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 渠道消息类型 + */ +@Getter +@AllArgsConstructor +public enum ChannelMsgTypeEnum { + + /** + * 文本 + */ + TEXT(0, "文本消息"), + /** + * 自定义 + */ + CUSTOM(100, "自定义消息"), + ; + + + private final Integer code; + + private final String message; + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java index cf15935..e97d2b9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/NimChannelService.java @@ -3,7 +3,17 @@ package cn.axzo.im.channel.netease; import cn.axzo.basics.common.exception.ServiceException; import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.im.channel.IMChannelProvider; -import cn.axzo.im.channel.netease.dto.*; +import cn.axzo.im.channel.netease.dto.MessageBatchDispatchRequest; +import cn.axzo.im.channel.netease.dto.MessageBatchDispatchResponse; +import cn.axzo.im.channel.netease.dto.MessageCustomDispatchRequest; +import cn.axzo.im.channel.netease.dto.MessageCustomDispatchResponse; +import cn.axzo.im.channel.netease.dto.MessageDispatchRequest; +import cn.axzo.im.channel.netease.dto.MessageDispatchResponse; +import cn.axzo.im.channel.netease.dto.NimAccountInfo; +import cn.axzo.im.channel.netease.dto.QueryAccountResp; +import cn.axzo.im.channel.netease.dto.RegisterRequest; +import cn.axzo.im.channel.netease.dto.RegisterResponse; +import cn.axzo.im.channel.netease.dto.RegisterUpdateRequest; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; @@ -11,17 +21,16 @@ import cn.hutool.json.JSONArray; import cn.hutool.json.JSONUtil; import com.google.common.collect.Maps; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Resource; +import javax.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - /** * 网易云信IM服务 * @@ -149,7 +158,6 @@ public class NimChannelService implements IMChannelProvider { } //目前支持的默认值 0、单聊消息 100、自定义消息 messageInfo.setOpe(MSG_OPE); - messageInfo.setType(MSG_TYPE); HashMap paramMap = Maps.newHashMap(); paramMap.put("from", messageInfo.getFrom()); paramMap.put("body", messageInfo.getBody()); diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/PrivateMessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/PrivateMessageController.java new file mode 100644 index 0000000..d1a10f2 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/controller/PrivateMessageController.java @@ -0,0 +1,176 @@ +package cn.axzo.im.controller; + +import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; +import cn.axzo.im.center.common.enums.AccountTypeEnum; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.ChannelMsgTypeEnum; +import cn.axzo.im.channel.IMChannelProvider; +import cn.axzo.im.channel.netease.dto.MessageDispatchRequest; +import cn.axzo.im.channel.netease.dto.MessageDispatchResponse; +import cn.axzo.im.channel.netease.dto.RegisterUpdateRequest; +import cn.axzo.im.entity.AccountRegister; +import cn.axzo.im.entity.bo.AccountQueryParam; +import cn.axzo.im.service.AccountService; +import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import java.util.List; +import java.util.Objects; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author syl + * @date 2024/1/8 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/private/message") +public class PrivateMessageController { + + @Autowired + private IMChannelProvider imChannel; + + @Autowired + private AccountService accountService; + + @PostMapping(value = "/send") + public CommonResponse> sendMessage(@RequestBody @Valid SendMessageRequest param) { + log.info("send message param: [{}]", JSONUtil.toJsonStr(param)); + String appKey = imChannel.getProviderAppKey(); + List result = Lists.newArrayList(); + + List accounts = accountService.listAccount( + AccountQueryParam.builder().accountType(param.getSenderType()) + .accountId(param.getSenderId()) + .appKey(appKey).build()); + if (CollUtil.isEmpty(accounts)) { + log.info("appKey= [{}], senderId= [{}] 用户没有注册im账号, 请检查", + appKey, param.getSenderId()); + return CommonResponse.success(result); + } + AccountRegister account = accounts.get(0); + if (param.getSenderType() == AccountTypeEnum.USER) { + // 更新用户头像和昵称到网易云信 + updateUserProfile(param, account); + } + + param.getAppTypeList().forEach(type -> { + List receivers = accountService.listAccount( + AccountQueryParam.builder().accountId(param.getToPersonId()) + .appKey(appKey) + .appType(type).build()); + if (CollUtil.isEmpty(receivers)) { + log.info("appKey= [{}], appType= [{}], receiverId= [{}] 用户没有注册im账号, 请检查", + appKey, type, param.getToPersonId()); + return; + } + AccountRegister receiver = receivers.get(0); + + MessageDispatchRequest messageRequest = new MessageDispatchRequest(); + messageRequest.setFrom(account.getImAccount()); + messageRequest.setTo(receiver.getImAccount()); + messageRequest.setType(param.getChannelMsgType().getCode()); + // 暂时支持客户端的文本消息,其他后续扩展 + messageRequest.setBody(new JSONObject().fluentPut("msg", param.getMsgContent()).toJSONString()); + + MessageDispatchResponse response = imChannel.dispatchMessage(messageRequest); + log.info("send text message= [{}]", JSONUtil.toJsonStr(response)); + + MessageDispatchResp messageDispatchResp = BeanUtil.copyProperties(response.getData(), + MessageDispatchResp.class); + if (StringUtils.isNotBlank(response.getDesc())) { + messageDispatchResp.setDesc(response.getDesc()); + } + messageDispatchResp.setAppType(type.getCode()); + messageDispatchResp.setFromImAccount(messageRequest.getFrom()); + messageDispatchResp.setToImAccount(messageRequest.getTo()); + messageDispatchResp.setPersonId(param.getToPersonId()); + result.add(messageDispatchResp); + }); + + return CommonResponse.success(result); + } + + private void updateUserProfile(SendMessageRequest param, AccountRegister account) { + if (Objects.isNull(param.getExt())) { + log.info("普通用户不用更新 头像昵称, personId= [{}]", account.getAccountId()); + return; + } + String extJson = new JSONObject().fluentPut("accountType", AccountTypeEnum.USER.getCode()).toJSONString(); + RegisterUpdateRequest updateProfile = new RegisterUpdateRequest(); + updateProfile.setAccid(account.getImAccount()); + updateProfile.setName(param.getExt().getString("nickName")); + updateProfile.setIcon(param.getExt().getString("avatarUrl")); + updateProfile.setExtJson(extJson); + log.info("updateUserProfile:{}", JSONUtil.toJsonStr(updateProfile)); + imChannel.updateAccountProfile(updateProfile); + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SendMessageRequest { + + /** + * 发送消息到App端 工人端、企业端、服务器 CM、CMP、SYSTEM + * + * @See cn.axzo.im.center.common.enums.AppTypeEnum + */ + @NotNull(message = "消息接收端类型appTypeList不能为空") + private List appTypeList; + + /** + * 发送者类型 + */ + @NotNull(message = "发送者类型不能为空") + private AccountTypeEnum senderType; + + /** + * 发送用户Id + */ + @NotBlank(message = "发送用户Id") + private String senderId; + + /** + * 接收用户自然人Id + */ + @NotBlank(message = "接收用户自然人Id不能为空") + private String toPersonId; + + /** + * 渠道消息类型 + */ + @NotNull(message = "渠道消息类型不能为空") + private ChannelMsgTypeEnum channelMsgType; + + /** + * 消息内容 + */ + @NotBlank(message = "消息内容不能为空") + private String msgContent; + + /** + * 扩展信息 + */ + private JSONObject ext; + } +} diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/bo/AccountQueryParam.java b/im-center-server/src/main/java/cn/axzo/im/entity/bo/AccountQueryParam.java new file mode 100644 index 0000000..8f86e76 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/entity/bo/AccountQueryParam.java @@ -0,0 +1,25 @@ +package cn.axzo.im.entity.bo; + +import cn.axzo.im.center.common.enums.AccountTypeEnum; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author syl + * @date 2024/1/6 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AccountQueryParam { + + private AccountTypeEnum accountType; + private AppTypeEnum appType; + private String appKey; + private String accountId; + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java b/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java index 7347eac..b2fc41b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/AccountService.java @@ -19,6 +19,8 @@ import cn.axzo.im.dao.repository.AccountRegisterDao; import cn.axzo.im.dao.repository.RobotInfoDao; import cn.axzo.im.entity.AccountRegister; import cn.axzo.im.entity.RobotInfo; +import cn.axzo.im.entity.bo.AccountQueryParam; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.google.common.collect.Lists; import java.util.Date; import java.util.List; @@ -292,4 +294,24 @@ public class AccountService { } return userAccountAll; } + + public List listAccount(AccountQueryParam param) { + return build(param).list(); + } + + + public LambdaQueryChainWrapper build(AccountQueryParam param) { + LambdaQueryChainWrapper lambdaQuery = accountRegisterDao.lambdaQuery() + .eq(AccountRegister::getIsDelete, 0) + .eq(StringUtils.isNotBlank(param.getAppKey()), AccountRegister::getAppKey, param.getAppKey()) + .eq(StringUtils.isNotBlank(param.getAccountId()), AccountRegister::getAccountId, param.getAccountId()) + .isNotNull(AccountRegister::getToken); + if (Objects.nonNull(param.getAccountType())) { + lambdaQuery.eq(AccountRegister::getAccountType, param.getAccountType().getCode()); + } + if (Objects.nonNull(param.getAppType())) { + lambdaQuery.eq(AccountRegister::getAppType, param.getAppType().getCode()); + } + return lambdaQuery; + } } diff --git a/im-center-server/src/main/java/cn/axzo/im/service/MessageService.java b/im-center-server/src/main/java/cn/axzo/im/service/MessageService.java index 5f99bf9..6408497 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/MessageService.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/MessageService.java @@ -11,6 +11,7 @@ import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.UserAccountResp; import cn.axzo.im.center.common.enums.AccountTypeEnum; import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.ChannelMsgTypeEnum; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.NimMsgTypeEnum; import cn.axzo.im.channel.netease.dto.MessageBatchDispatchRequest; @@ -305,6 +306,7 @@ public class MessageService { if (StringUtils.isNotEmpty(accountRegister.getImAccount()) && StringUtils.isNotEmpty(accountRegister.getToken())) { messageRequest.setTo(accountRegister.getImAccount()); + messageRequest.setType(ChannelMsgTypeEnum.CUSTOM.getCode()); MessageDispatchResponse response = imChannel.dispatchMessage(messageRequest); MessageDispatchResp messageDispatchResp = BeanMapper.map(response.getData(), MessageDispatchResp.class); if (messageDispatchResp == null) {