Merge branch 'feature/REQ-1507' of axzsource.com:universal/infrastructure/backend/msg-center-plat into dev

This commit is contained in:
luofu 2023-11-22 11:41:49 +08:00
commit 8f02cd393c
10 changed files with 212 additions and 34 deletions

View File

@ -1,13 +1,27 @@
package cn.axzo.msg.center.message.controller;
import cn.axzo.msg.center.dal.MessageRouteDetailDao;
import cn.axzo.msg.center.dal.MessageRouterConfigDao;
import cn.axzo.msg.center.dal.MessageTemplateRouterDao;
import cn.axzo.msg.center.domain.entity.MessageRouteDetail;
import cn.axzo.msg.center.domain.entity.MessageRouterConfig;
import cn.axzo.msg.center.domain.entity.MessageTemplateRouter;
import cn.axzo.msg.center.message.service.PendingMessageDataInitService;
import cn.axzo.msg.center.service.admin.MessageAdminConsoleClient;
import cn.axzo.msg.center.service.enums.BizDetailShowStrategyEnum;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* msg-center
@ -21,12 +35,61 @@ import javax.annotation.Resource;
@RequiredArgsConstructor
public class MessageAdminController implements MessageAdminConsoleClient {
@Resource
private PendingMessageDataInitService pendingMessageDataInitService;
private final PendingMessageDataInitService pendingMessageDataInitService;
private final MessageTemplateRouterDao messageTemplateRouterDao;
private final MessageRouteDetailDao messageRouteDetailDao;
private final MessageRouterConfigDao messageRouterConfigDao;
@Override
public CommonResponse<Void> transformPendingMessageRecord(Integer diffDays, Long minRecordId) {
pendingMessageDataInitService.transformPendingMessageRecord(diffDays, minRecordId);
return CommonResponse.success();
}
@PostMapping(value = "/admin/console/template/init-router", produces = {MediaType.APPLICATION_JSON_VALUE})
@Transactional(rollbackFor = Exception.class)
public CommonResponse<Void> initTemplateRouter() {
List<MessageTemplateRouter> routers = messageTemplateRouterDao.lambdaQuery()
.eq(MessageTemplateRouter::getIsDelete, 0)
.list();
Set<String> templates = messageRouteDetailDao.lambdaQuery()
.select(MessageRouteDetail::getTemplateCode)
.list().stream()
.map(MessageRouteDetail::getTemplateCode)
.collect(Collectors.toSet());
routers = routers.stream().filter(e -> !templates.contains(e.getTemplateCode())).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(routers)) {
batchSave(routers);
}
return CommonResponse.success();
}
private void batchSave(List<MessageTemplateRouter> routers) {
Map<String, List<MessageTemplateRouter>> map = routers.stream()
.collect(Collectors.groupingBy(MessageTemplateRouter::getTemplateCode));
List<MessageRouteDetail> routeDetails = map.values().stream()
.map(e -> convertRouteDetail(e.get(0)))
.collect(Collectors.toList());
List<MessageRouterConfig> routerConfigs = routers.stream()
.map(this::convertRouterConfig)
.collect(Collectors.toList());
messageRouteDetailDao.saveBatch(routeDetails);
messageRouterConfigDao.saveBatch(routerConfigs);
}
private MessageRouteDetail convertRouteDetail(MessageTemplateRouter router) {
return MessageRouteDetail.builder()
.templateCode(router.getTemplateCode())
.name(router.getName())
.showStrategy(BizDetailShowStrategyEnum.JUMP_TO)
.build();
}
private MessageRouterConfig convertRouterConfig(MessageTemplateRouter router) {
return MessageRouterConfig.builder()
.routerCode(router.getTemplateCode())
.url(router.getUrl())
.terminalType(router.getTerminalType())
.build();
}
}

View File

