Norai诺睿矩阵

API 集成文档

版本 v1.0  |  更新日期:2026年8月4日

1. 概述

Norai API 允许您以程序化方式提交算法计算任务(车床分配、PCB拼版、布料排版、交易拟合), 查询任务状态并导出结果。所有 API 均为 RESTful 风格,使用 JSON 格式通信。

基础 URL:https://norai.cn/api

2. 鉴权

2.1 获取 API Key

  1. 登录 Norai 平台 → 工作空间 → 设置 → API 密钥
  2. 点击「创建密钥」,填写名称并选择权限范围
  3. 复制生成的密钥(仅显示一次,请妥善保管)

注意:API Key 功能要求 Standard 或更高套餐。

2.2 使用方式

在每次 API 请求的 HTTP header 中携带 Key:

Authorization: Bearer sk_live_xxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2.3 权限范围 (Scopes)

Scope说明涉及接口
jobs:read查询任务列表和详情GET /api/jobs, GET /api/jobs/:id
jobs:write创建、取消、中止任务POST /api/jobs, DELETE /api/jobs/:id
upload上传数据文件POST /api/upload
export导出计算结果GET /api/lathe/export/:id 等
lathe:parse解析车床订单数据POST /api/lathe/parse-upload
pcb:parse解析 PCB 订单数据POST /api/pcb/parse
transaction:parse解析交易流水数据POST /api/transaction/parse-upload

3. 通用约定

请求格式

  • JSON 请求:Content-Type: application/json
  • 文件上传:Content-Type: multipart/form-data
  • 字符编码:UTF-8

响应格式

成功响应
{
  "success": true,
  "data": { ... }
}
错误响应
{
  "error": "错误描述信息"
}

HTTP 状态码

状态码含义
200请求成功
201资源创建成功
400请求参数错误
401未授权(Key 无效/过期/已吊销)
403权限不足(scope 不匹配/套餐限制)
404资源不存在
422请求体校验失败
429请求过于频繁
500服务器内部错误

4. 车床分配 API

车床智能分配是最常用的集成场景。提交任务有两种方式:直接提交 JSON 数据,或先上传 Excel 文件再提交。

两种提交流程对比:

直接提交 JSON(推荐)上传 Excel 后提交
source"json""upload"
步骤提交 → 轮询 → 获取结果上传 → 提交 → 轮询 → 获取结果
必需参数data + paramstempId + params
适合场景系统集成、数据已结构化数据来源为 Excel 文件

4.1 提交任务 — 方式一:直接 JSON(推荐)

适合系统集成场景。将结构化数据直接传入请求,无需上传文件,一步到位。

POST /api/jobs
{
  "type": "lathe",
  "source": "json",
  "data": {
    "sizes": ["S", "M", "L", "XL", "2XL"],
    "colors": ["白色", "黑色", "蓝色"],
    "demand": {
      "白色": { "S": 120, "M": 200, "L": 180, "XL": 150, "2XL": 80 },
      "黑色": { "S": 100, "M": 180, "L": 160, "XL": 120, "2XL": 60 },
      "蓝色": { "S": 80, "M": 150, "L": 140, "XL": 100, "2XL": 50 }
    }
  },
  "params": {
    "method": "auto",
    "max_layers_per_bed": 150,
    "max_pieces_per_layer": 10,
    "tolerance_over": 2,
    "tolerance_under": 2,
    "max_colors_per_bed": 2,
    "time_limit": 5
  }
}

4.2 提交任务 — 方式二:上传 Excel

适合数据来源为 Excel 文件的场景。分两步:先上传文件获取 tempId,再用 tempId 提交任务,服务端自动解析 Excel。

第一步:上传文件

POST /api/upload
Content-Type: multipart/form-data
Authorization: Bearer sk_live_xxxx_...

FormData:
  file: (订单数据.xlsx, ≤200KB)
  type: "lathe"
响应
{
  "success": true,
  "data": {
    "tempId": "a1b2c3d4-5678-9abc-def0-123456789abc",
    "originalName": "订单数据.xlsx"
  }
}

第二步:提交任务

POST /api/jobs
{
  "type": "lathe",
  "source": "upload",
  "tempId": "a1b2c3d4-5678-9abc-def0-123456789abc",
  "params": {
    "method": "auto",
    "time_limit": 5
  }
}

注:也可额外传入 data 字段覆盖 Excel 的自动解析结果(如需修正数据),但通常不需要。

可选:解析预览(提交前确认数据)

如需在提交前查看 Excel 解析结果,可调用此接口预览:

POST /api/lathe/parse-upload
{ "tempId": "a1b2c3d4-..." }

