Tencent / APIJSON

🏆 实时 零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构 🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and the returned JSON of API can be customized by Frontend(Client) users
http://apijson.cn
Other
17.11k stars 2.15k forks source link

apijson json服务编排 #482

Open cloudAndMonkey opened 1 year ago

cloudAndMonkey commented 1 year ago

Description

@TommyLemon 功能描述: 一个服务编排的伪码流程设计 将伪码转换为 apijson json、前置、后置函数、javascript、redis、elasticSearch等 并且能够mork每一步,执行中间流程,还能查询每个阶段执行的sql语句,及结果.

json格式:

{
    "@transaction": true, 
    "id@-()": "getCurrentUserId()", // 前置函数
    "@post": [
        "xxx"
    ], 
    "xxx": {
     "@method": "xxx",
      "@version": "", // 版本,控制校验
      "@datasource": "" // 数据源相关配置
     // 分表规则等,后面再说
   }
"@explain": true
}

1、独立定义一个url method, 通过解析不同method执行不同流程 和已有method区分开,避免歧义 2、最外层新增传参 "transaction": true 来指定开启事务 3、控制每条语句的数据源 4、完善 “@Explain" 如果没有执行计划,则返回sql语句. 能够在 reponse返回值中, 看到json中执行的每条sql,方便排错 5、@version支持 定义不同场景的 新增、修改、删除等执行规则. 请通过version版本区分 6、前置函数 1) 要能拿到其他数据源 2) 能拿到json执行过程中的数据 3) 拿到当前数据源(和外部json一个事物执行) 4) 从数据库/或其他数据源 查询获取对照关系 组装数据, 调用对应数据源的api方法执行即可 5) 前置函数,能调用其他函数 把一些计算, 执行,计算 等 放到一个函数进行整合 还没细化 7、支持mork 通过伪码,分解为不同 阶段 的json语句执行

测试点: 1、测试 一个json多条语句,后置函数啥时候执行 2、操作其他数据源, 事物是json执行完才会提交, 需要保证一致性 3、redis、elasticSearch、javascript、lua等功能测试

TommyLemon commented 1 year ago

@TommyLemon 你看看是否调整一下: 1、子类实现invoke , 不关心function函数相关校验逻辑, 建议把校验逻辑 放到AbstractFunctionParser 入口 https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L181-L254

image image 2、子类使用 ScriptEngine,或者自己实现脚本解析执行逻辑 比如 lua有两种实现方式: ScriptEngine调用方式 ScriptEngineManager manager = new ScriptEngineManager(); engineLua = manager.getEngineByName("luaj"); Globals调用方式 Globals globals = JsePlatform.standardGlobals(); 3、java lua需要依赖jar, luajc编译(bcel):

<dependency>
  <groupId>org.luaj</groupId>
  <artifactId>luaj-jse</artifactId>
  <version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.bcel/bcel -->
<dependency>
  <groupId>org.apache.bcel</groupId>
  <artifactId>bcel</artifactId>
  <version>6.5.0</version>
</dependency>

4、lua 建议 插件方式整合 apijson

这个是故意的,强制后端在 Function 表配置定义和说明,启动时强制校验: 1.避免前端等接口调用方看不到对应的文档,不方便使用; 2.避免后端通过微信、QQ、企业微信等直接发消息或者 Doc 文档等各种不友好的方式给到前端文档,可能有错误,更新不及时等

写插件, 就需要嵌入一大堆前置判断逻辑, 如果升级apijson, 这段代码也需要升级

应该不用,目前 Java,js 的远程函数都没有。 如果有通用的前置逻辑,例如加载表配置,可以参考 apijson-router,写一个 APIJSONApplication 的子类 https://github.com/APIJSON/apijson-router/blob/main/src/main/java/apijson/router/APIJSONRouterApplication.java

然后业务项目的 Application.main 中调用 https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/boot/DemoApplication.java#L90

image

当然也可能我误解了你说的这个“前置判断逻辑”,可以具体举个例子

TommyLemon commented 1 year ago

@TommyLemon Access、Request、Function 可以支持nacos、etcd等配置源, 通过监听增量加载

这个可以考虑通过新增插件来支持

TommyLemon commented 1 year ago

@TommyLemon 支持多种插件 POST、PUT、DELETE等, 数据存放在 map, 用这个会存在大量的弱类型,存取数据的时候都要进行强转,颇为不方便。 业务侧实现Bean ?

