Initial commit: workspace files including MEMORY.md, skills, and core configs

This commit is contained in:
2026-04-03 19:13:29 +08:00
commit 73ed53d531
33 changed files with 2443 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
# 铱云易订货 OpenAPI 助手
## 模块索引
本技能分散为多个文件,按模块管理:
| 模块 | 文件 | 说明 |
|------|------|------|
| 认证与通用 | `common.md` | 获取 token、基础调用方式 |
| 基础数据 | `basics.md` | 仓库、员工、单位、分类、品牌、区域等 |
| 客户 | `customer.md` | 客户查询(分类、列表、收货地址) |
| 商品 | `product.md` | 商品查询(列表、分类、品牌、授权方案) |
| 商品搜索(新版) | `product-suite.md` | 通过商品名称/编码搜索(用于下单前的商品信息查询) |
| 订单 | `order.md` | 订单查询(含 withDetails 参数) |
| **创建订单** | `create-order.md` | 根据客户+商品+数量创建订单 |
| 库存 | `inventory.md` | 库存查询 |
## 触发条件
当用户表达以下任一意图时触发:
- "查一下客户" / "客户列表"
- "看看有哪些订单" / "今天的新订单"
- "库存还剩多少" / "查库存"
- "对一下账" / "收款记录"
- "商品列表" / "有什么商品"
- "员工" / "仓库" 等基础数据查询
- "搜索商品" / "查一下XXX商品" / "帮我找XXX" / "这个商品的信息" 等商品搜索意图
- "帮我下单" / "创建订单" / "录个订单" / "我要订货" 等下单意图
## 前置准备
### 获取 API 凭证
联系铱云易订货客服:**lgc@77ircloud.com**
- `client_id`: 6767358
- `client_secret`: 1gk9ApiWV8IA2QrVDnU6Dx7uUo7CLuN2
- `userName`: 112983083
- `password`: 77ircloud
- API 地址: https://openapi.77ircloud.com
- 文档站: http://openapi-doc.77ircloud.com/
### 调用方式
1. 先获取 access_token有效期30天
2. 所有请求 header 必须包含 `access_token`
3. POST 请求还需要 `Content-Type: application/json`
详细见 `common.md`

View File

@@ -0,0 +1,6 @@
{
"ownerId": "user-luoxiaocun",
"slug": "irun-yidianhuo",
"version": "1.0.0",
"publishedAt": 1743033600000
}

View File

@@ -0,0 +1,205 @@
# 基础数据查询接口
## 1. 仓库列表
```
GET https://openapi.77ircloud.com/invoicing-aggregation/warehouses
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": {
"totalCount": 11,
"currentPage": 1,
"pageSize": 20,
"totalPage": 1,
"items": [
{
"id": 2116753909958624,
"code": "00000001",
"name": "总部仓库",
"type": 1,
"status": 1,
"orgId": 3607084,
"orgName": "总部"
}
]
}
}
```
**用途:** 查询仓库列表,取 `items[].id` 作为 `warehouseId`(库存查询必填)
---
## 2. 员工列表
```
POST https://openapi.77ircloud.com/organization-aggregation/employees/search
```
**Header:** `access_token: {token}` + `Content-Type: application/json`
**Body**
```json
{"pageNum": 1, "pageSize": 20}
```
**返回:**
```json
{
"code": 200,
"data": {
"items": [
{"id": 22652291, "name": "周总", "phone": "136xxx", "role": "MANAGER"}
]
}
}
```
---
## 3. 商品单位列表
```
GET https://openapi.77ircloud.com/product-aggregation/units
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 2116753910024416, "name": "个"},
{"id": 2116753910040800, "name": "箱"},
{"id": 2116753910057184, "name": "件"}
]
}
```
---
## 4. 商品分类列表
```
GET https://openapi.77ircloud.com/product-aggregation/categories
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 2116753909967072, "name": "通用", "parentId": 0},
{"id": 2116766451460320, "name": "特价活动", "parentId": 0},
{"id": 2116766567037824, "name": "水产生品类", "parentId": 0}
]
}
```
---
## 5. 商品品牌列表
```
GET https://openapi.77ircloud.com/product-aggregation/brands
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 2116767916034272, "name": "意食客"},
{"id": 2116767991238144, "name": "其他品牌"}
]
}
```
---
## 6. 省市区域列表
```
GET https://openapi.77ircloud.com/commondata/common/data/cities?parentId=0
```
**Header:** `access_token: {token}`
**参数:**
- `parentId`: 父级区域ID`0` 获取省份列表
**返回:**
```json
{
"code": 200,
"data": [
{"id": 1, "cityId": 110100, "provinceId": 110000, "name": "北京市"},
{"id": 2, "cityId": 120100, "provinceId": 120000, "name": "天津市"}
]
}
```
---
## 7. 物流公司列表
```
GET https://openapi.77ircloud.com/commondata/common/data/logistics-company
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 1, "name": "共速达", "code": "gongsuda", "phone": "400-111-0005"},
{"id": 2, "name": "邮政EMS速递", "code": "ems"}
]
}
```
---
## 8. 价格等级列表
```
GET https://openapi.77ircloud.com/product-aggregation/level-prices
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 0, "name": "基准订货价(市场价)", "levelIndex": 0},
{"id": 2116753910947264, "name": "尊宝配送价", "levelIndex": 1}
]
}
```
---
## 9. 组织信息
```
GET https://openapi.77ircloud.com/organization-aggregation/organization/info
```
**Header:** `access_token: {token}`
**返回:** 组织基本信息

