Magic API 生产环境实战指南

1. 框架定位与生产准入评估

1.1 什么是 Magic API

Magic API 是一个基于 Java 的接口快速开发框架,通过 Web UI 编写脚本(magic-script),自动映射为 HTTP 接口。保存即生效,无需重启、无需编译、无需定义 Controller/Service/Mapper

1.2 生产环境适用场景 ✅

场景 说明
运营后台 / 管理端接口 迭代频繁、逻辑相对简单、对性能要求不极端
报表查询 / 数据看板 复杂 SQL 直接在脚本中编写,避免大量 Mapper XML
第三方对接 / 网关转发 快速适配外部接口格式变更
原型验证 / MVP 天级别交付验证
动态配置 / 规则引擎 业务规则频繁调整,不想每次发版

1.3 不建议使用的场景 ❌

  • 核心交易链路(支付、下单等对稳定性和性能要求极高的场景)
  • 复杂领域模型驱动的业务(DDD 架构)
  • 需要严格单元测试覆盖率的项目(动态脚本难以做传统单测)
  • 团队对动态脚本完全陌生且无专人维护

2. 生产级环境搭建

2.1 Maven 依赖

xml

复制代码
1<!-- Magic API 核心 -->
2<dependency>
3    <groupId>org.ssssssss</groupId>
4    <artifactId>magic-api-spring-boot-starter</artifactId>
5    <version>2.1.0</version> <!-- 务必使用最新稳定版 -->
6</dependency>
7
8<!-- 数据库驱动(按需) -->
9<dependency>
10    <groupId>com.mysql</groupId>
11    <artifactId>mysql-connector-j</artifactId>
12    <scope>runtime</scope>
13</dependency>
14
15<!-- Redis(生产环境强烈建议引入,用于缓存和分布式锁) -->
16<dependency>
17    <groupId>org.springframework.boot</groupId>
18    <artifactId>spring-boot-starter-data-redis</artifactId>
19</dependency>

2.2 生产环境配置 (application-prod.yml)

yaml

编辑

复制代码
1magic-api:
2  web: /magic/web                    # ⚠️ 生产环境务必加鉴权或改路径
3  resource:
4    type: database                   # ⚠️ 生产必须用DB存储,不要用file
5    table-name: magic_api_resource   # 脚本存储表
6  backup:
7    enable: true                     # 开启备份
8    max-history: 30                  # 保留30天历史版本
9  response:                          # 统一响应格式
10    success-code: 200
11    exception-message-key: message
12  page:                              # 分页参数名统一
13    page: pageNum
14    size: pageSize
15  security:                          # ⚠️ 安全配置(关键!)
16    username: ${MAGIC_API_ADMIN}     # 从环境变量读取,不要硬编码
17    password: ${MAGIC_API_PWD}
18    token: ${MAGIC_API_TOKEN}        # Token验证
19    ip-white-list: 10.0.0.0/8,172.16.0.0/12  # 仅内网IP可访问UI

2.3 初始化数据库表

resource.type=database 时,Magic API 会自动建表。如需手动创建:

sql

复制代码
1CREATE TABLE `magic_api_resource` (
2  `id` varchar(32) NOT NULL,
3  `create_time` bigint NOT NULL,
4  `update_time` bigint NOT NULL,
5  `path` varchar(512) DEFAULT NULL,
6  `method` varchar(16) DEFAULT NULL,
7  `script` text,
8  `name` varchar(256) DEFAULT NULL,
9  `group_id` varchar(32) DEFAULT NULL,
10  PRIMARY KEY (`id`)
11) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. 核心语法速查与实战示例

3.1 基础 CRUD

GET 分页查询

javascript

复制代码
1// 路径: GET /api/user/list
2var status = request.getParameter('status', 1)
3var keyword = request.getParameter('keyword')
4
5return db.page('''
6    select id, username, phone, age, create_time
7    from t_user
8    where status = ?
9      <if test="keyword != null and keyword != ''">
10        and (username like concat('%', #{keyword}, '%') 
11             or phone like concat('%', #{keyword}, '%'))
12      </if>
13    order by create_time desc
14''', status, keyword)
POST 新增 + 参数校验

javascript

复制代码
1// 路径: POST /api/user/add
2var body = request.getBody()
3
4// ✅ 生产级校验:失败自动返回错误信息
5assert.isNotNull(body.username, '用户名不能为空')
6assert.isTrue(body.username.length() >= 2 && body.username.length() <= 32, '用户名长度2-32位')
7assert.matches(body.phone, '^1[3-9]\\d{9}$', '手机号格式不正确')
8
9// 检查唯一性
10var exists = db.selectInt('select count(*) from t_user where username = ?', body.username)
11assert.isTrue(exists == 0, '用户名已存在')
12
13var rows = db.insert('''
14    insert into t_user(username, phone, age, status, create_time)
15    values(?, ?, ?, 1, now())
16''', body.username, body.phone, body.age)
17
18return { id: db.lastInsertId(), affectedRows: rows }