这个是为了零代码的妥协之处,如果用 JavaBean,那就必须写代码了。 目前 cacheMap 中存的对象类型为 JSONObject,支持 getIntValue, getLong, getObject 等各种方便的方法,不过如果类型不匹配确实会强转。 如果某个领域对象有比较多的业务逻辑,可以用户自己写 JavaBean,例如 User, Order 等。

cloudAndMonkey commented 1 year ago

@TommyLemon 支持多种插件 POST、PUT、DELETE等, 数据存放在 map, 用这个会存在大量的弱类型,存取数据的时候都要进行强转,颇为不方便。 业务侧实现Bean ?

这个是为了零代码的妥协之处,如果用 JavaBean,那就必须写代码了。 目前 cacheMap 中存的对象类型为 JSONObject,支持 getIntValue, getLong, getObject 等各种方便的方法,不过如果类型不匹配确实会强转。 如果某个领域对象有比较多的业务逻辑,可以用户自己写 JavaBean,例如 User, Order 等。 @TommyLemon 我昨天看了源码 在于如何生成POST、PUT、DELETE SQLConfig对象. 目前 存放 Column、getContent、getWhere. 业务侧 生成 JavaBean map存储Object 实现类 executeUpdate、executeQuery 方法将Object强转回来即可, 目前还没时间测, 等我空了研究一下 这样, 不同插件(jdbc、非jdbc) 可以按照自己的方式组装数据即可 例如:


Map<String, Object> metaDataMap = new HashMap<String, Object>();
public <T> T getResponseData(String key){
    return (T) metaDataMap.get(key);
}

@Test
public void valueConvert() {
    metaDataMap.put("int", 1);
    metaDataMap.put("string", "a");

    int i = getResponseData("int");
    log.info("{}",i);
}
cloudAndMonkey commented 1 year ago

@TommyLemon LiteFlow实现业务逻辑流程编排 我的想法,能实现,哈哈😊 chain、node都可以通过动态脚步、java等实现 image

cloudAndMonkey commented 1 year ago

我这周就把这个流程打通😁

cloudAndMonkey commented 1 year ago

@TommyLemon 你看看是否调整一下: 1、子类实现invoke , 不关心function函数相关校验逻辑, 建议把校验逻辑 放到AbstractFunctionParser 入口 https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L181-L254

image image 2、子类使用 ScriptEngine,或者自己实现脚本解析执行逻辑 比如 lua有两种实现方式: ScriptEngine调用方式 ScriptEngineManager manager = new ScriptEngineManager(); engineLua = manager.getEngineByName("luaj"); Globals调用方式 Globals globals = JsePlatform.standardGlobals(); 3、java lua需要依赖jar, luajc编译(bcel):

<dependency>
    <groupId>org.luaj</groupId>
    <artifactId>luaj-jse</artifactId>
    <version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.bcel/bcel -->
<dependency>
    <groupId>org.apache.bcel</groupId>
    <artifactId>bcel</artifactId>
    <version>6.5.0</version>
</dependency>

4、lua 建议 插件方式整合 apijson

这个是故意的,强制后端在 Function 表配置定义和说明,启动时强制校验: 1.避免前端等接口调用方看不到对应的文档,不方便使用; 2.避免后端通过微信、QQ、企业微信等直接发消息或者 Doc 文档等各种不友好的方式给到前端文档,可能有错误,更新不及时等

写插件, 就需要嵌入一大堆前置判断逻辑, 如果升级apijson, 这段代码也需要升级

应该不用,目前 Java,js 的远程函数都没有。 如果有通用的前置逻辑,例如加载表配置,可以参考 apijson-router,写一个 APIJSONApplication 的子类 https://github.com/APIJSON/apijson-router/blob/main/src/main/java/apijson/router/APIJSONRouterApplication.java

然后业务项目的 Application.main 中调用 https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/boot/DemoApplication.java#L90 image

当然也可能我误解了你说的这个“前置判断逻辑”,可以具体举个例子

这块先不急,等我打通, 整理好

cloudAndMonkey commented 1 year ago

@TommyLemon Access、Request、Function 可以支持nacos、etcd等配置源, 通过监听增量加载

这个可以考虑通过新增插件来支持

等我空了,我来加, 这个还是需要的

