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等功能测试

cloudAndMonkey commented 1 year ago

@TommyLemon 后端开发 spring项目 提供 http 接口, 返回json数据 apijson通过远程函数调用,组装认证等信息, 调用http 接口 获取数据

TommyLemon commented 1 year ago

这个可行👍

cloudAndMonkey commented 1 year ago

这个可行👍

不好意思,我表述有问题, 给你造成歧义了,哈哈

cloudAndMonkey commented 1 year ago

@TommyLemon apijsondemo-multidatasource-elasticsearch 项目, 帮忙把里面的ip删除一下,忘记删了 DynamicJdbcDataSource image

TommyLemon commented 1 year ago

@cloudAndMonkey 已调整,还有没有别的地方? https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSONDemo-MultiDataSource-Elasticsearch/src/main/java/apijson/demo/DynamicJdbcDataSource.java

image
TommyLemon commented 1 year ago

另外我刚刚这个 Demo 移到了 APIJSON-Java-Server 目录 https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Elasticsearch

cloudAndMonkey commented 1 year ago

@TommyLemon 后端开发 spring项目 提供 http 接口, 返回json数据 apijson通过远程函数调用,组装认证等信息, 调用http 接口 获取数据

@TommyLemon 这种方式,相当于一个独立的jar(编写业务逻辑)在运行,和apijson 项目是独立的 随时更新,维护也没有那么复杂 现实场景: 运营活动组件, 通过流程编排, 流程每一步,调用一个url. 解决运营活动变动大、组件复用问题

cloudAndMonkey commented 1 year ago

crud 能解决多条语句, 但是无法解决 比如: 读取某个表数据(常用场景一): 1、从redis/es 读取, 存在则返回,不存在从数据库读取 2、数据库读取, 存储缓存

cloudAndMonkey commented 1 year ago

@cloudAndMonkey 已调整,还有没有别的地方? https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSONDemo-MultiDataSource-Elasticsearch/src/main/java/apijson/demo/DynamicJdbcDataSource.java image

谢谢,我已经将代码清理提交了.

cloudAndMonkey commented 1 year ago

我今天开始整合 redisql, redis 最新版本的redis 也支持redisql插件 redis sentinel、redis cluster 命令也是支持的, 也能路由

TommyLemon commented 1 year ago

crud 能解决多条语句, 但是无法解决 比如: 读取某个表数据(常用场景一): 1、从redis/es 读取, 存在则返回,不存在从数据库读取 2、数据库读取, 存储缓存

对,不过这两个都可以通过重写 AbstractSQLExecutor 的 putCache, getCache, removeCache 方法来实现, 另外也提供了关键词 @cache 控制缓存,不过这个可能会调整为只允许 DEBUG 模式下由前端传参,其它情况只能后端控制,并且可能增强下功能,增删改后 自动更新 相关缓存。

TommyLemon commented 1 year ago

@cloudAndMonkey 已调整,还有没有别的地方? https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSONDemo-MultiDataSource-Elasticsearch/src/main/java/apijson/demo/DynamicJdbcDataSource.java image

谢谢,我已经将代码清理提交了.

已合并

cloudAndMonkey commented 1 year ago

crud 能解决多条语句, 但是无法解决 比如: 读取某个表数据(常用场景一): 1、从redis/es 读取, 存在则返回,不存在从数据库读取 2、数据库读取, 存储缓存

对,不过这两个都可以通过重写 AbstractSQLExecutor 的 putCache, getCache, removeCache 方法来实现, 另外也提供了关键词 @cache 控制缓存,不过这个可能会调整为只允许 DEBUG 模式下由前端传参,其它情况只能后端控制,并且可能增强下功能,增删改后 自动更新 相关缓存。

@TommyLemon 我当时调试过 cache这块, removecache apijson是没有调用的,并且好像某些method(post、put...)是没有调用缓存的,需要完善

TommyLemon commented 1 year ago

crud 能解决多条语句, 但是无法解决 比如: 读取某个表数据(常用场景一): 1、从redis/es 读取, 存在则返回,不存在从数据库读取 2、数据库读取, 存储缓存

