【RuoYi-SpringBoot3-Pro】:Magic API 低代码开发

本文详细介绍 RuoYi-SpringBoot3-Pro[1] 框架中集成的 Magic API 低代码开发平台,帮助开发者快速构建 REST API 接口。

GitHub:https://github.com/undsky/RuoYi-SpringBoot3-Pro

一、什么是 Magic API?

Magic API[2] 是一个基于 Java 的接口快速开发框架,通过 Web 界面编写脚本即可完成接口开发,无需定义 Controller、Service、Mapper 等传统分层代码。

1.1 核心优势

特性说明
可视化开发通过 Web 界面编写接口,所见即所得
即时生效接口修改后无需重启,立即生效
脚本语言基于 Java 语法的脚本语言,学习成本低
数据库操作内置强大的数据库操作 API
多数据源支持动态切换多个数据源
参数校验内置参数校验功能
接口文档自动生成接口文档
版本控制支持接口历史记录和回滚

1.2 适用场景

二、项目集成

2.1 Maven 依赖

RuoYi-SpringBoot3-Pro 已集成 Magic API 2.2.2 版本:


    
    
    
  <!-- Magic API 核心依赖 -->
<dependency>

    <groupId>
org.ssssssss</groupId>
    <artifactId>
magic-api-spring-boot-starter</artifactId>
    <version>
2.2.2</version>
</dependency>


<!-- Magic API Redis 插件 -->

<dependency>

    <groupId>
org.ssssssss</groupId>
    <artifactId>
magic-api-plugin-redis</artifactId>
    <version>
2.2.2</version>
</dependency>

2.2 配置文件

application.yml 中配置 Magic API:


    
    
    
  # Magic API 配置
# 官方文档:https://www.ssssssss.org/magic-api/pages/config/spring-boot/

magic-api:

  # Web 编辑器访问路径(生产环境建议设为 null 禁用)

  web:
 /magic/web
  
  # 编辑器配置文件

  editor-config:
 classpath:./magic-editor-config.js
  
  # 接口存储方式

  resource:

    type:
 database           # 存储类型:database(数据库)/ file(文件)
    tableName:
 magic_api_file  # 存储表名
  
  # 接口访问前缀

  prefix:
 /magic/api
  
  # 编辑器登录认证

  security:

    username:
 jyx            # 登录用户名
    password:
 jyx_692483     # 登录密码
  
  # 历史记录备份

  backup:

    enable:
 true             # 是否启用备份
    max-history:
 -1          # 最大历史记录数(-1 表示不限制)
    table-name:
 magic_backup_record  # 备份表名
  
  # 响应结果格式

  response:
 |- 
    {
      code: code,
      msg: message,
      data,
    }

2.3 编辑器主题配置

magic-editor-config.js 文件配置编辑器主题:


    
    
    
  var MAGIC_EDITOR_CONFIG = {
    theme
: 'dark',  // 主题:dark(暗色)/ light(亮色)
}

2.4 安全配置

Magic API 接口默认放行,在 SecurityConfig.java 中配置:


    
    
    
  @Bean
public
 SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        .authorizeHttpRequests(auth -> auth
            // Magic API 接口放行

            .requestMatchers("/magic/**").permitAll()
            .anyRequest().authenticated()
        );
    return
 httpSecurity.build();
}

三、访问编辑器

3.1 访问地址

启动项目后,访问 Magic API 编辑器:


    
    
    
  http://localhost:8087/magic/web

3.2 登录认证

使用配置文件中设置的账号密码登录:

3.3 编辑器界面


    
    
    
  ┌─────────────────────────────────────────────────────────────┐
│  Magic API Editor                                    [用户] │
├─────────────┬───────────────────────────────────────────────┤
│             │                                               │
│  接口列表    │              脚本编辑区                        │
│             │                                               │
│  📁 分组1    │  // 编写接口脚本                              │
│    └─ 接口1  │  var list = db.select("""                    │
│    └─ 接口2  │      SELECT * FROM sys_user                  │
│  📁 分组2    │  """)                                        │
│    └─ 接口3  │  return list                                 │
│             │                                               │
├─────────────┼───────────────────────────────────────────────┤
│  接口配置    │              执行结果                          │
│  请求方式    │  [                                            │
│  请求路径    │    {"userId": 1, "userName": "admin"},       │
│  请求参数    │    {"userId": 2, "userName": "test"}         │
│             │  ]                                            │
└─────────────┴───────────────────────────────────────────────┘

四、接口开发