→ 响应: { "data": { "sizes": [...], "colors": [...], "demand": {...} } }

4.3 参数说明

data 字段(source="json" 时必填):

字段类型说明
sizesstring[]尺码列表,如 ["S", "M", "L", "XL"]
colorsstring[]颜色列表,如 ["白色", "黑色"]
demandobject需求矩阵: { 颜色: { 尺码: 数量 } }

params 参数说明:

参数类型默认值说明
methodstring"auto"算法模式: auto / quality / balanced / fast
max_layers_per_bedint150每床最大层数 (1-200)
min_layers_per_bedint1每床最小层数 (1-200)
max_pieces_per_layerint10每层最大件数 (1-20)
min_pieces_per_layerint1每层最小件数 (1-20)
max_same_size_per_layerint10同规格最大件数 (1-20)
tolerance_overint2允许多裁件数 (0-100)
tolerance_underint2允许少裁件数 (0-100)
planned_bedsint | nullnull目标床数(留空自动优化)
max_colors_per_bedint2每床最多混铺颜色数 (1-10)
time_limitint5最大求解时间,分钟(受套餐上限约束)

算法模式说明:

模式适用场景
auto系统根据订单规模自动选择最优策略
quality规模较小、追求最少床数
balanced兼顾速度与方案质量
fast数据量大或时间紧迫时快速出方案
响应
{
  "success": true,
  "data": {
    "id": 88,
    "jobNumber": "A3K7-LA-260804-0001",
    "status": "pending"
  }
}

4.4 轮询任务状态

GET /api/jobs/88
// 运行中
{
  "success": true,
  "data": {
    "id": 88,
    "status": "running",
    "progress": {
      "iteration": 5,
      "best_beds": 12,
      "elapsed_sec": 45.2,
      "status": "improving"
    }
  }
}

// 成功
{
  "success": true,
  "data": {
    "id": 88,
    "status": "success",
    "result": {
      "total_beds": 12,
      "status": "optimal",
      "run_time_sec": 193.5,
      "beds": [
        {
          "bed_id": 1,
          "pattern": { "M": 4, "L": 3, "XL": 3 },
          "layers": { "白色": 20, "黑色": 15 },
          "pieces_per_layer": 10
        }
      ],
      "deviation": [
        { "color": "白色", "size": "S", "demand": 120, "produced": 122, "diff": 2 }
      ]
    }
  }
}
status含义
pending排队等待执行
running正在求解
success求解成功
failure求解失败(查看 errorMessage)
canceled已取消/已中止

4.5 获取结果

任务成功后,有两种方式获取结果:

方式一:JSON 格式(通过任务详情接口)

当任务 status 为 success 时,GET /api/jobs/:id 响应中的data.result 字段即为完整结果 JSON,可直接用于程序化处理。

GET /api/jobs/88 → data.result
{
  "total_beds": 12,
  "status": "optimal",
  "run_time_sec": 193.5,
  "beds": [
    {
      "bed_id": 1,
      "pattern": { "M": 4, "L": 3, "XL": 3 },
      "layers": { "白色": 20, "黑色": 15 },
      "pieces_per_layer": 10
    }
  ],
  "deviation": [
    { "color": "白色", "size": "S", "demand": 120, "produced": 122, "diff": 2 }
  ]
}

方式二:Excel 文件

GET /api/lathe/export/88
响应: .xlsx 文件二进制流
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

Excel 包含三个 Sheet:
  - 分床方案: 每床颜色、各尺码件数、层数、总件数
  - 偏差分析: 各颜色尺码的需求 vs 实际产出偏差
  - 汇总: 总床数、方案状态、求解时间

5. PCB 拼版 API

5.1 上传并解析

上传: POST /api/upload
FormData: file (.xlsx, ≤200KB), type = "pcb"
解析: POST /api/pcb/parse
{ "source": "upload", "tempId": "uuid-from-upload" }

5.2 提交拼版任务

POST /api/jobs
{
  "type": "pcb",
  "source": "upload",
  "tempId": "uuid-from-upload",
  "originalName": "PCB订单.xlsx",
  "params": {
    "component_spacing": 1.6,
    "margin": 7.0,
    "min_orders_per_panel": 3,
    "max_extra_qty": 1,
    "max_extra_ratio": 0.1,
    "over_production_ratio": 0.03,
    "panel_sizes": "600 500",
    "time_minutes": 5
  }
}
参数类型默认值说明
component_spacingfloat1.6元件间距 (mm)
marginfloat7.0板边距 (mm)
min_orders_per_panelint3每版最少订单数
max_extra_qtyint1最大多拼数量
max_extra_ratiofloat0.1最大多拼比例
over_production_ratiofloat0.03超产比例
panel_sizesstring"600 500"拼版尺寸 (空格分隔, mm)
time_minutesint5求解时间 (分钟)

