REQ-2135: 方方面面

This commit is contained in:
yanglin 2024-03-22 15:42:29 +08:00
parent dd74b74505
commit c608e745a1
32 changed files with 369 additions and 324 deletions

View File

@ -6,8 +6,8 @@ import cn.axzo.basics.profiles.dto.basic.PersonProfileDto;
import cn.axzo.msg.center.api.request.v3.SearchPendingMessageReq;
import cn.axzo.msg.center.api.response.v3.SearchPendingMessageResp;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.dal.PendingMessageRecordDao;
import cn.axzo.msg.center.domain.entity.PendingMessageRecord;
import cn.axzo.msg.center.dal.TodoDao;
import cn.axzo.msg.center.domain.entity.Todo;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
@ -21,36 +21,32 @@ import java.util.List;
*/
@Component
@RequiredArgsConstructor
public class PendingMessageSearchService {
public class TodoSearchService {
private final PendingMessageRecordDao pendingMessageRecordDao;
private final TodoDao todoDao;
private final UserProfileServiceApi userProfileServiceApi;
public List<SearchPendingMessageResp> search(SearchPendingMessageReq req) {
if (req.getLimit() >= 100) {
req.setLimit(100);
}
if (req.getPromoterPersonId() == null && StringUtils.isNotBlank(req.getPromoterPhone())) {
req.setPromoterPersonId(getPersonIdByPhone(req.getPromoterPhone()));
}
if (req.getExecutorPersonId() == null && StringUtils.isNotBlank(req.getExecutorPhone())) {
req.setExecutorPersonId(getPersonIdByPhone(req.getExecutorPhone()));
}
List<PendingMessageRecord> records = pendingMessageRecordDao.lambdaQuery()
.in(CollectionUtils.isNotEmpty(req.getIdentityCodes()), PendingMessageRecord::getIdentityCode, req.getIdentityCodes())
.eq(req.getPromoterPersonId() != null, PendingMessageRecord::getPromoterPersonId, req.getPromoterPersonId())
.eq(req.getExecutorPersonId() != null, PendingMessageRecord::getExecutorPersonId, req.getExecutorPersonId())
.eq(req.getOuId() != null, PendingMessageRecord::getOuId, req.getOuId())
.eq(req.getState() != null, PendingMessageRecord::getState, req.getState())
.eq(req.getBizFinalState() != null, PendingMessageRecord::getBizFinalState, req.getBizFinalState())
.eq(StringUtils.isNotBlank(req.getBizCode()), PendingMessageRecord::getBizCode, req.getBizCode())
.eq(StringUtils.isNotBlank(req.getSubBizCode()), PendingMessageRecord::getSubBizCode, req.getSubBizCode())
.eq(StringUtils.isNotBlank(req.getTemplateCode()), PendingMessageRecord::getTemplateCode, req.getTemplateCode())
.like(StringUtils.isNotBlank(req.getTitle()), PendingMessageRecord::getTitle, req.getTitle())
.like(StringUtils.isNotBlank(req.getContent()), PendingMessageRecord::getContent, req.getContent())
.ge(req.getStartTime() != null, PendingMessageRecord::getCreateAt, req.getStartTime())
.le(req.getEndTime() != null, PendingMessageRecord::getCreateAt, req.getEndTime())
.orderByDesc(PendingMessageRecord::getId)
List<Todo> records = todoDao.lambdaQuery()
.in(Todo::getType, req.determineTodoTypes())
.eq(req.getExecutorPersonId() != null, Todo::getExecutorPersonId, req.getExecutorPersonId())
.eq(req.getOuId() != null, Todo::getOuId, req.getOuId())
.eq(req.getState() != null, Todo::getState, req.getState())
.eq(StringUtils.isNotBlank(req.getBizCode()), Todo::getBizCode, req.getBizCode())
.eq(StringUtils.isNotBlank(req.getSubBizCode()), Todo::getSubBizCode, req.getSubBizCode())
.eq(StringUtils.isNotBlank(req.getTemplateCode()), Todo::getTemplateCode, req.getTemplateCode())
.in(CollectionUtils.isNotEmpty(req.getIdentityCodes()), Todo::getIdentityCode, req.getIdentityCodes())
.like(StringUtils.isNotBlank(req.getTitle()), Todo::getTitle, req.getTitle())
.like(StringUtils.isNotBlank(req.getContent()), Todo::getContent, req.getContent())
.ge(req.getStartTime() != null, Todo::getCreateAt, req.getStartTime())
.le(req.getEndTime() != null, Todo::getCreateAt, req.getEndTime())
.orderByDesc(Todo::getId)
.last("LIMIT " + req.getLimit())
.list();
return BeanMapper.copyList(records, SearchPendingMessageResp.class);

View File

@ -79,7 +79,7 @@ public class PendingMessageNewController implements PendingMessageClient {
@Override
public CommonResponse<List<PendingMessageResponse>> getPendingMessageByAppWorker(PendingMessageFixedTemplatePageRequest request) {
log.info("getPendingMessageByAppWorker, request={}", JSON.toJSONString(request));
return CommonResponse.success(todoRangeQueryService.getPendingMessageByAppWorker(request));
return CommonResponse.success(todoRangeQueryService.getWorkerCalendar(request));
}
@Override
@ -100,8 +100,10 @@ public class PendingMessageNewController implements PendingMessageClient {
}
@Override
public CommonResponse<PendingMessageResponse> getBusinessDetailByBizCode(String templateCode, String bizCode) {
PendingMessageResponse response = todoSimpleQueryService.getBusinessDetailByBizCode(templateCode, bizCode);
public CommonResponse<PendingMessageResponse> getLatestTodoByBiz(
String templateCode, String bizCode, String subBizCode) {
PendingMessageResponse response = todoSimpleQueryService
.getLatestTodoByBiz(templateCode, bizCode, subBizCode);
return CommonResponse.success(response);
}

View File

@ -4,7 +4,7 @@ import cn.axzo.msg.center.api.MessageAPIV3;
import cn.axzo.msg.center.api.request.v3.MessageSendReqV3;
import cn.axzo.msg.center.api.request.v3.SearchMessageReqV3;
import cn.axzo.msg.center.api.request.v3.SearchPendingMessageReq;
import cn.axzo.msg.center.inside.notices.service.impl.PendingMessageSearchService;
import cn.axzo.msg.center.inside.notices.service.impl.TodoSearchService;
import cn.axzo.msg.center.inside.notices.service.impl.v3.MessageRecordServiceV3;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.message.service.todo.manage.TodoManager;
@ -29,7 +29,7 @@ public class PrivateMessageController {
private final MessageRecordServiceV3 messageRecordServiceV3;
private final MessageAPIV3 messageAPIV3;
private final PendingMessageSearchService pendingMessageSearchService;
private final TodoSearchService todoSearchService;
private final TodoManager todoManager;
@PostMapping("/sendPendingMessage")
@ -52,7 +52,7 @@ public class PrivateMessageController {
@PostMapping("/searchPendingRecords")
public Object searchPendingRecords(@RequestBody @Valid SearchPendingMessageReq req) {
return pendingMessageSearchService.search(req);
return todoSearchService.search(req);
}
}

View File

@ -4,10 +4,7 @@ import cn.axzo.core.utils.converter.BeanConverter;
import cn.axzo.msg.center.api.request.v3.PendingSendInfo;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
import cn.axzo.msg.center.service.util.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
@ -67,33 +64,6 @@ public class PendingMessagePushParam extends PendingSendInfo implements Serializ
*/
private String routerParams;
// !! helper
// !! 以下的字段为内部使用
// bizExtParams, routerParams 因为历史原因保留
// 不能直接传 bizExtParamsObj, routerParamsObj
@JsonIgnore
private JSONObject bizExtParamsObj;
@JsonIgnore
private JSONObject routerParamsObj;
public JSONObject getBizExtParamsObj() {
if (bizExtParams == null)
return null;
if (bizExtParamsObj == null)
bizExtParamsObj = JSONUtils.parseObjectOrThrow("bizExtParams", bizExtParams);
return bizExtParamsObj;
}
public JSONObject getRouterParamsObj() {
if (routerParams == null)
return null;
if (routerParamsObj == null)
routerParamsObj = JSONUtils.parseObjectOrThrow("routerParams", routerParams);
return routerParamsObj;
}
public static PendingMessagePushParam from(PendingMessagePushRequest request) {
return BeanConverter.convert(request, PendingMessagePushParam.class);
}

View File

@ -104,6 +104,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nullable;
import javax.validation.constraints.NotEmpty;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@ -961,21 +962,19 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
private List<Long> appendExecutorOuIdExpr(LambdaQueryChainWrapper<PendingMessageRecord> query, Long ouId) {
List<Long> ouIds = determineOuIds(ouId);
if (ouIds.isEmpty()) return Collections.emptyList();
query.and(expr -> expr
// 查询ouId下面所有的平台班组id当成ouId
.in(!ouIds.isEmpty(), PendingMessageRecord::getOuId, ouIds)
.or()
// 或者 ouId = 0 的数据
.eq(PendingMessageRecord::getOuId, 0));
query.in(PendingMessageRecord::getOuId, ouIds);
return ouIds;
}
@NotEmpty
public List<Long> determineOuIds(Long ouId) {
if (ouId == null)
return Collections.emptyList();
List<Long> ouIds = new ArrayList<>();
// 1. 查询 ouId = 0 的数据
ouIds.add(0L);
if (ouId == null)
return ouIds;
ouIds.add(ouId);
// 2. 查询ouId下面所有的平台班组id当成ouId
if (ouId != 0) {
ApiResult<List<OrganizationalTeamOuRelationResp>> resp =
organizationalTeamOuRelationApi.teamOuRelationList(ouId);

View File

@ -1,5 +1,6 @@
package cn.axzo.msg.center.message.service.todo;
import cn.axzo.maokai.api.util.Ref;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.mapper.TodoBusinessMapper;
import cn.axzo.msg.center.dal.mapper.TodoMapper;
@ -10,14 +11,15 @@ import cn.axzo.msg.center.domain.entity.TodoBusiness;
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
import cn.axzo.msg.center.inside.notices.config.PendingCalendarCodeConfig;
import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO;
import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam;
import cn.axzo.msg.center.message.service.MessageGroupNodeService;
import cn.axzo.msg.center.message.service.MessageTemplateGroupService;
import cn.axzo.msg.center.message.service.MessageTemplateNewService;
import cn.axzo.msg.center.message.service.impl.AnalysisPageFactory;
import cn.axzo.msg.center.message.service.impl.PendingMessageNewServiceImpl;
import cn.axzo.msg.center.message.service.todo.pagequery.PageQueryResult;
import cn.axzo.msg.center.message.service.todo.pagequery.PageQuerySort;
import cn.axzo.msg.center.notices.common.enums.IsDeletedEnum;
import cn.axzo.msg.center.service.enums.MessageGroupCategoryEnum;
import cn.axzo.msg.center.service.enums.PendingMessageRoleCategoryEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
@ -41,13 +43,13 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -71,17 +73,18 @@ public class TodoRangeQueryService {
private final MessageGroupNodeService messageGroupNodeService;
private final MessageTemplateGroupService messageTemplateGroupService;
private final PendingMessageNewServiceImpl pendingMessageNewServiceImpl;
private final MessageTemplateNewService messageTemplateNewService;
private final AnalysisPageFactory analysisPageFactory;
private final PendingCalendarCodeConfig calendarCodeConfig;
private final TodoRespBuilder todoRespBuilder;
// !! page query
public AnalysisPage<PendingMessageResponse> pageQuery(
PendingMessagePageRequest req) {
public AnalysisPage<PendingMessageResponse> pageQuery(PendingMessagePageRequest req) {
List<String> templateCodes = determinePageQueryTemplateCodes(req);
PageQueryResult queryResult;
if (req.getRoleCategory() == PendingMessageRoleCategoryEnum.PROMOTER)
if (req.determineToDoType() == TodoType.EXECUTABLE
&& req.getRoleCategory() == PendingMessageRoleCategoryEnum.PROMOTER)
// 我发起的
queryResult = pageQueryBusiness(req, templateCodes);
else
@ -91,9 +94,15 @@ public class TodoRangeQueryService {
.convertAdapter2MessageResponse(queryResult.getAdapters(), req.getTerminalType());
AnalysisPage<PendingMessageResponse> pageResult = analysisPageFactory
.createPage(req.getPage(), req.getPageSize(), queryResult.getTotal(), messages);
if (pageResult.isEnableAnalysis())
if (pageResult.isEnableAnalysis()) {
pageResult.addAnalysis("terminalConfig", pendingMessageNewServiceImpl.getTerminalConfigInfo());
pageResult.addAnalysis("templateCodes", new HashSet<>(templateCodes));
List<MessageTemplateDTO> messageTemplates = messageTemplateNewService
.listByTemplateCodes(templateCodes);
List<String> templates = messageTemplates.stream()
.map(t -> String.format("%s:%s", t.getCode(), t.getName()))
.collect(toList());
pageResult.addAnalysis("queryTemplate", templates);
}
pageResult.addAnalysis("personId", req.getPersonId());
pageResult.addAnalysis("ouId", req.getOuId());
pageResult.addAnalysis(queryResult.getAnalysis());
@ -103,16 +112,14 @@ public class TodoRangeQueryService {
/**
* 业务视角
*/
private PageQueryResult pageQueryBusiness(
PendingMessagePageRequest req, List<String> templateCodes) {
private PageQueryResult pageQueryBusiness(PendingMessagePageRequest req, List<String> templateCodes) {
Date startingAt = DateFormatUtil.toDate(LocalDateTime.now().minusDays(90));
LambdaQueryWrapper<TodoBusiness> query = query(TodoBusiness.class)
LambdaQueryWrapper<TodoBusiness> query = businessQuery(req.getTitle())
.eq(TodoBusiness::getPromoterPersonId, req.getPersonId())
.eq(TodoBusiness::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.gt(TodoBusiness::getCreateAt, startingAt)
.in(TodoBusiness::getTemplateCode, templateCodes)
.eq(req.getBizFinalState() != null, TodoBusiness::getBizFinalState, req.getBizFinalState())
.and(StringUtils.isNotBlank(req.getTitle()), expr -> appendBusinessKeywordSearchExpr(req, expr));
.eq(req.getBizFinalState() != null, TodoBusiness::getBizFinalState, req.getBizFinalState());
PageQuerySort.TODO_BUSINESS.appendSortExpr(req, query);
IPage<TodoBusiness> pageData = todoBusinessMapper.selectPage(req.toPage(), query);
List<PendingRecordAdapter> messages = todoRespBuilder.buildBusinessAdapters(pageData.getRecords());
@ -122,46 +129,44 @@ public class TodoRangeQueryService {
/**
* 接收者视角
*/
private PageQueryResult pageQueryTodo(
PendingMessagePageRequest req, List<String> templateCodes) {
private PageQueryResult pageQueryTodo(PendingMessagePageRequest req, List<String> templateCodes) {
Collection<Long> businessIdsByLike = Collections.emptySet();
// 模糊匹配标题/内容
if (StringUtils.isNotBlank(req.getTitle())) {
LambdaQueryWrapper<TodoBusiness> businessQuery = query(TodoBusiness.class);
appendBusinessKeywordSearchExpr(req, businessQuery);
List<TodoBusiness> businesses = todoBusinessMapper.selectList(businessQuery);
List<TodoBusiness> businesses = todoBusinessMapper.selectList(businessQuery(req.getTitle()));
if (businesses.isEmpty())
return PageQueryResult.empty();
businessIdsByLike = businesses.stream()
.map(BaseEntityExt::getId)
.collect(toSet());
}
CopiedToMeParam readOnly = req.getReadOnlyParam();
LambdaQueryWrapper<Todo> query = query(Todo.class)
CopiedToMeParam copiedToMeParam = req.getCopiedToMeParam();
Ref<List<Long>> ouCollector = Ref.create();
LambdaQueryWrapper<Todo> query = todoQuery(req.getOuId(), ouCollector)
// 查询的待办类型: COPIED_TO_ME, EXECUTABLE
.eq(Todo::getType, req.determineToDoType())
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.eq(Todo::getExecutorPersonId, req.getPersonId())
.in(Todo::getTemplateCode, templateCodes)
.in(CollectionUtils.isNotEmpty(businessIdsByLike), Todo::getTodoBusinessId, businessIdsByLike)
// set hide
.and(nested -> nested.isNull(Todo::getHideUntil)
.or()
.lt(Todo::getHideUntil, new Date()))
// copied to me
.and(req.determineToDoType() == TodoType.COPIED_TO_ME, nested -> nested
.eq(readOnly != null && readOnly.determineQueryUnreadOnly(),
.eq(copiedToMeParam != null && copiedToMeParam.determineQueryUnreadOnly(),
Todo::getState, PendingMessageStateEnum.CREATED)
)
.last(nested.isEmptyOfWhere(), "1 = 1"))
// executable
.and(req.determineToDoType() == TodoType.EXECUTABLE, nested -> nested
.eq(req.getMsgState() != null, Todo::getState, req.getMsgState())
.eq(req.getWorkspaceId() != null, Todo::getOrgId, req.getWorkspaceId())
);
.last(nested.isEmptyOfWhere(), "1 = 1"));
PageQuerySort.TODO.appendSortExpr(req, query);
List<Long> ouIds = appendExecutorOuIdExpr(query, req.getOuId());
IPage<Todo> pageData = todoMapper.selectPage(req.toPage(), query);
List<PendingRecordAdapter> messages = todoRespBuilder.buildTodoAdapters(pageData.getRecords());
return new PageQueryResult(ImmutableMap.of("determinedOuIds", ouIds), pageData.getTotal(), messages);
return new PageQueryResult(ImmutableMap.of(
"determinedOuIds", ouCollector.get()), pageData.getTotal(), messages);
}
// !! stat
@ -198,58 +203,44 @@ public class TodoRangeQueryService {
private int countStatByPaths(
Collection<GroupTreeNodePathDTO> groupNodePaths,
MessageGroupNodeStatisticParam param,
TodoType todoType) {
MessageGroupNodeStatisticParam param, TodoType todoType) {
List<String> groupNodePathCodes = groupNodePaths.stream()
.map(GroupTreeNodePathDTO::getNodeCodePath)
.collect(Collectors.toList());
List<String> templateCodes = messageTemplateGroupService.listMessageTemplateCodes(groupNodePathCodes);
if (CollectionUtils.isEmpty(templateCodes))
return 0;
LambdaQueryWrapper<Todo> query = query(Todo.class)
LambdaQueryWrapper<Todo> query = todoQuery(param.getOuId())
.in(Todo::getTemplateCode, templateCodes)
.eq(Todo::getExecutorPersonId, param.getOperator().getId())
.eq(Todo::getIsDelete, IsDeletedEnum.NO.getCode())
.and(todoType == TodoType.EXECUTABLE, nested -> nested
.eq(Todo::getType, TodoType.EXECUTABLE)
.eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT))
.and(todoType == TodoType.COPIED_TO_ME, nested -> nested
.eq(Todo::getType, TodoType.COPIED_TO_ME)
.eq(Todo::getState, PendingMessageStateEnum.CREATED));
appendExecutorOuIdExpr(query, param.getOuId());
Integer count = todoMapper.selectCount(query);
return count == null ? 0 : count;
}
// !! calendar
public List<PendingMessageResponse> getPendingMessageByAppWorker(PendingMessageFixedTemplatePageRequest request) {
public List<PendingMessageResponse> getWorkerCalendar(PendingMessageFixedTemplatePageRequest request) {
// 未来无待办
LocalDate localDate = request.getSelectDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if (localDate.isAfter(LocalDate.now()))
LocalDate selectedTime = request.getSelectDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate currentTime = LocalDate.now();
if (selectedTime.isAfter(currentTime))
return Collections.emptyList();
// 是否是当天? very tricky?
Boolean isNowDay = !localDate.isBefore(LocalDate.now());
Boolean isNowDay = !selectedTime.isBefore(currentTime);
PendingCalendarCodeDTO calendarCodeDTO = buildCalendarCodesDTO(calendarCodeConfig);
List<Todo> todos = todoMapper.queryByTemplateCodes(
List<Todo> todos = todoMapper.queryForWorkerCalendar(
calendarCodeDTO, request.getWorkspaceId(),
request.getSelectDate(), request.getPersonId(), isNowDay);
List<PendingRecordAdapter> adapters = todoRespBuilder.buildTodoAdapters(todos);
return todoRespBuilder.convertAdapter2MessageResponse(adapters, request.getTerminalType());
}
// helper
private void appendBusinessKeywordSearchExpr(
PendingMessagePageRequest req, LambdaQueryWrapper<TodoBusiness> query) {
String title = req.getTitle().trim();
if (StringUtils.isBlank(title))
return;
query.like(TodoBusiness::getTitle, title)
.or()
.like(TodoBusiness::getPromoterName, title);
}
/**
* 根据分类获取可见的模版编码
*/
@ -276,16 +267,30 @@ public class TodoRangeQueryService {
return messageTemplateGroupService.listMessageTemplateCodes(paths);
}
private List<Long> appendExecutorOuIdExpr(LambdaQueryWrapper<Todo> query, Long ouId) {
List<Long> ouIds = pendingMessageNewServiceImpl.determineOuIds(ouId);
if (ouIds.isEmpty()) return Collections.emptyList();
query.and(expr -> expr
// 查询ouId下面所有的平台班组id当成ouId
.in(!ouIds.isEmpty(), Todo::getOuId, ouIds)
// helper
private LambdaQueryWrapper<TodoBusiness> businessQuery(String title) {
return query(TodoBusiness.class)
.eq(TodoBusiness::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.and(StringUtils.isNotBlank(title), nested -> nested
.like(TodoBusiness::getTitle, title)
.or()
// 或者 ouId = 0 的数据
.eq(Todo::getOuId, 0));
return ouIds;
.like(TodoBusiness::getPromoterName, title));
}
private LambdaQueryWrapper<Todo> todoQuery(Long ouId) {
return todoQuery(ouId, null);
}
private LambdaQueryWrapper<Todo> todoQuery(Long ouId, @Nullable Ref<List<Long>> ouCollector) {
List<Long> ouIds = pendingMessageNewServiceImpl.determineOuIds(ouId);
if (ouCollector != null)
ouCollector.set(ouIds);
// 1. 查询 ouId = 0 的数据
// 2. 查询ouId下面所有的平台班组id当成ouId
return query(Todo.class)
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.in(Todo::getOuId, ouIds);
}
private PendingCalendarCodeDTO buildCalendarCodesDTO(PendingCalendarCodeConfig calendarCodeConfig) {

View File

@ -14,6 +14,7 @@ import cn.axzo.msg.center.service.enums.YesOrNo;
import com.alibaba.fastjson.JSONObject;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
@ -21,12 +22,12 @@ import java.util.Date;
*/
public class TodoRecordAdapter implements PendingRecordAdapter {
@Nullable
@NotNull
private final TodoBusiness business;
@Nullable
private final Todo todo;
private TodoRecordAdapter(@Nullable TodoBusiness business, @Nullable Todo todo) {
private TodoRecordAdapter(TodoBusiness business, @Nullable Todo todo) {
this.business = business;
this.todo = todo;
}
@ -51,22 +52,22 @@ public class TodoRecordAdapter implements PendingRecordAdapter {
@Override
public Long getPromoterId() {
return business == null ? 0L : business.getPromoterId();
return business.getPromoterId();
}
@Override
public Long getPromoterPersonId() {
return business == null ? 0L : business.getPromoterPersonId();
return business.getPromoterPersonId();
}
@Override
public String getPromoterName() {
return business == null ? null : business.getPromoterName();
return business.getPromoterName();
}
@Override
public IdentityTypeEnum getPromoterType() {
return business == null ? null : business.getPromoterType();
return business.getPromoterType();
}
@Override
@ -91,9 +92,7 @@ public class TodoRecordAdapter implements PendingRecordAdapter {
@Override
public String getTemplateCode() {
if (todo != null)
return todo.getTemplateCode();
return business == null ? null : business.getTemplateCode();
return todo != null ? todo.getTemplateCode() : business.getTemplateCode();
}
@Override
@ -138,9 +137,7 @@ public class TodoRecordAdapter implements PendingRecordAdapter {
@Override
public String getBizCode() {
if (todo != null)
return todo.getBizCode();
return business == null ? null : business.getBizCode();
return todo != null ? todo.getBizCode() : business.getBizCode();
}
@Override
@ -160,18 +157,14 @@ public class TodoRecordAdapter implements PendingRecordAdapter {
@Override
public String getBizExtParam() {
if (business == null)
return null;
JSONObject bizExtParam = business.getBizExtParam();
return bizExtParam == null ? null : bizExtParam.toJSONString();
JSONObject obj = todo != null ? todo.getBizExtParam() : business.getBizExtParam();
return obj == null ? null : obj.toJSONString();
}
@Override
public String getRouterParams() {
if (business == null)
return null;
JSONObject routerParams = business.getRouterParams();
return routerParams == null ? null : routerParams.toJSONString();
JSONObject obj = todo != null ? todo.getRouterParams() : business.getRouterParams();
return obj == null ? null : obj.toJSONString();
}
@Override

View File

@ -73,14 +73,14 @@ public class TodoSimpleQueryService {
.toResponse(terminalType);
}
public PendingMessageResponse getBusinessDetailByBizCode(String templateCode, String bizCode) {
TodoBusiness business = todoBusinessDao
.findTodoByBizCode(templateCode, bizCode)
public PendingMessageResponse getLatestTodoByBiz(String templateCode, String bizCode, String subBizCode) {
Todo todo = todoDao
.getLatestByBiz(templateCode, bizCode, subBizCode)
.orElse(null);
if (business == null)
if (todo == null)
return null;
return todoRespBuilder
.convertBusinessToMessage(business)
.convertTodoToMessage(todo)
.toResponse(null);
}

View File

@ -34,30 +34,23 @@ class PullTodoBroadcaster implements DisposableBean {
new ArrayBlockingQueue<>(50),
new NamedThreadFactory("TodoNotificationThread", false));
void asyncFireTodoChanged(Long personId) {
void fireTodoChanged(Collection<Long> personIds) {
try {
executor.execute(() -> fireTodoChanged(personId));
executor.execute(() -> personIds.forEach(this::fireTodoChangedImpl));
} catch (Exception e) {
log.error("{} is busy, check whether im-center is health", getClass().getSimpleName(), e);
log.error("{} is busy, check whether im-center is healthy", getClass().getSimpleName(), e);
}
}
void asyncFireTodoChanged(Collection<Long> personIds) {
try {
executor.execute(() -> personIds.forEach(this::fireTodoChanged));
} catch (Exception e) {
log.error("{} is busy, check whether im-center is health", getClass().getSimpleName(), e);
}
}
private void fireTodoChanged(Long personId) {
log.info("firing todo changed. personId={}", personId);
CustomMessageInfo messageInfo = CustomMessageInfo.builder()
private void fireTodoChangedImpl(Long personId) {
log.info("start - firing todo changed. personId={}", personId);
CustomMessageInfo request = CustomMessageInfo.builder()
.appTypeList(Lists.newArrayList(AppTypeEnum.CM, AppTypeEnum.CMP))
.toPersonId(String.valueOf(personId))
.bizType(BizTypeEnum.PENDING)
.build();
messageApi.sendCustomMessage(messageInfo);
messageApi.sendCustomMessage(request);
log.info("end - firing todo changed. personId={}, request={}", personId, request);
}
@Override

View File

@ -23,8 +23,8 @@ class TodoLogger {
private final TodoLogDao todoLogDao;
void logBusinessUpdated(TodoRequestContext ctx, TodoBusiness business) {
TodoLog log = createBusinessLog(ctx.getRequestNo(), business);
log.addLogContent("context", ctx.getName());
TodoLog log = createBusinessLog(ctx, business);
log.setContext(ctx.getName());
log.addLogContents(ctx.getLogContents());
todoLogDao.save(log);
}
@ -46,7 +46,7 @@ class TodoLogger {
void logTodosUpdated(TodoRequestContext ctx, List<Todo> todos) {
ArrayList<TodoLog> logs = new ArrayList<>();
for (Todo todo : todos) {
logs.add(createTodoLog(ctx.getRequestNo(), todo)
logs.add(createTodoLog(ctx, todo)
.addLogContent("context", ctx.getName())
.addLogContents(ctx.getLogContents()));
}
@ -55,7 +55,7 @@ class TodoLogger {
// !! factory
private static TodoLog createTodoLog(String requestNo, Todo todo) {
private static TodoLog createTodoLog(TodoRequestContext ctx, Todo todo) {
TodoLog log = new TodoLog();
log.setLogType(TodoLogType.TODO);
log.setSrcTemplateCode(todo.getSrcTemplateCode());
@ -63,18 +63,20 @@ class TodoLogger {
log.setIdentityCode(todo.getIdentityCode());
log.setBizCode(todo.getBizCode());
log.setSubBizCode(todo.getSubBizCode());
log.setRequestNo(requestNo);
log.setRequestNo(ctx.getRequestNo());
log.setContext(ctx.getName());
return log;
}
private static TodoLog createBusinessLog(String requestNo, TodoBusiness business) {
private static TodoLog createBusinessLog(TodoRequestContext ctx, TodoBusiness business) {
TodoLog log = new TodoLog();
log.setLogType(TodoLogType.TODO_BUSINESS);
log.setSrcTemplateCode(business.getTemplateCode());
log.setTemplateCode("");
log.setIdentityCode("");
log.setBizCode(business.getBizCode());
log.setRequestNo(requestNo);
log.setRequestNo(ctx.getRequestNo());
log.setContext(ctx.getName());
return log;
}

View File

@ -94,7 +94,7 @@ public class TodoManager {
Set<Long> executorPersonIds = todos.stream()
.map(Todo::getExecutorPersonId)
.collect(Collectors.<Long>toSet());
pullTodoBroadcaster.asyncFireTodoChanged(executorPersonIds);
pullTodoBroadcaster.fireTodoChanged(executorPersonIds);
// 记录日志
// @formatter:off
ctx.addLogContent("title", business.getTitle())
@ -107,7 +107,7 @@ public class TodoManager {
todoLogger.logTodosUpdated(ctx, todos);
return todos.stream()
.map(todo -> new PushPendingMessageDTO(
todo.getId(), todo.getRequestNo(),
todo.getId(), todo.getIdentityCode(), todo.getRequestNo(),
todo.getExecutorId(), todo.getExecutorPersonId()))
.collect(toList());
}
@ -135,45 +135,22 @@ public class TodoManager {
* 完成待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeById(CompletePendingMessageByIdRequest param) {
public boolean completeById(CompletePendingMessageByIdRequest request) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getId, param.getId())
.eq(Todo::getId, request.getId())
.set(Todo::getState, PendingMessageStateEnum.COMPLETED));
if (!advanceResult.isAdvanced())
return false;
TodoRequestContext ctx = TodoRequestContext.create("completeById");
todoLogger.logTodoCompleted(ctx, advanceResult.getTodos());
if (param.getBizExtParams() != null) {
JSONObject bizExtParams = JSONUtils.parseObjectOrThrow("bizExtParams", param.getBizExtParams());
if (request.getBizExtParams() != null) {
JSONObject bizExtParams = JSONUtils.parseObjectOrThrow("bizExtParams", request.getBizExtParams());
todoBusinessDao.lambdaUpdate()
.eq(TodoBusiness::getId, advanceResult.getBusiness().getId())
.set(TodoBusiness::getBizExtParam, bizExtParams);
ctx.addLogContent("bizExtParam", bizExtParams);
todoLogger.logBusinessUpdated(ctx, advanceResult.getBusiness());
}
return false;
}
/**
* 完成待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeByBizCode(CompletePendingMessageRequest param) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getTemplateCode, param.getTemplateCode())
.eq(Todo::getBizCode, param.getBizCode())
.set(Todo::getState, PendingMessageStateEnum.COMPLETED));
if (!advanceResult.isAdvanced())
return false;
TodoRequestContext ctx = TodoRequestContext.create("completeByBizCode");
todoLogger.logTodoCompleted(ctx, advanceResult.getTodos());
if (param.determineUpdateFinalBizState()) {
todoBusinessDao.lambdaUpdate()
.eq(TodoBusiness::getId, advanceResult.getBusiness().getId())
.set(TodoBusiness::getBizFinalState, param.getBizFinalStateEnum());
ctx.addLogContent("bizFinalState", param.getBizFinalStateEnum());
todoLogger.logBusinessUpdated(ctx, advanceResult.getBusiness());
}
return true;
}
@ -181,11 +158,34 @@ public class TodoManager {
* 完成待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeBySubBizCode(CompletePendingBySubCodeRequest param) {
public boolean completeByBizCode(CompletePendingMessageRequest request) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getTemplateCode, param.getTemplateCode())
.eq(Todo::getBizCode, param.getBizCode())
.eq(Todo::getSubBizCode, param.getSubBizCode())
.eq(Todo::getTemplateCode, request.getTemplateCode())
.eq(Todo::getBizCode, request.getBizCode())
.set(Todo::getState, PendingMessageStateEnum.COMPLETED));
if (!advanceResult.isAdvanced())
return false;
TodoRequestContext ctx = TodoRequestContext.create("completeByBizCode");
todoLogger.logTodoCompleted(ctx, advanceResult.getTodos());
if (request.determineUpdateFinalBizState()) {
todoBusinessDao.lambdaUpdate()
.eq(TodoBusiness::getId, advanceResult.getBusiness().getId())
.set(TodoBusiness::getBizFinalState, request.getBizFinalStateEnum());
ctx.addLogContent("bizFinalState", request.getBizFinalStateEnum());
todoLogger.logBusinessUpdated(ctx, advanceResult.getBusiness());
}
return true;
}
/**
* 完成待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeBySubBizCode(CompletePendingBySubCodeRequest request) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getTemplateCode, request.getTemplateCode())
.eq(Todo::getBizCode, request.getBizCode())
.eq(Todo::getSubBizCode, request.getSubBizCode())
.set(Todo::getState, PendingMessageStateEnum.COMPLETED));
if (!advanceResult.isAdvanced())
return false;
@ -232,9 +232,9 @@ public class TodoManager {
* 撤回待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean revokeById(RevokePendingMessageByIdRequest param) {
public boolean revokeById(RevokePendingMessageByIdRequest request) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getId, param.getId())
.eq(Todo::getId, request.getId())
.set(Todo::getState, PendingMessageStateEnum.RETRACT));
if (!advanceResult.isAdvanced())
return false;
@ -247,10 +247,10 @@ public class TodoManager {
* 撤回待办
*/
@Transactional(rollbackFor = Exception.class)
public boolean revokeByBizCode(CompletePendingMessageRequest param) {
public boolean revokeByBizCode(CompletePendingMessageRequest request) {
StateAdvanceResult advanceResult = advanceState(executableAdvanceBuilder()
.eq(Todo::getTemplateCode, param.getTemplateCode())
.eq(Todo::getBizCode, param.getBizCode())
.eq(Todo::getTemplateCode, request.getTemplateCode())
.eq(Todo::getBizCode, request.getBizCode())
.set(Todo::getState, PendingMessageStateEnum.RETRACT));
if (!advanceResult.isAdvanced())
return false;
@ -304,14 +304,16 @@ public class TodoManager {
&& routerParam == null
&& StringUtils.isBlank(param.getTemplateCode()),
"routerParam、bizParam、templateCode至少要传一个");
boolean updated = todoBusinessDao.lambdaUpdate()
.set(bizParam != null, TodoBusiness::getBizExtParam, bizParam)
.set(routerParam != null, TodoBusiness::getRouterParams, routerParam)
.set(StringUtils.isNotBlank(param.getTemplateCode()),
TodoBusiness::getTemplateCode, param.getTemplateCode())
.eq(TodoBusiness::getId, param.getId())
.eq(TodoBusiness::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.update();
TodoBusiness businessUpdate = new TodoBusiness();
businessUpdate.setId(business.getId());
if (bizParam != null)
businessUpdate.setBizExtParam(bizParam);
if (routerParam != null)
businessUpdate.setRouterParams(routerParam);
if (StringUtils.isNotBlank(param.getTemplateCode()))
businessUpdate.setTemplateCode(param.getTemplateCode());
// 不通过 lambdaUpdate 更新, 因为JSON序列化有问题
boolean updated = todoBusinessDao.updateById(businessUpdate);
if (updated) {
TodoRequestContext ctx = TodoRequestContext.create("updateBusinessById")
.addLogContent("updatedBizParam", bizParam)
@ -388,7 +390,7 @@ public class TodoManager {
Set<Long> executorPersonIds = todos.stream()
.map(Todo::getExecutorPersonId)
.collect(Collectors.toSet());
pullTodoBroadcaster.asyncFireTodoChanged(executorPersonIds);
pullTodoBroadcaster.fireTodoChanged(executorPersonIds);
}
return new StateAdvanceResult(updated, business, todos);
}

View File

@ -19,7 +19,9 @@ import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType;
import cn.axzo.msg.center.service.enums.YesOrNo;
import cn.axzo.msg.center.service.util.JSONUtils;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -43,22 +45,21 @@ class TodoRecordBuilder {
TodoBusiness buildBusiness(PendingMessagePushParam req, MessageTemplateDTO template) {
PersonDTO promoter = req.getPromoter();
IdentityDTO identity = promoter == null ? null : promoter.getIdentity();
JSONObject bizExtParamsObj = JSONUtils.parseObjectOrThrow("bizExtParams", req.getBizExtParams());
JSONObject routerParamsObj = JSONUtils.parseObjectOrThrow("routerParams", req.getRouterParams());
TodoBusiness business = new TodoBusiness();
business.setTemplateCode(req.getTemplateCode());
business.setBizCode(req.getBizCode());
business.setTitle(PlaceholderResolver.tryResolve(template.getTitle(), req.getBizExtParamsObj()));
business.setContent(PlaceholderResolver.tryResolve(template.getContent(), req.getBizExtParamsObj()));
business.setTitle(PlaceholderResolver.tryResolve(template.getTitle(), bizExtParamsObj));
business.setContent(PlaceholderResolver.tryResolve(template.getContent(), routerParamsObj));
business.setBizFlag(req.getBizFlag());
business.setPromoterId(identity == null || identity.getId() == null
? 0 : identity.getId());
business.setPromoterType(identity == null || identity.getType() == null
? IdentityTypeEnum.NOT_SUPPORT : identity.getType());
business.setPromoterPersonId(promoter == null ? 0 : promoter.getId());
business.setPromoterId(promoter == null ? 0L : promoter.identityIdOrDefault());
business.setPromoterType(promoter == null ? IdentityTypeEnum.NOT_SUPPORT : promoter.identityTypeOrDefault());
business.setPromoterPersonId(promoter == null ? 0 : promoter.personIdOrDefault());
business.setPromoterName(promoter == null ? "" : promoter.getName());
business.setBizCategory(req.getBizCategory() == null ? BizCategoryEnum.OTHER : req.getBizCategory());
business.setBizExtParam(req.getBizExtParamsObj());
business.setRouterParams(req.getRouterParamsObj());
business.setBizExtParam(bizExtParamsObj);
business.setRouterParams(routerParamsObj);
business.setDeadline(req.getDeadline());
business.setBizFinalState(null);
return business;
@ -75,29 +76,30 @@ class TodoRecordBuilder {
else
log.info("未查询到工作台信息. workspaceId={}", req.getWorkspaceId());
}
String title = PlaceholderResolver.tryResolve(template.getTitle(), req.getBizExtParamsObj());
String content = PlaceholderResolver.tryResolve(template.getContent(), req.getBizExtParamsObj());
JSONObject bizExtParamsObj = JSONUtils.parseObjectOrThrow("bizExtParams", req.getBizExtParams());
JSONObject routerParamsObj = JSONUtils.parseObjectOrThrow("routerParams", req.getRouterParams());
String title = PlaceholderResolver.tryResolve(template.getTitle(), bizExtParamsObj);
String content = PlaceholderResolver.tryResolve(template.getContent(), routerParamsObj);
Set<Long> sentPersonIds = new HashSet<>();
for (PersonDTO executor : req.getExecutor()) {
if (sentPersonIds.contains(executor.getId()))
continue;
sentPersonIds.add(executor.getId());
IdentityDTO identity = executor.getIdentity();
Todo todo = new Todo();
todos.add(todo);
todo.setTodoBusinessId(business.getId());
todo.setSrcTemplateCode(business.getTemplateCode());
todo.setTemplateCode(req.getTemplateCode());
todo.setTitle(title);
todo.setContent(content);
todo.setBizExtParam(req.getBizExtParamsObj());
todo.setRouterParams(req.getRouterParamsObj());
todo.setBizExtParam(bizExtParamsObj);
todo.setRouterParams(routerParamsObj);
todo.setIdentityCode(UUIDUtil.uuidString());
todo.setTemplateCode(req.getTemplateCode());
todo.setBizCode(req.getBizCode());
todo.setSubBizCode(req.getSubBizCode());
todo.setType(req.determineTodoType());
todo.setOuId(determineOuId(req.getOuId(), workspace, executor.getIdentity()));
todo.setExecutorPersonId(executor.getId());
todo.setExecutorPersonId(executor.personIdOrDefault());
todo.setExecutorName(executor.getName());
todo.setRequestNo(requestNo);
todo.setOrgId(req.getWorkspaceId());
@ -106,11 +108,10 @@ class TodoRecordBuilder {
todo.setIsOuIdMigrated(YesOrNo.NO);
todo.setHideUntil(null);
todo.setState(req.determineTodoType() == TodoType.COPIED_TO_ME
? PendingMessageStateEnum.CREATED : PendingMessageStateEnum.HAS_BEEN_SENT);
todo.setExecutorId(identity == null || identity.getId() == null
? 0 : identity.getId());
todo.setExecutorType(identity == null
? IdentityTypeEnum.NOT_SUPPORT : identity.getType());
? PendingMessageStateEnum.CREATED
: PendingMessageStateEnum.HAS_BEEN_SENT);
todo.setExecutorId(executor.identityIdOrDefault());
todo.setExecutorType(executor.identityTypeOrDefault());
todo.setOrgType(req.getOrgType() == null
? OrganizationTypeEnum.UNKNOWN : req.getOrgType());
}

View File

@ -28,12 +28,6 @@ public class TodoRequestContext {
return new TodoRequestContext(name, requestNo);
}
public TodoRequestContext addLogContents(Map<String, Object> values) {
for (Map.Entry<String, Object> e : values.entrySet())
addLogContent(e.getKey(), e.getValue());
return this;
}
public TodoRequestContext addLogContent(String name, Object value) {
if (name == null || value == null)
return this;

View File

@ -55,8 +55,6 @@ import static java.util.stream.Collectors.toMap;
@RequiredArgsConstructor
public class MigrateFromPendingMessageJob extends IJobHandler {
public static final int TODO_SAVE_BATCH_SIZE = 50;
private final PendingMessageRecordMapper pendingMessageRecordMapper;
private final TodoBusinessDao todoBusinessDao;
private final TodoDao todoDao;
@ -175,7 +173,7 @@ public class MigrateFromPendingMessageJob extends IJobHandler {
todos.add(buildTodo(business, record));
}
migrateCount.todoCount.addAndGet(todos.size());
for (List<Todo> todosBatch : Lists.partition(todos, TODO_SAVE_BATCH_SIZE)) {
for (List<Todo> todosBatch : Lists.partition(todos, 200)) {
todoDao.saveBatch(todosBatch);
}
}
@ -214,13 +212,20 @@ public class MigrateFromPendingMessageJob extends IJobHandler {
@Override
protected void compute() {
// 防止一上线就有人生成了bizCode为空的待办, 避免重复生成
TodoBusiness business = todoBusinessDao.lambdaQuery()
.eq(TodoBusiness::getTemplateCode, tb.templateCode)
.eq(TodoBusiness::getBizCode, tb.templateCode)
.one();
if (business == null) {
PendingMessageRecord sample = pendingMessageRecordMapper.selectOne(query(PendingMessageRecord.class)
.eq(PendingMessageRecord::getTemplateCode, tb.templateCode)
.eq(PendingMessageRecord::getBizCode, tb.bizCode)
.last("LIMIT 1"));
TodoBusiness business = buildTodoBusiness(sample);
business = buildTodoBusiness(sample);
todoBusinessDao.save(business);
migrateCount.businessCount.incrementAndGet();
}
Page<PendingMessageRecord> samplePage = absentPageData(tb.templateCode, 1);
@ -259,7 +264,7 @@ public class MigrateFromPendingMessageJob extends IJobHandler {
private Page<PendingMessageRecord> absentPageData(String templateCode, int pageNumber) {
Page<PendingMessageRecord> page = new Page<>();
page.setCurrent(pageNumber);
page.setSize(500);
page.setSize(3000);
return pendingMessageRecordMapper.selectPage(page, query(PendingMessageRecord.class)
.eq(PendingMessageRecord::getTemplateCode, templateCode)
.eq(PendingMessageRecord::getBizCode, "")
@ -271,7 +276,7 @@ public class MigrateFromPendingMessageJob extends IJobHandler {
private Supplier<List<TemplateAndBiz>> templateBizPageFun(boolean forEmptyBizCode) {
Page<PendingMessageRecord> bizCodePage = new Page<>();
bizCodePage.setCurrent(0);
bizCodePage.setSize(50);
bizCodePage.setSize(100);
return () -> {
bizCodePage.setCurrent(bizCodePage.getCurrent() + 1);
QueryWrapper<PendingMessageRecord> query = new QueryWrapper<>();

View File

@ -1,13 +1,13 @@
package cn.axzo.msg.center.api.request.v3;
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
import cn.axzo.msg.center.service.enums.BizFinalStateEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import javax.validation.constraints.NotBlank;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@ -19,18 +19,12 @@ import java.util.List;
public class SearchPendingMessageReq {
private String promoterPhone;
private String executorPhone;
private Long executorPersonId;
private List<TodoType> todoTypes;
/**
* 消息的唯一标识
*/
private List<String> identityCodes;
/**
* 发起者的自然人ID
*/
private Long promoterPersonId;
/**
* 执行者的自然人ID
*/
private Long executorPersonId;
/**
* 模板编码
*/
@ -59,14 +53,14 @@ public class SearchPendingMessageReq {
* 流程类代办的流程结点编码
*/
private String subBizCode;
/**
* 业务终态可为空
*/
private BizFinalStateEnum bizFinalState;
@JsonFormat(pattern = "yyyy-MM-hh HH:mm")
private Date startTime;
@JsonFormat(pattern = "yyyy-MM-hh HH:mm")
private Date endTime;
private int limit = 10;
public List<TodoType> determineTodoTypes() {
return CollectionUtils.isEmpty(todoTypes) ? Collections.singletonList(TodoType.EXECUTABLE) : todoTypes;
}
}

View File

@ -5,6 +5,7 @@ import cn.axzo.msg.center.service.enums.BizFinalStateEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -141,4 +142,9 @@ public class SearchPendingMessageResp {
*/
@JsonFormat(pattern = "yyyy-MM-hh HH:mm:ss")
private Date hideUntil;
/**
* 待办类型
*/
private TodoType type;
}

View File

@ -12,11 +12,10 @@ import java.io.Serializable;
import java.util.Objects;
/**
* @description
*
* @author cold_blade
* @date 2023/9/26
* @version 1.0
* @description
* @date 2023/9/26
*/
@Setter
@Getter
@ -40,6 +39,22 @@ public class PersonDTO implements Serializable {
*/
private String name;
public Long personIdOrDefault() {
return id == null ? 0L : id;
}
public Long identityIdOrDefault() {
if (identity == null)
return 0L;
return identity.getId() == null ? 0L : identity.getId();
}
public IdentityTypeEnum identityTypeOrDefault() {
if (identity == null)
return IdentityTypeEnum.NOT_SUPPORT;
return identity.getType() == null ? IdentityTypeEnum.NOT_SUPPORT : identity.getType();
}
public static PersonDTO from(Long personId, Long identityId, IdentityTypeEnum identityType) {
IdentityDTO identity = IdentityDTO.builder()
.id(identityId)

View File

@ -119,10 +119,11 @@ public interface PendingMessageClient {
*
* @return
*/
@PostMapping(value = "/pending-message/record/get-detail-by-biz-code", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<PendingMessageResponse> getBusinessDetailByBizCode(
@PostMapping(value = "/pending-message/record/get-latest-todo-by-biz", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<PendingMessageResponse> getLatestTodoByBiz(
@RequestParam(value = "templateCode", required = false) String templateCode,
@RequestParam("bizCode") String bizCode);
@RequestParam(value = "bizCode", required = false) String bizCode,
@RequestParam(value = "subBizCode", required = false) String subBizCode);
/**
* 发送代办

View File

@ -118,7 +118,7 @@ public class PendingMessagePageRequest extends PageRequest implements Serializab
/**
* 查询只读待办(抄送)的相关参数
*/
private CopiedToMeParam readOnlyParam;
private CopiedToMeParam copiedToMeParam;
// !! 用于排查问题
private String fetchTemplateCode;

View File

@ -73,11 +73,11 @@ public class PendingMessageStatisticResponseV2 implements Serializable {
@AllArgsConstructor
public static class Stat {
/**
* 代办数量
* 待处理数量
*/
private int pendingCount;
/**
* 未读数量
* 抄送未读数量
*/
private int unreadCount;
}

View File

@ -19,6 +19,11 @@ public class PushPendingMessageDTO {
*/
private Long id;
/**
* 消息唯一标识
*/
private String identityCode;
/**
* 请求批次号
*/

View File

@ -1,6 +1,9 @@
package cn.axzo.msg.center.common.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import java.util.HashSet;
import java.util.List;
@ -50,6 +53,14 @@ public class PlaceholderResolver {
this.placeholderSuffix = placeholderSuffix;
}
public static String tryResolve(String template, String objStr) {
if (StringUtils.isBlank(objStr)) {
return template;
}
JSONObject obj = JSON.parseObject(objStr);
return tryResolve(template, obj);
}
public static String tryResolve(String template, Map<String, Object> values) {
if (values == null || values.isEmpty()) {
return template;

View File

@ -1,19 +1,16 @@
package cn.axzo.msg.center.dal;
import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.mapper.TodoBusinessMapper;
import cn.axzo.msg.center.domain.entity.TodoBusiness;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* @author yanglin
@ -30,18 +27,6 @@ public class TodoBusinessDao extends ServiceImpl<TodoBusinessMapper, TodoBusines
.one();
}
public Optional<TodoBusiness> findTodoByBizCode(String templateCode, String bizCode) {
if (StringUtils.isBlank(bizCode))
return Optional.empty();
TodoBusiness todo = lambdaQuery()
.eq(StringUtils.isNotBlank(templateCode), TodoBusiness::getTemplateCode, templateCode)
.eq(TodoBusiness::getBizCode, bizCode)
.eq(TodoBusiness::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.last("LIMIT 1")
.one();
return Optional.ofNullable(todo);
}
public List<TodoBusiness> getByIds(Collection<Long> ids) {
if (CollectionUtils.isEmpty(ids))
return Collections.emptyList();

View File

@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
@ -18,11 +19,17 @@ import java.util.Optional;
@Component
public class TodoDao extends ServiceImpl<TodoMapper, Todo> {
public Optional<Todo> findTodoByCode(String identityCode) {
if (StringUtils.isBlank(identityCode))
public Optional<Todo> findByIdOrCode(Long id, String idCode) {
if (id != null && id > 0)
return Optional.ofNullable(getById(id));
return findTodoByCode(idCode);
}
public Optional<Todo> findTodoByCode(String idCode) {
if (StringUtils.isBlank(idCode))
return Optional.empty();
Todo todo = lambdaQuery()
.eq(Todo::getIdentityCode, identityCode)
.eq(Todo::getIdentityCode, idCode)
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.last("LIMIT 1")
.one();
@ -36,4 +43,19 @@ public class TodoDao extends ServiceImpl<TodoMapper, Todo> {
.list();
}
public Optional<Todo> getLatestByBiz(@Nullable String templateCode,
String bizCode, String subBizCode) {
if (StringUtils.isBlank(bizCode) && StringUtils.isBlank(subBizCode))
return Optional.empty();
Todo todo = lambdaQuery()
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
.eq(StringUtils.isNotBlank(templateCode), Todo::getTemplateCode, templateCode)
.eq(StringUtils.isNotBlank(bizCode), Todo::getBizCode, bizCode)
.eq(StringUtils.isNotBlank(subBizCode), Todo::getSubBizCode, subBizCode)
.orderByDesc(Todo::getId)
.last("LIMIT 1")
.one();
return Optional.ofNullable(todo);
}
}

View File

@ -1,7 +1,6 @@
package cn.axzo.msg.center.dal.mapper;
import cn.axzo.msg.center.domain.dto.PendingCalendarCodeDTO;
import cn.axzo.msg.center.domain.entity.PendingMessageRecord;
import cn.axzo.msg.center.domain.entity.Todo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
@ -13,7 +12,7 @@ import java.util.List;
* @author yanglin
*/
public interface TodoMapper extends BaseMapper<Todo> {
List<Todo> queryByTemplateCodes(@Param("pendingCalendarCodeDTO") PendingCalendarCodeDTO pendingCalendarCodeDTO,
List<Todo> queryForWorkerCalendar(@Param("calendarCodes") PendingCalendarCodeDTO calendarCodes,
@Param("workspaceId") Long workspaceId,
@Param("selectDate") Date selectDate,
@Param("personId") Long personId,

View File

@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.axzo.msg.center.dal.mapper.TodoMapper">
<select id="queryByTemplateCodes" resultType="cn.axzo.msg.center.domain.entity.Todo">
<select id="queryForWorkerCalendar" resultType="cn.axzo.msg.center.domain.entity.Todo">
select * from todo where id in(SELECT
id
FROM
@ -21,7 +21,7 @@
AND is_delete = 0
AND executor_person_id = #{personId}
AND type = 'EXECUTABLE'
AND template_code IN <foreach collection="pendingCalendarCodeDTO.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
AND template_code IN <foreach collection="calendarCodes.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
ORDER BY create_at ASC) old_undo) aa,(SELECT (@i:=0))tt
UNION
ALL
@ -39,7 +39,7 @@
AND is_delete = 0
AND executor_person_id = #{personId}
AND type = 'EXECUTABLE'
AND template_code IN <foreach collection="pendingCalendarCodeDTO.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
AND template_code IN <foreach collection="calendarCodes.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
ORDER BY create_at DESC) new_todo) aa,(SELECT (@i:=0))tt
UNION
ALL
@ -56,7 +56,7 @@
AND is_delete = 0
AND executor_person_id = #{personId}
AND type = 'EXECUTABLE'
AND template_code IN <foreach collection="pendingCalendarCodeDTO.punchInCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
AND template_code IN <foreach collection="calendarCodes.punchInCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
ORDER BY create_at DESC) new_todo_3) aa,(SELECT (@i:=0))tt
UNION
ALL
@ -73,7 +73,7 @@
AND is_delete = 0
AND executor_person_id = #{personId}
AND type = 'EXECUTABLE'
AND template_code IN <foreach collection="pendingCalendarCodeDTO.clockInCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
AND template_code IN <foreach collection="calendarCodes.clockInCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
ORDER BY create_at DESC) new_todo_4) aa,(SELECT (@i:=0))tt
UNION
ALL
@ -87,13 +87,13 @@
todo
WHERE (DATE(create_at) = DATE(#{selectDate}) <if test="isNowDay">
OR (DATE(create_at) <![CDATA[<]]> DATE(#{selectDate}) AND state = ('COMPLETED') AND DATE(update_at) = DATE(#{selectDate})
AND template_code IN <foreach collection="pendingCalendarCodeDTO.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>)
AND template_code IN <foreach collection="calendarCodes.constructionCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>)
</if>)
AND state = 'COMPLETED'
AND is_delete = 0
AND executor_person_id = #{personId}
AND type = 'EXECUTABLE'
AND template_code IN <foreach collection="pendingCalendarCodeDTO.allCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
AND template_code IN <foreach collection="calendarCodes.allCodes" index="index" item="item" open="(" separator="," close=")">#{item}</foreach>
ORDER BY create_at DESC) all_new_do) aa,(SELECT (@i:=0))tt
) ccc ORDER BY flag ASC,mi asc)
</select>

View File

@ -20,7 +20,7 @@ import java.util.Date;
*/
@Setter
@Getter
@TableName("todo")
@TableName(value = "todo", autoResultMap = true)
public class Todo extends BaseEntityExt<Todo> {
/**
@ -145,7 +145,7 @@ public class Todo extends BaseEntityExt<Todo> {
*/
private YesOrNo isOuIdMigrated;
@TableField(value = "record_ext", typeHandler = FastjsonTypeHandler.class)
@TableField(typeHandler = FastjsonTypeHandler.class)
private PendingRecordExt recordExt;
}

View File

@ -18,7 +18,7 @@ import java.util.Date;
*/
@Setter
@Getter
@TableName("todo_business")
@TableName(value = "todo_business", autoResultMap = true)
public class TodoBusiness extends BaseEntityExt<TodoBusiness> {
/**

View File

@ -16,7 +16,7 @@ import java.util.Map;
*/
@Setter
@Getter
@TableName("todo_log")
@TableName(value = "todo_log", autoResultMap = true)
public class TodoLog extends BaseEntityExt<TodoLog> {
/**
@ -34,6 +34,11 @@ public class TodoLog extends BaseEntityExt<TodoLog> {
*/
private TodoLogType logType;
/**
* 上下文
*/
private String context;
/**
* 消息的唯一标识
*/

View File

@ -1,6 +1,8 @@
package cn.axzo.msg.center.message.service;
import cn.axzo.msg.center.MsgCenterApplication;
import cn.axzo.msg.center.message.domain.dto.PendingMessageStatisticDTO;
import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.service.pending.request.PendingMessageIterateRequest;
import cn.axzo.msg.center.service.pending.response.PendingMessageIterateResponse;
@ -11,6 +13,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author yanglin
*/
@ -21,9 +25,10 @@ class PendingMessageNewServiceTest {
@Test
void foo() {
String str = "{\"bizCategory\":\"OTHER\",\"bizCode\":\"200000700321808\",\"bizExtParams\":\"{\\\"teamLeaderName\\\":\\\"袁均清\\\"}\",\"executor\":[{\"id\":16562,\"identity\":{\"id\":0,\"type\":\"NOT_SUPPORT\",\"valid\":true},\"name\":\"马元猛\",\"valid\":true},{\"id\":16563,\"identity\":{\"id\":0,\"type\":\"NOT_SUPPORT\",\"valid\":true},\"name\":\"辛宁\",\"valid\":true}],\"orgType\":\"PROJECT\",\"ouId\":6066,\"promoter\":{\"id\":16444,\"identity\":{\"id\":2004889,\"type\":\"PRACTITIONER\",\"valid\":true},\"name\":\"袁均清\",\"valid\":true},\"routerParams\":\"{\\\"acceptanceNo\\\":\\\"700013411\\\",\\\"status\\\":\\\"1\\\"}\",\"templateCode\":\"52ae3e8ec48242e485e9389202e102ce\",\"workspaceId\":375}";
PendingMessagePushParam param = JSON.parseObject(str, PendingMessagePushParam.class);
pendingMessageNewService.push(param);
String jsonStr = "{\"operator\":{\"id\":80792,\"identity\":{\"id\":2000028,\"type\":\"PRACTITIONER\",\"valid\":true},\"valid\":true},\"ouId\":5140,\"terminalType\":\"CMS_WEB_PC\"}";
MessageGroupNodeStatisticParam request = JSON.parseObject(jsonStr, MessageGroupNodeStatisticParam.class);
List<PendingMessageStatisticDTO> pendingMessageStatisticDTOS = pendingMessageNewService.groupStatistic(request);
System.out.println();
}
}

View File

@ -0,0 +1,37 @@
package cn.axzo.msg.center.message.service.todo.manage;
import cn.axzo.msg.center.MsgCenterApplication;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.TodoType;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author yanglin
*/
@SpringBootTest(classes = MsgCenterApplication.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
class TodoManagerTest {
private final TodoManager todoManager;
@Test @Commit
void send() {
PendingMessagePushParam request = new PendingMessagePushParam();
request.setPromoter(new PersonDTO(9000399704L, null, null));
request.setExecutor(Collections.singletonList(new PersonDTO(80792L, null, null)));
request.setTemplateCode("68ca17b9ee184921ba32dbb6ccb24d4a");
request.setBizCode("202403201407000000001");
request.setTodoType(TodoType.COPIED_TO_ME);
todoManager.send(request);
}
}

View File

@ -6,8 +6,6 @@ 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
*/
@ -18,7 +16,7 @@ class MigrateFromPendingMessageJobTest {
private final MigrateFromPendingMessageJob migrateFromPendingMessageJob;
@Test
void foo() {
void migrate() {
migrateFromPendingMessageJob.execute(null);
}