cloudAndMonkey commented 1 year ago

@TommyLemon apijson整合lua通了. package.path="/xxxx/?.lua" -- luaj只能引用lua文件, so文件无法引用 local dkjson = require("dkjson") function testLuaArray(strArr) local curObj = strArr[0] local arr = strArr[1] local index = strArr[2] print("发过来的消息:"..curObj..",".. arr ..",".. index ) print("curObj:"..curObj) local obj,pos,err = dkjson.decode(curObj , 1, nil)--解析json字符串 -- print("retObj:"..retObj) if err then print ("Error:", err) else local indexNum = tonumber(obj[index]) print ("obj:", obj) print ("indexNum:", indexNum) print ("obj[arr]:", obj[arr]) print ("obj[arr][indexNum]:", obj[arr][indexNum]) for i = 1,#obj[arr] do print (i, obj[arr][i]) end return obj[arr][indexNum] end return obj[arr][indexNum] end image image

这个 Java 调 Lua 脚本的库不支持传对象吗?我看以上代码是传字符串过去然后反序列化 @TommyLemon // 带参函数,table对象 String script1 = "function test(str)\n"

  • "print('str:'..str.bb)" +" for i = 1,#str.call do\n"
  • " print (i, str.call[i])\n"
  • " end\n"
  • "return '发过来的消息:'..type(str.call)\n"
  • "end";

// 测试table传参 globals = LuaJUtils.loadScriptFromScript(script1); LuaTable binding = LuaTable.tableOf(); LuaValue[] luaTable = new LuaValue[5]; luaTable[0] = LuaValue.valueOf(2); luaTable[1] = LuaValue.valueOf("dd"); luaTable[2] = LuaValue.valueOf(3); luaTable[3] = LuaValue.valueOf(4); luaTable[4] = LuaValue.valueOf(5); binding.set("call", LuaTable.listOf(luaTable)); binding.set("bb", "bb"); LuaValue luaValue = LuaJUtils.callFunction(globals, "test", binding); String s = LuaJUtils.fromResult(luaValue, String.class); log.info(s); binding.set("bb", "bb"); LuaValue luaValue = LuaJUtils.callFunction(globals, "test", binding); String s = LuaJUtils.fromResult(luaValue, String.class); log.info(s); image 还有一种方式: 字符串转换为 table -- 调用loadstring得到一个Table -- Lua5.2之后loadstring方法弃用了,得用load local res = load(data)() 这种json字符串格式也有要求 https://blog.csdn.net/aaaadong/article/details/124434622 https://blog.csdn.net/u013577276/article/details/77480456

这样啊,优先选对新版 JDK 及 Lua 兼容更好的方式

@TommyLemon 全局变量方式传参 javascript、lua都支持: javascript: String script = "function process(){"

lua: String script = "function test()\n"

也可以参考 liteflow脚本实现: https://github.com/dromara/liteflow/blob/master/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java

https://github.com/dromara/liteflow/blob/master/liteflow-script-plugin/liteflow-script-javascript/src/main/java/com/yomahub/liteflow/script/javascript/JavaScriptExecutor.java

https://github.com/dromara/liteflow/blob/master/liteflow-script-plugin/liteflow-script-lua/src/main/java/com/yomahub/liteflow/script/lua/LuaScriptExecutor.java

TommyLemon commented 1 year ago

赞,这样的话可以把 RequestMethod method, String tag, int version, @NotNull JSONObject request, HttpSession session 等参数作为全局参数传进去供脚本使用

cloudAndMonkey commented 1 year ago

@TommyLemon 记录一下, 等我空了, 我把这个插件补上 Javascript脚本,有两种引擎可以选择,一种是基于jdk的js引擎实现,只支持ES5。 另一种是基于GraalJs引擎实现,支持ES6。 官方建议,如果是简单的逻辑,采用jdk引擎,复杂的需要用ES6特性,就用GraalJs引擎。 jdk引擎并不支持java 17,而GraalJs引擎是能支持到java 17的。 https://blog.csdn.net/csdn_meng/article/details/125280812

TommyLemon commented 1 year ago

默认用内置的,用户有需求就自己依赖和使用 GraalJs 等第三方引擎。

cloudAndMonkey commented 1 year ago

image @TommyLemon 这个功能做了吗? 没有我来做吧

TommyLemon commented 1 year ago