4.1 创建第一个接口

  1. 1. 点击左侧「+」创建分组
  2. 2. 在分组下创建接口
  3. 3. 配置请求方式和路径
  4. 4. 编写脚本代码
  5. 5. 点击「执行」测试
  6. 6. 点击「保存」发布

4.2 基础语法

Magic API 使用类 Java 语法的脚本语言:


    
    
    
  // 变量定义
var
 name = "张三"
var
 age = 18

// 条件判断

if
 (age >= 18) {
    return
 "成年人"
} else {
    return
 "未成年人"
}

// 循环

var
 list = [1, 2, 3, 4, 5]
for
 (item in list) {
    print
(item)
}

// 函数定义

var
 add = (a, b) => a + b
return
 add(1, 2)

4.3 获取请求参数


    
    
    
  // 获取 Query 参数:/api/user?name=张三&age=18
var
 name = name        // 直接使用参数名
var
 age = age

// 获取 Path 参数:/api/user/{id}

var
 userId = path.id

// 获取 Body 参数(JSON)

var
 user = body
var
 userName = body.userName

// 获取 Header

var
 token = header.Authorization

// 获取完整请求对象

var
 request = request

4.4 数据库操作

Magic API 内置强大的数据库操作 API:


    
    
    
  // 查询列表
var
 list = db.select("""
    SELECT * FROM sys_user WHERE status = '0'
"
"")

// 查询单条

var
 user = db.selectOne("""
    SELECT * FROM sys_user WHERE user_id = #{userId}
"
"")

// 带参数查询

var
 users = db.select("""
    SELECT * FROM sys_user 
    WHERE dept_id = #{deptId} 
    AND user_name LIKE CONCAT('%', #{keyword}, '%')
"
"")

// 分页查询

var
 page = db.page("""
    SELECT * FROM sys_user WHERE status = '0'
"
"")

// 插入数据

var
 result = db.insert("""
    INSERT INTO sys_user (user_name, nick_name, email) 
    VALUES (#{userName}, #{nickName}, #{email})
"
"")

// 更新数据

var
 result = db.update("""
    UPDATE sys_user SET nick_name = #{nickName} WHERE user_id = #{userId}
"
"")

// 删除数据

var
 result = db.delete("""
    DELETE FROM sys_user WHERE user_id = #{userId}
"
"")

4.5 链式查询


    
    
    
  // 使用链式 API 构建查询
var
 list = db.table('sys_user')
    .where()
    .eq('status', '0')
    .like('user_name', keyword)
    .orderBy('create_time desc')
    .page(pageNum, pageSize)
    .select()

// 插入

db.table('sys_user').insert({
    userName
: '张三',
    nickName
: '小张',
    email
: 'zhangsan@example.com'
})

// 更新

db.table('sys_user')
    .where()
    .eq('user_id', 1)
    .update({
        nickName
: '新昵称'
    })

// 删除

db.table('sys_user')
    .where()
    .eq('user_id', 1)
    .delete()

4.6 多数据源


    
    
    
  // 切换数据源
var
 list = db.slave.select("""
    SELECT * FROM other_table
"
"")

// 或使用 datasource 指定