对,不过这两个都可以通过重写 AbstractSQLExecutor 的 putCache, getCache, removeCache 方法来实现, 另外也提供了关键词 @cache 控制缓存,不过这个可能会调整为只允许 DEBUG 模式下由前端传参,其它情况只能后端控制,并且可能增强下功能,增删改后 自动更新 相关缓存。

@TommyLemon 我当时调试过 cache这块, removecache apijson是没有调用的,并且好像某些method(post、put...)是没有调用缓存的,需要完善

对,是这样,目前还没想好在 ORM 或 framework 层怎么自动化实现,所以只是提供接口交给用户自己实现 https://github.com/Tencent/APIJSON/issues/337

cloudAndMonkey commented 1 year ago

crud 能解决多条语句, 但是无法解决 比如: 读取某个表数据(常用场景一): 1、从redis/es 读取, 存在则返回,不存在从数据库读取 2、数据库读取, 存储缓存

对,不过这两个都可以通过重写 AbstractSQLExecutor 的 putCache, getCache, removeCache 方法来实现, 另外也提供了关键词 @cache 控制缓存,不过这个可能会调整为只允许 DEBUG 模式下由前端传参,其它情况只能后端控制,并且可能增强下功能,增删改后 自动更新 相关缓存。

@TommyLemon 我当时调试过 cache这块, removecache apijson是没有调用的,并且好像某些method(post、put...)是没有调用缓存的,需要完善

对,是这样,目前还没想好在 ORM 或 framework 层怎么自动化实现,所以只是提供接口交给用户自己实现 #337 这块要想想 1、elasticsearch一样, apijson api可以直接增删改查 2、cache 要能覆盖所有method 3、cache 关联查询分解(这个已经实现了,我到时候再调一下) 4、外层配置哪些表应用外部缓存 我先研究研究

cloudAndMonkey commented 1 year ago

不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。 image

image

@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息 image 按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验 我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路:

  1. 后端页面配置生成crud模版
  2. 用户页面填写数据
  3. 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定
  4. 调用apijson crud方法

这个可行,有些用 APIJSON 的低代码平台基本就是这个原理。 不过我回答你上面那个问题用 POST register 举例不太合理,因为是单独实现的 /register 接口,不是用万能通用接口。 POST Document, DELETE Moment 这样的就是走万能通用接口,并且涉及多表关联 新增/删除。

好的,谢谢

image 看到你发的这个图,我一下想起来了,我还看过这个逻辑, 就是看了注册和 Moment,我才想搞crud

cloudAndMonkey commented 1 year ago

Java sql(库、表) 操作 jedis 单机、 jedis sentinel、jedis cluster 测完 开始整合apijson sql操作

TommyLemon commented 1 year ago

不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。 image

image

@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息 image 按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验 我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路:

  1. 后端页面配置生成crud模版
  2. 用户页面填写数据
  3. 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定
  4. 调用apijson crud方法

这个可行,有些用 APIJSON 的低代码平台基本就是这个原理。 不过我回答你上面那个问题用 POST register 举例不太合理,因为是单独实现的 /register 接口,不是用万能通用接口。 POST Document, DELETE Moment 这样的就是走万能通用接口,并且涉及多表关联 新增/删除。

好的,谢谢

image 看到你发的这个图,我一下想起来了,我还看过这个逻辑, 就是看了注册和 Moment,我才想搞crud

这样啊

cloudAndMonkey commented 1 year ago

@TommyLemon redis Cluster sql语句执行 新增, 哈哈😄 { "@datasource": "redisCluster", "R_data": { "name": "角色1", "address": "223232" }, "tag": "R_data", "@explain": true }

image

查询结果: 查询数据:9053f5b7-6875-4669-8bdc-0b762c4d5fd8;角色1;223232;4732209c-5785-4827-b532-5092f154fd94

cloudAndMonkey commented 1 year ago

@TommyLemon 非sql方式比如常规数据类型(字符串、hash、hashset、list等) 还没有调 针对 redis sql 数据库表 新增、修改、删除 、查询都通了 { "@datasource": "redisCluster", "R_data:a": { "address$": "address-0%" }, "@explain": true } image

查询的问题是: redis不返回字段名,稍微自己处理一下即可 image

cloudAndMonkey commented 1 year ago

@TommyLemon 解决 jdbc和非jdbc 查询, ResultSet 兼容, 不需要 framework添加新接口 public ResultSet executeQuery(@NotNull SQLConfig config, String sql) throws Exception

