Skip to content

开放 API v1 接入文档

开放 API 用于让企业自己的系统、自动化工具或数据分析程序读取珍妮面料软件中的部分业务数据。第一期是安全地基和样品基础资料只读试点,适合把样品编号、名称、成分、门幅、克重、规格、分类、公开封面等资料同步到外部系统。

第一期开通范围

第一期只支持读取样品基础资料:GET /openapi/v1/samples。不支持新增、修改、删除样品,不支持回写库存、订单、财务、图片、联系人等数据,也不支持回写珍妮主库。需要写入珍妮系统的数据,仍然请在珍妮软件内按正常业务流程操作。

如果你在软件里还看不到开放 API 入口,说明该功能尚未对当前企业开放。具体开通状态、接口域名和可用额度,以珍妮软件内实际入口或客服通知为准。

一、接入前准备

接入前先确认这几件事:

项目说明
开通权限由企业所有者在珍妮软件内管理开放 API。员工账号是否能看到入口,以企业权限设置为准
API Key用于标识调用方。一个企业可以按用途创建不同 Key,例如“企业微信表格同步”“内部 BI 同步”
API Secret用于生成请求签名。Secret 只在创建或轮换时显示一次,关闭页面后系统不会再展示明文
Scope第一期开通 samples:read.basic,表示“样品基础资料只读”
IP 白名单可限制只允许固定出口 IP 或 CIDR 网段调用。正式接入建议填写
限流按 API Key 做分钟级限流。默认 120 次/分钟;实际额度以创建 Key 时设置为准

二、重要边界

开放 API v1 第一期只读,不做任何回写:

  • 不提供 POST / PUT / PATCH / DELETE 形式的外部写入接口
  • 不支持外部系统创建、编辑、删除样品
  • 不支持外部系统写入珍妮主库
  • 不支持批量上传图片、文件或码单
  • 不返回成本、进价、供应商报价、内部图片、私有图片、联系人资料、财务信息
  • 不代替员工账号登录;第一期只按开放 API scope 输出有限字段

三、接口地址

第一期接口:

text
GET /openapi/v1/samples

完整请求地址由“接入域名 + 路径”组成:

text
https://<接入域名>/openapi/v1/samples

<接入域名> 以开通时提供的地址为准。不要把示例里的占位符直接用于生产调用。

四、请求头

每次请求都必须带以下请求头:

请求头必填说明
X-Jenny-KeyAPI Key,例如 jnk_...
X-Jenny-Timestamp当前时间戳。建议使用毫秒时间戳;秒级时间戳也会按秒处理。服务端只接受 5 分钟时间窗口内的请求
X-Jenny-Nonce随机字符串,建议使用 UUID。相同 API Key 下,同一个 nonce 在 10 分钟内不能重复使用
X-Jenny-Signature使用 API Secret 计算出来的 HMAC-SHA256 十六进制签名

五、签名规则

签名用于确认请求没有被篡改,并证明调用方持有 API Secret。

签名字符串由 4 行组成,行与行之间用换行符 \n 连接:

text
METHOD
/openapi/v1/samples
canonical_query
sha256(body)

说明:

  • METHOD 使用大写,例如 GET
  • 第二行是请求路径,不包含域名
  • canonical_query 是排序后的查询字符串
  • sha256(body) 是请求体的 SHA-256 十六进制摘要
  • 第一期开通的是 GET 查询接口,请求体为空。空字符串的 SHA-256 是 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  • X-Jenny-TimestampX-Jenny-Nonce 是独立校验的防重放请求头,不要把它们额外拼进签名字符串

canonical_query 规则:

  1. 解析 URL query 参数。
  2. 按参数名排序。
  3. 同名参数按参数值排序。
  4. 对参数名和参数值做 URL encode。
  5. & 拼接成标准查询字符串。

例如原始请求参数是:

text
page_size=50&page=1&keyword=cotton

排序后的 canonical_query 是:

text
keyword=cotton&page=1&page_size=50

签名计算:

text
hex(hmac_sha256(API_SECRET, canonical_string))

六、Node.js 签名示例

下面示例只演示签名生成方式。正式接入时,请把 API Secret 放在后端环境变量或密钥管理系统中,不要写进前端页面、浏览器脚本、公开仓库或截图。

js
import crypto from 'node:crypto'

const apiKey = process.env.JENNY_API_KEY
const apiSecret = process.env.JENNY_API_SECRET
const baseUrl = 'https://<接入域名>'
const path = '/openapi/v1/samples'

const params = new URLSearchParams({
  page: '1',
  page_size: '50',
})

function canonicalQuery(searchParams) {
  const pairs = Array.from(searchParams.entries())
  pairs.sort((a, b) => {
    if (a[0] === b[0]) return a[1].localeCompare(b[1])
    return a[0].localeCompare(b[0])
  })
  return new URLSearchParams(pairs).toString()
}

const method = 'GET'
const body = ''
const bodyHash = crypto.createHash('sha256').update(body).digest('hex')
const query = canonicalQuery(params)
const canonical = [method, path, query, bodyHash].join('\n')
const signature = crypto.createHmac('sha256', apiSecret).update(canonical).digest('hex')

const res = await fetch(`${baseUrl}${path}?${query}`, {
  method,
  headers: {
    'X-Jenny-Key': apiKey,
    'X-Jenny-Timestamp': String(Date.now()),
    'X-Jenny-Nonce': crypto.randomUUID(),
    'X-Jenny-Signature': signature,
  },
})

const data = await res.json()
console.log(data)

七、只读 samples 接口

请求

text
GET /openapi/v1/samples

需要 scope:

text
samples:read.basic

