Skip to content

事件+Magic-API

本页面介绍如何将事件推送与Magic API结合使用,实现完整的二次开发方案。

组合原理

┌─────────────┐     ┌─────────────┐     ┌─────────────────────────┐
│  MISEB系统   │     │   Redis     │     │   MagicEventProcessor  │
│  业务操作    │ ──→ │   Channel   │ ──→ │   事件处理器            │
└─────────────┘     └─────────────┘     └───────────┬─────────────┘

                    ┌───────────────────────────────┘


        ┌───────────────────────────────────────────────────────┐
        │                    Magic API                          │
        │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐   │
        │  │ 订单同步ERP │  │ 用户同步CRM │  │ 库存同步WMS │   │
        │  └─────────────┘  └─────────────┘  └─────────────┘   │
        └───────────────────────────────────────────────────────┘

                    ┌───────────────┼───────────────┐
                    ▼               ▼               ▼
            ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
            │   ERP系统    │ │   CRM系统    │ │   WMS系统    │
            └──────────────┘ └──────────────┘ └──────────────┘

工作流程

  1. 事件产生:系统业务操作(下单、支付等)触发事件
  2. 事件发布:事件发布到Redis Channel(magic-api:event
  3. 事件监听MagicEventProcessor接收并解析事件
  4. 处理器匹配:查询sys_event_handler表,找到对应的Magic API
  5. 执行脚本:调用配置的Magic API脚本处理事件
  6. 日志记录:处理结果记录到Elasticsearch

配置步骤

步骤1:创建Magic API接口

在Magic API中创建事件处理接口:

javascript
// 接口路径: /custom/erp/order-sync
// 方法: POST

// 1. 获取事件数据
var event = payload.event;
var eventKey = event.eventKey;
var orderNo = event.payload;

log.info("收到事件: eventKey={}, orderNo={}", eventKey, orderNo);

// 2. 查询订单详情
var order = db.selectOne(`
    SELECT o.*, u.phone, u.nickname
    FROM eb_store_order o
    LEFT JOIN eb_user u ON o.uid = u.uid
    WHERE o.order_id = ?
`, orderNo);

if (!order) {
    log.error("订单不存在: {}", orderNo);
    return { success: false, message: "订单不存在" };
}

// 3. 查询订单商品
var products = db.select(`
    SELECT product_id, product_name, sku, cart_num, price
    FROM eb_store_order_info
    WHERE order_no = ?
`, orderNo);

// 4. 转换为ERP格式并调用
var erpOrder = {
    sourceOrderNo: order.order_id,
    sourceSystem: "MISEB",
    customerId: "MISEB_" + order.uid,
    payAmount: order.pay_price / 100,
    items: products.map(p => ({
        skuCode: "MISEB_" + p.product_id,
        productName: p.product_name,
        quantity: p.cart_num
    }))
};

var result = http.post(env.get('ERP_API_URL') + '/order/create', {
    headers: { 'Authorization': 'Bearer ' + env.get('ERP_API_KEY') },
    body: erpOrder
});

// 5. 处理结果
if (result.code == 0) {
    log.info("ERP同步成功: orderNo={}", orderNo);
    return { success: true };
} else {
    log.error("ERP同步失败: orderNo={}, error={}", orderNo, result.message);
    throw new Error(result.message);  // 抛出异常会记录到日志
}

步骤2:配置事件处理器

在MISEB平台管理后台配置:

菜单路径:系统设置 → 事件处理器 → 新增

配置项
事件标识ORDER_PAY_SUCCESS
处理器名称订单支付成功-同步ERP
Magic API/custom/erp/order-sync
执行方式异步
状态启用

步骤3:配置环境变量

在Magic API环境变量中配置外部系统信息:

ERP_API_URL = https://erp.company.com/api
ERP_API_KEY = your_erp_api_key

完整示例:订单同步ERP

需求说明

订单支付成功后,自动同步到ERP系统,包括:

  • 订单基本信息
  • 订单商品信息
  • 收货地址信息

实现代码

javascript
// 接口路径: /custom/erp/order-sync
// 用于处理 ORDER_PAY_SUCCESS 事件

// ============ 获取事件数据 ============
var event = payload.event;
var orderNo = event.payload;

log.info("处理订单支付成功事件: orderNo={}", orderNo);

// ============ 查询订单详情 ============
var order = db.selectOne(`
    SELECT o.*, m.mer_name, u.nickname, u.phone
    FROM eb_store_order o
    LEFT JOIN eb_merchant m ON o.mer_id = m.id
    LEFT JOIN eb_user u ON o.uid = u.uid
    WHERE o.order_id = ?
`, orderNo);

if (!order) {
    log.error("订单不存在: {}", orderNo);
    return { success: false, message: "订单不存在" };
}

// ============ 查询订单商品 ============
var products = db.select(`
    SELECT oi.*, p.bar_code
    FROM eb_store_order_info oi
    LEFT JOIN eb_store_product p ON oi.product_id = p.id
    WHERE oi.order_no = ?
`, orderNo);

// ============ 查询收货地址 ============
var address = db.selectOne(`
    SELECT * FROM eb_store_order_address WHERE order_no = ?
`, orderNo);

// ============ 转换为ERP格式 ============
var erpOrder = {
    // 订单信息
    sourceOrderNo: order.order_id,
    sourceSystem: "MISEB",
    merchantId: "MER_" + order.mer_id,
    merchantName: order.mer_name,

    // 客户信息
    customerId: "MISEB_" + order.uid,
    customerName: order.nickname,
    customerPhone: order.phone,

    // 金额信息
    totalAmount: order.total_price / 100,
    payAmount: order.pay_price / 100,
    freightAmount: order.freight_price / 100,
    discountAmount: order.coupon_price / 100,

    // 支付信息
    payType: order.pay_type,
    payTime: order.pay_time,
    transactionId: order.transaction_id,

    // 收货地址
    shippingAddress: {
        name: address.real_name,
        phone: address.user_phone,
        province: address.province,
        city: address.city,
        district: address.district,
        detail: address.detail,
        fullAddress: address.province + address.city + address.district + address.detail
    },

    // 商品列表
    items: products.map(p => ({
        skuCode: p.bar_code || ("MISEB_" + p.product_id),
        productId: p.product_id,
        productName: p.product_name,
        spec: p.sku,
        quantity: p.cart_num,
        price: p.price / 100,
        amount: (p.price * p.cart_num) / 100
    })),

    // 备注
    remark: order.mark || ""
};

// ============ 调用ERP接口 ============
try {
    var result = http.post(env.get('ERP_API_URL') + '/order/create', {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + env.get('ERP_API_KEY')
        },
        body: erpOrder,
        timeout: 15000
    });

    if (result.code == 0 || result.code == 200) {
        // 记录ERP订单号到备注
        var erpOrderId = result.data?.orderId || result.data?.order_id;
        if (erpOrderId) {
            db.update(`
                UPDATE eb_store_order
                SET mark = CONCAT(IFNULL(mark, ''), ' [ERP:', ?, ']')
                WHERE order_id = ?
            `, erpOrderId, orderNo);
        }

        log.info("订单同步ERP成功: orderNo={}, erpOrderId={}", orderNo, erpOrderId);
        return { success: true, erpOrderId: erpOrderId };
    } else {
        log.error("订单同步ERP失败: orderNo={}, error={}", orderNo, result.message);
        throw new Error("ERP接口返回错误: " + result.message);
    }
} catch (e) {
    log.error("订单同步ERP异常: orderNo={}, error={}", orderNo, e.message);
    throw e;
}