var
 list = db.datasource('slave').select("""
    SELECT * FROM other_table
"
"")

五、接口鉴权

5.1 鉴权拦截器

RuoYi-SpringBoot3-Pro 实现了 Magic API 的请求拦截器,支持登录验证和权限校验:


    
    
    
  @Component
public
 class MagicApiRequestInterceptor implements RequestInterceptor {
    
    @Override

    public
 Object preHandle(ApiInfo info, MagicScriptContext context, 
            MagicHttpServletRequest request, MagicHttpServletResponse response)
 {
        
        // 获取接口配置的选项

        boolean
 needLogin = StringUtils.equals("true", info.getOptionValue(Options.REQUIRE_LOGIN));
        String
 role = info.getOptionValue(Options.ROLE);
        String
 permission = info.getOptionValue(Options.PERMISSION);
        
        // 需要角色或权限时,自动要求登录

        if
 (StringUtils.isNotBlank(role) || StringUtils.isNotBlank(permission)) {
            needLogin = true;
        }
        
        // 登录校验

        if
 (needLogin) {
            try
 {
                SecurityUtils.getLoginUser();
            } catch (Exception e) {
                return
 new JsonBean<>(401, "用户未登录");
            }
        }
        
        // 角色校验

        if
 (StringUtils.isNotBlank(role)) {
            if
 (!SecurityUtils.hasRole(role)) {
                return
 new JsonBean<>(403, "用户权限不足");
            }
        }
        
        // 权限校验

        if
 (StringUtils.isNotBlank(permission)) {
            if
 (!SecurityUtils.hasPermi(permission)) {
                return
 new JsonBean<>(403, "用户权限不足");
            }
        }
        
        return
 null;  // 返回 null 表示放行
    }
}

5.2 配置接口权限

在 Magic API 编辑器中,为接口配置权限选项:

选项说明示例值
require_login是否需要登录true / false
role需要的角色admin
permission需要的权限system:user:list

配置方式:

  1. 1. 在编辑器中选择接口
  2. 2. 点击「选项」标签
  3. 3. 添加对应的选项配置

5.3 获取当前用户

在脚本中获取当前登录用户信息:


    
    
    
  import com.ruoyi.common.utils.SecurityUtils

// 获取当前用户 ID

var
 userId = SecurityUtils.getUserId()

// 获取当前用户名

var
 username = SecurityUtils.getUsername()

// 获取当前用户信息

var
 loginUser = SecurityUtils.getLoginUser()
var
 user = loginUser.getUser()

六、Redis 缓存

6.1 基础操作

Magic API 集成了 Redis 插件,可直接使用:


    
    
    
  // 设置缓存
redis.set('key', 'value')

// 设置缓存(带过期时间,单位秒)

redis.set('key', 'value', 3600)

// 获取缓存

var
 value = redis.get('key')

// 删除缓存

redis.del('key')

// 判断是否存在

var
 exists = redis.exists('key')

// 设置过期时间

redis.expire('key', 3600)

6.2 接口缓存示例


    
    
    
  // 先从缓存获取
var
 cacheKey = 'user:list:' + deptId
var
 list = redis.get(cacheKey)

if
 (list == null) {
    // 缓存不存在,查询数据库

    list = db.select("""
        SELECT * FROM sys_user WHERE dept_id = #{deptId}
    "
"")
    // 存入缓存,1小时过期

    redis.set(cacheKey, list, 3600)
}

return
 list

6.3 Hash 操作


    
    
    
  // 设置 Hash 字段
redis.hset('user:1', 'name', '张三')
redis.hset('user:1', 'age', 18)

// 获取 Hash 字段

var
 name = redis.hget('user:1', 'name')

// 获取所有字段

var
 user = redis.hgetAll('user:1')

// 删除 Hash 字段

redis.hdel('user:1', 'name')

七、高级特性

7.1 事务支持


    
    
    
  import db

// 开启事务

db.transaction(() => {
    db.update("""
        UPDATE account SET balance = balance - 100 WHERE user_id = 1
    "
"")
    db.update("""
        UPDATE account SET balance = balance + 100 WHERE user_id = 2
    "
"")
})

7.2 异步执行


    
    
    
  import async

// 异步执行

