From 7bc09c5c82f36930ec7032e18545ad24178316d5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 5 Nov 2024 18:52:22 +0800 Subject: [PATCH 01/93] =?UTF-8?q?REQ-3057:=20=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/center/api/feign/MessageApi.java | 8 + .../center/api/vo/PersonAccountAttribute.java | 46 ++++++ .../api/vo/req/SendTemplateMessageParam.java | 43 ++---- .../api/vo/req/UpdateMessageRequest.java | 12 ++ .../center/api/vo/resp/MessageTaskResp.java | 3 + .../center/api/vo/resp/MessageUpdateResp.java | 12 ++ .../vo/resp/UpdatableMessageSendResult.java | 15 ++ .../im/center/common/enums/AppTypeEnum.java | 3 +- .../center/common/enums/TemplatedMsgType.java | 22 +++ .../axzo/im/controller/MessageController.java | 28 +++- .../cn/axzo/im/dao/mapper/BizDataMapper.java | 11 -- .../cn/axzo/im/dao/mapper/BizLogMapper.java | 10 -- .../dao/mapper/UpdatableMessageLogMapper.java | 10 ++ .../im/dao/mapper/UpdatableMessageMapper.java | 10 ++ .../cn/axzo/im/dao/repository/BizDataDao.java | 13 -- .../cn/axzo/im/dao/repository/BizLogDao.java | 13 -- .../dao/repository/UpdatableMessageDao.java | 26 ++++ .../repository/UpdatableMessageLogDao.java | 13 ++ .../java/cn/axzo/im/entity/MessageTask.java | 12 +- .../{BizData.java => UpdatableMessage.java} | 22 ++- .../{BizLog.java => UpdatableMessageLog.java} | 8 +- .../im/enums/UpdatableMessageLogLogType.java | 11 ++ .../axzo/im/enums/UpdatableMessageState.java | 10 ++ .../axzo/im/job/CreateMessageHistoryJob.java | 3 +- .../service/impl/MessageTaskServiceImpl.java | 11 +- .../cn/axzo/im/updatable/InitHistories.java | 60 ++++++++ .../im/updatable/UpdatableMessageManager.java | 139 ++++++++++++++++++ 27 files changed, 482 insertions(+), 92 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageSendResult.java create mode 100644 im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageLogDao.java rename im-center-server/src/main/java/cn/axzo/im/entity/{BizData.java => UpdatableMessage.java} (58%) rename im-center-server/src/main/java/cn/axzo/im/entity/{BizLog.java => UpdatableMessageLog.java} (65%) create mode 100644 im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index f068177..decba58 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -6,9 +6,11 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; @@ -54,6 +56,12 @@ public interface MessageApi { @PostMapping("/api/im/template-message/async/send") ApiResult sendTemplateMessageAsync(@RequestBody @Validated SendTemplateMessageParam sendMessageParam); + /** + * 更新消息 + */ + @PostMapping("/api/im/template-message/updateMessage") + ApiResult updateMessage(@RequestBody @Validated UpdateMessageRequest request); + /** * * 接口已经作废,可以使用sendTemplateMessage来替换 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java new file mode 100644 index 0000000..6c284b6 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -0,0 +1,46 @@ +package cn.axzo.im.center.api.vo; + +import cn.axzo.im.center.common.enums.AppTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author yanglin + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +// IMPORTANT: 不要删除这个注解, 避免@Data被@Setter, @Getter取代 +@EqualsAndHashCode +public class PersonAccountAttribute { + + /** + * 接收消息的personId + */ + @NotBlank(message = "personId不能为空") + private String personId; + + /** + * appType = AppTypeEnum.CMP时,因为网易云信无法对同一个账号做企业隔离,只能一个企业一个账号, + * 所以需要根据organizationalUnitId获取账号 + */ + private Long ouId; + + /** + * 发送消息到App端 + * 工人端、企业端、服务器 + * CM、CMP、SYSTEM + * + * @See cn.axzo.im.center.common.enums.AppTypeEnum + */ + @NotNull(message = "appType不能为空") + private AppTypeEnum appType; + +} diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index bd7bfe1..3188414 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -1,18 +1,19 @@ package cn.axzo.im.center.api.vo.req; -import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.common.enums.TemplatedMsgType; import com.alibaba.fastjson.JSONObject; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.NonNull; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Data @Builder @@ -25,7 +26,7 @@ public class SendTemplateMessageParam { */ @NotEmpty(message = "消息接收用户信息不能为空") @Valid - private List receivePersons; + private List receivePersons; /** * 消息标题 @@ -63,32 +64,16 @@ public class SendTemplateMessageParam { private Integer sendPriority; - @Data - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class ReceivePerson { + private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE; - /** - * 接收消息的personId - */ - @NotBlank(message = "personId不能为空") - private String personId; + private String updatableRefTemplateId; - /** - * appType = AppTypeEnum.CMP时,因为网易云信无法对同一个账号做企业隔离,只能一个企业一个账号, - * 所以需要根据organizationalUnitId获取账号 - */ - private Long ouId; - - /** - * 发送消息到App端 - * 工人端、企业端、服务器 - * CM、CMP、SYSTEM - * - * @See cn.axzo.im.center.common.enums.AppTypeEnum - */ - @NotNull(message = "appType不能为空") - private AppTypeEnum appType; + public boolean isUpdatable() { + return templatedMsgType.isUpdatable(); } + + public Set uniqueReceivePersons() { + return new HashSet<>(receivePersons); + } + } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java new file mode 100644 index 0000000..fcbe90d --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -0,0 +1,12 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class UpdateMessageRequest { +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java index c73fc87..80cd969 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java @@ -8,6 +8,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; +import java.util.List; @Data @Builder @@ -53,4 +54,6 @@ public class MessageTaskResp { private Date createAt; private Date updateAt; + + private List updatableMessageSendResults; } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java new file mode 100644 index 0000000..a9600d5 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java @@ -0,0 +1,12 @@ +package cn.axzo.im.center.api.vo.resp; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class MessageUpdateResp { +} diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageSendResult.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageSendResult.java new file mode 100644 index 0000000..5ca6951 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageSendResult.java @@ -0,0 +1,15 @@ +package cn.axzo.im.center.api.vo.resp; + +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class UpdatableMessageSendResult { + private String bizMessageId; + private PersonAccountAttribute account; +} \ No newline at end of file diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/AppTypeEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/AppTypeEnum.java index 2ca7e81..62cf580 100644 --- a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/AppTypeEnum.java +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/AppTypeEnum.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.common.enums; +import cn.axzo.basics.common.constant.enums.CodeDefinition; import lombok.Getter; import org.apache.commons.lang3.StringUtils; @@ -14,7 +15,7 @@ import java.util.List; * @date 2023/10/9 16:01 */ @Getter -public enum AppTypeEnum { +public enum AppTypeEnum implements CodeDefinition { /** * 工人端 diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java new file mode 100644 index 0000000..685eb30 --- /dev/null +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java @@ -0,0 +1,22 @@ +package cn.axzo.im.center.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * @author yanglin + */ +@Getter +@RequiredArgsConstructor +public enum TemplatedMsgType { + + TEMPLATE("template", "模板消息", false), + CARD("card", "卡片消息", true), + + ; + + private final String code; + private final String message; + private final boolean isUpdatable; + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index d5f6442..2516e3c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -11,9 +11,12 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; +import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; import cn.axzo.im.center.api.vo.resp.UserAccountResp; import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.entity.MessageTask; @@ -25,6 +28,7 @@ import cn.axzo.im.service.CustomMessageService; import cn.axzo.im.service.MessageHistoryService; import cn.axzo.im.service.MessageTaskService; import cn.axzo.im.service.RobotMsgTemplateService; +import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.pokonyan.exception.Aassert; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -38,10 +42,12 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -75,6 +81,8 @@ public class MessageController implements MessageApi { private MessageHistoryService messageHistoryService; @Autowired private CustomMessageService customMessageService; + @Autowired + private UpdatableMessageManager updatableMessageManager; @Override @@ -149,18 +157,22 @@ public class MessageController implements MessageApi { } @Override + @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam sendMessageParam) { String sendImAccount = check(sendMessageParam); MessageTask.BizData bizData = MessageTask.BizData.builder() .msgTemplateContent(sendMessageParam.getMsgTemplateContent()) .msgTemplateId(sendMessageParam.getMsgTemplateId()) + .templatedMsgType(sendMessageParam.getTemplatedMsgType()) .build(); Date now = new Date(); + List receivePersons = JSONArray.parseArray( + JSONObject.toJSONString(sendMessageParam.uniqueReceivePersons()), MessageTask.ReceivePerson.class); MessageTask messageTask = messageTaskService.create(MessageTask.builder() .bizId(sendMessageParam.getBizId()) .sendImAccount(sendImAccount) - .receivePersons(JSONArray.parseArray(JSONObject.toJSONString(sendMessageParam.getReceivePersons()), MessageTask.ReceivePerson.class)) + .receivePersons(receivePersons) .status(MessageTaskStatus.PENDING) .title(sendMessageParam.getMsgHeader()) .content(sendMessageParam.getMsgContent()) @@ -172,7 +184,19 @@ public class MessageController implements MessageApi { .determinePriority(sendMessageParam.getSendPriority())) .apiChannel(ApiChannel.COMMON_MESSAGE) .build()); - return ApiResult.ok(toMessageTaskResp(messageTask)); + List updatableMessageSendResults = Collections.emptyList(); + if (sendMessageParam.isUpdatable()) { + updatableMessageSendResults = updatableMessageManager.createUpdatableMessage(messageTask, sendMessageParam, receivePersons); + } + MessageTaskResp messageTaskResp = toMessageTaskResp(messageTask); + messageTaskResp.setUpdatableMessageSendResults(updatableMessageSendResults); + return ApiResult.ok(messageTaskResp); + } + + @Override + public ApiResult updateMessage(UpdateMessageRequest request) { + MessageUpdateResp resp = updatableMessageManager.updateMessage(request); + return ApiResult.ok(resp); } private void check(SendMessageParam sendMessageParam) { diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java deleted file mode 100644 index ddded85..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizDataMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.axzo.im.dao.mapper; - -import cn.axzo.im.entity.BizData; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.springframework.stereotype.Repository; - -/** - * @author yanglin - */ -public interface BizDataMapper extends BaseMapper { -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java deleted file mode 100644 index dff7def..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/BizLogMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package cn.axzo.im.dao.mapper; - -import cn.axzo.im.entity.BizLog; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; - -/** - * @author yanglin - */ -public interface BizLogMapper extends BaseMapper { -} diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java new file mode 100644 index 0000000..9a988cb --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java @@ -0,0 +1,10 @@ +package cn.axzo.im.dao.mapper; + +import cn.axzo.im.entity.UpdatableMessageLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @author yanglin + */ +public interface UpdatableMessageLogMapper extends BaseMapper { +} diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java new file mode 100644 index 0000000..42bfe2f --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java @@ -0,0 +1,10 @@ +package cn.axzo.im.dao.mapper; + +import cn.axzo.im.entity.UpdatableMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @author yanglin + */ +public interface UpdatableMessageMapper extends BaseMapper { +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java deleted file mode 100644 index 945c1ab..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizDataDao.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.axzo.im.dao.repository; - -import cn.axzo.im.dao.mapper.BizDataMapper; -import cn.axzo.im.entity.BizData; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.springframework.stereotype.Repository; - -/** - * @author yanglin - */ -@Repository("bizDataDao") -public class BizDataDao extends ServiceImpl { -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java deleted file mode 100644 index 7e8bd4e..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/BizLogDao.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.axzo.im.dao.repository; - -import cn.axzo.im.dao.mapper.BizLogMapper; -import cn.axzo.im.entity.BizLog; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.springframework.stereotype.Repository; - -/** - * @author yanglin - */ -@Repository("bizLogDao") -public class BizLogDao extends ServiceImpl { -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java new file mode 100644 index 0000000..918292f --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -0,0 +1,26 @@ +package cn.axzo.im.dao.repository; + +import cn.axzo.im.dao.mapper.UpdatableMessageMapper; +import cn.axzo.im.entity.UpdatableMessage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Repository; + +import java.util.Collections; +import java.util.List; + +/** + * @author yanglin + */ +@Repository("updatableMessageDao") +public class UpdatableMessageDao extends ServiceImpl { + + public List getByTaskIds(List taskIds) { + if (CollectionUtils.isEmpty(taskIds)) + return Collections.emptyList(); + return lambdaQuery() + .in(UpdatableMessage::getTaskId, taskIds) + .list(); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageLogDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageLogDao.java new file mode 100644 index 0000000..8cc332f --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageLogDao.java @@ -0,0 +1,13 @@ +package cn.axzo.im.dao.repository; + +import cn.axzo.im.dao.mapper.UpdatableMessageLogMapper; +import cn.axzo.im.entity.UpdatableMessageLog; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Repository; + +/** + * @author yanglin + */ +@Repository("updatableMessageLogDao") +public class UpdatableMessageLogDao extends ServiceImpl { +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java index b4e91a1..3218233 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java @@ -1,10 +1,11 @@ package cn.axzo.im.entity; +import cn.axzo.im.center.api.vo.ApiChannel; 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; +import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.config.BaseListTypeHandler; -import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.enums.MessageTaskStatus; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.IdType; @@ -17,6 +18,7 @@ import com.google.common.collect.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @@ -115,6 +117,9 @@ public class MessageTask { @NoArgsConstructor @AllArgsConstructor public static class BizData { + + private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE; + private String msgTemplateId; /** @@ -151,12 +156,17 @@ public class MessageTask { * @See cn.axzo.im.center.common.enums.AppTypeEnum */ private List appTypes; + + public TemplatedMsgType determineTemplatedMsgType() { + return templatedMsgType == null ? TemplatedMsgType.TEMPLATE : templatedMsgType; + } } @Data @Builder @AllArgsConstructor @NoArgsConstructor + @EqualsAndHashCode public static class ReceivePerson { /** diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/BizData.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java similarity index 58% rename from im-center-server/src/main/java/cn/axzo/im/entity/BizData.java rename to im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 7558742..16dd709 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/BizData.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -1,7 +1,8 @@ package cn.axzo.im.entity; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.AppTypeEnum; -import cn.axzo.im.utils.YesNo; +import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; @@ -14,16 +15,19 @@ import java.util.Date; */ @Setter @Getter -@TableName(value = "im_biz_data", autoResultMap = true) -public class BizData { +@TableName(value = "im_updatable_message", autoResultMap = true) +public class UpdatableMessage { private Long id; + private String batchNo; + private String templateId; + private String refTemplateId; private String bizId; private Long taskId; - private Long receiverPersonId; + private String receiverPersonId; private Long receiverOuId; private AppTypeEnum appType; - private YesNo valid; + private UpdatableMessageState state; private String bizMessageId; private Long initHistoryId; private Long updateHistoryId; @@ -39,6 +43,14 @@ public class BizData { private Date createAt; private Date updateAt; + public PersonAccountAttribute parsePersonAccount() { + PersonAccountAttribute person = new PersonAccountAttribute(); + person.setPersonId(receiverPersonId); + person.setOuId(receiverOuId); + person.setAppType(appType); + return person; + } + @Getter @Setter public static class RecordExt { diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java similarity index 65% rename from im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java rename to im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 9257147..7522cd6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/BizLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -1,5 +1,7 @@ package cn.axzo.im.entity; +import cn.axzo.im.enums.UpdatableMessageLogLogType; +import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; @@ -12,11 +14,13 @@ import java.util.Date; */ @Setter @Getter -@TableName(value = "im_biz_log", autoResultMap = true) -public class BizLog { +@TableName(value = "im_updatable_message_log", autoResultMap = true) +public class UpdatableMessageLog { private Long id; + private UpdatableMessageLogLogType type; private String bizId; private String bizMessageId; + private UpdatableMessageState messageState; private Long initHistoryId; private Long updateHistoryId; private JSONObject messageBody; diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java new file mode 100644 index 0000000..632838b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java @@ -0,0 +1,11 @@ +package cn.axzo.im.enums; + +/** + * @author yanglin + */ +public enum UpdatableMessageLogLogType { + + CREATE, + UPDATE + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java new file mode 100644 index 0000000..c1faef3 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -0,0 +1,10 @@ +package cn.axzo.im.enums; + +/** + * @author yanglin + */ +public enum UpdatableMessageState { + CREATED, + INVALID, + VALID +} diff --git a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java index 885755d..d17e963 100644 --- a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java @@ -34,7 +34,8 @@ public class CreateMessageHistoryJob extends IJobHandler { @Autowired private MessageTaskService messageTaskService; - private static final Integer DEFAULT_PAGE_SIZE = 500; + // 有数据量放大的可能, 设置小一些 + private static final Integer DEFAULT_PAGE_SIZE = 100; @Override @XxlJob("createMessageHistoryJob") diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 2594e67..1cb7460 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -4,11 +4,9 @@ import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.common.enums.AccountTypeEnum; import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.channel.IMChannelProvider; -import cn.axzo.im.channel.netease.NimMsgTypeEnum; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.mapper.MessageTaskMapper; import cn.axzo.im.entity.AccountRegister; -import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; import cn.axzo.im.enums.MessageHistoryStatus; @@ -17,6 +15,7 @@ import cn.axzo.im.service.AccountRegisterService.AccountRegisterDTO; import cn.axzo.im.service.AccountService; import cn.axzo.im.service.MessageHistoryService; import cn.axzo.im.service.MessageTaskService; +import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.im.utils.UUIDUtil; import cn.axzo.maokai.api.client.OrganizationalTeamOuRelationApi; import cn.axzo.maokai.api.vo.request.OrganizationalTeamOuRelationReq; @@ -64,6 +63,8 @@ public class MessageTaskServiceImpl extends ServiceImpl resolveMessageHistory(batchNo, messageTask, receivePerson, imAccounts, accountRegisters, ouIdMap)) .collect(Collectors.toList()); messageHistoryService.createBatch(messageHistories); + updatableMessageManager.onHistoryCreated(messageHistories); } private MessageHistory resolveMessageHistory(String batchNo, @@ -325,14 +328,14 @@ public class MessageTaskServiceImpl extends ServiceImpl defaultExtMap = Maps.newHashMap(); - MessageTask.BizData bizData = messageTask.getBizData(); if (StringUtils.isNotBlank(bizData.getMsgTemplateContent())) { messageBody.setMsgBody(bizData.getMsgTemplateContent()); defaultExtMap.put("msgTemplateId", bizData.getMsgTemplateId()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java new file mode 100644 index 0000000..2811e4a --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java @@ -0,0 +1,60 @@ +package cn.axzo.im.updatable; + +import cn.axzo.basics.common.constant.enums.CodeDefinition; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * @author yanglin + */ +class InitHistories { + + private final Map account2histories; + + InitHistories(List histories) { + this.account2histories = histories.stream() + .filter(history -> { + AppTypeEnum appType = CodeDefinition.findByCode( + AppTypeEnum.class, history.getAppType()).orElse(null); + return appType != null; + }) + .collect(toMap(history -> { + PersonAccountAttribute person = new PersonAccountAttribute(); + person.setPersonId(history.getReceivePersonId()); + person.setOuId(history.getReceiveOuId()); + person.setAppType(CodeDefinition.findByCode( + AppTypeEnum.class, history.getAppType()).orElse(null)); + return new HistoryTaskAccount(history.getImMessageTaskId(), person); + }, identity(), (oldValue, newValue) -> oldValue)); + } + + public Optional findHistory(UpdatableMessage message) { + HistoryTaskAccount account = new HistoryTaskAccount( + message.getTaskId(), message.parsePersonAccount()); + return Optional.ofNullable(account2histories.get(account)); + } + + @Setter + @Getter + @EqualsAndHashCode + // IMPORTANT: 不要删除这个注解 + @RequiredArgsConstructor + private static class HistoryTaskAccount { + final Long taskId; + final PersonAccountAttribute person; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java new file mode 100644 index 0000000..5dd8d97 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -0,0 +1,139 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; +import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.MessageTask; +import cn.axzo.im.entity.MessageTask.ReceivePerson; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.enums.MessageHistoryStatus; +import cn.axzo.im.enums.UpdatableMessageLogLogType; +import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.UUIDUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class UpdatableMessageManager { + + private static final int BATCH_UPDATE_SIZE = 1000; + + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + + @Transactional + public List createUpdatableMessage( + MessageTask task, SendTemplateMessageParam request, List receivePersons) { + if (CollectionUtils.isEmpty(receivePersons)) return Collections.emptyList(); + String batchNo = UUIDUtil.uuidString(); + ArrayList sendResults = new ArrayList<>(); + ArrayList messages = new ArrayList<>(); + for (ReceivePerson person : receivePersons) { + UpdatableMessage message = new UpdatableMessage(); + messages.add(message); + message.setBatchNo(batchNo); + message.setTemplateId(request.getMsgTemplateId()); + message.setRefTemplateId(request.getUpdatableRefTemplateId()); + message.setBizId(request.getBizId()); + message.setTaskId(task.getId()); + message.setReceiverPersonId(person.getPersonId()); + message.setReceiverOuId(person.getOuId()); + message.setAppType(person.getAppType()); + message.setState(UpdatableMessageState.CREATED); + message.setBizMessageId(message.getBizMessageId()); + message.setDataVersion(1L); + + UpdatableMessageSendResult sendResult = new UpdatableMessageSendResult(); + sendResults.add(sendResult); + sendResult.setBizMessageId(message.getBizMessageId()); + sendResult.setAccount(message.parsePersonAccount()); + } + for (List batch : Lists.partition(messages, BATCH_UPDATE_SIZE)) + updatableMessageDao.saveBatch(batch); + + ArrayList logs = new ArrayList<>(); + for (UpdatableMessage message : messages) { + UpdatableMessageLog log = new UpdatableMessageLog(); + logs.add(log); + log.setType(UpdatableMessageLogLogType.CREATE); + log.setMessageState(UpdatableMessageState.CREATED); + log.setBizId(message.getBizId()); + log.setBizMessageId(message.getBizMessageId()); + log.setDataVersion(message.getDataVersion()); + } + for (List batch : Lists.partition(logs, BATCH_UPDATE_SIZE)) + updatableMessageLogDao.saveBatch(batch); + return sendResults; + } + + @Transactional + public void onHistoryCreated(List histories) { + if (CollectionUtils.isEmpty(histories)) return; + List taskIds = histories.stream() + .map(MessageHistory::getImMessageTaskId) + .distinct() + .collect(toList()); + List messages = updatableMessageDao.getByTaskIds(taskIds); + log.info("onHistoryCreated, taskIdSize={}, messageSize={}", taskIds.size(), messages.size()); + InitHistories initHistories = new InitHistories(histories); + ArrayList messageUpdates = new ArrayList<>(); + ArrayList messageLogUpdates = new ArrayList<>(); + for (UpdatableMessage message : messages) { + MessageHistory history = initHistories.findHistory(message).orElse(null); + if (history == null) continue; + String msgBodyJson = message.getMessageBody().getString("msgBody"); + JSONObject bizBody = JSON.parseObject(msgBodyJson); + + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setId(message.getId()); + messageUpdate.setInitHistoryId(history.getImMessageTaskId()); + messageUpdate.setMessageBody(message.getMessageBody()); + messageUpdate.setBizBody(bizBody); + messageUpdate.setState(history.getStatus() == MessageHistoryStatus.FAILED + ? UpdatableMessageState.INVALID + : UpdatableMessageState.VALID); + + UpdatableMessageLog messageLogUpdate = new UpdatableMessageLog(); + messageLogUpdates.add(messageLogUpdate); + messageLogUpdate.setMessageBody(message.getMessageBody()); + messageLogUpdate.setBizBody(bizBody); + messageLogUpdate.setInitHistoryId(history.getImMessageTaskId()); + messageLogUpdate.setMessageState(messageUpdate.getState()); + } + if (CollectionUtils.isNotEmpty(messageUpdates)) { + for (List batch : Lists.partition(messageUpdates, BATCH_UPDATE_SIZE)) + updatableMessageDao.updateBatchById(batch); + } + if (CollectionUtils.isNotEmpty(messageLogUpdates)) { + for (List batch : Lists.partition(messageLogUpdates, BATCH_UPDATE_SIZE)) + updatableMessageLogDao.updateBatchById(batch); + } + } + + public MessageUpdateResp updateMessage(UpdateMessageRequest request) { + return null; + } +} \ No newline at end of file From 4a937cf5ca73634b52f0a72221cc856acc2cce9a Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 10:21:51 +0800 Subject: [PATCH 02/93] =?UTF-8?q?REQ-3057-card:=20=E7=94=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=9B=BF=E6=8D=A2=E8=A6=86=E7=9B=96=20for=20record=5F?= =?UTF-8?q?ext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/entity/MessageHistory.java | 8 ++++- .../cn/axzo/im/entity/UpdatableMessage.java | 7 ++++ .../axzo/im/entity/UpdatableMessageLog.java | 14 ++++++++ .../axzo/im/enums/UpdatableMessageState.java | 15 ++++++-- .../java/cn/axzo/im/send/SendExecutor.java | 29 ++++++++------- .../java/cn/axzo/im/send/SendManager.java | 21 ++++++----- .../main/java/cn/axzo/im/send/SendQueue.java | 20 +++++++---- .../send/handler/CommonSendBatchHandler.java | 16 ++++----- .../im/send/handler/CommonSendOneHandler.java | 10 +++--- .../send/handler/CustomSendBatchHandler.java | 16 ++++----- .../im/send/handler/CustomSendOneHandler.java | 10 +++--- .../impl/MessageHistoryServiceImpl.java | 19 ++++++---- .../im/updatable/UpdatableMessageManager.java | 8 +++-- .../java/cn/axzo/im/utils/PropsUtils.java | 36 +++++++++++++++++++ .../axzo/im/send/job/SendMessageJobTest.java | 3 +- 15 files changed, 165 insertions(+), 67 deletions(-) create mode 100644 im-center-server/src/main/java/cn/axzo/im/utils/PropsUtils.java diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index c7a54d3..9c7b548 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -26,12 +26,12 @@ import java.util.Optional; * @version V1.0 * @date 2023/10/19 16:01 */ -@TableName("im_message_history") @Data @SuperBuilder @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor +@TableName(value = "im_message_history", autoResultMap = true) public class MessageHistory implements Serializable { private static final long serialVersionUID = 1L; @@ -125,6 +125,12 @@ public class MessageHistory implements Serializable { @TableField private Date timestampForSend; + public HistoryRecordExt getOrCreateRecordExt() { + if (recordExt == null) + recordExt = new HistoryRecordExt(); + return recordExt; + } + public Optional determineBatchNo() { String batchNo = this.batchNo; // 兼容在途数据 diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 16dd709..485841a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -4,7 +4,9 @@ import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; @@ -24,6 +26,8 @@ public class UpdatableMessage { private String refTemplateId; private String bizId; private Long taskId; + private String fromAccount; + private String toAccount; private String receiverPersonId; private Long receiverOuId; private AppTypeEnum appType; @@ -32,12 +36,15 @@ public class UpdatableMessage { private Long initHistoryId; private Long updateHistoryId; private Long retryHistoryId; + @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject messageBody; + @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; private Long dataVersion; private Long ackDataVersion; private Date ackTime; private Integer retryCount; + @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; private Long isDelete; private Date createAt; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 7522cd6..33c5a69 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -3,7 +3,9 @@ package cn.axzo.im.entity; import cn.axzo.im.enums.UpdatableMessageLogLogType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; @@ -19,14 +21,26 @@ public class UpdatableMessageLog { private Long id; private UpdatableMessageLogLogType type; private String bizId; + private String fromAccount; + private String toAccount; private String bizMessageId; private UpdatableMessageState messageState; private Long initHistoryId; private Long updateHistoryId; + @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject messageBody; + @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; private Long dataVersion; + @TableField(typeHandler = FastjsonTypeHandler.class) + private RecordExt recordExt; private Long isDelete; private Date createAt; private Date updateAt; + + @Setter + @Getter + public static class RecordExt { + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index c1faef3..19640ad 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -4,7 +4,16 @@ package cn.axzo.im.enums; * @author yanglin */ public enum UpdatableMessageState { + // 已创建 CREATED, - INVALID, - VALID -} + // 消息已经放进队列 + MESSAGE_QUEUED, + // 消息已经发送 + MESSAGE_SENT, + // 更新已经放进队列 + UPDATE_QUEUED, + // 更新已经发送 + UPDATED, + // 未找到账号 + ACCOUNT_NOT_FOUND +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/send/SendExecutor.java b/im-center-server/src/main/java/cn/axzo/im/send/SendExecutor.java index cf88a01..dbf7a78 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/SendExecutor.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/SendExecutor.java @@ -97,13 +97,13 @@ public class SendExecutor implements Supplier { queue().log(message, args); } - public void scheduleRetrySend(MessageHistory history, HistoryRecordExt ext) { - scheduleRetrySend(Collections.singletonList(history), ext); + public void scheduleRetrySend(MessageHistory history, HistoryRecordExt updateExt) { + scheduleRetrySend(Collections.singletonList(history), updateExt); } - public void scheduleRetrySend(List histories, HistoryRecordExt ext) { + public void scheduleRetrySend(List histories, HistoryRecordExt updateExt) { if (CollectionUtils.isEmpty(histories)) return; - queue().scheduleRetrySend(histories, ext); + queue().scheduleRetrySend(histories, updateExt); scheduleRetryCount.addAndGet(histories.size()); } @@ -121,22 +121,25 @@ public class SendExecutor implements Supplier { sendManager.submitSetSendFail(history, failReason); } - public void setBatchSendSuccess( - List histories, MessageBatchDispatchResponse response, HistoryRecordExt ext) { + public void setBatchSendSuccess(List histories, + MessageBatchDispatchResponse response, + HistoryRecordExt updateExt) { sendCount.addAndGet(histories.size()); - sendManager.setBatchSendSuccess(histories, response, ext); + sendManager.setBatchSendSuccess(histories, response, updateExt); } - public void setBatchSendSuccess( - List histories, BatchSendCustomMessageResponse response, HistoryRecordExt ext) { + public void setBatchSendSuccess(List histories, + BatchSendCustomMessageResponse response, + HistoryRecordExt updateExt) { sendCount.addAndGet(histories.size()); - sendManager.setBatchSendSuccess(histories, response, ext); + sendManager.setBatchSendSuccess(histories, response, updateExt); } - public void setSendFail( - List histories, String failReason, HistoryRecordExt ext) { + public void setSendFail(List histories, + String failReason, + HistoryRecordExt updateExt) { sendCount.addAndGet(histories.size()); - sendManager.setSendFail(histories, failReason, ext); + sendManager.setSendFail(histories, failReason, updateExt); } private static class Stage { diff --git a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java index 174b119..90212ea 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java @@ -203,21 +203,24 @@ public class SendManager { failHistories = new HashMap<>(); } - void setBatchSendSuccess( - List histories, MessageBatchDispatchResponse response, HistoryRecordExt ext) { - messageHistoryService.setBatchSendSuccess(histories, response, ext); + void setBatchSendSuccess(List histories, + MessageBatchDispatchResponse response, + HistoryRecordExt updateExt) { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); queue.setSendComplete(histories); } - void setBatchSendSuccess( - List histories, BatchSendCustomMessageResponse response, HistoryRecordExt ext) { - messageHistoryService.setBatchSendSuccess(histories, response, ext); + void setBatchSendSuccess(List histories, + BatchSendCustomMessageResponse response, + HistoryRecordExt updateExt) { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); queue.setSendComplete(histories); } - void setSendFail( - List histories, String failReason, HistoryRecordExt ext) { - messageHistoryService.setSendFail(histories, failReason, ext); + void setSendFail(List histories, + String failReason, + HistoryRecordExt updateExt) { + messageHistoryService.setSendFail(histories, failReason, updateExt); queue.setSendComplete(histories); } diff --git a/im-center-server/src/main/java/cn/axzo/im/send/SendQueue.java b/im-center-server/src/main/java/cn/axzo/im/send/SendQueue.java index 7dc7b7e..7e055b7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/SendQueue.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/SendQueue.java @@ -2,12 +2,14 @@ package cn.axzo.im.send; import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.dao.mapper.MessageHistoryMapper; +import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.ImProperties.SendMessageConfig; +import cn.axzo.im.utils.PropsUtils; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.Getter; @@ -46,6 +48,7 @@ public class SendQueue { private final ApiChannel apiChannel; private final SendMessageConfig cfg; private final MessageHistoryMapper messageHistoryMapper; + private final MessageHistoryDao messageHistoryDao; @Getter private final BlockingQueue logQueue = new ArrayBlockingQueue<>(2048); private final LinkedList records = new LinkedList<>(); private final Date queueCreateTime = new Date(); @@ -60,6 +63,7 @@ public class SendQueue { this.apiChannel = apiChannel; cfg = applicationContext.getBean(ImProperties.class).getSendMessage().copy(); messageHistoryMapper = applicationContext.getBean(MessageHistoryMapper.class); + messageHistoryDao = applicationContext.getBean(MessageHistoryDao.class); totalCount = messageHistoryMapper.selectCount(recordsQuery()); } @@ -208,7 +212,7 @@ public class SendQueue { lastLoadEmpty = true; } - public void scheduleRetrySend(List histories, HistoryRecordExt ext) { + public void scheduleRetrySend(List histories, HistoryRecordExt updateExt) { if (CollectionUtils.isEmpty(histories)) return; List ids = histories.stream() .map(MessageHistory::getId) @@ -221,11 +225,15 @@ public class SendQueue { Date newTimestamp = DateTime.now() .plusSeconds(delaySeconds) .toDate(); - MessageHistory update = new MessageHistory(); - update.setRecordExt(ext); - update.setTimestampForSend(newTimestamp); - messageHistoryMapper.update(update, query(MessageHistory.class) - .in(MessageHistory::getId, ids)); + ArrayList updates = new ArrayList<>(); + for (MessageHistory history : histories) { + MessageHistory update = new MessageHistory(); + updates.add(update); + update.setId(history.getId()); + update.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); + update.setTimestampForSend(newTimestamp); + } + messageHistoryDao.updateBatchById(updates); setSendComplete(histories); } diff --git a/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendBatchHandler.java b/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendBatchHandler.java index 4ae4a37..a5f5984 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendBatchHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendBatchHandler.java @@ -37,17 +37,17 @@ public class CommonSendBatchHandler extends SendBatchHandler { batchRequest.setFromAccid(sample.getFromAccount()); batchRequest.setToAccids(Lists.transform(histories, MessageHistory::getToAccount)); MessageBatchDispatchResponse response = imChannelProvider.dispatchBatchMessage(batchRequest); - HistoryRecordExt ext = new HistoryRecordExt(); - ext.setSendApi("batchSendMessage"); - ext.setSendExecId(executor.sendExec().getExecId()); - ext.setBatchSendId(UUIDUtil.uuidString()); - ext.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setSendApi("batchSendMessage"); + updateExt.setSendExecId(executor.sendExec().getExecId()); + updateExt.setBatchSendId(UUIDUtil.uuidString()); + updateExt.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); if (response.isRateLimited()) - executor.scheduleRetrySend(histories, ext); + executor.scheduleRetrySend(histories, updateExt); else if (response.isSuccess()) - executor.setBatchSendSuccess(histories, response, ext); + executor.setBatchSendSuccess(histories, response, updateExt); else - executor.setSendFail(histories, response.getDesc(), ext); + executor.setSendFail(histories, response.getDesc(), updateExt); } @Override diff --git a/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendOneHandler.java b/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendOneHandler.java index 9c93d8f..c0bf15b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendOneHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/handler/CommonSendOneHandler.java @@ -34,12 +34,12 @@ public class CommonSendOneHandler extends SendOneHandler { sendRequest.setType(ChannelMsgTypeEnum.CUSTOM.getCode()); sendRequest.setBody(history.getMessageBody()); MessageDispatchResponse response = imChannelProvider.dispatchMessage(sendRequest); - HistoryRecordExt ext = new HistoryRecordExt(); - ext.setSendApi("sendMessage"); - ext.setSendExecId(executor.sendExec().getExecId()); - ext.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setSendApi("sendMessage"); + updateExt.setSendExecId(executor.sendExec().getExecId()); + updateExt.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); if (response.isRateLimited()) - executor.scheduleRetrySend(history, ext); + executor.scheduleRetrySend(history, updateExt); else if (response.isSuccess()) executor.submitSetSendSuccess(history, response.getMsgid()); else diff --git a/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendBatchHandler.java b/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendBatchHandler.java index f3b84e1..5d10d61 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendBatchHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendBatchHandler.java @@ -40,17 +40,17 @@ public class CustomSendBatchHandler extends SendBatchHandler { request.setAttachJsonString(sample.getMessageBody()); request.setPayloadJsonString(sample.getMessageBody()); BatchSendCustomMessageResponse response = nimClient.batchSendCustomMessage(request); - HistoryRecordExt ext = new HistoryRecordExt(); - ext.setSendApi("batchSendCustomMessage"); - ext.setSendExecId(executor.sendExec().getExecId()); - ext.setBatchSendId(UUIDUtil.uuidString()); - ext.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setSendApi("batchSendCustomMessage"); + updateExt.setSendExecId(executor.sendExec().getExecId()); + updateExt.setBatchSendId(UUIDUtil.uuidString()); + updateExt.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); if (response.isRateLimited()) - executor.scheduleRetrySend(histories, ext); + executor.scheduleRetrySend(histories, updateExt); else if (response.isSuccess()) - executor.setBatchSendSuccess(histories, response, ext); + executor.setBatchSendSuccess(histories, response, updateExt); else - executor.setSendFail(histories, response.getDesc(), ext); + executor.setSendFail(histories, response.getDesc(), updateExt); } @Override diff --git a/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendOneHandler.java b/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendOneHandler.java index 2b4f2dd..9374c23 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendOneHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/handler/CustomSendOneHandler.java @@ -32,12 +32,12 @@ public class CustomSendOneHandler extends SendOneHandler { request.setAttachJsonString(history.getMessageBody()); request.setPayloadJsonString(history.getMessageBody()); SendCustomMessageResponse response = nimClient.sendCustomMessage(request); - HistoryRecordExt ext = new HistoryRecordExt(); - ext.setSendApi("sendCustomMessage"); - ext.setSendExecId(executor.sendExec().getExecId()); - ext.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setSendApi("sendCustomMessage"); + updateExt.setSendExecId(executor.sendExec().getExecId()); + updateExt.setSendRespDesc(StringUtils.isBlank(response.getDesc()) ? "" : response.getDesc()); if (response.isRateLimited()) - executor.scheduleRetrySend(history, ext); + executor.scheduleRetrySend(history, updateExt); else if (response.isSuccess()) executor.submitSetSendSuccess(history, ""); else diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageHistoryServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageHistoryServiceImpl.java index c0a7e1c..f9fd6c2 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageHistoryServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageHistoryServiceImpl.java @@ -17,6 +17,7 @@ import cn.axzo.im.event.payload.MessageHistoryCreatedPayload; import cn.axzo.im.event.payload.MessageHistoryUpdatedPayload; import cn.axzo.im.service.AccountService; import cn.axzo.im.service.MessageHistoryService; +import cn.axzo.im.utils.PropsUtils; import cn.axzo.maokai.api.client.OrganizationalUnitApi; import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery; import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; @@ -218,13 +219,15 @@ public class MessageHistoryServiceImpl extends ServiceImpl histories, BatchSendCustomMessageResponse response, HistoryRecordExt ext) { + public void setBatchSendSuccess(List histories, + BatchSendCustomMessageResponse response, + HistoryRecordExt updateExt) { ArrayList updates = new ArrayList<>(histories.size()); for (MessageHistory history : histories) { MessageHistory update = new MessageHistory(); updates.add(update); update.setId(history.getId()); - update.setRecordExt(ext); + update.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); MessageHistoryStatus status = response.getUnregister().contains(history.getToAccount()) ? MessageHistoryStatus.FAILED : MessageHistoryStatus.SUCCEED; @@ -237,7 +240,9 @@ public class MessageHistoryServiceImpl extends ServiceImpl histories, MessageBatchDispatchResponse response, HistoryRecordExt ext) { + public void setBatchSendSuccess(List histories, + MessageBatchDispatchResponse response, + HistoryRecordExt updateExt) { // 发送成功的IMAccountId -> msgId Map msgids = response.getMsgids(); // unregister的账号 @@ -252,7 +257,7 @@ public class MessageHistoryServiceImpl extends ServiceImpl { MessageHistory messageHistory = MessageHistory.builder() .id(history.getId()) - .recordExt(ext) + .recordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)) .build(); if (finalUnregister.contains(history.getToAccount())) { messageHistory.setStatus(MessageHistoryStatus.FAILED); @@ -284,13 +289,15 @@ public class MessageHistoryServiceImpl extends ServiceImpl histories, String failReason, HistoryRecordExt ext) { + public void setSendFail(List histories, + String failReason, + HistoryRecordExt updateExt) { List updates = histories.stream() .map(e -> MessageHistory.builder() .id(e.getId()) .result(failReason) .status(MessageHistoryStatus.FAILED) - .recordExt(ext) + .recordExt(PropsUtils.updateProperties(e.getRecordExt(), updateExt)) .build()) .collect(toList()); this.updateBatch(updates); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 5dd8d97..8fb5e7f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -112,12 +112,16 @@ public class UpdatableMessageManager { messageUpdate.setInitHistoryId(history.getImMessageTaskId()); messageUpdate.setMessageBody(message.getMessageBody()); messageUpdate.setBizBody(bizBody); + messageUpdate.setFromAccount(history.getFromAccount()); + messageUpdate.setToAccount(history.getToAccount()); messageUpdate.setState(history.getStatus() == MessageHistoryStatus.FAILED - ? UpdatableMessageState.INVALID - : UpdatableMessageState.VALID); + ? UpdatableMessageState.MESSAGE_QUEUED + : UpdatableMessageState.ACCOUNT_NOT_FOUND); UpdatableMessageLog messageLogUpdate = new UpdatableMessageLog(); messageLogUpdates.add(messageLogUpdate); + messageLogUpdate.setFromAccount(history.getFromAccount()); + messageLogUpdate.setToAccount(history.getToAccount()); messageLogUpdate.setMessageBody(message.getMessageBody()); messageLogUpdate.setBizBody(bizBody); messageLogUpdate.setInitHistoryId(history.getImMessageTaskId()); diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/PropsUtils.java b/im-center-server/src/main/java/cn/axzo/im/utils/PropsUtils.java new file mode 100644 index 0000000..2eb13db --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/utils/PropsUtils.java @@ -0,0 +1,36 @@ +package cn.axzo.im.utils; + +import java.lang.reflect.Field; + +/** + * @author yanglin + */ +public class PropsUtils { + + /** + * 只合并第一层 + */ + public static T updateProperties(T oldObj, T newObj) { + if (oldObj == null) return newObj; + if (newObj == null) return oldObj; + try { + @SuppressWarnings("unchecked") + T merged = (T) oldObj.getClass().newInstance(); + for (Field field : merged.getClass().getDeclaredFields()) { + boolean accessible = field.isAccessible(); + field.setAccessible(true); + try { + Object oldFieldValue = field.get(oldObj); + Object newFieldValue = field.get(newObj); + field.set(merged, newFieldValue == null ? oldFieldValue : newFieldValue); + } finally { + field.setAccessible(accessible); + } + } + return merged; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} \ No newline at end of file diff --git a/im-center-server/src/test/java/cn/axzo/im/send/job/SendMessageJobTest.java b/im-center-server/src/test/java/cn/axzo/im/send/job/SendMessageJobTest.java index 405608b..0da37ef 100644 --- a/im-center-server/src/test/java/cn/axzo/im/send/job/SendMessageJobTest.java +++ b/im-center-server/src/test/java/cn/axzo/im/send/job/SendMessageJobTest.java @@ -18,7 +18,8 @@ class SendMessageJobTest { private final SendMessageJob sendMessageJob; @Test - void foo() { + void exec() { + sendMessageJob.execute(null);; } } \ No newline at end of file From 0c244011af9979c44e3ef996fad7fce7e6a7e4d9 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 14:19:29 +0800 Subject: [PATCH 03/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/entity/HistoryRecordExt.java | 7 +- .../axzo/im/entity/UpdatableMessageLog.java | 2 - .../im/enums/UpdatableMessageLogLogType.java | 11 -- .../axzo/im/enums/UpdatableMessageState.java | 14 ++- .../axzo/im/job/CreateMessageHistoryJob.java | 3 +- .../java/cn/axzo/im/send/SendManager.java | 31 +++++- .../axzo/im/updatable/HistoryAndMessage.java | 18 ++++ .../axzo/im/updatable/SendStateHandler.java | 62 +++++++++++ .../cn/axzo/im/updatable/StateHandler.java | 14 +++ .../im/updatable/UpdatableMessageManager.java | 101 +++++++++++++++--- .../axzo/im/updatable/UpdateStateHandler.java | 21 ++++ 11 files changed, 243 insertions(+), 41 deletions(-) delete mode 100644 im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 5f2d853..6f5cea7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -16,4 +16,9 @@ public class HistoryRecordExt { */ private String imAccountInfo; private Object customMessageBizInfo; -} + + private boolean isUpdatableMessage; + private String bizMessageId; + private Long dataVersion; + private boolean isUpdateMessage; +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 33c5a69..edc3ac1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -1,6 +1,5 @@ package cn.axzo.im.entity; -import cn.axzo.im.enums.UpdatableMessageLogLogType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableField; @@ -19,7 +18,6 @@ import java.util.Date; @TableName(value = "im_updatable_message_log", autoResultMap = true) public class UpdatableMessageLog { private Long id; - private UpdatableMessageLogLogType type; private String bizId; private String fromAccount; private String toAccount; diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java deleted file mode 100644 index 632838b..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.axzo.im.enums; - -/** - * @author yanglin - */ -public enum UpdatableMessageLogLogType { - - CREATE, - UPDATE - -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index 19640ad..ef9704b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -4,16 +4,22 @@ package cn.axzo.im.enums; * @author yanglin */ public enum UpdatableMessageState { + // 已创建 CREATED, // 消息已经放进队列 MESSAGE_QUEUED, - // 消息已经发送 - MESSAGE_SENT, + // 消息已经发送成功 + MESSAGE_SEND_SUCCESS, + // 消息已经发送失败 + MESSAGE_SEND_FAIL, // 更新已经放进队列 UPDATE_QUEUED, - // 更新已经发送 - UPDATED, + // 更新已经发送成功 + UPDATE_SEND_SUCCESS, + // 更新已经发送失败 + UPDATE_SEND_FAIL, // 未找到账号 ACCOUNT_NOT_FOUND + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java index d17e963..885755d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java @@ -34,8 +34,7 @@ public class CreateMessageHistoryJob extends IJobHandler { @Autowired private MessageTaskService messageTaskService; - // 有数据量放大的可能, 设置小一些 - private static final Integer DEFAULT_PAGE_SIZE = 100; + private static final Integer DEFAULT_PAGE_SIZE = 500; @Override @XxlJob("createMessageHistoryJob") diff --git a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java index 90212ea..14846f6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java @@ -7,6 +7,7 @@ import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.service.impl.MessageHistoryServiceImpl; +import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.im.utils.DateFormatUtil; import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.ImProperties.SendMessageConfig; @@ -15,6 +16,7 @@ import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Date; @@ -40,6 +42,8 @@ public class SendManager { private final SendExec sendExec; private final MessageHistoryMapper messageHistoryMapper; private final MessageHistoryServiceImpl messageHistoryService; + private final UpdatableMessageManager updatableMessageManager; + private final TransactionTemplate transactionTemplate; private final SendMessageConfig cfg; private final Date maxCreateAt; private final AsyncTasks asyncTasks; @@ -54,6 +58,8 @@ public class SendManager { this.cfg = applicationContext.getBean(ImProperties.class).getSendMessage().copy(); this.messageHistoryMapper = applicationContext.getBean(MessageHistoryMapper.class); this.messageHistoryService = applicationContext.getBean(MessageHistoryServiceImpl.class); + this.updatableMessageManager = applicationContext.getBean(UpdatableMessageManager.class); + this.transactionTemplate = applicationContext.getBean(TransactionTemplate.class); this.queue = new SendQueue(applicationContext, sendExec.getApiChannel()); this.sendExec = sendExec; this.maxCreateAt = getMaxCreateAt(); @@ -183,7 +189,10 @@ public class SendManager { return update; }) .collect(toList()); - messageHistoryService.updateBatch(updates); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.updateBatch(updates); + updatableMessageManager.onHistorySend(successHistories); + }); queue.setSendComplete(successHistories); } for (Map.Entry> e : failHistories.entrySet()) { @@ -196,7 +205,10 @@ public class SendManager { .status(MessageHistoryStatus.FAILED) .build()) .collect(toList()); - messageHistoryService.updateBatch(updates); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.updateBatch(updates); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } successHistories = new ArrayList<>(); @@ -206,21 +218,30 @@ public class SendManager { void setBatchSendSuccess(List histories, MessageBatchDispatchResponse response, HistoryRecordExt updateExt) { - messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } void setBatchSendSuccess(List histories, BatchSendCustomMessageResponse response, HistoryRecordExt updateExt) { - messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } void setSendFail(List histories, String failReason, HistoryRecordExt updateExt) { - messageHistoryService.setSendFail(histories, failReason, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setSendFail(histories, failReason, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java new file mode 100644 index 0000000..bcf4f6b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java @@ -0,0 +1,18 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +/** + * @author yanglin + */ +@Getter +@RequiredArgsConstructor +class HistoryAndMessage { + private final List histories; + private final UpdatableMessage message; +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java new file mode 100644 index 0000000..12d3eb4 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java @@ -0,0 +1,62 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.enums.UpdatableMessageState; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +class SendStateHandler implements StateHandler { + + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + + @Override + public void onSuccess(List historyAndMessages) { + updateState(historyAndMessages, true); + } + + @Override + public void onFail(List historyAndMessages) { + updateState(historyAndMessages, false); + } + + private void updateState(List historyAndMessages, boolean isSuccess) { + ArrayList messageUpdates = new ArrayList<>(); + ArrayList messageLogs = new ArrayList<>(); + for (HistoryAndMessage historyAndMessage : historyAndMessages) { + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setState(isSuccess + ? UpdatableMessageState.MESSAGE_SEND_SUCCESS + : UpdatableMessageState.MESSAGE_SEND_FAIL); + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogs.add(messageLog); + UpdatableMessage message = historyAndMessage.getMessage(); + messageLog.setBizId(message.getBizId()); + messageLog.setFromAccount(message.getFromAccount()); + messageLog.setToAccount(message.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(messageUpdate.getState()); + MessageHistory history = historyAndMessage.getHistories().get(0); + messageLog.setInitHistoryId(history.getId()); + messageLog.setMessageBody(message.getMessageBody()); + messageLog.setBizBody(message.getBizBody()); + messageLog.setDataVersion(message.getDataVersion()); + } + updatableMessageDao.updateBatchById(messageUpdates); + updatableMessageLogDao.saveBatch(messageLogs); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java new file mode 100644 index 0000000..03f4dd8 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java @@ -0,0 +1,14 @@ +package cn.axzo.im.updatable; + +import java.util.List; + +/** + * @author yanglin + */ +interface StateHandler { + + void onSuccess(List historyAndMessages); + + void onFail(List historyAndMessages); + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 8fb5e7f..b5c52d2 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -4,16 +4,18 @@ import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; +import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; import cn.axzo.im.entity.MessageTask.ReceivePerson; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; -import cn.axzo.im.enums.UpdatableMessageLogLogType; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -22,11 +24,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.BiFunction; import static java.util.stream.Collectors.toList; @@ -42,8 +44,12 @@ public class UpdatableMessageManager { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; + private final MessageHistoryDao messageHistoryDao; + private final SendStateHandler sendStateHandler; + private final UpdateStateHandler updateStateHandler; + + // !! 创建消息 - @Transactional public List createUpdatableMessage( MessageTask task, SendTemplateMessageParam request, List receivePersons) { if (CollectionUtils.isEmpty(receivePersons)) return Collections.emptyList(); @@ -77,9 +83,8 @@ public class UpdatableMessageManager { for (UpdatableMessage message : messages) { UpdatableMessageLog log = new UpdatableMessageLog(); logs.add(log); - log.setType(UpdatableMessageLogLogType.CREATE); - log.setMessageState(UpdatableMessageState.CREATED); log.setBizId(message.getBizId()); + log.setMessageState(message.getState()); log.setBizMessageId(message.getBizMessageId()); log.setDataVersion(message.getDataVersion()); } @@ -88,7 +93,6 @@ public class UpdatableMessageManager { return sendResults; } - @Transactional public void onHistoryCreated(List histories) { if (CollectionUtils.isEmpty(histories)) return; List taskIds = histories.stream() @@ -98,8 +102,9 @@ public class UpdatableMessageManager { List messages = updatableMessageDao.getByTaskIds(taskIds); log.info("onHistoryCreated, taskIdSize={}, messageSize={}", taskIds.size(), messages.size()); InitHistories initHistories = new InitHistories(histories); - ArrayList messageUpdates = new ArrayList<>(); - ArrayList messageLogUpdates = new ArrayList<>(); + List messageUpdates = new ArrayList<>(); + List messageLogUpdates = new ArrayList<>(); + List updatableHistories = new ArrayList<>(); for (UpdatableMessage message : messages) { MessageHistory history = initHistories.findHistory(message).orElse(null); if (history == null) continue; @@ -118,14 +123,27 @@ public class UpdatableMessageManager { ? UpdatableMessageState.MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); - UpdatableMessageLog messageLogUpdate = new UpdatableMessageLog(); - messageLogUpdates.add(messageLogUpdate); - messageLogUpdate.setFromAccount(history.getFromAccount()); - messageLogUpdate.setToAccount(history.getToAccount()); - messageLogUpdate.setMessageBody(message.getMessageBody()); - messageLogUpdate.setBizBody(bizBody); - messageLogUpdate.setInitHistoryId(history.getImMessageTaskId()); - messageLogUpdate.setMessageState(messageUpdate.getState()); + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogUpdates.add(messageLog); + messageLog.setBizId(history.getBizId()); + messageLog.setFromAccount(history.getFromAccount()); + messageLog.setToAccount(history.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(messageUpdate.getState()); + messageLog.setInitHistoryId(history.getImMessageTaskId()); + messageLog.setMessageBody(message.getMessageBody()); + messageLog.setBizBody(bizBody); + messageLog.setDataVersion(message.getDataVersion()); + + MessageHistory historyUpdate = new MessageHistory(); + updatableHistories.add(historyUpdate); + historyUpdate.setId(history.getId()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setUpdatableMessage(true); + updateExt.setBizMessageId(message.getBizMessageId()); + updateExt.setDataVersion(message.getDataVersion()); + updateExt.setUpdateMessage(false); + historyUpdate.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); } if (CollectionUtils.isNotEmpty(messageUpdates)) { for (List batch : Lists.partition(messageUpdates, BATCH_UPDATE_SIZE)) @@ -135,9 +153,60 @@ public class UpdatableMessageManager { for (List batch : Lists.partition(messageLogUpdates, BATCH_UPDATE_SIZE)) updatableMessageLogDao.updateBatchById(batch); } + if (CollectionUtils.isNotEmpty(updatableHistories)) { + for (List batch : Lists.partition(updatableHistories, BATCH_UPDATE_SIZE)) + messageHistoryDao.saveBatch(batch); + } } + // !! 更新消息 + public MessageUpdateResp updateMessage(UpdateMessageRequest request) { return null; } + + public void onHistorySend(List maybeUpdatedHistory) { + List historyIds = maybeUpdatedHistory.stream() + .filter(history -> { + HistoryRecordExt recordExt = history.getRecordExt(); + return recordExt != null && recordExt.isUpdateMessage(); + }) + .map(MessageHistory::getId) + .collect(toList()); + if (CollectionUtils.isEmpty(historyIds)) return; + List histories = messageHistoryDao.listByIds(historyIds); + List bizMessageIds = histories.stream() + .map(history -> history.getRecordExt().getBizMessageId()) + .distinct() + .sorted() + .collect(toList()); + List messages = updatableMessageDao.lambdaQuery() + .in(UpdatableMessage::getBizMessageId, bizMessageIds) + // 避免ack更新出错 + .last("FOR UPDATE") + .list(); + BiFunction> historyAndMessageBuilder = + (isUpdateMessage, isSuccess) -> { + List historyAndMessages = new ArrayList<>(); + for (UpdatableMessage message : messages) { + List stateHistories = histories.stream() + .filter(history -> isSuccess + ? history.getStatus() == MessageHistoryStatus.SUCCEED + : history.getStatus() == MessageHistoryStatus.FAILED) + .filter(history -> isUpdateMessage && history.getRecordExt().isUpdateMessage()) + .collect(toList()); + historyAndMessages.add(new HistoryAndMessage(stateHistories, message)); + } + return historyAndMessages; + }; + // send success + sendStateHandler.onSuccess(historyAndMessageBuilder.apply(true, true)); + // send fail + sendStateHandler.onSuccess(historyAndMessageBuilder.apply(true, false)); + // update success + updateStateHandler.onSuccess(historyAndMessageBuilder.apply(false, true)); + // update false + updateStateHandler.onSuccess(historyAndMessageBuilder.apply(false, false)); + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java new file mode 100644 index 0000000..2e6456c --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java @@ -0,0 +1,21 @@ +package cn.axzo.im.updatable; + +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author yanglin + */ +@Component +class UpdateStateHandler implements StateHandler { + + @Override + public void onSuccess(List historyAndMessages) { + } + + @Override + public void onFail(List historyAndMessages) { + } + +} \ No newline at end of file From 50f4ec0bde185c1297e5c341a50ac37e9cab2b7b Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 14:19:29 +0800 Subject: [PATCH 04/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/entity/HistoryRecordExt.java | 7 +- .../axzo/im/entity/UpdatableMessageLog.java | 2 - .../im/enums/UpdatableMessageLogLogType.java | 11 -- .../axzo/im/enums/UpdatableMessageState.java | 14 ++- .../axzo/im/job/CreateMessageHistoryJob.java | 3 +- .../java/cn/axzo/im/send/SendManager.java | 31 ++++- .../axzo/im/updatable/HistoryAndMessage.java | 18 +++ .../axzo/im/updatable/SendStateHandler.java | 62 ++++++++++ .../cn/axzo/im/updatable/StateHandler.java | 14 +++ .../im/updatable/UpdatableMessageManager.java | 116 +++++++++++++++--- .../axzo/im/updatable/UpdateStateHandler.java | 21 ++++ 11 files changed, 258 insertions(+), 41 deletions(-) delete mode 100644 im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 5f2d853..6f5cea7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -16,4 +16,9 @@ public class HistoryRecordExt { */ private String imAccountInfo; private Object customMessageBizInfo; -} + + private boolean isUpdatableMessage; + private String bizMessageId; + private Long dataVersion; + private boolean isUpdateMessage; +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 33c5a69..edc3ac1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -1,6 +1,5 @@ package cn.axzo.im.entity; -import cn.axzo.im.enums.UpdatableMessageLogLogType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableField; @@ -19,7 +18,6 @@ import java.util.Date; @TableName(value = "im_updatable_message_log", autoResultMap = true) public class UpdatableMessageLog { private Long id; - private UpdatableMessageLogLogType type; private String bizId; private String fromAccount; private String toAccount; diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java deleted file mode 100644 index 632838b..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageLogLogType.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.axzo.im.enums; - -/** - * @author yanglin - */ -public enum UpdatableMessageLogLogType { - - CREATE, - UPDATE - -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index 19640ad..ef9704b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -4,16 +4,22 @@ package cn.axzo.im.enums; * @author yanglin */ public enum UpdatableMessageState { + // 已创建 CREATED, // 消息已经放进队列 MESSAGE_QUEUED, - // 消息已经发送 - MESSAGE_SENT, + // 消息已经发送成功 + MESSAGE_SEND_SUCCESS, + // 消息已经发送失败 + MESSAGE_SEND_FAIL, // 更新已经放进队列 UPDATE_QUEUED, - // 更新已经发送 - UPDATED, + // 更新已经发送成功 + UPDATE_SEND_SUCCESS, + // 更新已经发送失败 + UPDATE_SEND_FAIL, // 未找到账号 ACCOUNT_NOT_FOUND + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java index d17e963..885755d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/job/CreateMessageHistoryJob.java @@ -34,8 +34,7 @@ public class CreateMessageHistoryJob extends IJobHandler { @Autowired private MessageTaskService messageTaskService; - // 有数据量放大的可能, 设置小一些 - private static final Integer DEFAULT_PAGE_SIZE = 100; + private static final Integer DEFAULT_PAGE_SIZE = 500; @Override @XxlJob("createMessageHistoryJob") diff --git a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java index 90212ea..14846f6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/SendManager.java @@ -7,6 +7,7 @@ import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.service.impl.MessageHistoryServiceImpl; +import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.im.utils.DateFormatUtil; import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.ImProperties.SendMessageConfig; @@ -15,6 +16,7 @@ import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Date; @@ -40,6 +42,8 @@ public class SendManager { private final SendExec sendExec; private final MessageHistoryMapper messageHistoryMapper; private final MessageHistoryServiceImpl messageHistoryService; + private final UpdatableMessageManager updatableMessageManager; + private final TransactionTemplate transactionTemplate; private final SendMessageConfig cfg; private final Date maxCreateAt; private final AsyncTasks asyncTasks; @@ -54,6 +58,8 @@ public class SendManager { this.cfg = applicationContext.getBean(ImProperties.class).getSendMessage().copy(); this.messageHistoryMapper = applicationContext.getBean(MessageHistoryMapper.class); this.messageHistoryService = applicationContext.getBean(MessageHistoryServiceImpl.class); + this.updatableMessageManager = applicationContext.getBean(UpdatableMessageManager.class); + this.transactionTemplate = applicationContext.getBean(TransactionTemplate.class); this.queue = new SendQueue(applicationContext, sendExec.getApiChannel()); this.sendExec = sendExec; this.maxCreateAt = getMaxCreateAt(); @@ -183,7 +189,10 @@ public class SendManager { return update; }) .collect(toList()); - messageHistoryService.updateBatch(updates); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.updateBatch(updates); + updatableMessageManager.onHistorySend(successHistories); + }); queue.setSendComplete(successHistories); } for (Map.Entry> e : failHistories.entrySet()) { @@ -196,7 +205,10 @@ public class SendManager { .status(MessageHistoryStatus.FAILED) .build()) .collect(toList()); - messageHistoryService.updateBatch(updates); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.updateBatch(updates); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } successHistories = new ArrayList<>(); @@ -206,21 +218,30 @@ public class SendManager { void setBatchSendSuccess(List histories, MessageBatchDispatchResponse response, HistoryRecordExt updateExt) { - messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } void setBatchSendSuccess(List histories, BatchSendCustomMessageResponse response, HistoryRecordExt updateExt) { - messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setBatchSendSuccess(histories, response, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } void setSendFail(List histories, String failReason, HistoryRecordExt updateExt) { - messageHistoryService.setSendFail(histories, failReason, updateExt); + transactionTemplate.executeWithoutResult(unused -> { + messageHistoryService.setSendFail(histories, failReason, updateExt); + updatableMessageManager.onHistorySend(histories); + }); queue.setSendComplete(histories); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java new file mode 100644 index 0000000..bcf4f6b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java @@ -0,0 +1,18 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +/** + * @author yanglin + */ +@Getter +@RequiredArgsConstructor +class HistoryAndMessage { + private final List histories; + private final UpdatableMessage message; +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java new file mode 100644 index 0000000..12d3eb4 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java @@ -0,0 +1,62 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.enums.UpdatableMessageState; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +class SendStateHandler implements StateHandler { + + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + + @Override + public void onSuccess(List historyAndMessages) { + updateState(historyAndMessages, true); + } + + @Override + public void onFail(List historyAndMessages) { + updateState(historyAndMessages, false); + } + + private void updateState(List historyAndMessages, boolean isSuccess) { + ArrayList messageUpdates = new ArrayList<>(); + ArrayList messageLogs = new ArrayList<>(); + for (HistoryAndMessage historyAndMessage : historyAndMessages) { + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setState(isSuccess + ? UpdatableMessageState.MESSAGE_SEND_SUCCESS + : UpdatableMessageState.MESSAGE_SEND_FAIL); + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogs.add(messageLog); + UpdatableMessage message = historyAndMessage.getMessage(); + messageLog.setBizId(message.getBizId()); + messageLog.setFromAccount(message.getFromAccount()); + messageLog.setToAccount(message.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(messageUpdate.getState()); + MessageHistory history = historyAndMessage.getHistories().get(0); + messageLog.setInitHistoryId(history.getId()); + messageLog.setMessageBody(message.getMessageBody()); + messageLog.setBizBody(message.getBizBody()); + messageLog.setDataVersion(message.getDataVersion()); + } + updatableMessageDao.updateBatchById(messageUpdates); + updatableMessageLogDao.saveBatch(messageLogs); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java new file mode 100644 index 0000000..03f4dd8 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java @@ -0,0 +1,14 @@ +package cn.axzo.im.updatable; + +import java.util.List; + +/** + * @author yanglin + */ +interface StateHandler { + + void onSuccess(List historyAndMessages); + + void onFail(List historyAndMessages); + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 8fb5e7f..239709e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -4,16 +4,18 @@ import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; +import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; import cn.axzo.im.entity.MessageTask.ReceivePerson; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; -import cn.axzo.im.enums.UpdatableMessageLogLogType; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -22,11 +24,12 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.function.BiFunction; import static java.util.stream.Collectors.toList; @@ -42,8 +45,12 @@ public class UpdatableMessageManager { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; + private final MessageHistoryDao messageHistoryDao; + private final SendStateHandler sendStateHandler; + private final UpdateStateHandler updateStateHandler; + + // !! 创建消息 - @Transactional public List createUpdatableMessage( MessageTask task, SendTemplateMessageParam request, List receivePersons) { if (CollectionUtils.isEmpty(receivePersons)) return Collections.emptyList(); @@ -77,9 +84,8 @@ public class UpdatableMessageManager { for (UpdatableMessage message : messages) { UpdatableMessageLog log = new UpdatableMessageLog(); logs.add(log); - log.setType(UpdatableMessageLogLogType.CREATE); - log.setMessageState(UpdatableMessageState.CREATED); log.setBizId(message.getBizId()); + log.setMessageState(message.getState()); log.setBizMessageId(message.getBizMessageId()); log.setDataVersion(message.getDataVersion()); } @@ -88,7 +94,6 @@ public class UpdatableMessageManager { return sendResults; } - @Transactional public void onHistoryCreated(List histories) { if (CollectionUtils.isEmpty(histories)) return; List taskIds = histories.stream() @@ -98,8 +103,9 @@ public class UpdatableMessageManager { List messages = updatableMessageDao.getByTaskIds(taskIds); log.info("onHistoryCreated, taskIdSize={}, messageSize={}", taskIds.size(), messages.size()); InitHistories initHistories = new InitHistories(histories); - ArrayList messageUpdates = new ArrayList<>(); - ArrayList messageLogUpdates = new ArrayList<>(); + List messageUpdates = new ArrayList<>(); + List messageLogUpdates = new ArrayList<>(); + List updatableHistories = new ArrayList<>(); for (UpdatableMessage message : messages) { MessageHistory history = initHistories.findHistory(message).orElse(null); if (history == null) continue; @@ -118,14 +124,27 @@ public class UpdatableMessageManager { ? UpdatableMessageState.MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); - UpdatableMessageLog messageLogUpdate = new UpdatableMessageLog(); - messageLogUpdates.add(messageLogUpdate); - messageLogUpdate.setFromAccount(history.getFromAccount()); - messageLogUpdate.setToAccount(history.getToAccount()); - messageLogUpdate.setMessageBody(message.getMessageBody()); - messageLogUpdate.setBizBody(bizBody); - messageLogUpdate.setInitHistoryId(history.getImMessageTaskId()); - messageLogUpdate.setMessageState(messageUpdate.getState()); + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogUpdates.add(messageLog); + messageLog.setBizId(history.getBizId()); + messageLog.setFromAccount(history.getFromAccount()); + messageLog.setToAccount(history.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(messageUpdate.getState()); + messageLog.setInitHistoryId(history.getImMessageTaskId()); + messageLog.setMessageBody(message.getMessageBody()); + messageLog.setBizBody(bizBody); + messageLog.setDataVersion(message.getDataVersion()); + + MessageHistory historyUpdate = new MessageHistory(); + updatableHistories.add(historyUpdate); + historyUpdate.setId(history.getId()); + HistoryRecordExt updateExt = new HistoryRecordExt(); + updateExt.setUpdatableMessage(true); + updateExt.setBizMessageId(message.getBizMessageId()); + updateExt.setDataVersion(message.getDataVersion()); + updateExt.setUpdateMessage(false); + historyUpdate.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); } if (CollectionUtils.isNotEmpty(messageUpdates)) { for (List batch : Lists.partition(messageUpdates, BATCH_UPDATE_SIZE)) @@ -135,9 +154,74 @@ public class UpdatableMessageManager { for (List batch : Lists.partition(messageLogUpdates, BATCH_UPDATE_SIZE)) updatableMessageLogDao.updateBatchById(batch); } + if (CollectionUtils.isNotEmpty(updatableHistories)) { + for (List batch : Lists.partition(updatableHistories, BATCH_UPDATE_SIZE)) + messageHistoryDao.saveBatch(batch); + } } + // !! 更新消息 + public MessageUpdateResp updateMessage(UpdateMessageRequest request) { return null; } + + public void onHistorySend(List maybeUpdatedHistory) { + List historyIds = maybeUpdatedHistory.stream() + .filter(history -> { + HistoryRecordExt recordExt = history.getRecordExt(); + return recordExt != null && recordExt.isUpdateMessage(); + }) + .map(MessageHistory::getId) + .collect(toList()); + if (CollectionUtils.isEmpty(historyIds)) return; + List histories = messageHistoryDao.listByIds(historyIds); + List bizMessageIds = histories.stream() + .map(history -> history.getRecordExt().getBizMessageId()) + .distinct() + .sorted() + .collect(toList()); + List messages = updatableMessageDao.lambdaQuery() + .in(UpdatableMessage::getBizMessageId, bizMessageIds) + // 避免ack更新出错 + .last("FOR UPDATE") + .list(); + HashMap id2Messages = new HashMap<>(); + messages.forEach(message -> id2Messages.put(message.getId(), message)); + BiFunction> historyAndMessageBuilder = + (isUpdateMessage, isSuccess) -> { + List historyAndMessages = new ArrayList<>(); + for (UpdatableMessage message : new ArrayList<>(id2Messages.values())) { + List stateHistories = histories.stream() + .filter(history -> isSuccess + ? history.getStatus() == MessageHistoryStatus.SUCCEED + : history.getStatus() == MessageHistoryStatus.FAILED) + .filter(history -> isUpdateMessage && history.getRecordExt().isUpdateMessage()) + .collect(toList()); + if (stateHistories.isEmpty()) + continue; + // 一次只会处理一种情况, 避免无用的遍历 + id2Messages.remove(message.getId()); + historyAndMessages.add(new HistoryAndMessage(stateHistories, message)); + } + return historyAndMessages; + }; + // send success + List sendSuccess = historyAndMessageBuilder.apply(true, true); + if (!sendSuccess.isEmpty()) + sendStateHandler.onSuccess(sendSuccess); + // send fail + List sendFail = historyAndMessageBuilder.apply(true, false); + if (!sendFail.isEmpty()) + sendStateHandler.onFail(sendFail); + // update success + List updateSuccess = historyAndMessageBuilder.apply(false, true); + if (!updateSuccess.isEmpty()) + updateStateHandler.onSuccess(updateSuccess); + // update fail + List updateFail = historyAndMessageBuilder.apply(false, false); + if (!updateFail.isEmpty()) + updateStateHandler.onFail(updateFail); + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java new file mode 100644 index 0000000..2e6456c --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java @@ -0,0 +1,21 @@ +package cn.axzo.im.updatable; + +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author yanglin + */ +@Component +class UpdateStateHandler implements StateHandler { + + @Override + public void onSuccess(List historyAndMessages) { + } + + @Override + public void onFail(List historyAndMessages) { + } + +} \ No newline at end of file From c926e7868e7b2edfe40e9256d94f5c6481ab7ce9 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 16:34:55 +0800 Subject: [PATCH 05/93] REQ-3057-card: a lot of staff --- .../im/center/api/feign/SendPriority.java | 1 + .../api/vo/req/UpdateMessageRequest.java | 21 +++ .../im/center/common/enums/BizTypeEnum.java | 2 + .../im/channel/netease/dto/MessageBody.java | 11 ++ .../netease/dto/MessageCustomBody.java | 21 +++ .../dao/repository/UpdatableMessageDao.java | 8 + .../cn/axzo/im/entity/MessageHistory.java | 12 ++ .../cn/axzo/im/entity/UpdatableMessage.java | 2 + .../axzo/im/updatable/HistoryAndMessage.java | 10 ++ .../im/updatable/MessageBodyJsonObject.java | 21 +++ .../axzo/im/updatable/SendStateHandler.java | 4 +- .../im/updatable/UpdatableMessageManager.java | 158 +++++++++++++++--- .../axzo/im/updatable/UpdateStateHandler.java | 51 ++++++ 13 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java index 2503a02..ccffcc9 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java @@ -13,6 +13,7 @@ public class SendPriority { public static final SendPriority TEMPLATE_MESSAGE = create(1000); public static final SendPriority SYSTEM_CUSTOM_MESSAGE = create(5000); + public static final SendPriority UPDATE_MESSAGE = create(5500); public static final SendPriority OP_MESSAGE = create(500000); private final Integer value; diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index fcbe90d..780f3e4 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -3,10 +3,31 @@ package cn.axzo.im.center.api.vo.req; import lombok.Getter; import lombok.Setter; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.ArrayList; +import java.util.List; + /** * @author yanglin */ @Setter @Getter public class UpdateMessageRequest { + + @Valid + private List updates = new ArrayList<>(); + + public void addUpdate(Update update) { + this.updates.add(update); + } + + @Setter + @Getter + public static class Update { + @NotBlank(message = "消息ID不能为空") + private String bizMessageId; + private String msgTemplateContent; + } + } \ No newline at end of file diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/BizTypeEnum.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/BizTypeEnum.java index 77c9c51..dd7c283 100644 --- a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/BizTypeEnum.java +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/BizTypeEnum.java @@ -18,6 +18,8 @@ public enum BizTypeEnum { * 待办 */ PENDING("PENDING", "待办"), + + MESSAGE_UPDATE("MESSAGE_UPDATE", "消息更新") ; diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java index 91398d9..22122b2 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java @@ -36,4 +36,15 @@ public class MessageBody { private Map messageExtension; + + /** + * 业务消息id, 用于接口拉取最新消息内容 + */ + private String bizMessageId; + + /** + * 数据版本 + */ + private Long dataVersion; + } diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java index 89aea4a..3f3333e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java @@ -1,6 +1,7 @@ package cn.axzo.im.channel.netease.dto; import cn.axzo.im.center.common.enums.BizTypeEnum; +import cn.axzo.im.center.common.enums.TemplatedMsgType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -37,4 +38,24 @@ public class MessageCustomBody { private String payload; + /** + * 业务消息id, 用于接口拉取最新消息内容 + */ + private String bizMessageId; + + /** + * 数据版本 + */ + private Long dataVersion; + + /** + * 最原始的网易云信消息id, 更新的哪条消息 + */ + private Long initMessageId; + + /** + * 更新的消息类型 + */ + private TemplatedMsgType msgType; + } diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 918292f..2231126 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -15,6 +15,14 @@ import java.util.List; @Repository("updatableMessageDao") public class UpdatableMessageDao extends ServiceImpl { + public List getByBizMessageIdsForUpdate(List bizMessageIds) { + return lambdaQuery() + .in(UpdatableMessage::getBizMessageId, bizMessageIds) + // 避免ack更新出错 + .last("FOR UPDATE") + .list(); + } + public List getByTaskIds(List taskIds) { if (CollectionUtils.isEmpty(taskIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index 9c7b548..4513b39 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -131,6 +131,18 @@ public class MessageHistory implements Serializable { return recordExt; } + public Long getDataVersion() { + return recordExt != null ? recordExt.getDataVersion() : null; + } + + public boolean isUpdatableMessage() { + return recordExt != null && recordExt.isUpdatableMessage(); + } + + public boolean isUpdateMessage() { + return recordExt != null && recordExt.isUpdateMessage(); + } + public Optional determineBatchNo() { String batchNo = this.batchNo; // 兼容在途数据 diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 485841a..fef42a5 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -2,6 +2,7 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableField; @@ -31,6 +32,7 @@ public class UpdatableMessage { private String receiverPersonId; private Long receiverOuId; private AppTypeEnum appType; + private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; private Long initHistoryId; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java index bcf4f6b..1f2e91f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java @@ -7,12 +7,22 @@ import lombok.RequiredArgsConstructor; import java.util.List; +import static java.util.stream.Collectors.toList; + /** * @author yanglin */ @Getter @RequiredArgsConstructor class HistoryAndMessage { + private final List histories; private final UpdatableMessage message; + + public List getDataVersionMatchHistories() { + return histories.stream() + .filter(history -> history.getDataVersion().equals(message.getDataVersion())) + .collect(toList()); + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java b/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java new file mode 100644 index 0000000..c4e14c2 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java @@ -0,0 +1,21 @@ +package cn.axzo.im.updatable; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.Getter; + +/** + * @author yanglin + */ +@Getter +public class MessageBodyJsonObject { + + private final JSONObject messageBody; + private final JSONObject bizBody; + + public MessageBodyJsonObject(String json) { + messageBody = JSON.parseObject(json); + bizBody = JSON.parseObject(messageBody.getString("msgBody")); + } + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java index 12d3eb4..96a2a4f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java @@ -33,8 +33,8 @@ class SendStateHandler implements StateHandler { } private void updateState(List historyAndMessages, boolean isSuccess) { - ArrayList messageUpdates = new ArrayList<>(); - ArrayList messageLogs = new ArrayList<>(); + List messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 239709e..fa58bf0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -1,9 +1,16 @@ package cn.axzo.im.updatable; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.im.center.api.feign.SendPriority; +import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; +import cn.axzo.im.center.common.enums.BizTypeEnum; +import cn.axzo.im.channel.IMChannelProvider; +import cn.axzo.im.channel.netease.dto.MessageBody; +import cn.axzo.im.channel.netease.dto.MessageCustomBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; @@ -15,21 +22,27 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; @@ -48,8 +61,9 @@ public class UpdatableMessageManager { private final MessageHistoryDao messageHistoryDao; private final SendStateHandler sendStateHandler; private final UpdateStateHandler updateStateHandler; + private final IMChannelProvider imChannel; - // !! 创建消息 + // !! schedule public List createUpdatableMessage( MessageTask task, SendTemplateMessageParam request, List receivePersons) { @@ -68,8 +82,9 @@ public class UpdatableMessageManager { message.setReceiverPersonId(person.getPersonId()); message.setReceiverOuId(person.getOuId()); message.setAppType(person.getAppType()); + message.setMsgType(request.getTemplatedMsgType()); message.setState(UpdatableMessageState.CREATED); - message.setBizMessageId(message.getBizMessageId()); + message.setBizMessageId(UUIDUtil.uuidString()); message.setDataVersion(1L); UpdatableMessageSendResult sendResult = new UpdatableMessageSendResult(); @@ -94,6 +109,8 @@ public class UpdatableMessageManager { return sendResults; } + // !! enqueue + public void onHistoryCreated(List histories) { if (CollectionUtils.isEmpty(histories)) return; List taskIds = histories.stream() @@ -109,15 +126,14 @@ public class UpdatableMessageManager { for (UpdatableMessage message : messages) { MessageHistory history = initHistories.findHistory(message).orElse(null); if (history == null) continue; - String msgBodyJson = message.getMessageBody().getString("msgBody"); - JSONObject bizBody = JSON.parseObject(msgBodyJson); + MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setInitHistoryId(history.getImMessageTaskId()); - messageUpdate.setMessageBody(message.getMessageBody()); - messageUpdate.setBizBody(bizBody); + messageUpdate.setMessageBody(object.getMessageBody()); + messageUpdate.setBizBody(object.getBizBody()); messageUpdate.setFromAccount(history.getFromAccount()); messageUpdate.setToAccount(history.getToAccount()); messageUpdate.setState(history.getStatus() == MessageHistoryStatus.FAILED @@ -132,13 +148,19 @@ public class UpdatableMessageManager { messageLog.setBizMessageId(message.getBizMessageId()); messageLog.setMessageState(messageUpdate.getState()); messageLog.setInitHistoryId(history.getImMessageTaskId()); - messageLog.setMessageBody(message.getMessageBody()); - messageLog.setBizBody(bizBody); + messageLog.setMessageBody(object.getMessageBody()); + messageLog.setBizBody(object.getBizBody()); messageLog.setDataVersion(message.getDataVersion()); MessageHistory historyUpdate = new MessageHistory(); updatableHistories.add(historyUpdate); + historyUpdate.setId(history.getId()); + MessageBody messageBody = JSON.parseObject(history.getMessageBody(), MessageBody.class); + messageBody.setBizMessageId(message.getBizMessageId()); + messageBody.setDataVersion(message.getDataVersion()); + historyUpdate.setMessageBody(JSON.toJSONString(messageBody)); + HistoryRecordExt updateExt = new HistoryRecordExt(); updateExt.setUpdatableMessage(true); updateExt.setBizMessageId(message.getBizMessageId()); @@ -152,40 +174,122 @@ public class UpdatableMessageManager { } if (CollectionUtils.isNotEmpty(messageLogUpdates)) { for (List batch : Lists.partition(messageLogUpdates, BATCH_UPDATE_SIZE)) - updatableMessageLogDao.updateBatchById(batch); + updatableMessageLogDao.saveBatch(batch); } if (CollectionUtils.isNotEmpty(updatableHistories)) { for (List batch : Lists.partition(updatableHistories, BATCH_UPDATE_SIZE)) - messageHistoryDao.saveBatch(batch); + messageHistoryDao.updateBatchById(batch); } } - // !! 更新消息 + // !! update message + @Transactional public MessageUpdateResp updateMessage(UpdateMessageRequest request) { - return null; + BizAssertions.assertNotEmpty(request.getUpdates(), "更新消息不能为空"); + Map bizMessageId2Message = updatableMessageDao + .getByBizMessageIdsForUpdate( + request.getUpdates().stream() + .map(UpdateMessageRequest.Update::getBizMessageId) + .distinct() + .collect(toList())).stream() + .collect(Collectors.toMap(UpdatableMessage::getBizMessageId, Function.identity())); + if (bizMessageId2Message.isEmpty()) + throw new ServiceException("未找到需要更新的消息"); + String batchNo = UUIDUtil.uuidString(); + List histories = new ArrayList<>(); + List messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); + Map message2History = new IdentityHashMap<>(); + Map messageLog2History = new IdentityHashMap<>(); + for (UpdateMessageRequest.Update update : request.getUpdates()) { + UpdatableMessage message = bizMessageId2Message.get(update.getBizMessageId()); + if (message == null) continue; + + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setId(message.getId()); + messageUpdate.setDataVersion(messageUpdate.getDataVersion() + 1); + messageUpdate.setState(UpdatableMessageState.UPDATE_QUEUED); + + MessageHistory history = new MessageHistory(); + histories.add(history); + message2History.put(message, history); + history.setBizId(message.getBizId()); + history.setFromAccount(message.getFromAccount()); + history.setToAccount(message.getToAccount()); + history.setAppType(message.getAppType().getCode()); + history.setChannel(imChannel.getProviderType()); + MessageCustomBody messageBody = new MessageCustomBody(); + messageBody.setToImAccount(message.getToAccount()); + messageBody.setPersonId(message.getReceiverPersonId()); + messageBody.setBizType(BizTypeEnum.MESSAGE_UPDATE); + messageBody.setPayload(update.getMsgTemplateContent()); + messageBody.setBizMessageId(message.getBizMessageId()); + messageBody.setDataVersion(messageUpdate.getDataVersion()); + messageBody.setInitMessageId(message.getInitHistoryId()); + messageBody.setMsgType(message.getMsgType()); + history.setMessageBody(JSON.toJSONString(messageBody)); + messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); + messageUpdate.setBizBody(JSON.parseObject(update.getMsgTemplateContent())); + + history.setImMessageTaskId(0L); + history.setReceivePersonId(message.getReceiverPersonId()); + history.setReceiveOuId(message.getReceiverOuId()); + history.setStatus(MessageHistoryStatus.PENDING); + history.setBatchNo(batchNo); + history.setSendPriority(SendPriority.UPDATE_MESSAGE.getPriority()); + history.setApiChannel(ApiChannel.CUSTOM_MESSAGE); + HistoryRecordExt recordExt = new HistoryRecordExt(); + recordExt.setUpdatableMessage(true); + recordExt.setBizMessageId(message.getBizMessageId()); + recordExt.setDataVersion(message.getDataVersion()); + recordExt.setUpdateMessage(true); + history.setRecordExt(recordExt); + history.setTimestampForSend(new Date()); + + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogs.add(messageLog); + messageLog2History.put(messageLog, history); + messageLog.setBizId(message.getBizId()); + messageLog.setFromAccount(message.getFromAccount()); + messageLog.setToAccount(message.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(messageUpdate.getState()); + messageLog.setInitHistoryId(message.getInitHistoryId()); + messageLog.setMessageBody(messageUpdate.getMessageBody()); + messageLog.setBizBody(messageUpdate.getBizBody()); + messageLog.setDataVersion(messageUpdate.getDataVersion()); + } + messageHistoryDao.saveBatch(histories); + for (UpdatableMessage messageUpdate : messageUpdates) { + MessageHistory history = message2History.get(messageUpdate); + messageUpdate.setUpdateHistoryId(history.getId()); + } + for (UpdatableMessageLog messageLog : messageLogs) { + MessageHistory history = messageLog2History.get(messageLog); + messageLog.setUpdateHistoryId(history.getId()); + } + updatableMessageLogDao.saveBatch(messageLogs); + updatableMessageDao.updateBatchById(messageUpdates); + return new MessageUpdateResp(); } + @Transactional public void onHistorySend(List maybeUpdatedHistory) { List historyIds = maybeUpdatedHistory.stream() - .filter(history -> { - HistoryRecordExt recordExt = history.getRecordExt(); - return recordExt != null && recordExt.isUpdateMessage(); - }) + .filter(MessageHistory::isUpdatableMessage) .map(MessageHistory::getId) .collect(toList()); if (CollectionUtils.isEmpty(historyIds)) return; List histories = messageHistoryDao.listByIds(historyIds); - List bizMessageIds = histories.stream() - .map(history -> history.getRecordExt().getBizMessageId()) - .distinct() - .sorted() - .collect(toList()); - List messages = updatableMessageDao.lambdaQuery() - .in(UpdatableMessage::getBizMessageId, bizMessageIds) - // 避免ack更新出错 - .last("FOR UPDATE") - .list(); + // 避免ack更新出错 + List messages = updatableMessageDao + .getByBizMessageIdsForUpdate(histories.stream() + .map(h -> h.getRecordExt().getBizMessageId()) + .distinct() + .sorted() + .collect(toList())); HashMap id2Messages = new HashMap<>(); messages.forEach(message -> id2Messages.put(message.getId(), message)); BiFunction> historyAndMessageBuilder = diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java index 2e6456c..25f0b3f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java @@ -1,21 +1,72 @@ package cn.axzo.im.updatable; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.enums.UpdatableMessageState; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import java.util.ArrayList; import java.util.List; /** * @author yanglin */ @Component +@RequiredArgsConstructor class UpdateStateHandler implements StateHandler { + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + @Override public void onSuccess(List historyAndMessages) { + updateState(historyAndMessages, true); } @Override public void onFail(List historyAndMessages) { + updateState(historyAndMessages, false); } + private void updateState(List historyAndMessages, boolean isSuccess) { + List messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); + for (HistoryAndMessage historyAndMessage : historyAndMessages) { + // 1. 第一次更新(或成功, 或失败); 2. 有一次失败, 后面ack重试成功 (针对同一次更新); + if (historyAndMessage.getMessage().getState() != UpdatableMessageState.UPDATE_SEND_SUCCESS + && !historyAndMessage.getDataVersionMatchHistories().isEmpty()) { + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setState(isSuccess + ? UpdatableMessageState.UPDATE_SEND_SUCCESS + : UpdatableMessageState.UPDATE_SEND_FAIL); + } + + UpdatableMessage message = historyAndMessage.getMessage(); + for (MessageHistory history : historyAndMessage.getHistories()) { + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLogs.add(messageLog); + messageLog.setBizId(message.getBizId()); + messageLog.setFromAccount(message.getFromAccount()); + messageLog.setToAccount(message.getToAccount()); + messageLog.setBizMessageId(message.getBizMessageId()); + messageLog.setMessageState(isSuccess + ? UpdatableMessageState.UPDATE_SEND_SUCCESS + : UpdatableMessageState.UPDATE_SEND_FAIL); + messageLog.setInitHistoryId(message.getInitHistoryId()); + messageLog.setUpdateHistoryId(history.getId()); + MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); + messageLog.setMessageBody(object.getMessageBody()); + messageLog.setBizBody(object.getBizBody()); + messageLog.setDataVersion(history.getDataVersion()); + } + } + if (!messageUpdates.isEmpty()) + updatableMessageDao.updateBatchById(messageUpdates); + updatableMessageLogDao.saveBatch(messageLogs); + } } \ No newline at end of file From a84628e113e1c7175cce3d601f9e3b8638c41b9e Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 20:11:56 +0800 Subject: [PATCH 06/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/dao/mapper/AckRetryMapper.java | 10 --- .../dao/mapper/MessageUpdateRetryMapper.java | 17 ++++ .../axzo/im/dao/repository/AckRetryDao.java | 13 --- .../dao/repository/MessageUpdateRetryDao.java | 13 +++ .../dao/repository/UpdatableMessageDao.java | 3 +- .../cn/axzo/im/entity/HistoryRecordExt.java | 2 + .../cn/axzo/im/entity/MessageHistory.java | 8 ++ ...{AckRetry.java => MessageUpdateRetry.java} | 6 +- .../cn/axzo/im/entity/UpdatableMessage.java | 19 +++- .../axzo/im/entity/UpdatableMessageLog.java | 5 +- .../axzo/im/enums/UpdatableMessageState.java | 2 + .../axzo/im/updatable/HistoryAndMessage.java | 6 +- .../im/updatable/UpdatableMessageManager.java | 33 +++---- .../{ => handler}/SendStateHandler.java | 18 ++-- .../updatable/{ => handler}/StateHandler.java | 6 +- .../{ => handler}/UpdateStateHandler.java | 25 +++--- .../updateretry/MessageUpdateRetryJob.java | 75 ++++++++++++++++ .../MessageUpdateRetryService.java | 88 +++++++++++++++++++ .../updateretry/UpdateRetryState.java | 32 +++++++ .../java/cn/axzo/im/utils/ImProperties.java | 4 + .../MessageUpdateRetryJobTest.java | 22 +++++ 21 files changed, 335 insertions(+), 72 deletions(-) delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/AckRetryMapper.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/AckRetryDao.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageUpdateRetryDao.java rename im-center-server/src/main/java/cn/axzo/im/entity/{AckRetry.java => MessageUpdateRetry.java} (74%) rename im-center-server/src/main/java/cn/axzo/im/updatable/{ => handler}/SendStateHandler.java (76%) rename im-center-server/src/main/java/cn/axzo/im/updatable/{ => handler}/StateHandler.java (60%) rename im-center-server/src/main/java/cn/axzo/im/updatable/{ => handler}/UpdateStateHandler.java (72%) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java create mode 100644 im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/AckRetryMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/AckRetryMapper.java deleted file mode 100644 index acc6ce4..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/AckRetryMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package cn.axzo.im.dao.mapper; - -import cn.axzo.im.entity.AckRetry; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; - -/** - * @author yanglin - */ -public interface AckRetryMapper extends BaseMapper { -} diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java new file mode 100644 index 0000000..1eacbe3 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java @@ -0,0 +1,17 @@ +package cn.axzo.im.dao.mapper; + +import cn.axzo.im.entity.MessageUpdateRetry; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; + +/** + * @author yanglin + */ +public interface MessageUpdateRetryMapper extends BaseMapper { + + void deleteByBizMessageIds( + @Param("bizMessageIds") Collection bizMessageIds); + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/AckRetryDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/AckRetryDao.java deleted file mode 100644 index aa14860..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/AckRetryDao.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.axzo.im.dao.repository; - -import cn.axzo.im.dao.mapper.AckRetryMapper; -import cn.axzo.im.entity.AckRetry; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.springframework.stereotype.Repository; - -/** - * @author yanglin - */ -@Repository("ackRetryDao") -public class AckRetryDao extends ServiceImpl { -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageUpdateRetryDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageUpdateRetryDao.java new file mode 100644 index 0000000..8b13d9c --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageUpdateRetryDao.java @@ -0,0 +1,13 @@ +package cn.axzo.im.dao.repository; + +import cn.axzo.im.dao.mapper.MessageUpdateRetryMapper; +import cn.axzo.im.entity.MessageUpdateRetry; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Repository; + +/** + * @author yanglin + */ +@Repository("messageUpdateRetryDao") +public class MessageUpdateRetryDao extends ServiceImpl { +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 2231126..4589ae6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -15,7 +16,7 @@ import java.util.List; @Repository("updatableMessageDao") public class UpdatableMessageDao extends ServiceImpl { - public List getByBizMessageIdsForUpdate(List bizMessageIds) { + public List getByBizMessageIdsForUpdate(Collection bizMessageIds) { return lambdaQuery() .in(UpdatableMessage::getBizMessageId, bizMessageIds) // 避免ack更新出错 diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 6f5cea7..820848f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -21,4 +21,6 @@ public class HistoryRecordExt { private String bizMessageId; private Long dataVersion; private boolean isUpdateMessage; + private boolean isAckRetry; + private Long updateRetryCount = 0L; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index 4513b39..de836cc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -143,6 +143,14 @@ public class MessageHistory implements Serializable { return recordExt != null && recordExt.isUpdateMessage(); } + public boolean isAckRetry() { + return recordExt != null && recordExt.isAckRetry(); + } + + public Long getUpdateRetryCount() { + return recordExt == null ? null : recordExt.getUpdateRetryCount(); + } + public Optional determineBatchNo() { String batchNo = this.batchNo; // 兼容在途数据 diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/AckRetry.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageUpdateRetry.java similarity index 74% rename from im-center-server/src/main/java/cn/axzo/im/entity/AckRetry.java rename to im-center-server/src/main/java/cn/axzo/im/entity/MessageUpdateRetry.java index 07b77dc..7b80449 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/AckRetry.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageUpdateRetry.java @@ -11,16 +11,14 @@ import java.util.Date; */ @Setter @Getter -@TableName(value = "im_ack_retry", autoResultMap = true) -public class AckRetry { +@TableName(value = "im_message_update_retry", autoResultMap = true) +public class MessageUpdateRetry { private Long id; private String bizMessageId; private Long initHistoryId; private Date nextRetryTime; private Long dataVersion; - private Long retryHistoryId; - private Integer retryCount; private Long isDelete; private Date createAt; private Date updateAt; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index fef42a5..6517b20 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -45,13 +45,30 @@ public class UpdatableMessage { private Long dataVersion; private Long ackDataVersion; private Date ackTime; - private Integer retryCount; + private Long retryCount; @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; private Long isDelete; private Date createAt; private Date updateAt; + public UpdatableMessageLog toUpdatableMessageLog() { + UpdatableMessageLog messageLog = new UpdatableMessageLog(); + messageLog.setBizId(bizId); + messageLog.setFromAccount(fromAccount); + messageLog.setToAccount(toAccount); + messageLog.setBizMessageId(bizMessageId); + messageLog.setMessageState(state); + messageLog.setInitHistoryId(initHistoryId); + messageLog.setMessageBody(messageBody); + messageLog.setBizBody(bizBody); + messageLog.setRetryCount(retryCount); + messageLog.setDataVersion(dataVersion); + // messageLog.setContext(null); + // messageLog.setContextHistoryId(null); + return messageLog; + } + public PersonAccountAttribute parsePersonAccount() { PersonAccountAttribute person = new PersonAccountAttribute(); person.setPersonId(receiverPersonId); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index edc3ac1..3016814 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -17,6 +17,7 @@ import java.util.Date; @Getter @TableName(value = "im_updatable_message_log", autoResultMap = true) public class UpdatableMessageLog { + private Long id; private String bizId; private String fromAccount; @@ -24,11 +25,13 @@ public class UpdatableMessageLog { private String bizMessageId; private UpdatableMessageState messageState; private Long initHistoryId; - private Long updateHistoryId; + private String context; + private Long contextHistoryId; @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject messageBody; @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; + private Long retryCount; private Long dataVersion; @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index ef9704b..ca6c555 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -19,6 +19,8 @@ public enum UpdatableMessageState { UPDATE_SEND_SUCCESS, // 更新已经发送失败 UPDATE_SEND_FAIL, + // 更新ACK + UPDATE_ACK, // 未找到账号 ACCOUNT_NOT_FOUND diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java index 1f2e91f..650ce52 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java @@ -14,11 +14,15 @@ import static java.util.stream.Collectors.toList; */ @Getter @RequiredArgsConstructor -class HistoryAndMessage { +public class HistoryAndMessage { private final List histories; private final UpdatableMessage message; + public boolean hasDataVersionMatchHistories() { + return !getDataVersionMatchHistories().isEmpty(); + } + public List getDataVersionMatchHistories() { return histories.stream() .filter(history -> history.getDataVersion().equals(message.getDataVersion())) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index fa58bf0..0ca85e4 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -22,6 +22,9 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.updatable.handler.SendStateHandler; +import cn.axzo.im.updatable.handler.UpdateStateHandler; +import cn.axzo.im.updatable.updateretry.MessageUpdateRetryService; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; @@ -62,6 +65,7 @@ public class UpdatableMessageManager { private final SendStateHandler sendStateHandler; private final UpdateStateHandler updateStateHandler; private final IMChannelProvider imChannel; + private final MessageUpdateRetryService messageUpdateRetryService; // !! schedule @@ -97,12 +101,9 @@ public class UpdatableMessageManager { ArrayList logs = new ArrayList<>(); for (UpdatableMessage message : messages) { - UpdatableMessageLog log = new UpdatableMessageLog(); - logs.add(log); - log.setBizId(message.getBizId()); - log.setMessageState(message.getState()); - log.setBizMessageId(message.getBizMessageId()); - log.setDataVersion(message.getDataVersion()); + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + logs.add(messageLog); + messageLog.setContext("scheduleInTask"); } for (List batch : Lists.partition(logs, BATCH_UPDATE_SIZE)) updatableMessageLogDao.saveBatch(batch); @@ -140,17 +141,17 @@ public class UpdatableMessageManager { ? UpdatableMessageState.MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); - UpdatableMessageLog messageLog = new UpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); messageLogUpdates.add(messageLog); messageLog.setBizId(history.getBizId()); messageLog.setFromAccount(history.getFromAccount()); messageLog.setToAccount(history.getToAccount()); - messageLog.setBizMessageId(message.getBizMessageId()); messageLog.setMessageState(messageUpdate.getState()); messageLog.setInitHistoryId(history.getImMessageTaskId()); messageLog.setMessageBody(object.getMessageBody()); messageLog.setBizBody(object.getBizBody()); - messageLog.setDataVersion(message.getDataVersion()); + messageLog.setContext("createHistoryFromTask"); + messageLog.setContextHistoryId(history.getId()); MessageHistory historyUpdate = new MessageHistory(); updatableHistories.add(historyUpdate); @@ -232,6 +233,8 @@ public class UpdatableMessageManager { history.setMessageBody(JSON.toJSONString(messageBody)); messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); messageUpdate.setBizBody(JSON.parseObject(update.getMsgTemplateContent())); + // 重置 + messageUpdate.setRetryCount(0L); history.setImMessageTaskId(0L); history.setReceivePersonId(message.getReceiverPersonId()); @@ -248,18 +251,14 @@ public class UpdatableMessageManager { history.setRecordExt(recordExt); history.setTimestampForSend(new Date()); - UpdatableMessageLog messageLog = new UpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); messageLogs.add(messageLog); messageLog2History.put(messageLog, history); - messageLog.setBizId(message.getBizId()); - messageLog.setFromAccount(message.getFromAccount()); - messageLog.setToAccount(message.getToAccount()); - messageLog.setBizMessageId(message.getBizMessageId()); messageLog.setMessageState(messageUpdate.getState()); - messageLog.setInitHistoryId(message.getInitHistoryId()); messageLog.setMessageBody(messageUpdate.getMessageBody()); messageLog.setBizBody(messageUpdate.getBizBody()); messageLog.setDataVersion(messageUpdate.getDataVersion()); + messageLog.setRetryCount(messageUpdate.getRetryCount()); } messageHistoryDao.saveBatch(histories); for (UpdatableMessage messageUpdate : messageUpdates) { @@ -268,10 +267,12 @@ public class UpdatableMessageManager { } for (UpdatableMessageLog messageLog : messageLogs) { MessageHistory history = messageLog2History.get(messageLog); - messageLog.setUpdateHistoryId(history.getId()); + messageLog.setContext("updateMessage"); + messageLog.setContextHistoryId(history.getId()); } updatableMessageLogDao.saveBatch(messageLogs); updatableMessageDao.updateBatchById(messageUpdates); + messageUpdateRetryService.scheduleRetry(bizMessageId2Message.values()); return new MessageUpdateResp(); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java similarity index 76% rename from im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java index 96a2a4f..72ac4f0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/SendStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable; +package cn.axzo.im.updatable.handler; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; @@ -6,6 +6,7 @@ import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.updatable.HistoryAndMessage; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -17,7 +18,7 @@ import java.util.List; */ @Component @RequiredArgsConstructor -class SendStateHandler implements StateHandler { +public class SendStateHandler implements StateHandler { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; @@ -41,19 +42,14 @@ class SendStateHandler implements StateHandler { messageUpdate.setState(isSuccess ? UpdatableMessageState.MESSAGE_SEND_SUCCESS : UpdatableMessageState.MESSAGE_SEND_FAIL); - UpdatableMessageLog messageLog = new UpdatableMessageLog(); - messageLogs.add(messageLog); UpdatableMessage message = historyAndMessage.getMessage(); - messageLog.setBizId(message.getBizId()); - messageLog.setFromAccount(message.getFromAccount()); - messageLog.setToAccount(message.getToAccount()); - messageLog.setBizMessageId(message.getBizMessageId()); + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + messageLogs.add(messageLog); messageLog.setMessageState(messageUpdate.getState()); MessageHistory history = historyAndMessage.getHistories().get(0); messageLog.setInitHistoryId(history.getId()); - messageLog.setMessageBody(message.getMessageBody()); - messageLog.setBizBody(message.getBizBody()); - messageLog.setDataVersion(message.getDataVersion()); + messageLog.setContext("sendMessage"); + messageLog.setContextHistoryId(history.getId()); } updatableMessageDao.updateBatchById(messageUpdates); updatableMessageLogDao.saveBatch(messageLogs); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/StateHandler.java similarity index 60% rename from im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/handler/StateHandler.java index 03f4dd8..14410ec 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/StateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/StateHandler.java @@ -1,11 +1,13 @@ -package cn.axzo.im.updatable; +package cn.axzo.im.updatable.handler; + +import cn.axzo.im.updatable.HistoryAndMessage; import java.util.List; /** * @author yanglin */ -interface StateHandler { +public interface StateHandler { void onSuccess(List historyAndMessages); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java similarity index 72% rename from im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java index 25f0b3f..84d6075 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable; +package cn.axzo.im.updatable.handler; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; @@ -6,6 +6,8 @@ import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.updatable.HistoryAndMessage; +import cn.axzo.im.updatable.MessageBodyJsonObject; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -17,7 +19,7 @@ import java.util.List; */ @Component @RequiredArgsConstructor -class UpdateStateHandler implements StateHandler { +public class UpdateStateHandler implements StateHandler { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; @@ -36,9 +38,10 @@ class UpdateStateHandler implements StateHandler { List messageUpdates = new ArrayList<>(); List messageLogs = new ArrayList<>(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { - // 1. 第一次更新(或成功, 或失败); 2. 有一次失败, 后面ack重试成功 (针对同一次更新); - if (historyAndMessage.getMessage().getState() != UpdatableMessageState.UPDATE_SEND_SUCCESS - && !historyAndMessage.getDataVersionMatchHistories().isEmpty()) { + UpdatableMessageState state = historyAndMessage.getMessage().getState(); + boolean queuedOrFail = state == UpdatableMessageState.UPDATE_QUEUED + || state == UpdatableMessageState.UPDATE_SEND_FAIL; + if (queuedOrFail && historyAndMessage.hasDataVersionMatchHistories()) { UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); messageUpdate.setState(isSuccess @@ -48,21 +51,19 @@ class UpdateStateHandler implements StateHandler { UpdatableMessage message = historyAndMessage.getMessage(); for (MessageHistory history : historyAndMessage.getHistories()) { - UpdatableMessageLog messageLog = new UpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); messageLogs.add(messageLog); - messageLog.setBizId(message.getBizId()); - messageLog.setFromAccount(message.getFromAccount()); - messageLog.setToAccount(message.getToAccount()); - messageLog.setBizMessageId(message.getBizMessageId()); messageLog.setMessageState(isSuccess ? UpdatableMessageState.UPDATE_SEND_SUCCESS : UpdatableMessageState.UPDATE_SEND_FAIL); - messageLog.setInitHistoryId(message.getInitHistoryId()); - messageLog.setUpdateHistoryId(history.getId()); + messageLog.setContextHistoryId(history.getId()); MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); messageLog.setMessageBody(object.getMessageBody()); messageLog.setBizBody(object.getBizBody()); messageLog.setDataVersion(history.getDataVersion()); + messageLog.setContextHistoryId(history.getId()); + messageLog.setContext(history.isAckRetry() ? "retrySendUpdateMessage" : "sendUpdateMessage"); + messageLog.setRetryCount(history.getUpdateRetryCount()); } } if (!messageUpdates.isEmpty()) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java new file mode 100644 index 0000000..06ee37d --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java @@ -0,0 +1,75 @@ +package cn.axzo.im.updatable.updateretry; + +import cn.axzo.im.dao.repository.MessageUpdateRetryDao; +import cn.axzo.im.entity.MessageUpdateRetry; +import cn.axzo.maokai.api.util.Ref; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.Date; +import java.util.List; +import java.util.function.Supplier; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ + +@Slf4j +@Component +@RequiredArgsConstructor +public class MessageUpdateRetryJob { + + private final MessageUpdateRetryDao messageUpdateRetryDao; + private final MessageUpdateRetryService messageUpdateRetryService; + private final TransactionTemplate transactionTemplate; + + @XxlJob("messageUpdateRetryJob") + public ReturnT execute(String paramStr) throws Exception { + try { + log.info("start - run job with param={}", paramStr); + executeImpl(); + log.info("end - run job with param={}", paramStr); + return ReturnT.SUCCESS; + } catch (Exception e) { + log.warn("job failed. param={}", paramStr, e); + return ReturnT.FAIL; + } + } + + private void executeImpl() { + Supplier> cursor = retryCursor(); + for (List retries = cursor.get(); !retries.isEmpty(); retries = cursor.get()) { + List bizMessageIds = retries.stream() + .map(MessageUpdateRetry::getBizMessageId) + .collect(toList()); + transactionTemplate.executeWithoutResult(unused -> { + messageUpdateRetryService.removePrevRetryByBizMessageId(bizMessageIds); + UpdateRetryState state = messageUpdateRetryService.getUpdateRetryState(bizMessageIds); + messageUpdateRetryService.retryMessageUpdate(state.getNonAckMessages()); + messageUpdateRetryService.scheduleRetry(state.getScheduleNextRetryMessages()); + }); + } + } + + private Supplier> retryCursor() { + Ref maxId = Ref.create(0L); + return () -> { + List retries = messageUpdateRetryDao.lambdaQuery() + .le(MessageUpdateRetry::getNextRetryTime, new Date()) + .gt(MessageUpdateRetry::getId, maxId.get()) + .orderByAsc(MessageUpdateRetry::getId) + .last("LIMIT 1000") + .list(); + if (!retries.isEmpty()) + maxId.set(retries.get(retries.size() - 1).getId()); + return retries; + }; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java new file mode 100644 index 0000000..2e28e19 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java @@ -0,0 +1,88 @@ +package cn.axzo.im.updatable.updateretry; + +import cn.axzo.im.dao.repository.MessageUpdateRetryDao; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageUpdateRetry; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.utils.ImProperties; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.joda.time.DateTime; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +@Service +@RequiredArgsConstructor +public class MessageUpdateRetryService { + + private final MessageUpdateRetryDao messageUpdateRetryDao; + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + private final ImProperties props; + + public void removePrevRetryByMessage(Collection messages) { + List bizMessageIds = messages.stream() + .map(UpdatableMessage::getBizMessageId) + .collect(toList()); + removePrevRetryByBizMessageId(bizMessageIds); + } + + public void removePrevRetryByBizMessageId(Collection bizMessageIds) { + if (CollectionUtils.isNotEmpty(bizMessageIds)) + messageUpdateRetryDao.getBaseMapper().deleteByBizMessageIds(bizMessageIds); + } + + public UpdateRetryState getUpdateRetryState(Collection bizMessageIds) { + List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); + return new UpdateRetryState(messages, props.getMessageUpdateAckMaxRetryCount()); + } + + @Transactional + public void scheduleRetry(Collection messages) { + if (CollectionUtils.isEmpty(messages)) return; + removePrevRetryByMessage(messages); + ArrayList retries = new ArrayList<>(); + ArrayList messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); + Date nextRetryTime = DateTime.now() + .plusSeconds(props.getMessageUpdateAckRetryIntervalSeconds()) + .toDate(); + for (UpdatableMessage message : messages) { + MessageUpdateRetry retry = new MessageUpdateRetry(); + retry.setBizMessageId(message.getBizMessageId()); + retry.setInitHistoryId(message.getInitHistoryId()); + retry.setNextRetryTime(nextRetryTime); + retry.setDataVersion(message.getDataVersion()); + retries.add(retry); + + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setId(retry.getId()); + messageUpdate.setRetryCount(message.getRetryCount() + 1); + + UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + messageLog.setRetryCount(messageUpdate.getRetryCount()); + messageLog.setContext("scheduleRetrySendUpdateMessage"); + } + messageUpdateRetryDao.saveBatch(retries); + updatableMessageLogDao.saveBatch(messageLogs); + updatableMessageDao.updateBatchById(messageUpdates); + } + + public void retryMessageUpdate(Collection messages) { + if (CollectionUtils.isEmpty(messages)) return; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java new file mode 100644 index 0000000..3fcd06d --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java @@ -0,0 +1,32 @@ +package cn.axzo.im.updatable.updateretry; + +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.enums.UpdatableMessageState; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +@RequiredArgsConstructor +public class UpdateRetryState { + private final List messages; + private final int maxRetryCount; + + List getScheduleNextRetryMessages() { + return messages.stream() + .filter(message -> message.getRetryCount() < maxRetryCount) + .filter(message -> message.getState() != UpdatableMessageState.UPDATE_ACK) + .collect(toList()); + } + + List getNonAckMessages() { + return messages.stream() + .filter(message -> message.getState() != UpdatableMessageState.UPDATE_ACK) + .collect(toList()); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index 5a72ba7..306877e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -26,6 +26,10 @@ public class ImProperties { private String controllerToken = "123442"; + private int messageUpdateAckMaxRetryCount = 6; + + private int messageUpdateAckRetryIntervalSeconds = 10; + private SendMessageConfig sendMessage = new SendMessageConfig(); private Set genImAccountJobCodes = Sets.newHashSet( diff --git a/im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java b/im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java new file mode 100644 index 0000000..04b7a79 --- /dev/null +++ b/im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java @@ -0,0 +1,22 @@ +package cn.axzo.im.updatable.updateretry; + +import cn.axzo.im.Application; +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 = Application.class) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class MessageUpdateRetryJobTest { + + private final MessageUpdateRetryJob messageUpdateRetryJob; + + @Test + void exec() { + } + +} \ No newline at end of file From 71f4c1ab192fb1879ab0f945ddfececabdd9e9d6 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 6 Nov 2024 20:37:25 +0800 Subject: [PATCH 07/93] REQ-3057-card: a lot of staff --- .../java/cn/axzo/im/updatable/HistoryAndMessage.java | 9 +-------- .../cn/axzo/im/updatable/UpdatableMessageManager.java | 2 +- .../{updateretry => retry}/MessageUpdateRetryJob.java | 2 +- .../MessageUpdateRetryService.java | 2 +- .../{updateretry => retry}/UpdateRetryState.java | 2 +- .../MessageUpdateRetryJobTest.java | 2 +- 6 files changed, 6 insertions(+), 13 deletions(-) rename im-center-server/src/main/java/cn/axzo/im/updatable/{updateretry => retry}/MessageUpdateRetryJob.java (98%) rename im-center-server/src/main/java/cn/axzo/im/updatable/{updateretry => retry}/MessageUpdateRetryService.java (98%) rename im-center-server/src/main/java/cn/axzo/im/updatable/{updateretry => retry}/UpdateRetryState.java (95%) rename im-center-server/src/test/java/cn/axzo/im/updatable/{updateretry => retry}/MessageUpdateRetryJobTest.java (91%) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java index 650ce52..47c801d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/HistoryAndMessage.java @@ -7,8 +7,6 @@ import lombok.RequiredArgsConstructor; import java.util.List; -import static java.util.stream.Collectors.toList; - /** * @author yanglin */ @@ -20,13 +18,8 @@ public class HistoryAndMessage { private final UpdatableMessage message; public boolean hasDataVersionMatchHistories() { - return !getDataVersionMatchHistories().isEmpty(); - } - - public List getDataVersionMatchHistories() { return histories.stream() - .filter(history -> history.getDataVersion().equals(message.getDataVersion())) - .collect(toList()); + .anyMatch(history -> history.getDataVersion().equals(message.getDataVersion())); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 0ca85e4..dc13d1f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -24,7 +24,7 @@ import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.handler.SendStateHandler; import cn.axzo.im.updatable.handler.UpdateStateHandler; -import cn.axzo.im.updatable.updateretry.MessageUpdateRetryService; +import cn.axzo.im.updatable.retry.MessageUpdateRetryService; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java similarity index 98% rename from im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java index 06ee37d..380b503 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable.updateretry; +package cn.axzo.im.updatable.retry; import cn.axzo.im.dao.repository.MessageUpdateRetryDao; import cn.axzo.im.entity.MessageUpdateRetry; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java similarity index 98% rename from im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 2e28e19..96df45d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable.updateretry; +package cn.axzo.im.updatable.retry; import cn.axzo.im.dao.repository.MessageUpdateRetryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java similarity index 95% rename from im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java index 3fcd06d..7287a2c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/updateretry/UpdateRetryState.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable.updateretry; +package cn.axzo.im.updatable.retry; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.enums.UpdatableMessageState; diff --git a/im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java b/im-center-server/src/test/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJobTest.java similarity index 91% rename from im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java rename to im-center-server/src/test/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJobTest.java index 04b7a79..9848869 100644 --- a/im-center-server/src/test/java/cn/axzo/im/updatable/updateretry/MessageUpdateRetryJobTest.java +++ b/im-center-server/src/test/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJobTest.java @@ -1,4 +1,4 @@ -package cn.axzo.im.updatable.updateretry; +package cn.axzo.im.updatable.retry; import cn.axzo.im.Application; import lombok.RequiredArgsConstructor; From 077dc9912cd0af8eb08289b10fb30751b263762d Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 7 Nov 2024 18:21:02 +0800 Subject: [PATCH 08/93] REQ-3057-card: a lot of staff --- .../axzo/im/center/api/feign/MessageApi.java | 8 +- .../api/vo/req/AdvanceRetryRequest.java | 17 ++ .../center/api/vo/req/MessageUpdateInfo.java | 32 +++ .../vo/req/UpdatableMessageAckRequest.java | 54 ++++ .../api/vo/req/UpdateMessageRequest.java | 22 +- .../center/api/vo/resp/MessageUpdateResp.java | 12 - .../api/vo/resp/MessageUpdateResponse.java | 66 +++++ .../axzo/im/controller/MessageController.java | 13 +- .../dao/mapper/MessageUpdateRetryMapper.java | 7 + .../im/dao/mapper/UpdatableMessageMapper.java | 21 ++ .../im/dao/repository/MessageHistoryDao.java | 17 +- .../dao/repository/UpdatableMessageDao.java | 6 + .../cn/axzo/im/entity/HistoryRecordExt.java | 8 +- .../cn/axzo/im/entity/MessageHistory.java | 16 +- .../cn/axzo/im/entity/UpdatableMessage.java | 15 +- .../axzo/im/entity/UpdatableMessageLog.java | 7 +- .../axzo/im/enums/UpdatableMessageState.java | 27 +- .../im/send/job/SendCustomMessageJob.java | 2 +- .../cn/axzo/im/send/job/SendMessageJob.java | 2 +- .../service/impl/MessageTaskServiceImpl.java | 5 +- .../im/updatable/MessageBodyJsonObject.java | 6 +- .../im/updatable/UpdatableMessageManager.java | 253 +++++++++--------- .../im/updatable/UpdateHistorySupport.java | 126 +++++++++ ...teHandler.java => InitMessageHandler.java} | 13 +- ...Handler.java => UpdateMessageHandler.java} | 21 +- .../retry/MessageUpdateRetryJob.java | 9 +- .../retry/MessageUpdateRetryService.java | 53 ++-- .../im/updatable/retry/UpdateRetryState.java | 7 +- 28 files changed, 621 insertions(+), 224 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AdvanceRetryRequest.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/MessageUpdateInfo.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java delete mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResponse.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java rename im-center-server/src/main/java/cn/axzo/im/updatable/handler/{SendStateHandler.java => InitMessageHandler.java} (84%) rename im-center-server/src/main/java/cn/axzo/im/updatable/handler/{UpdateStateHandler.java => UpdateMessageHandler.java} (78%) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index decba58..a337426 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -6,11 +6,12 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; -import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; @@ -60,7 +61,10 @@ public interface MessageApi { * 更新消息 */ @PostMapping("/api/im/template-message/updateMessage") - ApiResult updateMessage(@RequestBody @Validated UpdateMessageRequest request); + ApiResult updateMessage(@RequestBody @Validated UpdateMessageRequest request); + + @PostMapping("/api/im/template-message/ack") + ApiResult ack(@RequestBody @Validated UpdatableMessageAckRequest request); /** * diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AdvanceRetryRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AdvanceRetryRequest.java new file mode 100644 index 0000000..efc62e3 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AdvanceRetryRequest.java @@ -0,0 +1,17 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * @author yanglin + */ +@Setter +@Getter +public class AdvanceRetryRequest { + + private List bizMessageIds; + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/MessageUpdateInfo.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/MessageUpdateInfo.java new file mode 100644 index 0000000..e19660d --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/MessageUpdateInfo.java @@ -0,0 +1,32 @@ +package cn.axzo.im.center.api.vo.req; + +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.collections4.CollectionUtils; + +import javax.validation.constraints.NotNull; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +public interface MessageUpdateInfo { + + static List collectBizMessageIds(Collection updates) { + if (CollectionUtils.isEmpty(updates)) + return Collections.emptyList(); + return updates.stream() + .map(MessageUpdateInfo::bizMessageId) + .distinct() + .collect(toList()); + } + + String bizMessageId(); + + @NotNull + JSONObject bizBody(); + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java new file mode 100644 index 0000000..dd645ee --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -0,0 +1,54 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +@Setter +@Getter +public class UpdatableMessageAckRequest { + + @NotEmpty(message = "消息ACK不能为空") + private List acks; + + public List determineValidAcks() { + if (CollectionUtils.isEmpty(acks)) + return Collections.emptyList(); + HashMap id2Max = new HashMap<>(); + for (Ack ack : acks) { + Long max = id2Max.getOrDefault(ack.bizMessageId, 0L); + max = Math.max(max, ack.dataVersion); + id2Max.put(ack.bizMessageId, max); + } + return id2Max.entrySet().stream() + .map(e -> { + Ack ack = new Ack(); + ack.setBizMessageId(e.getKey()); + ack.setDataVersion(e.getValue()); + return ack; + }) + .collect(toList()); + } + + @Setter + @Getter + public static class Ack { + @NotBlank(message = "业务消息ID不能为空") + private String bizMessageId; + @NotNull(message = "数据版本不能为空") + private Long dataVersion; + } + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index 780f3e4..9cf4b62 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -1,7 +1,10 @@ package cn.axzo.im.center.api.vo.req; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.StringUtils; import javax.validation.Valid; import javax.validation.constraints.NotBlank; @@ -22,12 +25,29 @@ public class UpdateMessageRequest { this.updates.add(update); } + public List getBizMessageIds() { + return MessageUpdateInfo.collectBizMessageIds(updates); + } + @Setter @Getter - public static class Update { + public static class Update implements MessageUpdateInfo { + @NotBlank(message = "消息ID不能为空") private String bizMessageId; private String msgTemplateContent; + + @Override + public String bizMessageId() { + return bizMessageId; + } + + @Override + public JSONObject bizBody() { + return StringUtils.isBlank(msgTemplateContent) + ? new JSONObject() + : JSON.parseObject(msgTemplateContent); + } } } \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java deleted file mode 100644 index a9600d5..0000000 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResp.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.axzo.im.center.api.vo.resp; - -import lombok.Getter; -import lombok.Setter; - -/** - * @author yanglin - */ -@Setter -@Getter -public class MessageUpdateResp { -} diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResponse.java new file mode 100644 index 0000000..d709801 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageUpdateResponse.java @@ -0,0 +1,66 @@ +package cn.axzo.im.center.api.vo.resp; + +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yanglin + */ +@Setter +@Getter +public class MessageUpdateResponse { + + /** + * 更新的业务消息ID + */ + private List updatedBizMessageIds = new ArrayList<>(); + + /** + * 未更新的业务消息 + */ + private List nonUpdatedMessages = new ArrayList<>(); + + public void addUpdatedBizMessageId(String bizMessageId) { + this.updatedBizMessageIds.add(bizMessageId); + } + + public void addNonUpdatedMessage(String bizMessageId, + NonUpdateMessageReason reason) { + addNonUpdatedMessage(bizMessageId, reason, null); + } + + public void addNonUpdatedMessage(String bizMessageId, + NonUpdateMessageReason reason, + Object description) { + NonUpdatedMessage nonUpdatedMessage = new NonUpdatedMessage(); + nonUpdatedMessage.setBizMessageId(bizMessageId); + nonUpdatedMessage.setReason(reason); + nonUpdatedMessage.setDescription(description); + this.nonUpdatedMessages.add(nonUpdatedMessage); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } + + @Setter + @Getter + public static class NonUpdatedMessage { + private String bizMessageId; + private NonUpdateMessageReason reason; + private Object description; + } + + public enum NonUpdateMessageReason { + // 找不到首次发送的消息 + CANT_FIND_INIT_MESSAGE, + // 首次投递消息的状态不允许更新. 此时查看NonUpdatedMessage#description字段 + MESSAGE_STATE_NOT_ALLOWED + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 2516e3c..45dcf38 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -11,11 +11,12 @@ import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; -import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; import cn.axzo.im.center.api.vo.resp.UserAccountResp; import cn.axzo.im.center.common.enums.AppTypeEnum; @@ -194,11 +195,17 @@ public class MessageController implements MessageApi { } @Override - public ApiResult updateMessage(UpdateMessageRequest request) { - MessageUpdateResp resp = updatableMessageManager.updateMessage(request); + public ApiResult updateMessage(UpdateMessageRequest request) { + MessageUpdateResponse resp = updatableMessageManager.updateMessage(request); return ApiResult.ok(resp); } + @Override + public ApiResult ack(UpdatableMessageAckRequest request) { + updatableMessageManager.ack(request); + return ApiResult.ok(); + } + private void check(SendMessageParam sendMessageParam) { List accountRegisters = accountRegisterService.list(AccountRegisterService.ListAccountRegisterParam.builder() .imAccount(sendMessageParam.getSendImAccount()) diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java index 1eacbe3..562d1e6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/MessageUpdateRetryMapper.java @@ -2,6 +2,7 @@ package cn.axzo.im.dao.mapper; import cn.axzo.im.entity.MessageUpdateRetry; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Param; import java.util.Collection; @@ -11,6 +12,12 @@ import java.util.Collection; */ public interface MessageUpdateRetryMapper extends BaseMapper { + @Delete("") void deleteByBizMessageIds( @Param("bizMessageIds") Collection bizMessageIds); diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java index 42bfe2f..bf780dd 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageMapper.java @@ -2,9 +2,30 @@ package cn.axzo.im.dao.mapper; import cn.axzo.im.entity.UpdatableMessage; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; + +import java.util.List; /** * @author yanglin */ public interface UpdatableMessageMapper extends BaseMapper { + + @Update("") + void incrDataVersion(@Param("ids") List ids); + + @Update("") + void incrRetryCount(@Param("ids") List ids); + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageHistoryDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageHistoryDao.java index b1d0d20..f71b397 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageHistoryDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/MessageHistoryDao.java @@ -1,12 +1,14 @@ package cn.axzo.im.dao.repository; -import cn.axzo.im.dao.mapper.AccountRegisterMapper; import cn.axzo.im.dao.mapper.MessageHistoryMapper; -import cn.axzo.im.entity.AccountRegister; import cn.axzo.im.entity.MessageHistory; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Repository; +import java.util.Collections; +import java.util.List; + /** * im-center * @@ -18,4 +20,15 @@ import org.springframework.stereotype.Repository; @Repository("messageHistoryDao") public class MessageHistoryDao extends ServiceImpl { + /** + * 不用自带的listByIds + */ + public List getByIds(List historyIds) { + if (CollectionUtils.isEmpty(historyIds)) + return Collections.emptyList(); + return lambdaQuery() + .in(MessageHistory::getId, historyIds) + .list(); + } + } diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 4589ae6..8a63ec8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -24,6 +24,12 @@ public class UpdatableMessageDao extends ServiceImpl getByBizMessageIds(Collection bizMessageIds) { + return lambdaQuery() + .in(UpdatableMessage::getBizMessageId, bizMessageIds) + .list(); + } + public List getByTaskIds(List taskIds) { if (CollectionUtils.isEmpty(taskIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 820848f..4d9a816 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -17,10 +17,10 @@ public class HistoryRecordExt { private String imAccountInfo; private Object customMessageBizInfo; - private boolean isUpdatableMessage; + private Boolean isUpdatableMessage; private String bizMessageId; private Long dataVersion; - private boolean isUpdateMessage; - private boolean isAckRetry; - private Long updateRetryCount = 0L; + private Boolean isUpdateMessage; + private Boolean isUpdateRetry; + private Long updateRetryCount; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index de836cc..df0e206 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -117,7 +117,7 @@ public class MessageHistory implements Serializable { private ApiChannel apiChannel; @TableField(typeHandler = IgnorePropsJsonTypeHandler.class) - private HistoryRecordExt recordExt = new HistoryRecordExt(); + private HistoryRecordExt recordExt; /** * 用于发送扫描 @@ -136,15 +136,21 @@ public class MessageHistory implements Serializable { } public boolean isUpdatableMessage() { - return recordExt != null && recordExt.isUpdatableMessage(); + return recordExt != null + && recordExt.getIsUpdatableMessage() != null + && recordExt.getIsUpdatableMessage(); } public boolean isUpdateMessage() { - return recordExt != null && recordExt.isUpdateMessage(); + return recordExt != null + && recordExt.getIsUpdatableMessage() != null + && recordExt.getIsUpdatableMessage(); } - public boolean isAckRetry() { - return recordExt != null && recordExt.isAckRetry(); + public boolean isUpdateRetry() { + return recordExt != null + && recordExt.getIsUpdateRetry() != null + && recordExt.getIsUpdateRetry(); } public Long getUpdateRetryCount() { diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 6517b20..7ef8671 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -1,6 +1,7 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.enums.UpdatableMessageState; @@ -19,7 +20,7 @@ import java.util.Date; @Setter @Getter @TableName(value = "im_updatable_message", autoResultMap = true) -public class UpdatableMessage { +public class UpdatableMessage implements MessageUpdateInfo { private Long id; private String batchNo; @@ -52,7 +53,7 @@ public class UpdatableMessage { private Date createAt; private Date updateAt; - public UpdatableMessageLog toUpdatableMessageLog() { + public UpdatableMessageLog toMessageLog() { UpdatableMessageLog messageLog = new UpdatableMessageLog(); messageLog.setBizId(bizId); messageLog.setFromAccount(fromAccount); @@ -77,6 +78,16 @@ public class UpdatableMessage { return person; } + @Override + public String bizMessageId() { + return bizMessageId; + } + + @Override + public JSONObject bizBody() { + return bizBody; + } + @Getter @Setter public static class RecordExt { diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 3016814..3c0570d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -1,6 +1,7 @@ package cn.axzo.im.entity; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.IgnorePropsJsonTypeHandler; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; @@ -33,7 +34,7 @@ public class UpdatableMessageLog { private JSONObject bizBody; private Long retryCount; private Long dataVersion; - @TableField(typeHandler = FastjsonTypeHandler.class) + @TableField(typeHandler = IgnorePropsJsonTypeHandler.class) private RecordExt recordExt; private Long isDelete; private Date createAt; @@ -42,6 +43,10 @@ public class UpdatableMessageLog { @Setter @Getter public static class RecordExt { + private Boolean ackIgnored; + private String ackDescription; + private Long requestDataVersion; + private Long messageDataVersion; } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index ca6c555..35d8f55 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -1,27 +1,36 @@ package cn.axzo.im.enums; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + /** * @author yanglin */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public enum UpdatableMessageState { // 已创建 - CREATED, + TASK_CREATED(false, false), // 消息已经放进队列 - MESSAGE_QUEUED, + INIT_MESSAGE_QUEUED(false, false), // 消息已经发送成功 - MESSAGE_SEND_SUCCESS, + INIT_MESSAGE_SEND_SUCCESS(true, false), // 消息已经发送失败 - MESSAGE_SEND_FAIL, + INIT_MESSAGE_SEND_FAIL(false, false), // 更新已经放进队列 - UPDATE_QUEUED, + UPDATE_MESSAGE_QUEUED(true, false), // 更新已经发送成功 - UPDATE_SEND_SUCCESS, + UPDATE_MESSAGE_SEND_SUCCESS(true, true), // 更新已经发送失败 - UPDATE_SEND_FAIL, + UPDATE_MESSAGE_SEND_FAIL(true, false), // 更新ACK - UPDATE_ACK, + UPDATE_ACK(true, false), // 未找到账号 - ACCOUNT_NOT_FOUND + ACCOUNT_NOT_FOUND(false, false); + + private final boolean isUpdateMessageAllowed; + private final boolean isUpdateAckAllowed; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/send/job/SendCustomMessageJob.java b/im-center-server/src/main/java/cn/axzo/im/send/job/SendCustomMessageJob.java index 79d58b4..1c85ba4 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/job/SendCustomMessageJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/job/SendCustomMessageJob.java @@ -23,7 +23,7 @@ public class SendCustomMessageJob extends SendMessageExecInstance { } @XxlJob("sendCustomMessageJob") - ReturnT execute(String param) { + public ReturnT execute(String param) { try { scanAndSend(); return ReturnT.SUCCESS; diff --git a/im-center-server/src/main/java/cn/axzo/im/send/job/SendMessageJob.java b/im-center-server/src/main/java/cn/axzo/im/send/job/SendMessageJob.java index 2066211..435d1ab 100644 --- a/im-center-server/src/main/java/cn/axzo/im/send/job/SendMessageJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/send/job/SendMessageJob.java @@ -23,7 +23,7 @@ public class SendMessageJob extends SendMessageExecInstance { } @XxlJob("sendMessageJob") - ReturnT execute(String param) { + public ReturnT execute(String param) { try { scanAndSend(); return ReturnT.SUCCESS; diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 1cb7460..d929f50 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -192,7 +192,10 @@ public class MessageTaskServiceImpl extends ServiceImpl resolveMessageHistory(batchNo, messageTask, receivePerson, imAccounts, accountRegisters, ouIdMap)) .collect(Collectors.toList()); messageHistoryService.createBatch(messageHistories); - updatableMessageManager.onHistoryCreated(messageHistories); + List historyIds = messageHistories.stream() + .map(MessageHistory::getId) + .collect(Collectors.toList()); + updatableMessageManager.onHistoryCreated(historyIds); } private MessageHistory resolveMessageHistory(String batchNo, diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java b/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java index c4e14c2..41389df 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/MessageBodyJsonObject.java @@ -3,6 +3,7 @@ package cn.axzo.im.updatable; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; /** * @author yanglin @@ -15,7 +16,10 @@ public class MessageBodyJsonObject { public MessageBodyJsonObject(String json) { messageBody = JSON.parseObject(json); - bizBody = JSON.parseObject(messageBody.getString("msgBody")); + String bizBodyJson = messageBody.getString("msgBody"); + if (StringUtils.isBlank(bizBodyJson)) + bizBodyJson = messageBody.getString("payload"); + bizBody = JSON.parseObject(bizBodyJson); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index dc13d1f..bcedc68 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -1,16 +1,12 @@ package cn.axzo.im.updatable; -import cn.axzo.framework.domain.ServiceException; -import cn.axzo.im.center.api.feign.SendPriority; -import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; +import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; -import cn.axzo.im.center.api.vo.resp.MessageUpdateResp; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; +import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; -import cn.axzo.im.center.common.enums.BizTypeEnum; -import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageBody; -import cn.axzo.im.channel.netease.dto.MessageCustomBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.dao.repository.UpdatableMessageLogDao; @@ -22,8 +18,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; -import cn.axzo.im.updatable.handler.SendStateHandler; -import cn.axzo.im.updatable.handler.UpdateStateHandler; +import cn.axzo.im.updatable.handler.InitMessageHandler; +import cn.axzo.im.updatable.handler.UpdateMessageHandler; import cn.axzo.im.updatable.retry.MessageUpdateRetryService; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.PropsUtils; @@ -38,16 +34,14 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; -import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; +import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; /** * @author yanglin @@ -62,12 +56,12 @@ public class UpdatableMessageManager { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; private final MessageHistoryDao messageHistoryDao; - private final SendStateHandler sendStateHandler; - private final UpdateStateHandler updateStateHandler; - private final IMChannelProvider imChannel; + private final InitMessageHandler initMessageHandler; + private final UpdateMessageHandler updateMessageHandler; private final MessageUpdateRetryService messageUpdateRetryService; + private final UpdateHistorySupport updateHistorySupport; - // !! schedule + // !! schedule in task public List createUpdatableMessage( MessageTask task, SendTemplateMessageParam request, List receivePersons) { @@ -87,13 +81,13 @@ public class UpdatableMessageManager { message.setReceiverOuId(person.getOuId()); message.setAppType(person.getAppType()); message.setMsgType(request.getTemplatedMsgType()); - message.setState(UpdatableMessageState.CREATED); + message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); message.setDataVersion(1L); UpdatableMessageSendResult sendResult = new UpdatableMessageSendResult(); sendResults.add(sendResult); - sendResult.setBizMessageId(message.getBizMessageId()); + sendResult.setBizMessageId(message.bizMessageId()); sendResult.setAccount(message.parsePersonAccount()); } for (List batch : Lists.partition(messages, BATCH_UPDATE_SIZE)) @@ -101,7 +95,7 @@ public class UpdatableMessageManager { ArrayList logs = new ArrayList<>(); for (UpdatableMessage message : messages) { - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(); logs.add(messageLog); messageLog.setContext("scheduleInTask"); } @@ -110,10 +104,11 @@ public class UpdatableMessageManager { return sendResults; } - // !! enqueue + // !! init history created - public void onHistoryCreated(List histories) { - if (CollectionUtils.isEmpty(histories)) return; + public void onHistoryCreated(List historyIds) { + if (CollectionUtils.isEmpty(historyIds)) return; + List histories = messageHistoryDao.getByIds(historyIds); List taskIds = histories.stream() .map(MessageHistory::getImMessageTaskId) .distinct() @@ -132,25 +127,25 @@ public class UpdatableMessageManager { UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); messageUpdate.setId(message.getId()); - messageUpdate.setInitHistoryId(history.getImMessageTaskId()); + messageUpdate.setInitHistoryId(history.getId()); messageUpdate.setMessageBody(object.getMessageBody()); messageUpdate.setBizBody(object.getBizBody()); messageUpdate.setFromAccount(history.getFromAccount()); messageUpdate.setToAccount(history.getToAccount()); - messageUpdate.setState(history.getStatus() == MessageHistoryStatus.FAILED - ? UpdatableMessageState.MESSAGE_QUEUED + messageUpdate.setState(history.getStatus() == MessageHistoryStatus.PENDING + ? UpdatableMessageState.INIT_MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(); messageLogUpdates.add(messageLog); messageLog.setBizId(history.getBizId()); messageLog.setFromAccount(history.getFromAccount()); messageLog.setToAccount(history.getToAccount()); messageLog.setMessageState(messageUpdate.getState()); - messageLog.setInitHistoryId(history.getImMessageTaskId()); + messageLog.setInitHistoryId(history.getId()); messageLog.setMessageBody(object.getMessageBody()); messageLog.setBizBody(object.getBizBody()); - messageLog.setContext("createHistoryFromTask"); + messageLog.setContext("initHistoryCreated"); messageLog.setContextHistoryId(history.getId()); MessageHistory historyUpdate = new MessageHistory(); @@ -158,15 +153,17 @@ public class UpdatableMessageManager { historyUpdate.setId(history.getId()); MessageBody messageBody = JSON.parseObject(history.getMessageBody(), MessageBody.class); - messageBody.setBizMessageId(message.getBizMessageId()); + messageBody.setBizMessageId(message.bizMessageId()); messageBody.setDataVersion(message.getDataVersion()); historyUpdate.setMessageBody(JSON.toJSONString(messageBody)); HistoryRecordExt updateExt = new HistoryRecordExt(); - updateExt.setUpdatableMessage(true); - updateExt.setBizMessageId(message.getBizMessageId()); + updateExt.setIsUpdatableMessage(true); + updateExt.setBizMessageId(message.bizMessageId()); updateExt.setDataVersion(message.getDataVersion()); - updateExt.setUpdateMessage(false); + updateExt.setIsUpdateMessage(false); + updateExt.setIsUpdateRetry(false); + updateExt.setUpdateRetryCount(message.getRetryCount()); historyUpdate.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); } if (CollectionUtils.isNotEmpty(messageUpdates)) { @@ -186,96 +183,45 @@ public class UpdatableMessageManager { // !! update message @Transactional - public MessageUpdateResp updateMessage(UpdateMessageRequest request) { + public MessageUpdateResponse updateMessage(UpdateMessageRequest request) { BizAssertions.assertNotEmpty(request.getUpdates(), "更新消息不能为空"); - Map bizMessageId2Message = updatableMessageDao - .getByBizMessageIdsForUpdate( - request.getUpdates().stream() - .map(UpdateMessageRequest.Update::getBizMessageId) - .distinct() - .collect(toList())).stream() - .collect(Collectors.toMap(UpdatableMessage::getBizMessageId, Function.identity())); - if (bizMessageId2Message.isEmpty()) - throw new ServiceException("未找到需要更新的消息"); - String batchNo = UUIDUtil.uuidString(); - List histories = new ArrayList<>(); - List messageUpdates = new ArrayList<>(); - List messageLogs = new ArrayList<>(); - Map message2History = new IdentityHashMap<>(); - Map messageLog2History = new IdentityHashMap<>(); + List requestMessages = updatableMessageDao + .getByBizMessageIdsForUpdate(request.getBizMessageIds()); + BizAssertions.assertNotEmpty(requestMessages, "未找到任何需要更新的消息"); + Map bizMessageId2Message = requestMessages.stream() + .collect(toMap(UpdatableMessage::bizMessageId, identity())); + + MessageUpdateResponse response = new MessageUpdateResponse(); + List updates = new ArrayList<>(); + List updateIds = new ArrayList<>(); for (UpdateMessageRequest.Update update : request.getUpdates()) { UpdatableMessage message = bizMessageId2Message.get(update.getBizMessageId()); - if (message == null) continue; - - UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); - messageUpdate.setId(message.getId()); - messageUpdate.setDataVersion(messageUpdate.getDataVersion() + 1); - messageUpdate.setState(UpdatableMessageState.UPDATE_QUEUED); - - MessageHistory history = new MessageHistory(); - histories.add(history); - message2History.put(message, history); - history.setBizId(message.getBizId()); - history.setFromAccount(message.getFromAccount()); - history.setToAccount(message.getToAccount()); - history.setAppType(message.getAppType().getCode()); - history.setChannel(imChannel.getProviderType()); - MessageCustomBody messageBody = new MessageCustomBody(); - messageBody.setToImAccount(message.getToAccount()); - messageBody.setPersonId(message.getReceiverPersonId()); - messageBody.setBizType(BizTypeEnum.MESSAGE_UPDATE); - messageBody.setPayload(update.getMsgTemplateContent()); - messageBody.setBizMessageId(message.getBizMessageId()); - messageBody.setDataVersion(messageUpdate.getDataVersion()); - messageBody.setInitMessageId(message.getInitHistoryId()); - messageBody.setMsgType(message.getMsgType()); - history.setMessageBody(JSON.toJSONString(messageBody)); - messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); - messageUpdate.setBizBody(JSON.parseObject(update.getMsgTemplateContent())); - // 重置 - messageUpdate.setRetryCount(0L); - - history.setImMessageTaskId(0L); - history.setReceivePersonId(message.getReceiverPersonId()); - history.setReceiveOuId(message.getReceiverOuId()); - history.setStatus(MessageHistoryStatus.PENDING); - history.setBatchNo(batchNo); - history.setSendPriority(SendPriority.UPDATE_MESSAGE.getPriority()); - history.setApiChannel(ApiChannel.CUSTOM_MESSAGE); - HistoryRecordExt recordExt = new HistoryRecordExt(); - recordExt.setUpdatableMessage(true); - recordExt.setBizMessageId(message.getBizMessageId()); - recordExt.setDataVersion(message.getDataVersion()); - recordExt.setUpdateMessage(true); - history.setRecordExt(recordExt); - history.setTimestampForSend(new Date()); - - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); - messageLogs.add(messageLog); - messageLog2History.put(messageLog, history); - messageLog.setMessageState(messageUpdate.getState()); - messageLog.setMessageBody(messageUpdate.getMessageBody()); - messageLog.setBizBody(messageUpdate.getBizBody()); - messageLog.setDataVersion(messageUpdate.getDataVersion()); - messageLog.setRetryCount(messageUpdate.getRetryCount()); + if (message == null) { + response.addNonUpdatedMessage( + update.getBizMessageId(), + NonUpdateMessageReason.CANT_FIND_INIT_MESSAGE); + } else if (!message.getState().isUpdateMessageAllowed()) { + response.addNonUpdatedMessage( + update.getBizMessageId(), + NonUpdateMessageReason.MESSAGE_STATE_NOT_ALLOWED, + message.getState()); + } else { + updates.add(update); + updateIds.add(message.getId()); + response.addUpdatedBizMessageId(message.getBizMessageId()); + } } - messageHistoryDao.saveBatch(histories); - for (UpdatableMessage messageUpdate : messageUpdates) { - MessageHistory history = message2History.get(messageUpdate); - messageUpdate.setUpdateHistoryId(history.getId()); + if (!updates.isEmpty()) { + // incr data version + reset retry count + updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); + messageUpdateRetryService.scheduleNextRetry(updateIds); + updateHistorySupport.addUpdateHistories("updateHistoryCreated", updates); } - for (UpdatableMessageLog messageLog : messageLogs) { - MessageHistory history = messageLog2History.get(messageLog); - messageLog.setContext("updateMessage"); - messageLog.setContextHistoryId(history.getId()); - } - updatableMessageLogDao.saveBatch(messageLogs); - updatableMessageDao.updateBatchById(messageUpdates); - messageUpdateRetryService.scheduleRetry(bizMessageId2Message.values()); - return new MessageUpdateResp(); + return response; } + // !! init & update message sent + @Transactional public void onHistorySend(List maybeUpdatedHistory) { List historyIds = maybeUpdatedHistory.stream() @@ -283,7 +229,7 @@ public class UpdatableMessageManager { .map(MessageHistory::getId) .collect(toList()); if (CollectionUtils.isEmpty(historyIds)) return; - List histories = messageHistoryDao.listByIds(historyIds); + List histories = messageHistoryDao.getByIds(historyIds); // 避免ack更新出错 List messages = updatableMessageDao .getByBizMessageIdsForUpdate(histories.stream() @@ -301,7 +247,11 @@ public class UpdatableMessageManager { .filter(history -> isSuccess ? history.getStatus() == MessageHistoryStatus.SUCCEED : history.getStatus() == MessageHistoryStatus.FAILED) - .filter(history -> isUpdateMessage && history.getRecordExt().isUpdateMessage()) + .filter(history -> { + boolean isHistoryUpdateMessage = history.getRecordExt().getIsUpdateMessage(); + return isUpdateMessage && isHistoryUpdateMessage + || !isUpdateMessage && !isHistoryUpdateMessage; + }) .collect(toList()); if (stateHistories.isEmpty()) continue; @@ -312,21 +262,72 @@ public class UpdatableMessageManager { return historyAndMessages; }; // send success - List sendSuccess = historyAndMessageBuilder.apply(true, true); + List sendSuccess = historyAndMessageBuilder.apply(false, true); if (!sendSuccess.isEmpty()) - sendStateHandler.onSuccess(sendSuccess); + initMessageHandler.onSuccess(sendSuccess); // send fail - List sendFail = historyAndMessageBuilder.apply(true, false); + List sendFail = historyAndMessageBuilder.apply(false, false); if (!sendFail.isEmpty()) - sendStateHandler.onFail(sendFail); + initMessageHandler.onFail(sendFail); // update success - List updateSuccess = historyAndMessageBuilder.apply(false, true); + List updateSuccess = historyAndMessageBuilder.apply(true, true); if (!updateSuccess.isEmpty()) - updateStateHandler.onSuccess(updateSuccess); + updateMessageHandler.onSuccess(updateSuccess); // update fail - List updateFail = historyAndMessageBuilder.apply(false, false); + List updateFail = historyAndMessageBuilder.apply(true, false); if (!updateFail.isEmpty()) - updateStateHandler.onFail(updateFail); + updateMessageHandler.onFail(updateFail); + } + + @Transactional + public void ack(UpdatableMessageAckRequest request) { + List acks = request.determineValidAcks(); + if (CollectionUtils.isEmpty(acks)) return; + List bizMessageIds = acks.stream() + .map(UpdatableMessageAckRequest.Ack::getBizMessageId) + .collect(toList()); + Map bizMessageId2Ack = acks.stream() + .collect(toMap(UpdatableMessageAckRequest.Ack::getBizMessageId, identity())); + List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); + List messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); + for (UpdatableMessage message : messages) { + Long dataVersion = bizMessageId2Ack.get(message.bizMessageId()).getDataVersion(); + + UpdatableMessageLog messageLog = message.toMessageLog(); + messageLogs.add(messageLog); + messageLog.setContext("ack"); + messageLog.setContextHistoryId(0L); + messageLog.setRetryCount(0L); + messageLog.setMessageBody(null); + messageLog.setBizBody(null); + messageLog.setDataVersion(dataVersion); + UpdatableMessageLog.RecordExt logExt = new UpdatableMessageLog.RecordExt(); + messageLog.setRecordExt(logExt); + + if (!dataVersion.equals(message.getDataVersion())) { + logExt.setAckIgnored(true); + logExt.setAckDescription("数据版本不匹配"); + logExt.setRequestDataVersion(dataVersion); + logExt.setMessageDataVersion(message.getDataVersion()); + continue; + } + // 避免前端有bug + if (!message.getState().isUpdateAckAllowed()) { + logExt.setAckIgnored(true); + logExt.setAckDescription("消息状态不允许ack"); + continue; + } + logExt.setAckIgnored(false); + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setId(message.getId()); + messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); + } + if (CollectionUtils.isNotEmpty(messageUpdates)) + updatableMessageDao.updateBatchById(messageUpdates); + if (CollectionUtils.isNotEmpty(messageLogs)) + updatableMessageLogDao.saveBatch(messageLogs); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java new file mode 100644 index 0000000..efb9775 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java @@ -0,0 +1,126 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.center.api.feign.SendPriority; +import cn.axzo.im.center.api.vo.ApiChannel; +import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; +import cn.axzo.im.center.common.enums.BizTypeEnum; +import cn.axzo.im.channel.IMChannelProvider; +import cn.axzo.im.channel.netease.dto.MessageCustomBody; +import cn.axzo.im.dao.repository.MessageHistoryDao; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.HistoryRecordExt; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.enums.MessageHistoryStatus; +import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.UUIDUtil; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import static cn.axzo.im.center.api.vo.req.MessageUpdateInfo.collectBizMessageIds; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +public class UpdateHistorySupport { + + private final UpdatableMessageDao updatableMessageDao; + private final IMChannelProvider imChannel; + private final MessageHistoryDao messageHistoryDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + + public void addUpdateHistories(String context, Collection updates) { + Map bizMessageId2Message = updatableMessageDao + .getByBizMessageIds(collectBizMessageIds(updates)) + .stream().collect(toMap(UpdatableMessage::getBizMessageId, identity())); + String batchNo = UUIDUtil.uuidString(); + List histories = new ArrayList<>(); + List messageUpdates = new ArrayList<>(); + List messageLogs = new ArrayList<>(); + Map message2History = new IdentityHashMap<>(); + Map messageLog2History = new IdentityHashMap<>(); + for (MessageUpdateInfo update : updates) { + UpdatableMessage message = bizMessageId2Message.get(update.bizMessageId()); + if (message == null) continue; + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdates.add(messageUpdate); + messageUpdate.setId(message.getId()); + messageUpdate.setState(UpdatableMessageState.UPDATE_MESSAGE_QUEUED); + + MessageHistory history = new MessageHistory(); + histories.add(history); + message2History.put(messageUpdate, history); + history.setBizId(message.getBizId()); + history.setFromAccount(message.getFromAccount()); + history.setToAccount(message.getToAccount()); + history.setAppType(message.getAppType().getCode()); + history.setChannel(imChannel.getProviderType()); + MessageCustomBody messageBody = new MessageCustomBody(); + messageBody.setToImAccount(message.getToAccount()); + messageBody.setPersonId(message.getReceiverPersonId()); + messageBody.setBizType(BizTypeEnum.MESSAGE_UPDATE); + messageBody.setPayload(update.bizBody().toJSONString()); + messageBody.setBizMessageId(message.bizMessageId()); + messageBody.setDataVersion(messageUpdate.getDataVersion()); + messageBody.setInitMessageId(message.getInitHistoryId()); + messageBody.setMsgType(message.getMsgType()); + history.setMessageBody(JSON.toJSONString(messageBody)); + messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); + messageUpdate.setBizBody(update.bizBody()); + + history.setImMessageTaskId(0L); + history.setReceivePersonId(message.getReceiverPersonId()); + history.setReceiveOuId(message.getReceiverOuId()); + history.setStatus(MessageHistoryStatus.PENDING); + history.setBatchNo(batchNo); + history.setSendPriority(SendPriority.UPDATE_MESSAGE.getPriority()); + history.setApiChannel(ApiChannel.CUSTOM_MESSAGE); + HistoryRecordExt recordExt = new HistoryRecordExt(); + recordExt.setIsUpdatableMessage(true); + recordExt.setBizMessageId(message.bizMessageId()); + recordExt.setDataVersion(message.getDataVersion()); + recordExt.setIsUpdateMessage(true); + Long retryCount = message.getRetryCount(); + recordExt.setIsUpdateRetry(retryCount > 0); + recordExt.setUpdateRetryCount(retryCount); + history.setRecordExt(recordExt); + history.setTimestampForSend(new Date()); + + UpdatableMessageLog messageLog = message.toMessageLog(); + messageLogs.add(messageLog); + messageLog2History.put(messageLog, history); + messageLog.setDataVersion(message.getDataVersion()); + messageLog.setMessageState(messageUpdate.getState()); + messageLog.setMessageBody(messageUpdate.getMessageBody()); + messageLog.setBizBody(messageUpdate.getBizBody()); + messageLog.setRetryCount(message.getRetryCount()); + } + messageHistoryDao.saveBatch(histories); + for (UpdatableMessage messageUpdate : messageUpdates) { + MessageHistory history = message2History.get(messageUpdate); + messageUpdate.setUpdateHistoryId(history.getId()); + } + for (UpdatableMessageLog messageLog : messageLogs) { + MessageHistory history = messageLog2History.get(messageLog); + messageLog.setContext(context); + messageLog.setContextHistoryId(history.getId()); + } + updatableMessageLogDao.saveBatch(messageLogs); + updatableMessageDao.updateBatchById(messageUpdates); + } + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java similarity index 84% rename from im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index 72ac4f0..c6aa97f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/SendStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -18,7 +18,7 @@ import java.util.List; */ @Component @RequiredArgsConstructor -public class SendStateHandler implements StateHandler { +public class InitMessageHandler implements StateHandler { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; @@ -37,18 +37,19 @@ public class SendStateHandler implements StateHandler { List messageUpdates = new ArrayList<>(); List messageLogs = new ArrayList<>(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { + UpdatableMessage message = historyAndMessage.getMessage(); UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); + messageUpdate.setId(message.getId()); messageUpdate.setState(isSuccess - ? UpdatableMessageState.MESSAGE_SEND_SUCCESS - : UpdatableMessageState.MESSAGE_SEND_FAIL); - UpdatableMessage message = historyAndMessage.getMessage(); - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + ? UpdatableMessageState.INIT_MESSAGE_SEND_SUCCESS + : UpdatableMessageState.INIT_MESSAGE_SEND_FAIL); + UpdatableMessageLog messageLog = message.toMessageLog(); messageLogs.add(messageLog); messageLog.setMessageState(messageUpdate.getState()); MessageHistory history = historyAndMessage.getHistories().get(0); messageLog.setInitHistoryId(history.getId()); - messageLog.setContext("sendMessage"); + messageLog.setContext("sendInitMessage"); messageLog.setContextHistoryId(history.getId()); } updatableMessageDao.updateBatchById(messageUpdates); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java similarity index 78% rename from im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java index 84d6075..87ad10b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateStateHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java @@ -19,7 +19,7 @@ import java.util.List; */ @Component @RequiredArgsConstructor -public class UpdateStateHandler implements StateHandler { +public class UpdateMessageHandler implements StateHandler { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; @@ -39,30 +39,31 @@ public class UpdateStateHandler implements StateHandler { List messageLogs = new ArrayList<>(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessageState state = historyAndMessage.getMessage().getState(); - boolean queuedOrFail = state == UpdatableMessageState.UPDATE_QUEUED - || state == UpdatableMessageState.UPDATE_SEND_FAIL; + UpdatableMessage message = historyAndMessage.getMessage(); + boolean queuedOrFail = state == UpdatableMessageState.UPDATE_MESSAGE_QUEUED + || state == UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL; if (queuedOrFail && historyAndMessage.hasDataVersionMatchHistories()) { UpdatableMessage messageUpdate = new UpdatableMessage(); messageUpdates.add(messageUpdate); + messageUpdate.setId(historyAndMessage.getMessage().getId()); messageUpdate.setState(isSuccess - ? UpdatableMessageState.UPDATE_SEND_SUCCESS - : UpdatableMessageState.UPDATE_SEND_FAIL); + ? UpdatableMessageState.UPDATE_MESSAGE_SEND_SUCCESS + : UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL); } - UpdatableMessage message = historyAndMessage.getMessage(); for (MessageHistory history : historyAndMessage.getHistories()) { - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(); messageLogs.add(messageLog); messageLog.setMessageState(isSuccess - ? UpdatableMessageState.UPDATE_SEND_SUCCESS - : UpdatableMessageState.UPDATE_SEND_FAIL); + ? UpdatableMessageState.UPDATE_MESSAGE_SEND_SUCCESS + : UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL); messageLog.setContextHistoryId(history.getId()); MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); messageLog.setMessageBody(object.getMessageBody()); messageLog.setBizBody(object.getBizBody()); messageLog.setDataVersion(history.getDataVersion()); messageLog.setContextHistoryId(history.getId()); - messageLog.setContext(history.isAckRetry() ? "retrySendUpdateMessage" : "sendUpdateMessage"); + messageLog.setContext(history.isUpdateRetry() ? "retrySendUpdateMessage" : "sendUpdateMessage"); messageLog.setRetryCount(history.getUpdateRetryCount()); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java index 380b503..68108bc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryJob.java @@ -8,7 +8,6 @@ import com.xxl.job.core.handler.annotation.XxlJob; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import org.springframework.transaction.support.TransactionTemplate; import java.util.Date; import java.util.List; @@ -27,7 +26,6 @@ public class MessageUpdateRetryJob { private final MessageUpdateRetryDao messageUpdateRetryDao; private final MessageUpdateRetryService messageUpdateRetryService; - private final TransactionTemplate transactionTemplate; @XxlJob("messageUpdateRetryJob") public ReturnT execute(String paramStr) throws Exception { @@ -48,12 +46,7 @@ public class MessageUpdateRetryJob { List bizMessageIds = retries.stream() .map(MessageUpdateRetry::getBizMessageId) .collect(toList()); - transactionTemplate.executeWithoutResult(unused -> { - messageUpdateRetryService.removePrevRetryByBizMessageId(bizMessageIds); - UpdateRetryState state = messageUpdateRetryService.getUpdateRetryState(bizMessageIds); - messageUpdateRetryService.retryMessageUpdate(state.getNonAckMessages()); - messageUpdateRetryService.scheduleRetry(state.getScheduleNextRetryMessages()); - }); + messageUpdateRetryService.advanceRetry(bizMessageIds); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 96df45d..c5c008c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -6,6 +6,7 @@ import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.MessageUpdateRetry; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.updatable.UpdateHistorySupport; import cn.axzo.im.utils.ImProperties; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; @@ -31,58 +32,56 @@ public class MessageUpdateRetryService { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; private final ImProperties props; + private final UpdateHistorySupport updateHistorySupport; - public void removePrevRetryByMessage(Collection messages) { - List bizMessageIds = messages.stream() - .map(UpdatableMessage::getBizMessageId) - .collect(toList()); - removePrevRetryByBizMessageId(bizMessageIds); - } - - public void removePrevRetryByBizMessageId(Collection bizMessageIds) { + public void removePrevRetryByBizMessageIds(Collection bizMessageIds) { if (CollectionUtils.isNotEmpty(bizMessageIds)) messageUpdateRetryDao.getBaseMapper().deleteByBizMessageIds(bizMessageIds); } - public UpdateRetryState getUpdateRetryState(Collection bizMessageIds) { + @Transactional + public void advanceRetry(List bizMessageIds) { + removePrevRetryByBizMessageIds(bizMessageIds); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); - return new UpdateRetryState(messages, props.getMessageUpdateAckMaxRetryCount()); + UpdateRetryState state = new UpdateRetryState(messages, props.getMessageUpdateAckMaxRetryCount()); + // 先发本次生试, 再调度下一次重试 + retryUpdateMessage(state.getNonAckMessageIds()); + scheduleNextRetry(state.getScheduleNextRetryMessages()); } - @Transactional - public void scheduleRetry(Collection messages) { - if (CollectionUtils.isEmpty(messages)) return; - removePrevRetryByMessage(messages); + public void scheduleNextRetry(List messageIds) { + if (CollectionUtils.isEmpty(messageIds)) return; + List messages = updatableMessageDao.listByIds(messageIds); + List bizMessageIds = messages.stream() + .map(UpdatableMessage::getBizMessageId) + .collect(toList()); + removePrevRetryByBizMessageIds(bizMessageIds); ArrayList retries = new ArrayList<>(); - ArrayList messageUpdates = new ArrayList<>(); List messageLogs = new ArrayList<>(); Date nextRetryTime = DateTime.now() .plusSeconds(props.getMessageUpdateAckRetryIntervalSeconds()) .toDate(); for (UpdatableMessage message : messages) { MessageUpdateRetry retry = new MessageUpdateRetry(); - retry.setBizMessageId(message.getBizMessageId()); + retry.setBizMessageId(message.bizMessageId()); retry.setInitHistoryId(message.getInitHistoryId()); retry.setNextRetryTime(nextRetryTime); retry.setDataVersion(message.getDataVersion()); retries.add(retry); - UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); - messageUpdate.setId(retry.getId()); - messageUpdate.setRetryCount(message.getRetryCount() + 1); - - UpdatableMessageLog messageLog = message.toUpdatableMessageLog(); - messageLog.setRetryCount(messageUpdate.getRetryCount()); - messageLog.setContext("scheduleRetrySendUpdateMessage"); + UpdatableMessageLog messageLog = message.toMessageLog(); + messageLog.setRetryCount(message.getRetryCount() + 1); + messageLog.setContext("scheduleNextRetrySendUpdateMessage"); } messageUpdateRetryDao.saveBatch(retries); updatableMessageLogDao.saveBatch(messageLogs); - updatableMessageDao.updateBatchById(messageUpdates); } - public void retryMessageUpdate(Collection messages) { - if (CollectionUtils.isEmpty(messages)) return; + public void retryUpdateMessage(List messageIds) { + if (CollectionUtils.isEmpty(messageIds)) return; + updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); + List messages = updatableMessageDao.listByIds(messageIds); + updateHistorySupport.addUpdateHistories("retryUpdateHistoryCreated", messages); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java index 7287a2c..ba1194d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java @@ -13,19 +13,22 @@ import static java.util.stream.Collectors.toList; */ @RequiredArgsConstructor public class UpdateRetryState { + private final List messages; private final int maxRetryCount; - List getScheduleNextRetryMessages() { + List getScheduleNextRetryMessages() { return messages.stream() .filter(message -> message.getRetryCount() < maxRetryCount) .filter(message -> message.getState() != UpdatableMessageState.UPDATE_ACK) + .map(UpdatableMessage::getId) .collect(toList()); } - List getNonAckMessages() { + List getNonAckMessageIds() { return messages.stream() .filter(message -> message.getState() != UpdatableMessageState.UPDATE_ACK) + .map(UpdatableMessage::getId) .collect(toList()); } From 53f62cc239c57cdc1ae23e9fb650960b9e0062f5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 7 Nov 2024 18:48:55 +0800 Subject: [PATCH 09/93] REQ-3057-card: a lot of staff --- .../axzo/im/center/api/feign/MessageApi.java | 10 ++++-- .../vo/req/FetchUpdatableMessageRequest.java | 19 ++++++++++++ .../vo/req/UpdatableMessageAckRequest.java | 15 +++++---- .../resp/FetchUpdatableMessageResponse.java | 31 +++++++++++++++++++ .../axzo/im/controller/MessageController.java | 11 +++++++ .../im/updatable/UpdatableMessageManager.java | 8 ++--- .../im/updatable/UpdateHistorySupport.java | 25 ++++++++++++++- .../java/cn/axzo/im/utils/ImProperties.java | 2 ++ 8 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index a337426..3f98cc5 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -3,11 +3,13 @@ package cn.axzo.im.center.api.feign; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; +import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; +import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -60,12 +62,16 @@ public interface MessageApi { /** * 更新消息 */ - @PostMapping("/api/im/template-message/updateMessage") + @PostMapping("/api/im/template-message/updatable/updateMessage") ApiResult updateMessage(@RequestBody @Validated UpdateMessageRequest request); - @PostMapping("/api/im/template-message/ack") + @PostMapping("/api/im/template-message/updatable/ack") ApiResult ack(@RequestBody @Validated UpdatableMessageAckRequest request); + @PostMapping("/api/im/template-message/updatable/fetchUpdatableMessage") + ApiResult fetchUpdatableMessage( + @RequestBody @Validated FetchUpdatableMessageRequest request); + /** * * 接口已经作废,可以使用sendTemplateMessage来替换 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java new file mode 100644 index 0000000..04e6afe --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -0,0 +1,19 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +/** + * @author yanglin + */ +@Setter +@Getter +public class FetchUpdatableMessageRequest { + + @NotEmpty(message = "业务消息ID不能为空") + private Set bizMessageIds; + +} diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index dd645ee..6562b9a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -20,21 +20,24 @@ import static java.util.stream.Collectors.toList; @Getter public class UpdatableMessageAckRequest { + /** + * ack的消息信息列表 + */ @NotEmpty(message = "消息ACK不能为空") - private List acks; + private List acknowledgments; - public List determineValidAcks() { - if (CollectionUtils.isEmpty(acks)) + public List determineValidAcknowledgments() { + if (CollectionUtils.isEmpty(acknowledgments)) return Collections.emptyList(); HashMap id2Max = new HashMap<>(); - for (Ack ack : acks) { + for (Acknowledgment ack : acknowledgments) { Long max = id2Max.getOrDefault(ack.bizMessageId, 0L); max = Math.max(max, ack.dataVersion); id2Max.put(ack.bizMessageId, max); } return id2Max.entrySet().stream() .map(e -> { - Ack ack = new Ack(); + Acknowledgment ack = new Acknowledgment(); ack.setBizMessageId(e.getKey()); ack.setDataVersion(e.getValue()); return ack; @@ -44,7 +47,7 @@ public class UpdatableMessageAckRequest { @Setter @Getter - public static class Ack { + public static class Acknowledgment { @NotBlank(message = "业务消息ID不能为空") private String bizMessageId; @NotNull(message = "数据版本不能为空") diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java new file mode 100644 index 0000000..079d16e --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -0,0 +1,31 @@ +package cn.axzo.im.center.api.vo.resp; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yanglin + */ +@Setter +@Getter +public class FetchUpdatableMessageResponse { + + private List messages = new ArrayList<>(); + + public void addMessage(MessageInfo message) { + messages.add(message); + } + + @Setter + @Getter + public static class MessageInfo { + private String msgType; + private String msgBody; + private String bizMessageId; + private Long dataVersion; + } + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 45dcf38..8a3bded 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -8,11 +8,13 @@ import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.req.AccountQuery; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; +import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; +import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -30,6 +32,7 @@ import cn.axzo.im.service.MessageHistoryService; import cn.axzo.im.service.MessageTaskService; import cn.axzo.im.service.RobotMsgTemplateService; import cn.axzo.im.updatable.UpdatableMessageManager; +import cn.axzo.im.updatable.UpdateHistorySupport; import cn.axzo.pokonyan.exception.Aassert; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -84,6 +87,8 @@ public class MessageController implements MessageApi { private CustomMessageService customMessageService; @Autowired private UpdatableMessageManager updatableMessageManager; + @Autowired + private UpdateHistorySupport updateHistorySupport; @Override @@ -206,6 +211,12 @@ public class MessageController implements MessageApi { return ApiResult.ok(); } + @Override + public ApiResult fetchUpdatableMessage(FetchUpdatableMessageRequest request) { + FetchUpdatableMessageResponse resp = updateHistorySupport.fetchUpdatableMessage(request); + return ApiResult.ok(resp); + } + private void check(SendMessageParam sendMessageParam) { List accountRegisters = accountRegisterService.list(AccountRegisterService.ListAccountRegisterParam.builder() .imAccount(sendMessageParam.getSendImAccount()) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index bcedc68..c8b371c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -281,13 +281,13 @@ public class UpdatableMessageManager { @Transactional public void ack(UpdatableMessageAckRequest request) { - List acks = request.determineValidAcks(); + List acks = request.determineValidAcknowledgments(); if (CollectionUtils.isEmpty(acks)) return; List bizMessageIds = acks.stream() - .map(UpdatableMessageAckRequest.Ack::getBizMessageId) + .map(UpdatableMessageAckRequest.Acknowledgment::getBizMessageId) .collect(toList()); - Map bizMessageId2Ack = acks.stream() - .collect(toMap(UpdatableMessageAckRequest.Ack::getBizMessageId, identity())); + Map bizMessageId2Ack = acks.stream() + .collect(toMap(UpdatableMessageAckRequest.Acknowledgment::getBizMessageId, identity())); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); List messageUpdates = new ArrayList<>(); List messageLogs = new ArrayList<>(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java index efb9775..3fafe16 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java @@ -2,7 +2,10 @@ package cn.axzo.im.updatable; import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; +import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; +import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; +import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse.MessageInfo; import cn.axzo.im.center.common.enums.BizTypeEnum; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageCustomBody; @@ -15,6 +18,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.utils.BizAssertions; +import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; @@ -42,6 +47,7 @@ public class UpdateHistorySupport { private final IMChannelProvider imChannel; private final MessageHistoryDao messageHistoryDao; private final UpdatableMessageLogDao updatableMessageLogDao; + private final ImProperties props; public void addUpdateHistories(String context, Collection updates) { Map bizMessageId2Message = updatableMessageDao @@ -123,4 +129,21 @@ public class UpdateHistorySupport { updatableMessageDao.updateBatchById(messageUpdates); } -} + public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { + int maxSize = props.getFetchUpdatableMessageMaxSize(); + BizAssertions.assertTrue(request.getBizMessageIds().size() < maxSize, + "超过了最大的消息数量, 最大: {}", maxSize); + List messages = updatableMessageDao.getByBizMessageIds(request.getBizMessageIds()); + FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); + for (UpdatableMessage message : messages) { + MessageInfo imMessage = new MessageInfo(); + imMessage.setMsgType(message.getMsgType().getCode()); + imMessage.setMsgBody(message.getBizBody().toJSONString()); + imMessage.setBizMessageId(message.getBizMessageId()); + imMessage.setDataVersion(message.getDataVersion()); + response.addMessage(imMessage); + } + return response; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index 306877e..c1de7e8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -30,6 +30,8 @@ public class ImProperties { private int messageUpdateAckRetryIntervalSeconds = 10; + private int fetchUpdatableMessageMaxSize = 500; + private SendMessageConfig sendMessage = new SendMessageConfig(); private Set genImAccountJobCodes = Sets.newHashSet( From baf8a4cddf11398c59b2fd96719cc44e15b02ad4 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 7 Nov 2024 18:55:11 +0800 Subject: [PATCH 10/93] REQ-3057-card: a lot of staff --- .../dao/mapper/UpdatableMessageLogMapper.java | 8 +++ .../retry/ExpungeUpdatableMessageLogJob.java | 58 +++++++++++++++++++ .../ExpungeUpdatableMessageLogJobTest.java | 24 ++++++++ 3 files changed, 90 insertions(+) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJob.java create mode 100644 im-center-server/src/test/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJobTest.java diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java index 9a988cb..d700e5c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/mapper/UpdatableMessageLogMapper.java @@ -2,9 +2,17 @@ package cn.axzo.im.dao.mapper; import cn.axzo.im.entity.UpdatableMessageLog; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; /** * @author yanglin */ public interface UpdatableMessageLogMapper extends BaseMapper { + + @Delete("DELETE FROM im_updatable_message_log WHERE create_at <= #{until}") + int expunge(@Param("until") Date until); + } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJob.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJob.java new file mode 100644 index 0000000..e10cab4 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJob.java @@ -0,0 +1,58 @@ +package cn.axzo.im.updatable.retry; + +import cn.axzo.im.dao.mapper.UpdatableMessageLogMapper; +import cn.axzo.im.entity.SendJobInfo; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.utils.DateFormatUtil; +import cn.axzo.im.utils.JSONObjectUtil; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.stereotype.Component; + +import java.util.Date; + +import static cn.axzo.im.utils.Queries.query; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ExpungeUpdatableMessageLogJob { + + private final UpdatableMessageLogMapper updatableMessageLogMapper; + + @XxlJob("expungeUpdatableMessageLogJob") + public ReturnT execute(String paramStr) throws Exception { + log.info("start - run job with param={}", paramStr); + try { + Param param = StringUtils.isBlank(paramStr) + ? new Param() : + JSONObjectUtil.parseObject(paramStr, Param.class); + expunge(param); + log.info("end - run job with param={}", param); + return ReturnT.SUCCESS; + } catch (Exception e) { + log.warn("job failed. param={}", paramStr, e); + return ReturnT.FAIL; + } + } + + private void expunge(Param param) { + Date until = DateTime.now().minusDays(param.daysAgo).toDate(); + log.info("going to delete until={}", DateFormatUtil.toReadableString(until)); + int count = updatableMessageLogMapper.expunge(until); + log.info("deleted count={}", count); + } + + @Data + public static class Param { + private int daysAgo = 7; + } +} diff --git a/im-center-server/src/test/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJobTest.java b/im-center-server/src/test/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJobTest.java new file mode 100644 index 0000000..851d622 --- /dev/null +++ b/im-center-server/src/test/java/cn/axzo/im/updatable/retry/ExpungeUpdatableMessageLogJobTest.java @@ -0,0 +1,24 @@ +package cn.axzo.im.updatable.retry; + +import cn.axzo.im.Application; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author yanglin + */ +@SpringBootTest(classes = Application.class) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class ExpungeUpdatableMessageLogJobTest { + + private final ExpungeUpdatableMessageLogJob expungeUpdatableMessageLogJob; + + @Test + void exec() { + } + +} \ No newline at end of file From 1bf5d85e19221963de9e7d867827cf0c2755014a Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 7 Nov 2024 18:58:20 +0800 Subject: [PATCH 11/93] REQ-3057-card: a lot of staff --- .../api/vo/req/FetchUpdatableMessageRequest.java | 3 +++ .../api/vo/req/UpdatableMessageAckRequest.java | 7 +++++++ .../vo/resp/FetchUpdatableMessageResponse.java | 15 +++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index 04e6afe..67d3d5c 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -13,6 +13,9 @@ import java.util.Set; @Getter public class FetchUpdatableMessageRequest { + /** + * 业务消息ID列表 + */ @NotEmpty(message = "业务消息ID不能为空") private Set bizMessageIds; diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index 6562b9a..1dd501b 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -48,8 +48,15 @@ public class UpdatableMessageAckRequest { @Setter @Getter public static class Acknowledgment { + /** + * 业务消息ID + */ @NotBlank(message = "业务消息ID不能为空") private String bizMessageId; + + /** + * 数据版本 + */ @NotNull(message = "数据版本不能为空") private Long dataVersion; } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index 079d16e..69af6af 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -13,6 +13,9 @@ import java.util.List; @Getter public class FetchUpdatableMessageResponse { + /** + * 消息列表 + */ private List messages = new ArrayList<>(); public void addMessage(MessageInfo message) { @@ -22,9 +25,21 @@ public class FetchUpdatableMessageResponse { @Setter @Getter public static class MessageInfo { + /** + * 消息类型. 和首次发次的保持一致. template: 模板消息, card: 卡片消息 + */ private String msgType; + /** + * 消息体 + */ private String msgBody; + /** + * 业务消息ID + */ private String bizMessageId; + /** + * 数据版本 + */ private Long dataVersion; } From 10f672746d6bdcd9d2b61be45be5200e613548c1 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 10:45:44 +0800 Subject: [PATCH 12/93] REQ-3057-card: a lot of staff --- .../vo/req/UpdatableMessageAckRequest.java | 2 +- .../dao/repository/UpdatableMessageDao.java | 9 +- .../im/updatable/UpdatableMessageManager.java | 112 +++++++++--------- .../im/updatable/UpdateHistorySupport.java | 27 ++--- .../collector/ManipulateCollector.java | 101 ++++++++++++++++ .../collector/ManipulateCollectorFactory.java | 27 +++++ .../updatable/handler/InitMessageHandler.java | 18 ++- .../handler/UpdateMessageHandler.java | 19 ++- .../retry/MessageUpdateRetryService.java | 17 ++- ...ateRetryState.java => UpdateAckState.java} | 2 +- .../java/cn/axzo/im/utils/ImProperties.java | 2 + 11 files changed, 233 insertions(+), 103 deletions(-) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java rename im-center-server/src/main/java/cn/axzo/im/updatable/retry/{UpdateRetryState.java => UpdateAckState.java} (96%) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index 1dd501b..2fa7b64 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -21,7 +21,7 @@ import static java.util.stream.Collectors.toList; public class UpdatableMessageAckRequest { /** - * ack的消息信息列表 + * ack的消息信息列表, 同一个bizMessageId传需要ack的最大dataVersion. 每次最多值100条 */ @NotEmpty(message = "消息ACK不能为空") private List acknowledgments; diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 8a63ec8..6f9ca87 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -10,6 +10,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import static java.util.stream.Collectors.toList; + /** * @author yanglin */ @@ -17,8 +19,13 @@ import java.util.List; public class UpdatableMessageDao extends ServiceImpl { public List getByBizMessageIdsForUpdate(Collection bizMessageIds) { + if (CollectionUtils.isEmpty(bizMessageIds)) + return Collections.emptyList(); + // 避免死锁 + List sortedBizMessageIds = bizMessageIds.stream() + .sorted().collect(toList()); return lambdaQuery() - .in(UpdatableMessage::getBizMessageId, bizMessageIds) + .in(UpdatableMessage::getBizMessageId, sortedBizMessageIds) // 避免ack更新出错 .last("FOR UPDATE") .list(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index c8b371c..0fa3779 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -2,6 +2,7 @@ package cn.axzo.im.updatable; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; +import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest.Acknowledgment; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; @@ -9,7 +10,6 @@ import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; -import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; @@ -18,10 +18,13 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.updatable.collector.ManipulateCollector; +import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import cn.axzo.im.updatable.handler.InitMessageHandler; import cn.axzo.im.updatable.handler.UpdateMessageHandler; import cn.axzo.im.updatable.retry.MessageUpdateRetryService; import cn.axzo.im.utils.BizAssertions; +import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.PropsUtils; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; @@ -31,6 +34,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Collections; @@ -39,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.function.BiFunction; +import static cn.axzo.im.center.api.vo.req.MessageUpdateInfo.collectBizMessageIds; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @@ -51,15 +56,15 @@ import static java.util.stream.Collectors.toMap; @RequiredArgsConstructor public class UpdatableMessageManager { - private static final int BATCH_UPDATE_SIZE = 1000; - private final UpdatableMessageDao updatableMessageDao; - private final UpdatableMessageLogDao updatableMessageLogDao; private final MessageHistoryDao messageHistoryDao; private final InitMessageHandler initMessageHandler; private final UpdateMessageHandler updateMessageHandler; private final MessageUpdateRetryService messageUpdateRetryService; private final UpdateHistorySupport updateHistorySupport; + private final ManipulateCollectorFactory manipulateCollectorFactory; + private final TransactionTemplate transactionTemplate; + private final ImProperties props; // !! schedule in task @@ -68,10 +73,10 @@ public class UpdatableMessageManager { if (CollectionUtils.isEmpty(receivePersons)) return Collections.emptyList(); String batchNo = UUIDUtil.uuidString(); ArrayList sendResults = new ArrayList<>(); - ArrayList messages = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (ReceivePerson person : receivePersons) { UpdatableMessage message = new UpdatableMessage(); - messages.add(message); + collector.addMessage(message); message.setBatchNo(batchNo); message.setTemplateId(request.getMsgTemplateId()); message.setRefTemplateId(request.getUpdatableRefTemplateId()); @@ -90,17 +95,12 @@ public class UpdatableMessageManager { sendResult.setBizMessageId(message.bizMessageId()); sendResult.setAccount(message.parsePersonAccount()); } - for (List batch : Lists.partition(messages, BATCH_UPDATE_SIZE)) - updatableMessageDao.saveBatch(batch); - - ArrayList logs = new ArrayList<>(); - for (UpdatableMessage message : messages) { + for (UpdatableMessage message : collector.getUpdatableMessagesToAdd()) { UpdatableMessageLog messageLog = message.toMessageLog(); - logs.add(messageLog); + collector.addLog(messageLog); messageLog.setContext("scheduleInTask"); } - for (List batch : Lists.partition(logs, BATCH_UPDATE_SIZE)) - updatableMessageLogDao.saveBatch(batch); + collector.finish(); return sendResults; } @@ -116,16 +116,14 @@ public class UpdatableMessageManager { List messages = updatableMessageDao.getByTaskIds(taskIds); log.info("onHistoryCreated, taskIdSize={}, messageSize={}", taskIds.size(), messages.size()); InitHistories initHistories = new InitHistories(histories); - List messageUpdates = new ArrayList<>(); - List messageLogUpdates = new ArrayList<>(); - List updatableHistories = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { MessageHistory history = initHistories.findHistory(message).orElse(null); if (history == null) continue; MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); + collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setInitHistoryId(history.getId()); messageUpdate.setMessageBody(object.getMessageBody()); @@ -137,7 +135,7 @@ public class UpdatableMessageManager { : UpdatableMessageState.ACCOUNT_NOT_FOUND); UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogUpdates.add(messageLog); + collector.addLog(messageLog); messageLog.setBizId(history.getBizId()); messageLog.setFromAccount(history.getFromAccount()); messageLog.setToAccount(history.getToAccount()); @@ -149,7 +147,7 @@ public class UpdatableMessageManager { messageLog.setContextHistoryId(history.getId()); MessageHistory historyUpdate = new MessageHistory(); - updatableHistories.add(historyUpdate); + collector.updateHistory(historyUpdate); historyUpdate.setId(history.getId()); MessageBody messageBody = JSON.parseObject(history.getMessageBody(), MessageBody.class); @@ -166,35 +164,33 @@ public class UpdatableMessageManager { updateExt.setUpdateRetryCount(message.getRetryCount()); historyUpdate.setRecordExt(PropsUtils.updateProperties(history.getRecordExt(), updateExt)); } - if (CollectionUtils.isNotEmpty(messageUpdates)) { - for (List batch : Lists.partition(messageUpdates, BATCH_UPDATE_SIZE)) - updatableMessageDao.updateBatchById(batch); - } - if (CollectionUtils.isNotEmpty(messageLogUpdates)) { - for (List batch : Lists.partition(messageLogUpdates, BATCH_UPDATE_SIZE)) - updatableMessageLogDao.saveBatch(batch); - } - if (CollectionUtils.isNotEmpty(updatableHistories)) { - for (List batch : Lists.partition(updatableHistories, BATCH_UPDATE_SIZE)) - messageHistoryDao.updateBatchById(batch); - } + collector.finish(); } // !! update message - @Transactional public MessageUpdateResponse updateMessage(UpdateMessageRequest request) { BizAssertions.assertNotEmpty(request.getUpdates(), "更新消息不能为空"); + List messages = updatableMessageDao + .getByBizMessageIds(collectBizMessageIds(request.getUpdates())); + BizAssertions.assertNotEmpty(messages, "未找到任何需要更新的消息"); + MessageUpdateResponse response = new MessageUpdateResponse(); + List> batches = Lists.partition( + request.getUpdates(), props.getUpdatableMessageMaxLockRecords()); + for (List batch : batches) + transactionTemplate.executeWithoutResult(unused -> updateMessageImpl(batch, response)); + return response; + } + + private void updateMessageImpl(List requestUpdates, + MessageUpdateResponse response) { List requestMessages = updatableMessageDao - .getByBizMessageIdsForUpdate(request.getBizMessageIds()); - BizAssertions.assertNotEmpty(requestMessages, "未找到任何需要更新的消息"); + .getByBizMessageIdsForUpdate(collectBizMessageIds(requestUpdates)); Map bizMessageId2Message = requestMessages.stream() .collect(toMap(UpdatableMessage::bizMessageId, identity())); - - MessageUpdateResponse response = new MessageUpdateResponse(); - List updates = new ArrayList<>(); + List validUpdates = new ArrayList<>(); List updateIds = new ArrayList<>(); - for (UpdateMessageRequest.Update update : request.getUpdates()) { + for (UpdateMessageRequest.Update update : requestUpdates) { UpdatableMessage message = bizMessageId2Message.get(update.getBizMessageId()); if (message == null) { response.addNonUpdatedMessage( @@ -206,18 +202,17 @@ public class UpdatableMessageManager { NonUpdateMessageReason.MESSAGE_STATE_NOT_ALLOWED, message.getState()); } else { - updates.add(update); + validUpdates.add(update); updateIds.add(message.getId()); response.addUpdatedBizMessageId(message.getBizMessageId()); } } - if (!updates.isEmpty()) { + if (!validUpdates.isEmpty()) { // incr data version + reset retry count updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); messageUpdateRetryService.scheduleNextRetry(updateIds); - updateHistorySupport.addUpdateHistories("updateHistoryCreated", updates); + updateHistorySupport.addUpdateHistories("updateHistoryCreated", validUpdates); } - return response; } // !! init & update message sent @@ -281,21 +276,27 @@ public class UpdatableMessageManager { @Transactional public void ack(UpdatableMessageAckRequest request) { - List acks = request.determineValidAcknowledgments(); - if (CollectionUtils.isEmpty(acks)) return; - List bizMessageIds = acks.stream() - .map(UpdatableMessageAckRequest.Acknowledgment::getBizMessageId) + List acknowledgments = request.determineValidAcknowledgments(); + if (CollectionUtils.isEmpty(acknowledgments)) return; + List> batches = Lists.partition( + acknowledgments, props.getUpdatableMessageMaxLockRecords()); + for (List batch : batches) + transactionTemplate.executeWithoutResult(unused -> ackImpl(batch)); + } + + private void ackImpl(List acknowledgments) { + List bizMessageIds = acknowledgments.stream() + .map(Acknowledgment::getBizMessageId) .collect(toList()); - Map bizMessageId2Ack = acks.stream() - .collect(toMap(UpdatableMessageAckRequest.Acknowledgment::getBizMessageId, identity())); + Map bizMessageId2Ack = acknowledgments.stream() + .collect(toMap(Acknowledgment::getBizMessageId, identity())); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); - List messageUpdates = new ArrayList<>(); - List messageLogs = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { Long dataVersion = bizMessageId2Ack.get(message.bizMessageId()).getDataVersion(); UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogs.add(messageLog); + collector.addLog(messageLog); messageLog.setContext("ack"); messageLog.setContextHistoryId(0L); messageLog.setRetryCount(0L); @@ -320,14 +321,11 @@ public class UpdatableMessageManager { } logExt.setAckIgnored(false); UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); + collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); } - if (CollectionUtils.isNotEmpty(messageUpdates)) - updatableMessageDao.updateBatchById(messageUpdates); - if (CollectionUtils.isNotEmpty(messageLogs)) - updatableMessageLogDao.saveBatch(messageLogs); + collector.finish(); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java index 3fafe16..f09aecd 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java @@ -9,15 +9,15 @@ import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse.MessageInfo; import cn.axzo.im.center.common.enums.BizTypeEnum; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageCustomBody; -import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; -import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; +import cn.axzo.im.updatable.collector.ManipulateCollector; +import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.UUIDUtil; @@ -25,7 +25,6 @@ import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.IdentityHashMap; @@ -45,8 +44,7 @@ public class UpdateHistorySupport { private final UpdatableMessageDao updatableMessageDao; private final IMChannelProvider imChannel; - private final MessageHistoryDao messageHistoryDao; - private final UpdatableMessageLogDao updatableMessageLogDao; + private final ManipulateCollectorFactory manipulateCollectorFactory; private final ImProperties props; public void addUpdateHistories(String context, Collection updates) { @@ -54,21 +52,19 @@ public class UpdateHistorySupport { .getByBizMessageIds(collectBizMessageIds(updates)) .stream().collect(toMap(UpdatableMessage::getBizMessageId, identity())); String batchNo = UUIDUtil.uuidString(); - List histories = new ArrayList<>(); - List messageUpdates = new ArrayList<>(); - List messageLogs = new ArrayList<>(); Map message2History = new IdentityHashMap<>(); Map messageLog2History = new IdentityHashMap<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (MessageUpdateInfo update : updates) { UpdatableMessage message = bizMessageId2Message.get(update.bizMessageId()); if (message == null) continue; UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); + collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setState(UpdatableMessageState.UPDATE_MESSAGE_QUEUED); MessageHistory history = new MessageHistory(); - histories.add(history); + collector.addHistory(history); message2History.put(messageUpdate, history); history.setBizId(message.getBizId()); history.setFromAccount(message.getFromAccount()); @@ -107,7 +103,7 @@ public class UpdateHistorySupport { history.setTimestampForSend(new Date()); UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogs.add(messageLog); + collector.addLog(messageLog); messageLog2History.put(messageLog, history); messageLog.setDataVersion(message.getDataVersion()); messageLog.setMessageState(messageUpdate.getState()); @@ -115,18 +111,17 @@ public class UpdateHistorySupport { messageLog.setBizBody(messageUpdate.getBizBody()); messageLog.setRetryCount(message.getRetryCount()); } - messageHistoryDao.saveBatch(histories); - for (UpdatableMessage messageUpdate : messageUpdates) { + collector.finishAddHistory(); + for (UpdatableMessage messageUpdate : collector.getUpdatableMessagesToUpdate()) { MessageHistory history = message2History.get(messageUpdate); messageUpdate.setUpdateHistoryId(history.getId()); } - for (UpdatableMessageLog messageLog : messageLogs) { + for (UpdatableMessageLog messageLog : collector.getUpdatableMessageLogsToAdd()) { MessageHistory history = messageLog2History.get(messageLog); messageLog.setContext(context); messageLog.setContextHistoryId(history.getId()); } - updatableMessageLogDao.saveBatch(messageLogs); - updatableMessageDao.updateBatchById(messageUpdates); + collector.finish(); } public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java new file mode 100644 index 0000000..d37794a --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java @@ -0,0 +1,101 @@ +package cn.axzo.im.updatable.collector; + +import cn.axzo.im.dao.repository.MessageHistoryDao; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.entity.UpdatableMessageLog; +import com.google.common.collect.Lists; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * @author yanglin + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class ManipulateCollector { + + private static final int BATCH_SIZE = 1000; + + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + private final MessageHistoryDao messageHistoryDao; + + private final State updatableMessagesToAdd = new State<>(); + private final State updatableMessagesToUpdate = new State<>(); + private final State updatableMessageLogsToAdd = new State<>(); + private final State messageHistoriesToAdd = new State<>(); + private final State messageHistoriesToUpdate = new State<>(); + + public List getUpdatableMessagesToAdd() { + return updatableMessagesToAdd.elements; + } + + public List getUpdatableMessagesToUpdate() { + return updatableMessagesToUpdate.elements; + } + + public List getUpdatableMessageLogsToAdd() { + return updatableMessageLogsToAdd.elements; + } + + public void addMessage(UpdatableMessage message) { + updatableMessagesToAdd.add(message); + } + + public void updateMessage(UpdatableMessage message) { + updatableMessagesToUpdate.add(message); + } + + public void addLog(UpdatableMessageLog log) { + updatableMessageLogsToAdd.add(log); + } + + public void addHistory(MessageHistory history) { + messageHistoriesToAdd.add(history); + } + + public void updateHistory(MessageHistory history) { + messageHistoriesToUpdate.add(history); + } + + public void finishAddHistory() { + messageHistoriesToAdd.batched(messageHistoryDao::saveBatch); + } + + public void finish() { + updatableMessagesToAdd.batched(updatableMessageDao::saveBatch); + updatableMessagesToUpdate.batched(updatableMessageDao::updateBatchById); + updatableMessageLogsToAdd.batched(updatableMessageLogDao::saveBatch); + messageHistoriesToAdd.batched(messageHistoryDao::saveBatch); + messageHistoriesToUpdate.batched(messageHistoryDao::updateBatchById); + } + + private static class State { + private boolean finished; + private List elements; + + void add(T element) { + if (elements == null) + elements = new ArrayList<>(); + elements.add(element); + } + + void batched(Consumer> action) { + if (CollectionUtils.isEmpty(elements)) + return; + if (finished) + return; + Lists.partition(elements, BATCH_SIZE).forEach(action); + finished = true; + } + + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java new file mode 100644 index 0000000..d932e4e --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java @@ -0,0 +1,27 @@ +package cn.axzo.im.updatable.collector; + +import cn.axzo.im.dao.repository.MessageHistoryDao; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.dao.repository.UpdatableMessageLogDao; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +public class ManipulateCollectorFactory { + + private final UpdatableMessageDao updatableMessageDao; + private final UpdatableMessageLogDao updatableMessageLogDao; + private final MessageHistoryDao messageHistoryDao; + + public ManipulateCollector create() { + return new ManipulateCollector( + updatableMessageDao, + updatableMessageLogDao, + messageHistoryDao); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index c6aa97f..bce1d84 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -1,16 +1,15 @@ package cn.axzo.im.updatable.handler; -import cn.axzo.im.dao.repository.UpdatableMessageDao; -import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.HistoryAndMessage; +import cn.axzo.im.updatable.collector.ManipulateCollector; +import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.List; /** @@ -20,8 +19,7 @@ import java.util.List; @RequiredArgsConstructor public class InitMessageHandler implements StateHandler { - private final UpdatableMessageDao updatableMessageDao; - private final UpdatableMessageLogDao updatableMessageLogDao; + private final ManipulateCollectorFactory manipulateCollectorFactory; @Override public void onSuccess(List historyAndMessages) { @@ -34,26 +32,24 @@ public class InitMessageHandler implements StateHandler { } private void updateState(List historyAndMessages, boolean isSuccess) { - List messageUpdates = new ArrayList<>(); - List messageLogs = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessage message = historyAndMessage.getMessage(); UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); + collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setState(isSuccess ? UpdatableMessageState.INIT_MESSAGE_SEND_SUCCESS : UpdatableMessageState.INIT_MESSAGE_SEND_FAIL); UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogs.add(messageLog); + collector.addLog(messageLog); messageLog.setMessageState(messageUpdate.getState()); MessageHistory history = historyAndMessage.getHistories().get(0); messageLog.setInitHistoryId(history.getId()); messageLog.setContext("sendInitMessage"); messageLog.setContextHistoryId(history.getId()); } - updatableMessageDao.updateBatchById(messageUpdates); - updatableMessageLogDao.saveBatch(messageLogs); + collector.finish(); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java index 87ad10b..e651f9a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java @@ -1,17 +1,16 @@ package cn.axzo.im.updatable.handler; -import cn.axzo.im.dao.repository.UpdatableMessageDao; -import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.HistoryAndMessage; import cn.axzo.im.updatable.MessageBodyJsonObject; +import cn.axzo.im.updatable.collector.ManipulateCollector; +import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.List; /** @@ -21,8 +20,7 @@ import java.util.List; @RequiredArgsConstructor public class UpdateMessageHandler implements StateHandler { - private final UpdatableMessageDao updatableMessageDao; - private final UpdatableMessageLogDao updatableMessageLogDao; + private final ManipulateCollectorFactory manipulateCollectorFactory; @Override public void onSuccess(List historyAndMessages) { @@ -35,8 +33,7 @@ public class UpdateMessageHandler implements StateHandler { } private void updateState(List historyAndMessages, boolean isSuccess) { - List messageUpdates = new ArrayList<>(); - List messageLogs = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessageState state = historyAndMessage.getMessage().getState(); UpdatableMessage message = historyAndMessage.getMessage(); @@ -44,7 +41,7 @@ public class UpdateMessageHandler implements StateHandler { || state == UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL; if (queuedOrFail && historyAndMessage.hasDataVersionMatchHistories()) { UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdates.add(messageUpdate); + collector.updateMessage(messageUpdate); messageUpdate.setId(historyAndMessage.getMessage().getId()); messageUpdate.setState(isSuccess ? UpdatableMessageState.UPDATE_MESSAGE_SEND_SUCCESS @@ -53,7 +50,7 @@ public class UpdateMessageHandler implements StateHandler { for (MessageHistory history : historyAndMessage.getHistories()) { UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogs.add(messageLog); + collector.addLog(messageLog); messageLog.setMessageState(isSuccess ? UpdatableMessageState.UPDATE_MESSAGE_SEND_SUCCESS : UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL); @@ -67,8 +64,6 @@ public class UpdateMessageHandler implements StateHandler { messageLog.setRetryCount(history.getUpdateRetryCount()); } } - if (!messageUpdates.isEmpty()) - updatableMessageDao.updateBatchById(messageUpdates); - updatableMessageLogDao.saveBatch(messageLogs); + collector.finish(); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index c5c008c..fe1cbc0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -8,11 +8,12 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.updatable.UpdateHistorySupport; import cn.axzo.im.utils.ImProperties; +import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.joda.time.DateTime; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Collection; @@ -31,19 +32,26 @@ public class MessageUpdateRetryService { private final MessageUpdateRetryDao messageUpdateRetryDao; private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; - private final ImProperties props; private final UpdateHistorySupport updateHistorySupport; + private final TransactionTemplate transactionTemplate; + private final ImProperties props; public void removePrevRetryByBizMessageIds(Collection bizMessageIds) { if (CollectionUtils.isNotEmpty(bizMessageIds)) messageUpdateRetryDao.getBaseMapper().deleteByBizMessageIds(bizMessageIds); } - @Transactional public void advanceRetry(List bizMessageIds) { + List> batches = Lists.partition( + bizMessageIds, props.getUpdatableMessageMaxLockRecords()); + for (List batch : batches) + transactionTemplate.executeWithoutResult(unused -> advanceRetryImpl(batch)); + } + + private void advanceRetryImpl(List bizMessageIds) { removePrevRetryByBizMessageIds(bizMessageIds); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); - UpdateRetryState state = new UpdateRetryState(messages, props.getMessageUpdateAckMaxRetryCount()); + UpdateAckState state = new UpdateAckState(messages, props.getMessageUpdateAckMaxRetryCount()); // 先发本次生试, 再调度下一次重试 retryUpdateMessage(state.getNonAckMessageIds()); scheduleNextRetry(state.getScheduleNextRetryMessages()); @@ -70,6 +78,7 @@ public class MessageUpdateRetryService { retries.add(retry); UpdatableMessageLog messageLog = message.toMessageLog(); + messageLogs.add(messageLog); messageLog.setRetryCount(message.getRetryCount() + 1); messageLog.setContext("scheduleNextRetrySendUpdateMessage"); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateAckState.java similarity index 96% rename from im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateAckState.java index ba1194d..4f8b31b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateRetryState.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/UpdateAckState.java @@ -12,7 +12,7 @@ import static java.util.stream.Collectors.toList; * @author yanglin */ @RequiredArgsConstructor -public class UpdateRetryState { +public class UpdateAckState { private final List messages; private final int maxRetryCount; diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index c1de7e8..a1aa041 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -32,6 +32,8 @@ public class ImProperties { private int fetchUpdatableMessageMaxSize = 500; + private int updatableMessageMaxLockRecords = 5; + private SendMessageConfig sendMessage = new SendMessageConfig(); private Set genImAccountJobCodes = Sets.newHashSet( From 225abdcefd415c55c1b28f00f5d1ae56895557f4 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 10:47:02 +0800 Subject: [PATCH 13/93] REQ-3057-card: a lot of staff --- .../main/java/cn/axzo/im/entity/UpdatableMessageLog.java | 2 +- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 3c0570d..1002c0e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -43,7 +43,7 @@ public class UpdatableMessageLog { @Setter @Getter public static class RecordExt { - private Boolean ackIgnored; + private Boolean ackSuccess; private String ackDescription; private Long requestDataVersion; private Long messageDataVersion; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 0fa3779..82e9c51 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -307,7 +307,7 @@ public class UpdatableMessageManager { messageLog.setRecordExt(logExt); if (!dataVersion.equals(message.getDataVersion())) { - logExt.setAckIgnored(true); + logExt.setAckSuccess(false); logExt.setAckDescription("数据版本不匹配"); logExt.setRequestDataVersion(dataVersion); logExt.setMessageDataVersion(message.getDataVersion()); @@ -315,11 +315,11 @@ public class UpdatableMessageManager { } // 避免前端有bug if (!message.getState().isUpdateAckAllowed()) { - logExt.setAckIgnored(true); + logExt.setAckSuccess(false); logExt.setAckDescription("消息状态不允许ack"); continue; } - logExt.setAckIgnored(false); + logExt.setAckSuccess(true); UpdatableMessage messageUpdate = new UpdatableMessage(); collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); From 0ff1d4752fc9e2c17cfa87bd4d0927d09cac7c6f Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 10:59:20 +0800 Subject: [PATCH 14/93] REQ-3057-card: a lot of staff --- .../main/java/cn/axzo/im/entity/UpdatableMessage.java | 1 - .../cn/axzo/im/updatable/UpdatableMessageManager.java | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 7ef8671..657c7dc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -45,7 +45,6 @@ public class UpdatableMessage implements MessageUpdateInfo { private JSONObject bizBody; private Long dataVersion; private Long ackDataVersion; - private Date ackTime; private Long retryCount; @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 82e9c51..c8076ea 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -306,6 +306,13 @@ public class UpdatableMessageManager { UpdatableMessageLog.RecordExt logExt = new UpdatableMessageLog.RecordExt(); messageLog.setRecordExt(logExt); + UpdatableMessage messageUpdate = new UpdatableMessage(); + collector.updateMessage(messageUpdate); + messageUpdate.setId(message.getId()); + Long ackDataVersion = dataVersion >= message.getAckDataVersion() + ? dataVersion + : message.getAckDataVersion(); + messageUpdate.setAckDataVersion(ackDataVersion); if (!dataVersion.equals(message.getDataVersion())) { logExt.setAckSuccess(false); logExt.setAckDescription("数据版本不匹配"); @@ -320,9 +327,6 @@ public class UpdatableMessageManager { continue; } logExt.setAckSuccess(true); - UpdatableMessage messageUpdate = new UpdatableMessage(); - collector.updateMessage(messageUpdate); - messageUpdate.setId(message.getId()); messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); } collector.finish(); From 2e1cbd4d5b5c940452715fa568534bf94d1c1b95 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 11:12:12 +0800 Subject: [PATCH 15/93] REQ-3057-card: a lot of staff --- .../im/updatable/AddUpdateHistoryResult.java | 22 ++++++++++++++++++ .../im/updatable/UpdateHistorySupport.java | 11 +++++++-- .../collector/ManipulateCollector.java | 4 ++++ .../retry/MessageUpdateRetryService.java | 23 +++++++++++++++---- 4 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java b/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java new file mode 100644 index 0000000..95be02d --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java @@ -0,0 +1,22 @@ +package cn.axzo.im.updatable; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Optional; + +/** + * @author yanglin + */ +public class AddUpdateHistoryResult { + + private final Map bizMessageId2HistoryId = new IdentityHashMap<>(); + + public Optional findHistoryId(String bizMessageId) { + return Optional.ofNullable(bizMessageId2HistoryId.get(bizMessageId)); + } + + public void addHistoryId(String bizMessageId, Long historyId) { + bizMessageId2HistoryId.put(bizMessageId, historyId); + } + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java index f09aecd..2f7d8c9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java @@ -25,7 +25,6 @@ import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.Collection; import java.util.Date; import java.util.IdentityHashMap; import java.util.List; @@ -47,7 +46,8 @@ public class UpdateHistorySupport { private final ManipulateCollectorFactory manipulateCollectorFactory; private final ImProperties props; - public void addUpdateHistories(String context, Collection updates) { + public AddUpdateHistoryResult addUpdateHistories( + String context, List updates) { Map bizMessageId2Message = updatableMessageDao .getByBizMessageIds(collectBizMessageIds(updates)) .stream().collect(toMap(UpdatableMessage::getBizMessageId, identity())); @@ -122,6 +122,13 @@ public class UpdateHistorySupport { messageLog.setContextHistoryId(history.getId()); } collector.finish(); + AddUpdateHistoryResult result = new AddUpdateHistoryResult(); + for (int i = 0; i < updates.size(); i++) { + String bizMessageId = updates.get(i).bizMessageId(); + MessageHistory history = collector.getMessageHistoriesToAdd().get(i); + result.addHistoryId(bizMessageId, history.getId()); + } + return result; } public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java index d37794a..9e6acaa 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java @@ -45,6 +45,10 @@ public class ManipulateCollector { return updatableMessageLogsToAdd.elements; } + public List getMessageHistoriesToAdd() { + return messageHistoriesToAdd.elements; + } + public void addMessage(UpdatableMessage message) { updatableMessagesToAdd.add(message); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index fe1cbc0..3e96658 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -6,7 +6,10 @@ import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.MessageUpdateRetry; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; +import cn.axzo.im.updatable.AddUpdateHistoryResult; import cn.axzo.im.updatable.UpdateHistorySupport; +import cn.axzo.im.updatable.collector.ManipulateCollector; +import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import cn.axzo.im.utils.ImProperties; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; @@ -34,6 +37,7 @@ public class MessageUpdateRetryService { private final UpdatableMessageLogDao updatableMessageLogDao; private final UpdateHistorySupport updateHistorySupport; private final TransactionTemplate transactionTemplate; + private final ManipulateCollectorFactory manipulateCollectorFactory; private final ImProperties props; public void removePrevRetryByBizMessageIds(Collection bizMessageIds) { @@ -65,7 +69,7 @@ public class MessageUpdateRetryService { .collect(toList()); removePrevRetryByBizMessageIds(bizMessageIds); ArrayList retries = new ArrayList<>(); - List messageLogs = new ArrayList<>(); + ManipulateCollector collector = manipulateCollectorFactory.create(); Date nextRetryTime = DateTime.now() .plusSeconds(props.getMessageUpdateAckRetryIntervalSeconds()) .toDate(); @@ -78,19 +82,30 @@ public class MessageUpdateRetryService { retries.add(retry); UpdatableMessageLog messageLog = message.toMessageLog(); - messageLogs.add(messageLog); + collector.addLog(messageLog); messageLog.setRetryCount(message.getRetryCount() + 1); messageLog.setContext("scheduleNextRetrySendUpdateMessage"); } messageUpdateRetryDao.saveBatch(retries); - updatableMessageLogDao.saveBatch(messageLogs); + collector.finish(); } public void retryUpdateMessage(List messageIds) { if (CollectionUtils.isEmpty(messageIds)) return; updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); List messages = updatableMessageDao.listByIds(messageIds); - updateHistorySupport.addUpdateHistories("retryUpdateHistoryCreated", messages); + AddUpdateHistoryResult result = updateHistorySupport + .addUpdateHistories("retryUpdateHistoryCreated", messages); + ManipulateCollector collector = manipulateCollectorFactory.create(); + for (UpdatableMessage message : messages) { + result.findHistoryId(message.bizMessageId()).ifPresent(historyId -> { + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdate.setId(message.getId()); + messageUpdate.setRetryHistoryId(historyId); + collector.updateMessage(messageUpdate); + }); + } + collector.finish(); } } \ No newline at end of file From 004c9a46d88d28f9c6ab078012e1b169d0593fe6 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 11:12:47 +0800 Subject: [PATCH 16/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/updatable/retry/MessageUpdateRetryService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 3e96658..df5c478 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -2,7 +2,6 @@ package cn.axzo.im.updatable.retry; import cn.axzo.im.dao.repository.MessageUpdateRetryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; -import cn.axzo.im.dao.repository.UpdatableMessageLogDao; import cn.axzo.im.entity.MessageUpdateRetry; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; @@ -34,7 +33,6 @@ public class MessageUpdateRetryService { private final MessageUpdateRetryDao messageUpdateRetryDao; private final UpdatableMessageDao updatableMessageDao; - private final UpdatableMessageLogDao updatableMessageLogDao; private final UpdateHistorySupport updateHistorySupport; private final TransactionTemplate transactionTemplate; private final ManipulateCollectorFactory manipulateCollectorFactory; From ea3e6677ae0124d777527596b4e56c0993065524 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 11:43:03 +0800 Subject: [PATCH 17/93] REQ-3057-card: a lot of staff --- .../axzo/im/controller/MessageController.java | 6 ++-- .../cn/axzo/im/entity/UpdatableMessage.java | 1 + .../im/updatable/AddUpdateHistoryResult.java | 15 +++++--- .../im/updatable/UpdatableMessageManager.java | 6 ++-- ...HistorySupport.java => UpdateSupport.java} | 34 ++++++++++++------- .../collector/ManipulateCollector.java | 4 --- .../updatable/handler/InitMessageHandler.java | 5 ++- .../retry/MessageUpdateRetryService.java | 17 +++------- 8 files changed, 48 insertions(+), 40 deletions(-) rename im-center-server/src/main/java/cn/axzo/im/updatable/{UpdateHistorySupport.java => UpdateSupport.java} (87%) diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 8a3bded..c192ce1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -32,7 +32,7 @@ import cn.axzo.im.service.MessageHistoryService; import cn.axzo.im.service.MessageTaskService; import cn.axzo.im.service.RobotMsgTemplateService; import cn.axzo.im.updatable.UpdatableMessageManager; -import cn.axzo.im.updatable.UpdateHistorySupport; +import cn.axzo.im.updatable.UpdateSupport; import cn.axzo.pokonyan.exception.Aassert; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -88,7 +88,7 @@ public class MessageController implements MessageApi { @Autowired private UpdatableMessageManager updatableMessageManager; @Autowired - private UpdateHistorySupport updateHistorySupport; + private UpdateSupport updateSupport; @Override @@ -213,7 +213,7 @@ public class MessageController implements MessageApi { @Override public ApiResult fetchUpdatableMessage(FetchUpdatableMessageRequest request) { - FetchUpdatableMessageResponse resp = updateHistorySupport.fetchUpdatableMessage(request); + FetchUpdatableMessageResponse resp = updateSupport.fetchUpdatableMessage(request); return ApiResult.ok(resp); } diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 657c7dc..642bb4f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -36,6 +36,7 @@ public class UpdatableMessage implements MessageUpdateInfo { private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; + private String initNimMessageId; private Long initHistoryId; private Long updateHistoryId; private Long retryHistoryId; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java b/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java index 95be02d..5cfafed 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/AddUpdateHistoryResult.java @@ -1,5 +1,6 @@ package cn.axzo.im.updatable; +import java.util.Collection; import java.util.IdentityHashMap; import java.util.Map; import java.util.Optional; @@ -9,14 +10,18 @@ import java.util.Optional; */ public class AddUpdateHistoryResult { - private final Map bizMessageId2HistoryId = new IdentityHashMap<>(); + private final Map messageId2HistoryId = new IdentityHashMap<>(); - public Optional findHistoryId(String bizMessageId) { - return Optional.ofNullable(bizMessageId2HistoryId.get(bizMessageId)); + public Optional findHistoryId(Long messageId) { + return Optional.ofNullable(messageId2HistoryId.get(messageId)); } - public void addHistoryId(String bizMessageId, Long historyId) { - bizMessageId2HistoryId.put(bizMessageId, historyId); + public void addHistoryId(Long messageId, Long historyId) { + messageId2HistoryId.put(messageId, historyId); + } + + public Collection getMessageIds() { + return messageId2HistoryId.keySet(); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index c8076ea..350be31 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -61,7 +61,7 @@ public class UpdatableMessageManager { private final InitMessageHandler initMessageHandler; private final UpdateMessageHandler updateMessageHandler; private final MessageUpdateRetryService messageUpdateRetryService; - private final UpdateHistorySupport updateHistorySupport; + private final UpdateSupport updateSupport; private final ManipulateCollectorFactory manipulateCollectorFactory; private final TransactionTemplate transactionTemplate; private final ImProperties props; @@ -211,7 +211,9 @@ public class UpdatableMessageManager { // incr data version + reset retry count updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); messageUpdateRetryService.scheduleNextRetry(updateIds); - updateHistorySupport.addUpdateHistories("updateHistoryCreated", validUpdates); + AddUpdateHistoryResult result = updateSupport + .addUpdateHistories("updateHistoryCreated", validUpdates); + updateSupport.updateHistoryId(result, UpdatableMessage::setUpdateHistoryId); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java similarity index 87% rename from im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 2f7d8c9..6d57277 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateHistorySupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -29,6 +29,7 @@ import java.util.Date; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; import static cn.axzo.im.center.api.vo.req.MessageUpdateInfo.collectBizMessageIds; import static java.util.function.Function.identity; @@ -39,7 +40,7 @@ import static java.util.stream.Collectors.toMap; */ @Component @RequiredArgsConstructor -public class UpdateHistorySupport { +public class UpdateSupport { private final UpdatableMessageDao updatableMessageDao; private final IMChannelProvider imChannel; @@ -48,11 +49,11 @@ public class UpdateHistorySupport { public AddUpdateHistoryResult addUpdateHistories( String context, List updates) { - Map bizMessageId2Message = updatableMessageDao - .getByBizMessageIds(collectBizMessageIds(updates)) + List messages = updatableMessageDao + .getByBizMessageIds(collectBizMessageIds(updates)); + Map bizMessageId2Message = messages .stream().collect(toMap(UpdatableMessage::getBizMessageId, identity())); String batchNo = UUIDUtil.uuidString(); - Map message2History = new IdentityHashMap<>(); Map messageLog2History = new IdentityHashMap<>(); ManipulateCollector collector = manipulateCollectorFactory.create(); for (MessageUpdateInfo update : updates) { @@ -65,7 +66,6 @@ public class UpdateHistorySupport { MessageHistory history = new MessageHistory(); collector.addHistory(history); - message2History.put(messageUpdate, history); history.setBizId(message.getBizId()); history.setFromAccount(message.getFromAccount()); history.setToAccount(message.getToAccount()); @@ -112,10 +112,6 @@ public class UpdateHistorySupport { messageLog.setRetryCount(message.getRetryCount()); } collector.finishAddHistory(); - for (UpdatableMessage messageUpdate : collector.getUpdatableMessagesToUpdate()) { - MessageHistory history = message2History.get(messageUpdate); - messageUpdate.setUpdateHistoryId(history.getId()); - } for (UpdatableMessageLog messageLog : collector.getUpdatableMessageLogsToAdd()) { MessageHistory history = messageLog2History.get(messageLog); messageLog.setContext(context); @@ -123,10 +119,10 @@ public class UpdateHistorySupport { } collector.finish(); AddUpdateHistoryResult result = new AddUpdateHistoryResult(); - for (int i = 0; i < updates.size(); i++) { - String bizMessageId = updates.get(i).bizMessageId(); + for (int i = 0; i < messages.size(); i++) { + Long messageId = messages.get(i).getId(); MessageHistory history = collector.getMessageHistoriesToAdd().get(i); - result.addHistoryId(bizMessageId, history.getId()); + result.addHistoryId(messageId, history.getId()); } return result; } @@ -148,4 +144,18 @@ public class UpdateHistorySupport { return response; } + public void updateHistoryId(AddUpdateHistoryResult result, + BiConsumer historyIdSetter) { + ManipulateCollector collector = manipulateCollectorFactory.create(); + for (Long messageId : result.getMessageIds()) { + result.findHistoryId(messageId).ifPresent(historyId -> { + UpdatableMessage messageUpdate = new UpdatableMessage(); + messageUpdate.setId(messageId); + historyIdSetter.accept(messageUpdate, historyId); + collector.updateMessage(messageUpdate); + }); + } + collector.finish(); + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java index 9e6acaa..1afafa4 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java @@ -37,10 +37,6 @@ public class ManipulateCollector { return updatableMessagesToAdd.elements; } - public List getUpdatableMessagesToUpdate() { - return updatableMessagesToUpdate.elements; - } - public List getUpdatableMessageLogsToAdd() { return updatableMessageLogsToAdd.elements; } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index bce1d84..e8f93d9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -35,16 +35,19 @@ public class InitMessageHandler implements StateHandler { ManipulateCollector collector = manipulateCollectorFactory.create(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessage message = historyAndMessage.getMessage(); + MessageHistory history = historyAndMessage.getHistories().get(0); + UpdatableMessage messageUpdate = new UpdatableMessage(); collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); + if (isSuccess) + messageUpdate.setInitNimMessageId(history.getMessageId()); messageUpdate.setState(isSuccess ? UpdatableMessageState.INIT_MESSAGE_SEND_SUCCESS : UpdatableMessageState.INIT_MESSAGE_SEND_FAIL); UpdatableMessageLog messageLog = message.toMessageLog(); collector.addLog(messageLog); messageLog.setMessageState(messageUpdate.getState()); - MessageHistory history = historyAndMessage.getHistories().get(0); messageLog.setInitHistoryId(history.getId()); messageLog.setContext("sendInitMessage"); messageLog.setContextHistoryId(history.getId()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index df5c478..1980f67 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -6,7 +6,7 @@ import cn.axzo.im.entity.MessageUpdateRetry; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.updatable.AddUpdateHistoryResult; -import cn.axzo.im.updatable.UpdateHistorySupport; +import cn.axzo.im.updatable.UpdateSupport; import cn.axzo.im.updatable.collector.ManipulateCollector; import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; import cn.axzo.im.utils.ImProperties; @@ -33,7 +33,7 @@ public class MessageUpdateRetryService { private final MessageUpdateRetryDao messageUpdateRetryDao; private final UpdatableMessageDao updatableMessageDao; - private final UpdateHistorySupport updateHistorySupport; + private final UpdateSupport updateSupport; private final TransactionTemplate transactionTemplate; private final ManipulateCollectorFactory manipulateCollectorFactory; private final ImProperties props; @@ -92,18 +92,9 @@ public class MessageUpdateRetryService { if (CollectionUtils.isEmpty(messageIds)) return; updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); List messages = updatableMessageDao.listByIds(messageIds); - AddUpdateHistoryResult result = updateHistorySupport + AddUpdateHistoryResult result = updateSupport .addUpdateHistories("retryUpdateHistoryCreated", messages); - ManipulateCollector collector = manipulateCollectorFactory.create(); - for (UpdatableMessage message : messages) { - result.findHistoryId(message.bizMessageId()).ifPresent(historyId -> { - UpdatableMessage messageUpdate = new UpdatableMessage(); - messageUpdate.setId(message.getId()); - messageUpdate.setRetryHistoryId(historyId); - collector.updateMessage(messageUpdate); - }); - } - collector.finish(); + updateSupport.updateHistoryId(result, UpdatableMessage::setRetryHistoryId); } } \ No newline at end of file From ce3f17d7ff6d1c701c1d370e23e6087e60aafb72 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 13:35:26 +0800 Subject: [PATCH 18/93] REQ-3057-card: a lot of staff --- .../java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java | 2 +- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java index 3f3333e..04d1d82 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java @@ -51,7 +51,7 @@ public class MessageCustomBody { /** * 最原始的网易云信消息id, 更新的哪条消息 */ - private Long initMessageId; + private String initMessageId; /** * 更新的消息类型 diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 6d57277..b50abed 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -78,7 +78,7 @@ public class UpdateSupport { messageBody.setPayload(update.bizBody().toJSONString()); messageBody.setBizMessageId(message.bizMessageId()); messageBody.setDataVersion(messageUpdate.getDataVersion()); - messageBody.setInitMessageId(message.getInitHistoryId()); + messageBody.setInitMessageId(message.getInitNimMessageId()); messageBody.setMsgType(message.getMsgType()); history.setMessageBody(JSON.toJSONString(messageBody)); messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); From 3d87939570cd13fc6abaddbd52c5e988dad9d792 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 13:40:31 +0800 Subject: [PATCH 19/93] REQ-3057-card: a lot of staff --- .../src/main/java/cn/axzo/im/utils/ImProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index a1aa041..169c490 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -26,7 +26,7 @@ public class ImProperties { private String controllerToken = "123442"; - private int messageUpdateAckMaxRetryCount = 6; + private int messageUpdateAckMaxRetryCount = 3; private int messageUpdateAckRetryIntervalSeconds = 10; From e25f0750615b18e3654fbeed5b94d5a97750c8ac Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 8 Nov 2024 15:09:19 +0800 Subject: [PATCH 20/93] REQ-3057-card: a lot of staff --- .../center/api/vo/PersonAccountAttribute.java | 4 +- .../api/vo/req/SendTemplateMessageParam.java | 7 +++- .../center/common/enums/ImClientAppType.java | 34 ++++++++++++++++ .../axzo/im/controller/MessageController.java | 40 +++++++++++++------ .../cn/axzo/im/entity/HistoryRecordExt.java | 1 + .../java/cn/axzo/im/entity/MessageTask.java | 2 + .../cn/axzo/im/entity/UpdatableMessage.java | 4 +- .../service/impl/MessageTaskServiceImpl.java | 7 +++- .../cn/axzo/im/updatable/InitHistories.java | 6 ++- .../im/updatable/UpdatableMessageManager.java | 5 ++- .../cn/axzo/im/updatable/UpdateSupport.java | 2 +- 11 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 6c284b6..9c50a1a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -1,6 +1,6 @@ package cn.axzo.im.center.api.vo; -import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.ImClientAppType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -41,6 +41,6 @@ public class PersonAccountAttribute { * @See cn.axzo.im.center.common.enums.AppTypeEnum */ @NotNull(message = "appType不能为空") - private AppTypeEnum appType; + private ImClientAppType appType; } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 3188414..6997047 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -21,6 +21,11 @@ import java.util.Set; @AllArgsConstructor public class SendTemplateMessageParam { + /** + * 发送人 + */ + private PersonAccountAttribute sender; + /** * 消息接收用户信息 */ @@ -66,7 +71,7 @@ public class SendTemplateMessageParam { private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE; - private String updatableRefTemplateId; + private String refTemplateId; public boolean isUpdatable() { return templatedMsgType.isUpdatable(); diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java new file mode 100644 index 0000000..e3117a0 --- /dev/null +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java @@ -0,0 +1,34 @@ +package cn.axzo.im.center.common.enums; + +/** + * @author yanglin + */ +public enum ImClientAppType { + + /** + * 工人端 + */ + CM, + /** + * 企业管理端 + */ + CMP; + + public static ImClientAppType fromNimAppType(AppTypeEnum appType) { + if (appType == null) + return null; + if (appType == AppTypeEnum.CM) + return CM; + if (appType == AppTypeEnum.CMP) + return CMP; + throw new UnsupportedOperationException("Should never happen!"); + } + + public AppTypeEnum toNimAppType() { + if (this == CM) + return AppTypeEnum.CM; + if (this == CMP) + return AppTypeEnum.CMP; + throw new UnsupportedOperationException("Should never happen!"); + } +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index c192ce1..1190628 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -5,6 +5,8 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.feign.MessageApi; import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; +import cn.axzo.im.center.api.vo.PersonAccountAttribute; +import cn.axzo.im.center.api.vo.req.AccountAbsentQuery; import cn.axzo.im.center.api.vo.req.AccountQuery; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; @@ -164,35 +166,47 @@ public class MessageController implements MessageApi { @Override @Transactional - public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam sendMessageParam) { - String sendImAccount = check(sendMessageParam); + public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { + PersonAccountAttribute sender = request.getSender(); + String sendImAccount; + if (sender != null) { + AccountAbsentQuery accountQuery = new AccountAbsentQuery(); + accountQuery.setAppType(sender.getAppType().toNimAppType().getCode()); + accountQuery.setPersonId(sender.getPersonId()); + accountQuery.setOuId(sender.getOuId()); + List accounts = accountService.registerAccountIfAbsent(accountQuery); + sendImAccount = accounts.get(0).getImAccount(); + } else { + sendImAccount = check(request); + } MessageTask.BizData bizData = MessageTask.BizData.builder() - .msgTemplateContent(sendMessageParam.getMsgTemplateContent()) - .msgTemplateId(sendMessageParam.getMsgTemplateId()) - .templatedMsgType(sendMessageParam.getTemplatedMsgType()) + .msgTemplateContent(request.getMsgTemplateContent()) + .msgTemplateId(request.getMsgTemplateId()) + .templatedMsgType(request.getTemplatedMsgType()) + .isSenderRobot(request.getSender() == null) .build(); Date now = new Date(); List receivePersons = JSONArray.parseArray( - JSONObject.toJSONString(sendMessageParam.uniqueReceivePersons()), MessageTask.ReceivePerson.class); + JSONObject.toJSONString(request.uniqueReceivePersons()), MessageTask.ReceivePerson.class); MessageTask messageTask = messageTaskService.create(MessageTask.builder() - .bizId(sendMessageParam.getBizId()) + .bizId(request.getBizId()) .sendImAccount(sendImAccount) .receivePersons(receivePersons) .status(MessageTaskStatus.PENDING) - .title(sendMessageParam.getMsgHeader()) - .content(sendMessageParam.getMsgContent()) + .title(request.getMsgHeader()) + .content(request.getMsgContent()) .bizData(bizData) - .ext(sendMessageParam.getExt()) + .ext(request.getExt()) .planStartTime(now) .createAt(now) .sendPriority(SendPriority.TEMPLATE_MESSAGE - .determinePriority(sendMessageParam.getSendPriority())) + .determinePriority(request.getSendPriority())) .apiChannel(ApiChannel.COMMON_MESSAGE) .build()); List updatableMessageSendResults = Collections.emptyList(); - if (sendMessageParam.isUpdatable()) { - updatableMessageSendResults = updatableMessageManager.createUpdatableMessage(messageTask, sendMessageParam, receivePersons); + if (request.isUpdatable()) { + updatableMessageSendResults = updatableMessageManager.createUpdatableMessage(messageTask, request, receivePersons); } MessageTaskResp messageTaskResp = toMessageTaskResp(messageTask); messageTaskResp.setUpdatableMessageSendResults(updatableMessageSendResults); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 4d9a816..0c43005 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -7,6 +7,7 @@ import lombok.Data; */ @Data public class HistoryRecordExt { + private Boolean isSenderRobot; private String sendApi; private String sendRespDesc; private String batchSendId; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java index 3218233..f54cc2f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java @@ -157,6 +157,8 @@ public class MessageTask { */ private List appTypes; + private Boolean isSenderRobot; + public TemplatedMsgType determineTemplatedMsgType() { return templatedMsgType == null ? TemplatedMsgType.TEMPLATE : templatedMsgType; } diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 642bb4f..278559f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -2,7 +2,7 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; -import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.im.center.common.enums.ImClientAppType; import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; @@ -32,7 +32,7 @@ public class UpdatableMessage implements MessageUpdateInfo { private String toAccount; private String receiverPersonId; private Long receiverOuId; - private AppTypeEnum appType; + private ImClientAppType appType; private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index d929f50..3004fdf 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -7,6 +7,7 @@ import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.mapper.MessageTaskMapper; import cn.axzo.im.entity.AccountRegister; +import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; import cn.axzo.im.enums.MessageHistoryStatus; @@ -240,9 +241,13 @@ public class MessageTaskServiceImpl extends ServiceImpl oldValue)); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 350be31..a0777e3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -7,6 +7,7 @@ import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; +import cn.axzo.im.center.common.enums.ImClientAppType; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; @@ -79,12 +80,12 @@ public class UpdatableMessageManager { collector.addMessage(message); message.setBatchNo(batchNo); message.setTemplateId(request.getMsgTemplateId()); - message.setRefTemplateId(request.getUpdatableRefTemplateId()); + message.setRefTemplateId(request.getRefTemplateId()); message.setBizId(request.getBizId()); message.setTaskId(task.getId()); message.setReceiverPersonId(person.getPersonId()); message.setReceiverOuId(person.getOuId()); - message.setAppType(person.getAppType()); + message.setAppType(ImClientAppType.fromNimAppType(person.getAppType())); message.setMsgType(request.getTemplatedMsgType()); message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index b50abed..797ea52 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -69,7 +69,7 @@ public class UpdateSupport { history.setBizId(message.getBizId()); history.setFromAccount(message.getFromAccount()); history.setToAccount(message.getToAccount()); - history.setAppType(message.getAppType().getCode()); + history.setAppType(message.getAppType().toNimAppType().getCode()); history.setChannel(imChannel.getProviderType()); MessageCustomBody messageBody = new MessageCustomBody(); messageBody.setToImAccount(message.getToAccount()); From 2a6451b4a2b8bdbd6128fa91363990a917277a6b Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 11 Nov 2024 09:29:03 +0800 Subject: [PATCH 21/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 4 ++-- .../common/enums/{ImClientAppType.java => ImAppType.java} | 5 +++-- .../src/main/java/cn/axzo/im/entity/UpdatableMessage.java | 4 ++-- .../src/main/java/cn/axzo/im/updatable/InitHistories.java | 4 ++-- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) rename im-center-common/src/main/java/cn/axzo/im/center/common/enums/{ImClientAppType.java => ImAppType.java} (86%) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 9c50a1a..5a9ec2a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -1,6 +1,6 @@ package cn.axzo.im.center.api.vo; -import cn.axzo.im.center.common.enums.ImClientAppType; +import cn.axzo.im.center.common.enums.ImAppType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -41,6 +41,6 @@ public class PersonAccountAttribute { * @See cn.axzo.im.center.common.enums.AppTypeEnum */ @NotNull(message = "appType不能为空") - private ImClientAppType appType; + private ImAppType appType; } diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImAppType.java similarity index 86% rename from im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java rename to im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImAppType.java index e3117a0..4d60a4e 100644 --- a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImClientAppType.java +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/ImAppType.java @@ -3,18 +3,19 @@ package cn.axzo.im.center.common.enums; /** * @author yanglin */ -public enum ImClientAppType { +public enum ImAppType { /** * 工人端 */ CM, + /** * 企业管理端 */ CMP; - public static ImClientAppType fromNimAppType(AppTypeEnum appType) { + public static ImAppType fromNimAppType(AppTypeEnum appType) { if (appType == null) return null; if (appType == AppTypeEnum.CM) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 278559f..82236d3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -2,7 +2,7 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; -import cn.axzo.im.center.common.enums.ImClientAppType; +import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; @@ -32,7 +32,7 @@ public class UpdatableMessage implements MessageUpdateInfo { private String toAccount; private String receiverPersonId; private Long receiverOuId; - private ImClientAppType appType; + private ImAppType appType; private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java index a385634..eb37b08 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java @@ -3,7 +3,7 @@ package cn.axzo.im.updatable; import cn.axzo.basics.common.constant.enums.CodeDefinition; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.AppTypeEnum; -import cn.axzo.im.center.common.enums.ImClientAppType; +import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import lombok.EqualsAndHashCode; @@ -38,7 +38,7 @@ class InitHistories { person.setOuId(history.getReceiveOuId()); AppTypeEnum appType = CodeDefinition.findByCode( AppTypeEnum.class, history.getAppType()).orElse(null); - person.setAppType(ImClientAppType.fromNimAppType(appType)); + person.setAppType(ImAppType.fromNimAppType(appType)); return new HistoryTaskAccount(history.getImMessageTaskId(), person); }, identity(), (oldValue, newValue) -> oldValue)); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index a0777e3..4707fe6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -7,7 +7,7 @@ import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; -import cn.axzo.im.center.common.enums.ImClientAppType; +import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; @@ -85,7 +85,7 @@ public class UpdatableMessageManager { message.setTaskId(task.getId()); message.setReceiverPersonId(person.getPersonId()); message.setReceiverOuId(person.getOuId()); - message.setAppType(ImClientAppType.fromNimAppType(person.getAppType())); + message.setAppType(ImAppType.fromNimAppType(person.getAppType())); message.setMsgType(request.getTemplatedMsgType()); message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); From fba06c750ec012a88109900095eaea43d53ec6c8 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 11 Nov 2024 10:25:17 +0800 Subject: [PATCH 22/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 5 +++++ .../axzo/im/center/api/vo/req/SendTemplateMessageParam.java | 1 - .../main/java/cn/axzo/im/controller/MessageController.java | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 5a9ec2a..d6e6b90 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -33,6 +33,11 @@ public class PersonAccountAttribute { */ private Long ouId; + /** + * 可选, 暂时没用 + */ + private Long workspaceId; + /** * 发送消息到App端 * 工人端、企业端、服务器 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 6997047..5c63692 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -48,7 +48,6 @@ public class SendTemplateMessageParam { /** * 消息模板ID */ - @NotBlank(message = "消息模板ID不能为空") private String msgTemplateId; /** diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 1190628..3c75341 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -35,6 +35,7 @@ import cn.axzo.im.service.MessageTaskService; import cn.axzo.im.service.RobotMsgTemplateService; import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.im.updatable.UpdateSupport; +import cn.axzo.im.utils.BizAssertions; import cn.axzo.pokonyan.exception.Aassert; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -167,6 +168,8 @@ public class MessageController implements MessageApi { @Override @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { + BizAssertions.assertTrue(request.getSender() != null || StringUtils.isNotBlank(request.getMsgTemplateId()), + "消息模板ID和发送人必须选其一"); PersonAccountAttribute sender = request.getSender(); String sendImAccount; if (sender != null) { From 0a2fb2e193d5724c2f579ade1c174fcecf850878 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 11 Nov 2024 10:26:04 +0800 Subject: [PATCH 23/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index d6e6b90..5a9ec2a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -33,11 +33,6 @@ public class PersonAccountAttribute { */ private Long ouId; - /** - * 可选, 暂时没用 - */ - private Long workspaceId; - /** * 发送消息到App端 * 工人端、企业端、服务器 From 364b385172422be2fdbe63f9fe41226a1bc53429 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 11 Nov 2024 11:34:52 +0800 Subject: [PATCH 24/93] REQ-3057-card: a lot of staff --- .../cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 5a9ec2a..38c36c8 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -33,6 +33,11 @@ public class PersonAccountAttribute { */ private Long ouId; + /** + * 项目id, 暂时没用 + */ + private Long workspaceId; + /** * 发送消息到App端 * 工人端、企业端、服务器 From 8ebf927cf6d8e5c1a0a1e9fcd45a82df2f879f30 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 11 Nov 2024 11:38:56 +0800 Subject: [PATCH 25/93] REQ-3057-card: a lot of staff --- .../java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 38c36c8..173b4bf 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -34,7 +34,7 @@ public class PersonAccountAttribute { private Long ouId; /** - * 项目id, 暂时没用 + * 项目id */ private Long workspaceId; From 758a5eb4ba7e7e23d44db7b51131f01ae93e81cd Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 13:41:38 +0800 Subject: [PATCH 26/93] REQ-3057-card: a lot of staff --- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 797ea52..ff402ac 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -75,7 +75,7 @@ public class UpdateSupport { messageBody.setToImAccount(message.getToAccount()); messageBody.setPersonId(message.getReceiverPersonId()); messageBody.setBizType(BizTypeEnum.MESSAGE_UPDATE); - messageBody.setPayload(update.bizBody().toJSONString()); + messageBody.setPayload("{}"); messageBody.setBizMessageId(message.bizMessageId()); messageBody.setDataVersion(messageUpdate.getDataVersion()); messageBody.setInitMessageId(message.getInitNimMessageId()); From 80f6492b56ebc1a8e5636ca1e3a9b5b7574e5004 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 13:46:07 +0800 Subject: [PATCH 27/93] REQ-3057-card: a lot of staff --- .../api/vo/req/FetchUpdatableMessageRequest.java | 6 +++--- .../cn/axzo/im/dao/repository/UpdatableMessageDao.java | 10 ++++++++++ .../main/java/cn/axzo/im/updatable/UpdateSupport.java | 5 +++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index 67d3d5c..d288e57 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -14,9 +14,9 @@ import java.util.Set; public class FetchUpdatableMessageRequest { /** - * 业务消息ID列表 + * 初始消息ID列表(普通消息的网易云信ID) */ - @NotEmpty(message = "业务消息ID不能为空") - private Set bizMessageIds; + @NotEmpty(message = "初始消息ID列表不能为空") + private Set initNimMessageIds; } diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 6f9ca87..6929e48 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -32,11 +32,21 @@ public class UpdatableMessageDao extends ServiceImpl getByBizMessageIds(Collection bizMessageIds) { + if (CollectionUtils.isEmpty(bizMessageIds)) + return Collections.emptyList(); return lambdaQuery() .in(UpdatableMessage::getBizMessageId, bizMessageIds) .list(); } + public List getInitNimMessageIds(Collection initNimMessageIds) { + if (CollectionUtils.isEmpty(initNimMessageIds)) + return Collections.emptyList(); + return lambdaQuery() + .in(UpdatableMessage::getInitNimMessageId, initNimMessageIds) + .list(); + } + public List getByTaskIds(List taskIds) { if (CollectionUtils.isEmpty(taskIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index ff402ac..52d521a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -129,9 +129,10 @@ public class UpdateSupport { public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { int maxSize = props.getFetchUpdatableMessageMaxSize(); - BizAssertions.assertTrue(request.getBizMessageIds().size() < maxSize, + BizAssertions.assertTrue(request.getInitNimMessageIds().size() < maxSize, "超过了最大的消息数量, 最大: {}", maxSize); - List messages = updatableMessageDao.getByBizMessageIds(request.getBizMessageIds()); + List messages = updatableMessageDao + .getInitNimMessageIds(request.getInitNimMessageIds()); FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); for (UpdatableMessage message : messages) { MessageInfo imMessage = new MessageInfo(); From 03f7f4daeae1becf1c560bca6a1fa82263abffd3 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 13:51:30 +0800 Subject: [PATCH 28/93] REQ-3057-card: a lot of staff --- .../axzo/im/center/api/vo/req/SendTemplateMessageParam.java | 2 -- .../main/java/cn/axzo/im/controller/MessageController.java | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 5c63692..783e677 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -36,13 +36,11 @@ public class SendTemplateMessageParam { /** * 消息标题 */ - @NotBlank(message = "消息标题不能为空") private String msgHeader; /** * 消息内容 */ - @NotBlank(message = "消息内容不能为空") private String msgContent; /** diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 3c75341..19045e3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -197,8 +197,8 @@ public class MessageController implements MessageApi { .sendImAccount(sendImAccount) .receivePersons(receivePersons) .status(MessageTaskStatus.PENDING) - .title(request.getMsgHeader()) - .content(request.getMsgContent()) + .title(request.getMsgHeader() == null ? "" : request.getMsgHeader()) + .content(request.getMsgContent() == null ? "" : request.getMsgContent()) .bizData(bizData) .ext(request.getExt()) .planStartTime(now) From 75e905d9d888b5359db5f18c143ecc6f8ee2ec3b Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 14:15:54 +0800 Subject: [PATCH 29/93] REQ-3057-card: a lot of staff --- .../api/vo/req/UpdatableMessageAckRequest.java | 12 ++++++------ .../im/dao/repository/UpdatableMessageDao.java | 13 +++++++++++++ .../axzo/im/updatable/UpdatableMessageManager.java | 14 +++++++------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index 2fa7b64..8e45246 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -31,14 +31,14 @@ public class UpdatableMessageAckRequest { return Collections.emptyList(); HashMap id2Max = new HashMap<>(); for (Acknowledgment ack : acknowledgments) { - Long max = id2Max.getOrDefault(ack.bizMessageId, 0L); + Long max = id2Max.getOrDefault(ack.initNimMessageId, 0L); max = Math.max(max, ack.dataVersion); - id2Max.put(ack.bizMessageId, max); + id2Max.put(ack.initNimMessageId, max); } return id2Max.entrySet().stream() .map(e -> { Acknowledgment ack = new Acknowledgment(); - ack.setBizMessageId(e.getKey()); + ack.setInitNimMessageId(e.getKey()); ack.setDataVersion(e.getValue()); return ack; }) @@ -49,10 +49,10 @@ public class UpdatableMessageAckRequest { @Getter public static class Acknowledgment { /** - * 业务消息ID + * 初始消息ID(普通消息的网易云信ID) */ - @NotBlank(message = "业务消息ID不能为空") - private String bizMessageId; + @NotBlank(message = "初始消息ID不能为空") + private String initNimMessageId; /** * 数据版本 diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 6929e48..96f46da 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -31,6 +31,19 @@ public class UpdatableMessageDao extends ServiceImpl getInitNimMessageIdForUpdate(Collection initNimMessageIds) { + if (CollectionUtils.isEmpty(initNimMessageIds)) + return Collections.emptyList(); + // 避免死锁 + List sortedInitNimMessageIds = initNimMessageIds.stream() + .sorted().collect(toList()); + return lambdaQuery() + .in(UpdatableMessage::getInitNimMessageId, sortedInitNimMessageIds) + // 避免ack更新出错 + .last("FOR UPDATE") + .list(); + } + public List getByBizMessageIds(Collection bizMessageIds) { if (CollectionUtils.isEmpty(bizMessageIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 4707fe6..5fc4e15 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -288,16 +288,16 @@ public class UpdatableMessageManager { } private void ackImpl(List acknowledgments) { - List bizMessageIds = acknowledgments.stream() - .map(Acknowledgment::getBizMessageId) + List initNimMessageIds = acknowledgments.stream() + .map(Acknowledgment::getInitNimMessageId) .collect(toList()); - Map bizMessageId2Ack = acknowledgments.stream() - .collect(toMap(Acknowledgment::getBizMessageId, identity())); - List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); + Map initNimMessageId2Ack = acknowledgments.stream() + .collect(toMap(Acknowledgment::getInitNimMessageId, identity())); + List messages = updatableMessageDao.getInitNimMessageIdForUpdate(initNimMessageIds); ManipulateCollector collector = manipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { - Long dataVersion = bizMessageId2Ack.get(message.bizMessageId()).getDataVersion(); - + Long dataVersion = initNimMessageId2Ack.get(message.getInitNimMessageId()).getDataVersion(); + if (dataVersion == null) dataVersion = -1L; UpdatableMessageLog messageLog = message.toMessageLog(); collector.addLog(messageLog); messageLog.setContext("ack"); From e13ff1ba1a68fbc5960c730c343a536197fe3d1c Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 15:32:29 +0800 Subject: [PATCH 30/93] REQ-3057-card: a lot of staff --- .../axzo/im/updatable/UpdatableMessageManager.java | 12 ++++++------ .../java/cn/axzo/im/updatable/UpdateSupport.java | 10 +++++----- ...teCollector.java => CardManipulateCollector.java} | 2 +- ...tory.java => CardManipulateCollectorFactory.java} | 6 +++--- .../im/updatable/handler/InitMessageHandler.java | 8 ++++---- .../im/updatable/handler/UpdateMessageHandler.java | 8 ++++---- .../updatable/retry/MessageUpdateRetryService.java | 8 ++++---- 7 files changed, 27 insertions(+), 27 deletions(-) rename im-center-server/src/main/java/cn/axzo/im/updatable/collector/{ManipulateCollector.java => CardManipulateCollector.java} (98%) rename im-center-server/src/main/java/cn/axzo/im/updatable/collector/{ManipulateCollectorFactory.java => CardManipulateCollectorFactory.java} (82%) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 5fc4e15..8cdc027 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -19,8 +19,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; -import cn.axzo.im.updatable.collector.ManipulateCollector; -import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; +import cn.axzo.im.updatable.collector.CardManipulateCollector; +import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import cn.axzo.im.updatable.handler.InitMessageHandler; import cn.axzo.im.updatable.handler.UpdateMessageHandler; import cn.axzo.im.updatable.retry.MessageUpdateRetryService; @@ -63,7 +63,7 @@ public class UpdatableMessageManager { private final UpdateMessageHandler updateMessageHandler; private final MessageUpdateRetryService messageUpdateRetryService; private final UpdateSupport updateSupport; - private final ManipulateCollectorFactory manipulateCollectorFactory; + private final CardManipulateCollectorFactory cardManipulateCollectorFactory; private final TransactionTemplate transactionTemplate; private final ImProperties props; @@ -74,7 +74,7 @@ public class UpdatableMessageManager { if (CollectionUtils.isEmpty(receivePersons)) return Collections.emptyList(); String batchNo = UUIDUtil.uuidString(); ArrayList sendResults = new ArrayList<>(); - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (ReceivePerson person : receivePersons) { UpdatableMessage message = new UpdatableMessage(); collector.addMessage(message); @@ -117,7 +117,7 @@ public class UpdatableMessageManager { List messages = updatableMessageDao.getByTaskIds(taskIds); log.info("onHistoryCreated, taskIdSize={}, messageSize={}", taskIds.size(), messages.size()); InitHistories initHistories = new InitHistories(histories); - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { MessageHistory history = initHistories.findHistory(message).orElse(null); if (history == null) continue; @@ -294,7 +294,7 @@ public class UpdatableMessageManager { Map initNimMessageId2Ack = acknowledgments.stream() .collect(toMap(Acknowledgment::getInitNimMessageId, identity())); List messages = updatableMessageDao.getInitNimMessageIdForUpdate(initNimMessageIds); - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { Long dataVersion = initNimMessageId2Ack.get(message.getInitNimMessageId()).getDataVersion(); if (dataVersion == null) dataVersion = -1L; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 52d521a..e1f928d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -16,8 +16,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; -import cn.axzo.im.updatable.collector.ManipulateCollector; -import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; +import cn.axzo.im.updatable.collector.CardManipulateCollector; +import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.UUIDUtil; @@ -44,7 +44,7 @@ public class UpdateSupport { private final UpdatableMessageDao updatableMessageDao; private final IMChannelProvider imChannel; - private final ManipulateCollectorFactory manipulateCollectorFactory; + private final CardManipulateCollectorFactory cardManipulateCollectorFactory; private final ImProperties props; public AddUpdateHistoryResult addUpdateHistories( @@ -55,7 +55,7 @@ public class UpdateSupport { .stream().collect(toMap(UpdatableMessage::getBizMessageId, identity())); String batchNo = UUIDUtil.uuidString(); Map messageLog2History = new IdentityHashMap<>(); - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (MessageUpdateInfo update : updates) { UpdatableMessage message = bizMessageId2Message.get(update.bizMessageId()); if (message == null) continue; @@ -147,7 +147,7 @@ public class UpdateSupport { public void updateHistoryId(AddUpdateHistoryResult result, BiConsumer historyIdSetter) { - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (Long messageId : result.getMessageIds()) { result.findHistoryId(messageId).ifPresent(historyId -> { UpdatableMessage messageUpdate = new UpdatableMessage(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java similarity index 98% rename from im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java index 1afafa4..696d63f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java @@ -19,7 +19,7 @@ import java.util.function.Consumer; * @author yanglin */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class ManipulateCollector { +public class CardManipulateCollector { private static final int BATCH_SIZE = 1000; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollectorFactory.java similarity index 82% rename from im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java rename to im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollectorFactory.java index d932e4e..af38ebc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/ManipulateCollectorFactory.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollectorFactory.java @@ -11,14 +11,14 @@ import org.springframework.stereotype.Component; */ @Component @RequiredArgsConstructor -public class ManipulateCollectorFactory { +public class CardManipulateCollectorFactory { private final UpdatableMessageDao updatableMessageDao; private final UpdatableMessageLogDao updatableMessageLogDao; private final MessageHistoryDao messageHistoryDao; - public ManipulateCollector create() { - return new ManipulateCollector( + public CardManipulateCollector create() { + return new CardManipulateCollector( updatableMessageDao, updatableMessageLogDao, messageHistoryDao); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index e8f93d9..beadbcc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -5,8 +5,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.HistoryAndMessage; -import cn.axzo.im.updatable.collector.ManipulateCollector; -import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; +import cn.axzo.im.updatable.collector.CardManipulateCollector; +import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -19,7 +19,7 @@ import java.util.List; @RequiredArgsConstructor public class InitMessageHandler implements StateHandler { - private final ManipulateCollectorFactory manipulateCollectorFactory; + private final CardManipulateCollectorFactory cardManipulateCollectorFactory; @Override public void onSuccess(List historyAndMessages) { @@ -32,7 +32,7 @@ public class InitMessageHandler implements StateHandler { } private void updateState(List historyAndMessages, boolean isSuccess) { - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessage message = historyAndMessage.getMessage(); MessageHistory history = historyAndMessage.getHistories().get(0); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java index e651f9a..b1e4173 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java @@ -6,8 +6,8 @@ import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.HistoryAndMessage; import cn.axzo.im.updatable.MessageBodyJsonObject; -import cn.axzo.im.updatable.collector.ManipulateCollector; -import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; +import cn.axzo.im.updatable.collector.CardManipulateCollector; +import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -20,7 +20,7 @@ import java.util.List; @RequiredArgsConstructor public class UpdateMessageHandler implements StateHandler { - private final ManipulateCollectorFactory manipulateCollectorFactory; + private final CardManipulateCollectorFactory cardManipulateCollectorFactory; @Override public void onSuccess(List historyAndMessages) { @@ -33,7 +33,7 @@ public class UpdateMessageHandler implements StateHandler { } private void updateState(List historyAndMessages, boolean isSuccess) { - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (HistoryAndMessage historyAndMessage : historyAndMessages) { UpdatableMessageState state = historyAndMessage.getMessage().getState(); UpdatableMessage message = historyAndMessage.getMessage(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 1980f67..18ba4b7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -7,8 +7,8 @@ import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.entity.UpdatableMessageLog; import cn.axzo.im.updatable.AddUpdateHistoryResult; import cn.axzo.im.updatable.UpdateSupport; -import cn.axzo.im.updatable.collector.ManipulateCollector; -import cn.axzo.im.updatable.collector.ManipulateCollectorFactory; +import cn.axzo.im.updatable.collector.CardManipulateCollector; +import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import cn.axzo.im.utils.ImProperties; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; @@ -35,7 +35,7 @@ public class MessageUpdateRetryService { private final UpdatableMessageDao updatableMessageDao; private final UpdateSupport updateSupport; private final TransactionTemplate transactionTemplate; - private final ManipulateCollectorFactory manipulateCollectorFactory; + private final CardManipulateCollectorFactory cardManipulateCollectorFactory; private final ImProperties props; public void removePrevRetryByBizMessageIds(Collection bizMessageIds) { @@ -67,7 +67,7 @@ public class MessageUpdateRetryService { .collect(toList()); removePrevRetryByBizMessageIds(bizMessageIds); ArrayList retries = new ArrayList<>(); - ManipulateCollector collector = manipulateCollectorFactory.create(); + CardManipulateCollector collector = cardManipulateCollectorFactory.create(); Date nextRetryTime = DateTime.now() .plusSeconds(props.getMessageUpdateAckRetryIntervalSeconds()) .toDate(); From 88e73cce354bf8c716c6506c0e979da82b555979 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 16:38:59 +0800 Subject: [PATCH 31/93] REQ-3057-card: a lot of staff --- .../axzo/im/center/api/feign/MessageApi.java | 6 +++ .../vo/req/FetchUpdatableMessageRequest.java | 2 +- .../api/vo/req/GetBizMessageIdsRequest.java | 22 ++++++++ .../api/vo/resp/GetBizMessageIdsResponse.java | 28 ++++++++++ .../axzo/im/controller/MessageController.java | 13 ++++- .../dao/repository/UpdatableMessageDao.java | 31 ++++++----- .../cn/axzo/im/entity/UpdatableMessage.java | 2 +- .../im/updatable/UpdatableMessageManager.java | 8 +-- .../UpdatableMessageQueryService.java | 52 +++++++++++++++++++ .../cn/axzo/im/updatable/UpdateSupport.java | 26 +--------- .../updatable/handler/InitMessageHandler.java | 2 +- 11 files changed, 143 insertions(+), 49 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index 3f98cc5..a18b3ea 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -4,12 +4,14 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; +import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; +import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -72,6 +74,10 @@ public interface MessageApi { ApiResult fetchUpdatableMessage( @RequestBody @Validated FetchUpdatableMessageRequest request); + @PostMapping("/api/im/template-message/updatable/getBizMessageIds") + ApiResult getBizMessageIds( + @RequestBody @Validated GetBizMessageIdsRequest request); + /** * * 接口已经作废,可以使用sendTemplateMessage来替换 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index d288e57..ba166d4 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -17,6 +17,6 @@ public class FetchUpdatableMessageRequest { * 初始消息ID列表(普通消息的网易云信ID) */ @NotEmpty(message = "初始消息ID列表不能为空") - private Set initNimMessageIds; + private Set nimMessageIds; } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java new file mode 100644 index 0000000..3d93ae9 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java @@ -0,0 +1,22 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +/** + * @author yanglin + */ +@Setter +@Getter +public class GetBizMessageIdsRequest { + + /** + * 初始消息ID列表(普通消息的网易云信ID) + */ + @NotEmpty(message = "初始消息ID列表不能为空") + private Set nimMessageIds; + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java new file mode 100644 index 0000000..2a89a7c --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java @@ -0,0 +1,28 @@ +package cn.axzo.im.center.api.vo.resp; + +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author yanglin + */ +@Setter +@Getter +public class GetBizMessageIdsResponse { + + private Map nimMessageId2BizMessageId = new HashMap<>(); + + public void addMessage(String nimMessageId, String bizMessageId) { + nimMessageId2BizMessageId.put(nimMessageId, bizMessageId); + } + + public Set bizMessageIds() { + return new HashSet<>(nimMessageId2BizMessageId.values()); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 19045e3..00f4207 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -11,12 +11,14 @@ import cn.axzo.im.center.api.vo.req.AccountQuery; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; +import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; +import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -34,6 +36,7 @@ import cn.axzo.im.service.MessageHistoryService; import cn.axzo.im.service.MessageTaskService; import cn.axzo.im.service.RobotMsgTemplateService; import cn.axzo.im.updatable.UpdatableMessageManager; +import cn.axzo.im.updatable.UpdatableMessageQueryService; import cn.axzo.im.updatable.UpdateSupport; import cn.axzo.im.utils.BizAssertions; import cn.axzo.pokonyan.exception.Aassert; @@ -92,6 +95,8 @@ public class MessageController implements MessageApi { private UpdatableMessageManager updatableMessageManager; @Autowired private UpdateSupport updateSupport; + @Autowired + private UpdatableMessageQueryService updatableMessageQueryService; @Override @@ -230,10 +235,16 @@ public class MessageController implements MessageApi { @Override public ApiResult fetchUpdatableMessage(FetchUpdatableMessageRequest request) { - FetchUpdatableMessageResponse resp = updateSupport.fetchUpdatableMessage(request); + FetchUpdatableMessageResponse resp = updatableMessageQueryService.fetchUpdatableMessage(request); return ApiResult.ok(resp); } + @Override + public ApiResult getBizMessageIds(GetBizMessageIdsRequest request) { + GetBizMessageIdsResponse response = updatableMessageQueryService.getBizMessageIds(request); + return ApiResult.ok(response); + } + private void check(SendMessageParam sendMessageParam) { List accountRegisters = accountRegisterService.list(AccountRegisterService.ListAccountRegisterParam.builder() .imAccount(sendMessageParam.getSendImAccount()) diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 96f46da..c6de775 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -18,6 +18,18 @@ import static java.util.stream.Collectors.toList; @Repository("updatableMessageDao") public class UpdatableMessageDao extends ServiceImpl { + public List getByNimMessageIdForUpdate(Collection nimMessageIds) { + if (CollectionUtils.isEmpty(nimMessageIds)) + return Collections.emptyList(); + List bizMessageIds = getByNimMessageIds(nimMessageIds) + .stream() + .map(UpdatableMessage::getBizMessageId) + .distinct() + .collect(toList()); + // 统一使用bizMessageIds加锁, 避免死锁 + return getByBizMessageIdsForUpdate(bizMessageIds); + } + public List getByBizMessageIdsForUpdate(Collection bizMessageIds) { if (CollectionUtils.isEmpty(bizMessageIds)) return Collections.emptyList(); @@ -31,19 +43,6 @@ public class UpdatableMessageDao extends ServiceImpl getInitNimMessageIdForUpdate(Collection initNimMessageIds) { - if (CollectionUtils.isEmpty(initNimMessageIds)) - return Collections.emptyList(); - // 避免死锁 - List sortedInitNimMessageIds = initNimMessageIds.stream() - .sorted().collect(toList()); - return lambdaQuery() - .in(UpdatableMessage::getInitNimMessageId, sortedInitNimMessageIds) - // 避免ack更新出错 - .last("FOR UPDATE") - .list(); - } - public List getByBizMessageIds(Collection bizMessageIds) { if (CollectionUtils.isEmpty(bizMessageIds)) return Collections.emptyList(); @@ -52,11 +51,11 @@ public class UpdatableMessageDao extends ServiceImpl getInitNimMessageIds(Collection initNimMessageIds) { - if (CollectionUtils.isEmpty(initNimMessageIds)) + public List getByNimMessageIds(Collection nimMessageIds) { + if (CollectionUtils.isEmpty(nimMessageIds)) return Collections.emptyList(); return lambdaQuery() - .in(UpdatableMessage::getInitNimMessageId, initNimMessageIds) + .in(UpdatableMessage::getNimMessageId, nimMessageIds) .list(); } diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 82236d3..52fc6d3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -36,7 +36,7 @@ public class UpdatableMessage implements MessageUpdateInfo { private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; - private String initNimMessageId; + private String nimMessageId; private Long initHistoryId; private Long updateHistoryId; private Long retryHistoryId; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 8cdc027..5cf9afd 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -288,15 +288,15 @@ public class UpdatableMessageManager { } private void ackImpl(List acknowledgments) { - List initNimMessageIds = acknowledgments.stream() + List nimMessageIds = acknowledgments.stream() .map(Acknowledgment::getInitNimMessageId) .collect(toList()); - Map initNimMessageId2Ack = acknowledgments.stream() + Map nimMessageId2Ack = acknowledgments.stream() .collect(toMap(Acknowledgment::getInitNimMessageId, identity())); - List messages = updatableMessageDao.getInitNimMessageIdForUpdate(initNimMessageIds); + List messages = updatableMessageDao.getByNimMessageIdForUpdate(nimMessageIds); CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { - Long dataVersion = initNimMessageId2Ack.get(message.getInitNimMessageId()).getDataVersion(); + Long dataVersion = nimMessageId2Ack.get(message.getNimMessageId()).getDataVersion(); if (dataVersion == null) dataVersion = -1L; UpdatableMessageLog messageLog = message.toMessageLog(); collector.addLog(messageLog); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java new file mode 100644 index 0000000..a0b0a4a --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -0,0 +1,52 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; +import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; +import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; +import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; +import cn.axzo.im.dao.repository.UpdatableMessageDao; +import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.utils.BizAssertions; +import cn.axzo.im.utils.ImProperties; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +public class UpdatableMessageQueryService { + + private final UpdatableMessageDao updatableMessageDao; + private final ImProperties props; + + public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { + int maxSize = props.getFetchUpdatableMessageMaxSize(); + BizAssertions.assertTrue(request.getNimMessageIds().size() < maxSize, + "超过了最大的消息数量, 最大: {}", maxSize); + List messages = updatableMessageDao + .getByNimMessageIds(request.getNimMessageIds()); + FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); + for (UpdatableMessage message : messages) { + FetchUpdatableMessageResponse.MessageInfo imMessage = new FetchUpdatableMessageResponse.MessageInfo(); + imMessage.setMsgType(message.getMsgType().getCode()); + imMessage.setMsgBody(message.getBizBody().toJSONString()); + imMessage.setBizMessageId(message.getBizMessageId()); + imMessage.setDataVersion(message.getDataVersion()); + response.addMessage(imMessage); + } + return response; + } + + public GetBizMessageIdsResponse getBizMessageIds(GetBizMessageIdsRequest request) { + List messages = updatableMessageDao.getByNimMessageIds(request.getNimMessageIds()); + GetBizMessageIdsResponse response = new GetBizMessageIdsResponse(); + for (UpdatableMessage message : messages) + response.addMessage(message.getNimMessageId(), message.getBizMessageId()); + return response; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index e1f928d..6f82bb0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -2,10 +2,7 @@ package cn.axzo.im.updatable; import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; -import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; -import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; -import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse.MessageInfo; import cn.axzo.im.center.common.enums.BizTypeEnum; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageCustomBody; @@ -18,8 +15,6 @@ import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.collector.CardManipulateCollector; import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; -import cn.axzo.im.utils.BizAssertions; -import cn.axzo.im.utils.ImProperties; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; @@ -45,7 +40,6 @@ public class UpdateSupport { private final UpdatableMessageDao updatableMessageDao; private final IMChannelProvider imChannel; private final CardManipulateCollectorFactory cardManipulateCollectorFactory; - private final ImProperties props; public AddUpdateHistoryResult addUpdateHistories( String context, List updates) { @@ -78,7 +72,7 @@ public class UpdateSupport { messageBody.setPayload("{}"); messageBody.setBizMessageId(message.bizMessageId()); messageBody.setDataVersion(messageUpdate.getDataVersion()); - messageBody.setInitMessageId(message.getInitNimMessageId()); + messageBody.setInitMessageId(message.getNimMessageId()); messageBody.setMsgType(message.getMsgType()); history.setMessageBody(JSON.toJSONString(messageBody)); messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); @@ -127,24 +121,6 @@ public class UpdateSupport { return result; } - public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { - int maxSize = props.getFetchUpdatableMessageMaxSize(); - BizAssertions.assertTrue(request.getInitNimMessageIds().size() < maxSize, - "超过了最大的消息数量, 最大: {}", maxSize); - List messages = updatableMessageDao - .getInitNimMessageIds(request.getInitNimMessageIds()); - FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); - for (UpdatableMessage message : messages) { - MessageInfo imMessage = new MessageInfo(); - imMessage.setMsgType(message.getMsgType().getCode()); - imMessage.setMsgBody(message.getBizBody().toJSONString()); - imMessage.setBizMessageId(message.getBizMessageId()); - imMessage.setDataVersion(message.getDataVersion()); - response.addMessage(imMessage); - } - return response; - } - public void updateHistoryId(AddUpdateHistoryResult result, BiConsumer historyIdSetter) { CardManipulateCollector collector = cardManipulateCollectorFactory.create(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index beadbcc..3805f2f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -41,7 +41,7 @@ public class InitMessageHandler implements StateHandler { collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); if (isSuccess) - messageUpdate.setInitNimMessageId(history.getMessageId()); + messageUpdate.setNimMessageId(history.getMessageId()); messageUpdate.setState(isSuccess ? UpdatableMessageState.INIT_MESSAGE_SEND_SUCCESS : UpdatableMessageState.INIT_MESSAGE_SEND_FAIL); From 0741a9d1f1710a2ea5afb4fc724e96ad4f99787f Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 16:40:19 +0800 Subject: [PATCH 32/93] REQ-3057-card: a lot of staff --- .../im/center/api/vo/req/UpdatableMessageAckRequest.java | 8 ++++---- .../cn/axzo/im/updatable/UpdatableMessageManager.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index 8e45246..be5dfab 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -31,14 +31,14 @@ public class UpdatableMessageAckRequest { return Collections.emptyList(); HashMap id2Max = new HashMap<>(); for (Acknowledgment ack : acknowledgments) { - Long max = id2Max.getOrDefault(ack.initNimMessageId, 0L); + Long max = id2Max.getOrDefault(ack.nimMessageId, 0L); max = Math.max(max, ack.dataVersion); - id2Max.put(ack.initNimMessageId, max); + id2Max.put(ack.nimMessageId, max); } return id2Max.entrySet().stream() .map(e -> { Acknowledgment ack = new Acknowledgment(); - ack.setInitNimMessageId(e.getKey()); + ack.setNimMessageId(e.getKey()); ack.setDataVersion(e.getValue()); return ack; }) @@ -52,7 +52,7 @@ public class UpdatableMessageAckRequest { * 初始消息ID(普通消息的网易云信ID) */ @NotBlank(message = "初始消息ID不能为空") - private String initNimMessageId; + private String nimMessageId; /** * 数据版本 diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 5cf9afd..fa55acb 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -289,10 +289,10 @@ public class UpdatableMessageManager { private void ackImpl(List acknowledgments) { List nimMessageIds = acknowledgments.stream() - .map(Acknowledgment::getInitNimMessageId) + .map(Acknowledgment::getNimMessageId) .collect(toList()); Map nimMessageId2Ack = acknowledgments.stream() - .collect(toMap(Acknowledgment::getInitNimMessageId, identity())); + .collect(toMap(Acknowledgment::getNimMessageId, identity())); List messages = updatableMessageDao.getByNimMessageIdForUpdate(nimMessageIds); CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { From 9a1aa889a782bf61d858767e82633a9773476a2d Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 12 Nov 2024 18:48:00 +0800 Subject: [PATCH 33/93] REQ-3057-card: a lot of staff --- .../axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index be5dfab..b5c6ac3 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -21,7 +21,7 @@ import static java.util.stream.Collectors.toList; public class UpdatableMessageAckRequest { /** - * ack的消息信息列表, 同一个bizMessageId传需要ack的最大dataVersion. 每次最多值100条 + * ack的消息信息列表, 同一个nimMessageId传需要ack的最大dataVersion. 每次最多值100条 */ @NotEmpty(message = "消息ACK不能为空") private List acknowledgments; From f9afb908ed7292dfdd47dba36709a1bdb2663627 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 09:39:55 +0800 Subject: [PATCH 34/93] =?UTF-8?q?REQ-3057-card:=20=E9=80=9A=E8=BF=87http?= =?UTF-8?q?=E8=A7=A6=E5=8F=91job?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-center-api/pom.xml | 4 +++ im-center-server/pom.xml | 6 ++++ .../java/cn/axzo/im/job/JobHttpHandler.java | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 im-center-server/src/main/java/cn/axzo/im/job/JobHttpHandler.java diff --git a/im-center-api/pom.xml b/im-center-api/pom.xml index 415ad9d..ce36ab5 100644 --- a/im-center-api/pom.xml +++ b/im-center-api/pom.xml @@ -48,6 +48,10 @@ cn.axzo.apollo apollo-api + + cn.axzo.pluto + pluto-api + diff --git a/im-center-server/pom.xml b/im-center-server/pom.xml index cf7e363..768fb25 100644 --- a/im-center-server/pom.xml +++ b/im-center-server/pom.xml @@ -109,6 +109,12 @@ cn.axzo.maokai maokai-api + + + cn.axzo.pluto + pluto-api + + diff --git a/im-center-server/src/main/java/cn/axzo/im/job/JobHttpHandler.java b/im-center-server/src/main/java/cn/axzo/im/job/JobHttpHandler.java new file mode 100644 index 0000000..608e986 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/job/JobHttpHandler.java @@ -0,0 +1,28 @@ +package cn.axzo.im.job; + +import com.alibaba.fastjson.JSONObject; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.executor.XxlJobExecutor; +import com.xxl.job.core.handler.IJobHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author yanglin + */ +@RestController +public class JobHttpHandler { + + @PostMapping("/jobs/{jobName}") + ReturnT exec(@PathVariable String jobName, + @RequestBody(required = false) JSONObject paramObj) throws Exception { + IJobHandler jobHandler = XxlJobExecutor.loadJobHandler(jobName); + if (jobHandler == null) + return new ReturnT<>(ReturnT.FAIL_CODE, String.format("找不到job: %s", jobName)); + jobHandler.execute(paramObj == null? null : paramObj.toJSONString()); + return ReturnT.SUCCESS; + } + +} \ No newline at end of file From 3cea2f4fb0ea10c95748d074bd2199cc343b5cb4 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 11:36:16 +0800 Subject: [PATCH 35/93] REQ-3057-card: backup --- .../updatable/collector/CardManipulateCollector.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java index 696d63f..4e01d89 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java @@ -12,6 +12,7 @@ import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -34,15 +35,15 @@ public class CardManipulateCollector { private final State messageHistoriesToUpdate = new State<>(); public List getUpdatableMessagesToAdd() { - return updatableMessagesToAdd.elements; + return updatableMessagesToAdd.elementsOrEmpty(); } public List getUpdatableMessageLogsToAdd() { - return updatableMessageLogsToAdd.elements; + return updatableMessageLogsToAdd.elementsOrEmpty(); } public List getMessageHistoriesToAdd() { - return messageHistoriesToAdd.elements; + return messageHistoriesToAdd.elementsOrEmpty(); } public void addMessage(UpdatableMessage message) { @@ -87,6 +88,10 @@ public class CardManipulateCollector { elements.add(element); } + List elementsOrEmpty() { + return elements == null ? Collections.emptyList() : elements; + } + void batched(Consumer> action) { if (CollectionUtils.isEmpty(elements)) return; From cb0d17b8325cf7b0aeb0a1581e8b1ae200b25e66 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 13:34:18 +0800 Subject: [PATCH 36/93] REQ-3057-card: backup --- .../cn/axzo/im/updatable/UpdatableMessageManager.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index fa55acb..fbcfbc6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -91,16 +91,15 @@ public class UpdatableMessageManager { message.setBizMessageId(UUIDUtil.uuidString()); message.setDataVersion(1L); + UpdatableMessageLog messageLog = message.toMessageLog(); + collector.addLog(messageLog); + messageLog.setContext("scheduleInTask"); + UpdatableMessageSendResult sendResult = new UpdatableMessageSendResult(); sendResults.add(sendResult); sendResult.setBizMessageId(message.bizMessageId()); sendResult.setAccount(message.parsePersonAccount()); } - for (UpdatableMessage message : collector.getUpdatableMessagesToAdd()) { - UpdatableMessageLog messageLog = message.toMessageLog(); - collector.addLog(messageLog); - messageLog.setContext("scheduleInTask"); - } collector.finish(); return sendResults; } From 9f38bebd574090e2aa835e2a7b38eabef3437b32 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 16:20:06 +0800 Subject: [PATCH 37/93] REQ-3057-card: backup --- .../axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index ba166d4..3de918b 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -14,7 +14,7 @@ import java.util.Set; public class FetchUpdatableMessageRequest { /** - * 初始消息ID列表(普通消息的网易云信ID) + * 初始消息ID列表(普通消息的网易云信ID). 列表最大数量: 500 */ @NotEmpty(message = "初始消息ID列表不能为空") private Set nimMessageIds; From 6a624dac9554eb0a514c00430d5de3148217214e Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 16:22:32 +0800 Subject: [PATCH 38/93] REQ-3057-card: backup --- .../java/cn/axzo/im/updatable/UpdatableMessageQueryService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index a0b0a4a..f6b54ec 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -25,7 +25,7 @@ public class UpdatableMessageQueryService { public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { int maxSize = props.getFetchUpdatableMessageMaxSize(); - BizAssertions.assertTrue(request.getNimMessageIds().size() < maxSize, + BizAssertions.assertTrue(request.getNimMessageIds().size() <= maxSize, "超过了最大的消息数量, 最大: {}", maxSize); List messages = updatableMessageDao .getByNimMessageIds(request.getNimMessageIds()); From 79f9687d6bea569f15c9a3eb72732e576500b504 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 13 Nov 2024 16:37:31 +0800 Subject: [PATCH 39/93] REQ-3057-card: backup --- .../cn/axzo/im/entity/UpdatableMessageLog.java | 6 ++++++ .../im/updatable/UpdatableMessageManager.java | 16 +++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 1002c0e..dddfa48 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -43,6 +43,12 @@ public class UpdatableMessageLog { @Setter @Getter public static class RecordExt { + private AckInfo ack; + } + + @Setter + @Getter + public static class AckInfo { private Boolean ackSuccess; private String ackDescription; private Long requestDataVersion; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index fbcfbc6..10ff53f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -315,20 +315,22 @@ public class UpdatableMessageManager { ? dataVersion : message.getAckDataVersion(); messageUpdate.setAckDataVersion(ackDataVersion); + UpdatableMessageLog.AckInfo ackInfo = new UpdatableMessageLog.AckInfo(); + logExt.setAck(ackInfo); if (!dataVersion.equals(message.getDataVersion())) { - logExt.setAckSuccess(false); - logExt.setAckDescription("数据版本不匹配"); - logExt.setRequestDataVersion(dataVersion); - logExt.setMessageDataVersion(message.getDataVersion()); + ackInfo.setAckSuccess(false); + ackInfo.setAckDescription("数据版本不匹配"); + ackInfo.setRequestDataVersion(dataVersion); + ackInfo.setMessageDataVersion(message.getDataVersion()); continue; } // 避免前端有bug if (!message.getState().isUpdateAckAllowed()) { - logExt.setAckSuccess(false); - logExt.setAckDescription("消息状态不允许ack"); + ackInfo.setAckSuccess(false); + ackInfo.setAckDescription("消息状态不允许ack"); continue; } - logExt.setAckSuccess(true); + ackInfo.setAckSuccess(true); messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); } collector.finish(); From 081c63a75515bcf768e7bfd61ea63fc61ac1e15c Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 09:24:43 +0800 Subject: [PATCH 40/93] =?UTF-8?q?REQ-3057-card:=20=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=B6=88=E6=81=AF=E7=9A=84=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/updatable/UpdatableMessageQueryService.java | 2 +- .../main/java/cn/axzo/im/utils/ImProperties.java | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index f6b54ec..6da20b7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -24,7 +24,7 @@ public class UpdatableMessageQueryService { private final ImProperties props; public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { - int maxSize = props.getFetchUpdatableMessageMaxSize(); + int maxSize = props.getUpdatable().getFetchMaxSize(); BizAssertions.assertTrue(request.getNimMessageIds().size() <= maxSize, "超过了最大的消息数量, 最大: {}", maxSize); List messages = updatableMessageDao diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index 169c490..bd4f909 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -3,6 +3,8 @@ package cn.axzo.im.utils; import cn.axzo.basics.common.BeanMapper; import com.google.common.collect.Sets; import lombok.Data; +import lombok.Getter; +import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.Configuration; @@ -30,12 +32,12 @@ public class ImProperties { private int messageUpdateAckRetryIntervalSeconds = 10; - private int fetchUpdatableMessageMaxSize = 500; - private int updatableMessageMaxLockRecords = 5; private SendMessageConfig sendMessage = new SendMessageConfig(); + private UpdatableMessageConfig updatable = new UpdatableMessageConfig(); + private Set genImAccountJobCodes = Sets.newHashSet( // 企业班组长, 项目班组长 "entTeamLeader", "projTeamLeader", @@ -46,6 +48,14 @@ public class ImProperties { // 带班长 "projectTeamManager"); + @Setter + @Getter + public static class UpdatableMessageConfig { + + private int fetchMaxSize = 500; + + } + @Data public static class SendMessageConfig { private int memoryQueueQueryLimitSize = 1000; From 357794a150dc4cb276b2e4f51f7124e32c1b3258 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 13:40:20 +0800 Subject: [PATCH 41/93] REQ-3057-card: backup --- .../axzo/im/updatable/collector/CardManipulateCollector.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java index 4e01d89..c6f9ee7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java @@ -34,10 +34,6 @@ public class CardManipulateCollector { private final State messageHistoriesToAdd = new State<>(); private final State messageHistoriesToUpdate = new State<>(); - public List getUpdatableMessagesToAdd() { - return updatableMessagesToAdd.elementsOrEmpty(); - } - public List getUpdatableMessageLogsToAdd() { return updatableMessageLogsToAdd.elementsOrEmpty(); } From cbc78ffbe1ae84c14a1c1f8596d1bb0bc29eb217 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 13:44:33 +0800 Subject: [PATCH 42/93] REQ-3057-card: backup --- .../src/main/java/cn/axzo/im/updatable/InitHistories.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java index eb37b08..d45121c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java @@ -51,9 +51,9 @@ class InitHistories { @Setter @Getter - @EqualsAndHashCode - // IMPORTANT: 不要删除这个注解 @RequiredArgsConstructor + // IMPORTANT: 不要删除这个注解, 避免@Data被@Setter, @Getter取代 + @EqualsAndHashCode private static class HistoryTaskAccount { final Long taskId; final PersonAccountAttribute person; From da3638f7dcd051dd04282480f9ce1550b301ac99 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 13:44:58 +0800 Subject: [PATCH 43/93] REQ-3057-card: backup --- .../src/main/java/cn/axzo/im/updatable/InitHistories.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java index d45121c..d49a0d9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java @@ -52,7 +52,7 @@ class InitHistories { @Setter @Getter @RequiredArgsConstructor - // IMPORTANT: 不要删除这个注解, 避免@Data被@Setter, @Getter取代 + // IMPORTANT: 不要删除这个注解 @EqualsAndHashCode private static class HistoryTaskAccount { final Long taskId; From 6bb8123daa0160880a1f8adcc04d74d638906b5b Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 13:52:23 +0800 Subject: [PATCH 44/93] REQ-3057-card: backup --- .../im/center/api/vo/req/FetchUpdatableMessageRequest.java | 5 +++++ .../axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java | 5 +++++ .../axzo/im/center/api/vo/req/SendTemplateMessageParam.java | 5 +++++ .../im/center/api/vo/req/UpdatableMessageAckRequest.java | 6 ++++++ .../cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java | 5 +++++ .../center/api/vo/resp/FetchUpdatableMessageResponse.java | 6 ++++++ .../im/center/api/vo/resp/GetBizMessageIdsResponse.java | 5 +++++ .../java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java | 6 ++++++ .../main/java/cn/axzo/im/controller/MessageController.java | 5 +++++ 9 files changed, 48 insertions(+) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index 3de918b..2a9be61 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.req; +import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -19,4 +20,8 @@ public class FetchUpdatableMessageRequest { @NotEmpty(message = "初始消息ID列表不能为空") private Set nimMessageIds; + @Override + public String toString() { + return JSON.toJSONString(this); + } } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java index 3d93ae9..024599a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.req; +import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -19,4 +20,8 @@ public class GetBizMessageIdsRequest { @NotEmpty(message = "初始消息ID列表不能为空") private Set nimMessageIds; + @Override + public String toString() { + return JSON.toJSONString(this); + } } \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 783e677..0945efe 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -2,6 +2,7 @@ package cn.axzo.im.center.api.vo.req; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.TemplatedMsgType; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.AllArgsConstructor; import lombok.Builder; @@ -78,4 +79,8 @@ public class SendTemplateMessageParam { return new HashSet<>(receivePersons); } + @Override + public String toString() { + return JSON.toJSONString(this); + } } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index b5c6ac3..42e65a9 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.req; +import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; @@ -45,6 +46,11 @@ public class UpdatableMessageAckRequest { .collect(toList()); } + @Override + public String toString() { + return JSON.toJSONString(this); + } + @Setter @Getter public static class Acknowledgment { diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index 9cf4b62..27b672e 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -29,6 +29,11 @@ public class UpdateMessageRequest { return MessageUpdateInfo.collectBizMessageIds(updates); } + @Override + public String toString() { + return JSON.toJSONString(this); + } + @Setter @Getter public static class Update implements MessageUpdateInfo { diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index 69af6af..9aab45d 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.resp; +import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -22,6 +23,11 @@ public class FetchUpdatableMessageResponse { messages.add(message); } + @Override + public String toString() { + return JSON.toJSONString(this); + } + @Setter @Getter public static class MessageInfo { diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java index 2a89a7c..48a36d7 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.resp; +import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -25,4 +26,8 @@ public class GetBizMessageIdsResponse { return new HashSet<>(nimMessageId2BizMessageId.values()); } + @Override + public String toString() { + return JSON.toJSONString(this); + } } \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java index 80cd969..d23da86 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/MessageTaskResp.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.resp; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import lombok.AllArgsConstructor; @@ -56,4 +57,9 @@ public class MessageTaskResp { private Date updateAt; private List updatableMessageSendResults; + + @Override + public String toString() { + return JSON.toJSONString(this); + } } diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 00f4207..6e65c82 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -173,6 +173,7 @@ public class MessageController implements MessageApi { @Override @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { + log.info("sendTemplateMessageAsync, request={}", request); BizAssertions.assertTrue(request.getSender() != null || StringUtils.isNotBlank(request.getMsgTemplateId()), "消息模板ID和发送人必须选其一"); PersonAccountAttribute sender = request.getSender(); @@ -223,24 +224,28 @@ public class MessageController implements MessageApi { @Override public ApiResult updateMessage(UpdateMessageRequest request) { + log.info("updateMessage, request={}", request); MessageUpdateResponse resp = updatableMessageManager.updateMessage(request); return ApiResult.ok(resp); } @Override public ApiResult ack(UpdatableMessageAckRequest request) { + log.info("ack, request={}", request); updatableMessageManager.ack(request); return ApiResult.ok(); } @Override public ApiResult fetchUpdatableMessage(FetchUpdatableMessageRequest request) { + log.info("fetchUpdatableMessage, request={}", request); FetchUpdatableMessageResponse resp = updatableMessageQueryService.fetchUpdatableMessage(request); return ApiResult.ok(resp); } @Override public ApiResult getBizMessageIds(GetBizMessageIdsRequest request) { + log.info("getBizMessageIds, request={}", request); GetBizMessageIdsResponse response = updatableMessageQueryService.getBizMessageIds(request); return ApiResult.ok(response); } From 76eedce01ec8bf1721f508a0634a3596daa79bb9 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 14 Nov 2024 16:25:40 +0800 Subject: [PATCH 45/93] =?UTF-8?q?REQ-3057-card-2:=20=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/vo/req/SendTemplateMessageParam.java | 5 ++++ .../axzo/im/controller/MessageController.java | 25 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 0945efe..f91bccc 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -22,6 +22,11 @@ import java.util.Set; @AllArgsConstructor public class SendTemplateMessageParam { + /** + * 指定发送机器人 + */ + private String sendRobotAccount; + /** * 发送人 */ diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 6e65c82..fad8da1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -60,6 +60,7 @@ import org.springframework.web.bind.annotation.RestController; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Optional; import static cn.axzo.im.config.BizResultCode.ALL_PERSSON_TYPE_NOT_EMPTY; import static cn.axzo.im.config.BizResultCode.SEND_IM_ACCOUNT_MAX; @@ -176,13 +177,17 @@ public class MessageController implements MessageApi { log.info("sendTemplateMessageAsync, request={}", request); BizAssertions.assertTrue(request.getSender() != null || StringUtils.isNotBlank(request.getMsgTemplateId()), "消息模板ID和发送人必须选其一"); - PersonAccountAttribute sender = request.getSender(); String sendImAccount; - if (sender != null) { + if (StringUtils.isNotBlank(request.getSendRobotAccount())) { + UserAccountResp robotAccount = findRobotAccount(request.getSendRobotAccount()).orElse(null); + BizAssertions.assertNotNull(robotAccount, String.format( + "找不到指定的机器人账号: %s", request.getSendRobotAccount())); + sendImAccount = request.getSendRobotAccount(); + } else if (request.getSender() != null) { AccountAbsentQuery accountQuery = new AccountAbsentQuery(); - accountQuery.setAppType(sender.getAppType().toNimAppType().getCode()); - accountQuery.setPersonId(sender.getPersonId()); - accountQuery.setOuId(sender.getOuId()); + accountQuery.setAppType(request.getSender().getAppType().toNimAppType().getCode()); + accountQuery.setPersonId(request.getSender().getPersonId()); + accountQuery.setOuId(request.getSender().getOuId()); List accounts = accountService.registerAccountIfAbsent(accountQuery); sendImAccount = accounts.get(0).getImAccount(); } else { @@ -305,6 +310,16 @@ public class MessageController implements MessageApi { return robotImAccount; } + private Optional findRobotAccount(String robotId) { + AccountQuery accountQuery = new AccountQuery(); + accountQuery.setAccountId(robotId); + accountQuery.setAppType(AppTypeEnum.SYSTEM.getCode()); + List accounts = accountService.queryAccountInfo(accountQuery); + if (CollectionUtils.isEmpty(accounts)) + return Optional.empty(); + return Optional.of(accounts.get(0)); + } + public MessageTaskResp toMessageTaskResp(MessageTask messageTask) { MessageTaskResp messageTaskResp = MessageTaskResp.builder().build(); BeanUtils.copyProperties(messageTask, messageTaskResp); From d06767562a1d9e07352d635fb0789db80bc308c3 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 15 Nov 2024 13:44:56 +0800 Subject: [PATCH 46/93] REQ-3057-card: backup --- .../axzo/im/center/api/feign/MessageApi.java | 3 +- .../api/vo/req/SendTemplateMessageParam.java | 21 ++++++++++---- .../axzo/im/center/common/enums/YesOrNo.java | 23 +++++++++++++++ .../im/channel/netease/dto/MessageBody.java | 4 +++ .../netease/dto/MessageCustomBody.java | 4 +++ .../cn/axzo/im/channel/netease/dto/Peer.java | 28 +++++++++++++++++++ .../axzo/im/controller/MessageController.java | 20 ++++++------- .../java/cn/axzo/im/entity/MessageTask.java | 9 ++++++ .../cn/axzo/im/entity/UpdatableMessage.java | 19 ++++++++++++- .../service/impl/MessageTaskServiceImpl.java | 7 ++++- .../im/updatable/UpdatableMessageManager.java | 5 +++- .../cn/axzo/im/updatable/UpdateSupport.java | 8 ++++++ 12 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 im-center-common/src/main/java/cn/axzo/im/center/common/enums/YesOrNo.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/Peer.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index a18b3ea..eff2f25 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -59,7 +59,8 @@ public interface MessageApi { * @return */ @PostMapping("/api/im/template-message/async/send") - ApiResult sendTemplateMessageAsync(@RequestBody @Validated SendTemplateMessageParam sendMessageParam); + ApiResult sendTemplateMessageAsync( + @RequestBody @Validated SendTemplateMessageParam sendMessageParam); /** * 更新消息 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index f91bccc..44da93e 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -22,11 +22,6 @@ import java.util.Set; @AllArgsConstructor public class SendTemplateMessageParam { - /** - * 指定发送机器人 - */ - private String sendRobotAccount; - /** * 发送人 */ @@ -74,7 +69,21 @@ public class SendTemplateMessageParam { private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE; - private String refTemplateId; + public boolean isSendByRobot() { + return sender == null; + } + + public Long determineSenderPersonId() { + if (sender != null) + return Long.parseLong(sender.getPersonId()); + return 0L; + } + + public Long determineSenderOuId() { + if (sender != null) + return sender.getOuId(); + return 0L; + } public boolean isUpdatable() { return templatedMsgType.isUpdatable(); diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/YesOrNo.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/YesOrNo.java new file mode 100644 index 0000000..7b836b5 --- /dev/null +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/YesOrNo.java @@ -0,0 +1,23 @@ +package cn.axzo.im.center.common.enums; + +import cn.axzo.basics.common.constant.enums.CodeDefinition; +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author yanglin + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum YesOrNo implements CodeDefinition { + YES("YES", "是"), + NO("NO", "是") + ; + + @EnumValue + private final String code; + private final String desc; + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java index 22122b2..a0ce53e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java @@ -47,4 +47,8 @@ public class MessageBody { */ private Long dataVersion; + /** + * 端信息 + */ + private Peer peer; } diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java index 04d1d82..1800523 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java @@ -58,4 +58,8 @@ public class MessageCustomBody { */ private TemplatedMsgType msgType; + /** + * 端信息 + */ + private Peer peer; } diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/Peer.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/Peer.java new file mode 100644 index 0000000..d8fefaa --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/Peer.java @@ -0,0 +1,28 @@ +package cn.axzo.im.channel.netease.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class Peer { + + /** + * IM是否为机器人发送 + */ + private boolean isSenderRobot; + + /** + * IM发送者自然人id, 机器人发送时为0 + */ + private Long senderPersonId; + + /** + * IM接收者自然人id + */ + private Long receiverPersonId; + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index fad8da1..91f659d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -175,22 +175,19 @@ public class MessageController implements MessageApi { @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { log.info("sendTemplateMessageAsync, request={}", request); - BizAssertions.assertTrue(request.getSender() != null || StringUtils.isNotBlank(request.getMsgTemplateId()), + PersonAccountAttribute sender = request.getSender(); + BizAssertions.assertTrue(sender != null || StringUtils.isNotBlank(request.getMsgTemplateId()), "消息模板ID和发送人必须选其一"); String sendImAccount; - if (StringUtils.isNotBlank(request.getSendRobotAccount())) { - UserAccountResp robotAccount = findRobotAccount(request.getSendRobotAccount()).orElse(null); - BizAssertions.assertNotNull(robotAccount, String.format( - "找不到指定的机器人账号: %s", request.getSendRobotAccount())); - sendImAccount = request.getSendRobotAccount(); - } else if (request.getSender() != null) { + if (sender != null) { AccountAbsentQuery accountQuery = new AccountAbsentQuery(); - accountQuery.setAppType(request.getSender().getAppType().toNimAppType().getCode()); - accountQuery.setPersonId(request.getSender().getPersonId()); - accountQuery.setOuId(request.getSender().getOuId()); + accountQuery.setAppType(sender.getAppType().toNimAppType().getCode()); + accountQuery.setPersonId(sender.getPersonId()); + accountQuery.setOuId(sender.getOuId()); List accounts = accountService.registerAccountIfAbsent(accountQuery); sendImAccount = accounts.get(0).getImAccount(); } else { + //todo: 根据类型验证模版是否配置了机器人 sendImAccount = check(request); } @@ -198,7 +195,8 @@ public class MessageController implements MessageApi { .msgTemplateContent(request.getMsgTemplateContent()) .msgTemplateId(request.getMsgTemplateId()) .templatedMsgType(request.getTemplatedMsgType()) - .isSenderRobot(request.getSender() == null) + .isSenderRobot(request.isSendByRobot()) + .senderPersonId(request.determineSenderPersonId()) .build(); Date now = new Date(); List receivePersons = JSONArray.parseArray( diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java index f54cc2f..77b8257 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java @@ -24,6 +24,7 @@ import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import lombok.experimental.SuperBuilder; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.cglib.beans.BeanMap; import java.util.Date; @@ -157,6 +158,8 @@ public class MessageTask { */ private List appTypes; + private Long senderPersonId = 0L; + private Boolean isSenderRobot; public TemplatedMsgType determineTemplatedMsgType() { @@ -198,6 +201,12 @@ public class MessageTask { private Long workspaceId; + public Long personIdAsLong() { + if (NumberUtils.isDigits(personId)) + return Long.parseLong(personId); + return 0L; + } + public String buildKey(Map ouIdMap) { if (StringUtils.isNotBlank(this.getImAccount())) { return this.getImAccount(); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 52fc6d3..8958431 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -4,6 +4,7 @@ import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.center.common.enums.TemplatedMsgType; +import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.enums.UpdatableMessageState; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.annotation.TableField; @@ -11,6 +12,8 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import java.util.Date; @@ -25,13 +28,15 @@ public class UpdatableMessage implements MessageUpdateInfo { private Long id; private String batchNo; private String templateId; - private String refTemplateId; private String bizId; private Long taskId; private String fromAccount; private String toAccount; private String receiverPersonId; private Long receiverOuId; + private String senderPersonId; + private Long senderOuId; + private YesOrNo isSenderRobot; private ImAppType appType; private TemplatedMsgType msgType; private UpdatableMessageState state; @@ -78,6 +83,18 @@ public class UpdatableMessage implements MessageUpdateInfo { return person; } + public Long senderPersonIdAsLong() { + if (NumberUtils.isDigits(senderPersonId)) + return NumberUtils.toLong(senderPersonId); + return 0L; + } + + public Long receiverPersonIdAsLong() { + if (NumberUtils.isDigits(receiverPersonId)) + return NumberUtils.toLong(receiverPersonId); + return 0L; + } + @Override public String bizMessageId() { return bizMessageId; diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 3004fdf..58e87ef 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -5,9 +5,9 @@ import cn.axzo.im.center.common.enums.AccountTypeEnum; import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageBody; +import cn.axzo.im.channel.netease.dto.Peer; import cn.axzo.im.dao.mapper.MessageTaskMapper; import cn.axzo.im.entity.AccountRegister; -import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.MessageTask; import cn.axzo.im.enums.MessageHistoryStatus; @@ -342,6 +342,11 @@ public class MessageTaskServiceImpl extends ServiceImpl defaultExtMap = Maps.newHashMap(); if (StringUtils.isNotBlank(bizData.getMsgTemplateContent())) { diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 10ff53f..64eeb5a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -8,6 +8,7 @@ import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; import cn.axzo.im.center.common.enums.ImAppType; +import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.repository.MessageHistoryDao; import cn.axzo.im.dao.repository.UpdatableMessageDao; @@ -80,7 +81,6 @@ public class UpdatableMessageManager { collector.addMessage(message); message.setBatchNo(batchNo); message.setTemplateId(request.getMsgTemplateId()); - message.setRefTemplateId(request.getRefTemplateId()); message.setBizId(request.getBizId()); message.setTaskId(task.getId()); message.setReceiverPersonId(person.getPersonId()); @@ -90,6 +90,9 @@ public class UpdatableMessageManager { message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); message.setDataVersion(1L); + message.setSenderPersonId(request.determineSenderPersonId() + "0"); + message.setSenderOuId(request.determineSenderOuId()); + message.setIsSenderRobot(request.isSendByRobot() ? YesOrNo.YES : YesOrNo.NO); UpdatableMessageLog messageLog = message.toMessageLog(); collector.addLog(messageLog); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 6f82bb0..638ee0c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -4,8 +4,10 @@ import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; import cn.axzo.im.center.common.enums.BizTypeEnum; +import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageCustomBody; +import cn.axzo.im.channel.netease.dto.Peer; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; @@ -74,6 +76,12 @@ public class UpdateSupport { messageBody.setDataVersion(messageUpdate.getDataVersion()); messageBody.setInitMessageId(message.getNimMessageId()); messageBody.setMsgType(message.getMsgType()); + + messageBody.setPeer(new Peer()); + messageBody.getPeer().setSenderRobot(message.getIsSenderRobot() == YesOrNo.YES); + messageBody.getPeer().setSenderPersonId(message.senderPersonIdAsLong()); + messageBody.getPeer().setReceiverPersonId(message.receiverPersonIdAsLong()); + history.setMessageBody(JSON.toJSONString(messageBody)); messageUpdate.setMessageBody(JSON.parseObject(history.getMessageBody())); messageUpdate.setBizBody(update.bizBody()); From 37d13da963ef3171041e8932f7317d22bf9f4aa5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 19 Nov 2024 09:32:27 +0800 Subject: [PATCH 47/93] REQ-3057-card: backup --- .../java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java | 4 ++-- .../main/java/cn/axzo/im/controller/MessageController.java | 2 +- .../src/main/java/cn/axzo/im/entity/UpdatableMessage.java | 3 ++- .../src/main/java/cn/axzo/im/updatable/InitHistories.java | 3 +-- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 3 +-- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java index 173b4bf..94aba2b 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/PersonAccountAttribute.java @@ -1,6 +1,6 @@ package cn.axzo.im.center.api.vo; -import cn.axzo.im.center.common.enums.ImAppType; +import cn.axzo.im.center.common.enums.AppTypeEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -46,6 +46,6 @@ public class PersonAccountAttribute { * @See cn.axzo.im.center.common.enums.AppTypeEnum */ @NotNull(message = "appType不能为空") - private ImAppType appType; + private AppTypeEnum appType; } diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 91f659d..df8d846 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -181,7 +181,7 @@ public class MessageController implements MessageApi { String sendImAccount; if (sender != null) { AccountAbsentQuery accountQuery = new AccountAbsentQuery(); - accountQuery.setAppType(sender.getAppType().toNimAppType().getCode()); + accountQuery.setAppType(sender.getAppType().getCode()); accountQuery.setPersonId(sender.getPersonId()); accountQuery.setOuId(sender.getOuId()); List accounts = accountService.registerAccountIfAbsent(accountQuery); diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 8958431..2a241a1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -2,6 +2,7 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; +import cn.axzo.im.center.common.enums.AppTypeEnum; import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.center.common.enums.YesOrNo; @@ -37,7 +38,7 @@ public class UpdatableMessage implements MessageUpdateInfo { private String senderPersonId; private Long senderOuId; private YesOrNo isSenderRobot; - private ImAppType appType; + private AppTypeEnum appType; private TemplatedMsgType msgType; private UpdatableMessageState state; private String bizMessageId; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java index d49a0d9..8fbdd27 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/InitHistories.java @@ -3,7 +3,6 @@ package cn.axzo.im.updatable; import cn.axzo.basics.common.constant.enums.CodeDefinition; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.common.enums.AppTypeEnum; -import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.entity.MessageHistory; import cn.axzo.im.entity.UpdatableMessage; import lombok.EqualsAndHashCode; @@ -38,7 +37,7 @@ class InitHistories { person.setOuId(history.getReceiveOuId()); AppTypeEnum appType = CodeDefinition.findByCode( AppTypeEnum.class, history.getAppType()).orElse(null); - person.setAppType(ImAppType.fromNimAppType(appType)); + person.setAppType(appType); return new HistoryTaskAccount(history.getImMessageTaskId(), person); }, identity(), (oldValue, newValue) -> oldValue)); } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 64eeb5a..0f413f3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -7,7 +7,6 @@ import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse; import cn.axzo.im.center.api.vo.resp.MessageUpdateResponse.NonUpdateMessageReason; import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult; -import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.channel.netease.dto.MessageBody; import cn.axzo.im.dao.repository.MessageHistoryDao; @@ -85,7 +84,7 @@ public class UpdatableMessageManager { message.setTaskId(task.getId()); message.setReceiverPersonId(person.getPersonId()); message.setReceiverOuId(person.getOuId()); - message.setAppType(ImAppType.fromNimAppType(person.getAppType())); + message.setAppType(person.getAppType()); message.setMsgType(request.getTemplatedMsgType()); message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 638ee0c..be0bec0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -65,7 +65,7 @@ public class UpdateSupport { history.setBizId(message.getBizId()); history.setFromAccount(message.getFromAccount()); history.setToAccount(message.getToAccount()); - history.setAppType(message.getAppType().toNimAppType().getCode()); + history.setAppType(message.getAppType().getCode()); history.setChannel(imChannel.getProviderType()); MessageCustomBody messageBody = new MessageCustomBody(); messageBody.setToImAccount(message.getToAccount()); From ab00be978f5c21e276b101ff4d32fc3b1e1de319 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 19 Nov 2024 14:50:50 +0800 Subject: [PATCH 48/93] REQ-3057-card: backup --- im-center-server/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-server/pom.xml b/im-center-server/pom.xml index 768fb25..47969a4 100644 --- a/im-center-server/pom.xml +++ b/im-center-server/pom.xml @@ -111,8 +111,8 @@ maokai-api - cn.axzo.pluto - pluto-api + event-hub-api + cn.axzo.event-hub From 2861576d3aafa6149430ddc17b04cb1c52020e33 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 5 Dec 2024 18:45:33 +0800 Subject: [PATCH 49/93] =?UTF-8?q?REQ-3201:=20=E4=BF=9D=E7=95=99=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E6=B6=88=E6=81=AF=E7=9A=84messageExtension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../center/api/vo/req/UpdateMessageRequest.java | 2 ++ .../java/cn/axzo/im/entity/HistoryRecordExt.java | 3 +++ .../java/cn/axzo/im/entity/UpdatableMessage.java | 15 ++++++++++----- .../cn/axzo/im/entity/UpdatableMessageLog.java | 2 -- .../im/service/impl/MessageTaskServiceImpl.java | 10 +++++++--- .../im/updatable/UpdatableMessageManager.java | 7 ++++--- .../java/cn/axzo/im/updatable/UpdateSupport.java | 2 -- 7 files changed, 26 insertions(+), 15 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index 27b672e..d089dcc 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -40,6 +40,8 @@ public class UpdateMessageRequest { @NotBlank(message = "消息ID不能为空") private String bizMessageId; + private String header; + private String content; private String msgTemplateContent; @Override diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index bf5c6cf..1b3f501 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -2,6 +2,8 @@ package cn.axzo.im.entity; import lombok.Data; +import java.util.Map; + /** * @author yanglin */ @@ -31,4 +33,5 @@ public class HistoryRecordExt { private Boolean isUpdateMessage; private Boolean isUpdateRetry; private Long updateRetryCount; + private Map initMessageExt; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 2a241a1..11f55f0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -3,7 +3,6 @@ package cn.axzo.im.entity; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; import cn.axzo.im.center.common.enums.AppTypeEnum; -import cn.axzo.im.center.common.enums.ImAppType; import cn.axzo.im.center.common.enums.TemplatedMsgType; import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.enums.UpdatableMessageState; @@ -13,10 +12,10 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import java.util.Date; +import java.util.Map; /** * @author yanglin @@ -46,8 +45,8 @@ public class UpdatableMessage implements MessageUpdateInfo { private Long initHistoryId; private Long updateHistoryId; private Long retryHistoryId; - @TableField(typeHandler = FastjsonTypeHandler.class) - private JSONObject messageBody; + private String header; + private String content; @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; private Long dataVersion; @@ -59,6 +58,12 @@ public class UpdatableMessage implements MessageUpdateInfo { private Date createAt; private Date updateAt; + public RecordExt getOrCreateRecordExt() { + if (recordExt == null) + recordExt = new RecordExt(); + return recordExt; + } + public UpdatableMessageLog toMessageLog() { UpdatableMessageLog messageLog = new UpdatableMessageLog(); messageLog.setBizId(bizId); @@ -67,7 +72,6 @@ public class UpdatableMessage implements MessageUpdateInfo { messageLog.setBizMessageId(bizMessageId); messageLog.setMessageState(state); messageLog.setInitHistoryId(initHistoryId); - messageLog.setMessageBody(messageBody); messageLog.setBizBody(bizBody); messageLog.setRetryCount(retryCount); messageLog.setDataVersion(dataVersion); @@ -109,6 +113,7 @@ public class UpdatableMessage implements MessageUpdateInfo { @Getter @Setter public static class RecordExt { + private Map initMessageExt; } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index dddfa48..01fb8f9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -29,8 +29,6 @@ public class UpdatableMessageLog { private String context; private Long contextHistoryId; @TableField(typeHandler = FastjsonTypeHandler.class) - private JSONObject messageBody; - @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; private Long retryCount; private Long dataVersion; diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 827b5dc..79fc22e 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -262,7 +262,8 @@ public class MessageTaskServiceImpl extends ServiceImpl Date: Thu, 5 Dec 2024 18:46:20 +0800 Subject: [PATCH 50/93] =?UTF-8?q?REQ-3201:=20=E4=BF=9D=E7=95=99=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E6=B6=88=E6=81=AF=E7=9A=84messageExtension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index d089dcc..c1b32e1 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -40,7 +40,9 @@ public class UpdateMessageRequest { @NotBlank(message = "消息ID不能为空") private String bizMessageId; + @NotBlank(message = "消息头不能为空") private String header; + @NotBlank(message = "消息内容不能为空") private String content; private String msgTemplateContent; From 8c5f1d05efa644238261c7e41dde2f42d4fa39da Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 6 Dec 2024 14:26:39 +0800 Subject: [PATCH 51/93] =?UTF-8?q?REQ-3201:=20=E6=9F=A5=E8=AF=A2=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/req/FetchUpdatableMessageRequest.java | 37 ++++++++++++++++--- .../api/vo/req/UpdateMessageRequest.java | 4 -- .../resp/FetchUpdatableMessageResponse.java | 10 ++--- .../im/channel/netease/dto/MessageBody.java | 19 +++++----- .../cn/axzo/im/entity/UpdatableMessage.java | 4 -- .../im/updatable/UpdatableMessageManager.java | 11 ++---- .../UpdatableMessageQueryService.java | 12 +++--- .../cn/axzo/im/updatable/UpdateSupport.java | 9 ++++- .../handler/UpdateMessageHandler.java | 1 - .../java/cn/axzo/im/utils/ImProperties.java | 2 +- 10 files changed, 62 insertions(+), 47 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index 2a9be61..b4de93c 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -4,8 +4,8 @@ import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; -import javax.validation.constraints.NotEmpty; -import java.util.Set; +import javax.validation.constraints.NotBlank; +import java.util.Date; /** * @author yanglin @@ -15,13 +15,38 @@ import java.util.Set; public class FetchUpdatableMessageRequest { /** - * 初始消息ID列表(普通消息的网易云信ID). 列表最大数量: 500 + * 接收者IM账号 */ - @NotEmpty(message = "初始消息ID列表不能为空") - private Set nimMessageIds; + @NotBlank(message = "接收者账号不能为空") + private String toAccount; + + /** + * 用于增量更新. 上次增量更新时最大的时间戳, 客户端每次增量更新结束需要按账号缓存该值 + * 默认为1970年xxxx, 比较逻辑为IM消息更新大于该时间戳 + */ + private Long updateTime = 0L; + + /** + * 用于批量(分页)查询. 游标ID, 上一页查询时返回的IM消息的最小ID + * 默认为最大整数值, 比较逻辑为IM消息ID小于该ID, 也就是先返回最新的消息 + */ + private Long minCursorId = Long.MAX_VALUE; + + public Date determineUpdateTime() { + if (updateTime == null || updateTime <= 0) + return new Date(0); + return new Date(updateTime); + } + + public Long determineMinCursorId() { + if (minCursorId == null || minCursorId <= 0) + return Long.MIN_VALUE; + return minCursorId; + } @Override public String toString() { return JSON.toJSONString(this); } -} + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java index c1b32e1..27b672e 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdateMessageRequest.java @@ -40,10 +40,6 @@ public class UpdateMessageRequest { @NotBlank(message = "消息ID不能为空") private String bizMessageId; - @NotBlank(message = "消息头不能为空") - private String header; - @NotBlank(message = "消息内容不能为空") - private String content; private String msgTemplateContent; @Override diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index 9aab45d..efaa11a 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -15,7 +15,7 @@ import java.util.List; public class FetchUpdatableMessageResponse { /** - * 消息列表 + * 消息列表, 返回的列表为空时表示没有更多满足条件的消息. */ private List messages = new ArrayList<>(); @@ -31,20 +31,16 @@ public class FetchUpdatableMessageResponse { @Setter @Getter public static class MessageInfo { - /** - * 消息类型. 和首次发次的保持一致. template: 模板消息, card: 卡片消息 - */ - private String msgType; /** * 消息体 */ private String msgBody; /** - * 业务消息ID + * 业务消息ID. 用于ack */ private String bizMessageId; /** - * 数据版本 + * 数据版本. 用于ack */ private Long dataVersion; } diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java index a0ce53e..3d99e33 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageBody.java @@ -1,6 +1,9 @@ package cn.axzo.im.channel.netease.dto; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.Map; @@ -37,18 +40,14 @@ public class MessageBody { private Map messageExtension; - /** - * 业务消息id, 用于接口拉取最新消息内容 - */ - private String bizMessageId; - - /** - * 数据版本 - */ - private Long dataVersion; - /** * 端信息 */ private Peer peer; + + public JSONObject parseMsgBody() { + if (StringUtils.isBlank(msgBody)) + return new JSONObject(); + return JSON.parseObject(msgBody); + } } diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 11f55f0..86c5bf5 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -15,7 +15,6 @@ import lombok.Setter; import org.apache.commons.lang3.math.NumberUtils; import java.util.Date; -import java.util.Map; /** * @author yanglin @@ -45,8 +44,6 @@ public class UpdatableMessage implements MessageUpdateInfo { private Long initHistoryId; private Long updateHistoryId; private Long retryHistoryId; - private String header; - private String content; @TableField(typeHandler = FastjsonTypeHandler.class) private JSONObject bizBody; private Long dataVersion; @@ -113,7 +110,6 @@ public class UpdatableMessage implements MessageUpdateInfo { @Getter @Setter public static class RecordExt { - private Map initMessageExt; } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index fc3b4a7..4ed626b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -82,8 +82,6 @@ public class UpdatableMessageManager { message.setTemplateId(request.getMsgTemplateId()); message.setBizId(request.getBizId()); message.setTaskId(task.getId()); - message.setHeader(request.getMsgHeader()); - message.setContent(request.getMsgContent()); message.setReceiverPersonId(person.getPersonId()); message.setReceiverOuId(person.getOuId()); message.setAppType(person.getAppType()); @@ -91,7 +89,7 @@ public class UpdatableMessageManager { message.setState(UpdatableMessageState.TASK_CREATED); message.setBizMessageId(UUIDUtil.uuidString()); message.setDataVersion(1L); - message.setSenderPersonId(request.determineSenderPersonId() + "0"); + message.setSenderPersonId(request.determineSenderPersonId() + ""); message.setSenderOuId(request.determineSenderOuId()); message.setIsSenderRobot(request.isSendByRobot() ? YesOrNo.YES : YesOrNo.NO); @@ -133,8 +131,6 @@ public class UpdatableMessageManager { messageUpdate.setBizBody(object.getBizBody()); messageUpdate.setFromAccount(history.getFromAccount()); messageUpdate.setToAccount(history.getToAccount()); - messageUpdate.getOrCreateRecordExt().setInitMessageExt( - history.getOrCreateRecordExt().getInitMessageExt()); messageUpdate.setState(history.getStatus() == MessageHistoryStatus.PENDING ? UpdatableMessageState.INIT_MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); @@ -155,8 +151,9 @@ public class UpdatableMessageManager { historyUpdate.setId(history.getId()); MessageBody messageBody = JSON.parseObject(history.getMessageBody(), MessageBody.class); - messageBody.setBizMessageId(message.bizMessageId()); - messageBody.setDataVersion(message.getDataVersion()); + messageBody.setMsgBody(updateSupport + .setDataVersion(messageBody.parseMsgBody(), message) + .toJSONString()); historyUpdate.setMessageBody(JSON.toJSONString(messageBody)); HistoryRecordExt updateExt = new HistoryRecordExt(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index 6da20b7..2c6be86 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -6,7 +6,6 @@ import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.entity.UpdatableMessage; -import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -25,14 +24,15 @@ public class UpdatableMessageQueryService { public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { int maxSize = props.getUpdatable().getFetchMaxSize(); - BizAssertions.assertTrue(request.getNimMessageIds().size() <= maxSize, - "超过了最大的消息数量, 最大: {}", maxSize); - List messages = updatableMessageDao - .getByNimMessageIds(request.getNimMessageIds()); + List messages = updatableMessageDao.lambdaQuery() + .eq(UpdatableMessage::getToAccount, request.getToAccount()) + .gt(UpdatableMessage::getUpdateAt, request.determineUpdateTime()) + .lt(UpdatableMessage::getId, request.determineMinCursorId()) + .last("LIMIT " + maxSize) + .list(); FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); for (UpdatableMessage message : messages) { FetchUpdatableMessageResponse.MessageInfo imMessage = new FetchUpdatableMessageResponse.MessageInfo(); - imMessage.setMsgType(message.getMsgType().getCode()); imMessage.setMsgBody(message.getBizBody().toJSONString()); imMessage.setBizMessageId(message.getBizMessageId()); imMessage.setDataVersion(message.getDataVersion()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 1e844fc..b2ae4e8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -19,6 +19,7 @@ import cn.axzo.im.updatable.collector.CardManipulateCollector; import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -83,7 +84,7 @@ public class UpdateSupport { messageBody.getPeer().setReceiverPersonId(message.receiverPersonIdAsLong()); history.setMessageBody(JSON.toJSONString(messageBody)); - messageUpdate.setBizBody(update.bizBody()); + messageUpdate.setBizBody(setDataVersion(update.bizBody(), message)); history.setImMessageTaskId(0L); history.setReceivePersonId(message.getReceiverPersonId()); @@ -141,4 +142,10 @@ public class UpdateSupport { collector.finish(); } + public JSONObject setDataVersion(JSONObject obj, UpdatableMessage message) { + obj.put("bizMessageId", message.getBizMessageId()); + obj.put("dataVersion", message.getDataVersion()); + return obj; + } + } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java index b1e4173..6a7163a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java @@ -56,7 +56,6 @@ public class UpdateMessageHandler implements StateHandler { : UpdatableMessageState.UPDATE_MESSAGE_SEND_FAIL); messageLog.setContextHistoryId(history.getId()); MessageBodyJsonObject object = new MessageBodyJsonObject(history.getMessageBody()); - messageLog.setMessageBody(object.getMessageBody()); messageLog.setBizBody(object.getBizBody()); messageLog.setDataVersion(history.getDataVersion()); messageLog.setContextHistoryId(history.getId()); diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index b74abc0..fb95a67 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -54,7 +54,7 @@ public class ImProperties { @Getter public static class UpdatableMessageConfig { - private int fetchMaxSize = 500; + private int fetchMaxSize = 200; } From 664cd7e739959ec278eb333d25a0a98964fdcc17 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 6 Dec 2024 14:30:50 +0800 Subject: [PATCH 52/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4ack?= =?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/vo/req/UpdatableMessageAckRequest.java | 14 +++++++------- .../im/dao/repository/UpdatableMessageDao.java | 12 ------------ .../axzo/im/updatable/UpdatableMessageManager.java | 8 ++++---- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java index 42e65a9..fff74e0 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/UpdatableMessageAckRequest.java @@ -22,7 +22,7 @@ import static java.util.stream.Collectors.toList; public class UpdatableMessageAckRequest { /** - * ack的消息信息列表, 同一个nimMessageId传需要ack的最大dataVersion. 每次最多值100条 + * ack的消息信息列表, 同一个bizMessageId传需要ack的最大dataVersion. 每次最多值100条 */ @NotEmpty(message = "消息ACK不能为空") private List acknowledgments; @@ -32,14 +32,14 @@ public class UpdatableMessageAckRequest { return Collections.emptyList(); HashMap id2Max = new HashMap<>(); for (Acknowledgment ack : acknowledgments) { - Long max = id2Max.getOrDefault(ack.nimMessageId, 0L); + Long max = id2Max.getOrDefault(ack.bizMessageId, 0L); max = Math.max(max, ack.dataVersion); - id2Max.put(ack.nimMessageId, max); + id2Max.put(ack.bizMessageId, max); } return id2Max.entrySet().stream() .map(e -> { Acknowledgment ack = new Acknowledgment(); - ack.setNimMessageId(e.getKey()); + ack.setBizMessageId(e.getKey()); ack.setDataVersion(e.getValue()); return ack; }) @@ -55,10 +55,10 @@ public class UpdatableMessageAckRequest { @Getter public static class Acknowledgment { /** - * 初始消息ID(普通消息的网易云信ID) + * 业务消息ID */ - @NotBlank(message = "初始消息ID不能为空") - private String nimMessageId; + @NotBlank(message = "业务消息ID不能为空") + private String bizMessageId; /** * 数据版本 diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index c6de775..79f2b53 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -18,18 +18,6 @@ import static java.util.stream.Collectors.toList; @Repository("updatableMessageDao") public class UpdatableMessageDao extends ServiceImpl { - public List getByNimMessageIdForUpdate(Collection nimMessageIds) { - if (CollectionUtils.isEmpty(nimMessageIds)) - return Collections.emptyList(); - List bizMessageIds = getByNimMessageIds(nimMessageIds) - .stream() - .map(UpdatableMessage::getBizMessageId) - .distinct() - .collect(toList()); - // 统一使用bizMessageIds加锁, 避免死锁 - return getByBizMessageIdsForUpdate(bizMessageIds); - } - public List getByBizMessageIdsForUpdate(Collection bizMessageIds) { if (CollectionUtils.isEmpty(bizMessageIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 4ed626b..defe4b3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -288,12 +288,12 @@ public class UpdatableMessageManager { } private void ackImpl(List acknowledgments) { - List nimMessageIds = acknowledgments.stream() - .map(Acknowledgment::getNimMessageId) + List bizMessageIds = acknowledgments.stream() + .map(Acknowledgment::getBizMessageId) .collect(toList()); Map nimMessageId2Ack = acknowledgments.stream() - .collect(toMap(Acknowledgment::getNimMessageId, identity())); - List messages = updatableMessageDao.getByNimMessageIdForUpdate(nimMessageIds); + .collect(toMap(Acknowledgment::getBizMessageId, identity())); + List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); CardManipulateCollector collector = cardManipulateCollectorFactory.create(); for (UpdatableMessage message : messages) { Long dataVersion = nimMessageId2Ack.get(message.getNimMessageId()).getDataVersion(); From cd1e97485e351e256c629eeb9fed0db9434d5a72 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 6 Dec 2024 14:33:04 +0800 Subject: [PATCH 53/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4ack?= =?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 1 - .../src/main/java/cn/axzo/im/utils/ImProperties.java | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index defe4b3..3d31be5 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -277,7 +277,6 @@ public class UpdatableMessageManager { updateMessageHandler.onFail(updateFail); } - @Transactional public void ack(UpdatableMessageAckRequest request) { List acknowledgments = request.determineValidAcknowledgments(); if (CollectionUtils.isEmpty(acknowledgments)) return; diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index fb95a67..e4373e9 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -34,7 +34,10 @@ public class ImProperties { private int messageUpdateAckRetryIntervalSeconds = 10; - private int updatableMessageMaxLockRecords = 5; + /** + * 用于避免大事务以及死锁 + */ + private int updatableMessageMaxLockRecords = 20; private SendMessageConfig sendMessage = new SendMessageConfig(); @@ -54,7 +57,7 @@ public class ImProperties { @Getter public static class UpdatableMessageConfig { - private int fetchMaxSize = 200; + private int fetchMaxSize = 500; } From 1eb5980cbba1fdfb7520bb7d2073b6b270b94a55 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 6 Dec 2024 14:44:33 +0800 Subject: [PATCH 54/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4ack?= =?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/req/FetchUpdatableMessageRequest.java | 17 +++++----- .../resp/FetchUpdatableMessageResponse.java | 31 +++++++++++++++++++ .../UpdatableMessageQueryService.java | 6 +++- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java index b4de93c..eca6329 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/FetchUpdatableMessageRequest.java @@ -14,6 +14,9 @@ import java.util.Date; @Getter public class FetchUpdatableMessageRequest { + public static final Long DEFAULT_MAX_UPDATE_TIME = 0L; + public static final Long DEFAULT_MIN_CURSOR_ID = Long.MAX_VALUE; + /** * 接收者IM账号 */ @@ -24,23 +27,23 @@ public class FetchUpdatableMessageRequest { * 用于增量更新. 上次增量更新时最大的时间戳, 客户端每次增量更新结束需要按账号缓存该值 * 默认为1970年xxxx, 比较逻辑为IM消息更新大于该时间戳 */ - private Long updateTime = 0L; + private Long maxUpdateTime = DEFAULT_MAX_UPDATE_TIME; /** * 用于批量(分页)查询. 游标ID, 上一页查询时返回的IM消息的最小ID * 默认为最大整数值, 比较逻辑为IM消息ID小于该ID, 也就是先返回最新的消息 */ - private Long minCursorId = Long.MAX_VALUE; + private Long minCursorId = DEFAULT_MIN_CURSOR_ID; - public Date determineUpdateTime() { - if (updateTime == null || updateTime <= 0) - return new Date(0); - return new Date(updateTime); + public Date determineMaxUpdateTime() { + if (maxUpdateTime == null || maxUpdateTime <= 0) + return new Date(DEFAULT_MAX_UPDATE_TIME); + return new Date(maxUpdateTime); } public Long determineMinCursorId() { if (minCursorId == null || minCursorId <= 0) - return Long.MIN_VALUE; + return DEFAULT_MIN_CURSOR_ID; return minCursorId; } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index efaa11a..8a346f6 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -1,5 +1,6 @@ package cn.axzo.im.center.api.vo.resp; +import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -19,10 +20,32 @@ public class FetchUpdatableMessageResponse { */ private List messages = new ArrayList<>(); + /** + * 当前批次最大的更新时间 + */ + private Long maxUpdateTime; + + /** + * 当前批次IM消息的最小ID + */ + private Long minCursorId; + public void addMessage(MessageInfo message) { messages.add(message); } + public void setQueryProcess() { + if (messages == null) return; + minCursorId = messages.stream() + .mapToLong(MessageInfo::getId) + .min() + .orElse(FetchUpdatableMessageRequest.DEFAULT_MIN_CURSOR_ID); + maxUpdateTime = messages.stream() + .mapToLong(MessageInfo::getUpdateTime) + .max() + .orElse(FetchUpdatableMessageRequest.DEFAULT_MAX_UPDATE_TIME); + } + @Override public String toString() { return JSON.toJSONString(this); @@ -35,6 +58,14 @@ public class FetchUpdatableMessageResponse { * 消息体 */ private String msgBody; + /** + * 消息ID + */ + private Long id; + /** + * 更新时间 + */ + private Long updateTime; /** * 业务消息ID. 用于ack */ diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index 2c6be86..bb447ef 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -26,8 +26,9 @@ public class UpdatableMessageQueryService { int maxSize = props.getUpdatable().getFetchMaxSize(); List messages = updatableMessageDao.lambdaQuery() .eq(UpdatableMessage::getToAccount, request.getToAccount()) - .gt(UpdatableMessage::getUpdateAt, request.determineUpdateTime()) + .gt(UpdatableMessage::getUpdateAt, request.determineMaxUpdateTime()) .lt(UpdatableMessage::getId, request.determineMinCursorId()) + .orderByDesc(UpdatableMessage::getId) .last("LIMIT " + maxSize) .list(); FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); @@ -35,9 +36,12 @@ public class UpdatableMessageQueryService { FetchUpdatableMessageResponse.MessageInfo imMessage = new FetchUpdatableMessageResponse.MessageInfo(); imMessage.setMsgBody(message.getBizBody().toJSONString()); imMessage.setBizMessageId(message.getBizMessageId()); + imMessage.setId(message.getId()); + imMessage.setUpdateTime(message.getUpdateAt().getTime()); imMessage.setDataVersion(message.getDataVersion()); response.addMessage(imMessage); } + response.setQueryProcess(); return response; } From 90b71a3333fe63923a472a7088b0572e1a76e338 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 6 Dec 2024 14:59:25 +0800 Subject: [PATCH 55/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4ack?= =?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/center/api/vo/resp/FetchUpdatableMessageResponse.java | 2 +- .../java/cn/axzo/im/updatable/UpdatableMessageQueryService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index 8a346f6..4612771 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -34,7 +34,7 @@ public class FetchUpdatableMessageResponse { messages.add(message); } - public void setQueryProcess() { + public void setQueryProgress() { if (messages == null) return; minCursorId = messages.stream() .mapToLong(MessageInfo::getId) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index bb447ef..167621d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -41,7 +41,7 @@ public class UpdatableMessageQueryService { imMessage.setDataVersion(message.getDataVersion()); response.addMessage(imMessage); } - response.setQueryProcess(); + response.setQueryProgress(); return response; } From e8b1779c308048086778deb518090749b2fdaa00 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 9 Dec 2024 11:46:47 +0800 Subject: [PATCH 56/93] =?UTF-8?q?REQ-3201:=20=E6=8C=87=E5=AE=9A=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=98=AF=E5=90=A6=E5=8F=AF=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/center/api/vo/req/SendTemplateMessageParam.java | 7 ++++++- .../cn/axzo/im/center/common/enums/TemplatedMsgType.java | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index b81ec81..812aaec 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -69,6 +69,11 @@ public class SendTemplateMessageParam { private TemplatedMsgType templatedMsgType = TemplatedMsgType.TEMPLATE; + /** + * 消息是否可更新 + */ + private boolean isUpdatable; + public boolean isSendByRobot() { return sender == null; } @@ -93,7 +98,7 @@ public class SendTemplateMessageParam { } public boolean isUpdatable() { - return templatedMsgType.isUpdatable(); + return isUpdatable; } public Set uniqueReceivePersons() { diff --git a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java index 685eb30..3d5af0a 100644 --- a/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java +++ b/im-center-common/src/main/java/cn/axzo/im/center/common/enums/TemplatedMsgType.java @@ -10,8 +10,7 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public enum TemplatedMsgType { - TEMPLATE("template", "模板消息", false), - CARD("card", "卡片消息", true), + TEMPLATE("template", "模板消息", false) ; From f7ec87b869b0053cea38093c4d2f7df13cefb17c Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 10 Dec 2024 10:25:25 +0800 Subject: [PATCH 57/93] =?UTF-8?q?REQ-3201:=20=E5=AE=9A=E4=B9=89entity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../netease/dto/MessageCustomBody.java | 26 ------------------- .../cn/axzo/im/updatable/UpdateSupport.java | 17 +++++------- .../updatable/domain/MessageUpdateBody.java | 21 +++++++++++++++ 3 files changed, 27 insertions(+), 37 deletions(-) create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java index 1800523..d02189b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/MessageCustomBody.java @@ -1,7 +1,6 @@ package cn.axzo.im.channel.netease.dto; import cn.axzo.im.center.common.enums.BizTypeEnum; -import cn.axzo.im.center.common.enums.TemplatedMsgType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -37,29 +36,4 @@ public class MessageCustomBody { */ private String payload; - - /** - * 业务消息id, 用于接口拉取最新消息内容 - */ - private String bizMessageId; - - /** - * 数据版本 - */ - private Long dataVersion; - - /** - * 最原始的网易云信消息id, 更新的哪条消息 - */ - private String initMessageId; - - /** - * 更新的消息类型 - */ - private TemplatedMsgType msgType; - - /** - * 端信息 - */ - private Peer peer; } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index b2ae4e8..1d91832 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -4,10 +4,8 @@ import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.req.MessageUpdateInfo; import cn.axzo.im.center.common.enums.BizTypeEnum; -import cn.axzo.im.center.common.enums.YesOrNo; import cn.axzo.im.channel.IMChannelProvider; import cn.axzo.im.channel.netease.dto.MessageCustomBody; -import cn.axzo.im.channel.netease.dto.Peer; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.entity.HistoryRecordExt; import cn.axzo.im.entity.MessageHistory; @@ -17,6 +15,7 @@ import cn.axzo.im.enums.MessageHistoryStatus; import cn.axzo.im.enums.UpdatableMessageState; import cn.axzo.im.updatable.collector.CardManipulateCollector; import cn.axzo.im.updatable.collector.CardManipulateCollectorFactory; +import cn.axzo.im.updatable.domain.MessageUpdateBody; import cn.axzo.im.utils.UUIDUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -72,16 +71,12 @@ public class UpdateSupport { messageBody.setToImAccount(message.getToAccount()); messageBody.setPersonId(message.getReceiverPersonId()); messageBody.setBizType(BizTypeEnum.MESSAGE_UPDATE); - messageBody.setPayload("{}"); - messageBody.setBizMessageId(message.bizMessageId()); - messageBody.setDataVersion(messageUpdate.getDataVersion()); - messageBody.setInitMessageId(message.getNimMessageId()); - messageBody.setMsgType(message.getMsgType()); - messageBody.setPeer(new Peer()); - messageBody.getPeer().setSenderRobot(message.getIsSenderRobot() == YesOrNo.YES); - messageBody.getPeer().setSenderPersonId(message.senderPersonIdAsLong()); - messageBody.getPeer().setReceiverPersonId(message.receiverPersonIdAsLong()); + MessageUpdateBody imBody = new MessageUpdateBody(); + imBody.setBizMessageId(message.bizMessageId()); + imBody.setDataVersion(messageUpdate.getDataVersion()); + imBody.setInitMessageId(message.getNimMessageId()); + messageBody.setPayload(JSON.toJSONString(imBody)); history.setMessageBody(JSON.toJSONString(messageBody)); messageUpdate.setBizBody(setDataVersion(update.bizBody(), message)); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java b/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java new file mode 100644 index 0000000..83fe9fa --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java @@ -0,0 +1,21 @@ +package cn.axzo.im.updatable.domain; + +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class MessageUpdateBody { + private String bizMessageId; + private Long dataVersion; + private String initMessageId; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} \ No newline at end of file From 70aea293f5716fd699b8a99eede93662c3ad4517 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 10 Dec 2024 16:59:15 +0800 Subject: [PATCH 58/93] =?UTF-8?q?REQ-3284:=20=E6=99=AE=E9=80=9A=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=8E=A7=E5=88=B6push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/service/impl/MessageTaskServiceImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 79fc22e..76d7b32 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -250,7 +250,8 @@ public class MessageTaskServiceImpl extends ServiceImpl excludePushPayloads = bizData.getExcludePushPayloads() == null + List excludePushPayloads = + bizData == null || bizData.getExcludePushPayloads() == null ? Collections.emptyList() : bizData.getExcludePushPayloads(); boolean excludePayload = excludePushPayloads.stream() @@ -261,7 +262,8 @@ public class MessageTaskServiceImpl extends ServiceImpl Date: Wed, 11 Dec 2024 17:45:12 +0800 Subject: [PATCH 59/93] =?UTF-8?q?REQ-3201:=20=E6=9F=A5=E8=AF=A2=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/center/api/feign/MessageApi.java | 6 ++++ .../api/vo/req/GetMessageDetailRequest.java | 22 ++++++++++++ .../resp/FetchUpdatableMessageResponse.java | 33 +++-------------- .../api/vo/resp/GetMessageDetailResponse.java | 25 +++++++++++++ .../api/vo/resp/UpdatableMessageInfo.java | 32 +++++++++++++++++ .../axzo/im/controller/MessageController.java | 9 +++++ .../UpdatableMessageQueryService.java | 36 ++++++++++++++----- 7 files changed, 125 insertions(+), 38 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetMessageDetailRequest.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetMessageDetailResponse.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index 4e5c39e..6e23d3f 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -5,6 +5,7 @@ import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; +import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; @@ -12,6 +13,7 @@ import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; +import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -77,6 +79,10 @@ public interface MessageApi { ApiResult fetchUpdatableMessage( @RequestBody @Validated FetchUpdatableMessageRequest request); + @PostMapping("/api/im/template-message/updatable/getMessageDetails") + ApiResult getMessageDetails( + @RequestBody @Validated GetMessageDetailRequest request); + @PostMapping("/api/im/template-message/updatable/getBizMessageIds") ApiResult getBizMessageIds( @RequestBody @Validated GetBizMessageIdsRequest request); diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetMessageDetailRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetMessageDetailRequest.java new file mode 100644 index 0000000..bc6d19b --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetMessageDetailRequest.java @@ -0,0 +1,22 @@ +package cn.axzo.im.center.api.vo.req; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +/** + * @author yanglin + */ +@Setter +@Getter +public class GetMessageDetailRequest { + + /** + * 业务消息ID列表,每次最大数量100 + */ + @NotEmpty(message = "消息ID不能为空") + private Set bizMessageIds; + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index 4612771..ab075f9 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -18,7 +18,7 @@ public class FetchUpdatableMessageResponse { /** * 消息列表, 返回的列表为空时表示没有更多满足条件的消息. */ - private List messages = new ArrayList<>(); + private List messages = new ArrayList<>(); /** * 当前批次最大的更新时间 @@ -30,18 +30,18 @@ public class FetchUpdatableMessageResponse { */ private Long minCursorId; - public void addMessage(MessageInfo message) { + public void addMessage(UpdatableMessageInfo message) { messages.add(message); } public void setQueryProgress() { if (messages == null) return; minCursorId = messages.stream() - .mapToLong(MessageInfo::getId) + .mapToLong(UpdatableMessageInfo::getId) .min() .orElse(FetchUpdatableMessageRequest.DEFAULT_MIN_CURSOR_ID); maxUpdateTime = messages.stream() - .mapToLong(MessageInfo::getUpdateTime) + .mapToLong(UpdatableMessageInfo::getUpdateTime) .max() .orElse(FetchUpdatableMessageRequest.DEFAULT_MAX_UPDATE_TIME); } @@ -51,29 +51,4 @@ public class FetchUpdatableMessageResponse { return JSON.toJSONString(this); } - @Setter - @Getter - public static class MessageInfo { - /** - * 消息体 - */ - private String msgBody; - /** - * 消息ID - */ - private Long id; - /** - * 更新时间 - */ - private Long updateTime; - /** - * 业务消息ID. 用于ack - */ - private String bizMessageId; - /** - * 数据版本. 用于ack - */ - private Long dataVersion; - } - } diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetMessageDetailResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetMessageDetailResponse.java new file mode 100644 index 0000000..d19f76d --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetMessageDetailResponse.java @@ -0,0 +1,25 @@ +package cn.axzo.im.center.api.vo.resp; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yanglin + */ +@Setter +@Getter +public class GetMessageDetailResponse { + + /** + * 消息列表 + */ + private List messages = new ArrayList<>(); + + public void addMessage(UpdatableMessageInfo message) { + messages.add(message); + } + +} diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java new file mode 100644 index 0000000..5545fbd --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java @@ -0,0 +1,32 @@ +package cn.axzo.im.center.api.vo.resp; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class UpdatableMessageInfo { + /** + * 消息体 + */ + private String msgBody; + /** + * 消息ID + */ + private Long id; + /** + * 更新时间 + */ + private Long updateTime; + /** + * 业务消息ID. 用于ack + */ + private String bizMessageId; + /** + * 数据版本. 用于ack + */ + private Long dataVersion; +} diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 927bb7a..54ea88c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -12,6 +12,7 @@ import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; +import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; @@ -19,6 +20,7 @@ import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; +import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; import cn.axzo.im.center.api.vo.resp.MessageTaskResp; @@ -249,6 +251,13 @@ public class MessageController implements MessageApi { return ApiResult.ok(resp); } + @Override + public ApiResult getMessageDetails(GetMessageDetailRequest request) { + log.info("getMessageDetail, request={}", request); + GetMessageDetailResponse resp = updatableMessageQueryService.getMessageDetails(request); + return ApiResult.ok(resp); + } + @Override public ApiResult getBizMessageIds(GetBizMessageIdsRequest request) { log.info("getBizMessageIds, request={}", request); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index 167621d..51d95dc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -2,10 +2,14 @@ package cn.axzo.im.updatable; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; +import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; +import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; +import cn.axzo.im.center.api.vo.resp.UpdatableMessageInfo; import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.entity.UpdatableMessage; +import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -22,6 +26,17 @@ public class UpdatableMessageQueryService { private final UpdatableMessageDao updatableMessageDao; private final ImProperties props; + public GetMessageDetailResponse getMessageDetails(GetMessageDetailRequest request) { + BizAssertions.assertTrue(request.getBizMessageIds().size() <= 100, "消息ID数量不能超过100"); + List messages = updatableMessageDao.lambdaQuery() + .in(UpdatableMessage::getBizMessageId, request.getBizMessageIds()) + .list(); + GetMessageDetailResponse response = new GetMessageDetailResponse(); + for (UpdatableMessage message : messages) + response.addMessage(toMessageInfo(message)); + return response; + } + public FetchUpdatableMessageResponse fetchUpdatableMessage(FetchUpdatableMessageRequest request) { int maxSize = props.getUpdatable().getFetchMaxSize(); List messages = updatableMessageDao.lambdaQuery() @@ -32,19 +47,22 @@ public class UpdatableMessageQueryService { .last("LIMIT " + maxSize) .list(); FetchUpdatableMessageResponse response = new FetchUpdatableMessageResponse(); - for (UpdatableMessage message : messages) { - FetchUpdatableMessageResponse.MessageInfo imMessage = new FetchUpdatableMessageResponse.MessageInfo(); - imMessage.setMsgBody(message.getBizBody().toJSONString()); - imMessage.setBizMessageId(message.getBizMessageId()); - imMessage.setId(message.getId()); - imMessage.setUpdateTime(message.getUpdateAt().getTime()); - imMessage.setDataVersion(message.getDataVersion()); - response.addMessage(imMessage); - } + for (UpdatableMessage message : messages) + response.addMessage(toMessageInfo(message)); response.setQueryProgress(); return response; } + private static UpdatableMessageInfo toMessageInfo(UpdatableMessage message) { + UpdatableMessageInfo imMessage = new UpdatableMessageInfo(); + imMessage.setMsgBody(message.getBizBody().toJSONString()); + imMessage.setBizMessageId(message.getBizMessageId()); + imMessage.setId(message.getId()); + imMessage.setUpdateTime(message.getUpdateAt().getTime()); + imMessage.setDataVersion(message.getDataVersion()); + return imMessage; + } + public GetBizMessageIdsResponse getBizMessageIds(GetBizMessageIdsRequest request) { List messages = updatableMessageDao.getByNimMessageIds(request.getNimMessageIds()); GetBizMessageIdsResponse response = new GetBizMessageIdsResponse(); From 20a799353a2f39d9ede6a8211921ce428507e875 Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 11 Dec 2024 17:53:31 +0800 Subject: [PATCH 60/93] =?UTF-8?q?REQ-3201:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/center/api/feign/MessageApi.java | 6 ---- .../api/vo/req/GetBizMessageIdsRequest.java | 27 --------------- .../api/vo/resp/GetBizMessageIdsResponse.java | 33 ------------------- .../axzo/im/controller/MessageController.java | 9 ----- .../dao/repository/UpdatableMessageDao.java | 8 ----- .../UpdatableMessageQueryService.java | 10 ------ 6 files changed, 93 deletions(-) delete mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java delete mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java index 6e23d3f..0fa9362 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageApi.java @@ -4,7 +4,6 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; -import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; @@ -12,7 +11,6 @@ import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; -import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; @@ -83,10 +81,6 @@ public interface MessageApi { ApiResult getMessageDetails( @RequestBody @Validated GetMessageDetailRequest request); - @PostMapping("/api/im/template-message/updatable/getBizMessageIds") - ApiResult getBizMessageIds( - @RequestBody @Validated GetBizMessageIdsRequest request); - /** * * 接口已经作废,可以使用sendTemplateMessage来替换 diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java deleted file mode 100644 index 024599a..0000000 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/GetBizMessageIdsRequest.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.axzo.im.center.api.vo.req; - -import com.alibaba.fastjson.JSON; -import lombok.Getter; -import lombok.Setter; - -import javax.validation.constraints.NotEmpty; -import java.util.Set; - -/** - * @author yanglin - */ -@Setter -@Getter -public class GetBizMessageIdsRequest { - - /** - * 初始消息ID列表(普通消息的网易云信ID) - */ - @NotEmpty(message = "初始消息ID列表不能为空") - private Set nimMessageIds; - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java deleted file mode 100644 index 48a36d7..0000000 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/GetBizMessageIdsResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.axzo.im.center.api.vo.resp; - -import com.alibaba.fastjson.JSON; -import lombok.Getter; -import lombok.Setter; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * @author yanglin - */ -@Setter -@Getter -public class GetBizMessageIdsResponse { - - private Map nimMessageId2BizMessageId = new HashMap<>(); - - public void addMessage(String nimMessageId, String bizMessageId) { - nimMessageId2BizMessageId.put(nimMessageId, bizMessageId); - } - - public Set bizMessageIds() { - return new HashSet<>(nimMessageId2BizMessageId.values()); - } - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 54ea88c..5aa2262 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -11,7 +11,6 @@ import cn.axzo.im.center.api.vo.req.AccountQuery; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; -import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.req.MessageInfo; import cn.axzo.im.center.api.vo.req.SendMessageParam; @@ -19,7 +18,6 @@ import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; import cn.axzo.im.center.api.vo.req.UpdatableMessageAckRequest; import cn.axzo.im.center.api.vo.req.UpdateMessageRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; -import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; import cn.axzo.im.center.api.vo.resp.MessageCustomResp; import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; @@ -258,13 +256,6 @@ public class MessageController implements MessageApi { return ApiResult.ok(resp); } - @Override - public ApiResult getBizMessageIds(GetBizMessageIdsRequest request) { - log.info("getBizMessageIds, request={}", request); - GetBizMessageIdsResponse response = updatableMessageQueryService.getBizMessageIds(request); - return ApiResult.ok(response); - } - private void check(SendMessageParam sendMessageParam) { List accountRegisters = accountRegisterService.list(AccountRegisterService.ListAccountRegisterParam.builder() .imAccount(sendMessageParam.getSendImAccount()) diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 79f2b53..3d2518c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -39,14 +39,6 @@ public class UpdatableMessageDao extends ServiceImpl getByNimMessageIds(Collection nimMessageIds) { - if (CollectionUtils.isEmpty(nimMessageIds)) - return Collections.emptyList(); - return lambdaQuery() - .in(UpdatableMessage::getNimMessageId, nimMessageIds) - .list(); - } - public List getByTaskIds(List taskIds) { if (CollectionUtils.isEmpty(taskIds)) return Collections.emptyList(); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index 51d95dc..ea95dbb 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -1,10 +1,8 @@ package cn.axzo.im.updatable; import cn.axzo.im.center.api.vo.req.FetchUpdatableMessageRequest; -import cn.axzo.im.center.api.vo.req.GetBizMessageIdsRequest; import cn.axzo.im.center.api.vo.req.GetMessageDetailRequest; import cn.axzo.im.center.api.vo.resp.FetchUpdatableMessageResponse; -import cn.axzo.im.center.api.vo.resp.GetBizMessageIdsResponse; import cn.axzo.im.center.api.vo.resp.GetMessageDetailResponse; import cn.axzo.im.center.api.vo.resp.UpdatableMessageInfo; import cn.axzo.im.dao.repository.UpdatableMessageDao; @@ -63,12 +61,4 @@ public class UpdatableMessageQueryService { return imMessage; } - public GetBizMessageIdsResponse getBizMessageIds(GetBizMessageIdsRequest request) { - List messages = updatableMessageDao.getByNimMessageIds(request.getNimMessageIds()); - GetBizMessageIdsResponse response = new GetBizMessageIdsResponse(); - for (UpdatableMessage message : messages) - response.addMessage(message.getNimMessageId(), message.getBizMessageId()); - return response; - } - } \ No newline at end of file From eb2537bad91beccd915cf8139a0e07a87258f05f Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 11 Dec 2024 17:59:23 +0800 Subject: [PATCH 61/93] =?UTF-8?q?REQ-3201:=20=E5=AD=98=E5=85=A5initMessage?= =?UTF-8?q?Id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/updatable/UpdatableMessageManager.java | 2 +- .../java/cn/axzo/im/updatable/UpdateSupport.java | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 3d31be5..dcefe03 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -152,7 +152,7 @@ public class UpdatableMessageManager { historyUpdate.setId(history.getId()); MessageBody messageBody = JSON.parseObject(history.getMessageBody(), MessageBody.class); messageBody.setMsgBody(updateSupport - .setDataVersion(messageBody.parseMsgBody(), message) + .injectUpdatableFields(messageBody.parseMsgBody(), message) .toJSONString()); historyUpdate.setMessageBody(JSON.toJSONString(messageBody)); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 1d91832..1a0c077 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -79,7 +79,7 @@ public class UpdateSupport { messageBody.setPayload(JSON.toJSONString(imBody)); history.setMessageBody(JSON.toJSONString(messageBody)); - messageUpdate.setBizBody(setDataVersion(update.bizBody(), message)); + messageUpdate.setBizBody(injectUpdatableFields(update.bizBody(), message)); history.setImMessageTaskId(0L); history.setReceivePersonId(message.getReceiverPersonId()); @@ -137,10 +137,13 @@ public class UpdateSupport { collector.finish(); } - public JSONObject setDataVersion(JSONObject obj, UpdatableMessage message) { - obj.put("bizMessageId", message.getBizMessageId()); - obj.put("dataVersion", message.getDataVersion()); - return obj; + public JSONObject injectUpdatableFields(JSONObject bizBody, UpdatableMessage message) { + MessageUpdateBody body = new MessageUpdateBody(); + body.setInitMessageId(message.getNimMessageId()); + body.setBizMessageId(message.getBizMessageId()); + body.setDataVersion(message.getDataVersion()); + bizBody.putAll(JSON.parseObject(JSON.toJSONString(body))); + return bizBody; } } \ No newline at end of file From f3efb99ab2944307b0d9006bdb5cd4dd1ae62d3d Mon Sep 17 00:00:00 2001 From: yanglin Date: Wed, 11 Dec 2024 18:00:10 +0800 Subject: [PATCH 62/93] =?UTF-8?q?REQ-3201:=20=E5=AD=98=E5=85=A5initMessage?= =?UTF-8?q?Id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 4 ++-- .../java/cn/axzo/im/updatable/domain/MessageUpdateBody.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 1a0c077..18b53ab 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -75,7 +75,7 @@ public class UpdateSupport { MessageUpdateBody imBody = new MessageUpdateBody(); imBody.setBizMessageId(message.bizMessageId()); imBody.setDataVersion(messageUpdate.getDataVersion()); - imBody.setInitMessageId(message.getNimMessageId()); + imBody.setNimMessageId(message.getNimMessageId()); messageBody.setPayload(JSON.toJSONString(imBody)); history.setMessageBody(JSON.toJSONString(messageBody)); @@ -139,7 +139,7 @@ public class UpdateSupport { public JSONObject injectUpdatableFields(JSONObject bizBody, UpdatableMessage message) { MessageUpdateBody body = new MessageUpdateBody(); - body.setInitMessageId(message.getNimMessageId()); + body.setNimMessageId(message.getNimMessageId()); body.setBizMessageId(message.getBizMessageId()); body.setDataVersion(message.getDataVersion()); bizBody.putAll(JSON.parseObject(JSON.toJSONString(body))); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java b/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java index 83fe9fa..d8af1bf 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/domain/MessageUpdateBody.java @@ -12,7 +12,7 @@ import lombok.Setter; public class MessageUpdateBody { private String bizMessageId; private Long dataVersion; - private String initMessageId; + private String nimMessageId; @Override public String toString() { From 39792e6cbe89853191faa22a4fcdb90d2202beda Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 12 Dec 2024 11:45:04 +0800 Subject: [PATCH 63/93] =?UTF-8?q?REQ-3201:=20=E8=BF=94=E5=9B=9Eerror=20cod?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/axzo/im/center/api/vo/ErrorCodes.java | 12 ++++++++++++ .../cn/axzo/im/controller/MessageController.java | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java new file mode 100644 index 0000000..cf7739b --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java @@ -0,0 +1,12 @@ +package cn.axzo.im.center.api.vo; + +/** + * @author yanglin + */ +public enum ErrorCodes { + + ; + + public static final Integer TEMPLATE_NO_ROBOT = 4000; + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 5aa2262..656a19b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -5,6 +5,7 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.feign.MessageApi; import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; +import cn.axzo.im.center.api.vo.ErrorCodes; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.AccountAbsentQuery; import cn.axzo.im.center.api.vo.req.AccountQuery; @@ -286,7 +287,8 @@ public class MessageController implements MessageApi { private String check(SendTemplateMessageParam sendMessageParam) { List robotIdList = robotMsgTemplateService.queryRobotIdByTemplate(sendMessageParam.getMsgTemplateId()); if (CollectionUtils.isEmpty(robotIdList)) { - throw new ServiceException("消息模板ID[" + sendMessageParam.getMsgTemplateId() + "],还未维护机器人账户!"); + throw new ServiceException(ErrorCodes.TEMPLATE_NO_ROBOT, + "消息模板ID[" + sendMessageParam.getMsgTemplateId() + "],还未维护机器人账户!"); } if (CollectionUtils.size(robotIdList) > 1) { throw new ServiceException("消息模板ID[" + sendMessageParam.getMsgTemplateId() + "],关联了多个机器人!"); From 78d74b65c19a5ceeb00a94bb6d6052b5988d6d52 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 12 Dec 2024 11:46:30 +0800 Subject: [PATCH 64/93] =?UTF-8?q?REQ-3201:=20=E8=BF=94=E5=9B=9Eerror=20cod?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/center/api/vo/ErrorCodes.java | 12 ----------- .../axzo/im/center/api/vo/ImErrorCodes.java | 20 +++++++++++++++++++ .../axzo/im/controller/MessageController.java | 4 ++-- 3 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/ImErrorCodes.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java deleted file mode 100644 index cf7739b..0000000 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ErrorCodes.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.axzo.im.center.api.vo; - -/** - * @author yanglin - */ -public enum ErrorCodes { - - ; - - public static final Integer TEMPLATE_NO_ROBOT = 4000; - -} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ImErrorCodes.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ImErrorCodes.java new file mode 100644 index 0000000..9e42220 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/ImErrorCodes.java @@ -0,0 +1,20 @@ +package cn.axzo.im.center.api.vo; + +import cn.axzo.basics.common.constant.enums.CodeDefinition; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * @author yanglin + */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum ImErrorCodes implements CodeDefinition { + + TEMPLATE_NO_ROBOT(4000), + ; + + private final Integer code; + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 656a19b..55093e8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -5,7 +5,7 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.im.center.api.feign.MessageApi; import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; -import cn.axzo.im.center.api.vo.ErrorCodes; +import cn.axzo.im.center.api.vo.ImErrorCodes; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.AccountAbsentQuery; import cn.axzo.im.center.api.vo.req.AccountQuery; @@ -287,7 +287,7 @@ public class MessageController implements MessageApi { private String check(SendTemplateMessageParam sendMessageParam) { List robotIdList = robotMsgTemplateService.queryRobotIdByTemplate(sendMessageParam.getMsgTemplateId()); if (CollectionUtils.isEmpty(robotIdList)) { - throw new ServiceException(ErrorCodes.TEMPLATE_NO_ROBOT, + throw new ServiceException(ImErrorCodes.TEMPLATE_NO_ROBOT.getCode(), "消息模板ID[" + sendMessageParam.getMsgTemplateId() + "],还未维护机器人账户!"); } if (CollectionUtils.size(robotIdList) > 1) { From d7d0a70d1713835d232b3e8ca268a76b55523cc5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 12 Dec 2024 18:09:15 +0800 Subject: [PATCH 65/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/vo/resp/FetchUpdatableMessageResponse.java | 9 --------- .../im/center/api/vo/resp/UpdatableMessageInfo.java | 10 ++-------- .../im/updatable/UpdatableMessageQueryService.java | 10 ++++++---- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java index ab075f9..00dccb1 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/FetchUpdatableMessageResponse.java @@ -20,11 +20,6 @@ public class FetchUpdatableMessageResponse { */ private List messages = new ArrayList<>(); - /** - * 当前批次最大的更新时间 - */ - private Long maxUpdateTime; - /** * 当前批次IM消息的最小ID */ @@ -40,10 +35,6 @@ public class FetchUpdatableMessageResponse { .mapToLong(UpdatableMessageInfo::getId) .min() .orElse(FetchUpdatableMessageRequest.DEFAULT_MIN_CURSOR_ID); - maxUpdateTime = messages.stream() - .mapToLong(UpdatableMessageInfo::getUpdateTime) - .max() - .orElse(FetchUpdatableMessageRequest.DEFAULT_MAX_UPDATE_TIME); } @Override diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java index 5545fbd..d124103 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/resp/UpdatableMessageInfo.java @@ -9,6 +9,7 @@ import lombok.Setter; @Setter @Getter public class UpdatableMessageInfo { + /** * 消息体 */ @@ -21,12 +22,5 @@ public class UpdatableMessageInfo { * 更新时间 */ private Long updateTime; - /** - * 业务消息ID. 用于ack - */ - private String bizMessageId; - /** - * 数据版本. 用于ack - */ - private Long dataVersion; + } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java index ea95dbb..c389804 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageQueryService.java @@ -9,6 +9,7 @@ import cn.axzo.im.dao.repository.UpdatableMessageDao; import cn.axzo.im.entity.UpdatableMessage; import cn.axzo.im.utils.BizAssertions; import cn.axzo.im.utils.ImProperties; +import com.alibaba.fastjson.JSONObject; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -23,6 +24,7 @@ public class UpdatableMessageQueryService { private final UpdatableMessageDao updatableMessageDao; private final ImProperties props; + private final UpdateSupport updateSupport; public GetMessageDetailResponse getMessageDetails(GetMessageDetailRequest request) { BizAssertions.assertTrue(request.getBizMessageIds().size() <= 100, "消息ID数量不能超过100"); @@ -51,13 +53,13 @@ public class UpdatableMessageQueryService { return response; } - private static UpdatableMessageInfo toMessageInfo(UpdatableMessage message) { + private UpdatableMessageInfo toMessageInfo(UpdatableMessage message) { UpdatableMessageInfo imMessage = new UpdatableMessageInfo(); - imMessage.setMsgBody(message.getBizBody().toJSONString()); - imMessage.setBizMessageId(message.getBizMessageId()); + JSONObject bizBody = message.getBizBody(); + updateSupport.injectUpdatableFields(bizBody, message); + imMessage.setMsgBody(bizBody.toJSONString()); imMessage.setId(message.getId()); imMessage.setUpdateTime(message.getUpdateAt().getTime()); - imMessage.setDataVersion(message.getDataVersion()); return imMessage; } From 7e391133f7e8b1fd41eb3b4b4257d8174b0a31e9 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 09:13:29 +0800 Subject: [PATCH 66/93] =?UTF-8?q?REQ-3201:=20=E8=B0=83=E6=95=B4=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/axzo/im/entity/UpdatableMessage.java | 3 ++- .../java/cn/axzo/im/entity/UpdatableMessageLog.java | 8 ++++++++ .../cn/axzo/im/updatable/UpdatableMessageManager.java | 10 +++++----- .../main/java/cn/axzo/im/updatable/UpdateSupport.java | 2 +- .../axzo/im/updatable/handler/InitMessageHandler.java | 2 +- .../im/updatable/handler/UpdateMessageHandler.java | 2 +- .../im/updatable/retry/MessageUpdateRetryService.java | 2 +- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index 86c5bf5..d10b7f8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -61,7 +61,7 @@ public class UpdatableMessage implements MessageUpdateInfo { return recordExt; } - public UpdatableMessageLog toMessageLog() { + public UpdatableMessageLog toMessageLog(Object request) { UpdatableMessageLog messageLog = new UpdatableMessageLog(); messageLog.setBizId(bizId); messageLog.setFromAccount(fromAccount); @@ -72,6 +72,7 @@ public class UpdatableMessage implements MessageUpdateInfo { messageLog.setBizBody(bizBody); messageLog.setRetryCount(retryCount); messageLog.setDataVersion(dataVersion); + messageLog.addLogContent("request", request); // messageLog.setContext(null); // messageLog.setContextHistoryId(null); return messageLog; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java index 01fb8f9..89448d3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessageLog.java @@ -33,11 +33,19 @@ public class UpdatableMessageLog { private Long retryCount; private Long dataVersion; @TableField(typeHandler = IgnorePropsJsonTypeHandler.class) + private JSONObject logContent; + @TableField(typeHandler = IgnorePropsJsonTypeHandler.class) private RecordExt recordExt; private Long isDelete; private Date createAt; private Date updateAt; + public void addLogContent(String name, Object value) { + if (logContent == null) + logContent = new JSONObject(); + logContent.put(name, value); + } + @Setter @Getter public static class RecordExt { diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index dcefe03..ef879d7 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -93,7 +93,7 @@ public class UpdatableMessageManager { message.setSenderOuId(request.determineSenderOuId()); message.setIsSenderRobot(request.isSendByRobot() ? YesOrNo.YES : YesOrNo.NO); - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(request); collector.addLog(messageLog); messageLog.setContext("scheduleInTask"); @@ -135,7 +135,7 @@ public class UpdatableMessageManager { ? UpdatableMessageState.INIT_MESSAGE_QUEUED : UpdatableMessageState.ACCOUNT_NOT_FOUND); - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(null); collector.addLog(messageLog); messageLog.setBizId(history.getBizId()); messageLog.setFromAccount(history.getFromAccount()); @@ -283,10 +283,10 @@ public class UpdatableMessageManager { List> batches = Lists.partition( acknowledgments, props.getUpdatableMessageMaxLockRecords()); for (List batch : batches) - transactionTemplate.executeWithoutResult(unused -> ackImpl(batch)); + transactionTemplate.executeWithoutResult(unused -> ackImpl(request, batch)); } - private void ackImpl(List acknowledgments) { + private void ackImpl(UpdatableMessageAckRequest request, List acknowledgments) { List bizMessageIds = acknowledgments.stream() .map(Acknowledgment::getBizMessageId) .collect(toList()); @@ -297,7 +297,7 @@ public class UpdatableMessageManager { for (UpdatableMessage message : messages) { Long dataVersion = nimMessageId2Ack.get(message.getNimMessageId()).getDataVersion(); if (dataVersion == null) dataVersion = -1L; - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(request); collector.addLog(messageLog); messageLog.setContext("ack"); messageLog.setContextHistoryId(0L); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 18b53ab..334e85c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -99,7 +99,7 @@ public class UpdateSupport { history.setRecordExt(recordExt); history.setTimestampForSend(new Date()); - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(null); collector.addLog(messageLog); messageLog2History.put(messageLog, history); messageLog.setDataVersion(message.getDataVersion()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java index 3805f2f..ee49af1 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/InitMessageHandler.java @@ -45,7 +45,7 @@ public class InitMessageHandler implements StateHandler { messageUpdate.setState(isSuccess ? UpdatableMessageState.INIT_MESSAGE_SEND_SUCCESS : UpdatableMessageState.INIT_MESSAGE_SEND_FAIL); - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(null); collector.addLog(messageLog); messageLog.setMessageState(messageUpdate.getState()); messageLog.setInitHistoryId(history.getId()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java index 6a7163a..31e358b 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/handler/UpdateMessageHandler.java @@ -49,7 +49,7 @@ public class UpdateMessageHandler implements StateHandler { } for (MessageHistory history : historyAndMessage.getHistories()) { - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(null); collector.addLog(messageLog); messageLog.setMessageState(isSuccess ? UpdatableMessageState.UPDATE_MESSAGE_SEND_SUCCESS diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 18ba4b7..2e588de 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -79,7 +79,7 @@ public class MessageUpdateRetryService { retry.setDataVersion(message.getDataVersion()); retries.add(retry); - UpdatableMessageLog messageLog = message.toMessageLog(); + UpdatableMessageLog messageLog = message.toMessageLog(null); collector.addLog(messageLog); messageLog.setRetryCount(message.getRetryCount() + 1); messageLog.setContext("scheduleNextRetrySendUpdateMessage"); From 2ebb78afe0f7efe66a3967a74268c4615416bbe7 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 09:47:02 +0800 Subject: [PATCH 67/93] REQ-3201: ack --- .../dao/repository/UpdatableMessageDao.java | 2 +- .../axzo/im/enums/UpdatableMessageState.java | 8 +-- .../im/updatable/UpdatableMessageManager.java | 59 +++++++++++-------- .../retry/MessageUpdateRetryService.java | 7 +-- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java index 3d2518c..85dfec3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java +++ b/im-center-server/src/main/java/cn/axzo/im/dao/repository/UpdatableMessageDao.java @@ -26,7 +26,7 @@ public class UpdatableMessageDao extends ServiceImpl bizMessageIds = acknowledgments.stream() .map(Acknowledgment::getBizMessageId) .collect(toList()); - Map nimMessageId2Ack = acknowledgments.stream() + Map bizMessageId2Ack = acknowledgments.stream() .collect(toMap(Acknowledgment::getBizMessageId, identity())); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); CardManipulateCollector collector = cardManipulateCollectorFactory.create(); + ArrayList ackedBizMessageIds = new ArrayList<>(); for (UpdatableMessage message : messages) { - Long dataVersion = nimMessageId2Ack.get(message.getNimMessageId()).getDataVersion(); - if (dataVersion == null) dataVersion = -1L; + Long ackDataVersion = bizMessageId2Ack.get(message.getBizMessageId()).getDataVersion(); UpdatableMessageLog messageLog = message.toMessageLog(request); collector.addLog(messageLog); messageLog.setContext("ack"); messageLog.setContextHistoryId(0L); messageLog.setRetryCount(0L); messageLog.setBizBody(null); - messageLog.setDataVersion(dataVersion); - UpdatableMessageLog.RecordExt logExt = new UpdatableMessageLog.RecordExt(); - messageLog.setRecordExt(logExt); - + messageLog.setDataVersion(message.getDataVersion()); + messageLog.addLogContent("ackDataVersion", ackDataVersion); + messageLog.addLogContent("dataVersion", message.getDataVersion()); + messageLog.addLogContent("messageState", message.getState()); + BiConsumer ackLogger = (ackSuccess, ackDescription) -> { + messageLog.addLogContent("ackSuccess", ackSuccess); + messageLog.addLogContent("ackDescription", ackDescription); + }; + if (ackDataVersion == null) { + ackLogger.accept(false, "ackDataVersion为空"); + continue; + } + if (ackDataVersion > message.getDataVersion()) { + ackLogger.accept(false, "ackDataVersion大于当前dataVersion"); + continue; + } + if (!ackDataVersion.equals(message.getDataVersion())) { + ackLogger.accept(false, "数据版本不匹配"); + continue; + } + // 避免前端有bug或者有新的消息更新 + if (!message.getState().isUpdateAckAllowed()) { + ackLogger.accept(false, "消息状态不允许ack"); + continue; + } + ackLogger.accept(true, "ack成功"); UpdatableMessage messageUpdate = new UpdatableMessage(); collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); - Long ackDataVersion = dataVersion >= message.getAckDataVersion() - ? dataVersion - : message.getAckDataVersion(); messageUpdate.setAckDataVersion(ackDataVersion); - UpdatableMessageLog.AckInfo ackInfo = new UpdatableMessageLog.AckInfo(); - logExt.setAck(ackInfo); - if (!dataVersion.equals(message.getDataVersion())) { - ackInfo.setAckSuccess(false); - ackInfo.setAckDescription("数据版本不匹配"); - ackInfo.setRequestDataVersion(dataVersion); - ackInfo.setMessageDataVersion(message.getDataVersion()); - continue; - } - // 避免前端有bug - if (!message.getState().isUpdateAckAllowed()) { - ackInfo.setAckSuccess(false); - ackInfo.setAckDescription("消息状态不允许ack"); - continue; - } - ackInfo.setAckSuccess(true); messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); + ackedBizMessageIds.add(message.bizMessageId()); } collector.finish(); + // 只需要通过bizMessageId去删除就保证安全了 + messageUpdateRetryService.removeRetryByBizMessageIds(ackedBizMessageIds); } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 2e588de..2641f41 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -38,7 +38,7 @@ public class MessageUpdateRetryService { private final CardManipulateCollectorFactory cardManipulateCollectorFactory; private final ImProperties props; - public void removePrevRetryByBizMessageIds(Collection bizMessageIds) { + public void removeRetryByBizMessageIds(Collection bizMessageIds) { if (CollectionUtils.isNotEmpty(bizMessageIds)) messageUpdateRetryDao.getBaseMapper().deleteByBizMessageIds(bizMessageIds); } @@ -51,8 +51,8 @@ public class MessageUpdateRetryService { } private void advanceRetryImpl(List bizMessageIds) { - removePrevRetryByBizMessageIds(bizMessageIds); List messages = updatableMessageDao.getByBizMessageIdsForUpdate(bizMessageIds); + removeRetryByBizMessageIds(bizMessageIds); UpdateAckState state = new UpdateAckState(messages, props.getMessageUpdateAckMaxRetryCount()); // 先发本次生试, 再调度下一次重试 retryUpdateMessage(state.getNonAckMessageIds()); @@ -65,7 +65,7 @@ public class MessageUpdateRetryService { List bizMessageIds = messages.stream() .map(UpdatableMessage::getBizMessageId) .collect(toList()); - removePrevRetryByBizMessageIds(bizMessageIds); + removeRetryByBizMessageIds(bizMessageIds); ArrayList retries = new ArrayList<>(); CardManipulateCollector collector = cardManipulateCollectorFactory.create(); Date nextRetryTime = DateTime.now() @@ -96,5 +96,4 @@ public class MessageUpdateRetryService { .addUpdateHistories("retryUpdateHistoryCreated", messages); updateSupport.updateHistoryId(result, UpdatableMessage::setRetryHistoryId); } - } \ No newline at end of file From b15d95881e80ff4d9dc883bc90c17a4005fb6644 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 10:12:15 +0800 Subject: [PATCH 68/93] REQ-3201: ack --- .../axzo/im/enums/UpdatableMessageState.java | 19 +++++++-------- .../im/updatable/UpdatableMessageManager.java | 24 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java index 6aa891c..83894d6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java +++ b/im-center-server/src/main/java/cn/axzo/im/enums/UpdatableMessageState.java @@ -12,25 +12,24 @@ import lombok.RequiredArgsConstructor; public enum UpdatableMessageState { // 已创建 - TASK_CREATED(true, false), + TASK_CREATED(true), // 消息已经放进队列 - INIT_MESSAGE_QUEUED(true, false), + INIT_MESSAGE_QUEUED(true), // 消息已经发送成功 - INIT_MESSAGE_SEND_SUCCESS(true, false), + INIT_MESSAGE_SEND_SUCCESS(true), // 消息已经发送失败 - INIT_MESSAGE_SEND_FAIL(true, false), + INIT_MESSAGE_SEND_FAIL(true), // 更新已经放进队列 - UPDATE_MESSAGE_QUEUED(true, false), + UPDATE_MESSAGE_QUEUED(true), // 更新已经发送成功 - UPDATE_MESSAGE_SEND_SUCCESS(true, true), + UPDATE_MESSAGE_SEND_SUCCESS(true), // 更新已经发送失败 - UPDATE_MESSAGE_SEND_FAIL(true, true), + UPDATE_MESSAGE_SEND_FAIL(true), // 更新ACK - UPDATE_ACK(true, false), + UPDATE_ACK(true), // 未找到账号 - ACCOUNT_NOT_FOUND(false, false); + ACCOUNT_NOT_FOUND(false); private final boolean isUpdateMessageAllowed; - private final boolean isUpdateAckAllowed; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index b96fcab..7410fc6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -305,12 +305,14 @@ public class UpdatableMessageManager { messageLog.setRetryCount(0L); messageLog.setBizBody(null); messageLog.setDataVersion(message.getDataVersion()); - messageLog.addLogContent("ackDataVersion", ackDataVersion); - messageLog.addLogContent("dataVersion", message.getDataVersion()); - messageLog.addLogContent("messageState", message.getState()); BiConsumer ackLogger = (ackSuccess, ackDescription) -> { - messageLog.addLogContent("ackSuccess", ackSuccess); - messageLog.addLogContent("ackDescription", ackDescription); + HashMap ackInfo = new HashMap<>(); + ackInfo.put("ackDataVersion", ackDataVersion); + ackInfo.put("dataVersion", message.getDataVersion()); + ackInfo.put("messageState", message.getState()); + ackInfo.put("ackSuccess", ackSuccess); + ackInfo.put("ackDescription", ackDescription); + messageLog.addLogContent("ackInfo", ackInfo); }; if (ackDataVersion == null) { ackLogger.accept(false, "ackDataVersion为空"); @@ -320,13 +322,8 @@ public class UpdatableMessageManager { ackLogger.accept(false, "ackDataVersion大于当前dataVersion"); continue; } - if (!ackDataVersion.equals(message.getDataVersion())) { - ackLogger.accept(false, "数据版本不匹配"); - continue; - } - // 避免前端有bug或者有新的消息更新 - if (!message.getState().isUpdateAckAllowed()) { - ackLogger.accept(false, "消息状态不允许ack"); + if (ackDataVersion < message.getAckDataVersion()) { + ackLogger.accept(false, "ackDataVersion小于上次ackDataVersion"); continue; } ackLogger.accept(true, "ack成功"); @@ -334,7 +331,8 @@ public class UpdatableMessageManager { collector.updateMessage(messageUpdate); messageUpdate.setId(message.getId()); messageUpdate.setAckDataVersion(ackDataVersion); - messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); + if (ackDataVersion.equals(message.getDataVersion())) + messageUpdate.setState(UpdatableMessageState.UPDATE_ACK); ackedBizMessageIds.add(message.bizMessageId()); } collector.finish(); From 6c0d70659b539225cd9d1eac417424997539e299 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 10:15:29 +0800 Subject: [PATCH 69/93] REQ-3201: ack --- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 7410fc6..87e045a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -322,8 +322,8 @@ public class UpdatableMessageManager { ackLogger.accept(false, "ackDataVersion大于当前dataVersion"); continue; } - if (ackDataVersion < message.getAckDataVersion()) { - ackLogger.accept(false, "ackDataVersion小于上次ackDataVersion"); + if (ackDataVersion <= message.getAckDataVersion()) { + ackLogger.accept(false, "ackDataVersion小于等于上次ackDataVersion"); continue; } ackLogger.accept(true, "ack成功"); From 0d081482d40501b717f8ea1e1620ae63cff1a253 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 10:39:11 +0800 Subject: [PATCH 70/93] REQ-3201: ack --- .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 7 ++++--- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 6 ++++-- .../axzo/im/updatable/retry/MessageUpdateRetryService.java | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 87e045a..2487ead 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -180,11 +180,12 @@ public class UpdatableMessageManager { List> batches = Lists.partition( request.getUpdates(), props.getUpdatableMessageMaxLockRecords()); for (List batch : batches) - transactionTemplate.executeWithoutResult(unused -> updateMessageImpl(batch, response)); + transactionTemplate.executeWithoutResult(unused -> updateMessageImpl(request, batch, response)); return response; } - private void updateMessageImpl(List requestUpdates, + private void updateMessageImpl(UpdateMessageRequest request, + List requestUpdates, MessageUpdateResponse response) { List requestMessages = updatableMessageDao .getByBizMessageIdsForUpdate(collectBizMessageIds(requestUpdates)); @@ -214,7 +215,7 @@ public class UpdatableMessageManager { updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); messageUpdateRetryService.scheduleNextRetry(updateIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories("updateHistoryCreated", validUpdates); + .addUpdateHistories(request, "updateHistoryCreated", validUpdates); updateSupport.updateHistoryId(result, UpdatableMessage::setUpdateHistoryId); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 334e85c..bcb51fc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -44,7 +44,9 @@ public class UpdateSupport { private final CardManipulateCollectorFactory cardManipulateCollectorFactory; public AddUpdateHistoryResult addUpdateHistories( - String context, List updates) { + Object request, + String context, + List updates) { List messages = updatableMessageDao .getByBizMessageIds(collectBizMessageIds(updates)); Map bizMessageId2Message = messages @@ -99,7 +101,7 @@ public class UpdateSupport { history.setRecordExt(recordExt); history.setTimestampForSend(new Date()); - UpdatableMessageLog messageLog = message.toMessageLog(null); + UpdatableMessageLog messageLog = message.toMessageLog(request); collector.addLog(messageLog); messageLog2History.put(messageLog, history); messageLog.setDataVersion(message.getDataVersion()); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 2641f41..105c64d 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -93,7 +93,7 @@ public class MessageUpdateRetryService { updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); List messages = updatableMessageDao.listByIds(messageIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories("retryUpdateHistoryCreated", messages); + .addUpdateHistories(null, "retryUpdateHistoryCreated", messages); updateSupport.updateHistoryId(result, UpdatableMessage::setRetryHistoryId); } } \ No newline at end of file From e743164e620913054e097a303dc310647ad9db8e Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 10:40:19 +0800 Subject: [PATCH 71/93] REQ-3201: ack --- .../main/java/cn/axzo/im/updatable/UpdatableMessageManager.java | 2 +- .../cn/axzo/im/updatable/retry/MessageUpdateRetryService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 2487ead..8082973 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -215,7 +215,7 @@ public class UpdatableMessageManager { updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); messageUpdateRetryService.scheduleNextRetry(updateIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories(request, "updateHistoryCreated", validUpdates); + .addUpdateHistories(request, "createUpdateHistory", validUpdates); updateSupport.updateHistoryId(result, UpdatableMessage::setUpdateHistoryId); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 105c64d..3c100a8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -93,7 +93,7 @@ public class MessageUpdateRetryService { updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); List messages = updatableMessageDao.listByIds(messageIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories(null, "retryUpdateHistoryCreated", messages); + .addUpdateHistories(null, "createRetryUpdateHistory", messages); updateSupport.updateHistoryId(result, UpdatableMessage::setRetryHistoryId); } } \ No newline at end of file From 1ae80fa3a620c5d3fee5d0fcf0367c29d4d6877e Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 13 Dec 2024 17:15:53 +0800 Subject: [PATCH 72/93] REQ-3201: ack --- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index bcb51fc..3e4dcfe 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -76,7 +76,7 @@ public class UpdateSupport { MessageUpdateBody imBody = new MessageUpdateBody(); imBody.setBizMessageId(message.bizMessageId()); - imBody.setDataVersion(messageUpdate.getDataVersion()); + imBody.setDataVersion(message.getDataVersion()); imBody.setNimMessageId(message.getNimMessageId()); messageBody.setPayload(JSON.toJSONString(imBody)); From ad69d3afab857717848d4c61e28ef07ec79bbc5d Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 09:44:19 +0800 Subject: [PATCH 73/93] REQ-3201: ack --- .../im/updatable/collector/CardManipulateCollector.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java index c6f9ee7..f11894f 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/collector/CardManipulateCollector.java @@ -89,10 +89,8 @@ public class CardManipulateCollector { } void batched(Consumer> action) { - if (CollectionUtils.isEmpty(elements)) - return; - if (finished) - return; + if (finished) return; + if (CollectionUtils.isEmpty(elements)) return; Lists.partition(elements, BATCH_SIZE).forEach(action); finished = true; } From 11ab967991389afa84774a8e81a274f0d1a560b5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 10:00:35 +0800 Subject: [PATCH 74/93] =?UTF-8?q?REQ-3201:=20=E8=BF=90=E8=90=A5push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java index 25bdda3..aaab1de 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java @@ -74,6 +74,8 @@ public class AsyncSendMessageParam { private Integer sendPriority; + private String payload; + @Data @Builder @AllArgsConstructor From 4375d6478ee76565aeb3b23f4ebac4c0f324da7a Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 10:03:40 +0800 Subject: [PATCH 75/93] =?UTF-8?q?REQ-3201:=20=E8=BF=90=E8=90=A5push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/controller/MessageController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 55093e8..36563cc 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -136,6 +136,7 @@ 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() From de49f0ea09bf9997ed6b8f7c1d7fb1d802cbf330 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 14:44:10 +0800 Subject: [PATCH 76/93] REQ-3201: push --- .../center/api/vo/req/CustomMessageInfo.java | 2 +- .../im/center/api/vo/req/PushContent.java | 58 +++++++++++ .../im/center/api/vo/req/PushMessageTye.java | 24 +++++ .../api/vo/req/SendTemplateMessageParam.java | 5 +- .../axzo/im/controller/MessageController.java | 3 +- .../java/cn/axzo/im/entity/MessageTask.java | 7 +- .../gateway/OrganizationalNodeApiGateway.java | 29 ------ .../java/cn/axzo/im/push/NimPushService.java | 61 ++++++++++++ .../main/java/cn/axzo/im/push/PushPeer.java | 33 +++++++ .../main/java/cn/axzo/im/push/PushProps.java | 96 +++++++++++++++++++ .../payload/AndroidPushPayloadBuilder.java | 28 ++++++ .../im/push/payload/HWPushPayloadBuilder.java | 45 +++++++++ .../push/payload/OppoPushPayloadBuilder.java | 40 ++++++++ .../im/push/payload/PGPushPayloadBuilder.java | 46 +++++++++ .../im/push/payload/PushPayloadBuilder.java | 23 +++++ .../im/push/payload/RYPushPayloadBuilder.java | 45 +++++++++ .../push/payload/VivoPushPayloadBuilder.java | 41 ++++++++ .../im/push/payload/XMPushPayloadBuilder.java | 37 +++++++ .../axzo/im/push/payload/intent/Intent.java | 26 +++++ .../im/push/payload/intent/IntentValue.java | 52 ++++++++++ .../im/push/payload/intent/JsonIntent.java | 25 +++++ .../im/push/payload/intent/UriIntent.java | 41 ++++++++ .../axzo/im/service/CustomMessageService.java | 18 +++- .../service/impl/MessageTaskServiceImpl.java | 18 +++- .../java/cn/axzo/im/utils/BizAssertions.java | 45 ++++----- 25 files changed, 774 insertions(+), 74 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushMessageTye.java delete mode 100644 im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/PushProps.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/HWPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/OppoPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/RYPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/VivoPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/XMPushPayloadBuilder.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/intent/Intent.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/intent/IntentValue.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/intent/JsonIntent.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/push/payload/intent/UriIntent.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/CustomMessageInfo.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/CustomMessageInfo.java index b142d80..57905c0 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/CustomMessageInfo.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/CustomMessageInfo.java @@ -59,7 +59,7 @@ public class CustomMessageInfo { */ private Map personId2BizInfo = new HashMap<>(); - private boolean isPush = false; + private PushContent pushContent; public void addBizInfo(String personId, Object bizInfo) { if (personId2BizInfo == null) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java new file mode 100644 index 0000000..d439b78 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java @@ -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 intent = new HashMap<>(); + + public void addIntent(String key, String value) { + intent.put(key, value); + } + + public String determineAndroidCategory() { + return messageTye == PushMessageTye.OP + ? "MARKETING" + : "IM"; + } + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushMessageTye.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushMessageTye.java new file mode 100644 index 0000000..2893245 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushMessageTye.java @@ -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 { + + SYSTEM("system", "系统消息"), + OP("op", "运营消息"), + ; + + private final String code; + private final String description; + + @Override + public String getCode() { + return code; + } + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 812aaec..7e35fde 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -78,10 +78,7 @@ public class SendTemplateMessageParam { return sender == null; } - /** - * 推送消息 - */ - private String payload; + private PushContent pushContent; private List excludePushPayloads; diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 36563cc..e9b0aad 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -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()) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java index 92adbaa..95ed465 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java @@ -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; /** * 跳转信息 diff --git a/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java b/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java deleted file mode 100644 index 01d8ca9..0000000 --- a/im-center-server/src/main/java/cn/axzo/im/gateway/OrganizationalNodeApiGateway.java +++ /dev/null @@ -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 fetchNodesByNodeIds(List nodeIdList) { - OrganizationalNodeBatchQueryVO organizationalNodeBatchQueryVO = new OrganizationalNodeBatchQueryVO(); - organizationalNodeBatchQueryVO.setNodeIds(nodeIdList); - organizationalNodeBatchQueryVO.setContainsDeleted(false); - List nodeVOList = organizationalNodeApi.listNode(organizationalNodeBatchQueryVO).getData(); - return RpcUtil.rpcApiResultProcessor(() -> organizationalNodeApi.listNode(organizationalNodeBatchQueryVO), "fetchNodesByNodeIds", JSON.toJSONString(nodeVOList)); - } - -} diff --git a/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java b/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java new file mode 100644 index 0000000..5ab5a53 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java @@ -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 payloadBuilders) { + this.payloadBuilders = payloadBuilders.getIfAvailable(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public String buildPayload(PushContent content, PushPeer peer) { + Consumer> 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 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(); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java b/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java new file mode 100644 index 0000000..9b72dfc --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java @@ -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(); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/PushProps.java b/im-center-server/src/main/java/cn/axzo/im/push/PushProps.java new file mode 100644 index 0000000..407df98 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/PushProps.java @@ -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); + } + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java new file mode 100644 index 0000000..751184b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java @@ -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> + implements PushPayloadBuilder { + + @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(); + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/HWPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/HWPushPayloadBuilder.java new file mode 100644 index 0000000..9a5fab0 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/HWPushPayloadBuilder.java @@ -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 { + + @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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/OppoPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/OppoPushPayloadBuilder.java new file mode 100644 index 0000000..9dfd43b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/OppoPushPayloadBuilder.java @@ -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 { + + 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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java new file mode 100644 index 0000000..00c0199 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java @@ -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 { + + @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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java new file mode 100644 index 0000000..a8b3517 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java @@ -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; + +/** + * 云信推送 + * 各端推送配置 + *

+ * 文字, 链接、声音 + * + * @author yanglin + */ +public interface PushPayloadBuilder> { + + T createIntent(PushContent content); + + void build(PushPeer peer, PushContent content, + T intent, JSONObject payload); + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/RYPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/RYPushPayloadBuilder.java new file mode 100644 index 0000000..64793de --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/RYPushPayloadBuilder.java @@ -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 { + + @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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/VivoPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/VivoPushPayloadBuilder.java new file mode 100644 index 0000000..c4c88a3 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/VivoPushPayloadBuilder.java @@ -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 { + + @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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/XMPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/XMPushPayloadBuilder.java new file mode 100644 index 0000000..f66d320 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/XMPushPayloadBuilder.java @@ -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 { + + 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()); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/Intent.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/Intent.java new file mode 100644 index 0000000..2b99a16 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/Intent.java @@ -0,0 +1,26 @@ +package cn.axzo.im.push.payload.intent; + +/** + * @author yanglin + */ +public interface Intent { + + 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); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/IntentValue.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/IntentValue.java new file mode 100644 index 0000000..d026af5 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/IntentValue.java @@ -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 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); + } + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/JsonIntent.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/JsonIntent.java new file mode 100644 index 0000000..05ffa32 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/JsonIntent.java @@ -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 { + + 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; + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/UriIntent.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/UriIntent.java new file mode 100644 index 0000000..30e5d23 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/intent/UriIntent.java @@ -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 { + + private static final String INTENT_PREFIX = + "intent://cn.axzo.codelabpush/deeplink?#Intent;scheme=pushschema;launchFlags=0x4000000;"; + + private final LinkedHashMap 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 entry : values.entrySet()) { + entry.getValue().consume(stringValue -> + buf.append("S.") + .append(entry.getKey()) + .append("=") + .append(stringValue) + .append(";")); + } + buf.append("end"); + return buf.toString(); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/service/CustomMessageService.java b/im-center-server/src/main/java/cn/axzo/im/service/CustomMessageService.java index d68a98b..22fa6d3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/CustomMessageService.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/CustomMessageService.java @@ -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 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)); diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 76d7b32..2d06750 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -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 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 assertResponse(CommonResponse response) { return assertResponse(response, "error resp={}", JSON.toJSONString(response)); } public static T assertResponse(CommonResponse 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 assertResponse(ApiResult 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 List assertResponse(ApiListResult 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(); From 541202eaa1feca3b3c7c003a658b7282276252be Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 14:55:47 +0800 Subject: [PATCH 77/93] REQ-3201: push --- .../axzo/im/center/api/vo/req/PushContent.java | 14 ++++++++++++-- .../java/cn/axzo/im/push/NimPushService.java | 2 +- .../src/main/java/cn/axzo/im/push/PushPeer.java | 16 ++++++++++++++++ .../push/payload/AndroidPushPayloadBuilder.java | 10 +++++----- .../im/push/payload/PGPushPayloadBuilder.java | 7 ++++--- .../axzo/im/push/payload/PushPayloadBuilder.java | 2 +- 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java index d439b78..e3c340e 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/PushContent.java @@ -26,12 +26,22 @@ public class PushContent { /** * android点击push的跳转链接 */ - private String androidPushUrl; + private String workerAndroidPushUrl; /** * ios点击push的跳转链接 */ - private String iosPushUrl; + private String workerIosPushUrl; + + /** + * android点击push的跳转链接 + */ + private String mangerAndroidPushUrl; + + /** + * ios点击push的跳转链接 + */ + private String managerIosPushUrl; /** * push消息类型. SYSTEM: 系统消息, OP: 运营消息 diff --git a/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java b/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java index 5ab5a53..afa86c2 100644 --- a/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java +++ b/im-center-server/src/main/java/cn/axzo/im/push/NimPushService.java @@ -50,7 +50,7 @@ public class NimPushService { JSONObject payload = new JSONObject(); payload.put("pushTitle", content.getTitle()); for (PushPayloadBuilder builder : payloadBuilders) { - Intent intent = builder.createIntent(content); + Intent intent = builder.createIntent(peer, content); BizAssertions.assertNotNull(intent, "intent can't be null"); intentPopulator.accept(intent); builder.build(peer, content, intent, payload); diff --git a/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java b/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java index 9b72dfc..f4a9c1a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java +++ b/im-center-server/src/main/java/cn/axzo/im/push/PushPeer.java @@ -20,6 +20,22 @@ public class PushPeer { private AppTypeEnum appType; private ApiChannel apiChannel; + public String determineAndroidUrl(PushContent content) { + if (appType == AppTypeEnum.CM) + return content.getWorkerAndroidPushUrl(); + else if (appType == AppTypeEnum.CMP) + return content.getMangerAndroidPushUrl(); + return null; + } + + public String determineIosUrl(PushContent content) { + if (appType == AppTypeEnum.CM) + return content.getWorkerIosPushUrl(); + else if (appType == AppTypeEnum.CMP) + return content.getManagerIosPushUrl(); + return null; + } + public String determineChannelId(ChannelConfig config, PushContent content) { PushProps.ChannelIds channelIds = appType == AppTypeEnum.CM diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java index 751184b..5831e52 100644 --- a/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/AndroidPushPayloadBuilder.java @@ -1,6 +1,7 @@ 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.utils.BizAssertions; @@ -13,13 +14,12 @@ abstract class AndroidPushPayloadBuilder> implements PushPayloadBuilder { @Override - public final T createIntent(PushContent content) { + public final T createIntent(PushPeer peer, 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()); + String url = peer.determineAndroidUrl(content); + if (StringUtils.isNotBlank(url)) + Intent.setRouter(intent, IntentValue.create(url).urlEncode()); return intent; } diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java index 00c0199..097523c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/PGPushPayloadBuilder.java @@ -18,11 +18,12 @@ import org.springframework.stereotype.Component; class PGPushPayloadBuilder implements PushPayloadBuilder { @Override - public JsonIntent createIntent(PushContent content) { + public JsonIntent createIntent(PushPeer peer, PushContent content) { JsonIntent intent = new JsonIntent(); - if (StringUtils.isNotBlank(content.getIosPushUrl())) + String url = peer.determineIosUrl(content); + if (StringUtils.isNotBlank(url)) Intent.setRouter(intent, IntentValue - .create(content.getIosPushUrl())); + .create(url)); return intent; } diff --git a/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java b/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java index a8b3517..adc4718 100644 --- a/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java +++ b/im-center-server/src/main/java/cn/axzo/im/push/payload/PushPayloadBuilder.java @@ -15,7 +15,7 @@ import com.alibaba.fastjson.JSONObject; */ public interface PushPayloadBuilder> { - T createIntent(PushContent content); + T createIntent(PushPeer peer, PushContent content); void build(PushPeer peer, PushContent content, T intent, JSONObject payload); From 5342e1be545f8291077c7cce91c94a727971b3f5 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 15:11:39 +0800 Subject: [PATCH 78/93] REQ-3201: push --- .../cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java | 2 +- .../src/main/java/cn/axzo/im/controller/MessageController.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java index aaab1de..3f9d7e0 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/AsyncSendMessageParam.java @@ -74,7 +74,7 @@ public class AsyncSendMessageParam { private Integer sendPriority; - private String payload; + private PushContent pushContent; @Data @Builder diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index e9b0aad..3dd5dd4 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -136,6 +136,7 @@ public class MessageController implements MessageApi { // 全员发送是不常用的场景,不应该由业务处理,所以把配置放在bizData里面 .allPerson(sendMessageParam.isAllPerson()) .appTypes(sendMessageParam.getAppTypes()) + .pushContent(sendMessageParam.getPushContent()) .build(); Date now = new Date(); MessageTask messageTask = messageTaskService.create(MessageTask.builder() From f8b48bbc9df1b1a0901dc8cf44d94d44258eb0b2 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 16 Dec 2024 16:00:17 +0800 Subject: [PATCH 79/93] REQ-3201: fix npe --- .../java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 2d06750..8b1eadb 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -266,7 +266,7 @@ public class MessageTaskServiceImpl extends ServiceImpl Date: Tue, 17 Dec 2024 15:34:35 +0800 Subject: [PATCH 80/93] =?UTF-8?q?REQ-3201:=20=E6=92=A4=E5=9B=9E=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/center/api/feign/MessageTool.java | 20 +++ .../api/vo/req/RevokeMessageRequest.java | 33 +++++ .../im/controller/MessageToolController.java | 34 +++++ .../cn/axzo/im/entity/MessageHistory.java | 2 +- .../cn/axzo/im/entity/MessageHistoryCold.java | 2 +- .../cn/axzo/im/entity/NimMessageHistory.java | 14 ++ .../cn/axzo/im/updatable/RevokeService.java | 123 ++++++++++++++++++ .../java/cn/axzo/im/utils/ImProperties.java | 2 +- .../java/cn/axzo/im/utils/RecordCursor.java | 73 +++++++++++ .../cn/axzo/im/utils/ThrowableConsumer.java | 10 ++ 10 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageTool.java create mode 100644 im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/RevokeMessageRequest.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/controller/MessageToolController.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/entity/NimMessageHistory.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/updatable/RevokeService.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/utils/RecordCursor.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/utils/ThrowableConsumer.java diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageTool.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageTool.java new file mode 100644 index 0000000..355544f --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/MessageTool.java @@ -0,0 +1,20 @@ +package cn.axzo.im.center.api.feign; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.im.center.api.vo.req.RevokeMessageRequest; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; + +/** + * @author yanglin + */ +@FeignClient(name = "im-center", url = "${axzo.service.im-center:http://im-center:8080}") +public interface MessageTool { + + @PostMapping("/api/im/message/tool/revoke") + ApiResult revoke(@RequestBody @Valid RevokeMessageRequest request); + +} \ No newline at end of file diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/RevokeMessageRequest.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/RevokeMessageRequest.java new file mode 100644 index 0000000..e916b44 --- /dev/null +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/RevokeMessageRequest.java @@ -0,0 +1,33 @@ +package cn.axzo.im.center.api.vo.req; + +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.Set; + +/** + * @author yanglin + */ +@Setter +@Getter +public class RevokeMessageRequest { + + private Set bizIds; + private String pattern; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @NotNull(message = "since不能为空") + private Date since; + + private int parallelism = 25; + + @Override + public String toString() { + return JSON.toJSONString(this); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageToolController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageToolController.java new file mode 100644 index 0000000..523e05b --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageToolController.java @@ -0,0 +1,34 @@ +package cn.axzo.im.controller; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.im.center.api.feign.MessageTool; +import cn.axzo.im.center.api.vo.req.RevokeMessageRequest; +import cn.axzo.im.updatable.RevokeService; +import cn.axzo.im.utils.BizAssertions; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author yanglin + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class MessageToolController implements MessageTool { + + private final RevokeService revokeService; + + @Override + public ApiResult revoke(RevokeMessageRequest request) { + log.info("Revoke message request:{}", request); + BizAssertions.assertTrue( + CollectionUtils.isNotEmpty(request.getBizIds()) + || StringUtils.isNotBlank(request.getPattern()), + "bizIds and pattern can't be blank at the same time"); + return ApiResult.ok(revokeService.revoke(request)); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index 0d3dd0c..6fc1505 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -32,7 +32,7 @@ import java.util.Optional; @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor -public class MessageHistory implements Serializable { +public class MessageHistory implements Serializable, NimMessageHistory { private static final long serialVersionUID = 1L; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistoryCold.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistoryCold.java index 3f573d0..1ad6d1a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistoryCold.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistoryCold.java @@ -30,7 +30,7 @@ import java.util.Date; @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor -public class MessageHistoryCold implements Serializable { +public class MessageHistoryCold implements Serializable, NimMessageHistory { private static final long serialVersionUID = 1L; diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/NimMessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/NimMessageHistory.java new file mode 100644 index 0000000..16826a2 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/entity/NimMessageHistory.java @@ -0,0 +1,14 @@ +package cn.axzo.im.entity; + +/** + * @author yanglin + */ +public interface NimMessageHistory { + + String getMessageId(); + + String getFromAccount(); + + String getToAccount(); + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/RevokeService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/RevokeService.java new file mode 100644 index 0000000..45bcc78 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/RevokeService.java @@ -0,0 +1,123 @@ +package cn.axzo.im.updatable; + +import cn.axzo.im.center.api.vo.req.RevokeMessageRequest; +import cn.axzo.im.channel.netease.client.NimClient; +import cn.axzo.im.dao.repository.MessageHistoryColdDao; +import cn.axzo.im.dao.repository.MessageHistoryDao; +import cn.axzo.im.entity.MessageHistory; +import cn.axzo.im.entity.MessageHistoryCold; +import cn.axzo.im.entity.NimMessageHistory; +import cn.axzo.im.utils.RecordCursor; +import cn.axzo.im.utils.ThrowableConsumer; +import com.google.common.util.concurrent.RateLimiter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author yanglin + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RevokeService { + + private final NimClient nimClient; + private final MessageHistoryDao messageHistoryDao; + private final MessageHistoryColdDao messageHistoryColdDao; + + private volatile boolean isRunning = false; + + public String revoke(RevokeMessageRequest request) { + if (isRunning) + return "revoke is running..."; + synchronized (this) { + if (isRunning) + return "revoke is running..."; + isRunning = true; + } + try { + log.info("revoke start..."); + revokeImpl(request); + log.info("revoke done normally..."); + } catch (Exception e) { + log.error("revoke failed", e); + return "revoke failed: " + e.getMessage(); + } finally { + log.info("revoke done..."); + synchronized (this) { + isRunning = false; + } + } + return "done..."; + } + + private void revokeImpl(RevokeMessageRequest request) throws Exception { + int threadSize = request.getParallelism() + 3; + ThreadPoolExecutor executor = new ThreadPoolExecutor(threadSize, threadSize, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>()); + + RateLimiter rateLimiter = RateLimiter.create(request.getParallelism()); + ThrowableConsumer> runner = cursor -> { + for (List histories : cursor) { + for (NimMessageHistory history : histories) { + if (StringUtils.isBlank(history.getMessageId())) + continue; + executor.submit(() -> { + try { + rateLimiter.acquire(); + revoke(history); + } catch (Exception e) { + log.error("revoke failed", e); + } + }); + } + } + }; + + runner.accept(hotCursor(request)); + log.info("hot cursor submitted..."); + runner.accept(coldCursor(request)); + log.info("cold cursor submitted..."); + + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.HOURS); + } + + private void revoke(NimMessageHistory history) { + cn.axzo.im.channel.netease.dto.RevokeMessageRequest nimRequest = + new cn.axzo.im.channel.netease.dto.RevokeMessageRequest(); + nimRequest.setMessageId(history.getMessageId()); + nimRequest.setFrom(history.getFromAccount()); + nimRequest.setTo(history.getToAccount()); + NimClient.CodeResponse resp = nimClient.revoke(nimRequest); + log.info("revoke messageId={}, code={}, desc={}", history.getMessageId(), resp.getCode(), resp.getDesc()); + } + + private RecordCursor hotCursor(RevokeMessageRequest request) { + return new RecordCursor<>(MessageHistory::getId, () -> messageHistoryDao.lambdaQuery() + .select(MessageHistory::getMessageId, + MessageHistory::getFromAccount, + MessageHistory::getToAccount) + .in(CollectionUtils.isNotEmpty(request.getBizIds()), MessageHistory::getBizId, request.getBizIds()) + .likeRight(StringUtils.isNoneBlank(request.getPattern()), MessageHistory::getBizId, request.getPattern())); + } + + private RecordCursor coldCursor(RevokeMessageRequest request) { + return new RecordCursor<>(MessageHistoryCold::getId, () -> messageHistoryColdDao.lambdaQuery() + .select(MessageHistoryCold::getMessageId, + MessageHistoryCold::getFromAccount, + MessageHistoryCold::getToAccount) + .in(CollectionUtils.isNotEmpty(request.getBizIds()), MessageHistoryCold::getBizId, request.getBizIds()) + .likeRight(StringUtils.isNoneBlank(request.getPattern()), MessageHistoryCold::getBizId, request.getPattern())); + } + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java index e4373e9..256a6e8 100644 --- a/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ImProperties.java @@ -32,7 +32,7 @@ public class ImProperties { private int messageUpdateAckMaxRetryCount = 3; - private int messageUpdateAckRetryIntervalSeconds = 10; + private int messageUpdateAckRetryIntervalSeconds = 15; /** * 用于避免大事务以及死锁 diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/RecordCursor.java b/im-center-server/src/main/java/cn/axzo/im/utils/RecordCursor.java new file mode 100644 index 0000000..733dfd8 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/utils/RecordCursor.java @@ -0,0 +1,73 @@ +package cn.axzo.im.utils; + +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; + +import javax.validation.constraints.NotNull; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.function.Supplier; + +/** + * @author yanglin + */ +public class RecordCursor implements Iterable> { + + private final int batchSize; + private final SFunction idGetter; + private final Supplier> query; + + public RecordCursor(SFunction idGetter, + Supplier> query) { + this(1000, idGetter, query); + } + + public RecordCursor(int batchSize, + SFunction idGetter, + Supplier> query) { + this.batchSize = batchSize; + this.idGetter = idGetter; + this.query = query; + } + + @Override @NotNull + public Iterator> iterator() { + return new Iterator>() { + + long maxId = 0; + List batch; + Boolean batchConsumed; + + @Override + public boolean hasNext() { + if (batchConsumed == null || batchConsumed) { + batch = nextBatch(); + batchConsumed = false; + } + return !batch.isEmpty(); + } + + @Override + public List next() { + if (!hasNext()) + throw new NoSuchElementException(); + batchConsumed = true; + return batch; + } + + private List nextBatch() { + List cards = query.get() + .gt(idGetter, maxId) + .orderByAsc(idGetter) + .last("LIMIT " + batchSize) + .list(); + if (!cards.isEmpty()) { + T maxIdRecord = cards.get(cards.size() - 1); + maxId = idGetter.apply(maxIdRecord); + } + return cards; + } + }; + } +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/utils/ThrowableConsumer.java b/im-center-server/src/main/java/cn/axzo/im/utils/ThrowableConsumer.java new file mode 100644 index 0000000..27adbbf --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/utils/ThrowableConsumer.java @@ -0,0 +1,10 @@ +package cn.axzo.im.utils; + +/** + * @author yanglin + */ +public interface ThrowableConsumer { + + void accept(T t) throws Exception; + +} From 6be9482e42572ab765a8a7410475ec13e919e7ce Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 17 Dec 2024 15:52:30 +0800 Subject: [PATCH 81/93] =?UTF-8?q?REQ-3201:=20=E6=9F=A5=E8=AF=A2=E7=BE=A4?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/channel/netease/client/NimClient.java | 10 ++++++++++ .../netease/dto/DismissGroupRequest.java | 18 ++++++++++++++++++ .../netease/dto/DismissGroupResponse.java | 13 +++++++++++++ .../netease/dto/GetGroupInfoRequest.java | 18 ++++++++++++++++++ .../netease/dto/GetGroupInfoResponse.java | 16 ++++++++++++++++ .../axzo/im/controller/PrivateController.java | 13 +++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupRequest.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupResponse.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoRequest.java create mode 100644 im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoResponse.java diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/client/NimClient.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/client/NimClient.java index 8a179de..c99ebdf 100644 --- a/im-center-server/src/main/java/cn/axzo/im/channel/netease/client/NimClient.java +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/client/NimClient.java @@ -2,8 +2,12 @@ package cn.axzo.im.channel.netease.client; import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageRequest; import cn.axzo.im.channel.netease.dto.BatchSendCustomMessageResponse; +import cn.axzo.im.channel.netease.dto.DismissGroupRequest; +import cn.axzo.im.channel.netease.dto.DismissGroupResponse; import cn.axzo.im.channel.netease.dto.GetAccountInfoRequest; import cn.axzo.im.channel.netease.dto.GetAccountInfoResponse; +import cn.axzo.im.channel.netease.dto.GetGroupInfoRequest; +import cn.axzo.im.channel.netease.dto.GetGroupInfoResponse; import cn.axzo.im.channel.netease.dto.QueryEventRequest; import cn.axzo.im.channel.netease.dto.QueryEventResponse; import cn.axzo.im.channel.netease.dto.QueryMessageRequest; @@ -56,6 +60,12 @@ public interface NimClient { @PostMapping(value = "/user/updateUinfo.action") UpdateAccountInfoResponse updateAccountInfo(UpdateAccountInfoRequest request); + @PostMapping(value = "/team/remove.action") + DismissGroupResponse dismissGroup(DismissGroupRequest request); + + @PostMapping(value = "/team/queryDetail.action") + GetGroupInfoResponse getGroupInfo(GetGroupInfoRequest request); + @Data class CodeResponse { private Integer code; diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupRequest.java new file mode 100644 index 0000000..ecd14f0 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupRequest.java @@ -0,0 +1,18 @@ +package cn.axzo.im.channel.netease.dto; + +import cn.axzo.im.channel.netease.client.FormRequest; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author yanglin + */ +@Data +@FormRequest +public class DismissGroupRequest { + @NotBlank(message = "群id不能为空") + private String tid; + @NotBlank(message = "群主id不能为空") + private String owner; +} diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupResponse.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupResponse.java new file mode 100644 index 0000000..cfe6047 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/DismissGroupResponse.java @@ -0,0 +1,13 @@ +package cn.axzo.im.channel.netease.dto; + +import cn.axzo.im.channel.netease.client.NimClient; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class DismissGroupResponse extends NimClient.CodeResponse { +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoRequest.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoRequest.java new file mode 100644 index 0000000..4ccd917 --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoRequest.java @@ -0,0 +1,18 @@ +package cn.axzo.im.channel.netease.dto; + +import cn.axzo.im.channel.netease.client.FormRequest; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author yanglin + */ +@Data +@FormRequest +public class GetGroupInfoRequest { + + @NotBlank(message = "群id不能为空") + private String tid; + +} diff --git a/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoResponse.java b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoResponse.java new file mode 100644 index 0000000..bd904ac --- /dev/null +++ b/im-center-server/src/main/java/cn/axzo/im/channel/netease/dto/GetGroupInfoResponse.java @@ -0,0 +1,16 @@ +package cn.axzo.im.channel.netease.dto; + +import cn.axzo.im.channel.netease.client.NimClient; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class GetGroupInfoResponse extends NimClient.CodeResponse { + + private Object tinfo; + +} \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/PrivateController.java b/im-center-server/src/main/java/cn/axzo/im/controller/PrivateController.java index 795abae..5fdb511 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/PrivateController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/PrivateController.java @@ -2,6 +2,8 @@ package cn.axzo.im.controller; import cn.axzo.im.center.api.vo.req.SendMessageParam; import cn.axzo.im.channel.netease.client.NimClient; +import cn.axzo.im.channel.netease.dto.DismissGroupRequest; +import cn.axzo.im.channel.netease.dto.GetGroupInfoRequest; import cn.axzo.im.channel.netease.dto.QueryEventRequest; import cn.axzo.im.channel.netease.dto.QueryMessageRequest; import cn.axzo.im.channel.netease.dto.RevokeMessageRequest; @@ -80,4 +82,15 @@ public class PrivateController { int count = expungeImTaskJob.expunge(param); return CommonResponse.success(count); } + + @PostMapping("/private/group/dismissGroup") + public Object dismissGroup(@Valid @RequestBody DismissGroupRequest request) { + return CommonResponse.success(nimClient.dismissGroup(request)); + } + + @PostMapping("/private/group/getGroupInfo") + public Object getGroupInfo(@Valid @RequestBody GetGroupInfoRequest request) { + return CommonResponse.success(nimClient.getGroupInfo(request)); + } + } \ No newline at end of file From b3bd9de1aa50384c4231bd2dc03ad1260c89876a Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 23 Dec 2024 11:47:32 +0800 Subject: [PATCH 82/93] =?UTF-8?q?REQ-3201:=20=E9=87=8D=E8=AF=95=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=B6=88=E6=81=AF=E4=BD=BF=E7=94=A8=E6=9B=B4?= =?UTF-8?q?=E5=BA=95=E7=9A=84=E4=BC=98=E5=85=88=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/axzo/im/center/api/feign/SendPriority.java | 1 + .../java/cn/axzo/im/updatable/UpdatableMessageManager.java | 2 +- .../src/main/java/cn/axzo/im/updatable/UpdateSupport.java | 6 ++++-- .../axzo/im/updatable/retry/MessageUpdateRetryService.java | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java index ccffcc9..40ac9da 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/feign/SendPriority.java @@ -14,6 +14,7 @@ public class SendPriority { public static final SendPriority TEMPLATE_MESSAGE = create(1000); public static final SendPriority SYSTEM_CUSTOM_MESSAGE = create(5000); public static final SendPriority UPDATE_MESSAGE = create(5500); + public static final SendPriority UPDATE_MESSAGE_RETRY = create(5510); // lower priority than UPDATE_MESSAGE public static final SendPriority OP_MESSAGE = create(500000); private final Integer value; diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 8082973..922b584 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -215,7 +215,7 @@ public class UpdatableMessageManager { updatableMessageDao.getBaseMapper().incrDataVersion(updateIds); messageUpdateRetryService.scheduleNextRetry(updateIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories(request, "createUpdateHistory", validUpdates); + .addUpdateHistories(request, "createUpdateHistory", validUpdates, false); updateSupport.updateHistoryId(result, UpdatableMessage::setUpdateHistoryId); } } diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java index 3e4dcfe..d6935f6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdateSupport.java @@ -46,7 +46,8 @@ public class UpdateSupport { public AddUpdateHistoryResult addUpdateHistories( Object request, String context, - List updates) { + List updates, + boolean isRetry) { List messages = updatableMessageDao .getByBizMessageIds(collectBizMessageIds(updates)); Map bizMessageId2Message = messages @@ -88,7 +89,8 @@ public class UpdateSupport { history.setReceiveOuId(message.getReceiverOuId()); history.setStatus(MessageHistoryStatus.PENDING); history.setBatchNo(batchNo); - history.setSendPriority(SendPriority.UPDATE_MESSAGE.getPriority()); + SendPriority priority = isRetry ? SendPriority.UPDATE_MESSAGE_RETRY : SendPriority.UPDATE_MESSAGE; + history.setSendPriority(priority.getPriority()); history.setApiChannel(ApiChannel.CUSTOM_MESSAGE); HistoryRecordExt recordExt = new HistoryRecordExt(); recordExt.setIsUpdatableMessage(true); diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java index 3c100a8..93326b6 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/retry/MessageUpdateRetryService.java @@ -93,7 +93,7 @@ public class MessageUpdateRetryService { updatableMessageDao.getBaseMapper().incrRetryCount(messageIds); List messages = updatableMessageDao.listByIds(messageIds); AddUpdateHistoryResult result = updateSupport - .addUpdateHistories(null, "createRetryUpdateHistory", messages); + .addUpdateHistories(null, "createRetryUpdateHistory", messages, true); updateSupport.updateHistoryId(result, UpdatableMessage::setRetryHistoryId); } } \ No newline at end of file From ce9692e06810695de7a19337f0b98859419d62e2 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 24 Dec 2024 18:18:46 +0800 Subject: [PATCH 83/93] =?UTF-8?q?REQ-3282:=20fix=20=E5=8F=91=E9=94=99?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/entity/MessageHistory.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java index 6fc1505..c7b9fb3 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageHistory.java @@ -158,6 +158,8 @@ public class MessageHistory implements Serializable, NimMessageHistory { } public Optional determineBatchNo() { + if (isUpdatableMessage()) + return Optional.empty(); String batchNo = this.batchNo; // 兼容在途数据 if (StringUtils.isBlank(batchNo) && imMessageTaskId != null) From d0578022b92ff6be7531c1fb1726da1b62a3efce Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 26 Dec 2024 11:41:03 +0800 Subject: [PATCH 84/93] =?UTF-8?q?REQ-3201:=20fix=20=E5=8F=91=E9=94=99?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/updatable/UpdatableMessageManager.java | 1 + .../im/job/CreateMessageHistoryJobTest.java | 25 ------------------- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 im-center-server/src/test/java/cn/axzo/im/job/CreateMessageHistoryJobTest.java diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 922b584..8f05139 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -244,6 +244,7 @@ public class UpdatableMessageManager { List historyAndMessages = new ArrayList<>(); for (UpdatableMessage message : new ArrayList<>(id2Messages.values())) { List stateHistories = histories.stream() + .filter(history -> message.getBizMessageId().equals(history.getRecordExt().getBizMessageId())) .filter(history -> isSuccess ? history.getStatus() == MessageHistoryStatus.SUCCEED : history.getStatus() == MessageHistoryStatus.FAILED) diff --git a/im-center-server/src/test/java/cn/axzo/im/job/CreateMessageHistoryJobTest.java b/im-center-server/src/test/java/cn/axzo/im/job/CreateMessageHistoryJobTest.java deleted file mode 100644 index b0da6c3..0000000 --- a/im-center-server/src/test/java/cn/axzo/im/job/CreateMessageHistoryJobTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.axzo.im.job; - -import cn.axzo.im.Application; -import lombok.RequiredArgsConstructor; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author yanglin - */ -@SpringBootTest(classes = Application.class) -@RequiredArgsConstructor(onConstructor_ = @Autowired) -class CreateMessageHistoryJobTest { - - private final CreateMessageHistoryJob createMessageHistoryJob; - - @Test - void foo() throws Exception { - createMessageHistoryJob.execute(null); - } - -} \ No newline at end of file From 9be0b5500962ed73f23ded9727c07c36b24ae782 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 26 Dec 2024 12:36:05 +0800 Subject: [PATCH 85/93] =?UTF-8?q?REQ-3201:=20=E6=8C=87=E5=AE=9A=E5=8F=91?= =?UTF-8?q?=E9=80=81=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/center/api/vo/req/SendTemplateMessageParam.java | 2 ++ .../java/cn/axzo/im/controller/MessageController.java | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 7e35fde..22b6eaf 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -25,6 +25,7 @@ public class SendTemplateMessageParam { /** * 发送人 */ + @NotBlank(message = "发送人不能为空") private PersonAccountAttribute sender; /** @@ -47,6 +48,7 @@ public class SendTemplateMessageParam { /** * 消息模板ID */ + @NotBlank(message = "消息模板ID不能为空") private String msgTemplateId; /** diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 3dd5dd4..8af2c59 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -40,7 +40,6 @@ import cn.axzo.im.service.RobotMsgTemplateService; import cn.axzo.im.updatable.UpdatableMessageManager; import cn.axzo.im.updatable.UpdatableMessageQueryService; import cn.axzo.im.updatable.UpdateSupport; -import cn.axzo.im.utils.BizAssertions; import cn.axzo.pokonyan.exception.Aassert; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -178,20 +177,19 @@ public class MessageController implements MessageApi { @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { log.info("sendTemplateMessageAsync, request={}", request); + String templateImAccount = check(request); PersonAccountAttribute sender = request.getSender(); - BizAssertions.assertTrue(sender != null || StringUtils.isNotBlank(request.getMsgTemplateId()), - "消息模板ID和发送人必须选其一"); String sendImAccount; - if (sender != null) { + if (sender.getAppType() != null) { AccountAbsentQuery accountQuery = new AccountAbsentQuery(); accountQuery.setAppType(sender.getAppType().getCode()); accountQuery.setPersonId(sender.getPersonId()); accountQuery.setOuId(sender.getOuId()); + accountQuery.setAppType(sender.getAppType().getCode()); List accounts = accountService.registerAccountIfAbsent(accountQuery); sendImAccount = accounts.get(0).getImAccount(); } else { - //todo: 根据类型验证模版是否配置了机器人 - sendImAccount = check(request); + sendImAccount = templateImAccount; } MessageTask.BizData bizData = MessageTask.BizData.builder() From 91432449113cd049f04f6ead3eed965588cfb9f1 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 26 Dec 2024 13:38:33 +0800 Subject: [PATCH 86/93] =?UTF-8?q?REQ-3201:=20=E6=8C=87=E5=AE=9A=E5=8F=91?= =?UTF-8?q?=E9=80=81=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index 22b6eaf..f53e3f1 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -12,6 +12,7 @@ import lombok.NoArgsConstructor; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -25,7 +26,7 @@ public class SendTemplateMessageParam { /** * 发送人 */ - @NotBlank(message = "发送人不能为空") + @NotNull(message = "发送人不能为空") private PersonAccountAttribute sender; /** From 106a42cc9c19f937b261778adbe15989d85d169d Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 26 Dec 2024 15:32:53 +0800 Subject: [PATCH 87/93] =?UTF-8?q?REQ-3201:=20=E4=BF=9D=E5=AD=98workspaceId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/entity/UpdatableMessage.java | 2 ++ .../main/java/cn/axzo/im/updatable/UpdatableMessageManager.java | 1 + 2 files changed, 3 insertions(+) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java index d10b7f8..e8a9e08 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/UpdatableMessage.java @@ -83,6 +83,7 @@ public class UpdatableMessage implements MessageUpdateInfo { person.setPersonId(receiverPersonId); person.setOuId(receiverOuId); person.setAppType(appType); + person.setWorkspaceId(getOrCreateRecordExt().getReceiverWorkspaceId()); return person; } @@ -111,6 +112,7 @@ public class UpdatableMessage implements MessageUpdateInfo { @Getter @Setter public static class RecordExt { + private Long receiverWorkspaceId; } } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java index 8f05139..3090b60 100644 --- a/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java +++ b/im-center-server/src/main/java/cn/axzo/im/updatable/UpdatableMessageManager.java @@ -93,6 +93,7 @@ public class UpdatableMessageManager { message.setSenderPersonId(request.determineSenderPersonId() + ""); message.setSenderOuId(request.determineSenderOuId()); message.setIsSenderRobot(request.isSendByRobot() ? YesOrNo.YES : YesOrNo.NO); + message.getOrCreateRecordExt().setReceiverWorkspaceId(person.getWorkspaceId()); UpdatableMessageLog messageLog = message.toMessageLog(request); collector.addLog(messageLog); From a289a4a0edda714c56f5903aaed56be4a44a1939 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 26 Dec 2024 16:08:09 +0800 Subject: [PATCH 88/93] =?UTF-8?q?REQ-3201:=20=E4=BF=9D=E5=AD=98workspaceId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/entity/HistoryRecordExt.java | 1 + .../java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java | 1 + .../src/main/java/cn/axzo/im/updatable/InitHistories.java | 1 + 3 files changed, 3 insertions(+) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java index 1b3f501..6a816c0 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/HistoryRecordExt.java @@ -34,4 +34,5 @@ public class HistoryRecordExt { private Boolean isUpdateRetry; private Long updateRetryCount; private Map initMessageExt; + private Long workspaceId; } \ No newline at end of file diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 8b1eadb..7676bea 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -266,6 +266,7 @@ public class MessageTaskServiceImpl extends ServiceImpl oldValue)); } From da5b9573f977eda0027512007bd5d77572df16ca Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 27 Dec 2024 10:24:57 +0800 Subject: [PATCH 89/93] =?UTF-8?q?REQ-3201:=20=E6=B2=A1=E6=9C=89=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E8=B4=A6=E5=8F=B7=E6=98=AF=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=AF=94=E8=BE=83=E5=B0=91=E5=B0=B1=E5=B0=9D?= =?UTF-8?q?=E8=AF=95=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/im/controller/MessageController.java | 10 +---- .../cn/axzo/im/service/AccountService.java | 9 ++++ .../service/impl/MessageTaskServiceImpl.java | 42 ++++++++++++++++--- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index 8af2c59..abea05a 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -7,7 +7,6 @@ import cn.axzo.im.center.api.feign.SendPriority; import cn.axzo.im.center.api.vo.ApiChannel; import cn.axzo.im.center.api.vo.ImErrorCodes; import cn.axzo.im.center.api.vo.PersonAccountAttribute; -import cn.axzo.im.center.api.vo.req.AccountAbsentQuery; import cn.axzo.im.center.api.vo.req.AccountQuery; import cn.axzo.im.center.api.vo.req.AsyncSendMessageParam; import cn.axzo.im.center.api.vo.req.CustomMessageInfo; @@ -181,13 +180,8 @@ public class MessageController implements MessageApi { PersonAccountAttribute sender = request.getSender(); String sendImAccount; if (sender.getAppType() != null) { - AccountAbsentQuery accountQuery = new AccountAbsentQuery(); - accountQuery.setAppType(sender.getAppType().getCode()); - accountQuery.setPersonId(sender.getPersonId()); - accountQuery.setOuId(sender.getOuId()); - accountQuery.setAppType(sender.getAppType().getCode()); - List accounts = accountService.registerAccountIfAbsent(accountQuery); - sendImAccount = accounts.get(0).getImAccount(); + sendImAccount = accountService.registerAccountIfAbsent( + sender.getPersonId(), sender.getOuId(), sender.getAppType()); } else { sendImAccount = templateImAccount; } 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 772b521..ea565cc 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 @@ -377,6 +377,15 @@ public class AccountService { return Lists.newArrayList(); } + public String registerAccountIfAbsent(String personId, Long ouId, AppTypeEnum appType) { + AccountAbsentQuery accountQuery = new AccountAbsentQuery(); + accountQuery.setPersonId(personId); + accountQuery.setOuId(ouId); + accountQuery.setAppType(appType.getCode()); + List accounts = registerAccountIfAbsent(accountQuery); + return accounts.get(0).getImAccount(); + } + /** * 查询用户注册IM账户信息,如果没有则进行注册 * diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 7676bea..8ec1800 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -36,6 +36,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.RateLimiter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -72,6 +73,10 @@ public class MessageTaskServiceImpl extends ServiceImpl receivePersons = page.getRecords().stream() .map(e -> MessageTask.ReceivePerson.builder().imAccount(e.getImAccount()).build()) .collect(Collectors.toList()); - saveMessageHistory(batchNo, receivePersons, messageTask); + saveMessageHistory(batchNo, receivePersons, messageTask, false); } if (!page.hasNext()) { @@ -161,14 +166,15 @@ public class MessageTaskServiceImpl extends ServiceImpl> receivePersons = Lists.partition(messageTask.getReceivePersons(), DEFAULT_PAGE_SIZE); - receivePersons.forEach(e -> saveMessageHistory(batchNo, e, messageTask)); + receivePersons.forEach(e -> saveMessageHistory(batchNo, e, messageTask, totalPersonSize <= 2)); } private void saveMessageHistory(String batchNo, List receivePersons, - MessageTask messageTask) { + MessageTask messageTask, boolean tryCreateAccount) { // 排除已经发送成功的记录,防止重复发送 Set existPersons = listExistPerson(receivePersons, messageTask); Set existImAccounts = listExistImAccount(receivePersons, messageTask); @@ -196,7 +202,7 @@ public class MessageTaskServiceImpl extends ServiceImpl imAccounts = listImAccount(absentReceivePersons); List messageHistories = absentReceivePersons.stream() - .map(receivePerson -> resolveMessageHistory(batchNo, messageTask, receivePerson, imAccounts, accountRegisters, ouIdMap)) + .map(receivePerson -> resolveMessageHistory(batchNo, messageTask, receivePerson, imAccounts, accountRegisters, ouIdMap, tryCreateAccount)) .collect(Collectors.toList()); messageHistoryService.createBatch(messageHistories); List historyIds = messageHistories.stream() @@ -210,7 +216,8 @@ public class MessageTaskServiceImpl extends ServiceImpl imAccounts, Map accountRegisters, - Map ouIdMap) { + Map ouIdMap, + boolean tryCreateAccount) { MessageHistory messageHistory = new MessageHistory(); messageHistory.setBizId(Optional.ofNullable(messageTask.getBizId()).orElseGet(() -> messageTask.getId().toString())); messageHistory.setImMessageTaskId(messageTask.getId()); @@ -243,9 +250,32 @@ public class MessageTaskServiceImpl extends ServiceImpl 0; + if (StringUtils.isBlank(imAccount) + && receivePerson.getAppType() == AppTypeEnum.CMP + && tryCreateAccount + && ouValid) { + if (createRateLimiter.tryAcquire()) { + try { + imAccount = accountService.registerAccountIfAbsent( + messageHistory.getReceivePersonId(), + messageHistory.getReceiveOuId(), + receivePerson.getAppType()); + messageHistory.setToAccount(imAccount); + } catch (Exception e) { + log.warn("创建账号失败", e); + externalMessage = "(未尝试创建账号失败: " + e.getMessage() + ")"; + } + } else { + externalMessage = "(未尝试创建账号: 限流)"; + } + } + if (StringUtils.isBlank(imAccount)) { messageHistory.setToAccount(""); - messageHistory.setResult(AccountService.NO_IM_ACCOUNT_MESSAGE); + messageHistory.setResult(AccountService.NO_IM_ACCOUNT_MESSAGE + externalMessage); messageHistory.setStatus(MessageHistoryStatus.FAILED); messageHistory.getOrCreateRecordExt().setImAccountInfo(key); } From 693f7441d577c5dcd3cf17e4a4376f1ae9f400ff Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 27 Dec 2024 14:30:33 +0800 Subject: [PATCH 90/93] =?UTF-8?q?REQ-3201:=20=E6=B2=A1=E6=9C=89=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E8=B4=A6=E5=8F=B7=E6=98=AF=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=AF=94=E8=BE=83=E5=B0=91=E5=B0=B1=E5=B0=9D?= =?UTF-8?q?=E8=AF=95=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 8ec1800..2ae8e21 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -266,7 +266,7 @@ public class MessageTaskServiceImpl extends ServiceImpl Date: Fri, 3 Jan 2025 15:30:22 +0800 Subject: [PATCH 91/93] =?UTF-8?q?REQ-3201:=20=E6=9C=AA=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E5=8F=91=E9=80=81=E4=BA=BA=E6=97=B6=E6=89=8D=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/axzo/im/controller/MessageController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java index abea05a..0d09d2c 100644 --- a/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java +++ b/im-center-server/src/main/java/cn/axzo/im/controller/MessageController.java @@ -176,14 +176,13 @@ public class MessageController implements MessageApi { @Transactional public ApiResult sendTemplateMessageAsync(SendTemplateMessageParam request) { log.info("sendTemplateMessageAsync, request={}", request); - String templateImAccount = check(request); PersonAccountAttribute sender = request.getSender(); String sendImAccount; if (sender.getAppType() != null) { sendImAccount = accountService.registerAccountIfAbsent( sender.getPersonId(), sender.getOuId(), sender.getAppType()); } else { - sendImAccount = templateImAccount; + sendImAccount = check(request); } MessageTask.BizData bizData = MessageTask.BizData.builder() From c7673df60889e3e5fd90d016cf2f25dd651ebd03 Mon Sep 17 00:00:00 2001 From: yanglin Date: Fri, 3 Jan 2025 18:04:31 +0800 Subject: [PATCH 92/93] =?UTF-8?q?REQ-3201:=20=E6=9C=AA=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E5=8F=91=E9=80=81=E4=BA=BA=E6=97=B6=E6=89=8D=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java index f53e3f1..7cb48c0 100644 --- a/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java +++ b/im-center-api/src/main/java/cn/axzo/im/center/api/vo/req/SendTemplateMessageParam.java @@ -78,7 +78,7 @@ public class SendTemplateMessageParam { private boolean isUpdatable; public boolean isSendByRobot() { - return sender == null; + return sender.getAppType() == null; } private PushContent pushContent; From c0ae46e9854477864af5d55b1461d426d0f55897 Mon Sep 17 00:00:00 2001 From: yanglin Date: Tue, 7 Jan 2025 15:27:26 +0800 Subject: [PATCH 93/93] REQ-3201: fix bugs --- .../src/main/java/cn/axzo/im/entity/MessageTask.java | 4 ++++ .../java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java index 95ed465..7415b57 100644 --- a/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java +++ b/im-center-server/src/main/java/cn/axzo/im/entity/MessageTask.java @@ -162,6 +162,10 @@ public class MessageTask { private Boolean isSenderRobot; + public boolean determineIsSenderRobot() { + return isSenderRobot != null && isSenderRobot; + } + public TemplatedMsgType determineTemplatedMsgType() { return templatedMsgType == null ? TemplatedMsgType.TEMPLATE : templatedMsgType; } diff --git a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java index 2ae8e21..ff971bf 100644 --- a/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java +++ b/im-center-server/src/main/java/cn/axzo/im/service/impl/MessageTaskServiceImpl.java @@ -405,7 +405,7 @@ public class MessageTaskServiceImpl extends ServiceImpl