添加 Magic-API 接口快速开发框架的脚本编写助手

This commit is contained in:
李龙龙 2026-05-11 15:35:27 +08:00
parent b2d286035b
commit 8c6bec031b
39 changed files with 2874 additions and 0 deletions

18
magic-script-skill/.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
# Build and Release Folders
bin-debug/
bin-release/
[Oo]bj/
[Bb]in/
# Other files and folders
.settings/
# Executables
*.swf
*.air
*.ipa
*.apk
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
# should NOT be excluded as they contain compiler settings and other important
# information for Eclipse / Flash Builder.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Assassin-Q
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

154
magic-script-skill/SKILL.md Normal file
View File

@ -0,0 +1,154 @@
---
name: magic-script
description: magic-api 框架接口开发助手,提供 magic-script 脚本编写指导、DB/HTTP 模块使用及语法纠错;当用户需要在 magic-api 中编写接口脚本、调试脚本或排查脚本错误时使用
---
# Magic-Script 脚本开发助手
Magic-API 是一个基于 Java 的接口快速开发框架,支持通过 magic-script 脚本语言编写接口。magic-script 是一种基于 Mozilla Rhino 的脚本语言,语法类似 JavaScript专为接口开发优化。
> **说明**:本 Skill 同时兼容以下称呼方式:
> - magic-api / magicapi框架名称
> - magic-script / MagicScript / magicscript脚本语言名称
> 无论用户使用哪种称呼,都指代同一套技术栈。
## 任务目标
- 本 Skill 用于:为 magic-api/magicapi 框架提供 magic-script/MagicScript 脚本编写的完整指导
- 能力包含magic-script 语法参考、内置模块db、http 等)使用方法、接口开发示例和脚本故障排查
- 触发条件:用户需要在 magic-api/magicapi 中编写接口脚本、调试 magic-script/MagicScript 代码或解决脚本执行错误时使用
## 操作步骤
- 标准流程:确认场景 → 阅读 SKILL.md → 查阅 references/ 文档 → 编写脚本
- 可选分支:遇到脚本错误 → 查阅 faq 相关文档
## 使用示例
**示例1快速查询接口**
```javascript
// GET /api/user/list 分页查询
return db.table('sys_user')
.column('id', 'userId')
.column('name', 'userName')
.where()
.eq('status', 1)
.page(page, size);
```
**示例2带参数的增删改查**
```javascript
// POST /api/user/save 新增用户
assert not_blank(body.name) : 400, '用户名不能为空';
var userId = db.table('sys_user')
.column('name', body.name)
.column('create_time', now())
.insert();
return {code: 200, data: {id: userId}};
```
**示例3调用外部接口**
```javascript
import http;
var result = http.connect('https://api.example.com/data')
.param({key: 'value'})
.header('Authorization', 'Bearer xxx')
.post()
.getBody();
return result;
```
## 资源索引
### 语法类
| 文件 | 说明 |
|------|------|
| `references/keywords.md` | 关键字、运算符、数据类型 |
| `references/script-syntax.md` | 脚本语法详解 |
| `references/lambda-async.md` | Lambda表达式与异步调用 |
### 函数扩展
| 文件 | 说明 |
|------|------|
| `references/aggregation.md` | 聚合函数count/sum/max/min/avg/group_concat |
| `references/string-functions.md` | 字符串函数uuid/is_blank/not_blank |
| `references/date-functions.md` | 日期函数date_format/now/current_timestamp_millis/current_timestamp |
| `references/number-extensions.md` | Number扩展与数学函数 |
| `references/collection-extensions.md` | 列表与Map扩展 |
| `references/date-extensions.md` | 日期扩展 |
| `references/array-functions.md` | 数组创建函数 |
| `references/math-functions.md` | 数学函数round/floor/ceil/percent |
| `references/other-functions.md` | 其它函数print/println/ifnull/is_null/not_null |
| `references/object-extensions.md` | Object扩展方法 |
| `references/class-extensions.md` | Class扩展方法 |
| `references/pattern-extensions.md` | Pattern扩展方法 |
### 数据库
| 文件 | 说明 |
|------|------|
| `references/db-query.md` | 数据库查询select/selectOne/selectInt/selectValue |
| `references/db-update.md` | 数据库增删改insert/update/batchUpdate/call |
| `references/db-transaction.md` | 事务操作 |
| `references/db-cache.md` | 缓存操作cache/deleteCache |
| `references/single-table.md` | 单表操作db.table链式API |
| `references/sql-param.md` | SQL参数#{}、${}、动态SQL、Mybatis语法 |
| `references/page.md` | 分页查询 |
### 模块
| 文件 | 说明 |
|------|------|
| `references/http-module.md` | HTTP模块调用外部接口 |
| `references/request-module.md` | Request模块获取请求信息 |
| `references/response-module.md` | Response模块设置响应 |
| `references/log-module.md` | 日志模块 |
| `references/env-module.md` | 环境配置模块 |
| `references/magic-module.md` | Magic模块调用其他接口 |
### 集成
| 文件 | 说明 |
|------|------|
| `references/java-integration.md` | 脚本调用Java |
| `references/api-integration.md` | Java调用接口 |
### 示例
| 文件 | 说明 |
|------|------|
| `references/quick-start.md` | 快速入门(工程创建、三分钟写接口) |
| `references/quick-param.md` | 请求参数获取 |
| `references/quick-crud.md` | CRUD操作示例 |
| `references/linq.md` | Lambda/LINQ操作示例 |
### FAQ
| 文件 | 说明 |
|------|------|
| `references/faq.md` | 常见问题 |
| `references/validate.md` | 参数校验 |
## 注意事项
1. **语法限制**magic-script 不支持 ES6 默认参数语法 `excludeValue = null`,需用 `if (excludeValue === undefined)` 判断;不支持 `new Set()`,需用数组替代
2. **数组方法**:不支持传统 `for` 循环,需用 `for (value in list)``for (index, value in list)`**注意** Collection 扩展提供了 `reduce` 方法用于归约操作
3. **方法命名**:遍历集合用 `each` 而不是 `forEach`;获取长度用 `size()` 而不是 `length`判断Map键用 `containsKey()` 而不是 `hasOwnProperty()`
4. **SQL参数**`#{}` 防注入占位符用于普通参数,`${}` 字符串拼接用于动态表名/列名/排序字段(存在注入风险)
5. **类型转换**`::type` 语法用于类型转换,如 `'123'::int`,转换失败可指定默认值 `'abc'::int(0)`
6. **获取请求参数**
- URL参数直接使用变量名 `name`
- 表单参数:直接使用变量名 `name`
- Header参数`header.xxx`
- Body参数`body.xxx`
- Path参数`path.xxx` 或直接使用变量名
- Cookie参数`cookie.xxx`
- Session参数`session.xxx`
7. **循环拼接参数**`in (#{ids})` 语法会自动对集合参数展开
8. **多数据源**:使用 `db.slave.select(...)` 格式切换数据源
9. **SQL缓存**`db.cache("cacheName", ttl).select(...)` 使用缓存
10. **事务操作**
- 自动事务:`db.transaction(()=>{...})`
- 手动事务:`var tx = db.transaction(); tx.commit(); tx.rollback();`

View File

@ -0,0 +1,63 @@
# 聚合函数
## count
- 入参:`target`:`Object`
- 返回值:`int`
- 函数说明:计算集合大小
```javascript
var list = [1,2,3,4,5]
return count(list); // 5
```
## sum
- 入参:`target`:`Object`
- 返回值:`Number`
- 函数说明:对集合进行求和
```javascript
var list = [1,2,3,4,5]
return sum(list); // 15
```
## max
- 入参:`target`:`Object`
- 返回值:`Object`
- 函数说明:求集合最大值
```javascript
var list = [1,2,3,4,5]
return max(list); // 5
```
## min
- 入参:`target`:`Object`
- 返回值:`Object`
- 函数说明:求集合最小值
```javascript
var list = [1,2,3,4,5]
return min(list); // 1
```
## avg
- 入参:`target`:`Object`
- 返回值:`Object`
- 函数说明:求集合平均值
```javascript
var list = [1,2,3,4,5]
return avg(list); // 3
```
## group_concat
- 入参:`target`:`Object`
- 入参:`separator`:`String` 分隔符,可省略
- 返回值:`Object`
- 函数说明:将集合拼接起来
```javascript
var list = [1,2,3,4,5]
return group_concat(list); // "1,2,3,4,5"
// return group_concat(list,'|'); // "1|2|3|4|5"
```

View File