6. 布料排版 API

6.1 上传 PLT 文件

POST /api/upload
FormData: file (.plt, ≤10MB), type = "fabric"

6.2 提交排版任务

POST /api/jobs
{
  "type": "fabric",
  "source": "upload",
  "tempId": "uuid-from-upload",
  "originalName": "裁片.plt",
  "params": {
    "strip_height": 1480,
    "time_minutes": 3
  }
}
参数类型默认值说明
strip_heightint1480布幅高度 (mm)
time_minutesint1求解时间 (分钟)

7. 交易拟合 API

7.1 上传 Excel

POST /api/upload
FormData: file (.xlsx, ≤10MB, 支持多文件), type = "transaction"

响应: { "data": { "tempIds": ["uuid-1", "uuid-2"], "originalNames": [...] } }

7.2 提交拟合任务

POST /api/jobs
{
  "type": "transaction",
  "source": "upload",
  "tempIds": ["uuid-1", "uuid-2"],
  "params": {
    "target_month": "2026-07",
    "target_mode": "ratio",
    "target_ratio": 0.15,
    "tolerance": 0.10,
    "amount_column": "收款金额",
    "time_column": "收款时间",
    "time_limit": 2
  }
}
参数类型默认值说明
target_monthstring上月目标月份 (YYYY-MM)
target_modestring"ratio"目标方式: ratio(按比例) / amount(按金额)
target_ratiofloat0.15目标比例 (0-1, mode=ratio 时使用)
target_amountfloat | nullnull目标金额 (元, mode=amount 时使用)
tolerancefloat0.10容差范围 (0-0.5)
amount_columnstring"收款金额"Excel 中金额列的列名
time_columnstring"收款时间"Excel 中时间列的列名
time_limitint2求解时间 (分钟)

7.3 获取结果

方式一:GET /api/jobs/:id 响应中data.result 即为完整结果 JSON。

方式二: GET /api/transaction/export/:id → 返回 .xlsx 结果文件

8. 任务管理 API

8.1 查询任务列表

GET /api/jobs?type=lathe&page=1&pageSize=20
响应:
{
  "success": true,
  "data": {
    "items": [
      {
        "id": 88,
        "jobNumber": "A3K7-LA-260804-0001",
        "type": "lathe",
        "status": "success",
        "createdAt": "2026-08-04T10:00:00.000Z",
        "completedAt": "2026-08-04T10:03:15.000Z",
        "totalBeds": 12,
        "runTimeSec": 193.5
      }
    ],
    "total": 15,
    "page": 1,
    "pageSize": 20
  }
}
Query 参数类型默认值说明
typestring"fabric"产品类型: fabric / lathe / pcb / transaction
pageint1页码
pageSizeint20每页条数 (最大 100)

8.2 取消/删除任务

DELETE /api/jobs/:id

行为:
  pending → 标记为 canceled
  running → 中止进程,标记为 canceled
  其他状态 → 软删除(从列表隐藏)

8.3 中止运行中任务

POST /api/jobs/:id/stop

仅对 running 状态有效。算法会在下一个检查点退出,返回当前最优解。

9. 套餐与限制

API Key 继承其所属用户的套餐限制:

套餐任务优先级最大求解时间上传权限
Standard普通10 分钟允许
Pro较高30 分钟允许
VIP最高60 分钟允许

当请求的 time_minutes 超过套餐上限时,系统自动截断至最大允许值。

10. 完整集成示例

Python 示例(直接提交 JSON)

lathe_integration.py
import requests
import time

BASE_URL = "https://norai.cn/api"
API_KEY = "sk_live_a3k7_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
headers = {"Authorization": f"Bearer {API_KEY}"}

# 步骤 1: 直接提交任务 (无需上传文件)
resp = requests.post(f"{BASE_URL}/jobs",
    headers={**headers, "Content-Type": "application/json"},
    json={
        "type": "lathe",
        "source": "json",
        "data": {
            "sizes": ["S", "M", "L", "XL", "2XL"],
            "colors": ["白色", "黑色", "蓝色"],
            "demand": {
                "白色": {"S": 120, "M": 200, "L": 180, "XL": 150, "2XL": 80},
                "黑色": {"S": 100, "M": 180, "L": 160, "XL": 120, "2XL": 60},
                "蓝色": {"S": 80, "M": 150, "L": 140, "XL": 100, "2XL": 50},
            },
        },
        "params": {"method": "auto", "time_limit": 5},
    })