多事件处理

可以为同一个事件配置多个处理器:

事件标识处理器名称Magic API
ORDER_PAY_SUCCESS同步ERP/custom/erp/order-sync
ORDER_PAY_SUCCESS发送通知/custom/notify/order-paid
ORDER_PAY_SUCCESS积分奖励/custom/points/order-reward

每个处理器都会被执行,互不影响。

通过FRP对接内网系统

结合FRP实现对接内网ERP/WMS:

javascript
// 通过FRP访问内网ERP
// FRP配置: localIP=192.168.1.10, localPort=8080, remotePort=16001

var result = http.post('http://FRP服务器IP:16001/api/order/create', {
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + env.get('ERP_TOKEN')
    },
    body: erpOrder
});

或者配置Magic API数据源直接操作内网数据库:

yaml
# application.yml
magic-api:
  datasource:
    erp-db:
      url: jdbc:mysql://FRP服务器IP:16003/erp_database
      username: readonly_user
      password: password
javascript
// 直接查询内网ERP数据库
var erpProducts = db['erp-db'].select(`
    SELECT sku_code, stock FROM products WHERE sku_code IN (?)
`, skuCodes);

事件日志查询

处理结果会自动记录到Elasticsearch:

菜单路径:系统监控 → 事件日志

可以查看:

  • 事件标识和消息内容
  • 处理器名称
  • 执行状态(SUCCESS/FAIL)
  • 错误信息
  • 处理耗时

最佳实践

1. 异步处理

对于调用外部API等耗时操作,配置为异步执行:

执行方式: 异步

2. 错误处理

使用try-catch捕获异常,抛出的异常会记录到日志:

javascript
try {
    var result = http.post(url, options);
    if (result.code != 0) {
        throw new Error("接口返回错误: " + result.message);
    }
    return { success: true };
} catch (e) {
    log.error("处理失败: {}", e.message);
    throw e;  // 抛出异常,记录到事件日志
}

3. 幂等设计

部分事件可能重复触发,做好幂等处理:

javascript
// 检查是否已同步
var exists = db.selectInt(`
    SELECT COUNT(*) FROM sync_log
    WHERE order_no = ? AND sync_type = 'erp'
`, orderNo);

if (exists > 0) {
    log.info("订单已同步,跳过: orderNo={}", orderNo);
    return { success: true, message: "已同步" };
}

// 同步处理...

// 记录同步日志
db.insert(`
    INSERT INTO sync_log (order_no, sync_type, sync_time)
    VALUES (?, 'erp', NOW())
`, orderNo);

4. 使用环境变量

敏感配置使用环境变量:

javascript
var apiUrl = env.get('ERP_API_URL');
var apiKey = env.get('ERP_API_KEY');

5. 详细日志

记录关键步骤日志,便于问题排查:

javascript
log.info("开始处理: orderNo={}", orderNo);
log.info("查询订单完成: order={}", order);
log.info("调用ERP接口: url={}", apiUrl);
log.info("ERP返回结果: result={}", result);

下一步

成都艾唯特软件有限公司