async
.run(() => {
    // 耗时操作

    db.insert("""
        INSERT INTO log (content) VALUES ('异步日志')
    "
"")
})

return
 "请求已接收"

7.3 HTTP 请求


    
    
    
  import http

// GET 请求

var
 result = http.get('https://api.example.com/users')

// POST 请求

var
 result = http.post('https://api.example.com/users', {
    body
: {
        name
: '张三',
        age
: 18
    }
})

// 带 Header 的请求

var
 result = http.get('https://api.example.com/users', {
    headers
: {
        'Authorization'
: 'Bearer token'
    }
})

7.4 参数校验


    
    
    
  import assert

// 非空校验

assert.notNull(userId, '用户ID不能为空')
assert.notBlank(userName, '用户名不能为空')

// 条件校验

assert.isTrue(age >= 18, '年龄必须大于等于18')

// 正则校验

assert.regx(email, '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', '邮箱格式不正确')

7.5 日志输出


    
    
    
  import log

log.info('这是一条信息日志')
log.warn('这是一条警告日志')
log.error('这是一条错误日志')
log.debug('这是一条调试日志')

八、实战示例

8.1 用户列表接口


    
    
    
  /**
 * 接口路径:GET /magic/api/user/list
 * 请求参数:pageNum, pageSize, userName, status
 */


// 参数处理

var
 pageNum = pageNum ?: 1
var
 pageSize = pageSize ?: 10

// 构建查询

var
 query = db.table('sys_user')
    .where()
    .eq('del_flag', '0')

// 动态条件

if
 (userName) {
    query.like('user_name', userName)
}
if
 (status) {
    query.eq('status', status)
}

// 分页查询

var
 page = query.orderBy('create_time desc')
    .page(pageNum, pageSize)
    .select()

return
 page

8.2 用户详情接口


    
    
    
  /**
 * 接口路径:GET /magic/api/user/{id}
 * 路径参数:id
 */


import
 assert

// 参数校验

assert.notNull(path.id, '用户ID不能为空')

// 查询用户

var
 user = db.selectOne("""
    SELECT u.*, d.dept_name 
    FROM sys_user u
    LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
    WHERE u.user_id = #{path.id} AND u.del_flag = '0'
"
"")

if
 (user == null) {
    return
 {
        code
: 404,
        msg
: '用户不存在'
    }
}

return
 user

8.3 新增用户接口


    
    
    
  /**
 * 接口路径:POST /magic/api/user
 * 请求体:userName, nickName, email, phonenumber, deptId
 */


import
 assert
import
 com.ruoyi.common.utils.SecurityUtils

// 参数校验

assert.notBlank(body.userName, '用户名不能为空')
assert.notBlank(body.nickName, '昵称不能为空')

// 检查用户名是否存在

var
 exists = db.selectOne("""
    SELECT 1 FROM sys_user WHERE user_name = #{body.userName} AND del_flag = '0'
"
"")
if
 (exists) {
    return
 {
        code
: 500,
        msg
: '用户名已存在'
    }
}

// 插入用户

var
 result = db.insert("""
    INSERT INTO sys_user (user_name, nick_name, email, phonenumber, dept_id, 
        password, create_by, create_time)
    VALUES (#{body.userName}, #{body.nickName}, #{body.email}, #{body.phonenumber}, 
        #{body.deptId}, #{SecurityUtils.encryptPassword('123456')}, 
        #{SecurityUtils.getUsername()}, NOW())
"
"")

return
 {
    code
: 200,
    msg
: '新增成功'
}

8.4 统计报表接口


    
    
    
  /**
 * 接口路径:GET /magic/api/report/user-stats
 * 请求参数:startDate, endDate
 */


// 用户统计

var
 stats = db.selectOne("""
    SELECT 
        COUNT(*) as totalCount,
        SUM(CASE WHEN status = '0' THEN 1 ELSE 0 END) as activeCount,
        SUM(CASE WHEN status = '1' THEN 1 ELSE 0 END) as disabledCount
    FROM sys_user 
    WHERE del_flag = '0'
"
"")

// 部门分布

var
 deptStats = db.select("""
    SELECT d.dept_name, COUNT(u.user_id) as userCount
    FROM sys_dept d
    LEFT JOIN sys_user u ON d.dept_id = u.dept_id AND u.del_flag = '0'
    WHERE d.del_flag = '0'
    GROUP BY d.dept_id, d.dept_name
    ORDER BY userCount DESC
    LIMIT 10
"
"")

// 注册趋势

var
 trend = db.select("""
    SELECT DATE(create_time) as date, COUNT(*) as count
    FROM sys_user
    WHERE del_flag = '0'
    AND create_time BETWEEN #{startDate} AND #{endDate}
    GROUP BY DATE(create_time)
    ORDER BY date
"
"")

return
 {
    stats
: stats,
    deptStats
: deptStats,
    trend
: trend
}

九、生产环境配置

9.1 禁用编辑器

生产环境建议禁用 Web 编辑器:


    
    
    
  magic-api:
  web:
 null  # 设为 null 禁用编辑器

9.2 修改默认密码


    
    
    
  magic-api:
  security:

    username:
 your_username
    password:
 your_strong_password

9.3 接口访问控制

通过 Spring Security 控制 Magic API 接口访问:


    
    
    
  .requestMatchers("/magic/api/**").authenticated()  // 需要登录
.requestMatchers("/magic/web/**").hasRole("ADMIN") // 仅管理员

十、常见问题

10.1 接口不生效

10.2 数据库连接失败

10.3 权限校验失败

十一、总结

RuoYi-SpringBoot3-Pro 集成的 Magic API 具有以下特点:

Magic API 适合快速开发简单接口,与传统 Controller 开发方式互补,可根据实际需求灵活选择。


引用链接

[1] RuoYi-SpringBoot3-Pro: https://github.com/undsky/RuoYi-SpringBoot3-Pro
[2] Magic API: https://www.ssssssss.org/magic-api/pages/quick/intro/
[3] RuoYi-SpringBoot3-Pro 文档: https://www.undsky.com/blog/?category=RuoYi-SpringBoot3-Pro#