@ -69,6 +69,15 @@ public class MessageTemplateRouterDTO implements Serializable {
return Optional.empty();
}
public MessageTemplateRouterDTO deepClone() {
List<MessageRouteButtonDTO> buttons = CollectionUtils.isEmpty(this.routeButtons) ? Collections.emptyList()
: this.routeButtons.stream().map(MessageRouteButtonDTO::deepClone).collect(Collectors.toList());
return MessageTemplateRouterDTO.builder()
.routeDetail(Optional.ofNullable(this.routeDetail).map(MessageRouteDetailDTO::deepClone).orElse(null))
.routeButtons(buttons)
.build();
}
@Override
public String toString() {
return JSON.toJSONString(this);
@ -149,6 +158,17 @@ public class MessageTemplateRouterDTO implements Serializable {
return MessageDetailRouteStrategyDTO.builder().showStrategy(this.showStrategy).terminals(terminals).build();
}
public MessageRouteDetailDTO deepClone() {
List<MessageRouterConfigDTO> configs = CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList()
: this.routerConfigs.stream().map(MessageRouterConfigDTO::deepClone).collect(Collectors.toList());
return MessageRouteDetailDTO.builder()
.name(this.name)
.templateCode(this.templateCode)
.showStrategy(this.showStrategy)
.routerConfigs(configs)
.build();
}
@Override
public String toString() {
return JSON.toJSONString(this);
@ -283,6 +303,22 @@ public class MessageTemplateRouterDTO implements Serializable {
.build();
}
public MessageRouteButtonDTO deepClone() {
List<MessageRouterConfigDTO> configs = CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList()
: this.routerConfigs.stream().map(MessageRouterConfigDTO::deepClone).collect(Collectors.toList());
return MessageRouteButtonDTO.builder()
.name(this.name)
.btnCode(this.btnCode)
.templateCode(this.templateCode)
.source(this.source)
.category(this.category)
.apiUrl(this.apiUrl)
.style(Optional.ofNullable(this.style).map(JSONArray::new).orElse(null))
.priority(this.priority)
.routerConfigs(configs)
.build();
}
@Override
public String toString() {
return JSON.toJSONString(this);
@ -326,6 +362,14 @@ public class MessageTemplateRouterDTO implements Serializable {
.build();
}
public MessageRouterConfigDTO deepClone() {
return MessageRouterConfigDTO.builder()
.routerCode(this.routerCode)
.terminalType(this.terminalType)
.url(this.url)
.build();
}
@Override
public String toString() {
return JSON.toJSONString(this);

View File

@ -59,7 +59,7 @@ public interface MessageTemplateNewService {
* @param msgTemplateCode 模板编码
* @return 模板信息
*/
Optional<MessageTemplateDTO> queryByTemplateCode(String msgTemplateCode);
Optional<MessageTemplateDTO> queryEnableTemplateByCode(String msgTemplateCode);
/**
* 通过模板编码查询模板信息

View File

@ -85,7 +85,7 @@ public class GeneralMessageServiceImpl implements GeneralMessageService {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public List<SendImMessageDTO> batchSendMessage(GeneralMessageSendRequest request) {
// 查询模板基础信息
MessageTemplateDTO template = messageTemplateNewService.queryByTemplateCode(request.getTemplateCode())
MessageTemplateDTO template = messageTemplateNewService.queryEnableTemplateByCode(request.getTemplateCode())
.orElseThrow(() -> new ServiceException("未查询到对应的模板"));
// 构建消息记录并存储
List<GeneralMessageRecord> messageRecords = buildMessageRecord(request, template);

View File

@ -75,7 +75,6 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
@Override
@Transactional(rollbackFor = Exception.class)
public String createTemplate(MessageTemplateSaveOrUpdateParam param) {
// 参数检测 TODO:[cold_blade] [P0]
// 创建模板基础数据
String templateCode = saveTemplate(param);
// 创建模板的路由数据
@ -133,7 +132,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
}
@Override
public Optional<MessageTemplateDTO> queryByTemplateCode(String msgTemplateCode) {
public Optional<MessageTemplateDTO> queryEnableTemplateByCode(String msgTemplateCode) {
if (StringUtils.isBlank(msgTemplateCode)) {
log.info("msgTemplateCode is blank.");
return Optional.empty();
@ -141,6 +140,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
MessageBaseTemplate msgBaseTemplate = messageBaseTemplateDao.lambdaQuery()
.eq(MessageBaseTemplate::getCode, msgTemplateCode)
.eq(MessageBaseTemplate::getIsDelete, 0)
.eq(MessageBaseTemplate::getStatus, StatusEnum.ENABLE)
.one();
if (Objects.isNull(msgBaseTemplate)) {
log.info("not find the [{}] template.", msgTemplateCode);

View File

@ -1,5 +1,10 @@
package cn.axzo.msg.center.message.service.impl;
import cn.axzo.apollo.core.web.Result;
import cn.axzo.apollo.workspace.api.workspace.OrganizationalNodePractitionerWideApi;
import cn.axzo.apollo.workspace.api.workspace.req.OrganizationalNodePractitionerWideReqParams;
import cn.axzo.apollo.workspace.api.workspace.res.OrganizationalNodePractitionerWideDto;
import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.api.enums.MsgTypeEnum;
import cn.axzo.msg.center.common.exception.ServiceException;
import cn.axzo.msg.center.dal.MessageRecordDao;
@ -17,16 +22,18 @@ import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.utils.DateFormatUtil;
import cn.axzo.msg.center.utils.PersonIdentityUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@ -42,8 +49,9 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PendingMessageDataInitServiceImpl implements PendingMessageDataInitService {
@Resource
private Environment environment;
private final Environment environment;
private final OrganizationalNodePractitionerWideApi organizationalNodePractitionerWideApi;
private static final PersonDTO SYSTEM = PersonDTO.builder()
.id(0L)
@ -66,14 +74,15 @@ public class PendingMessageDataInitServiceImpl implements PendingMessageDataInit
.lt(MessageRecord::getId, scrollId)
.gt(MessageRecord::getId, minRecordId)
.orderByDesc(MessageRecord::getId)
.last("LIMIT 1000")
.last("LIMIT 2000")
.list();
final Map<String, Long> ouIdMap = Maps.newHashMap();
while (!records.isEmpty()) {
String profile = environment.getProperty("spring.profiles.active");
if ("test".equals(profile) || "master".equals(profile)) {
batchSavePendingMessage(records);
if ("dev".equals(profile)) {
log.warn("系统环境[" + profile + "],不支持该接口操作");
} else {
log.warn("目前只有测试test环境和生产master环境支持该操作");
batchSavePendingMessage(records, ouIdMap);
}
scrollId = records.stream().min(Comparator.comparing(MessageRecord::getId))
.map(MessageRecord::getId).orElse(0L);
@ -85,22 +94,63 @@ public class PendingMessageDataInitServiceImpl implements PendingMessageDataInit
.lt(MessageRecord::getId, scrollId)
.gt(MessageRecord::getId, minRecordId)
.orderByDesc(MessageRecord::getId)
.last("LIMIT 1000")
.last("LIMIT 2000")
.list();
}
}
private void batchSavePendingMessage(List<MessageRecord> records) {
private void batchSavePendingMessage(List<MessageRecord> records, Map<String, Long> ouIdMap) {
if (CollectionUtils.isEmpty(records)) {
return;
}
List<PendingMessageRecord> pendingMessageRecords = records.stream()
.map(this::convert)
.filter(e -> StringUtils.isNotBlank(e.getTemplateCode()))
.collect(Collectors.toList());
log.info("[cold_blade] transform rows:[{}]", pendingMessageRecords.size());
Map<String, Long> executorIdWorkspceIdMap = pendingMessageRecords.stream()
.filter(e -> Objects.equals(e.getExecutorType(), IdentityTypeEnum.PRACTITIONER))
.filter(e -> !ouIdMap.containsKey(getKey(e)))
.collect(Collectors.toMap(this::getKey, PendingMessageRecord::getExecutorId, (cur, next) -> next));
Map<String, Long> map = fetchOuId(executorIdWorkspceIdMap);
if (MapUtil.isNotEmpty(map)) {
ouIdMap.putAll(map);
}
// 设置ouId
pendingMessageRecords.stream()
.filter(e -> ouIdMap.containsKey(getKey(e)))
.forEach(e -> e.setOuId(ouIdMap.get(getKey(e))));
//批量插入数据库
pendingMessageRecordDao.saveOrUpdateBatch(pendingMessageRecords);
}
private Map<String, Long> fetchOuId(Map<String, Long> executorIdWorkspceIdMap) {
if (MapUtil.isEmpty(executorIdWorkspceIdMap)) {
return Collections.emptyMap();
}
OrganizationalNodePractitionerWideReqParams param = new OrganizationalNodePractitionerWideReqParams();
param.setIdentityIds(Lists.newArrayList(executorIdWorkspceIdMap.values()));
Result<List<OrganizationalNodePractitionerWideDto>> result = organizationalNodePractitionerWideApi.getList(param);
if (CollectionUtils.isEmpty(result.getData())) {
log.info("errCode:[{}], msg:{}", result.getCode(), result.getMsg());
return Collections.emptyMap();
}
Map<String, List<OrganizationalNodePractitionerWideDto>> groupingBy = result.getData().stream()
.filter(e -> executorIdWorkspceIdMap.containsKey(getKey(e)))
.collect(Collectors.groupingBy(this::getKey));
return groupingBy.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0).getOrganizationalUnitId()));
}
private String getKey(PendingMessageRecord record) {
return record.getExecutorId() + "_" + record.getOrgId();
}
private String getKey(OrganizationalNodePractitionerWideDto dto) {
return dto.getIdentityId() + "_" + dto.getWorkspaceId();
}
private PendingMessageRecord convert(MessageRecord record) {
PendingMessageRecord pendingMsgRecord = new PendingMessageRecord();
pendingMsgRecord.setPromoterPersonId(SYSTEM.getId());
@ -132,14 +182,12 @@ public class PendingMessageDataInitServiceImpl implements PendingMessageDataInit
}
private String getTemplateCode(Long relationId) {
// TODO: [zuoqinbo]
// 需要通过映射表获取 https://alidocs.dingtalk.com/i/nodes/YndMj49yWj7q1dB9h793q5dKV3pmz5aA?iframeQuery=utm_source%3Dportal%26utm_medium%3Dportal_recent
String profile = environment.getProperty("spring.profiles.active");
if ("test".equals(profile) || "master".equals(profile)) {
String templateCode = msgTemplateMap.get(Integer.parseInt(String.valueOf(relationId)));
return templateCode;
if ("dev".equals(profile)) {
throw new ServiceException("系统环境[" + profile + "],不支持该接口操作");
}
throw new ServiceException("系统环境[" + profile + "],不支持该接口操作");
return msgTemplateMap.get(Integer.parseInt(String.valueOf(relationId)));
}
/**

View File

@ -1,7 +1,10 @@
package cn.axzo.msg.center.message.service.impl;
import cn.axzo.apollo.core.web.Result;
import cn.axzo.apollo.workspace.api.workspace.OrganizationalNodePractitionerWideApi;
import cn.axzo.apollo.workspace.api.workspace.WorkspaceApi;
import cn.axzo.apollo.workspace.api.workspace.req.OrganizationalNodePractitionerWideReqParams;
import cn.axzo.apollo.workspace.api.workspace.res.OrganizationalNodePractitionerWideDto;
import cn.axzo.apollo.workspace.api.workspace.res.SimpleWorkspaceRes;
import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.api.enums.MsgRecordTerminalTypeEnum;
@ -52,6 +55,7 @@ import cn.axzo.msg.center.utils.MessageRouterUtil;
import cn.axzo.msg.center.utils.OrderFieldParseUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import cn.azxo.framework.common.model.Page;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
@ -90,6 +94,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
private final MessageTemplateNewService messageTemplateNewService;
private final MessageTemplateGroupService messageTemplateGroupService;
private final WorkspaceApi workspaceApi;
private final OrganizationalNodePractitionerWideApi organizationalNodePractitionerWideApi;
@Override
public List<PendingMessageStatisticDTO> groupStatistic(MessageGroupNodeStatisticParam param) {
@ -274,7 +279,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
@Override
public List<PushPendingMessageDTO> push(PendingMessagePushParam param) {
MessageTemplateDTO msgTemplate = messageTemplateNewService
.queryByTemplateCode(param.getTemplateCode())
.queryEnableTemplateByCode(param.getTemplateCode())
.orElseThrow(() -> new ServiceException("not found message template."));
// 生成requestNo
String requestNo = UUIDUtil.uuidString();
@ -289,7 +294,6 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
}
List<PendingMessageRecord> record = convert(param, msgTemplate, requestNo, workspace);
pendingMessageRecordDao.saveBatch(record);
// TODO 消息推送 @luofu
return record.stream().map(e -> PushPendingMessageDTO.builder()
.id(e.getId())
.executorId(e.getExecutorId())
@ -531,7 +535,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
record.setRequestNo(requestNo);
record.setState(PendingMessageStateEnum.HAS_BEEN_SENT);
// 构建代办记录的人维度的相关信息
buildPersonInfo(record, param.getPromoter(), executor);
buildPersonInfo(record, param.getPromoter(), executor,workspace.getId());
// 构建模板信息
buildTemplateInfo(record, msgTemplate, param.getBizExtParams());
// 构建代办所属企业/项目等相关信息
@ -546,7 +550,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
}).collect(Collectors.toList());
}
private void buildPersonInfo(PendingMessageRecord record, PersonDTO promoter, PersonDTO executor) {
private void buildPersonInfo(PendingMessageRecord record, PersonDTO promoter, PersonDTO executor, Long workspaceId) {
record.setPromoterId(promoter.getIdentity().getId());
record.setPromoterType(promoter.getIdentity().getType());
record.setPromoterPersonId(promoter.getId());
@ -555,9 +559,26 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
record.setExecutorType(executor.getIdentity().getType());
record.setExecutorPersonId(executor.getId());
record.setExecutorName(executor.getName());
// 执行人身份是从业人员查询ouId
record.setOuId(getPractitionOuId(executor,workspaceId));
}
private Long getPractitionOuId(PersonDTO executor, Long workspaceId) {
// 从业人员需要获取ouId
if(IdentityTypeEnum.PRACTITIONER == executor.getIdentity().getType()){
OrganizationalNodePractitionerWideReqParams param = new OrganizationalNodePractitionerWideReqParams();
param.setWorkspaceId(workspaceId);
param.setIdentityId(executor.getIdentity().getId());
log.info("get ouId params->{}", JSON.toJSONString(param));
Result<OrganizationalNodePractitionerWideDto> result = organizationalNodePractitionerWideApi.getOne(param);
log.info("get ouId result->{}", JSON.toJSONString(result));
return result.getData().getOrganizationalUnitId();
}
return null;
}
private void buildTemplateInfo(PendingMessageRecord record, MessageTemplateDTO msgTemplate, String bizParam) {
JSONObject bizExtParam = JSONObjectUtil.parseObject(bizParam);
String title = PlaceholderResolver

View File

@ -1,6 +1,5 @@
package cn.axzo.msg.center.utils;
import cn.axzo.msg.center.common.utils.BeanConvertUtils;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO.MessageRouteButtonDTO;
@ -133,10 +132,13 @@ public final class MessageRouterUtil {
*/
public static MessageTemplateRouterDTO parseAndConcatRouteUrl(MessageTemplateRouterDTO msgTemplateRouter,
JSONObject routerParam) {
if (Objects.isNull(msgTemplateRouter)) {
return msgTemplateRouter;
}
// 拷贝一份避免修改入参
msgTemplateRouter = msgTemplateRouter.deepClone();
// 路由参数有效
if (Objects.nonNull(routerParam) && Objects.nonNull(msgTemplateRouter)) {
// 拷贝一份避免修改入参
msgTemplateRouter = BeanConvertUtils.copyBean(msgTemplateRouter, MessageTemplateRouterDTO.class);
if (Objects.nonNull(routerParam)) {
// 编排业务详情路由
parseAndConcatDetailRouterUrl(msgTemplateRouter.getRouteDetail(), routerParam);
// 编排路由按钮

View File

@ -67,8 +67,8 @@ public final class TreeHelperUtil {
LinkedList<GroupTreeNodeDTO> stack = new LinkedList<>(treeNode.getNodeChildren());
while (!stack.isEmpty()) {
GroupTreeNodeDTO node = stack.pop();
if (node.canMountTemplate()) {
return node.getNodeName().contains(mountTemplateNodeName);
if (node.canMountTemplate() && node.getNodeName().contains(mountTemplateNodeName)) {
return true;
}
stack.addAll(node.getNodeChildren());
}

View File

@ -36,7 +36,7 @@ public interface MessageAdminConsoleClient {
* @return 消息的唯一标识
*/
@PostMapping("/admin/console/transform")
CommonResponse<Void> transformPendingMessageRecord(@RequestParam(required = true) Integer diffDays,
@RequestParam(required = true) Long minRecordId);
CommonResponse<Void> transformPendingMessageRecord(@RequestParam Integer diffDays,
@RequestParam Long minRecordId);
}