Operation.ON 改为 IF 并新增支持执行自定义代码

This commit is contained in:
TommyLemon 2023-06-04 18:21:13 +08:00
parent 39bdc5899a
commit 1b7c6f1d08
4 changed files with 157 additions and 27 deletions

View File

@ -98,8 +98,8 @@ public class JSON {
* @return
*/
public static <T> T parseObject(String json, Class<T> clazz) {
if (clazz == null) {
Log.e(TAG, "parseObject clazz == null >> return null;");
if (clazz == null || StringUtil.isEmpty(json, true)) {
Log.e(TAG, "parseObject clazz == null || StringUtil.isEmpty(json, true) >> return null;");
} else {
try {
return com.alibaba.fastjson.JSON.parseObject(getCorrectJson(json), clazz, DEFAULT_FASTJSON_FEATURES);
@ -132,11 +132,15 @@ public class JSON {
* @return
*/
public static JSONArray parseArray(String json) {
if (StringUtil.isEmpty(json, true)) {
Log.e(TAG, "parseArray StringUtil.isEmpty(json, true) >> return null;");
} else {
try {
return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true));
} catch (Exception e) {
Log.i(TAG, "parseArray catch \n" + e.getMessage());
}
}
return null;
}
/**JSONArray转实体类列表
@ -153,8 +157,8 @@ public class JSON {
* @return
*/
public static <T> List<T> parseArray(String json, Class<T> clazz) {
if (clazz == null) {
Log.e(TAG, "parseArray clazz == null >> return null;");
if (clazz == null || StringUtil.isEmpty(json, true)) {
Log.e(TAG, "parseArray clazz == null || StringUtil.isEmpty(json, true) >> return null;");
} else {
try {
return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true), clazz);
@ -170,6 +174,9 @@ public class JSON {
* @return
*/
public static String toJSONString(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
}
@ -187,6 +194,9 @@ public class JSON {
* @return
*/
public static String toJSONString(Object obj, SerializerFeature... features) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
}

View File

@ -23,7 +23,8 @@ import static apijson.orm.Operation.TYPE;
import static apijson.orm.Operation.UNIQUE;
import static apijson.orm.Operation.UPDATE;
import static apijson.orm.Operation.VERIFY;
import static apijson.orm.Operation.ON;
import static apijson.orm.Operation.IF;
//import static apijson.orm.Operation.CODE;
import java.net.URL;
import java.time.LocalDate;
@ -33,6 +34,8 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import apijson.orm.script.JavaScriptExecutor;
import apijson.orm.script.ScriptExecutor;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@ -65,6 +68,10 @@ import apijson.orm.model.AllTableComment;
import apijson.orm.model.AllColumnComment;
import apijson.orm.model.TestRecord;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
/**校验器(权限请求参数返回结果等)
* TODO 合并 Structure 的代码
* @author Lemon
@ -107,6 +114,10 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
*/
public static final String ADMIN = "ADMIN";
public static ParserCreator PARSER_CREATOR;
public static ScriptEngineManager SCRIPT_ENGINE_MANAGER;
public static ScriptEngine SCRIPT_ENGINE;
// 共享 STRUCTURE_MAP 则不能 remove 等做任何变更否则在并发情况下可能会出错加锁效率又低所以这里改为忽略对应的 key
public static Map<String, Entry<String, Object>> ROLE_MAP;
@ -133,6 +144,9 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
@NotNull
public static final Map<String, Pattern> COMPILE_MAP;
static {
SCRIPT_ENGINE_MANAGER = new ScriptEngineManager();
SCRIPT_ENGINE = SCRIPT_ENGINE_MANAGER.getEngineByName("js");
ROLE_MAP = new LinkedHashMap<>();
ROLE_MAP.put(UNKNOWN, new Entry<String, Object>());
ROLE_MAP.put(LOGIN, new Entry<String, Object>("userId>", 0));
@ -152,7 +166,8 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
OPERATION_KEY_LIST.add(REMOVE.name());
OPERATION_KEY_LIST.add(MUST.name());
OPERATION_KEY_LIST.add(REFUSE.name());
OPERATION_KEY_LIST.add(ON.name());
OPERATION_KEY_LIST.add(IF.name());
// OPERATION_KEY_LIST.add(CODE.name());
OPERATION_KEY_LIST.add(ALLOW_PARTIAL_UPDATE_FAIL.name());
@ -914,7 +929,19 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
String remove = StringUtil.getString(target.getString(REMOVE.name()));
String must = StringUtil.getString(target.getString(MUST.name()));
String refuse = StringUtil.getString(target.getString(REFUSE.name()));
JSONObject on = target.getJSONObject(ON.name());
Object _if = target.get(IF.name());
boolean ifIsStr = _if instanceof String && StringUtil.isNotEmpty(_if, true);
JSONObject ifObj = ifIsStr == false && _if instanceof JSONObject ? (JSONObject) _if : null;
// : (_if instanceof String ? new apijson.JSONRequest((String) _if, "" /* "throw new Error('')" */ ) : null);
if (ifObj == null && _if != null && ifIsStr == false) {
// if (_if instanceof JSONArray) {
// }
throw new IllegalArgumentException(name + ": { " + IF.name() + ": value } 中 value 类型错误!只允许 String, JSONObject");
}
// Object code = target.get(CODE.name());
String allowPartialUpdateFail = StringUtil.getString(target.getString(ALLOW_PARTIAL_UPDATE_FAIL.name()));
@ -1176,17 +1203,45 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
// 校验并配置允许部分批量增删改失败>>>>>>>>>>>>>>>>>>>
String[] nks = on == null ? null : StringUtil.split(real.getString(JSONRequest.KEY_NULL));
String[] nks = ifObj == null ? null : StringUtil.split(real.getString(JSONRequest.KEY_NULL));
Collection<?> nkl = nks == null || nks.length <= 0 ? new HashSet<>() : Arrays.asList(nks);
Set<Map.Entry<String, Object>> onSet = on == null ? null : on.entrySet();
if (onSet != null) {
Set<Map.Entry<String, Object>> ifSet = ifObj == null ? null : ifObj.entrySet();
if (ifIsStr || (ifSet != null && ifSet.isEmpty() == false)) {
// 没必要限制都是后端配置的安全可控而且可能确实有特殊需求需要 id, @column
// List<String> condKeys = new ArrayList<>(Arrays.asList(apijson.JSONRequest.KEY_ID, apijson.JSONRequest.KEY_ID_IN
// , apijson.JSONRequest.KEY_USER_ID, apijson.JSONRequest.KEY_USER_ID_IN));
// condKeys.addAll(JSONRequest.TABLE_KEY_LIST);
for (Map.Entry<String, Object> entry : onSet) {
String preCode = "var curObj = " + JSON.format(real) + ";";
// 未传的 key 在后面 eval 时总是报错 undefined而且可能有冲突例如对象里有 "curObj": val 键值对就会覆盖当前对象定义还不如都是 curObj.sex 这样取值
// Set<Map.Entry<String, Object>> rset = real.entrySet();
// for (Map.Entry<String, Object> entry : rset) {
// String k = entry == null ? null : entry.getKey();
// if (StringUtil.isEmpty(k)) {
// continue;
// }
// String vn = JSONResponse.formatOtherKey(k);
// if (StringUtil.isName(vn) == false) { // 通过 curObj['id@'] 这样取值写在 IF 配置里
// continue;
// }
//
// Object v = entry.getValue();
// String vs = v instanceof String ? "\"" + ((String) v).replaceAll("\"", "\\\"") + "\""
// : (JSON.isBooleanOrNumberOrString(v) ? v.toString() : JSON.format(v));
// preCode += "\nvar " + vn + " = " + vs + ";";
// }
if (ifIsStr) {
String ifStr = (String) _if;
int ind = ifStr.indexOf(":");
String lang = ind < 0 ? null : ifStr.substring(0, ind);
ScriptEngine engine = getScriptEngine(lang);
engine.eval(preCode + "\n" + _if);
}
else {
for (Map.Entry<String, Object> entry : ifSet) {
String k = entry == null ? null : entry.getKey();
// if (condKeys.contains(k)) {
// throw new IllegalArgumentException("Request 表 structure 配置的 " + ON.name()
@ -1194,8 +1249,31 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
// }
Object v = k == null ? null : entry.getValue();
if (v instanceof String) {
int ind = k.indexOf(":");
String lang = ind < 0 ? null : k.substring(0, ind);
ScriptEngine engine = getScriptEngine(lang);
k = ind < 0 ? k : k.substring(ind + 1);
boolean isElse = StringUtil.isEmpty(k, false); // 其它直接报错不允许传 StringUtil.isEmpty(k, true) || "ELSE".equals(k);
// String code = preCode + "\n\n" + (StringUtil.isEmpty(v, false) ? k : (isElse ? v : "if (" + k + ") {\n " + v + "\n}"));
String code = preCode + "\n\n" + (isElse ? v : "if (" + k + ") {\n " + v + "\n}");
// ScriptExecutor executor = new JavaScriptExecutor();
// executor.execute(null, real, )
engine.eval(code);
// PARSER_CREATOR.createFunctionParser()
// .setCurrentObject(real)
// .setKey(k)
// .setMethod(method)
// .invoke()
continue;
}
if (v instanceof JSONObject == false) {
throw new IllegalArgumentException("Request 表 structure 配置的 " + ON.name()
throw new IllegalArgumentException("Request 表 structure 配置的 " + IF.name()
+ ":{ " + k + ":value } 中 value 不合法,必须是 JSONObject {} ");
}
@ -1204,10 +1282,32 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
}
}
}
}
Log.i(TAG, "parse return real = " + JSON.toJSONString(real));
return real;
}
public static ScriptEngine getScriptEngine(String lang) {
List<ScriptEngineFactory> list = StringUtil.isEmpty(lang, true) ? null : SCRIPT_ENGINE_MANAGER.getEngineFactories();
ScriptEngine engine = null;
if (list == null || list.isEmpty()) {
engine = SCRIPT_ENGINE; // StringUtil.isEmpty(lang) ? SCRIPT_ENGINE : null;
}
else {
for (ScriptEngineFactory sef : list) {
if (sef != null && lang.equals(sef.getEngineName())) {
engine = sef.getScriptEngine();
}
}
}
if (engine == null) {
throw new NullPointerException("找不到可执行 " + (StringUtil.isEmpty(lang, true) ? "js" : lang) + " 脚本的引擎engine == null!");
}
return engine;
}
/**执行操作

View File

@ -119,8 +119,28 @@ public enum Operation {
* "key0": {}
* 例如 "name": { "UPDATE": { "Comment": { "userName@": "/name" } } }
* User.name 被修改时同步修改 Comment.userName
*
* 例如 "sex != 0 && sex != 1": "throw new Error('sex 必须在 [0, 1] 内!')"
* 自定义代码当满足条件是执行后面的代码
*
* 还有
* "ELSE": ""
* 自定义代码不处理和不传一样
*/
ON,
IF,
// /** 直接用 IF 替代
// * 自定义代码结构是 "code"例如
// * "var a = 1;
// * var b = a + 2;
// * if (b % 2 == 0) {
// * throw new Error('b % 2 == 0 !');
// * }
// * "
// *
// * { "code": "JS", "code2": "LUA" }
// */
// CODE,
/**
* 允许批量增删改部分失败结构是

View File

@ -66,8 +66,8 @@ public abstract class JSR223ScriptExecutor implements ScriptExecutor {
}
Map<String, Object> metaMap = new HashMap<>();
metaMap.put("version", parser.getVersion());
metaMap.put("tag", parser.getTag());
metaMap.put("version", parser == null ? 0 : parser.getVersion());
metaMap.put("tag", parser == null ? null : parser.getTag());
metaMap.put("args", args);
bindings.put("_meta", metaMap);
return compiledScript.eval(bindings);