查询参数:

参数必填默认值说明
page1页码,从 1 开始
page_size50每页条数。默认最大 100;超过最大值会按最大值处理
updated_since毫秒时间戳,只返回更新时间大于等于该值的样品
keyword按编号、名称、成分、分类做基础搜索

返回按更新时间倒序排列,适合外部系统做增量同步。updated_since 用毫秒时间戳,例如 1783075200000

返回字段

每条样品只返回基础展示资料:

字段说明
external_id对外使用的样品 ID。外部系统请保存这个值,不要依赖内部编号
code样品编号
name样品名称
constituents成分
width门幅
weight克重
specification规格
type分类
public_cover公开封面图地址。只返回公共图片,不返回内部图库和私有图库
created_at创建时间,毫秒时间戳
updated_at更新时间,毫秒时间戳

接口不返回以下信息:

  • 成本、进价、供应商报价、利润、成本计算器结果
  • 成品供应商、坯布供应商、工厂、联系人、联系方式
  • 内部图片、私有图片
  • 财务、付款、对账、发票、客户隐私资料
  • 库存明细、订单明细、操作日志等不在第一期范围内的数据

返回示例

json
{
  "code": 0,
  "data": {
    "list": [
      {
        "external_id": "smp_xxxxx",
        "code": "A-1001",
        "name": "斜纹棉弹",
        "constituents": ["棉 97%", "氨纶 3%"],
        "width": ["150CM"],
        "weight": ["220GSM"],
        "specification": ["现货"],
        "type": ["棉弹"],
        "public_cover": "https://<图片地址>",
        "created_at": 1783075200000,
        "updated_at": 1783078800000
      }
    ],
    "total": 1,
    "page": 1,
    "page_size": 50,
    "has_more": false
  }
}

八、防重放:时间戳和 nonce

每个请求都要带 X-Jenny-TimestampX-Jenny-Nonce

时间戳规则:

  • 建议使用毫秒时间戳,例如 1783075200000
  • 服务端只接受当前时间前后 5 分钟内的请求
  • 如果时间差过大,请先校准调用端时间

nonce 规则:

  • 每次请求生成新的随机字符串,建议使用 UUID
  • 同一个 API Key 下,同一个 nonce 在 10 分钟内只能使用一次
  • 重试请求时,也要重新生成 nonce 和签名

如果重复使用 nonce,服务端会拒绝请求,避免同一条已签名请求被重复提交。

九、scope 权限

第一期只有一个 scope:

text
samples:read.basic

它代表“样品基础资料只读”。创建 API Key 时必须勾选这个 scope,才能调用 GET /openapi/v1/samples

后续如果新增其它接口,会按接口单独增加 scope。不要为了省事把同一个 Key 给所有系统共用;建议按用途分别创建 Key,并只授予所需 scope。

十、限流

开放 API 按 API Key 做分钟级限流:

  • 默认 120 次/分钟
  • 创建 Key 时可设置实际分钟限流
  • 超过限流会返回 429

建议外部系统做增量同步时使用 updated_since,不要高频全量拉取。同步程序遇到 429 时,应暂停一段时间后重试。

十一、IP 白名单

创建 API Key 时可以填写 IP 白名单:

  • 每行一个公网 IP,例如 203.0.113.10
  • 也可以填写 CIDR 网段,例如 203.0.113.0/24
  • 留空表示不限制来源 IP

正式接入建议填写固定出口 IP 或网段。若调用方使用动态公网 IP、云函数弹性出口或多个 NAT 出口,需要提前确认所有可能出口,否则会出现“当前 IP 不在白名单内”的错误。

十二、Secret 保管和轮换

Secret 只显示一次:

  • 创建 API Key 时显示一次
  • 轮换 Secret 时显示一次
  • 关闭弹窗后系统不会再展示明文 Secret
  • 如果丢失,只能轮换生成新的 Secret

保管建议:

  • 放在后端环境变量、密钥管理系统或 CI/CD 的密钥配置里
  • 不要写进前端代码、App 包、浏览器脚本或公开仓库
  • 不要通过截图、群聊、邮件明文长期保存
  • 为不同用途创建不同 Key,便于单独停用或轮换
  • 如果怀疑 Secret 泄露,立即停用该 Key 或轮换 Secret

十三、常见错误

HTTP 状态常见 code含义
401missing_key / invalid_key缺少 API Key,或 Key 无效 / 已停用
401timestamp_invalid时间戳缺失、格式错误或超出 5 分钟窗口
401missing_nonce / nonce_replayed缺少 nonce,或 nonce 已被使用
401signature_invalid签名不正确,通常是 query 排序、body hash、路径或 Secret 不一致
403ip_not_allowed调用方公网 IP 不在白名单内
403scope_deniedAPI Key 没有当前接口所需 scope
413body_too_large请求体超过限制。第一期 samples 查询通常不需要请求体
429rate_limited超过分钟限流

排查签名问题时,先把以下四项打印到调用方日志中核对:请求方法、路径、排序后的 query、body hash。不要把 API Secret 打进日志。

十四、接入建议

  1. 先在测试脚本中只拉取 page=1&page_size=10,确认签名、时间戳、nonce 正常。
  2. 再按页读取全量样品,保存每条样品的 external_idupdated_at
  3. 后续同步使用 updated_since 增量拉取。
  4. 外部系统只保存自己需要展示的字段,不要把 Secret、签名串、完整请求头写入业务日志。
  5. 发现字段不够用时,先联系珍妮确认是否属于后续开放范围;第一期不要通过其它非公开入口绕过开放 API。

相关文章

珍妮软件 - 纺织行业数字化解决方案