@ -0,0 +1,128 @@
# Java调用接口
## 调用接口
```java
@Autowired
MagicAPIService service;
Map<String, Object> params = new HashMap<>();
// 注入变量信息
params.put("id", 123);
// 内部调用接口不包含code以及message信息同时也不走拦截器。
Object value = service.execute("GET", "/hello", params);
// 内部调用接口包含code以及message信息同时也不走拦截器。
// Object value = service.call("GET", "/hello", params);
```
## 调用函数
```java
@Autowired
MagicAPIService service;
Map<String, Object> params = new HashMap<>();
// 注入变量信息
params.put("a", 1);
params.put("b", 1);
// 调用函数
Object value = service.invoke("/test/add", params);
```
## 保存资源
```java
@Autowired
MagicResourceService service;
// 保存分组信息
service.saveGroup(group);
// 保存接口ApiInfo、函数FunctionInfo、数据源DataSourceInfo
service.saveFile(apiInfo);
```
## 删除资源
```java
@Autowired
MagicResourceService service;
// 删除分组或文件
service.delete(id);
```
## 资源列表
```java
@Autowired
MagicResourceService service;
// 获取分组下的所有文件
service.listFiles(groupId);
// 获取接口api、函数function、数据源datasource列表
service.files(type);
// 获取接口api、函数function、数据源datasource树结构
service.tree(type);
// 获取全部资源的树结构
service.tree();
```
## 其它API
除了以上列举的`API`以外 `MagicAPIService`还有:
```java
/**
* 上传
*/
boolean upload(InputStream inputStream, String mode) throws IOException;
/**
* 下载
*/
void download(String groupId, List<SelectedResource> resources, OutputStream os) throws IOException;
/**
* 推送
*/
JsonBean<?> push(String target, String secretKey, String mode, List<SelectedResource> resources);
/**
* 处理刷新通知
*/
boolean processNotify(MagicNotify magicNotify);
```
`MagicResourceService` 还有以下方法:
```java
/**
* 刷新缓存
*/
void refresh();
/**
* 移动
* @param src 源ID
* @param groupId 目标分组
*/
boolean move(String src, String groupId);
/**
* 复制分组
* @param src 源ID
* @param target 目标分组
*/
String copyGroup(String src, String target);
/**
* 获取文件详情
*/
<T extends MagicEntity> T file(String id);
/**
* 获取分组详情
*/
Group getGroup(String id);
/**
* 获取完整分组路径
*/
String getGroupPath(String groupId);
/**
* 获取完整分组名称
*/
String getGroupName(String groupId);
```

View File

@ -0,0 +1,75 @@
# 数组函数
## new_int_array
- 入参:`size``int` 数组长度
- 函数说明,创建`int`类型的数组
```javascript
return new_int_array(1); // [0]
```
## new_long_array
- 入参:`size``int` 数组长度
- 函数说明,创建`long`类型的数组
```javascript
return new_long_array(1); // [0]
```
## new_double_array
- 入参:`size``int` 数组长度
- 函数说明,创建`double`类型的数组
```javascript
return new_double_array(1); // [0.0]
```
## new_float_array
- 入参:`size``int` 数组长度
- 函数说明,创建`float`类型的数组
```javascript
return new_float_array(1); // [0.0]
```
## new_short_array
- 入参:`size``int` 数组长度
- 函数说明,创建`short`类型的数组
```javascript
return new_short_array(1); // [0]
```
## new_byte_array
- 入参:`size``int` 数组长度
- 函数说明,创建`byte`类型的数组
```javascript
return new_byte_array(1); // [0]
```
## new_boolean_array
- 入参:`size``int` 数组长度
- 函数说明,创建`boolean`类型的数组
```javascript
return new_boolean_array(1); // [false]
```
## new_char_array
- 入参:`size``int` 数组长度
- 函数说明,创建`char`类型的数组
```javascript
return new_char_array(1); // ['\0']
```
## new_array
- 入参:`Class`:类型
- 入参:`size``int` 数组长度
- 函数说明,创建`Object`类型的数组
```javascript
return new_array(1); // [null] // Object 类型的数组
return new_array(String.class, 1); // [null] String类型的数组
```

View File

@ -0,0 +1,25 @@
# Class扩展方法
## newInstance
- 入参:`values`:`Object` 可变参数,构造函数的参数
- 返回值:`Object`
- 函数说明:将`Class`实例化
```javascript
import 'java.text.SimpleDateFormat' as SimpleDateFormat;
return SimpleDateFormat.newInstance('yyyy-MM-dd HH:mm:ss');
//其实可以简写成 new SimpleDateFormat('yyyy-MM-dd HH:mm:ss'); //这是一个语法糖
```
## 获取类名称
支持以下方法
- `getName` - 获取完整类名
- `getSimpleName` - 获取简单类名
- `getCanonicalName` - 获取规范类名
```javascript
import 'java.text.SimpleDateFormat' as SimpleDateFormat;
println(SimpleDateFormat.getName()); // java.text.SimpleDateFormat
println(SimpleDateFormat.getSimpleName()); // SimpleDateFormat
println(SimpleDateFormat.getCanonicalName()); // java.text.SimpleDateFormat
```

View File

@ -0,0 +1,304 @@
# 数组&集合扩展方法
为`Collection`,`Iterator`,`Enumeration`,`Object[]` 添加的扩展方法
## map
- 入参:`function`:`Function` 接收一个`Lambda`表达式
- 返回值:`Object`
- 函数说明:将集合进行循环转换
```javascript
var list = [1,2,3,4,5];
return list.map(e=>e+1); //返回[2,3,4,5,6]
```
## filter
- 入参:`function`:`Function` 接收一个`Lambda`表达式
- 返回值:`Object`
- 函数说明:将集合进行过滤
```javascript
var list = [1,2,3,4,5];
return list.filter(e=>e>3); //返回[4,5]
return list.filter((item,index)=>index>1); //返回[3,4,5]
```
## each
- 入参:`function`:`Function` 接收一个`Lambda`表达式
- 返回值:`Object`
- 函数说明:循环处理
```javascript
var list = [{name : '小明'},{name : '小花'}];
return list.each(item=>item.put('age',18)); //循环添加age属性
```
## sort
- 入参:`function`:`Function` 接收一个`Lambda`表达式
- 返回值:`Object`
- 函数说明:对集合进行排序
```javascript
var list = [1,5,2,3,6];
return list.sort((a,b)=>a-b);
```
## first
- 返回值:`Object`
- 函数说明:返回集合的第一项,集合为空时返回`null`
```javascript
var list = [1,2,3,4,5]
return list.first(); // 1
```
## last
- 返回值:`Object`
- 函数说明:返回集合的最后一项,集合为空时返回`null`
```javascript
var list = [1,2,3,4,5]
return list.last(); // 5
```
## reserve
- 返回值:`Object`
- 函数说明:对集合进行反转操作
```javascript
var list = [1,5,2,3,6];
return list.reserve();
```
## join(拼接)
- 入参:`separator` : `String` 分隔符
- 返回值:`String`
- 函数说明:对集合进行拼接操作
```javascript
var list = [1,5,2,3,6];
return list.join('-'); // 1-5-2-3-6
```
## shuffle
- 返回值:`Object`
- 函数说明:对集合进行打乱处理
```javascript
var list = [1,5,2,3,6];
return list.shuffle();
```
## max
- 返回值:`Object`
- 函数说明取出集合最大值如果找不到返回null
```javascript
var list = [1,6,8,9,18,12];
return list.max(); // 18
```
## min
- 返回值:`Object`
- 函数说明取出集合最小值如果找不到返回null
```javascript
var list = [6,1,8,9,18,12];
return list.min(); // 1
```
## sum
- 返回值:`Object`
- 函数说明累加求和计算不出返回0.0
```javascript
var list = [1,2,3,4];
return list.sum(); // 10
```
## avg
- 返回值:`Object`
- 函数说明计算平均值计算不出返回null
```javascript
var list = [1,2,3,4];
return list.avg(); // 2.5
```
## group
- 入参:`condition` : `Function` 分组条件
- 入参:`mapping` : `Function` 结果映射(省略时不做映射返回List)
- 返回值:`Map<Object, List<Object>>`或`Map<Object, Object>`
- 函数说明:分组
```javascript
var result = [
{ xxx : 1, yyy : 2, value : 11},
{ xxx : 1, yyy : 2, value : 22},
{ xxx : 2, yyy : 2, value : 33}
];
return result.group(item=>item.xxx + '_' + item.yyy)
// 结果:{"1_2": [{...}, {...}], "2_2": [{...}]}
```
## join(关联)
- 入参:`target` : `Object` 关联的集合
- 入参:`condition` : `Function` 关联条件
- 入参:`mapping` : `Function` 结果映射
- 返回值:`List<Object>`
- 函数说明:将两个集合关联起来
```javascript
var year2019 = [
{ "pt":2019, "item_code":"code_1", "sum_price":2234 },
{ "pt":2019, "item_code":"code_2", "sum_price":234 }
];
var year2018 = [
{ "pt":2018, "item_code":"code_1", "sum_price":1234.0 }
];
return year2019.join(year2018, (left, right) => left.item_code == right.item_code, (left, right) => {
'年份' : left.pt,
'编号' : left.item_code,
'今年' : left.sum_price,
'去年' : right == null ? 'unknow' : right.sum_price
});
```
## asBean(转为Java对象)
- 入参:`target` : `Class<?>` 目标类型
- 返回值:`List<?>`
- 函数说明:将`List<Object>` 转为目标`List`
```javascript
import 'org.ssssssss.script.functions.User' as User;
var userList = [{
age : 18,
weight : 121,
money : 123456789L,
name : '法外狂徒'
}]
return userList.asBean(User.class);
```
## every
- 入参:`condition` : `Function` 判断条件
- 返回值:`boolean`
- 函数说明:判断集合是否都符合条件
```javascript
var vals = [1, 2, 3, 4, 5, 6, 7];
return vals.every(e => e > 0); // true
```
## some
- 入参:`condition` : `Function` 判断条件
- 返回值:`boolean`
- 函数说明:判断集合是否有符合条件的
```javascript
var vals = [1, 2, 3, 4, 5, 6, 7];
return vals.some(e => e == 0); // false
```
## reduce
- 入参:`function` : `Function` 计算函数
- 返回值:`Object`
- 函数说明:循环集合通过给定的计算函数返回一个新值
```javascript
var vals = [1, 2, 3];
return vals.reduce((sum, val) => sum + val); // 6
```
## find
- 入参:`function``Function` 查找函数
- 返回值:`Object`
- 函数说明:循环集合查找符合条件的对象
```javascript
var list = [{name: 'A'}, {name:'B'}]
return list.find(it => it.name == 'A'); // {name: 'A'}
```
## findIndex
- 入参:`function``Function` 查找函数
- 返回值:`Object`
- 函数说明:循环集合查找符合条件的对象位置
```javascript
var list = [{name: 'A'}, {name: 'B'}]
return list.findIndex(it => it.name == 'A'); // 0
```
## concat
- 入参:`Object`,要连接的集合对象,可写多个
- 返回值:`Object`
- 函数说明:拼接一个或多个集合,返回新的集合
```javascript
var list = [1];
return [1].concat([2]); // [1,2] list不变
return [1].concat([2],[3, 4]); // [1, 2, 3, 4] list不变
```
## toMap
- 入参:`mappingKey``Function`key映射方法
- 入参:`mappingValue``Function`value映射方法可省略默认为本身
```javascript
var list = [
{id : 1, name: 'A'},
{id : 2, name: 'B'},
{id : 3, name: 'C'},
]
return list.toMap(k => k.id, v => v.name) // {1: 'A', 2: 'B', 3: 'C'}
```
## skip
- 入参:`value` : `int` 跳过的数量
- 返回值:`Object`
- 函数说明:跳过指定个数截取集合
```javascript
var vals = [1, 2, 3, 4];
return vals.skip(2); // [3, 4]
```
## limit
- 入参:`value` : `int` 限制的数量
- 返回值:`Object`
- 函数说明:取指定个数的集合
```javascript
var vals = [1, 2, 3, 4];
return vals.limit(3); // [1, 2, 3]
```
## findNotNull
- 返回值:`Object`
- 函数说明:找到第一个不为`null`的值
```javascript
var vals = [null, null, 3, null];
return vals.findNotNull(); // 3
```
## distinct
- 返回值:`Object`
- 函数说明:去掉重复元素
```javascript
var arr = [1, 2, 2, 3];
return arr.distinct(); // [1, 2, 3]
```
## distinct(func)
- 入参: 映射函数, 形如`e -> e.id`
- 返回值:`Object`
- 函数说明:根据函数返回值去重,去掉重复元素
```javascript
var arr = [{id: 1, name: "xiaodong"}, {id:1, name: "magic-api"}];
return arr.distinct(e => e.id); // [{id: 1, name: "xiaodong"}]
```