@cloudAndMonkey 还没做,感谢~ 可以先把其它代码提交哈,这次你贡献了这么多功能,我可以发个 6.0 版本了,从 5.4.0 多级连跳哈哈

cloudAndMonkey commented 1 year ago

@cloudAndMonkey 还没做,感谢~ 可以先把其它代码提交哈,这次你贡献了这么多功能,我可以发个 6.0 版本了,从 5.4.0 多级连跳哈哈

好的, 这周我找个时间提交一下

cloudAndMonkey commented 1 year ago

image @TommyLemon 这个功能做了吗? 没有我来做吧 @TommyLemon 修改: image 删除: {

"User_address:del": {
    "id{}": [
        "2f954611-f218-4fa8-8716-251d21e68c58"
    ]
},
"tag": "User_address",
"explain": true

} image 查询: { "User_address:a": { }, "@explain": true }

image

combine查询 { "User_address[]": { "User_address": { "user_id!~": "3123f016-a4cc-455c-aac5-264c1230dcb4", "count~": "5", "@combine": "user_id!~ & count~" }, "page": 0, "count": 10 }, "@explain": true } image

子查询 { "sql@": { "with": true, "from": "Sys_user_role", "Sys_user_role": { "@column": "role_id", "user_id": "4cb82d79-c1f3-49e8-a97d-df6fe0749d46" } }, "Sys_role_permission[]": { "Sys_role_permission": { "role_id{}@": "sql" }, "page": 0, "count": 10 }, "@explain": true }

image 分组查询 { "[]": { "User_address":{ "user_id{}": ["3123f016-a4cc-455c-aac5-264c1230dcb4","53f73b01-90e6-4d08-87e8-72d58bd7941c"], "@column":"user_id;count(id)", "@group":"user_id" }, "page": 0, "count": 2, "query": 2 }, "total@": "/[]/total", "@explain": true } image

关联查询 { "format": true, "[]": { "join":"&/User_address/user_id@", "User": { "@column": "id,username" }, "User_address": { "user_id@": "/User/id", "@column": "id:id1,addr", "user_id!~": "3123f016-a4cc-455c-aac5-264c1230dcb4", "count~": "5", "@combine": "user_id!~ & count~" } }, "@explain": true } image

实现思路和一些问题点: 1、不是所有表都需要支持假删除 Access 表新增字段 deletedKey, deletedValue, notDeletedValue image 如果配置, 应用 假删除 method = delete 可以调用 SQLConfig.onFakeDelete(Map<String, Object> map) 来新增条件 比如:加一个删除时间 deletedTime 之类的字段( DELETE 改为 PUT ,deletedTime 作为更新字段) 2、@MethodAccess image 测试, 注解支持 假删除

TommyLemon commented 1 year ago

notDeletedValue 目前看起来没必要, deletedKey != deletedValue 就包含了 deletedKey = notDeletedValue 的情况。 或者用 validKey = validValue 表示数据有效(未删除),validKey != validValue 表示无效(已删除), 因为 deletedTime 是不固定的,不能用一个值表示已删除,所以最好用一个值(NULL, 0, '' 等其中一个)表示未删除,其它表示已删除更好

cloudAndMonkey commented 1 year ago

notDeletedValue 目前看起来没必要, deletedKey != deletedValue 就包含了 deletedKey = notDeletedValue 的情况。 或者用 validKey = validValue 表示数据有效(未删除),validKey != validValue 表示无效(已删除), 因为 deletedTime 是不固定的,不能用一个值表示已删除,所以最好用一个值(NULL, 0, '' 等其中一个)表示未删除,其它表示已删除更好

@TommyLemon update、delete where 条件 不支持 不等于 { "User_address": { "user_id!~": "3123f016-a4cc-455c-aac5-264c1230dcb4", "count+": 1 }, "tag": "User_address", "@explain": true } image

查询支持 { "User_address:a": { "user_id!~": "3123f016-a4cc-455c-aac5-264c1230dcb4" }, "@explain": true } image

源码 查询做了处理: https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5490-L5510 image

TommyLemon commented 1 year ago

@cloudAndMonkey 现在可以取消 isQueryMethod 这个限制了。 另外 "key!": value 就是 key != value 如果是 "key!~": value 那就是正则 key NOT REGEXP value

378BF271-E8C9-4FBB-A06D-EA63DB3B33E7