自定义 ResultSet image

cloudAndMonkey commented 1 year ago

@TommyLemon redis常规数据类型(String、HashMap, HashSet等)集成,不需要添加接口, 做好apijson 和 jedis等数据源操作api对应起来即可 1、 json方式 ,定义 数据源、数据类型(String、HashMap, HashSet等), 然后解析 2、远程函数, 参数解析 你当时没有加redis, 有哪些顾虑呀?

cloudAndMonkey commented 1 year ago

我先去测远程函数

TommyLemon commented 1 year ago

@TommyLemon redis常规数据类型(String、HashMap, HashSet等)集成,不需要添加接口, 做好apijson 和 jedis等数据源操作api对应起来即可 1、 json方式 ,定义 数据源、数据类型(String、HashMap, HashSet等), 然后解析 2、远程函数, 参数解析 你当时没有加redis, 有哪些顾虑呀?

当时不知道可以通过 SQL 访问 Redis,另外 APIJSON 的 JOIN、子查询等复杂查询等功能还不够完善,那些功能优先级更高

cloudAndMonkey commented 1 year ago

@TommyLemon 请问调用js脚本, 要咋弄呀, 我也测一下

用最新代码,然后 Function 表配置(type=1) 属性、Script 表配置具体脚本就行了,前端不需要改动参数。 image

