REQ-2135: 提供更加强大的模版内容解析

This commit is contained in:
yanglin 2024-04-01 14:04:35 +08:00
parent c3042796a5
commit 8bfdc54d20
6 changed files with 71 additions and 44 deletions

View File

@ -93,6 +93,7 @@ import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -785,6 +786,9 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
.orElse(null); .orElse(null);
// 解析并替换掉路由地址中的动态参数变量 // 解析并替换掉路由地址中的动态参数变量
JSONObject routerParam = JSONObjectUtil.parseObject(adapter.getRouterParams()); JSONObject routerParam = JSONObjectUtil.parseObject(adapter.getRouterParams());
routerParam.put(MessageRouterUtil.CTX, ImmutableMap.of(
"business", adapter.getTodoBusiness(),
"todo", adapter.getTodo()));
JSONObject authParam = new JSONObject(); JSONObject authParam = new JSONObject();
if (!routerParam.containsKey("ouId")) { if (!routerParam.containsKey("ouId")) {
authParam.put("ouId", adapter.getOuId()); authParam.put("ouId", adapter.getOuId());

View File

@ -13,12 +13,14 @@ import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType; import cn.axzo.msg.center.service.enums.TodoType;
import cn.axzo.msg.center.service.enums.YesOrNo; import cn.axzo.msg.center.service.enums.YesOrNo;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import java.util.Date; import java.util.Date;
/** /**
* @author yanglin * @author yanglin
*/ */
@Getter
public class TodoRecordAdapter implements PendingRecordAdapter { public class TodoRecordAdapter implements PendingRecordAdapter {
private final TodoBusiness business; private final TodoBusiness business;
@ -226,4 +228,9 @@ public class TodoRecordAdapter implements PendingRecordAdapter {
public Boolean isRead() { public Boolean isRead() {
return getState() == PendingMessageStateEnum.READ; return getState() == PendingMessageStateEnum.READ;
} }
@Override
public TodoBusiness getTodoBusiness() {
return business;
}
} }

View File

@ -47,6 +47,7 @@ import java.util.stream.Collectors;
public final class MessageRouterUtil { public final class MessageRouterUtil {
public static final String DETAIL_ROUTER_DESC = "详情"; public static final String DETAIL_ROUTER_DESC = "详情";
public static final String CTX = "ctx";
/** /**
* 获取业务详情路由策略 * 获取业务详情路由策略
@ -227,11 +228,13 @@ public final class MessageRouterUtil {
} }
private static String concatRouterParam(String originalUrl, JSONObject routerParam) { private static String concatRouterParam(String originalUrl, JSONObject routerParam) {
if (routerParam.isEmpty()) { JSONObject copy = new JSONObject(routerParam);
copy.remove(CTX);
if (copy.isEmpty()) {
return originalUrl; return originalUrl;
} }
StringBuilder paramBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder();
for (Map.Entry<String, Object> entry : routerParam.entrySet()) { for (Map.Entry<String, Object> entry : copy.entrySet()) {
if (originalUrl.contains(entry.getKey() + "=")) { if (originalUrl.contains(entry.getKey() + "=")) {
continue; continue;
} }

View File

@ -19,6 +19,10 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency> <dependency>
<groupId>cn.axzo.framework</groupId> <groupId>cn.axzo.framework</groupId>
<artifactId>axzo-auth-spring-boot-starter</artifactId> <artifactId>axzo-auth-spring-boot-starter</artifactId>

View File

@ -1,11 +1,18 @@
package cn.axzo.msg.center.common.utils; package cn.axzo.msg.center.common.utils;
import lombok.RequiredArgsConstructor; import cn.axzo.basics.common.exception.ServiceException;
import org.apache.ibatis.ognl.NullHandler; import com.alibaba.fastjson.JSON;
import org.apache.ibatis.ognl.OgnlRuntime; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.parsing.TokenHandler; import org.springframework.context.expression.MapAccessor;
import org.apache.ibatis.scripting.xmltags.OgnlCache; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.NonNull;
import java.util.Map; import java.util.Map;
@ -14,59 +21,53 @@ import java.util.Map;
* *
* @author yanglin * @author yanglin
*/ */
@Slf4j
public class TemplateParser { public class TemplateParser {
static {
OgnlRuntime.setNullHandler(Map.class, new MapPropertyHandler());
}
public static String parseNumberSign(String expression, Object root) { public static String parseNumberSign(String expression, Object root) {
if (expression == null || root == null) return parseImpl("#{", "}", expression, root);
return expression;
GenericTokenParser parser = new GenericTokenParser("#{", "}", new OgnlTokenHandler(root));
return parser.parse(expression);
} }
public static String parseDollarSign(String expression, Object root) { public static String parseDollarSign(String expression, Object root) {
return parseImpl("${", "}", expression, root);
}
@SuppressWarnings("SameParameterValue")
private static String parseImpl(String openToken, String closeToken, String expression, Object root) {
if (expression == null || root == null) if (expression == null || root == null)
return expression; return expression;
GenericTokenParser parser = new GenericTokenParser("${", "}", new OgnlTokenHandler(root)); try {
return parser.parse(expression); StandardEvaluationContext evalContext = new StandardEvaluationContext(root);
evalContext.addPropertyAccessor(new NullableMapAccessor());
evalContext.addPropertyAccessor(new ReflectivePropertyAccessor());
TemplateParserContext templateContext = new TemplateParserContext(openToken, closeToken);
Expression exp = new SpelExpressionParser().parseExpression(expression, templateContext);
return exp.getValue(evalContext, String.class);
} catch (BuilderException e) {
log.warn("无效的模版内容, 请检查格式是否正确. expression={}, value={}",
expression, JSON.toJSONString(root), e);
throw new ServiceException(String.format("无效的模版内容, 请检查格式是否正确. expression=%s, value=%s",
expression, JSON.toJSONString(root)));
}
} }
@RequiredArgsConstructor private static class NullableMapAccessor extends MapAccessor {
private static class OgnlTokenHandler implements TokenHandler {
private final Object root;
@Override @Override
public String handleToken(String content) { public boolean canRead(@NonNull EvaluationContext context, Object target, @NonNull String name) {
Object value = OgnlCache.getValue(content, root); return target instanceof Map;
return value == null ? "" : String.valueOf(value);
} }
}
private static class MapPropertyHandler implements NullHandler {
@Override @Override
public Object nullMethodResult(Map context, Object target, String methodName, Object[] args) { @NonNull
return null; public TypedValue read(@NonNull EvaluationContext context, Object target, @NonNull String name) {
Map<?, ?> map = (Map<?, ?>) target;
Object value = map.get(name);
if (value == null)
return TypedValue.NULL;
return new TypedValue(value);
} }
/**
* 支持流程的 [_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;
}
} }
} }

View File

@ -102,4 +102,12 @@ public interface PendingRecordAdapter {
default Long getPromoterOuId() { default Long getPromoterOuId() {
return 0L; return 0L;
} }
default Todo getTodo() {
return null;
}
default TodoBusiness getTodoBusiness() {
return null;
}
} }