REQ-2135: 模版支持属性级联访问
This commit is contained in:
parent
cfe90fce11
commit
91257be07e
@ -67,8 +67,7 @@ public class PrivateMessageController {
|
||||
|
||||
@PostMapping("/getGroupTemplates")
|
||||
public Object getGroupTemplates(@RequestParam("groupNodeCode") String groupNodeCode) {
|
||||
return groupTemplateService.collectGroupTemplates(groupNodeCode)
|
||||
.getTemplateCodes();
|
||||
return groupTemplateService.collectTemplateCodes(groupNodeCode);
|
||||
}
|
||||
|
||||
@PostMapping("/getPersonIdByPhone")
|
||||
|
||||
@ -38,6 +38,8 @@ public class GroupTemplateService {
|
||||
private final MessageTemplateGroupDao messageTemplateGroupDao;
|
||||
private final PendingMessageBizConfig cfg;
|
||||
|
||||
// !! group nodes
|
||||
|
||||
public RootNodeWrapper getGroupRoot() {
|
||||
List<MessageGroupNode> groupNodes = messageGroupNodeDao.lambdaQuery()
|
||||
.eq(MessageGroupNode::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
||||
@ -45,12 +47,12 @@ public class GroupTemplateService {
|
||||
Map<String, Long> nodeCode2Id = groupNodes.stream()
|
||||
.collect(toMap(MessageGroupNode::getCode, BaseEntityExt::getId));
|
||||
List<NodeWrapper> nodes = groupNodes.stream()
|
||||
.map(n -> new NodeWrapper(n, nodeCode2Id))
|
||||
.map(node -> new NodeWrapper(node, nodeCode2Id))
|
||||
.collect(toList());
|
||||
return new RootNodeWrapper(TreeBuilder.build(nodes));
|
||||
return new RootNodeWrapper(TreeBuilder.build(nodes, true));
|
||||
}
|
||||
|
||||
public List<ValueNode<NodeWrapper>> getTodoConfiguredGroups(
|
||||
public List<ValueNode<NodeWrapper>> getTodoGroups(
|
||||
AppTerminalTypeEnum terminal, boolean leafOnly) {
|
||||
Set<Long> configuredIds = new HashSet<>(
|
||||
cfg.fetchMessageGroupTreeNodeIds(terminal));
|
||||
@ -61,9 +63,7 @@ public class GroupTemplateService {
|
||||
if (valueNode == null)
|
||||
return true;
|
||||
MessageGroupNode groupNode = valueNode.getValue().unwrap();
|
||||
if (!MessageGroupCategoryEnum.PENDING
|
||||
.getMsgGroupNodeCategories()
|
||||
.contains(groupNode.getCategory()))
|
||||
if (!MessageGroupCategoryEnum.PENDING.isGroupCategory(groupNode.getCategory()))
|
||||
return true;
|
||||
if (configuredIds.contains(groupNode.getId()))
|
||||
configuredNodes.add(valueNode);
|
||||
@ -84,26 +84,40 @@ public class GroupTemplateService {
|
||||
return leafNodes;
|
||||
}
|
||||
|
||||
public GroupTemplates collectGroupTemplates(List<ValueNode<NodeWrapper>> nodes) {
|
||||
// !! templates
|
||||
|
||||
public List<String> collectTemplateCodes(List<ValueNode<NodeWrapper>> nodes) {
|
||||
return collectTemplates(nodes).getTemplateCodes();
|
||||
}
|
||||
|
||||
public List<String> collectTemplateCodes(String groupNodeCode) {
|
||||
return collectTemplates(groupNodeCode).getTemplateCodes();
|
||||
}
|
||||
|
||||
public List<String> collectTemplateCodes(Collection<String> groupNodeCodes) {
|
||||
return collectTemplates(groupNodeCodes).getTemplateCodes();
|
||||
}
|
||||
|
||||
public GroupTemplates collectTemplates(List<ValueNode<NodeWrapper>> nodes) {
|
||||
Set<String> groupNodeCodes = nodes.stream()
|
||||
.map(ValueNode::getValue)
|
||||
.map(NodeWrapper::unwrap)
|
||||
.map(MessageGroupNode::getCode)
|
||||
.collect(toSet());
|
||||
return collectGroupTemplates(groupNodeCodes);
|
||||
return collectTemplates(groupNodeCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过分组结点编码路径查询关联的模板编码列表, 包含子节点
|
||||
*/
|
||||
public GroupTemplates collectGroupTemplates(String groupNodeCode) {
|
||||
return collectGroupTemplates(Collections.singletonList(groupNodeCode));
|
||||
public GroupTemplates collectTemplates(String groupNodeCode) {
|
||||
return collectTemplates(Collections.singletonList(groupNodeCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过分组结点编码路径查询关联的模板编码列表, 包含子节点
|
||||
*/
|
||||
public GroupTemplates collectGroupTemplates(Collection<String> groupNodeCodes) {
|
||||
public GroupTemplates collectTemplates(Collection<String> groupNodeCodes) {
|
||||
RootNodeWrapper root = getGroupRoot();
|
||||
if (CollectionUtils.isEmpty(groupNodeCodes))
|
||||
return new GroupTemplates(root);
|
||||
|
||||
@ -383,9 +383,7 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
|
||||
private IPage<MessageBaseTemplate> pageQueryBaseTemplate(MessageTemplatePageRequest request) {
|
||||
List<String> templateCodes = Lists.newArrayList();
|
||||
if (StringUtils.isNotBlank(request.getGroupNodeCode())) {
|
||||
templateCodes = groupTemplateService
|
||||
.collectGroupTemplates(request.getGroupNodeCode())
|
||||
.getTemplateCodes();
|
||||
templateCodes = groupTemplateService.collectTemplateCodes(request.getGroupNodeCode());
|
||||
if (CollectionUtils.isEmpty(templateCodes)) {
|
||||
// 入参中的分类没有关联任何模板,直接返回查询结果
|
||||
return null;
|
||||
|
||||
@ -156,7 +156,7 @@ public class TodoRangeQueryService {
|
||||
.eq(request.getWorkspaceId() != null, Todo::getOrgId, request.getWorkspaceId())
|
||||
.last(nested.isEmptyOfWhere(), "1 = 1"))
|
||||
// 这个条件放在最后, 因为templateCodes一般比较多
|
||||
// 如果放到前面, 格式化后会不方便查询其它的查询条件
|
||||
// 如果放到前面, 格式化后会不方便查看其它的查询条件
|
||||
// 用于查询的模版会通过一个单独的字段打印出来, 用于排查问题
|
||||
.in(Todo::getTemplateCode, templateCodes);
|
||||
PageQuerySort.TODO.appendSortExpr(request, query);
|
||||
@ -172,15 +172,11 @@ public class TodoRangeQueryService {
|
||||
*/
|
||||
public List<String> determineVisibleTemplateCodes(PendingMessagePageRequest request) {
|
||||
if (!request.determineGroupNodeCodes().isEmpty())
|
||||
return groupTemplateService
|
||||
.collectGroupTemplates(request.determineGroupNodeCodes())
|
||||
.getTemplateCodes();
|
||||
return groupTemplateService.collectTemplateCodes(request.determineGroupNodeCodes());
|
||||
// 获取可见的模版时, 不用定位到叶子节点
|
||||
List<ValueNode<NodeWrapper>> nodes = groupTemplateService
|
||||
.getTodoConfiguredGroups(request.getAppTerminalType(), false);
|
||||
return groupTemplateService
|
||||
.collectGroupTemplates(nodes)
|
||||
.getTemplateCodes();
|
||||
.getTodoGroups(request.getAppTerminalType(), false);
|
||||
return groupTemplateService.collectTemplateCodes(nodes);
|
||||
}
|
||||
|
||||
// !! stat
|
||||
@ -219,9 +215,7 @@ public class TodoRangeQueryService {
|
||||
|
||||
private int countStatByNodes(MessageGroupNodeStatisticParam request,
|
||||
List<ValueNode<NodeWrapper>> nodes, TodoType todoType) {
|
||||
List<String> templateCodes = groupTemplateService
|
||||
.collectGroupTemplates(nodes)
|
||||
.getTemplateCodes();
|
||||
List<String> templateCodes = groupTemplateService.collectTemplateCodes(nodes);
|
||||
if (CollectionUtils.isEmpty(templateCodes))
|
||||
return 0;
|
||||
LambdaQueryWrapper<Todo> query = todoQuery(request.getOuId())
|
||||
@ -241,7 +235,7 @@ public class TodoRangeQueryService {
|
||||
if (CollectionUtils.isNotEmpty(request.getGroupNodeCodes()))
|
||||
return groupTemplateService.getGroupRoot().getNodes(request.getGroupNodeCodes());
|
||||
// 只返回给前端叶子节点的信息, 前端不需要层级信息
|
||||
return groupTemplateService.getTodoConfiguredGroups(request.getTerminalType(), true);
|
||||
return groupTemplateService.getTodoGroups(request.getTerminalType(), true);
|
||||
}
|
||||
|
||||
// helper
|
||||
|
||||
@ -81,7 +81,7 @@ public class TodoWorkerQueryService {
|
||||
: pageResp.getList();
|
||||
if (CollectionUtils.isEmpty(pendingList))
|
||||
return statResp;
|
||||
GroupTemplates groupTemplates = groupTemplateService.collectGroupTemplates(groupNodeCodes);
|
||||
GroupTemplates groupTemplates = groupTemplateService.collectTemplates(groupNodeCodes);
|
||||
Function<String, PendingMessageResponse> findPendingFun = groupNodeCode -> {
|
||||
for (PendingMessageResponse pending : pendingList) {
|
||||
if (groupTemplates.groupContainsTemplate(groupNodeCode, pending.getTemplateCode()))
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package cn.axzo.msg.center.utils;
|
||||
|
||||
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
|
||||
import cn.axzo.msg.center.common.utils.TemplateParser;
|
||||
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
@ -25,9 +27,7 @@ public class QueryFormatter {
|
||||
Map<String, Object> name2Values = wrapper.getParamNameValuePairs();
|
||||
if (StringUtils.isBlank(sql) || name2Values == null || name2Values.isEmpty())
|
||||
return sql;
|
||||
sql = sql.replaceAll("ew.paramNameValuePairs\\.", "");
|
||||
sql = sql.replaceAll("#", "\\$");
|
||||
return PlaceholderResolver.tryResolve(sql, name2Values);
|
||||
return TemplateParser.parseNumberSign(sql, ImmutableMap.of(Constants.WRAPPER, wrapper));
|
||||
}
|
||||
|
||||
}
|
||||
@ -30,4 +30,8 @@ public enum MessageGroupCategoryEnum {
|
||||
|
||||
private final Set<MessageGroupNodeCategoryEnum> msgGroupNodeCategories;
|
||||
private final Set<MessageCategoryEnum> msgCategories;
|
||||
|
||||
public boolean isGroupCategory(MessageGroupNodeCategoryEnum category) {
|
||||
return msgGroupNodeCategories.contains(category);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package cn.axzo.msg.center.common.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.utils.Lists;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -17,6 +18,7 @@ import java.util.stream.Stream;
|
||||
* @author wangli
|
||||
* @date 2022/3/26 14:28
|
||||
*/
|
||||
@Slf4j
|
||||
public class PlaceholderResolver {
|
||||
|
||||
/**
|
||||
@ -182,8 +184,15 @@ public class PlaceholderResolver {
|
||||
* @return 替换完成后的字符串。
|
||||
*/
|
||||
public String resolveByMap(String content, final Map<String, Object> valueMap) {
|
||||
return resolveByRule(content,
|
||||
placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
|
||||
try {
|
||||
// 支持级联: person.addresses[1].info
|
||||
return TemplateParser.parseDollarSign(content, valueMap);
|
||||
} catch (Exception e) {
|
||||
log.warn("error eval {}, parameters={}", content, JSON.toJSONString(valueMap), e);
|
||||
// fallback
|
||||
return resolveByRule(content,
|
||||
placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
package cn.axzo.msg.center.common.utils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.ibatis.ognl.NullHandler;
|
||||
import org.apache.ibatis.ognl.OgnlRuntime;
|
||||
import org.apache.ibatis.parsing.GenericTokenParser;
|
||||
import org.apache.ibatis.parsing.TokenHandler;
|
||||
import org.apache.ibatis.scripting.xmltags.OgnlCache;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支持级联: person.addresses[1].info
|
||||
*
|
||||
* @author yanglin
|
||||
*/
|
||||
public class TemplateParser {
|
||||
|
||||
static {
|
||||
OgnlRuntime.setNullHandler(Map.class, new MapPropertyHandler());
|
||||
}
|
||||
|
||||
public static String parseNumberSign(String expression, Object root) {
|
||||
if (expression == null || root == null)
|
||||
return expression;
|
||||
GenericTokenParser parser = new GenericTokenParser("#{", "}", new OgnlTokenHandler(root));
|
||||
return parser.parse(expression);
|
||||
}
|
||||
|
||||
public static String parseDollarSign(String expression, Object root) {
|
||||
if (expression == null || root == null)
|
||||
return expression;
|
||||
GenericTokenParser parser = new GenericTokenParser("${", "}", new OgnlTokenHandler(root));
|
||||
return parser.parse(expression);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class OgnlTokenHandler implements TokenHandler {
|
||||
|
||||
private final Object root;
|
||||
|
||||
@Override
|
||||
public String handleToken(String content) {
|
||||
Object value = OgnlCache.getValue(content, root);
|
||||
return value == null ? "" : String.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MapPropertyHandler implements NullHandler {
|
||||
|
||||
@Override
|
||||
public Object nullMethodResult(Map context, Object target, String methodName, Object[] args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持流程的 [_PENDING_VARIABLES] 的写法. 只支持顶层的这种写法, 不然词法解析不了
|
||||
*/
|
||||
@Override
|
||||
public Object nullPropertyValue(Map context, Object target, Object property) {
|
||||
if (target instanceof Map && property instanceof String) {
|
||||
String key = (String) property;
|
||||
String bracketsKey = String.format("[%s]", key.trim());
|
||||
if (((Map) target).containsKey(bracketsKey))
|
||||
return bracketsKey;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user