INSERT INTO `Function` VALUES (3,0,0,0,'countArray','int','array','{\"array\": [1, 2, 3]}','获取数组长度。没写调用键值对,会自动补全 \"result()\": \"countArray(array)\"',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(4,0,0,0,'countObject','int','object','{\"object\": {\"key0\": 1, \"key1\": 2}}','获取对象长度。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(5,0,0,1,'isContain','Boolean','array,value','{\"array\": [1, 2, 3], \"value\": 2}','判断是否数组包含值。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(6,0,0,0,'isContainKey','boolean','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','判断是否对象包含键。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(7,0,0,0,'isContainValue','boolean','object,value','{\"value\": 1, \"object\": {\"id\": 1}}','判断是否对象包含值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(8,0,0,0,'getFromArray','Object','array,position','{\"array\": [1, 2, 3], \"result()\": \"getFromArray(array,1)\"}','根据下标获取数组里的值。position 传数字时直接作为值,而不是从所在对象 request 中取值',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(9,0,0,0,'getFromObject','Object','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','根据键获取对象里的值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(10,0,0,0,'deleteCommentOfMoment','int','momentId','{\"momentId\": 1}','根据动态 id 删除它的所有评论',0,'Moment','DELETE','2019-08-17 18:46:56',NULL),(11,0,0,0,'verifyIdList',NULL,'array','{\"array\": [1, 2, 3], \"result()\": \"verifyIdList(array)\"}','校验类型为 id 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(12,0,0,0,'verifyURLList',NULL,'array','{\"array\": [\"http://123.com/1.jpg\", \"http://123.com/a.png\", \"http://www.abc.com/test.gif\"], \"result()\": \"verifyURLList(array)\"}','校验类型为 URL 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(13,0,0,0,'getWithDefault','Object','value,defaultValue','{\"value\": null, \"defaultValue\": 1}','如果 value 为 null,则返回 defaultValue',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(14,0,0,0,'removeKey','Object','key','{\"key\": \"s\", \"key2\": 2}','从对象里移除 key',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(15,0,0,0,'getFunctionDemo','JSONObject',NULL,'{}','获取远程函数的 Demo',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(16,0,0,0,'getFunctionDetail','String',NULL,'{}','获取远程函数的详情',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(17,0,0,0,'getMethodArguments','String','methodArgs','{\"methodArgs\": \"methodArgs\"}','获取远程函数的参数',0,NULL,NULL,'2021-07-29 09:32:22',NULL),(18,0,0,0,'getMethodDefination','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL),(19,0,0,0,'getMethodRequest','String',NULL,'{}','获取远程函数的请求',0,NULL,NULL,'2021-07-29 09:35:37',NULL),(20,0,0,0,'deleteChildComment','int','commentId','{}','删除评论的子评论',0,NULL,NULL,'2021-09-10 06:53:24',NULL),(21,0,0,0,'getCurrentUserId','Long',NULL,'{}','获取当前登录用户 id',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(22,0,0,0,'getCurrentUserIdAsList','List',NULL,'{}','获取当前登录用户 id 列表,只包含一个 id,只是为了前端方便构造某些请求',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(24,0,0,0,'getCurrentUser','Visitor',NULL,'{}','获取当前登录用户公开信息',0,NULL,NULL,'2022-02-19 17:34:58',NULL),(26,0,0,0,'getCurrentContactIdList','List',NULL,'{}','获取当前登录用户的联系人 id 列表',0,NULL,NULL,'2022-02-19 17:37:35',NULL),(27,0,0,1,'getType','String','val','{\"val\": 1}','获取类型',0,NULL,NULL,'2022-11-16 16:05:41',NULL),(28,0,0,1,'length','Integer','val','{\"val\": [1, 2, 3]}','获取长度',0,NULL,NULL,'2022-11-16 17:21:53',NULL),(29,0,0,0,'getMethodDefinition','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL);
image
INSERT INTO `Script` VALUES (1,0,0,0,'getType','function getType(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val instanceof Array ? \"array\" : typeof val;\n}','2022-11-16 16:01:23',0),(2,0,0,0,'isContain','function isContain(curObj, arrKey, valKey) {\n    var arr = curObj == null ? null : curObj[arrKey];\n    var val = curObj == null ? null : curObj[valKey];\n    return arr != null && arr.indexOf(val) >=0;\n}','2022-11-16 16:02:48',0),(3,0,0,1,'init','var i = 1;\n\"init done \"  + i;','2022-11-16 16:41:35',0),(4,0,0,0,'length','function length(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val == null ? 0 : val.length;\n}','2022-11-16 17:18:43',0);

远程函数:支持 JavaScript 外的更多脚本语言,例如 Python, Ruby, Lua, PHP 等 dd37279 image image

DROP TABLE IF EXISTS `Function`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `Function` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `debug` tinyint NOT NULL DEFAULT '0' COMMENT '是否为 DEBUG 调试数据,只允许在开发环境使用,测试和线上环境禁用:0-否,1-是。',
  `userId` bigint NOT NULL COMMENT '管理员用户Id',
  `language` varchar(45) DEFAULT NULL COMMENT '语言:Java(java), JavaScript(js), Lua(lua), Python(py), Ruby(ruby), PHP(php) 等,NULL 默认为 Java,JDK 1.6-11 默认支持 JavaScript,JDK 12+ 需要额外依赖 Nashron/Rhiro 等 js 引擎库,其它的语言需要依赖对应的引擎库,并在 ScriptEngineManager 中注册',
  `name` varchar(50) NOT NULL COMMENT '方法名',
  `returnType` varchar(50) DEFAULT 'Object' COMMENT '返回值类型。TODO RemoteFunction 校验 type 和 back',
  `arguments` varchar(100) DEFAULT NULL COMMENT '参数列表,每个参数的类型都是 String。\n用 , 分割的字符串 比 [JSONArray] 更好,例如 array,item ,更直观,还方便拼接函数。',
  `demo` json NOT NULL COMMENT '可用的示例。\nTODO 改成 call,和返回值示例 back 对应。',
  `detail` varchar(1000) NOT NULL COMMENT '详细描述',
  `version` tinyint NOT NULL DEFAULT '0' COMMENT '允许的最低版本号,只限于GET,HEAD外的操作方法。\nTODO 使用 requestIdList 替代 version,tag,methods',
  `tag` varchar(20) DEFAULT NULL COMMENT '允许的标签.\nnull - 允许全部\nTODO 使用 requestIdList 替代 version,tag,methods',
  `methods` varchar(50) DEFAULT NULL COMMENT '允许的操作方法。\nnull - 允许全部\nTODO 使用 requestIdList 替代 version,tag,methods',
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `return` varchar(45) DEFAULT NULL COMMENT '返回值示例',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb3 COMMENT='远程函数。强制在启动时校验所有demo是否能正常运行通过';
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `Function` VALUES (3,0,0,0,'countArray','int','array','{\"array\": [1, 2, 3]}','获取数组长度。没写调用键值对,会自动补全 \"result()\": \"countArray(array)\"',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(4,0,0,0,'countObject','int','object','{\"object\": {\"key0\": 1, \"key1\": 2}}','获取对象长度。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(5,0,0,1,'isContain','Boolean','array,value','{\"array\": [1, 2, 3], \"value\": 2}','判断是否数组包含值。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(6,0,0,0,'isContainKey','boolean','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','判断是否对象包含键。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(7,0,0,0,'isContainValue','boolean','object,value','{\"value\": 1, \"object\": {\"id\": 1}}','判断是否对象包含值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(8,0,0,0,'getFromArray','Object','array,position','{\"array\": [1, 2, 3], \"result()\": \"getFromArray(array,1)\"}','根据下标获取数组里的值。position 传数字时直接作为值,而不是从所在对象 request 中取值',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(9,0,0,0,'getFromObject','Object','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','根据键获取对象里的值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(10,0,0,0,'deleteCommentOfMoment','int','momentId','{\"momentId\": 1}','根据动态 id 删除它的所有评论',0,'Moment','DELETE','2019-08-17 18:46:56',NULL),(11,0,0,0,'verifyIdList',NULL,'array','{\"array\": [1, 2, 3], \"result()\": \"verifyIdList(array)\"}','校验类型为 id 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(12,0,0,0,'verifyURLList',NULL,'array','{\"array\": [\"http://123.com/1.jpg\", \"http://123.com/a.png\", \"http://www.abc.com/test.gif\"], \"result()\": \"verifyURLList(array)\"}','校验类型为 URL 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(13,0,0,0,'getWithDefault','Object','value,defaultValue','{\"value\": null, \"defaultValue\": 1}','如果 value 为 null,则返回 defaultValue',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(14,0,0,0,'removeKey','Object','key','{\"key\": \"s\", \"key2\": 2}','从对象里移除 key',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(15,0,0,0,'getFunctionDemo','JSONObject',NULL,'{}','获取远程函数的 Demo',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(16,0,0,0,'getFunctionDetail','String',NULL,'{}','获取远程函数的详情',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(17,0,0,0,'getMethodArguments','String','methodArgs','{\"methodArgs\": \"methodArgs\"}','获取远程函数的参数',0,NULL,NULL,'2021-07-29 09:32:22',NULL),(18,0,0,0,'getMethodDefination','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL),(19,0,0,0,'getMethodRequest','String',NULL,'{}','获取远程函数的请求',0,NULL,NULL,'2021-07-29 09:35:37',NULL),(20,0,0,0,'deleteChildComment','int','commentId','{}','删除评论的子评论',0,NULL,NULL,'2021-09-10 06:53:24',NULL),(21,0,0,0,'getCurrentUserId','Long',NULL,'{}','获取当前登录用户 id',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(22,0,0,0,'getCurrentUserIdAsList','List',NULL,'{}','获取当前登录用户 id 列表,只包含一个 id,只是为了前端方便构造某些请求',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(24,0,0,0,'getCurrentUser','Visitor',NULL,'{}','获取当前登录用户公开信息',0,NULL,NULL,'2022-02-19 17:34:58',NULL),(26,0,0,0,'getCurrentContactIdList','List',NULL,'{}','获取当前登录用户的联系人 id 列表',0,NULL,NULL,'2022-02-19 17:37:35',NULL),(27,0,0,1,'getType','String','val','{\"val\": 1}','获取类型',0,NULL,NULL,'2022-11-16 16:05:41',NULL),(28,0,0,1,'length','Integer','val','{\"val\": [1, 2, 3]}','获取长度',0,NULL,NULL,'2022-11-16 17:21:53',NULL),(29,0,0,0,'getMethodDefinition','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL); 

js 的 Array 是用 Object 实现的,key 为下标 index,用 typeof arr 得到的不是 array 而是 object 😂

image

目前对于脚本格式的远程函数,实现有个 NPE 问题: Invocable invocable = engine instanceof Invocable ? (Invocable) engine : null; 为 null 时执行 invocable.invokeFunction 会抛 NullPointerException,得想想不是什么情况下 engine 不属于 Invocable,如果不属于,以什么方式来替代 Invocable.invokeFunction 方法,能够执行脚本里的 方法/函数 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L360-L364

dd37279#commitcomment-92319801

image

@TommyLemon js 的 Array 是用 Object 实现的,key 为下标 index,用 typeof arr 得到的不是 array 而是 object 😂 是传参导致的 最新代码 远程函数: function getType(curObj, key) { var val = curObj[key]; return val; } json格式: { "arr":[ 1, 2 ], "index": 1, "type()": "getType(arr,index)" } 单元测试: String script = "function getType(curObj, key) {\n"

Object[] args = new Object[2]; args[0] = new Object[] { 1, 2, "dd", 3, 4 }; args[1] = 2; Object result = invocable.invokeFunction("getType", args); System.out.println(result);

List argsList = new ArrayList(); argsList.add(new Object[] { 1, 2, "dd", 3, 4 }); argsList.add(1); result = invocable.invokeFunction("getType", args); System.out.println(result); 正确执行结果: dd 测试数据类型: image number number

apijson 生成args参数: [{"arr":[1,2],"index":1,"type()":"getType(arr,index)"}, arr, index] result = invocable.invokeFunction(methodName, args);

请问如上参数格式,是基于什么考虑?

TommyLemon commented 1 year ago

远程函数:

function getType(curObj, key) {
var val = curObj[key];
return val;
}

这个不是 Script.sql 里的 demo,demo 应该是

function getType(curObj, key) {
     var val = curObj == null ? null : curObj[key];
     return typeof val; 
}

后面改成了

function getType(curObj, key) {
     var val = curObj == null ? null : curObj[key];
     return val instanceof Array ? "array" : typeof val; 
}

这样就正常了。 确实是 js 的问题,可以用 Chrome 控制台输入

typeof []

看到打印的是 'object' 而不是 'array'

image

https://stackoverflow.com/questions/12996871/why-does-typeof-array-with-objects-return-object-and-not-array

以上格式是对应 Invocable.invokeFunction(String functionName) 的要求和 APIJSON 远程函数的要求,类似用 Java 反射方法 Method.invoke 执行 APIJSON 远程函数,APIJSON 远程函数格式都是

ReturnType fun(JSONObject curObj, String key0, String key1...) {
   ...
}
cloudAndMonkey commented 1 year ago

远程函数:

function getType(curObj, key) {
var val = curObj[key];
return val;
}

这个不是 Script.sql 里的 demo,demo 应该是

function getType(curObj, key) {
     var val = curObj == null ? null : curObj[key];
     return typeof val; 
}

后面改成了

function getType(curObj, key) {
     var val = curObj == null ? null : curObj[key];
     return val instanceof Array ? "array" : typeof val; 
}

这样就正常了。 确实是 js 的问题,可以用 Chrome 控制台输入

typeof []

看到打印的是 'object' 而不是 'array' image

https://stackoverflow.com/questions/12996871/why-does-typeof-array-with-objects-return-object-and-not-array

以上格式是对应 Invocable.invokeFunction(String functionName) 的要求和 APIJSON 远程函数的要求,类似用 Java 反射方法 Method.invoke 执行 APIJSON 远程函数,APIJSON 远程函数格式都是

ReturnType fun(JSONObject curObj, String key0, String key1...) {
   ...
}

@TommyLemon 在 JavaScript 中,数组可以是任意类型元素组成的集合。这意味着,创建一个数组,它的元素类型可以是 String、Boolean、Number、Object,甚至是另一个数组

apijson function 字段 returnType 不好定义呀 比如: 返回数组某一个下标, 可能是number, 也可能是String

TommyLemon commented 1 year ago

Java 用 Object,js 不需要定义 returnType,Function 表字段 returnType 配置 NULL 即可

cloudAndMonkey commented 1 year ago

Java 用 Object,js 不需要定义 returnType,Function 表字段 returnType 配置 NULL 即可

@TommyLemon https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L412-L436 debug模式, js还是会判断返回值类型呀

TommyLemon commented 1 year ago

Class.isAssignableFrom 支持判断继承的子类,例如 对 Objec 的所有子类,包括 Number, String 都返回 true。 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L431-L435

image

另外一个判断方式是 Class.isInstance

cloudAndMonkey commented 1 year ago

@TommyLemon javascript: function getType(curObj, key) { var val = curObj[key]; return val; } json: { "arr":[ 1, 2, 3 ], "type()": "getType(arr,0)" } 配置AbstractFunctionParser.IS_PARSE_ARG_VALUE = true; 可以正常执行,返回. AbstractFunctionParser.IS_PARSE_ARG_VALUE = false; https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L536-L547 生成json: [{"arr":[1,2],"index":1,"type()":"getType(arr,index)"}, arr, index] 无法按照预期返回某一个下标的数组元素

请问是我使用方式不对吗?

TommyLemon commented 1 year ago

AbstractFunctionParser.IS_PARSE_ARG_VALUE = true 会把值提取出来,传给远程函数的参数就不是 String key 了,而是 Object value。 脚本代码改为

function getType(curObj, val) {
    return val instanceof Array ? 'array' : typeof val;
}

如果是 Java 方法,那就是

String getType(JSONObject curObj, JSONArray arr, Number index) {
        Object val = arr == null || index == null || index >= arr.size() ? null : arr.get(index.intValue());
    return val == null ? null : val.getClass().getSimpleName();
}

注:arr 类型为 JSONArray,所以参数类型也必须为 JSONArray。index: 1 经过 getArgValue 处理,会返回 Double 值,但又经过parseFunction 处理变成了 Number.class:

                else if (v instanceof Number) {
                    types[i] = Number.class;
                }

https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L501-L515

image

https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L625-L635

image

当然也可以再优化下,对于整数返回 Integer 或 Long,不过这会使得情况复杂不少,因为 Integer.MIN_VALUE ~ Integer.MAX_VALUE 中的所有数既可能是 Integer 也可能是 Long,为了避免误判导致的 MethodNotFoundException 等问题,得写带不同类型参数的方法

String getType(JSONObject curObj, JSONArray arr, Integer index) {
        Object val = arr == null || index == null || index >= arr.size() ? null : arr.get(index);
    return val == null ? null : val.getClass().getSimpleName();
}
String getType(JSONObject curObj, JSONArray arr, Long index) {
    return getType(curObj, arr, index == null ? null : index.intValue());
}
cloudAndMonkey commented 1 year ago

@TommyLemon 休息两天, 把 function解析流程忘完了,哈哈😄 搞定啦 和 java 远程函数 一样, 配置参数名, 函数内部 从requestObject 获取对应参数的值,进行解析即可 json: { "arr1":[ 1, 2, "dd", 3 ], "index()": "testArray(arr1,2)" }

js function: function testArray(curObj,arr, index) { print("curObj:"+curObj) print("arr:"+arr) print("curObj[arr][index]:"+curObj[arr][index]) print("index:"+index) return curObj[arr][index]; } image

function returnType 配置为 Object (我的问题) 上面我询问返回类型, 是因为忘记流程, 导致js 返回jsonArray 和返回类型不匹配 image

cloudAndMonkey commented 1 year ago

AbstractFunctionParser.IS_PARSE_ARG_VALUE = true 会把值提取出来,传给远程函数的参数就不是 String key 了,而是 Object value。 脚本代码改为

function getType(curObj, val) {
  return val instanceof Array ? 'array' : typeof val;
}

如果是 Java 方法,那就是

String getType(JSONObject curObj, JSONArray arr, Number index) {
        Object val = arr == null || index == null || index >= arr.size() ? null : arr.get(index.intValue());
  return val == null ? null : val.getClass().getSimpleName();
}

注:arr 类型为 JSONArray,所以参数类型也必须为 JSONArray。index: 1 经过 getArgValue 处理,会返回 Double 值,但又经过parseFunction 处理变成了 Number.class:

              else if (v instanceof Number) {
                  types[i] = Number.class;
              }

https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L501-L515

image

https://github.com/Tencent/APIJSON/blob/0d01008d9cb6059b69ef7e10f7149ea5fb6aa127/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L625-L635

image

当然也可以再优化下,对于整数返回 Integer 或 Long,不过这会使得情况复杂不少,因为 Integer.MIN_VALUE ~ Integer.MAX_VALUE 中的所有数既可能是 Integer 也可能是 Long,为了避免误判导致的 MethodNotFoundException 等问题,得写带不同类型参数的方法

String getType(JSONObject curObj, JSONArray arr, Integer index) {
        Object val = arr == null || index == null || index >= arr.size() ? null : arr.get(index);
  return val == null ? null : val.getClass().getSimpleName();
}
String getType(JSONObject curObj, JSONArray arr, Long index) {
  return getType(curObj, arr, index == null ? null : index.intValue());
}

明白

cloudAndMonkey commented 1 year ago

请问 远程函数, 除了js, 其他方式有哪些支持的? @TommyLemon lua 基本调通了 支持 eval、function、Java创建自定义Lua库、dkjson json对象转换等等 明天再和apijson整合

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

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

cloudAndMonkey commented 1 year ago

等我明天完善一下, 我把redisql、lua demo提交一下

cloudAndMonkey commented 1 year ago

@TommyLemon mq消息队列, 我先实现json 插件方式吧 kafka: topicName = tablename 单条:

{
    "@datasource": "kafka",
    "Topic_User":{
        "message":"test-100"
    },
    "tag": "Topic_User",
    "@explain": false
}

image 批量:

{
    "Topic_User[]": [
        {
           "message":"test-100"
        },
        {
            "message":"test-101"
        }
    ],
    "tag": "Topic_User[]",
    "@datasource": "kafka",
    "@explain": true
}

image

业务侧: 后续按照自己的业务需求,去配置组装消息格式即可 和流程编排贴合起来

cloudAndMonkey commented 1 year ago

@TommyLemon 远程函数调用 java http接口, 我搜集一下需求和场景, 再补充 会涉及 认证等 这块功能不复杂, 插件搞定即可

cloudAndMonkey commented 1 year ago

redis 整合, 我还没有想好 1、json方式 如果是apijson json方式, 在executeQuery、executeUpdate做兼容即可 2、 cachemap cacheMap 实现方式和 redis 数据库表操作存在互斥 cacheMap 比如缓存主表查询数据, 而 redis 数据库表操作 跟着请求method走 3、远程函数 redis支持 没办法获取数据源,只能通过方法名、参数来区分操作

比较下来, 我先实现json方式

TommyLemon 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 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 文档等各种不友好的方式给到前端文档,可能有错误,更新不及时等

TommyLemon commented 1 year ago

@TommyLemon 远程函数调用 java http接口, 我搜集一下需求和场景, 再补充 会涉及 认证等 这块功能不复杂, 插件搞定即可

对,如果是同一个项目内部调用接口,一般都是在网关处理权限问题,内部服务调用不需要考虑认证; 这个往往也是很个性化的,很难有通用的解决方案,用户自己实现即可,我们最多针对常用场景提供插件。

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

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, 这段代码也需要升级

cloudAndMonkey commented 1 year ago

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

cloudAndMonkey commented 1 year ago

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

cloudAndMonkey commented 1 year ago

apijson api方式 redis整合, 支持功能点有点多,我还没有测好,支持: String: 字符串 Hash: 散列 List: 列表 Set: 集合 Sorted Set: 有序集合 比如: // redis command //string

{
    "@datasource": "redisCluster",
    "REDIS_STRING":{
        "key": "b",
        "value": "b",
        "option_method": "set"
    },
    "tag": "REDIS_STRING"
}

// keys

{
    "@datasource": "redisCluster",
    "REDIS_KEYS":{
        "key": "b",
        "expire_time": "5",
        "option_method": "expire"
    },
    "tag": "REDIS_KEYS"
}

// list

{
    "@datasource": "redisCluster",
    "REDIS_LIST":{
        "key": "list_1",
        "value": 2,
        "option_method": "lpush"
    },
    "tag": "REDIS_LIST"
}
{
    "@datasource": "redisCluster",
    "REDIS_LIST":{
        "key": "list_1",
        "value": 2323,
        "option_method": "rpush"
    },
    "tag": "REDIS_LIST"
}
{
    "@datasource": "redisCluster",
    "REDIS_LIST":{
        "key": "list_1",
        "value": "lset2323",
        "index": 0,
        "option_method": "lset"
    },
    "tag": "REDIS_LIST"
}
{
    "@datasource": "redisCluster",
    "REDIS_LIST":{
        "key": "list_1",
        "index": "AFTER",
        "value": "ddd",
        "value1": "AFTER",
        "option_method": "linsert"
    },
    "tag": "REDIS_LIST"
}
TommyLemon 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 兼容更好的方式