REQ-2135: 模版支持属性级联访问

This commit is contained in:
yanglin 2024-04-01 11:14:07 +08:00
parent cfe90fce11
commit 91257be07e
9 changed files with 125 additions and 35 deletions

View File

@ -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")

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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()))

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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)));
}
}
/**

View File

@ -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;
}
}
}