https://github.com/Tencent/APIJSON/blob/master/Document.md#3.2

cloudAndMonkey commented 1 year ago

@cloudAndMonkey 现在可以取消 isQueryMethod 这个限制了。 另外 "key!": value 就是 key != value 如果是 "key!~": value 那就是正则 key NOT REGEXP value

378BF271-E8C9-4FBB-A06D-EA63DB3B33E7

https://github.com/Tencent/APIJSON/blob/master/Document.md#3.2 @TommyLemon 取消 isQueryMethod 这个限制 PUT 条件修改, 非和正则 都能正常执行: { "User_address": { "user_id!": "3123f016-a4cc-455c-aac5-264c1230dcb4", "count+": 1 }, "tag": "User_address", "@explain": true } "sql": "UPDATE housekeeping.User_address SET count = count + 1, addr = 'null_1672896387696' WHERE ( (deleted = '0') AND (user_id != '3123f016-a4cc-455c-aac5-264c1230dcb') ) "

请问PUT 条件修改 json 如何实现 where a = "" 呢? 区分不了呀,哈哈😁 image update 是把key作为 update set 字段执行

TommyLemon commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine https://github.com/Tencent/APIJSON/issues/277

cloudAndMonkey commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@explain": true }

"msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!"

这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

cloudAndMonkey commented 1 year ago

@MethodAccess @TommyLemon 注解方式需要实现 假删除吗? image // 由 init 方法读取数据库 Access 表来替代手动输入配置 // // <TableName, <METHOD, allowRoles>> // // <User, <GET, [OWNER, ADMIN]>> // static { //注册权限 // ACCESS_MAP.put(User.class.getSimpleName(), getAccessMap(User.class.getAnnotation(MethodAccess.class))); // ACCESS_MAP.put(Privacy.class.getSimpleName(), getAccessMap(Privacy.class.getAnnotation(MethodAccess.class))); // ACCESS_MAP.put(Moment.class.getSimpleName(), getAccessMap(Moment.class.getAnnotation(MethodAccess.class))); // ACCESS_MAP.put(Comment.class.getSimpleName(), getAccessMap(Comment.class.getAnnotation(MethodAccess.class))); // ACCESS_MAP.put(Verify.class.getSimpleName(), getAccessMap(Verify.class.getAnnotation(MethodAccess.class))); // ACCESS_MAP.put(Login.class.getSimpleName(), getAccessMap(Login.class.getAnnotation(MethodAccess.class))); // }

TommyLemon commented 1 year ago

这个不方便用注解实现,直接用一个 Map 存假删除配置就行,虽然字段在一张表,但代码中配置可以分离

cloudAndMonkey commented 1 year ago

这个不方便用注解实现,直接用一个 Map 存假删除配置就行,虽然字段在一张表,但代码中配置可以分离

哈哈,已经弄好了 image

TommyLemon commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true }

"msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!"

这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。

有两个 getWhereString 方法都需要多传一个参数 content,然后有个再传给 parseCombineExpression https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2677-L2679

image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714

image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2798

image
cloudAndMonkey commented 1 year ago

@TommyLemon 是的,那我来调整一下

cloudAndMonkey commented 1 year ago

@TommyLemon 假删除 功能已经开发完,我再测一下 delete 扩展 update set 字段,比如deletedTime 我已经搞好了, 业务侧按照 table 去控制即可 image @Override public void onFakeDelete(Map<String, Object> map) { map.put("deletedTime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN)); }

cloudAndMonkey commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true } "msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!" 这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。

需要多传一个参数 content https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714 image @TommyLemon 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。 json数据只能放到 content、where其中之一 我先看看代码逻辑

TommyLemon commented 1 year ago

条件键值对都放在 wehre,PUT(UPDATE) 赋值键值对都放在 content,POST(INSERT) 赋值键值对都放在 values

TommyLemon commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true } "msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!" 这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。

有两个 getWhereString 方法都需要多传一个参数 content,然后有个再传给 parseCombineExpression https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2677-L2679 image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714 image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2798 image

应该只有 "@combine":"a | (b & c)" 这种逻辑表达式才会出现以上 bug,只需要改 parseCombineExpression 相关的。

因为 "@combine":"a,b,&c,!d" 这种简单组合方式已经提前处理过了:

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4951-L4956