View File

@ -0,0 +1,11 @@
# Date扩展方法
## format
- 入参:`pattern`:`String` 格式
- 返回值:`String`
- 函数说明:将日期格式化
```javascript
var date = new Date();
return date.format('yyyy-MM-dd'); // 2020-01-01
```

View File

@ -0,0 +1,36 @@
# 日期函数
## date_format
- 入参:`target`:`Date` 日期
- 入参:`pattern`:`String` 格式
- 返回值:`String`
- 函数说明:日期格式化
```javascript
return date_format(new Date()); // 2020-01-01 20:30:30
// return date_format(new Date(),'yyyy-MM-dd'); // 2020-01-01
```
## now
- 返回值:`Date`
- 函数说明:返回当前日期
```javascript
return now(); // 等同于 new Date();
```
## current_timestamp_millis
- 返回值:`long`
- 函数说明:取当前时间戳(毫秒)
```javascript
return current_timestamp_millis(); // 等同于 System.currentTimeMillis();
```
## current_timestamp
- 返回值:`long`
- 函数说明:取当前时间戳(秒)
```javascript
return current_timestamp(); // 等同于 current_timestamp_millis() / 1000;
```

View File

@ -0,0 +1,22 @@
# 缓存操作
## cache
- 入参:`cacheName`:`String`
- 入参:`ttl`:`long` 缓存有效期,单位毫秒,可省略,默认为配置的值
- 返回值:`db` //返回当前实例,即可以链式调用
- 函数说明:使用缓存
```javascript
// 使用缓存名为user的查询
return db.cache('user').select('select * from sys_user');
```
## deleteCache
- 入参:`cacheName`:`String`
- 返回值:`db` //返回当前实例,即可以链式调用
- 函数说明:删除名为`cacheName`的缓存
```javascript
// 删除名为user的缓存
db.deleteCache('user');
```

View File

@ -0,0 +1,63 @@
# 数据库查询
db模块是默认引入的模块无需import。
## select
- 入参:`sql`:`String`
- 返回值:`List<Map<String,Object>>`
- 函数说明:查询`List`结果
```javascript
return db.select('select * from sys_user');
```
## selectInt
- 入参:`sql`:`String`
- 返回值:`Integer`
- 函数说明:查询`int`结果
```javascript
// 需要保证结果返回一行一列
return db.selectInt('select count(*) from sys_user');
```
## selectOne
- 入参:`sql`:`String`
- 返回值:`Map<String,Object>`
- 函数说明:查询单个对象
```javascript
return db.selectOne('select * from sys_user limit 1');
```
## selectValue
- 入参:`sql`:`String`
- 返回值:`Object`
- 函数说明:查询单个值
```javascript
//需要保证结果返回一行一列
return db.selectValue('select user_name from sys_user limit 1');
```
## page
- 入参:`sql`:`String`
- 入参:`limit` : `long` 可省略
- 入参:`offset` : `long` 可省略
- 返回值:`Object` 默认返回为Object如果自定义了分页结果则返回自定义结果
- 函数说明:分页查询
```javascript
return db.page('select * from sys_user');
```
## 列名转换
- normal 列名保持原样
- camel 列名使用驼峰命名
- pascal 列名使用帕斯卡命名
- upper 列名保持全大写
- lower 列名保持全小写
```javascript
return db.camel().select('select * from sys_user');
```

View File

@ -0,0 +1,23 @@
# 事务操作
## 自动事务
```javascript
var val = db.transaction(()=>{
var v1 = db.update('...');
var v2 = db.update('....');
return v2;
});
return val;
```
## 手动事务
```javascript
var tx = db.transaction(); //开启事务
try{
var value = db.update('...');
tx.commit(); // 提交事务
return value;
}catch(e){
tx.rollback(); // 回滚事务
}
```

View File

@ -0,0 +1,49 @@
# 数据库增删改
## update
- 入参:`sql`:`String`
- 返回值:`Integer`
- 函数说明:执行增删改操作
```javascript
return db.update('delete from sys_user');
```
## insert
- 入参:`sql``String`
- 入参:`id``String`,主键列,可空,如无特殊情况不需要传入
- 返回值: `Object`
```javascript
return db.insert("insert into sys_user(username,password) values('admin','admin)");
```
## call
- 入参:`sql`: `String`
- 返回值:`Map<String,Object>`
- 函数说明:调用存储过程
```javascript
// 入参格式: #{参数名}
// 出参格式: @{参数名, java.sql.Types的类型字符串}
// 出入参格式:@{参数名(值、变量、表达式), java.sql.Types的类型字符串}
var cs1 = body.cs1;
var cs2 = body.cs2;
return db.call("""
call test(#{cs1}, @{height(cs2), INTEGER}, @{v_area, VARCHAR})
""")
// 返回:{height: 10, v_area: "16.85"}
```
## batchUpdate
- 入参:`sql``String`
- 入参:`batchArgs``List<Object[]>`数据,占位符和数组下标对应
- 返回值: `int`
```javascript
return db.batchUpdate("""
update sys_dict set is_del = ? where is_del = ?
""", [
["1", "0"].toArray()
])
```

View File

@ -0,0 +1,18 @@
# 环境配置模块
## 引用模块
```javascript
import env;
```
## 使用
```javascript
import env;
return env.get('server.port')
```
## get
- 入参:`key`:`String` 配置项
- 入参:`defaultValue`:`String` 默认值,可省略
- 返回值:`String`
- 函数说明:获取`Spring`配置项

View File