3.2 事务处理

javascript

复制代码
1// 路径: POST /api/order/create
2var body = request.getBody()
3
4return db.transaction(() => {
5    // 扣减库存(乐观锁)
6    var stockRows = db.update('''
7        update t_product set stock = stock - ?
8        where id = ? and stock >= ?
9    ''', body.quantity, body.productId, body.quantity)
10    assert.isTrue(stockRows > 0, '库存不足或商品不存在')
11
12    // 创建订单
13    db.insert('''
14        insert into t_order(order_no, product_id, quantity, user_id, create_time)
15        values(?, ?, ?, ?, now())
16    ''', UUID.randomUUID().toString(), body.productId, body.quantity, body.userId)
17
18    return { orderId: db.lastInsertId() }
19})
20// ⚠️ lambda 内任何异常自动回滚,无需手动 rollback

3.3 Redis 缓存模式

javascript

复制代码
1// 路径: GET /api/config/{key}
2var cacheKey = 'sys:config:' + pathVariable.key
3
4// Cache-Aside 模式
5var cached = redis.get(cacheKey)
6if (cached != null) {
7    return JSON.parse(cached)
8}
9
10var config = db.selectOne('select * from sys_config where config_key = ?', pathVariable.key)
11if (config != null) {
12    redis.set(cacheKey, JSON.stringify(config), 3600) // TTL 1小时
13}
14return config

3.4 模块化复用

在 UI 左侧 "模块" 中新建 user-utils

javascript

复制代码
1// 模块: user-utils
2function checkUserExists(userId) {
3    var count = db.selectInt('select count(*) from t_user where id = ?', userId)
4    assert.isTrue(count > 0, '用户不存在')
5}
6
7function formatUserVO(user) {
8    user.phoneMask = user.phone.replaceAll('(\\d{3})\\d{4}(\\d{4})', '$1****$2')
9    delete user.password
10    return user
11}

在接口中引用:

javascript

复制代码
1import user_utils as userUtil
2
3userUtil.checkUserExists(body.userId)
4var user = db.selectOne('select * from t_user where id = ?', body.userId)
5return userUtil.formatUserVO(user)

4. ⚠️ 生产安全规范(必读)

4.1 访问控制

表格

措施 实现方式
IP 白名单 security.ip-white-list 限制仅内网访问
Token 验证 配置 security.token,请求头携带 Magic-Token
Nginx 层拦截 生产 Nginx 禁止 对外暴露 /magic/web 路径
操作审计 实现 MagicAPIRequestInterceptor 记录所有修改操作

4.2 SQL 注入防护

  • 始终使用 ? 占位符,Magic API 底层使用 PreparedStatement
  • 严禁字符串拼接 SQL'select * from user where name = "' + name + '"'
  • ✅ 动态条件使用 <if> 标签,而非字符串拼接

4.3 敏感数据处理

  • 数据库密码、Redis 密码等通过环境变量注入,不写入脚本
  • 日志中脱敏:logger.info('用户{}登录', phoneMask)
  • 响应中删除敏感字段:delete user.password

5. 运维与监控

5.1 版本管理与发布

  • 开发环境:type=file + Git 管理脚本文件
  • 生产环境:type=database,通过 UI 的 "导出/导入" 功能同步
  • 推荐流程:开发 → Git Review → 导出 JSON → 生产导入 → 验证

5.2 监控指标

Magic API 内置 Actuator 端点,接入 Prometheus/Grafana:

yaml

复制代码
1management:
2  endpoints:
3    web:
4      exposure:
5        include: magic-api

关注指标:接口调用次数、平均耗时、错误率、慢脚本告警。

5.3 日志规范

javascript

复制代码
1// ✅ 使用占位符,避免字符串拼接
2logger.info('查询用户列表, pageNum={}, size={}, keyword={}', pageNum, size, keyword)
3
4// ✅ 异常日志带上下文
5try {
6    // 业务逻辑
7} catch(e) {
8    logger.error('创建订单失败, userId={}, productId={}', body.userId, body.productId, e)
9    throw e  // 重新抛出,让全局异常处理器返回统一错误格式
10}

6. 常见问题排查清单

表格

现象 排查方向
保存后接口未生效 检查是否点击保存;确认 DB 连接正常;查看启动日志有无报错
SQL 执行报错 检查 ? 数量与参数是否匹配;在 UI 中使用"调试"功能预览实际 SQL
性能突然下降 检查是否缺少索引;是否循环查库(应改为批量查询);Redis 是否穿透
内存溢出 检查是否在脚本中缓存了大量数据;避免在循环中创建大对象
并发问题 脚本变量是请求级隔离的,但共享模块中的全局变量需注意线程安全

7. 学习资源

最后提醒: Magic API 是利器但不是银弹。建议先在非核心业务试点,团队熟悉后再逐步推广。建立代码审查机制,定期 Review 线上脚本质量,避免"方便"变成"灾难"。