image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5013-L5050

image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5072-L5085

image

解析表达式比较复杂,所以这里就没有提前处理了,还是简单的加个 content 参数再加上 if (value == null) { value = content.get(key) } 来解决 bug 更简单方便

TommyLemon commented 1 year ago

加了个用户业务侧自己使用 Redis 缓存的 Demo https://github.com/APIJSON/APIJSON-Demo/commit/060a10e56818b31ab332770815467af9f052c261

cloudAndMonkey commented 1 year ago

@TommyLemon 哈哈,很早以前我加过,差点蒙圈😁 redis好像还有一个问题 更新数据库Access、Request表字段,重启没法自动更新redis。需要调用增量初始化接口

cloudAndMonkey commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true } "msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!" 这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。 有两个 getWhereString 方法都需要多传一个参数 content,然后有个再传给 parseCombineExpression https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2677-L2679 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2798 image

应该只有 "@combine":"a | (b & c)" 这种逻辑表达式才会出现以上 bug,只需要改 parseCombineExpression 相关的。

因为 "@combine":"a,b,&c,!d" 这种简单组合方式已经提前处理过了:

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4951-L4956 image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5013-L5050 image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5072-L5085 image

解析表达式比较复杂,所以这里就没有提前处理了,还是简单的加个 content 参数再加上 if (value == null) { value = content.get(key) } 来解决 bug 更简单方便

@TommyLemon 兼容 PUT @combine https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5073 image

这里需要将key 和 combineExpr 表达式进行比较,或者 将combineExpr表达式进行解析获取字段名,然后进行比较? { "User_address": { "user_id!": "3123f016-a4cc-455c-aac5-264c1230dcb", "count": "10", "count+": 1, "@combine": "user_id! | count" }, "tag": "User_address", "@explain": true } UPDATE housekeeping.User_address SET count = count + 1, addr = 'null_1672908535358' WHERE (deleted != '1') AND ( ( user_id != '3123f016-a4cc-455c-aac5-264c1230dcb' ) OR ( count = '10' ) )

TommyLemon commented 1 year ago

@TommyLemon 哈哈,很早以前我加过,差点蒙圈😁 redis好像还有一个问题 更新数据库Access、Request表字段,重启没法自动更新redis。需要调用增量初始化接口

可以在启动后、init 前 REDIS_TEMPLATE.discard(); 清空缓存,但更好的方式是不缓存 APIJSON 配置表,毕竟数据量非常有限,而且很少更新(尤其是相当于业务表,更新频率低很多)。 https://github.com/APIJSON/APIJSON-Demo/commit/3c1a542fbb647fd1ed93d0acd66a74ddbee0e303

TommyLemon commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true } "msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!" 这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。 有两个 getWhereString 方法都需要多传一个参数 content,然后有个再传给 parseCombineExpression https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2677-L2679 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2798 image

应该只有 "@combine":"a | (b & c)" 这种逻辑表达式才会出现以上 bug,只需要改 parseCombineExpression 相关的。 因为 "@combine":"a,b,&c,!d" 这种简单组合方式已经提前处理过了: https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4951-L4956 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5013-L5050 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5072-L5085 image 解析表达式比较复杂,所以这里就没有提前处理了,还是简单的加个 content 参数再加上 if (value == null) { value = content.get(key) } 来解决 bug 更简单方便

@TommyLemon 兼容 PUT @combine https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5073 image

这里需要将key 和 combineExpr 表达式进行比较,或者 将combineExpr表达式进行解析获取字段名,然后进行比较? { "User_address": { "user_id!": "3123f016-a4cc-455c-aac5-264c1230dcb", "count": "10", "count+": 1, "@combine": "user_id! | count" }, "tag": "User_address", "@Explain": true } UPDATE housekeeping.User_address SET count = count + 1, addr = 'null_1672908535358' WHERE (deleted != '1') AND ( ( user_id != '3123f016-a4cc-455c-aac5-264c1230dcb' ) OR ( count = '10' ) )

直接 combineExpr.contains(key) 会有误判,例如 "admin_user_id | name".contains("user_id"), image

可以按这个逻辑来判断,key 左侧、右侧可能的字符不能和它拼接成一个有效的键名 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4992-L5002

image
cloudAndMonkey commented 1 year ago