@ -0,0 +1,177 @@
# 常见问题
## 如何配置JSON日期的格式
使用`Jackson`的配置如下(`Spring Boot`默认使用`Jackson`)
```yaml
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
```
## 出现找不到db模块的错误
目前已知两种情况:
- 未配置数据源
- 未引用`spring-boot-starter-jdbc`
## 如何获取RequestBody中的参数
脚本中使用`body.xxx`获取`RequestBody`中的参数
SQL中使用`#{body.xxx}`或`${body.xxx}`获取`RequestBody`中的参数
## 如何获取Header中的参数
脚本中使用`header.xxx`获取`Header`中的参数
SQL中使用`#{header.xxx}`或`${header.xxx}`获取`Header`中的参数
## 如何获取Cookie中的参数
脚本使用`cookie.xxx`获取`Cookie`中的参数
SQL中使用`#{cookie.xxx}`或`${cookie.xxx}`获取`Cookie`中的参数
## 如何获取Session中的参数
脚本中使用`session.xxx`获取`Session`中的参数
SQL中使用`#{session.xxx}`获取`Session`中的参数
## 如何获取PathVariable中的参数
脚本中使用`PathVariableName`或`path.xxxx`获取`PathVariable`中的参数
SQL中使用`#{PathVariableName}`或`#{path.xxx}`获取`PathVariable`中的参数
## 如何获取上传的文件
利用Request模块
```javascript
import request;
request.getFile('name');
```
## 如何获取提交的数组参数
利用Request模块
```javascript
import request;
return request.getValues('name');
```
## 如何给接口添加权限
一般情况采用`拦截器`实现。在`接口选项`中配置`permisson`或`role`或自定义选项,随后在拦截器中实现:
```java
@Component
@Order(1)
public class PermissionInterceptor implements RequestInterceptor {
@Override
public Object preHandle(ApiInfo info, MagicScriptContext context, MagicHttpServletRequest request, MagicHttpServletResponse response) {
String permissionCode = info.getOptionValue(Options.PERMISSION);
// 执行自己的代码逻辑判断是否有权限
if(无权限){
return new JsonBean<>(403,"无权访问");
}
return null;
}
}
```
## 如何给UI添加权限
请参考[自定义UI鉴权](https://www.ssssssss.org/magic-api/pages/security/operation/)
## ${}和#{}的区别
主要区别在于`${}`用于拼接SQL(会产生SQL注入问题)`#{}`会替换成占位符不会产生SQL注入问题这里的区别与`Mybatis`一致
## 如何循环拼接参数
两种办法:
- `in (#{ids})`的语法会自动对集合参数展开
```javascript
var ids = [1,2,3,4,5,6];
return db.select('select * from sys_user where id in(#{ids})');
//会自动变成select * from sys_user where id in(?,?,?,?,?,?)
```
- 循环拼接SQL
```javascript
var list = [1,2,3,4,5];
var sql = "select * from sys_user where ";
for(index,item in list){
sql = sql + 'id = #{list['+index+']}';
if(index + 1 < list.size()){
sql = sql + ' or ';
}
}
return db.select(sql);
```
## 多数据源如何配置
编写java代码如下
```java
@Bean
public MagicDynamicDataSource magicDynamicDataSource(){
MagicDynamicDataSource dynamicDataSource = new MagicDynamicDataSource();
dynamicDataSource.setDefault(ds1);
dynamicDataSource.add("slave",ds2);
return dynamicDataSource;
}
```
脚本中使用:
```javascript
db.select('select * from sys_user'); //使用默认数据源
db.slave.select('select * from sys_user'); //使用slave数据源
```
## SQL执行报错java.sql.SQLFeatureNotSupportedException: null
原因druid版本过低升级至最新版后即可。
## 如何自定义返回结果
- 通过配置文件进行配置,具体参考[spring-boot配置](https://www.ssssssss.org/magic-api/pages/config/spring-boot/)
- 通过`自定义JSON结果`,具体定义方法查看[自定义JSON结果](https://www.ssssssss.org/magic-api/pages/base/response/)
- 通过`自定义拦截器`拦截返回自己想要的格式,具体定义方法查看[自定义拦截器](https://www.ssssssss.org/magic-api/pages/senior/interceptor/)
- 通过`spring`的拦截器返回想要的格式,如`ResponseBodyAdvice``HandlerMethodReturnValueHandler`这种方式目前会影响到UI,故不推荐使用)
## 页面加载缓慢
由于`monaco-editor`编辑器比较大,建议开启压缩静态资源
```yaml
server.compression.enabled=true #启用压缩
server.compression.min-response-size=256 #大于256kb时压缩
```
## 脚本内容被转义
出现这种情况,请检查自身项目是否有`XSS`一类的过滤器,需要把`UI`界面对应的后台接口排除掉即可。
## 执行测试无响应
目前已知有两种情况:
- 使用了Spring Boot 2.3.5版本升级至2.3.6解决
- 使用了`nginx`代理,加一条配置`proxy_buffering off;`解决
## 访问UI404
- 请检查访问路径是否正确
- 请检查`magic-editor`包是否被引入
- 如果是拉源码运行,则需要编译一下前端
- 如果以上确定没问题,请检查应用中是否有关于`mvc`的配置,如果有请检查是否是`extends WebMvcConfigurationSupport`的形式,是的话,改成`implements WebMvcConfigurer`的形式
- 如以上问题均不存在,请提[ISSUE](https://gitee.com/ssssssss-team/magic-api/issues) 或加群700818216反馈
## 无法DEBUG或无法查看日志
由于`DEBUG`和日志是依赖于`WebSocket`实现的,所以需要`WebSocket`支持。
- 请检查`Web`容器是否支持`WebSocket`,如果不支持需要引入对应依赖或更换支持`WebSocket`的`Web`容器
- 请检查是否使用了`nginx`之类的代理,如果使用了,需要对配置其支持`WebSocket`,样例如下:
```nginx
location /magic/web/console {
proxy_pass http://localhost:9999;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 900s;
}
```
## 保存图片(Blob)数据到数据库
假设将图片的二进制数据传输到body.img中, sql可以这么写
```javascript
var sql = """
insert into img_table(img)
values(#{img::sql('blob')})
""";
```

View File

@ -0,0 +1,97 @@
# HTTP模块
## 模块说明
`http`模块是基于`RestTemplate`封装而来,目前只做了少量的封装。对于一些通用的配置可以使用自定义`RestTemplate`来实现
```java
@Bean
public HttpModule magicHttpModule() {
RestTemplate template = new RestTemplate();
// 对RestTemplate进行配置.
return new HttpModule(template);
}
```
## 引用模块
```javascript
import http;
```
## connect
- 入参:`url`:`string`
- 返回值:`HttpModule`
- 函数说明创建新的http请求对象
```javascript
import http;
http.connect("http://localhost:9999/sql/select")
```
## 设置URL参数、表单参数、Header
```javascript
import http;
http.param('url_param1','url_param_value1') // 设置URL参数
.param({ // 批量设置URL参数
url_param_2 : 2,
url_param_3 : 3,
})
.data('form_param1','form_param_value1') // 设置表单参数
.data({ // 批量设置表单参数
form_param_2 : 2,
form_param_3 : 3,
})
.header('header_param1','header_param_value1') // 设置header参数
.header({ // 批量设置header参数
header_param_2 : 2,
header_param_3 : 3,
})
```
## body
- 入参:`body`:`Object`
- 函数说明设置请求Body
```javascript
import http;
http.connect('..').body({
id: 1,
name: 'magic-api'
});
```
## entity
- 入参: `entity`: `HttpEntity`
- 函数说明:自定义`HttpEntity`
```javascript
import http;
http.connect('..').entity(entity)
```
## contentType
- 入参: `contentType`: `String`或`MediaType`
- 函数说明:定义请求内容类型
```javascript
import http;
http.connect('..').contentType('application/json')
```
## 请求方法
- `post()` - POST请求
- `get()` - GET请求
- `delete()` - DELETE请求
- `put()` - PUT请求
- `head()` - HEAD请求
- `patch()` - PATCH请求
- `options()` - OPTIONS请求
- `trace()` - TRACE请求
## execute
- 返回值:`ResponseEntity`
- 函数说明:执行对应的请求
```javascript
import http;
return http.connect('http://localhost:9999/sql/select').post().getBody()
```

View File

@ -0,0 +1,42 @@
# 脚本调用Java
## 注入Spring Bean
```javascript
// 第一种方式
import xx.xxx.xxx.xxx.UserService; // 使用类名
return UserService.selectUserList();
// 第二种方式
import "userUservice" as userService; // 使用Bean名
return userService.selectUserList();
```
## 调用静态方法
```javascript
import xxx.xxx.xx.xx.xx.StringUtils;
return StringUtils.isBlank("");
```
## 调用普通方法
```javascript
// 对于java.util、java.lang 包下的类,可以直接使用。
return new ArrayList();
// 对于其他类需要import
import "java.text.SimpleDateFormat";
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
```
## 调用magic-api的接口
```javascript
// 可以在脚本中直接调用非http方式
import "@get:/api/sys/user/list" as userList; // 导入定义的GET请求的 /api/sys/user/list 接口。
// 脚本中变量是共享给调用者的。所以无需指定参数传入。只需要在本脚本中定义该变量即可。
return userList();
```
## 调用magic-api的函数
```javascript
import "@/common/encode/md5" as md5; // 导入页面上定义的函数信息
return md5('123456');
```

View File

@ -0,0 +1,108 @@
# 关键字、运算符、数据类型
## 关键字
| 关键字 | 含义 |
|--------|------|
| var | 定义变量 |
| if | 条件语句的引导词 |
| else | 用在条件语句中,表明当条件不成立时的分支 |
| for | for循环语句 |
| in | 与for配合使用 |
| while | while循环语句 |
| continue | 执行下一次循环 |
| break | 跳出循环 |
| return | 终止当前过程的执行并正常退出到上一个执行过程中 |
| exit | 终止当前脚本,并退出返回,如`exit 200,'执行成功',[1,2,3];` |
| assert | 断言 |
| instanceof | 判断一个对象是否为一个类的实例 |
| try | 用于捕获可能发生异常的代码块 |
| catch | 与try关键字配合使用当发生异常时执行 |
| finally | 与try关键字配合使用finally块无论发生异常都会执行 |
| import | 导入Java类或导入已定义好的模块 |
| as | 与 import 关键字配合使用,用作将导入的 Java类或模块 命名为一个本地变量名 |
| new | 创建对象 |
| true | 基础类型之一,表示 Boolean 的:真值 |
| false | 基础类型之一,表示 Boolean 的:假值 |
| null | 基础类型之一,表示 NULL 值 |
| async | 异步调用 |
## 运算符
### 数学运算
| 运算符 | 说明 |
|--------|------|
| + | 加法 |
| - | 减法 |
| * | 乘法 |
| / | 除法 |
| % | 取模 |
| ++ | 自增 |
| -- | 自减 |
| += | 加等于 |
| -= | 减等于 |
| *= | 乘等于 |
| /= | 除等于 |
| %= | 取模等于 |
### 比较运算符
| 运算符 | 说明 |
|--------|------|
| < | 小于 |
| <= | 小于等于 |
| > | 大于 |
| >= | 大于等于 |
| == | 等于 |
| != | 不等于 |
| === | 等于 |
| !== | 不等于 |
### 逻辑运算符
| 运算符 | 说明 |
|--------|------|
| && | 并且 |
| \|\| | 或者 |
| ! | 取反 |
### 位运算符
| 运算符 | 说明 |
|--------|------|
| & | 与 |
| \| | 或 |
| ^ | 异或 |
| ~ | 取反 |
| << | 左移 |
| >> | 右移 |
| >>> | 无符号右移 |
## 数据类型
| 类型 | 写法 |
|------|------|
| byte | `123b`、`123B` |
| short | `123s`、`123S` |
| int | `123` |
| long | `123l`、`123L` |
| float | `123f`、`123F` |
| double | `123d`、`123D` |
| BigDecimal | `123m`、`123M` |
| boolean | `true`、`false` |
| string | `'hello'``"hello"` |
| string | `"""多行文本块,主要用于编写SQL"""` |
| Pattern | `/\d+/g`,`/pattern/gimuy` 用于定义正则 |
| lambda | `()=>expr`、`(param1,param2....)=>{...}` |
| list | `[1,2,3,4,5]` |
| map | `{key : value,key1 : value}``{[key] : "value"}` |
## 三元运算符
三元运算符是`if`语句的简写形式其工作方式类似于Java中例如`true ? "yes" : "no"`
增强的`if`和三元运算符,不再强制值必须是布尔类型,可以写`if(xxx)`的形式当`xxx`为以下情况时为`false`、其它情况为`true`
- `null`
- 空集合
- 空Map
- 空数组
- 数值==0
- 空字符串
- `false`

View File

@ -0,0 +1,102 @@
# Lambda表达式与异步调用
## Lambda表达式
### 映射(map)
```javascript
var list = [
{sex : 0,name : '小明',age : 19},
{sex : 1,name : '小花',age : 18}
];
var getAge = (age) => age > 18 ? '成人' : '未成年'
return list.map(item => {
age : getAge(item.age),
sex : item.sex == 0 ? '男' : '女',
name : item.name
});
// 结果:[{sex: "男", name: "小明", age: "成人"}, {sex: "女", name: "小花", age: "未成年"}]
```
### 过滤(filter)
```javascript
var list = [
{sex : 0,name : '小明'},
{sex : 1,name : '小花'}
]
return list.filter(item => item.sex == 0);
// 结果:[{sex: 0, name: "小明"}]
```
### 过滤+映射(filter + map)
```javascript
var list = [
{sex : 0,name : '小明'},
{sex : 1,name : '小花'}
]
return list.filter(item => item.sex == 0).map(item => {
sex : item.sex == 0 ? '男' : '女',
name : item.name
});
// 结果:[{sex: "男", name: "小明"}]
```
### 分组(group)
默认聚合为List
```javascript
var result = [
{ xxx : 1, yyy : 2, value : 11},
{ xxx : 1, yyy : 2, value : 22},
{ xxx : 2, yyy : 2, value : 33}
];
return result.group(item => item.xxx + '_' + item.yyy)
// 结果:{"1_2": [{...}, {...}], "2_2": [{...}]}
```
自定义聚合对象
```javascript
return result.group(item => item.xxx + '_' + item.yyy,list => {
count : list.size(),
sum : list.map(v=>v.value).sum(),
avg : list.map(v=>v.value).avg()
})
// 结果:{"1_2": {"avg": 16.5, "count": 2, "sum": 33}, "2_2": {"avg": 33, "count": 1, "sum": 33}}
```
### 关联(join)
```javascript
var year2019 = [
{ "pt":2019, "item_code":"code_1", "sum_price":2234 },
{ "pt":2019, "item_code":"code_2", "sum_price":234 }
];
var year2018 = [
{ "pt":2018, "item_code":"code_1", "sum_price":1234.0 }
];
return year2019.join(year2018, (left, right) => left.item_code == right.item_code, (left, right) => {
'年份' : left.pt,
'编号' : left.item_code,
'今年' : left.sum_price,
'去年' : right == null ? 'unknow' : right.sum_price
});
```
## 异步调用
### 普通方法
```javascript
// 使用async关键字会启动一个线程去执行返回Future
var user1 = async db.select("select * from sys_user where id = 1");
var user2 = async db.select("select * from sys_user where id = 2");
// 调用get方法表示阻塞等待获取结果
return [user1.get(),user2.get()];
```
### lambda
```javascript
var list = [];
for(index in range(1,10)){
// 当异步中使用外部变量时,为了确保线程安全的变量,可以将其放在形参中
list.add(async (index)=>db.select("select * from sys_user where id = #{index}"));
}
return list.map(item=>item.get());
```

View File

@ -0,0 +1,76 @@
# Linq
## 基本语法
```sql
select
tableAlias.*|[tableAlias.]field[ columnAlias]
[,tableAlias.field2[ columnAlias2][,…]]
from expr[,…] tableAlias
[[left ]join expr tableAlias2 on condition]
[where condition]
[group by tableAlias.field[,...]]
[having condition]
[order by tableAlias.field[asc|desc][,tableAlias.field[asc|desc]]]
[limit expr [offset expr]]
```
## 执行步骤
- 先从`from`子句创建虚拟表VT1
- 处理`join`创建虚拟表VT2筛选符合条件`condition`的行加入到虚拟表VT2中
- 处理`where` 将符合`condition`的行加入虚拟表VT1中
- 处理`group by` 对虚拟表VT1、VT2进行分组操作将符合`having condition`的值加入虚拟表VT3中
- 处理`select` 从VT3中选择指定的列加入虚拟表VT4中
- 处理`order by` 对虚拟表VT4进行排序
- 处理`limit`
## select子句
```sql
select t.name,sum(t.score) score,t.*
```
> select 中带有聚合函数的应该有group by语句否则不会进行聚合处理
## from子句
```sql
-- 以下三种方式均可(别名是必须的)
from [{name: 'Gitee'},[name:'Github']] t
from results t
from {name:'Gitee'} t
```
> from 跟着的必须是`List`或者`Map`
## join子句
```sql
-- 以下三种方式均可(别名是必须的)
[left] join [{name: 'Gitee'},[name:'Github']] t1 on t1.name = t.name
[left] join results t1 on 1 = 1
[left] join {name:'Gitee'} t1 on t1.name = 'Gitee' and 1=1
```
## where子句
```sql
-- or 等价于|| and 等价于 && 可以混合使用。
where t.name = 'Gitee' or t.name = 'Github' and 1=1 && 2=2
```
## group by子句
```sql
group by t.name, t1.xxx
```
## having 子句
```sql
having count(t.name) > 1
```
## order by子句
```sql
-- asc可以不写默认是asc
order by t.name desc,t.xxx
```
## limit 子句
```sql
limit 1 -- 固定取第一项返回值会是对象而非List
limit pageSize offset (page - 1) * pageSize
limit pageSize
```

View File

@ -0,0 +1,15 @@
# 日志模块
## 引用模块
```javascript
import log;
```
## 使用
```javascript
import log; //org.slf4j.Logger
// 使用方法与SLF4J完全一致
log.info('Hello');
log.info('Hello {}','MagicAPI');
log.debug('test');
```

View File

@ -0,0 +1,45 @@
# Magic模块
## 引用模块
```javascript
import magic;
```
## call
- 入参:`method`:`String` 定义的请求方法
- 入参:`path`:`String` 定义的路径
- 入参:`parameters`:`Map` 变量信息
- 返回值:`Object`
- 函数说明执行MagicAPI中的接口,返回值带code和message信息
```javascript
return magic.call('get','execute/sql',{
message : 'Hello,Magic API!' //传入参数
})
```
## execute
- 入参:`method`:`String` 定义的请求方法
- 入参:`path`:`String` 定义的请求路径
- 入参:`parameters`:`Map` 变量信息
- 返回值:`Object`
- 函数说明执行MagicAPI中的接口,返回原始内容不包含code以及message信息
```javascript
return magic.execute('get','execute/sql',{
message : 'Hello,Magic API!' //传入参数
})
```
## invoke
- 入参:`path`:`String` 函数路径
- 入参:`parameters`:`Map` 变量信息
- 返回值:`Object`
- 函数说明执行MagicAPI中的函数
```javascript
return magic.invoke('/test/add',{
a: 1,
b: 2
})
```

View File

@ -0,0 +1,39 @@
# 数学函数
## round
- 入参:`number`:`Number` 目标值
- 入参:`len`:`int` 要保留的小数位数 可省略默认0
- 返回值:`Number`
- 函数说明四舍五入保留N位小数
```javascript
return round(123.456d,2); //123.46
```
## floor
- 入参:`number`:`Number` 目标值
- 返回值:`Number`
- 函数说明:向下取整
```javascript
return floor(123.456d); // 123;
```
## ceil
- 入参:`number`:`Number` 目标值
- 返回值:`Number`
- 函数说明:向上取整
```javascript
return ceil(123.456d); // 124;
```
## percent
- 入参:`number`:`Number` 目标值
- 入参:`len`:`int` 要保留的小数 可省略默认0
- 返回值:`String`
- 函数说明:将数值转为百分比
```javascript
return percent(0.1289999999,2); // "12.90%"
```

View File

@ -0,0 +1,51 @@
# Number扩展方法
`java.lang.Number`的扩展方法,用于数值类型的扩展
## round
- 入参:`number`:`int` 要保留的小数
- 返回值:`Number`
- 函数说明四舍五入保留N位小数
```javascript
var value = 123.456d;
return value.round(2); //123.46
```
## toFixed
- 入参:`number`:`int` 要保留的小数
- 返回值:`String`
- 函数说明四舍五入保留N位小数(和JS一样强制限制位数)
```javascript
var value = 123.456d;
return value.toFixed(10); // "123.4560000000"
```
## floor
- 返回值:`Number`
- 函数说明:向下取整
```javascript
var value = 123.456d;
return value.floor(); // 123;
```
## ceil
- 返回值:`Number`
- 函数说明:向上取整
```javascript
var value = 123.456d;
return value.ceil(); // 124;
```
## asPercent
- 入参:`number`:`int` 要保留的小数
- 返回值:`String`
- 函数说明:将数值转为百分比
```javascript
var value = 0.1289999999;
return value.asPercent(2); // "12.90%"
```

View File

@ -0,0 +1,129 @@
# Object扩展方法
## asInt
- 入参:`defaultValue`:`int` 选填,当转换失败时返回默认值,默认为`0`
- 返回值:`int`
- 函数说明转对象为int类型
```javascript
var obj = '123';
return obj.asInt();
//return obj.asInt(1); //转换失败时返回1
```
## asDouble
- 入参:`defaultValue`:`double` 选填,当转换失败时返回默认值,默认为`0.0`
- 返回值:`double`
- 函数说明:转对象为`double`类型
```javascript
var obj = '123';
return obj.asDouble();
//return obj.asDouble(1.0d); //转换失败时返回1.0d
```
## asDecimal
- 入参:`defaultValue`:`BigDecimal` 选填,当转换失败时返回默认值,默认为`null`
- 返回值:`BigDecimal`
- 函数说明:转对象为`BigDecimal`类型
```javascript
var obj = '123.456';
return obj.asDecimal();
//return obj.asDecimal(1.5m); //转换失败时返回1.5m
```
## asFloat
- 入参:`defaultValue`:`float` 选填,当转换失败时返回默认值,默认为`0.0f`
- 返回值:`float`
- 函数说明:转对象为`float`类型
```javascript
var obj = '123';
return obj.asFloat();
//return obj.asFloat(1.0f); //转换失败时返回1.0f
```
## asLong
- 入参:`defaultValue`:`long` 选填,当转换失败时返回默认值,默认为`0L`
- 返回值:`long`
- 函数说明:转对象为`long`类型
```javascript
var obj = '123';
return obj.asLong();
//return obj.asLong(1L); //转换失败时返回1L
```
## asByte
- 入参:`defaultValue`:`byte` 选填,当转换失败时返回默认值,默认为`0b`
- 返回值:`byte`
- 函数说明:转对象为`byte`类型
```javascript
var obj = '123';
return obj.asByte();
//return obj.asByte(1b); //转换失败时返回1b
```
## asShort
- 入参:`defaultValue`:`short` 选填,当转换失败时返回默认值,默认为`0s`
- 返回值:`short`
- 函数说明:转对象为`short`类型
```javascript
var obj = '123';
return obj.asShort();
//return obj.asShort(1s); //转换失败时返回1s
```
## asDate
- 入参:`formats`:`String` 可变参数,日期格式
- 返回值:`Date`
- 函数说明:转对象为`Date`类型
```javascript
var obj = '2020-01-01 08:00:00';
return obj.asDate('yyyy-MM-dd HH:mm:ss','yyyy-MM-dd HH:mm');
```
## asString
- 入参:`defaultValue`:`String` 选填,当转换失败时返回默认值,默认为`null`
- 返回值:`String`
- 函数说明:转对象为`String`类型
```javascript
var obj = 123;
return obj.asString();
//return obj.asString("empty"); //转换失败时,返回"empty"
```
## is
- 入参:`type`:`String/Class` 判断是否该类型
- 返回值:`boolean`
- 函数说明:判断是否是指定类型
```javascript
import 'java.util.Date' as Date;
var str = 'hello,MagicAPI';
return str.is('string'); // true
return str.is('java.lang.String'); // true
return str.is('java.lang.Integer'); // false
return str.is(Date); // false
```
## 类型判断方法
- `isString()` - 判断是否是String类型
- `isInt()` - 判断是否是int类型
- `isLong()` - 判断是否是long类型
- `isDouble()` - 判断是否是double类型
- `isFloat()` - 判断是否是float类型
- `isByte()` - 判断是否是byte类型
- `isBoolean()` - 判断是否是boolean类型
- `isShort()` - 判断是否是short类型
- `isDecimal()` - 判断是否是decimal类型
- `isDate()` - 判断是否是Date类型
- `isArray()` - 判断是否是数组
- `isList()` - 判断是否是List
- `isMap()` - 判断是否是Map
- `isCollection()` - 判断是否是集合

View File

@ -0,0 +1,55 @@
# 其它函数
## print
- 入参:`target``Object` 要打印的对象
- 函数说明:打印
```javascript
print('abc'); // 等同于 System.out.print("abc");
```
## println
- 入参:`target``Object` 要打印的对象
- 函数说明:打印并换行
```javascript
println('abc'); // 等同于 System.out.println("abc");
```
## printf
- 入参:`format``String` 要打印的对象
- 入参:`target` `Object` 参数值,可以写多个
- 函数说明:按照格式打印并换行
```javascript
printf('%s:%s', 'a','b'); // 等同于 System.out.printf("%s:%S", "a", "b");
```
## not_null
- 入参:`target` : `Object` 判断的模板
- 返回值:`boolean`
- 函数说明:判断值不是`null`
```javascript
return not_null(target); // 等同于 target != null
```
## is_null
- 入参:`target` : `Object` 判断的模板
- 返回值:`boolean`
- 函数说明:判断值是`null`
```javascript
return is_null(target); // 等同于 target == null
```
## ifnull
- 入参:`target`:`Object` 判断的目标
- 入参:`trueValue`:`Object` 为空时的值
- 返回值:`Object`
- 函数说明:对空值进行判断,返回特定值
```javascript
return ifnull(null,1) // 1
// return ifnull(0,1) // 0
```

View File

@ -0,0 +1,32 @@
# 分页查询
## 自动分页
`db.page`可从形如`xxx?page=1&size=10`的url中获取分页参数。
```javascript
// 自动从请求参数中获取页码(默认为page)、页大小(默认为size)
return db.page("""
select * from sys_user
""")
```
## 手动分页
可手动传入分页参数。
```javascript
return db.page("""
select * from sys_user
""", 10, 20) // 跳过前20条查10条(limit, offset)
```
## 自定义分页参数
可根据需要在自己的项目中,调整以下分页参数。
```yaml
magic-api:
page:
size: size # 页大小的请求参数名称 缺省时为size
page: page # 页码的请求参数名称 缺省时为page
default-page: 1 # 自定义默认首页 缺省时为1
default-size: 10 # 自定义为默认页大小 缺省时为10
```

View File

@ -0,0 +1,13 @@
# Pattern扩展方法
`java.util.regex.Pattern`的扩展方法
## test
- 入参:`source`:`String` 目标字符串
- 返回值:`boolean`
- 函数说明:校验文本是否符合正则
```javascript
var regx = /^\d+$/;
return regx.test('123456') // true
```

View File

@ -0,0 +1,125 @@
# 增删改查
## SQL参数
### #{} 注入参数
作用和`mybatis`一致,都是将`#{}`区域替换为占位符`?`
```javascript
var id = 123;
return db.select("""
select * from sys_user where id = #{id}
""");
// 运行时生成的SQL为select * from sys_user where id = ?
```
### ${} 拼接参数
作用和`mybatis`一致,都是将`${}`区域替换为对应的字符串
```javascript
var id = 123;
return db.select("""
select * from sys_user where id = ${id}
""");
// 运行时生成的SQL为select * from sys_user where id = 123
```
## 动态SQL参数
通过`?{condition,expression}`来实现动态拼接`SQL`
```javascript
return db.select("select * from sys_user ?{id,where id = #{id}}");
// 当id有值时,生成SQLselect * from sys_user where id = ?
// 当id无值时,生成SQLselect * from sys_user
```
## 切换数据源
```javascript
// 从数据源key定义为slave的库中查询
return db.slave.select("""
select * from sys_user
""")
```
## SQL缓存
```javascript
// 将查询结果缓存到名为user_cache的缓存中有效期1小时
return db.cache("user_cache", 3600 * 1000).select("""
select * from sys_user
""")
```
## 使用事务
### 自动事务
```javascript
var val = db.transaction(()=>{
var v1 = db.update('...');
var v2 = db.update('....');
return v2;
});
return val;
```
### 手动事务
```javascript
var tx = db.transaction(); //开启事务
try{
var value = db.update('...');
tx.commit(); // 提交事务
return value;
}catch(e){
tx.rollback(); // 回滚事务
}
```
## Mybatis语法支持
### if
```javascript
var sql = """
select * from test_data
where 1 = 1
<if test="id != null">
and id = #{id}
</if>
"""
return db.select(sql)
```
### where
```javascript
var sql = """
select * from test_data
<where>
<if test="id != null">
and id = #{id}
</if>
</where>
"""
return db.select(sql)
```
### set、trim
```javascript
var sql = """
update test_data
<set>
<if test="name != null">
name = #{name}
</if>
</set>
where `id` = #{id}
"""
return db.update(sql)
```
### foreach
```javascript
var sql = """
select * from test_data
where id in
<foreach item='item' index='index' collection='body.ids'
open="(" separator="," close=")">
#{item}
</foreach>
"""
return db.select(sql)
```

View File

@ -0,0 +1,47 @@
# 请求参数获取
## RequestParam
```
GET http://localhost:9999/xxx/xxx?name=abc&age=49
```
这样的`URL`参数`magic-api` 会自动将`name`和`age`映射为同名变量。
## 表单参数
```
POST http://localhost:9999/xxx/xxx
name=abc&age=49
```
这样的表单参数`magic-api` 也会自动将`name`和`age`映射为同名变量。
## Request Header参数获取
`magic-api` 会对所有`RequestHeader`统一封装为一个名为`header`的变量
如要获取 `token` 可以通过`header.token` 来获取
## Request Body参数获取
对于`RequestBody` `magic-api`会将整个请求体映射为`body`变量,如:
```json
{
"name": "magic-api",
"version": "9.9.9"
}
```
如要获取`name`属性 则可通过 `body.name` 来获取
如果提交的body为数组或者List, `body`为数组。
## Path参数获取
主要是针对`URL`定义为`http://localhost:9999/user/{id}` 的类似接口
如要获取path路径上的id可通过`path.id` 或 `id`来获取。
对于请求时使用了`http://localhost:9999/user/1?id=2`的请求, `id`变量的值将是`RequestParam`中的值,此时可以通过`path.id` 来避免冲突。
## Cookie参数获取
`magic-api` 会对所有`Cookie`统一封装为一个名为`cookie`的对象。
如要获取 `JSESSIONID` 可以通过`cookie.JSESSIONID` 来获取。
## Session参数获取
`magic-api` 会将`HttpSession`封装为一个名为`session`的变量
要获取`session`中的值,可以通过`session.xxx`来获取
## 注意事项
如果脚本自定义变量和参数变量冲突,自定义变量优先。

View File

@ -0,0 +1,66 @@
# 快速入门
## 初始化工程
创建一个空的`Spring Boot`工程, 以`mysql`作为默认数据库进行演示。
## 添加依赖
引入`Spring Boot Starter`父工程:
```xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>spring-boot-latest-version</version>
<relativePath/>
</parent>
```
引入`magic-api-spring-boot-starter`依赖:
```xml
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-spring-boot-starter</artifactId>
<version>magic-api-lastest-version</version>
</dependency>
```
## 配置
`application.yml`
```yaml
server:
port: 9999
magic-api:
web: /magic/web
resource:
location: D:/data/magic-api
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/magic-api-test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: test
```
## 访问api管理界面
启动项目之后,访问`http://localhost:9999/magic/web` 即可看到Web页面
## 三分钟写出查询接口
**1. 创建分组**
点击创建分组按钮后,输入分组信息,点击创建。
**2. 新建接口**
右键分组,点击新建接口。在编辑器输入内容后,填写接口名称和及其路径。
```javascript
var sql = """
select * from test_data
"""
return db.select(sql)
```
`ctrl+s`保存后,即可访问接口。
**3.访问接口**
```
> curl http://localhost:9999/test/test
```

View File

@ -0,0 +1,64 @@
# Request模块
## 引用模块
```javascript
import request;
```
## getFile
- 入参:`name`:`string`
- 返回值:`MultipartFile`
- 函数说明:获取上传的文件
```javascript
import request;
request.getFile('image');
```
## getFiles
- 入参:`name`:`String`
- 返回值:`List<MultipartFile>`
- 函数说明:获取上传的文件集合
```javascript
import request;
request.getFiles('image');
```
## getValues
- 入参:`name`:`String`
- 返回值:`List<String>`
- 函数说明:获取提交的数组参数
```javascript
import request;
return request.getValues('name');
```
## getHeaders
- 入参:`name`:`String`
- 返回值:`List<String>`
- 函数说明获取请求的header数组
```javascript
import request;
return request.getHeaders('xxx');
```
## get
- 返回值:`MagicHttpServletRequest`
- 函数说明:获取`Request`对象
```javascript
import request;
request.get();
```
## getClientIP
- 返回值:`String`
- 函数说明:获取客户端`IP`
```javascript
import request;
return request.getClientIP();
```

View File

@ -0,0 +1,141 @@
# Response模块
## 引用模块
```javascript
import response;
```
## page
- 入参:`total`:`long`
- 入参:`values`:`list`
- 返回值:`Object`
- 函数说明:构建分页结果
```javascript
import response;
//返回: 共计10条第一页的5条数据
return response.page(10,[1,2,3,4,5]);
```
## json
- 入参:`value`:`Object`
- 返回值:`ResponseEntity`
- 函数说明构建Json结果
```javascript
import response;
//直接返回该json不会被包装处理
return response.json({
success : true,
message : '执行成功'
});
```
## text
- 入参:`value`:`String` 文本内容
- 返回值:`ResponseEntity`
- 函数说明:输出文本
```javascript
import response;
//直接返回该text不会被包装处理
return response.text('ok');
```
## redirect
- 入参:`url`:`String` 目标网址
- 返回值:`ResponseEntity`
- 函数说明:重定向
```javascript
import response;
//重定向到该地址内部利用HttpServletResponse的sendRedirect方法
return response.redirect('/xxx/xx');
```
## download
- 入参:`value`:`Object`
- 入参:`filename`:`文件名`
- 返回值:`ResponseEntity`
- 函数说明:下载文件
```javascript
import response;
return response.download('文件内容','test.txt');
```
## image
- 入参:`value`:`Object`
- 入参:`mine`:`String`
- 返回值:`ResponseEntity`
- 函数说明:主要用于输出图片
```javascript
import response;
// 输出图片
return response.image(bytes,'image/png');
```
## addHeader
- 入参:`key`:`string`
- 入参:`value`:`String`
- 返回值:无返回值
- 函数说明添加Response Header
```javascript
import response;
response.addHeader('AccessToken','123');
```
## setHeader
- 入参:`key`:`string`
- 入参:`value`:`String`
- 返回值:无返回值
- 函数说明设置Response Header
```javascript
import response;
response.setHeader('AccessToken','123');
```
## addCookie
- 入参:`key`:`string`
- 入参:`value`:`String`
- 入参:`options`:`Map` cookie参数可选
- 返回值:无返回值
- 函数说明添加Cookie
```javascript
import response;
response.addCookie('cookieKey','cookieValue');
response.addCookie('cookieKey','cookieValue',{
path : '/',
httpOnly : true,
domain : 'ssssssss.org',
maxAge : 3600
});
```
## addCookies
- 入参:`cookies`:`Map` cookie Map必填
- 入参:`options`:`Map` cookie参数可选
- 返回值:无返回值
- 函数说明批量添加Cookie
```javascript
import response;
response.addCookies({
cookieKey1 : 'cookieValue1',
cookieKey2 : 'cookieValue2',
});
```
## getOutputStream
- 返回值:`OutputStream`
- 函数说明:获取`ServletOutputStream`
注意:在调用`getOutputStream`后 返回值应为`response.end()` 告诉框架无需处理返回值。
## end
- 返回值:无返回值
- 函数说明取消返回默认的json结构通过其他方式的输出结果调用outputstream输出

View File

@ -0,0 +1,163 @@
# 脚本语法详解
## for循环
### 循环集合
```javascript
var list = [1,2,3];
for(index,item in list){ //如果不需要index也可以写成for(item in list)
println(index + ":" + item);
}
// 结果0:1, 1:2, 2:3
```
### 循环指定次数
```javascript
var sum = 0;
for(value in range(0,100)){ //包括0包括100
sum = sum + value; //不支持+= -= *= /= ++ -- 这种运算
}
return sum; // 5050
```
## while循环
```javascript
var count = 100;
var sum = 0;
while(count){
sum = sum + count;
count = count - 1;
}
return sum; // 5050
```
## 循环map
```javascript
var map = {
key1 : 123,
key2 : 456
};
for(key,value in map){ //如果不需要key也可以写成for(value in map)
println(key + ":" + value);
}
// 结果key1:123, key2:456
```
## Import导入
### 导入Java类
```javascript
import 'java.lang.System' as System;
import 'javax.sql.DataSource' as ds;
import 'org.apache.commons.lang3.StringUtils' as string;
import 'java.text.*' // 此写法跟Java一致
System.out.println('调用System打印');
System.out.println(ds);
System.out.println(string.isBlank(''));
System.out.println(new SimpleDateFormat('yyyy-MM-dd').format(new Date()));
```
### 导入已定义的模块
```javascript
import log; //导入log模块并定义一个与模块名相同的变量名
//import log as logger; //导入log模块并赋值给变量 logger
log.info('Hello {}','Magic API!')
```
## new创建对象
```javascript
import 'java.util.Date' as Date;//创建之前先导包,不支持.*的操作
return new Date();
```
## 异步调用
### 异步调用方法
```javascript
var val = async db.select('.....'); // 异步调用返回Future类型
return val.get(); //调用Future的get方法
```
### 异步调用lambda
```javascript
var list = [];
for(index in range(1,10)){
list.add(async (index)=>db.selectInt('select #{index}'));
}
return list.map(item=>item.get()); // 循环获取结果
```
## exit
语法格式为 `exit expr[,expr][,expr]....`
在`magic-api`中只取前三个值,分别对应`code`、`message`、`data`
如:`exit 400,'参数填写有误'`
## assert
语法格式为 `assert expr : expr[,expr][,expr]....`
如:`assert a == 1 : 400, 'a的值应为1'` 相当于
```javascript
if(a != 1){
exit 400, 'a的值应为1'
}
```
## 类型转换
通过`::`进行类型转换,如`xxx::int`、`xxx::double`等,当前支持转换类型有`int`、`double`、`long`、`byte`、`short`、`float`、`date`
```javascript
var a = "1";
return {
v1: a::int,
v2: a::int(0), //转换失败时值为0
v3: "2020-01-01"::date('yyyy-MM-dd') //转为Date
}
```
`::sql`支持将数据转换为对应的sql类型比如
```javascript
img::sql('blob')
```
可传入的参数请参考`java.sql.Types`中定义的常量,不区分大小写。
## 嵌入其它脚本语言
```javascript
var name = "hello";
var test = ```javascript
name + ' ~ world'
```;
return test();
```
## 可选链操作符
可选链操作符(`?.`)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
```javascript
var a = null;
var b = a?.name; // b = null;
var c = a?.getName(); // c = null;
```
## 扩展运算符
扩展运算符,又叫展开语法(Spread syntax)是用于将list或map在语法层面展开
### lambda 调用
```javascript
var sum = (a,b,c) => a + b + c;
System.out.println(sum(...[1,2,3])) // 结果6
```
### list 展开
```javascript
var arr = [3,4,5];
System.out.println([1,2,...arr,6,7]) // 结果:[1, 2, 3, 4, 5, 6, 7]
```
### map 展开
```javascript
var map = {key2:2}
System.out.println({key1:1,...map,key3:3}) // 结果:{key1=1, key2=2, key3=3}
```

View File

@ -0,0 +1,104 @@
# 单表操作
操作入口:`db.table('table_name')`
## logic
- 作用:设置本查询是带有逻辑删除的,在执行`delete`方法时,会转换为`update`语句,在执行`select`相关方法时,会拼接`logic_field <> logic_value`
## withBlank
- 作用:设置后续插入或修改时,不过滤空值。
## column
- 入参:`column`: `String` 列名
- 作用:设置要查询列,`select`语句中有效
## column
- 入参:`column`: `String` 列名
- 入参:`value` : `Object`
- 作用:设置要操作的列的值,非`select`语句中有效
## primary
- 入参:`primary`: `String` 主键
- 入参:`defaultValue`: `Object` 插入时使用的默认值,可省略
- 作用:设置主键列,在`update`中语句有效,或`save`方法判断标准
## insert
- 入参: `data` : `Map` `insert`的列和值,可省略(通过`column`设置)
```javascript
// insert into sys_user(user_name,role) values('李富贵','admin')
return db.table('sys_user').insert({ user_name : '李富贵', role : 'admin'})
```
## batchInsert
- 入参: `collection` : `Collection` `insert`的列和值的集合
- 入参: `batchSize` `int` batchSize
```javascript
return db.table('sys_user').batchInsert([
{ user_name : '李富贵', role : 'admin'},
{ user_name : '王二狗', role : 'admin'},
{ user_name : '管理员', role : 'super-admin'},
])
```
## update
- 入参: `data` : `Map` `insert`的列和值,可省略(通过`column`设置)
- 入参:`isUpdateBlank`: `boolean` 是否更新空值字段(可省略,默认为`false`
```javascript
// update sys_user set user_name = '王二狗' where id = 1
return db.table('sys_user').primary('id').update({ id: 1, user_name : '王二狗'})
```
## save
- 入参: `data` : `Map` `insert`或`update`的列和值,可省略(通过`column`设置)
- 入参:`beforeQuery` `boolean` 是否根据id查询有没有数据可省略(默认`false`)
```javascript
// insert into sys_user(id,user_name) values('xxx','王二狗');
return db.table('sys_user').primary('id', uuid()).save({user_name: '王二狗'});
// insert into sys_user(user_name) values('王二狗');
return db.table('sys_user').primary('id').save({user_name: '王二狗'});
// update sys_user set user_name = '王二狗' where id = 1
return db.table('sys_user').primary('id').save({id: 1,user_name: '王二狗'});
```
## select
查询list与db.select 作用相同)
```javascript
// select * from sys_user
return db.table('sys_user').select()
```
## page
分页查询与db.page 作用相同)
```javascript
// select * from sys_user
return db.table('sys_user').page()
```
## where
设置查询条件
- eq --> `==`
- ne --> `<>`
- lt --> `<`
- gt --> `>`
- lte --> `<=`
- gte --> `>=`
- in --> `in`
- notIn --> `not in`
- like --> `like`
- notLike --> `not like`
```javascript
// select * from sys_user where user_name like '%李富贵%' and role = 'admin'
return db.table('sys_user')
.where()
.like('user_name','%李富贵%')
.eq('role','admin')
.select()
```

View File

@ -0,0 +1,125 @@
# SQL参数
## #{} 注入参数
作用和`mybatis`一致,都是将`#{}`区域替换为占位符`?`
```javascript
var id = 123;
return db.select("""
select * from sys_user where id = #{id}
""");
// 运行时生成的SQL为select * from sys_user where id = ?
```
此方法可以避免`sql`注入。
## ${} 拼接参数
作用和`mybatis`一致,都是将`${}`区域替换为对应的字符串
```javascript
var id = 123;
return db.select("""
select * from sys_user where id = ${id}
""");
// 运行时生成的SQL为select * from sys_user where id = 123
```
## 动态SQL参数
通过`?{condition,expression}`来实现动态拼接`SQL`
```javascript
return db.select("select * from sys_user ?{id,where id = #{id}}");
// 当id有值时,生成SQLselect * from sys_user where id = ?
// 当id无值时,生成SQLselect * from sys_user
return db.select("select * from sys_user ?{id!=null&&id.length() > 3,where id = #{id}}");
```
## 循环拼接参数
两种办法:
### in语法自动展开
```javascript
var ids = [1,2,3,4,5,6];
//会自动变成select * from sys_user where id in(?,?,?,?,?,?)
return db.select('select * from sys_user where id in(#{ids})');
```
### 循环拼接SQL
```javascript
var list = [1,2,3,4,5];
var sql = "select * from sys_user where ";
for(index,item in list){
sql = sql + 'id = #{list['+index+']}';
if(index + 1 < list.size()){
sql = sql + ' or ';
}
}
return db.select(sql);
```
## Mybatis语法支持
### 支持的关键字
- `<if>`
- `<elseif>`
- `<else>`
- `<where>`
- `<foreach>`
- `<trim>`
- `<set>`
### if
```javascript
var sql = """
select * from test_data
where 1 = 1
<if test="id != null">
and id = #{id}
</if>
"""
return db.select(sql)
```
### where
```javascript
var sql = """
select * from test_data
<where>
<if test="id != null">
and id = #{id}
</if>
</where>
"""
return db.select(sql)
```
### set、trim
```javascript
var sql = """
update test_data
<set>
<if test="name != null">
name = #{name}
</if>
<if test="content != null">
content = #{content}
</if>
</set>
where `id` = #{id}
"""
return db.update(sql)
```
### foreach
```javascript
var sql = """
select * from test_data
where id in
<foreach item='item' index='index' collection='body.ids'
open="(" separator="," close=")">
#{item}
</foreach>
"""
return db.select(sql)
```

View File

@ -0,0 +1,26 @@
# 字符串函数
## uuid
- 返回值: `String` `32`位无`-`的`UUID`
```javascript
return uuid(); // 等同于 UUID.randomUUID().toString().replace("-", "");
```
## is_blank
- 入参:`target`:`String` 判断的目标
- 返回值:`boolean`
- 函数说明:判断字符串是否为空
```javascript
return is_blank(''); // true 等同于 StringUtils.isBlank 一致
```
## not_blank
- 入参:`target`:`String` 判断的目标
- 返回值:`boolean`
- 函数说明:判断字符串是否不为空
```javascript
return not_blank(''); // false 等同于 !is_blank('')
```

View File

@ -0,0 +1,22 @@
# 参数校验
## 自动验证
在magic-api的Web界面中可配置
- 必填验证
- 表达式验证
- 正则验证
## 手动验证
对于表达式和正则无法实现的可以通过脚本来实现。
```javascript
var count = db.selectInt("""
select count(*) from sys_user where phone = #{phone}
""")
// count 值应该为0如果不为0则验证不予通过。
assert count == 0 : 400, '手机号已存在';
// 上述写法可以转换为
if(count != 0){
exit 400, '手机号已存在'
}
```