OMS OpenAPI接口架构和使用指南
你是 OMS 系统OpenAPI接口架构专家,熟悉OpenAPI的整体架构、请求流程、签名算法、响应格式、错误处理机制和调用方法。
核心概念
OMS系统提供了丰富的开放API接口,供外部系统进行数据交互。所有API接口采用统一的架构设计,包括系统级参数、签名验证、权限控制和响应格式。
系统架构
请求流程
OpenAPI接口的请求处理流程如下:
- 参数接收:接收POST请求参数,包括系统级参数和应用级参数
- 参数解析:解析method参数,提取class和method名称
- 系统参数检查:验证系统级参数(ver, charset, type, method, flag, timestamp, sign)
- 签名验证:验证请求签名是否正确
- 时间戳验证:检查时间戳是否在有效范围内(±3600秒)
- 防重复验证:检查签名是否已存在于缓存中(防重复提交)
- 接口存在性检查:验证接口类和方法是否存在
- 权限检查:验证调用方是否有权限访问该接口
- 参数验证:验证应用级参数是否符合要求
- 接口调用:执行接口方法,获取原始数据
- 数据格式化:按照模板格式化输出数据
- 响应返回:返回成功或错误响应
核心类说明
- openapi_entrance:入口类,负责参数解析、签名验证、权限检查
- openapi_object:接口对象类,负责接口实例化、参数验证、接口调用
- openapi_response:响应类,负责响应格式化、错误处理
- openapi_privilege:权限类,负责权限验证
- openapi_setting:配置类,负责配置管理
- openapi_errorcode:错误码类,负责错误码定义
请求方式
请求URL
http(s)://[域名]/index.php/openapi/rpc/service/请求方法
所有API接口均支持POST方式请求。
系统级参数
所有API请求必须包含以下系统级参数:
| 参数名 | 类型 | 必选 | 描述 |
|---|---|---|---|
| ver | number | 否 | 接口版本号,默认值:1 |
| charset | string | 否 | 字符编码,支持:utf-8,默认值:utf-8 |
| type | string | 否 | 返回数据格式,支持:json,默认值:json |
| method | string | 是 | 接口方法名,格式:class.method |
| flag | string | 是 | 来源标识,对应系统中配置的接入方标识 |
| timestamp | string | 是 | 时间戳,单位:秒,必须在服务器时间±3600秒范围内 |
| sign | string | 是 | 签名,详见签名算法章节 |
method参数格式
method参数格式为:{class}.{method}
例如:
orders.getList- 订单列表接口sales.getList- 销售单列表接口stock.getAll- 库存查询接口
签名算法
签名生成步骤
- 收集所有请求参数(不包括sign本身)
- 按照参数名的ASCII码顺序进行排序(ksort)
- 递归拼接参数名和参数值(对于嵌套数组,递归处理)
- 对拼接后的字符串进行MD5加密,再转大写
- 将上一步的结果与token(接口密钥)拼接
- 再次进行MD5加密并转大写,得到最终签名
签名算法代码示例(PHP)
php
// 签名生成函数
function gen_sign($params, $token)
{
// 参数排序
ksort($params, SORT_STRING);
// 拼接字符串
$assemble_str = '';
foreach($params AS $key=>$val){
if(is_null($val)) continue;
if(is_bool($val)) $val = ($val) ? 1 : 0;
$assemble_str .= $key . (is_array($val) ? assemble($val) : $val);
}
// 生成签名
return strtoupper(md5(strtoupper(md5($assemble_str)).$token));
}
// 递归拼接数组参数
function assemble($params)
{
if(!is_array($params)) return null;
ksort($params, SORT_STRING);
$sign = '';
foreach($params AS $key=>$val){
if(is_null($val)) continue;
if(is_bool($val)) $val = ($val) ? 1 : 0;
$sign .= $key . (is_array($val) ? assemble($val) : $val);
}
return $sign;
}签名防重复机制
- 系统会缓存生成的签名1小时(3600秒)
- 相同签名的重复请求将被拒绝,返回错误码:e000009
响应格式
成功响应
json
{
"response": {
// 具体接口返回数据
}
}错误响应
json
{
"error_response": {
"code": "错误码",
"msg": "错误信息",
"sub_msg": "详细错误信息(可选)"
}
}系统级错误码
| 错误码 | 描述 | 可能原因 |
|---|---|---|
| e000001 | system params lost or error | 系统级参数缺失或错误 |
| e000002 | sign error | 签名错误,请检查签名算法和参数 |
| e000003 | class or method not exist | 接口类或方法不存在 |
| e000004 | no permissions to access | 没有权限访问该接口 |
| e000005 | init interface fail | 初始化接口失败 |
| e000006 | application params error | 应用级参数错误 |
| e000007 | init template fail | 初始化模板失败 |
| e000008 | refer server time invalid | 时间戳无效,与服务器时间偏差过大 |
| e000009 | repeat request invalid | 重复请求,签名已存在于缓存中 |
调用示例
PHP示例
php
<?php
// 应用标识(需要替换为实际的flag)
$flag = 'your_oms_flag';
// 私钥(需要替换为实际的token)
$token = 'your_interface_key';
// 接口方法(例如,查询订单列表:orders.getList)
$method = 'orders.getList';
// 系统级参数
$systemParams = array(
'flag' => $flag, // 应用标识
'method' => $method, // 接口方法
'ver' => 1,
'charset' => 'utf-8',
'type' => 'json',
'timestamp' => time(),
);
// 应用级参数
$ApiParams = array(
'start_time' => '2025-10-01 00:00:00',
'end_time' => '2025-11-01 00:00:00',
'page_no' => 1,
'page_size' => 50
);
// 组装接口参数
$params = array_merge($systemParams, $ApiParams);
// 签名生成函数
function gen_sign($params, $token)
{
// 参数排序
ksort($params, SORT_STRING);
// 拼接字符串
$assemble_str = '';
foreach($params AS $key=>$val){
if(is_null($val)) continue;
if(is_bool($val)) $val = ($val) ? 1 : 0;
$assemble_str .= $key . (is_array($val) ? assemble($val) : $val);
}
// 生成签名
return strtoupper(md5(strtoupper(md5($assemble_str)).$token));
}
// 递归拼接数组参数
function assemble($params)
{
if(!is_array($params)) return null;
ksort($params, SORT_STRING);
$sign = '';
foreach($params AS $key=>$val){
if(is_null($val)) continue;
if(is_bool($val)) $val = ($val) ? 1 : 0;
$sign .= $key . (is_array($val) ? assemble($val) : $val);
}
return $sign;
}
// 生成签名
$params['sign'] = gen_sign($params, $token);
// curl发送请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http(s)://www.***.com/index.php/openapi/rpc/service/');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// 处理响应
$result = json_decode($response, true);
// 测试Api接口返回的结果
echo('<pre>Response:<br>');
print_r($response);
echo('<br><br>Result:<br>');
print_r($result);
exit;Java示例(使用HttpClient)
java
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import java.security.MessageDigest;
import java.util.*;
public class OpenApiClient {
public static void main(String[] args) throws Exception {
// 接口URL
String url = "http://your-domain.com/index.php/openapi/rpc/service/";
// 接口参数
Map<String, String> params = new TreeMap<>(); // TreeMap自动按键排序
params.put("ver", "1");
params.put("charset", "utf-8");
params.put("type", "json");
params.put("method", "orders.getList");
params.put("flag", "test_flag");
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
params.put("start_time", "2024-01-01 00:00:00");
params.put("end_time", "2024-01-31 23:59:59");
params.put("page", "1");
params.put("page_size", "20");
// 生成签名
String token = "your_interface_key";
String assembleStr = assemble(params);
String sign = md5(md5(assembleStr).toUpperCase() + token).toUpperCase();
params.put("sign", sign);
// 发送请求
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(url);
List<NameValuePair> nvps = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
httppost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
} finally {
response.close();
}
}
// 拼接参数
private static String assemble(Map<String, String> params) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(entry.getKey()).append(entry.getValue());
}
return sb.toString();
}
// MD5加密
private static String md5(String str) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes("UTF-8"));
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}支持的API模块
系统支持以下API模块:
| 模块名 | 描述 | 主要方法 |
|---|---|---|
| orders | 订单 | getList, getCouponList, getPmtList |
| sales | 销售单 | getList, getSalesAmount, getDeliveryList |
| aftersales | 售后单 | getList |
| refunds | 退款单 | getList, getDetailList |
| delivery | 发货单 | getList |
| po | 采购单 | add, getList, cancel |
| purchasereturn | 采购退货单 | getList, add, cancel |
| transfer | 出入库单 | add, getList, getIsoList |
| stock | 库存查询 | getAll, getDetailList, getBarcodeStock, getBnStock |
| inventory | 库存管理 | getList, getDetail, getApplyDetail, getApplyList |
| stockdump | 转储单 | getList |
| workorder | 加工单 | getList |
| appropriation | 调拨单 | getList, add |
| warehouse | 仓库转仓单 | getList, add |
| basicmaterial | 基础物料 | getList, add, edit |
| salesmaterial | 销售物料 | getList, add, edit |
| branch | 仓库 | getList |
| brand | 品牌 | getList |
| shop | 店铺 | getList, add |
| supplier | 供应商 | add, edit |
| member | 会员 | add, edit |
| finance | 财务 | getList, getJZT, getJDbill, getReportItems |
| ar | 应收账款 | getList |
| invoice | 发票操作 | getList, update, getResultList |
| iostock | 出入库明细 | getList |
权限控制
OpenAPI接口采用基于配置的权限控制机制:
- 配置管理:每个接入方(flag)在系统中配置允许访问的接口列表
- 权限验证:系统在接口调用前验证调用方是否有权限访问该接口
- 配置格式:
config[class][method]格式,例如:config[orders][getList] - 权限检查:如果调用方没有权限,返回错误码:e000004
数据脱敏
部分敏感数据(如客户信息、联系方式等)在API返回时可能会进行脱敏处理。脱敏配置在接入方配置中设置(is_data_mask字段)。
注意事项
- 密钥管理:请妥善保管接口密钥(token),避免泄露
- 时间戳同步:时间戳必须与服务器时间保持同步,误差不超过3600秒
- 防重复提交:每个请求必须生成唯一的签名,避免重复提交
- 参数选择:请根据实际业务需求选择合适的API接口和参数
- 分页控制:对于大数据量查询,请使用分页参数控制返回结果数量
- 签名检查:如遇签名错误,请仔细检查参数拼写、排序及token是否正确
- 调用频率:为保证系统稳定性,API调用可能存在频率限制,具体限制规则请联系系统管理员获取
参考文档
- 订单API:
oms-openapi-orders.md - 销售单API:
oms-openapi-sales.md - 库存查询API:
oms-openapi-stock.md - 其他业务接口Skills:
oms-openapi-{api-name}.md
Guidelines
- 签名算法:严格按照签名算法步骤生成签名,注意参数排序和递归处理
- 错误处理:根据错误码判断错误类型,e000002通常是签名错误,e000004是权限错误
- 时间戳:使用当前时间的Unix时间戳(秒),确保与服务器时间同步
- 参数验证:在调用接口前,确保所有必填参数都已提供
- 响应处理:检查响应中的
error_response字段,判断请求是否成功 - 日志记录:系统会自动记录所有API调用日志,便于问题排查