@cloudAndMonkey "@combine": "a" 强制指定 "a": "" 为条件, 带功能符号的就直接作为条件,可以不用 @combine #277 @TommyLemon 嗯嗯, 你以前跟我说过 ,带功能符号的就直接作为条件,可以不用 @combine

{ "User_address": { "user_id": "3123f016-a4cc-455c-aac5-264c1230dcb", "count+": 1, "@combine": "user_id" }, "tag": "User_address", "@Explain": true } "msg": "User_address:{ @combine:'user_id' } 中字符 'user_id' 对应的条件键值对 user_id:value 不存在!" 这个和apijson解析顺序有关, 如果要实现"h@combine": "a" 强制指定 "a": "" 为条件,需要调整解析顺序,还要考虑是否有其他影响?

应该是解析到 @combine 时是从 Map<String, Object> where 中取值,但 PUT user_id 是放在 Map<String, Object> content 中, 应该要提前处理 @combine,或者 where 中取不到再从 content 中取。 有两个 getWhereString 方法都需要多传一个参数 content,然后有个再传给 parseCombineExpression https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2677-L2679 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2688-L2714 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2798 image

应该只有 "@combine":"a | (b & c)" 这种逻辑表达式才会出现以上 bug,只需要改 parseCombineExpression 相关的。 因为 "@combine":"a,b,&c,!d" 这种简单组合方式已经提前处理过了: https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4951-L4956 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5013-L5050 image https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5072-L5085 image 解析表达式比较复杂,所以这里就没有提前处理了,还是简单的加个 content 参数再加上 if (value == null) { value = content.get(key) } 来解决 bug 更简单方便

@TommyLemon 兼容 PUT @combine https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L5073 image 这里需要将key 和 combineExpr 表达式进行比较,或者 将combineExpr表达式进行解析获取字段名,然后进行比较? { "User_address": { "user_id!": "3123f016-a4cc-455c-aac5-264c1230dcb", "count": "10", "count+": 1, "@combine": "user_id! | count" }, "tag": "User_address", "@Explain": true } UPDATE housekeeping.User_address SET count = count + 1, addr = 'null_1672908535358' WHERE (deleted != '1') AND ( ( user_id != '3123f016-a4cc-455c-aac5-264c1230dcb' ) OR ( count = '10' ) )

直接 combineExpr.contains(key) 会有误判,例如 "admin_user_id | name".contains("user_id"), image

可以按这个逻辑来判断,key 左侧、右侧可能的字符不能和它拼接成一个有效的键名 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4992-L5002 image

收到,我改一下

cloudAndMonkey commented 1 year ago

@TommyLemon private static boolean keyInCombineExpr(String combineExpr, String key) { // while 解决 key = "count" ,combineExpr = "ab & (user_id! | count! | count )" 的场景 while (combineExpr.isEmpty() == false) { int index = combineExpr.indexOf(key); if (index < 0) { return false; }

        char left = index <= 0 ? ' ' : combineExpr.charAt(index - 1);
        char right = index >= combineExpr.length() - key.length() ? ' ' : combineExpr.charAt(index + key.length());
        if ((left == ' ' || left == '(' || left == '&' || left == '|' || left == '!') && (right == ' ' || right == ')')) {
            return true;
        }
        int newIndex = index + key.length() + 1;
        if (combineExpr.length() <= newIndex) {
            break;
        }
        combineExpr = combineExpr.substring(newIndex);
    }
    return false;
}

我把这个地方改一下,调用一个方法哈 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4992-L5002

cloudAndMonkey commented 1 year ago

搞完了, 累死了 明天来提交代码 redis(api太多了)、redisql (调整了,还没复测),测完我提交

cloudAndMonkey commented 1 year ago

赞,这样的话可以把 RequestMethod method, String tag, int version, @NotNull JSONObject request, HttpSession session 等参数作为全局参数传进去供脚本使用 @TommyLemon 脚本支持传递全局对象参数, 也可以自定义java类,供脚本调用, 这块你改一下哈

TommyLemon commented 1 year ago

可以的

cloudAndMonkey commented 1 year ago

可以的 @TommyLemon 纠结了一下,哈哈 等我忙完,我来加吧,最近整理了资料,我搞也要不了多久 把自定义类传参也加上去,抽象一个方法,业务侧通过脚本名去控制

TommyLemon commented 1 year ago

哈哈,感谢~