View File

@@ -0,0 +1,80 @@
# 通用:认证与调用方式
## 获取 Access Token
**重要:路径和参数都经过实际验证,注意不是 POST**
```
GET https://openapi.77ircloud.com/v2/oauth2/token
```
参数直接在 URL 查询字符串中:
```
https://openapi.77ircloud.com/v2/oauth2/token?userName=112983083&password=77ircloud&client_id=6767358&client_secret=1gk9ApiWV8IA2QrVDnU6Dx7uUo7CLuN2&grant_type=client_credentials&scope=basic
```
返回:
```json
{
"code": 200,
"data": {
"access_token": "194557f72e5594557d796f216ae9f9fe3607084",
"expires_in": 2592000
}
}
```
**注意**
- 是 GET 请求,不是 POST
- 有效期 30 天,可复用,不必每次调用都刷新
- token 过期后会返回非 200重新获取即可
## 调用示例Python
```python
import requests, json
# 获取 token
def get_token():
url = "https://openapi.77ircloud.com/v2/oauth2/token"
params = {
"userName": "112983083",
"password": "77ircloud",
"client_id": "6767358",
"client_secret": "1gk9ApiWV8IA2QrVDnU6Dx7uUo7CLuN2",
"grant_type": "client_credentials",
"scope": "basic"
}
resp = requests.get(url, params=params)
return resp.json()["data"]["access_token"]
# 调用 API
token = get_token()
headers = {"access_token": token}
resp = requests.post(
"https://openapi.77ircloud.com/order-aggregation/organizations/orders/search",
headers=headers,
json={"pageNum": 1, "pageSize": 10, "withDetails": True}
)
print(resp.json())
```
## 通用请求 Header
所有 API 调用都必须包含:
- `access_token: {token}` — 必须
- `Content-Type: application/json` — POST 请求必须
## 通用返回格式
```json
{
"code": 200,
"data": {},
"message": "操作成功"
}
```
- `code=200` 表示成功
- `code≠200` 表示失败,检查 `message` 字段

View File