job_id = resp.json()["data"]["id"]

# 步骤 2: 轮询状态
while True:
    resp = requests.get(f"{BASE_URL}/jobs/{job_id}", headers=headers)
    data = resp.json()["data"]
    if data["status"] in ("success", "failure"):
        break
    time.sleep(3)

# 步骤 3: 获取结果
if data["status"] == "success":
    # 方式一: JSON 结果 (已在轮询响应中, 推荐)
    result = data["result"]
    print(f"完成! 总床数: {result['total_beds']}")
    print(f"床方案: {result['beds']}")

    # 方式二: 导出 Excel 文件
    resp = requests.get(f"{BASE_URL}/lathe/export/{job_id}", headers=headers)
    with open("result.xlsx", "wb") as f:
        f.write(resp.content)

cURL 示例

Shell
# 直接提交 JSON 任务 (推荐, 无需上传文件)
curl -X POST https://norai.cn/api/jobs \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "lathe",
    "source": "json",
    "data": {"sizes":["S","M","L"],"colors":["白色"],
             "demand":{"白色":{"S":100,"M":200,"L":150}}},
    "params": {"method":"auto","time_limit":5}
  }'

# 查询状态 (轮询直到 status 为 success/failure)
curl https://norai.cn/api/jobs/88 \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..."

# 导出 Excel (可选, JSON 结果已在上一步响应中)
curl -o result.xlsx https://norai.cn/api/lathe/export/88 \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..."

# --- 若数据来源为 Excel 文件, 使用上传流程 ---
# 上传文件
curl -X POST https://norai.cn/api/upload \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..." \
  -F "file=@订单数据.xlsx" -F "type=lathe"

# 解析数据
curl -X POST https://norai.cn/api/lathe/parse-upload \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..." \
  -H "Content-Type: application/json" \
  -d '{"tempId": "uuid-from-upload"}'

# 提交任务 (source=upload 需要 tempId + data)
curl -X POST https://norai.cn/api/jobs \
  -H "Authorization: Bearer sk_live_a3k7_xxxx..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "lathe", "source": "upload",
    "tempId": "uuid-from-upload",
    "data": {"sizes":["S","M","L"],"colors":["白色"],
             "demand":{"白色":{"S":100,"M":200,"L":150}}},
    "params": {"method":"auto","time_limit":5}
  }'

Node.js 示例

integration.js
const API_KEY = "sk_live_a3k7_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const BASE = "https://norai.cn/api";
const headers = { Authorization: `Bearer ${API_KEY}` };

// 直接提交 JSON 任务
const jobResp = await fetch(`${BASE}/jobs`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({
    type: "lathe",
    source: "json",
    data: {
      sizes: ["S", "M", "L"],
      colors: ["白色", "黑色"],
      demand: { "白色": { S: 100, M: 200, L: 150 }, "黑色": { S: 80, M: 160, L: 120 } },
    },
    params: { method: "auto", time_limit: 5 },
  }),
});
const { data: job } = await jobResp.json();

// 轮询
let result;
while (true) {
  const resp = await fetch(`${BASE}/jobs/${job.id}`, { headers });
  const { data } = await resp.json();
  if (data.status === "success") { result = data.result; break; }
  if (data.status === "failure") throw new Error(data.errorMessage);
  await new Promise(r => setTimeout(r, 3000));
}
console.log("总床数:", result.total_beds);

11. 错误处理

重试策略

状态码是否重试建议
401检查 Key 是否正确/过期/已吊销
403检查 scope 或套餐权限
429等待后重试
500指数退避重试(最多 3 次)

任务失败原因

errorMessage含义建议
无可行解当前约束下无法满足所有需求调大容差或减少约束
计算超时限定时间内未找到可行解增加 time_limit
文件格式错误输入数据有问题检查 Excel 格式

12. 最佳实践

  • 轮询间隔:建议 3 秒一次,避免过于频繁
  • 超时处理:设置合理的 time_limit,标准套餐最大 10 分钟
  • 密钥安全:API Key 保存在服务端环境变量中,不要硬编码在客户端代码或提交到代码仓库
  • 最小权限:创建 Key 时仅授予必要的 scope
  • 定期轮换:建议每 90 天更换一次 API Key
  • 错误日志:记录所有非 200 响应以便排查问题
  • 幂等处理:同一请求不要重复提交(通过 jobNumber 去重判断)

技术支持

如有集成问题,请联系技术支持:navy.xie@qq.com

平台地址:https://norai.cn