@@ -0,0 +1,161 @@
# 创建订单
> 用户提供客户信息 + 商品信息(名称、单位、数量),自动完成订单创建。
## 触发条件
用户表达以下意图时触发:
- "帮我下单" / "创建订单" / "录个订单"
- "我要订货" / "订一批货"
- 提供商品名称 + 数量时(如"5件鸡肉"
---
## 第一步:查询客户
用户可能提供:客户名称、客户编码
调用客户搜索接口:
```
POST https://openapi.77ircloud.com/organization-aggregation/customer/search
Header: access_token, Content-Type: application/json
Body: {"pageNum":1,"pageSize":10,"keyword":"客户名称或编码"}
```
返回结果中找到对应客户,记录 `id`(即 `buyerId`)。
**注意:**
- 如果客户不存在,返回错误并告知用户
- 如果搜索结果有多个,选择第一个(默认取第一个)
---
## 第二步:查询商品
用户提供:商品名称、单位名称(如"包"、"件"、"斤"、"瓶")、购买数量
调用商品搜索接口(见 `product-suite.md`
```
GET https://suite.77ircloud.com/product-aggregation/v1/products?pageSize=30&currentPage=1&queryTag=true&queryInventories=true&loadSortingTag=true&loadSortingTagName=true&q={商品名称}
Header: authorization={jwt}, x-exclude-login-mutex=77ircloud
```
在返回结果中找对应商品,**默认取第一个**,记录:
- `skuId` → 下单用 `productSku`
- `multiUnitId` → 下单用 `unitId`
- `unitName` → 与用户提供的单位核对
**如果商品有多单位**(如"件"和"包"),根据用户提供的单位名称匹配对应的 `multiUnitId`
---
## 第三步:计算金额
- `salePrice` = `originalPrice` = 商品的 `basePrice`(第一档价格)
- `subtotal` = `salePrice` × 购买数量
- `productTotalAmount` = 所有商品 `subtotal` 之和
- `orderTotalAmount` = `productTotalAmount`(无额外运费/优惠时)
---
## 第四步:组装订单参数
```json
{
"buyerId": "{客户ID}",
"orderDetails": [{
"productSku": "{skuId}",
"productSpu": "{skuId}", // 用 skuId 作为 spuId
"purchaseNumbers": {},
"unitId": "{multiUnitId}",
"salePrice": {},
"originalPrice": {},
"saleRate": 100.0,
"subtotal": {},
"type": "NORMAL"
}],
"productTotalAmount": {},
"orderTotalAmount": {},
"productDiscountAmount": 0,
"orderDiscountAmount": 0,
"totalFreight": 0,
"orderSource": "APP",
"orderStatus": "ORDER_AUDIT_PENDING",
"deliveryMethodId": 1
}
```
**字段说明:**
| 字段 | 默认值 | 说明 |
|------|--------|------|
| `deliveryMethodId` | 1到店自提 | 可选,用户指定则用用户值 |
| `orderSource` | APP | 固定 |
| `orderStatus` | ORDER_AUDIT_PENDING | 固定 |
| `saleRate` | 100.0 | 固定 |
| `productDiscountAmount` | 0 | 固定 |
| `orderDiscountAmount` | 0 | 固定 |
| `totalFreight` | 0 | 固定 |
**用户可能指定的额外参数:**
- `deliveryMethodId`: 1=到店自提, 2=送货上门(需要客户有配送地址)
---
## 第五步:调用下单接口
```
POST https://openapi.77ircloud.com/order-aggregation/organizations/orders
Header: access_token, Content-Type: application/json
Body: 见上面
```
**返回格式:**
```json
{
"code": 200,
"message": "操作成功",
"data": 2390029347669600 // 这是订单ID
}
```
---
## 第六步:返回订单编号
下单接口只返回订单 ID不返回订单编号。需要再查一次订单详情
```
POST https://openapi.77ircloud.com/order-aggregation/organizations/orders/search
Body: {"pageNum":1,"pageSize":100,"withDetails":true}
```
在结果中用返回的订单 ID 找到对应订单,返回 `orderCode`
---
## 完整流程示例
**用户输入:** "帮华誉供货链下 3 包嘉吉尚选霸王鸡肉条"
**执行:**
1. 客户搜索"华誉供货链" → buyerId: 2364948772653120默认取第一个
2. 商品搜索"嘉吉尚选霸王鸡肉条" → 默认取第一个结果skuId: 2116793372668192, multiUnitId: 2116793372668193, basePrice: 18.9
3. 组装参数purchaseNumbers=3, subtotal=56.7, productTotalAmount=56.7
4. 调用下单接口 → orderId: 2390029347669600
5. 查询订单详情 → orderCode: CA000000-260331-102428
**返回:** "订单创建成功订单编号CA000000-260331-102428"
---
## 注意事项
1. **用户输入可能是语音转文字**,注意处理口语化表达(如"3件"="3件"、"5斤"="5斤"
2. **单位匹配**:商品接口返回 `unitName`,用户可能说"件"或"包",需匹配
3. **商品多规格**:用户未指定单位时,默认用第一个单位
4. **库存检查**:创建订单前不强制检查库存,但可以查一下 `availableAmount` 告知用户
5. **下单失败常见原因**
- 客户未配置配送方式(到店自提也需配置)→ 改用 `deliveryMethodId=1`
- 必填字段缺失 → 检查 `productSku`, `unitId`, `purchaseNumbers`

View File

@@ -0,0 +1,93 @@
# 客户查询接口
## 1. 客户分类列表
```
GET https://openapi.77ircloud.com/organization-aggregation/organizations/customer-categories
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": [
{"id": 2116753907815520, "name": "普通客户", "defaultFlag": true},
{"id": 2116796845712896, "name": "尊宝快递", "defaultFlag": false},
{"id": 2116796846321152, "name": "家庭客户", "defaultFlag": false}
]
}
```
---
## 2. 客户列表(查询)
```
POST https://openapi.77ircloud.com/organization-aggregation/customer/search
```
**Header:** `access_token: {token}` + `Content-Type: application/json`
**Body 参数:**
```json
{
"pageNum": 1,
"pageSize": 10,
"keyword": "成都" // 可选,按客户名称模糊搜索
}
```
**返回:**
```json
{
"code": 200,
"data": {
"items": [
{
"id": 2139500795701856,
"code": "0411190513901",
"name": "成都冷鲜优选供应链公司",
"contactName": "周总",
"contactPhone": "136xxx",
"categoryId": 2116753907815520,
"categoryName": "普通客户"
}
]
}
}
```
**说明:** 这是查询类接口POST 方法是业务含义上的查询,不是写操作)
---
## 3. 收货地址列表
```
GET https://openapi.77ircloud.com/organization-aggregation/organizations/{orgId}/delivery-addresses
```
**Header:** `access_token: {token}`
**路径参数:**
- `orgId`: 客户ID从客户列表获取
**返回:**
```json
{
"code": 200,
"data": [
{
"id": 2139500796169856,
"contactName": "",
"contactPhone": "",
"provinceId": 510000,
"cityId": 510100,
"districtId": 510106,
"address": "四川省成都市金牛区..."
}
]
}
```

View File

@@ -0,0 +1,66 @@
# 库存查询接口
## 库存列表(按 SKU 查)
```
POST https://openapi.77ircloud.com/invoicing-aggregation/inventories-sku/search
```
**Header:** `access_token: {token}` + `Content-Type: application/json`
**Body 参数:**
```json
{
"currentPage": 1,
"pageSize": 10,
"warehouseId": 2116753909958624,
"skuIds": [2116792188293408, 2287816343303072]
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `warehouseId` | int | **必填** | 仓库ID从仓库列表接口获取 |
| `skuIds` | array | 可选 | 商品SKU ID数组不传则返回该仓库全部库存 |
| `currentPage` | int | 可选 | 页码默认1 |
| `pageSize` | int | 可选 | 每页数量默认10 |
**返回:**
```json
{
"code": 200,
"data": {
"totalCount": 80,
"currentPage": 1,
"pageSize": 10,
"items": [
{
"skuId": 2116792188293408,
"spuId": 2116792188162336,
"name": "霜火菓子(柿子+花生)",
"code": "101-030-0008",
"amount": 3,
"availableAmount": 7,
"preOrderCount": 83,
"unitName": "件",
"unitId": 2116753910024416,
"status": 23
}
]
}
}
```
**关键字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `availableAmount` | number | **可用库存**(扣减预占后的真实可用),用于判断是否充足 |
| `amount` | number | 总库存(含预占) |
| `preOrderCount` | number | 预占数量(被其他订单占用) |
| `skuId` | number | 商品SKU ID与订单中商品对应 |
**注意:**
- ⚠️ **只有产生过出入库数据的商品才会出现在结果中**无业务数据的SKU不返回
- `warehouseId` 必填,必须先从仓库列表获取
- 库存判断用 `availableAmount > 3` 作为是否充足的条件

View File

@@ -0,0 +1,95 @@
# 订单查询接口
## 订单列表
```
POST https://openapi.77ircloud.com/order-aggregation/organizations/orders/search
```
**Header:** `access_token: {token}` + `Content-Type: application/json`
**Body 参数:**
```json
{
"pageNum": 1,
"pageSize": 10,
"withDetails": true
}
```
**关键参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| `withDetails` | boolean | **⚠️ 重要**true=返回 `orderDetails`商品明细false=不返回 |
| `pageNum` | int | 页码 |
| `pageSize` | int | 每页数量建议不超过100 |
**⚠️ 注意:`orderStatus` 参数会导致 500 错误!** 正确做法:先拉全量订单,在返回结果中用 `orderStatus` 字段过滤。
**订单状态枚举值(从数据中观测):**
- `ORDER_AUDIT_PENDING` — 待订单审核7条测试数据
- `FINANCE_AUDIT_PENDING` — 待财务审核
- `AUDITED` — 已审核34条测试数据
- `FINISHED` — 已完成9条测试数据
- `CANCELLED` — 已作废
**返回字段(重要!)**
- 订单列表在 `data.items`(不是 `data.list`
- 分页信息:`data.totalCount``data.currentPage``data.totalPage`
**返回示例(已审核订单):**
```json
{
"id": 2374359742917152,
"orderCode": "CA000000-260309-79705",
"buyer": {"id": 2139500795701856, "name": "粥大师店(南宁)"},
"orderStatus": "ORDER_AUDIT_PENDING",
"paymentStatus": "UN_PAID",
"createTime": 1774344996000
}
```
**`withDetails: true` 时返回的 `orderDetails` 结构:**
```json
{
"id": 2385059802414657,
"orderCode": "CA000000-260324-95463",
"productSku": 2116792188293408,
"productSpu": 2116792188162336,
"productCode": "101-030-0008",
"productName": "霜火菓子(柿子+花生)",
"type": "NORMAL",
"purchaseNumbers": 2.0,
"unitName": "包",
"salePrice": 9.3,
"subtotal": 18.6
}
```
**关键字段:**
- `productSku`: 商品 SKU ID用于关联库存
- `purchaseNumbers`: 购买数量(判断是否 > 2 的依据)
- `productName`: 商品名称
---
## 订单审核
```
PUT https://openapi.77ircloud.com/order-aggregation/organizations/orders/order-audit
```
**Header:** `access_token: {token}` + `Content-Type: application/json`
**Body**
```json
{"ids": [2374359742917152]}
```
**返回:**
```json
{"code": 200, "message": "操作成功"}
```
**⚠️ 注意:** 部分订单(如客户结算模式为"先款后货"需要先付款才能审核API会返回 525 错误码和具体原因,此时该订单无法自动审核。

View File

@@ -0,0 +1,146 @@
# 商品搜索接口(新版)
> 用于通过商品名称或编码查询商品关键信息,是创建订单的前置步骤。
## 认证流程
### 第一步:登录获取 JWT
```
POST https://accounts.77ircloud.com/api/v2/accounts/login
Content-Type: application/json
```
**请求体:**
```json
{
"userName": "112983083",
"password": "77ircloud",
"loginServerType": 4
}
```
**返回的 jwtToken 即为 authorization 值:**
```json
{
"code": 200,
"data": {
"jwtToken": "eyJhbGci...",
"expires_in": 604800000
}
}
```
### 第二步:带 JWT 请求商品接口
请求头:
- `authorization`: `{jwtToken}`**注意:没有 Bearer 前缀**
- `x-exclude-login-mutex`: `77ircloud` — 固定值
## 商品搜索
```
GET https://suite.77ircloud.com/product-aggregation/v1/products?pageSize=30&currentPage=1&queryTag=true&queryInventories=true&loadSortingTag=true&loadSortingTagName=true&q={关键词}
```
**参数说明:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `q` | ✅ | 搜索关键词(商品名称或商品编码) |
| `pageSize` | ✅ | 每页数量 |
| `currentPage` | ✅ | 页码从1开始 |
| `queryTag` | 可选 | 是否查询标签 |
| `queryInventories` | 可选 | 是否查询库存 |
| `loadSortingTag` | 可选 | 是否查询分拣标签 |
| `loadSortingTagName` | 可选 | 是否查询分拣标签名称 |
**返回示例:**
```json
{
"code": 200,
"data": {
"totalCount": 4,
"currentPage": 1,
"pageSize": 30,
"items": [
{
"name": "嘉吉尚选霸王鸡肉条1kg",
"skuId": 2116793372668192,
"multiUnitId": 2116793372668193,
"unitName": "包",
"basePrice": 18.9,
"availableAmount": 1035.0,
"specJson": "{\"规格\":\"包=约33片/包、件=1kg*10包\"}",
"code": "165-009-00068",
"levelPrices": [
{"index": 1, "price": 18.9},
{"index": 2, "price": 18.0},
{"index": 3, "price": 19.5},
{"index": 4, "price": 18.5}
]
}
]
}
}
```
## 关键字段说明
| 字段 | 说明 | 用途 |
|------|------|------|
| `skuId` | 商品SKU ID | 下单时用 `productSku` |
| `multiUnitId` | 单位ID | 下单时用 `unitId` |
| `unitName` | 单位名称(如"包" | 展示用 |
| `basePrice` | 参考单价 | 填写 `salePrice``originalPrice` |
| `availableAmount` | 当前可用库存 | 判断是否充足 |
## 完整调用示例
```python
import urllib.request, json, urllib.parse
# Step 1: 登录获取 JWT
login_data = json.dumps({
"userName": "112983083",
"password": "77ircloud",
"loginServerType": 4
}).encode()
login_req = urllib.request.Request(
"https://accounts.77ircloud.com/api/v2/accounts/login",
data=login_data,
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(login_req, timeout=10) as resp:
jwt = json.loads(resp.read())['data']['jwtToken']
# Step 2: 搜索商品
keyword = "嘉吉尚选霸王鸡肉条"
q = urllib.parse.quote(keyword)
url = f"https://suite.77ircloud.com/product-aggregation/v1/products?pageSize=30&currentPage=1&queryTag=true&queryInventories=true&loadSortingTag=true&loadSortingTagName=true&q={q}"
search_req = urllib.request.Request(url, headers={
"authorization": jwt,
"x-exclude-login-mutex": "77ircloud"
})
with urllib.request.urlopen(search_req, timeout=15) as resp:
data = json.loads(resp.read())
items = data.get('data', {}).get('items', [])
for item in items:
print(f"商品: {item['name']}")
print(f" skuId: {item['skuId']}")
print(f" multiUnitId: {item['multiUnitId']}")
print(f" 单位: {item['unitName']}")
print(f" 库存: {item['availableAmount']}")
```
## 注意事项
1. **每次查询都需要重新登录获取 JWT**有效期约7天
2. **authorization 没有 Bearer 前缀**,直接写 JWT 字符串
3. **搜索关键词用 URL 编码**,中文需要 encode
4. **搜索结果可能为空**,可尝试不同的关键词组合(如品牌名、商品简称等)

View File

@@ -0,0 +1,89 @@
# 商品查询接口
## 1. 商品列表SKU
```
GET https://openapi.77ircloud.com/product-aggregation/products/sku?pageNum=1&pageSize=10
```
**Header:** `access_token: {token}`
**Query 参数:**
- `pageNum`: 页码
- `pageSize`: 每页数量建议不超过100
- `keyword`: 可选,按商品名称模糊搜索
- `categoryId`: 可选按分类ID筛选
**返回:**
```json
{
"code": 200,
"data": {
"totalCount": 1024,
"currentPage": 1,
"pageSize": 10,
"items": [
{
"skuId": 2116792188293408,
"spuId": 2116792188162336,
"code": "101-030-0008",
"name": "霜火果子(柿子+花生)",
"categoryId": 2116766652419584,
"categoryName": "水产生品类",
"brandId": 2116767991238144,
"brandName": "其他品牌",
"unitId": 2116753910024416,
"unitName": "个",
"status": 23,
"price": 9.3
}
]
}
}
```
**关键字段:**
- `skuId`: 商品SKU ID用于库存查询
- `status`: 商品状态
---
## 2. 商品授权方案列表
```
GET https://openapi.77ircloud.com/product-aggregation/authorizations
```
**Header:** `access_token: {token}`
**返回:**
```json
{
"code": 200,
"data": {
"totalCount": 4,
"items": [
{"id": 2116753910171872, "name": "默认授权方案"},
{"id": 2117317654711808, "name": "南宁分公司无商品"}
]
}
}
```
---
## 3. skuId 增量更新列表注意调用报400参数错误暂时跳过
```
GET https://openapi.77ircloud.com/product-aggregation/sku/pull?pageNum=1&pageSize=10
```
**Header:** `access_token: {token}`
**⚠️ 状态:调用返回 `illegal parameters!`,参数格式待进一步排查。当前可跳过此接口,用商品列表代替。**
---
## 4. 价格等级列表
`basics.md``/product-aggregation/level-prices`

View File

@@ -0,0 +1,147 @@
# 铱云易订货 OpenAPI 2.0 接口文档
> 文档地址http://openapi-doc.77ircloud.com/
> API 基础地址https://openapi.77ircloud.com
## 认证流程
### 获取 Access Token
**⚠️ 重要:文档描述有误,实际接口信息如下**
| 项目 | 说明 |
|------|------|
| URL | `https://openapi.77ircloud.com/v2/oauth2/token` |
| 方法 | **GET**(文档写的是 POST实际是 GET |
| 参数位置 | URL 查询字符串 |
**请求参数:**
| 参数 | 必填 | 说明 | 示例 |
|------|------|------|------|
| userName | ✅ | 铱云系统登录账号 | 112983083 |
| password | ✅ | 铱云系统登录密码 | 77ircloud |
| client_id | ✅ | API 客户端ID | 6767358 |
| client_secret | ✅ | API 密钥 | 1gk9ApiWV8IA2QrVDnU6Dx7uUo7CLuN2 |
| grant_type | ✅ | 授权类型,固定值 | client_credentials |
| scope | ✅ | 作用域,固定值 | basic |
**响应格式:**
```json
{
"code": 200,
"data": {
"access_token": "b615caeda3562eece7fcf834923685ee3607084",
"create_time": 1774593254433,
"expires_in": 2592000,
"refresh_token": "8caf350dc4f013201af6a10efbf3fecd",
"scope": "basic",
"extra": {
"enabledPassPort": null,
"hasMoreAccount": null,
"userId": null,
"userName": null,
"userType": null
},
"nodeCode": ""
},
"message": "操作成功"
}
```
**错误响应:**
```json
{"code":153,"data":null,"message":"illegal client_id"}
```
- `illegal client_id` = client_id 未开通 API 权限
---
## 订单接口
### 订单审核
| 项目 | 说明 |
|------|------|
| URL | `https://openapi.77ircloud.com/order-aggregation/organizations/orders/order-audit` |
| 方法 | **PUT** |
| 认证 | Header: `access_token: {token}` |
**请求 Body**
```json
{
"ids": [2384840119003488, 2384830331153312]
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| ids | long[] | ✅ | 订单ID数组可同时审核多个订单 |
**响应:**
```json
{
"code": 200,
"message": "操作成功",
"data": []
}
```
**常见错误:**
- `ids: must not be empty` - 未传入订单ID数组
- `illegal parameters!` - 参数格式错误
---
## 客户接口
### 客户列表查询
| 项目 | 说明 |
|------|------|
| URL | `https://openapi.77ircloud.com/openapi/customer/list` |
| 方法 | **POST** |
| 认证 | Header: `access_token: {token}` |
**请求 Body**
```json
{
"pageNum": 1,
"pageSize": 10
}
```
---
## 注意事项
1. **接口方法可能与文档不符**:建议先用 POST 测试,收到 400 错误再切换方法
2. **时间参数必须成对**startCreateTime 和 endCreateTime 要么都提供,要么都不提供
3. **Token 有效期**30 天,过期后重新获取
4. **审核接口**:使用 PUT 方法,参数为订单 ID 数组
---
## ⚠️ 重要 Bug订单查询接口的时间过滤参数不生效
**问题描述:**
实测发现 `startCreateTime` / `endCreateTime` 参数完全无效——无论传入什么值API 都返回全部数据,后端过滤逻辑未实现。
**验证过程:**
- 传入精确到毫秒的时间范围如只查1秒startCreateTime=1773990796000, endCreateTime=1773990796001
- API 仍返回全部 99 条订单totalCount 也仍是 99
**createTime 单位:** 确认是毫秒时间戳13位`1773990796000` = `2026-03-20 14:xx GMT+8`
**正确做法:**
1. 调用 API 时不传时间过滤参数,拉取全部数据
2. 在本地用 `createTime` 字段过滤指定日期范围
3. 可用订单号日期编码校验(格式:`CA000000-260320-78534` → 2026-03-20
**订单号日期提取方法:**
```python
def parse_order_date(order_code):
# 格式: CA{yymmdd}-{seq} 例如 CA000000-260320-78534
yymmdd = order_code.replace('CA','').split('-')[1]
yy, mm, dd = int('20'+yymmdd[:2]), int(yymmdd[2:4]), int(yymmdd[4:6])
return datetime.date(yy, mm, dd)
```

View File

@@ -0,0 +1,89 @@
# 铱云易订货助手 - 工作流程
## 查询决策逻辑
### ✅ 自动执行(无需询问)
| 场景 | 决策 |
|-----|------|
| 用户只说"查客户" | 自动列出前10条客户 |
| 用户只说"看订单" | 自动列出最近30天的订单 |
| 用户只说"库存" | 自动列出所有仓库库存 |
| 分页查询 | 自动处理分页,用户说"下一页"时继续 |
### ❓ 需要询问用户
| 场景 | 询问内容 |
|-----|---------|
| 精确查询客户 | "请提供客户名称或分类" |
| 特定时间段 | "请提供开始和结束时间" |
| 查看详情 | "需要查看哪条记录的详情?" |
| 涉及操作(修改/删除)| "确认要执行此操作吗?" |
### 🚫 绝不自动做的事
- 不猜测具体客户名称
- 不自动执行敏感操作(需用户确认)
- 不泄露客户敏感信息
## 数据展示格式
### 客户列表展示
```
## 客户列表
共 {total} 条记录
| 序号 | 客户名称 | 联系电话 | 地址 |
|-----|---------|---------|-----|
| 1 | {name} | {phone} | {address} |
| 2 | ... | ... | ... |
第 {pageNum}/{totalPages} 页
```
### 订单列表展示
```
## 订单列表
共 {total} 条记录
| 订单号 | 客户名称 | 金额 | 状态 | 下单时间 |
|-------|---------|------|------|---------|
| {orderNo} | {customerName} | ¥{totalAmount} | {status} | {createTime} |
```
### 库存列表展示
```
## 库存列表
| 仓库 | 商品名称 | 库存数量 | 可用量 |
|-----|---------|---------|-------|
| {warehouseName} | {productName} | {quantity} | {availableQuantity} |
```
## 时间范围默认值
| 查询类型 | 默认时间范围 |
|---------|------------|
| 订单查询 | 最近30天 |
| 收付款查询 | 最近30天 |
| 库存查询 | 不限 |
| 客户查询 | 不限 |
## 常见问题处理
### 1. 无数据返回
"未查询到数据,请检查查询条件是否正确"
### 2. Token过期
提示用户需要重新获取 access_token
### 3. 权限不足
"当前账号无权访问此数据,请联系管理员"
### 4. 网络异常
"网络请求失败,请稍后重试"

View File

@@ -0,0 +1,278 @@
#!/usr/bin/env python3
"""
铱云易订货 OpenAPI 2.0 Python Client
用于查询客户、商品、订单、库存、收付款等业务数据
"""
import os
import json
import time
import requests
from typing import Optional, Dict, Any, List
# 默认配置
DEFAULT_BASE_URL = "https://openapi.77ircloud.com"
DEFAULT_TIMEOUT = 30
class IrunClient:
"""铱云易订货 API 客户端"""
def __init__(self, client_id: str = None, client_secret: str = None,
access_token: str = None, base_url: str = DEFAULT_BASE_URL):
"""
初始化客户端
Args:
client_id: 开发者ID
client_secret: 密钥
access_token: 访问令牌(可直接传入,跳过授权步骤)
base_url: API基础地址
"""
self.client_id = client_id or os.getenv("IRUN_CLIENT_ID")
self.client_secret = client_secret or os.getenv("IRUN_CLIENT_SECRET")
self.access_token = access_token or os.getenv("IRUN_ACCESS_TOKEN")
self.base_url = base_url
self._token_expires_at = 0
def _get_headers(self) -> Dict[str, str]:
"""获取请求头"""
return {
"Content-Type": "application/json; charset=UTF-8",
"access_token": self.access_token,
"x-client-id": self.client_id
}
def _request(self, method: str, endpoint: str, params: Dict = None,
data: Dict = None) -> Dict[str, Any]:
"""
发送API请求
Args:
method: 请求方法 (GET, POST, PUT, DELETE)
endpoint: API端点
params: URL参数
data: 请求体数据
Returns:
API响应数据
"""
url = f"{self.base_url}{endpoint}"
headers = self._get_headers()
try:
response = requests.request(
method=method,
url=url,
headers=headers,
params=params,
json=data,
timeout=DEFAULT_TIMEOUT
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"code": 500, "message": str(e), "data": None}
def get_token(self) -> Dict[str, Any]:
"""
获取访问令牌
Returns:
包含access_token的响应
"""
if not self.client_id or not self.client_secret:
return {"code": 400, "message": "缺少 client_id 或 client_secret"}
url = f"{self.base_url}/oauth/token"
data = {
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret
}
try:
response = requests.post(url, json=data, timeout=DEFAULT_TIMEOUT)
response.raise_for_status()
result = response.json()
if result.get("code") == 200:
self.access_token = result["data"]["access_token"]
self._token_expires_at = time.time() + result["data"].get("expires_in", 7200)
return result
except requests.exceptions.RequestException as e:
return {"code": 500, "message": str(e), "data": None}
# ==================== 客户管理 ====================
def get_customer_list(self, page_num: int = 1, page_size: int = 10,
name: str = None, category_id: str = None) -> Dict[str, Any]:
"""获取客户列表"""
params = {"pageNum": page_num, "pageSize": page_size}
if name:
params["name"] = name
if category_id:
params["categoryId"] = category_id
return self._request("GET", "/openapi/customer/list", params=params)
def get_customer_detail(self, customer_id: str) -> Dict[str, Any]:
"""获取客户详情"""
return self._request("GET", f"/openapi/customer/{customer_id}")
def get_customer_categories(self) -> Dict[str, Any]:
"""获取客户分类列表"""
return self._request("GET", "/openapi/customer/category/list")
# ==================== 商品管理 ====================
def get_product_list(self, page_num: int = 1, page_size: int = 10,
name: str = None, category_id: str = None) -> Dict[str, Any]:
"""获取商品列表"""
params = {"pageNum": page_num, "pageSize": page_size}
if name:
params["name"] = name
if category_id:
params["categoryId"] = category_id
return self._request("GET", "/openapi/product/list", params=params)
def get_product_detail(self, product_id: str) -> Dict[str, Any]:
"""获取商品详情"""
return self._request("GET", f"/openapi/product/{product_id}")
def get_category_list(self) -> Dict[str, Any]:
"""获取商品分类列表"""
return self._request("GET", "/openapi/category/list")
# ==================== 订单管理 ====================
def get_order_list(self, page_num: int = 1, page_size: int = 10,
order_no: str = None, customer_id: str = None,
start_time: int = None, end_time: int = None,
status: str = None) -> Dict[str, Any]:
"""获取订单列表"""
params = {"pageNum": page_num, "pageSize": page_size}
if order_no:
params["orderNo"] = order_no
if customer_id:
params["customerId"] = customer_id
if start_time:
params["startTime"] = start_time
if end_time:
params["endTime"] = end_time
if status:
params["status"] = status
return self._request("GET", "/openapi/order/list", params=params)
def get_order_detail(self, order_id: str) -> Dict[str, Any]:
"""获取订单详情"""
return self._request("GET", f"/openapi/order/{order_id}")
def get_refund_list(self, page_num: int = 1, page_size: int = 10,
order_no: str = None, customer_id: str = None,
start_time: int = None, end_time: int = None) -> Dict[str, Any]:
"""获取退单列表"""
params = {"pageNum": page_num, "pageSize": page_size}
if order_no:
params["orderNo"] = order_no
if customer_id:
params["customerId"] = customer_id
if start_time:
params["startTime"] = start_time
if end_time:
params["endTime"] = end_time
return self._request("GET", "/openapi/refund/list", params=params)
def get_refund_detail(self, refund_id: str) -> Dict[str, Any]:
"""获取退单详情"""
return self._request("GET", f"/openapi/refund/{refund_id}")
# ==================== 进销存 ====================
def get_inventory_list(self, warehouse_id: str = None,
product_id: str = None) -> Dict[str, Any]:
"""查询库存列表"""
params = {}
if warehouse_id:
params["warehouseId"] = warehouse_id
if product_id:
params["productId"] = product_id
return self._request("GET", "/openapi/inventory/list", params=params)
def get_warehouse_list(self) -> Dict[str, Any]:
"""获取仓库列表"""
return self._request("GET", "/openapi/warehouse/list")
def get_storage_list(self, storage_type: str = None,
start_time: int = None, end_time: int = None) -> Dict[str, Any]:
"""获取出入库记录"""
params = {}
if storage_type:
params["type"] = storage_type
if start_time:
params["startTime"] = start_time
if end_time:
params["endTime"] = end_time
return self._request("GET", "/openapi/storage/list", params=params)
# ==================== 资金管理 ====================
def get_payment_list(self, page_num: int = 1, page_size: int = 10,
payment_type: str = None, start_time: int = None,
end_time: int = None) -> Dict[str, Any]:
"""获取收付款记录"""
params = {"pageNum": page_num, "pageSize": page_size}
if payment_type:
params["type"] = payment_type
if start_time:
params["startTime"] = start_time
if end_time:
params["endTime"] = end_time
return self._request("GET", "/openapi/payment/list", params=params)
# ==================== 基础数据 ====================
def get_employee_list(self) -> Dict[str, Any]:
"""获取员工列表"""
return self._request("GET", "/openapi/employee/list")
def get_area_list(self) -> Dict[str, Any]:
"""获取省市数据"""
return self._request("GET", "/openapi/area/province")
def get_logistics_company_list(self) -> Dict[str, Any]:
"""获取物流公司列表"""
return self._request("GET", "/openapi/logistics/company")
# ==================== 便捷函数 ====================
def create_client(client_id: str = None, client_secret: str = None,
access_token: str = None) -> IrunClient:
"""创建API客户端"""
return IrunClient(client_id, client_secret, access_token)
def get_timestamp(days_ago: int = 0) -> int:
"""获取时间戳"""
import datetime
return int((datetime.datetime.now() - datetime.timedelta(days=days_ago)).timestamp() * 1000)
if __name__ == "__main__":
# 示例用法
client = IrunClient()
# 获取Token
# result = client.get_token()
# print(result)
# 查询客户
# result = client.get_customer_list(page_num=1, page_size=10)
# print(result)
# 查询订单
# result = client.get_order_list(start_time=get_timestamp(30))
# print(result)
print("铱云易订货 API 客户端示例")