n8n-nodes-kuaimai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,356 @@
1
+ # n8n-nodes-kuaimai
2
+
3
+ 快麦ERP开放平台的 n8n 社区节点,实现与快麦ERP系统的API集成。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/n8n-nodes-kuaimai.svg)](https://www.npmjs.com/package/n8n-nodes-kuaimai)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## 📋 目录
9
+
10
+ - [特性](#特性)
11
+ - [安装](#安装)
12
+ - [快速开始](#快速开始)
13
+ - [配置说明](#配置说明)
14
+ - [支持的操作](#支持的操作)
15
+ - [开发指南](#开发指南)
16
+ - [常见问题](#常见问题)
17
+ - [许可证](#许可证)
18
+
19
+ ## ✨ 特性
20
+
21
+ - ✅ **完整的 API 支持**:基于快麦开放平台官方文档实现
22
+ - 🔐 **安全认证**:支持 HMAC-MD5、MD5、HMAC-SHA256 签名算法
23
+ - 🔄 **会话管理**:自动处理 Token 刷新(30天有效期)
24
+ - 📦 **资源管理**:订单、商品、仓库、基础数据查询
25
+ - 🎯 **易于使用**:可视化配置,无需编写代码
26
+ - 🚀 **Docker 部署**:提供完整的 Docker Compose 配置
27
+
28
+ ## 📦 安装
29
+
30
+ ### 方式一:Docker Compose(推荐)
31
+
32
+ #### 快速开始
33
+
34
+ 1. **获取 GitHub Token**
35
+
36
+ 访问 [GitHub Settings](https://github.com/settings/tokens) 创建一个带有 `read:packages` 权限的 Personal Access Token。
37
+
38
+ 2. **配置环境变量**
39
+
40
+ ```bash
41
+ # 复制环境变量模板
42
+ cp .env.example .env
43
+
44
+ # 编辑 .env 文件,填入你的 GitHub Token
45
+ ```
46
+
47
+ 3. **启动服务**
48
+
49
+ ```bash
50
+ # 启动 n8n 和 PostgreSQL
51
+ docker-compose up -d
52
+
53
+ # 查看日志
54
+ docker-compose logs -f n8n
55
+ ```
56
+
57
+ 4. **访问 n8n**
58
+
59
+ 打开浏览器访问 http://localhost:5678
60
+
61
+ 详细部署说明请参考 [QUICKSTART.md](./QUICKSTART.md) 和 [DOCKER_DEPLOYMENT.md](./DOCKER_DEPLOYMENT.md)。
62
+
63
+ ### 方式二:npm 安装
64
+
65
+ ```bash
66
+ npm install @gabrielsworld/n8n-node-kuaimai --registry=https://npm.pkg.github.com
67
+ ```
68
+
69
+ ## 🚀 快速开始
70
+
71
+ ### 1. 获取快麦 API 凭证
72
+
73
+ 1. 联系快麦ERP销售或实施同学申请 API 权限
74
+ 2. 访问 [快麦开放平台](https://open.kuaimai.com/applyApp) 填写申请表
75
+ 3. 审核通过后,您将收到包含以下信息的邮件:
76
+ - **App Key**
77
+ - **App Secret**
78
+ - **Access Token**
79
+ - **Refresh Token**
80
+
81
+ ### 2. 在 n8n 中配置凭证
82
+
83
+ 1. 在 n8n 中打开 **Credentials** 页面
84
+ 2. 点击 **New Credential**
85
+ 3. 搜索并选择 **快麦ERP API**
86
+ 4. 填入从邮件中获取的凭证信息:
87
+
88
+ | 字段 | 说明 | 示例 |
89
+ | ------------- | ---------------------- | ------------------ |
90
+ | App Key | 开放平台分配的应用密钥 | `123456` |
91
+ | App Secret | 开放平台分配的应用秘钥 | `helloworld` |
92
+ | Access Token | 访问令牌(session) | `xxxx-xxxx-xxxx` |
93
+ | Refresh Token | 刷新令牌 | `yyyy-yyyy-yyyy` |
94
+ | 签名方法 | 签名算法 | `HMAC-MD5`(推荐) |
95
+ | API 环境 | 请求环境 | `V2正式环境` |
96
+
97
+ 5. 点击 **Save**
98
+
99
+ ### 3. 使用节点
100
+
101
+ 在工作流中添加 **快麦ERP** 节点,选择要执行的操作:
102
+
103
+ ```
104
+ 快麦ERP 节点
105
+ ├── 基础
106
+ │ ├── 刷新会话
107
+ │ ├── 查询仓库列表
108
+ │ └── 查询店铺列表
109
+ ├── 订单
110
+ │ └── 查询订单列表
111
+ ├── 商品
112
+ │ └── 查询商品列表
113
+ └── 仓库
114
+ └── 查询库存
115
+ ```
116
+
117
+ ## ⚙️ 配置说明
118
+
119
+ ### 签名方法
120
+
121
+ 快麦开放平台支持三种签名算法:
122
+
123
+ | 方法 | 说明 | 安全性 | 推荐 |
124
+ | ------------ | ------------------------- | -------- | ------- |
125
+ | **HMAC-MD5** | HMAC 消息认证码(MD5) | ⭐⭐⭐ | ✅ 推荐 |
126
+ | MD5 | 简单 MD5 摘要 | ⭐⭐ | ⚠️ |
127
+ | HMAC-SHA256 | HMAC 消息认证码(SHA256) | ⭐⭐⭐⭐ | ✅ 推荐 |
128
+
129
+ ### 会话管理
130
+
131
+ - **有效期**:Access Token 有效期为 **30天**
132
+ - **刷新**:必须在会话过期前调用 **刷新会话** 操作
133
+ - **限制**:刷新接口每小时最多调用 1 次
134
+
135
+ ⚠️ **重要**:建议每月至少刷新一次会话,避免 Token 过期。
136
+
137
+ ### API 环境
138
+
139
+ | 环境 | 地址 | 说明 |
140
+ | ------------------ | --------------------------------- | --------------------------------- |
141
+ | V2正式环境(推荐) | `https://gw.superboss.cc/router` | 2022年4月1日后申请的 App Key 使用 |
142
+ | V2内部专用 | `https://gw3.superboss.cc/router` | 内部测试使用 |
143
+
144
+ ## 🔧 支持的操作
145
+
146
+ ### 基础操作
147
+
148
+ #### 刷新会话
149
+
150
+ 延长 Access Token 有效期(30天)。
151
+
152
+ **输入**:无需额外参数(使用凭证中的 Refresh Token)
153
+
154
+ **输出**:
155
+
156
+ ```json
157
+ {
158
+ "session": {
159
+ "accessToken": "新的访问令牌",
160
+ "refreshToken": "新的刷新令牌",
161
+ "expiresIn": 2592000,
162
+ "userId": 12345,
163
+ "companyId": 67890
164
+ }
165
+ }
166
+ ```
167
+
168
+ #### 查询仓库列表
169
+
170
+ 查询公司下的所有仓库。
171
+
172
+ **API 方法**:`erp.warehouse.query`
173
+
174
+ #### 查询店铺列表
175
+
176
+ 查询公司下的所有店铺。
177
+
178
+ **API 方法**:`erp.shop.query`
179
+
180
+ ### 订单操作
181
+
182
+ #### 查询订单列表
183
+
184
+ 查询非淘系/拼多多订单列表。
185
+
186
+ **API 方法**:`erp.trade.list.query`
187
+
188
+ **参数**:
189
+
190
+ | 参数 | 类型 | 必填 | 说明 |
191
+ | -------- | -------- | ---- | -------------------------------- |
192
+ | 时间类型 | 选项 | ✅ | `创建时间`/`更新时间`/`付款时间` |
193
+ | 开始时间 | 日期时间 | ✅ | 查询起始时间 |
194
+ | 结束时间 | 日期时间 | ✅ | 查询结束时间 |
195
+ | 页码 | 数字 | ✅ | 从 1 开始 |
196
+ | 每页数量 | 数字 | ✅ | 1-100 |
197
+
198
+ **示例**:
199
+
200
+ ```javascript
201
+ // 工作流配置
202
+ {
203
+ "resource": "order",
204
+ "operation": "queryOrders",
205
+ "timeType": "upd_time",
206
+ "startTime": "2024-01-01 00:00:00",
207
+ "endTime": "2024-01-31 23:59:59",
208
+ "pageNo": 1,
209
+ "pageSize": 20
210
+ }
211
+ ```
212
+
213
+ ### 商品操作
214
+
215
+ #### 查询商品列表
216
+
217
+ 查询商品信息。
218
+
219
+ **API 方法**:`erp.item.list.query`
220
+
221
+ ### 仓库操作
222
+
223
+ #### 查询库存
224
+
225
+ 查询商品库存信息。
226
+
227
+ **API 方法**:`erp.inventory.query`
228
+
229
+ ## 🛠️ 开发指南
230
+
231
+ ### 环境要求
232
+
233
+ - Node.js >= 18.x
234
+ - npm >= 8.x
235
+ - TypeScript >= 5.x
236
+
237
+ ### 本地开发
238
+
239
+ ```bash
240
+ # 克隆项目
241
+ git clone https://github.com/gabrielsworld/n8n-nodes-kuaimai.git
242
+ cd n8n-nodes-kuaimai
243
+
244
+ # 安装依赖
245
+ npm install
246
+
247
+ # 开发模式(监听文件变化)
248
+ npm run dev
249
+
250
+ # 构建
251
+ npm run build
252
+
253
+ # 运行测试
254
+ npm test
255
+
256
+ # 代码检查
257
+ npm run lint
258
+
259
+ # 格式化代码
260
+ npm run format
261
+ ```
262
+
263
+ ### 项目结构
264
+
265
+ ```
266
+ ├── credentials/ # 凭证定义
267
+ │ └── KuaimaiCredentials.credentials.ts
268
+ ├── nodes/ # 节点实现
269
+ │ └── KuaimaiErp/
270
+ │ ├── KuaimaiErp.node.ts # 主节点
271
+ │ ├── KuaimaiSignature.ts # 签名工具
272
+ │ └── kuaimai.svg # 图标
273
+ ├── test/ # 测试文件
274
+ ├── dist/ # 构建输出
275
+ ├── docker-compose.yml # Docker 开发环境
276
+ ├── docker-compose.prod.yml # Docker 生产环境
277
+ ├── .env.example # 环境变量模板
278
+ └── package.json
279
+ ```
280
+
281
+ ### 添加新操作
282
+
283
+ 1. 在 `KuaimaiErp.node.ts` 中添加操作选项
284
+ 2. 添加对应的参数配置
285
+ 3. 在 `execute` 方法中实现业务逻辑
286
+ 4. 更新文档和测试
287
+
288
+ 详细开发指南请参考 [AGENTS.md](./AGENTS.md)。
289
+
290
+ ## ❓ 常见问题
291
+
292
+ ### 1. 节点不显示?
293
+
294
+ **解决方案**:
295
+
296
+ ```bash
297
+ # 检查日志
298
+ docker-compose logs n8n | grep -i "custom"
299
+
300
+ # 重启容器
301
+ docker-compose restart n8n
302
+ ```
303
+
304
+ ### 2. 会话过期怎么办?
305
+
306
+ 如果 Access Token 过期(超过30天未刷新):
307
+
308
+ 1. 联系快麦实施或客服
309
+ 2. 在问题群告知业务机器人
310
+ 3. 格式:`公司名称-开放平台刷新授权-appKey`
311
+
312
+ ### 3. 签名错误?
313
+
314
+ 常见原因:
315
+
316
+ - App Secret 填写错误
317
+ - 时间戳误差超过10分钟
318
+ - 参数编码问题
319
+
320
+ **调试工具**:使用 [快麦 API 测试工具](https://open.kuaimai.com/tools) 验证签名。
321
+
322
+ ### 4. 如何获取拼多多订单?
323
+
324
+ 拼多多订单需要单独接入自研接口,详见:
325
+ [拼多多自研接口接入公告](https://open.kuaimai.com/docs/question/系统公告/拼多多自研接口接入开放公告)
326
+
327
+ ### 5. API 调用频率限制?
328
+
329
+ - 刷新会话接口:每小时 1 次
330
+ - 其他接口:具体限制请参考快麦开放平台文档
331
+
332
+ ## 📚 相关文档
333
+
334
+ - [快速开始指南](./QUICKSTART.md)
335
+ - [Docker 部署文档](./DOCKER_DEPLOYMENT.md)
336
+ - [部署检查清单](./DEPLOYMENT_CHECKLIST.md)
337
+ - [开发指南](./AGENTS.md)
338
+ - [快麦开放平台官方文档](https://open.kuaimai.com/)
339
+
340
+ ## 🤝 贡献
341
+
342
+ 欢迎提交 Issue 和 Pull Request!
343
+
344
+ ## 📄 许可证
345
+
346
+ [MIT License](LICENSE)
347
+
348
+ ## 📞 支持
349
+
350
+ - 📧 Email: gabrielsworld@github.com
351
+ - 🐛 Issues: https://github.com/gabrielsworld/n8n-nodes-kuaimai/issues
352
+ - 📖 文档: https://open.kuaimai.com/
353
+
354
+ ---
355
+
356
+ **Made with ❤️ by gabrielsworld**
@@ -0,0 +1,8 @@
1
+ import { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class KuaimaiCredentials implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ test: ICredentialTestRequest;
8
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KuaimaiCredentials = void 0;
4
+ class KuaimaiCredentials {
5
+ constructor() {
6
+ this.name = 'kuaimaiApi';
7
+ this.displayName = '快麦ERP API';
8
+ this.documentationUrl = 'https://open.kuaimai.com/docs/api/API对接说明/接入指南';
9
+ this.properties = [
10
+ {
11
+ displayName: 'App Key',
12
+ name: 'appKey',
13
+ type: 'string',
14
+ default: '',
15
+ required: true,
16
+ description: '快麦开放平台分配的 App Key',
17
+ },
18
+ {
19
+ displayName: 'App Secret',
20
+ name: 'appSecret',
21
+ type: 'string',
22
+ typeOptions: {
23
+ password: true,
24
+ },
25
+ default: '',
26
+ required: true,
27
+ description: '快麦开放平台分配的 App Secret',
28
+ },
29
+ {
30
+ displayName: 'Access Token',
31
+ name: 'accessToken',
32
+ type: 'string',
33
+ typeOptions: {
34
+ password: true,
35
+ },
36
+ default: '',
37
+ required: true,
38
+ description: '访问令牌(session),由快麦开放平台邮件提供',
39
+ },
40
+ {
41
+ displayName: 'Refresh Token',
42
+ name: 'refreshToken',
43
+ type: 'string',
44
+ typeOptions: {
45
+ password: true,
46
+ },
47
+ default: '',
48
+ required: true,
49
+ description: '刷新令牌,用于延长会话有效期(30天)',
50
+ },
51
+ {
52
+ displayName: '签名方法',
53
+ name: 'signMethod',
54
+ type: 'options',
55
+ options: [
56
+ {
57
+ name: 'HMAC-MD5',
58
+ value: 'hmac',
59
+ },
60
+ {
61
+ name: 'MD5',
62
+ value: 'md5',
63
+ },
64
+ {
65
+ name: 'HMAC-SHA256',
66
+ value: 'hmac-sha256',
67
+ },
68
+ ],
69
+ default: 'hmac',
70
+ description: '签名摘要算法',
71
+ },
72
+ {
73
+ displayName: 'API 环境',
74
+ name: 'environment',
75
+ type: 'options',
76
+ options: [
77
+ {
78
+ name: 'V2正式环境',
79
+ value: 'production',
80
+ },
81
+ {
82
+ name: 'V2内部专用',
83
+ value: 'internal',
84
+ },
85
+ ],
86
+ default: 'production',
87
+ description: 'API 请求环境',
88
+ },
89
+ ];
90
+ this.test = {
91
+ request: {
92
+ baseURL: '={{$credentials.environment === "internal" ? "https://gw3.superboss.cc" : "https://gw.superboss.cc"}}',
93
+ url: '/router',
94
+ method: 'POST',
95
+ },
96
+ };
97
+ }
98
+ }
99
+ exports.KuaimaiCredentials = KuaimaiCredentials;
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class KuaimaiErp implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,401 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KuaimaiErp = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const KuaimaiSignature_1 = require("./KuaimaiSignature");
6
+ class KuaimaiErp {
7
+ constructor() {
8
+ this.description = {
9
+ displayName: '快麦ERP',
10
+ name: 'kuaimaiErp',
11
+ icon: 'file:kuaimai.svg',
12
+ group: ['transform'],
13
+ version: 1,
14
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
15
+ description: '与快麦ERP开放平台API交互,进行订单、商品和库存管理',
16
+ defaults: {
17
+ name: '快麦ERP',
18
+ },
19
+ inputs: ['main'],
20
+ outputs: ['main'],
21
+ credentials: [
22
+ {
23
+ name: 'kuaimaiApi',
24
+ required: true,
25
+ },
26
+ ],
27
+ properties: [
28
+ {
29
+ displayName: '资源类型',
30
+ name: 'resource',
31
+ type: 'options',
32
+ noDataExpression: true,
33
+ options: [
34
+ {
35
+ name: '基础',
36
+ value: 'base',
37
+ },
38
+ {
39
+ name: '订单',
40
+ value: 'order',
41
+ },
42
+ {
43
+ name: '商品',
44
+ value: 'product',
45
+ },
46
+ {
47
+ name: '仓库',
48
+ value: 'warehouse',
49
+ },
50
+ ],
51
+ default: 'base',
52
+ },
53
+ // ==================== 基础操作 ====================
54
+ {
55
+ displayName: '操作',
56
+ name: 'operation',
57
+ type: 'options',
58
+ noDataExpression: true,
59
+ displayOptions: {
60
+ show: {
61
+ resource: ['base'],
62
+ },
63
+ },
64
+ options: [
65
+ {
66
+ name: '刷新会话',
67
+ value: 'refreshToken',
68
+ description: '刷新会话 Token,延长30天有效期',
69
+ action: 'Refresh session token',
70
+ },
71
+ {
72
+ name: '查询仓库列表',
73
+ value: 'listWarehouses',
74
+ description: '查询公司下的仓库列表',
75
+ action: 'List warehouses',
76
+ },
77
+ {
78
+ name: '查询店铺列表',
79
+ value: 'listShops',
80
+ description: '查询公司下的店铺列表',
81
+ action: 'List shops',
82
+ },
83
+ ],
84
+ default: 'refreshToken',
85
+ },
86
+ // ==================== 订单操作 ====================
87
+ {
88
+ displayName: '操作',
89
+ name: 'operation',
90
+ type: 'options',
91
+ noDataExpression: true,
92
+ displayOptions: {
93
+ show: {
94
+ resource: ['order'],
95
+ },
96
+ },
97
+ options: [
98
+ {
99
+ name: '查询订单列表',
100
+ value: 'queryOrders',
101
+ description: '查询订单列表(非淘系/拼多多)',
102
+ action: 'Query orders',
103
+ },
104
+ ],
105
+ default: 'queryOrders',
106
+ },
107
+ // 订单查询参数
108
+ {
109
+ displayName: '时间类型',
110
+ name: 'timeType',
111
+ type: 'options',
112
+ displayOptions: {
113
+ show: {
114
+ resource: ['order'],
115
+ operation: ['queryOrders'],
116
+ },
117
+ },
118
+ options: [
119
+ {
120
+ name: '创建时间',
121
+ value: 'create_time',
122
+ },
123
+ {
124
+ name: '更新时间',
125
+ value: 'upd_time',
126
+ },
127
+ {
128
+ name: '付款时间',
129
+ value: 'pay_time',
130
+ },
131
+ ],
132
+ default: 'upd_time',
133
+ description: '查询订单的时间类型',
134
+ },
135
+ {
136
+ displayName: '开始时间',
137
+ name: 'startTime',
138
+ type: 'dateTime',
139
+ displayOptions: {
140
+ show: {
141
+ resource: ['order'],
142
+ operation: ['queryOrders'],
143
+ },
144
+ },
145
+ default: '',
146
+ required: true,
147
+ description: '查询开始时间',
148
+ },
149
+ {
150
+ displayName: '结束时间',
151
+ name: 'endTime',
152
+ type: 'dateTime',
153
+ displayOptions: {
154
+ show: {
155
+ resource: ['order'],
156
+ operation: ['queryOrders'],
157
+ },
158
+ },
159
+ default: '',
160
+ required: true,
161
+ description: '查询结束时间',
162
+ },
163
+ {
164
+ displayName: '页码',
165
+ name: 'pageNo',
166
+ type: 'number',
167
+ displayOptions: {
168
+ show: {
169
+ resource: ['order'],
170
+ operation: ['queryOrders'],
171
+ },
172
+ },
173
+ default: 1,
174
+ description: '页码,从1开始',
175
+ },
176
+ {
177
+ displayName: '每页数量',
178
+ name: 'pageSize',
179
+ type: 'number',
180
+ displayOptions: {
181
+ show: {
182
+ resource: ['order'],
183
+ operation: ['queryOrders'],
184
+ },
185
+ },
186
+ default: 20,
187
+ typeOptions: {
188
+ minValue: 1,
189
+ maxValue: 100,
190
+ },
191
+ description: '每页返回的记录数,最大100',
192
+ },
193
+ // ==================== 商品操作 ====================
194
+ {
195
+ displayName: '操作',
196
+ name: 'operation',
197
+ type: 'options',
198
+ noDataExpression: true,
199
+ displayOptions: {
200
+ show: {
201
+ resource: ['product'],
202
+ },
203
+ },
204
+ options: [
205
+ {
206
+ name: '查询商品列表',
207
+ value: 'queryProducts',
208
+ action: 'Query products',
209
+ },
210
+ ],
211
+ default: 'queryProducts',
212
+ },
213
+ // ==================== 仓库操作 ====================
214
+ {
215
+ displayName: '操作',
216
+ name: 'operation',
217
+ type: 'options',
218
+ noDataExpression: true,
219
+ displayOptions: {
220
+ show: {
221
+ resource: ['warehouse'],
222
+ },
223
+ },
224
+ options: [
225
+ {
226
+ name: '查询库存',
227
+ value: 'queryInventory',
228
+ description: '查询商品库存信息',
229
+ action: 'Query inventory',
230
+ },
231
+ ],
232
+ default: 'queryInventory',
233
+ },
234
+ // ==================== 附加字段 ====================
235
+ {
236
+ displayName: '附加字段',
237
+ name: 'additionalFields',
238
+ type: 'collection',
239
+ placeholder: '添加字段',
240
+ default: {},
241
+ options: [
242
+ {
243
+ displayName: '自定义参数',
244
+ name: 'customParams',
245
+ type: 'fixedCollection',
246
+ placeholder: '添加参数',
247
+ default: {},
248
+ typeOptions: {
249
+ multipleValues: true,
250
+ },
251
+ options: [
252
+ {
253
+ name: 'parameter',
254
+ displayName: '参数',
255
+ values: [
256
+ {
257
+ displayName: '参数名',
258
+ name: 'name',
259
+ type: 'string',
260
+ default: '',
261
+ description: '参数名称',
262
+ },
263
+ {
264
+ displayName: '参数值',
265
+ name: 'value',
266
+ type: 'string',
267
+ default: '',
268
+ },
269
+ ],
270
+ },
271
+ ],
272
+ description: '自定义请求参数',
273
+ },
274
+ ],
275
+ },
276
+ ],
277
+ };
278
+ }
279
+ async execute() {
280
+ const items = this.getInputData();
281
+ const returnData = [];
282
+ for (let i = 0; i < items.length; i++) {
283
+ try {
284
+ const credentials = await this.getCredentials('kuaimaiApi');
285
+ const resource = this.getNodeParameter('resource', i);
286
+ const operation = this.getNodeParameter('operation', i);
287
+ const appKey = credentials.appKey;
288
+ const appSecret = credentials.appSecret;
289
+ const accessToken = credentials.accessToken;
290
+ const refreshToken = credentials.refreshToken;
291
+ const signMethod = credentials.signMethod;
292
+ const environment = credentials.environment;
293
+ // 确定 API 端点
294
+ const baseURL = environment === 'internal' ? 'https://gw3.superboss.cc' : 'https://gw.superboss.cc';
295
+ let method = '';
296
+ const businessParams = {};
297
+ // 根据资源和操作确定 API 方法和参数
298
+ if (resource === 'base') {
299
+ if (operation === 'refreshToken') {
300
+ method = 'open.token.refresh';
301
+ businessParams.refreshToken = refreshToken;
302
+ }
303
+ else if (operation === 'listWarehouses') {
304
+ method = 'erp.warehouse.query';
305
+ }
306
+ else if (operation === 'listShops') {
307
+ method = 'erp.shop.query';
308
+ }
309
+ }
310
+ else if (resource === 'order') {
311
+ if (operation === 'queryOrders') {
312
+ method = 'erp.trade.list.query';
313
+ const timeType = this.getNodeParameter('timeType', i);
314
+ const startTime = this.getNodeParameter('startTime', i);
315
+ const endTime = this.getNodeParameter('endTime', i);
316
+ const pageNo = this.getNodeParameter('pageNo', i);
317
+ const pageSize = this.getNodeParameter('pageSize', i);
318
+ businessParams.timeType = timeType;
319
+ businessParams.startTime = formatDateTimeForKuaimai(new Date(startTime));
320
+ businessParams.endTime = formatDateTimeForKuaimai(new Date(endTime));
321
+ businessParams.pageNo = String(pageNo);
322
+ businessParams.pageSize = String(pageSize);
323
+ }
324
+ }
325
+ else if (resource === 'product') {
326
+ if (operation === 'queryProducts') {
327
+ method = 'erp.item.list.query';
328
+ }
329
+ }
330
+ else if (resource === 'warehouse') {
331
+ if (operation === 'queryInventory') {
332
+ method = 'erp.inventory.query';
333
+ }
334
+ }
335
+ // 处理附加字段
336
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
337
+ if (additionalFields.customParams) {
338
+ const customParams = additionalFields.customParams;
339
+ if (customParams.parameter && Array.isArray(customParams.parameter)) {
340
+ customParams.parameter.forEach((param) => {
341
+ const name = param.name;
342
+ const value = param.value;
343
+ if (name && value !== undefined) {
344
+ businessParams[name] = value;
345
+ }
346
+ });
347
+ }
348
+ }
349
+ // 构建完整的请求参数(包含签名)
350
+ const requestParams = (0, KuaimaiSignature_1.buildKuaimaiRequestParams)(method, accessToken, appKey, appSecret, signMethod, businessParams);
351
+ // 发起 API 请求
352
+ const response = await this.helpers.request({
353
+ method: 'POST',
354
+ url: `${baseURL}/router`,
355
+ headers: {
356
+ 'Content-Type': 'application/x-www-form-urlencoded',
357
+ },
358
+ form: requestParams,
359
+ json: true,
360
+ });
361
+ // 检查响应
362
+ if (response.success === false) {
363
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `快麦 API 错误: ${response.msg || '未知错误'} (错误码: ${response.code || 'N/A'})`, {
364
+ description: `Trace ID: ${response.trace_id || 'N/A'}`,
365
+ });
366
+ }
367
+ returnData.push({
368
+ json: response,
369
+ });
370
+ }
371
+ catch (error) {
372
+ if (this.continueOnFail()) {
373
+ returnData.push({
374
+ json: {
375
+ error: error instanceof Error ? error.message : String(error),
376
+ },
377
+ });
378
+ }
379
+ else {
380
+ throw error;
381
+ }
382
+ }
383
+ }
384
+ return [returnData];
385
+ }
386
+ }
387
+ exports.KuaimaiErp = KuaimaiErp;
388
+ /**
389
+ * 格式化日期时间为快麦要求的格式
390
+ * @param date 日期对象
391
+ * @returns 格式化的日期时间字符串 (yyyy-MM-dd HH:mm:ss)
392
+ */
393
+ function formatDateTimeForKuaimai(date) {
394
+ const year = date.getFullYear();
395
+ const month = String(date.getMonth() + 1).padStart(2, '0');
396
+ const day = String(date.getDate()).padStart(2, '0');
397
+ const hours = String(date.getHours()).padStart(2, '0');
398
+ const minutes = String(date.getMinutes()).padStart(2, '0');
399
+ const seconds = String(date.getSeconds()).padStart(2, '0');
400
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
401
+ }
@@ -0,0 +1,42 @@
1
+ import { IDataObject } from 'n8n-workflow';
2
+ /**
3
+ * 快麦开放平台签名工具
4
+ * 参考文档: https://open.kuaimai.com/docs/api/API对接说明/API调用方法详解
5
+ */
6
+ export interface SignatureParams {
7
+ [key: string]: string | number | boolean | undefined;
8
+ }
9
+ /**
10
+ * 生成快麦 API 签名
11
+ * @param params 所有请求参数(包括公共参数和业务参数,但不包括 sign 参数)
12
+ * @param secret App Secret
13
+ * @param signMethod 签名方法:hmac | md5 | hmac-sha256
14
+ * @returns 签名字符串(32位大写十六进制)
15
+ */
16
+ export declare function generateKuaimaiSignature(params: SignatureParams, secret: string, signMethod?: string): string;
17
+ /**
18
+ * 构建快麦 API 公共参数
19
+ * @param method API 方法名
20
+ * @param session Access Token
21
+ * @param appKey App Key
22
+ * @param additionalParams 额外的公共参数
23
+ * @returns 公共参数对象
24
+ */
25
+ export declare function buildKuaimaiCommonParams(method: string, session: string, appKey: string, additionalParams?: IDataObject): IDataObject;
26
+ /**
27
+ * 格式化时间为快麦要求的格式
28
+ * @param date 日期对象
29
+ * @returns 格式化的时间字符串 (yyyy-MM-dd HH:mm:ss)
30
+ */
31
+ export declare function formatKuaimaiTimestamp(date: Date): string;
32
+ /**
33
+ * 构建完整的 API 请求参数(包含签名)
34
+ * @param method API 方法名
35
+ * @param session Access Token
36
+ * @param appKey App Key
37
+ * @param appSecret App Secret
38
+ * @param signMethod 签名方法
39
+ * @param businessParams 业务参数
40
+ * @returns 包含签名的完整请求参数
41
+ */
42
+ export declare function buildKuaimaiRequestParams(method: string, session: string, appKey: string, appSecret: string, signMethod: string, businessParams?: IDataObject): IDataObject;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateKuaimaiSignature = generateKuaimaiSignature;
4
+ exports.buildKuaimaiCommonParams = buildKuaimaiCommonParams;
5
+ exports.formatKuaimaiTimestamp = formatKuaimaiTimestamp;
6
+ exports.buildKuaimaiRequestParams = buildKuaimaiRequestParams;
7
+ const crypto_1 = require("crypto");
8
+ /**
9
+ * 生成快麦 API 签名
10
+ * @param params 所有请求参数(包括公共参数和业务参数,但不包括 sign 参数)
11
+ * @param secret App Secret
12
+ * @param signMethod 签名方法:hmac | md5 | hmac-sha256
13
+ * @returns 签名字符串(32位大写十六进制)
14
+ */
15
+ function generateKuaimaiSignature(params, secret, signMethod = 'hmac') {
16
+ // 1. 移除 sign 参数、null 值和 undefined 值
17
+ const filteredParams = {};
18
+ Object.keys(params).forEach((key) => {
19
+ const value = params[key];
20
+ if (key !== 'sign' && value !== null && value !== undefined) {
21
+ filteredParams[key] = value;
22
+ }
23
+ });
24
+ // 2. 按照参数名 ASCII 码排序
25
+ const sortedKeys = Object.keys(filteredParams).sort();
26
+ // 3. 拼接参数名和参数值
27
+ let signString = '';
28
+ // MD5 方法需要在前后加 secret
29
+ if (signMethod === 'md5') {
30
+ signString = secret;
31
+ }
32
+ sortedKeys.forEach((key) => {
33
+ signString += key + String(filteredParams[key]);
34
+ });
35
+ if (signMethod === 'md5') {
36
+ signString += secret;
37
+ }
38
+ // 4. 生成签名
39
+ let signature;
40
+ switch (signMethod) {
41
+ case 'md5':
42
+ signature = (0, crypto_1.createHash)('md5').update(signString, 'utf8').digest('hex');
43
+ break;
44
+ case 'hmac':
45
+ signature = (0, crypto_1.createHmac)('md5', secret).update(signString, 'utf8').digest('hex');
46
+ break;
47
+ case 'hmac-sha256':
48
+ signature = (0, crypto_1.createHmac)('sha256', secret).update(signString, 'utf8').digest('hex');
49
+ break;
50
+ default:
51
+ throw new Error(`不支持的签名方法: ${signMethod}`);
52
+ }
53
+ // 5. 转换为大写并返回
54
+ return signature.toUpperCase();
55
+ }
56
+ /**
57
+ * 构建快麦 API 公共参数
58
+ * @param method API 方法名
59
+ * @param session Access Token
60
+ * @param appKey App Key
61
+ * @param additionalParams 额外的公共参数
62
+ * @returns 公共参数对象
63
+ */
64
+ function buildKuaimaiCommonParams(method, session, appKey, additionalParams) {
65
+ const timestamp = formatKuaimaiTimestamp(new Date());
66
+ const commonParams = {
67
+ method,
68
+ appKey,
69
+ timestamp,
70
+ format: 'json',
71
+ version: '1.0',
72
+ sign_method: (additionalParams === null || additionalParams === void 0 ? void 0 : additionalParams.sign_method) || 'hmac',
73
+ session,
74
+ };
75
+ // 合并额外的参数
76
+ if (additionalParams) {
77
+ Object.assign(commonParams, additionalParams);
78
+ }
79
+ return commonParams;
80
+ }
81
+ /**
82
+ * 格式化时间为快麦要求的格式
83
+ * @param date 日期对象
84
+ * @returns 格式化的时间字符串 (yyyy-MM-dd HH:mm:ss)
85
+ */
86
+ function formatKuaimaiTimestamp(date) {
87
+ const year = date.getFullYear();
88
+ const month = String(date.getMonth() + 1).padStart(2, '0');
89
+ const day = String(date.getDate()).padStart(2, '0');
90
+ const hours = String(date.getHours()).padStart(2, '0');
91
+ const minutes = String(date.getMinutes()).padStart(2, '0');
92
+ const seconds = String(date.getSeconds()).padStart(2, '0');
93
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
94
+ }
95
+ /**
96
+ * 构建完整的 API 请求参数(包含签名)
97
+ * @param method API 方法名
98
+ * @param session Access Token
99
+ * @param appKey App Key
100
+ * @param appSecret App Secret
101
+ * @param signMethod 签名方法
102
+ * @param businessParams 业务参数
103
+ * @returns 包含签名的完整请求参数
104
+ */
105
+ function buildKuaimaiRequestParams(method, session, appKey, appSecret, signMethod, businessParams) {
106
+ // 构建公共参数
107
+ const commonParams = buildKuaimaiCommonParams(method, session, appKey, {
108
+ sign_method: signMethod,
109
+ });
110
+ // 合并业务参数
111
+ const allParams = { ...commonParams };
112
+ if (businessParams) {
113
+ Object.keys(businessParams).forEach((key) => {
114
+ const value = businessParams[key];
115
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
116
+ allParams[key] = value;
117
+ }
118
+ });
119
+ }
120
+ // 生成签名
121
+ const sign = generateKuaimaiSignature(allParams, appSecret, signMethod);
122
+ // 返回完整参数(包含签名)
123
+ return {
124
+ ...allParams,
125
+ sign,
126
+ };
127
+ }
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
3
+ <circle cx="9" cy="9" r="2"/>
4
+ <path d="m21 15-3.086-3.086a2 2 0 0 0-1.414-.586H13l-2.5 2.5"/>
5
+ <path d="M3 3l3 3 2-2 4 4"/>
6
+ </svg>
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Main entry point for n8n node package
2
+ // This file exports all nodes and credentials
3
+
4
+ export * from './dist/nodes/KuaimaiErp/KuaimaiErp.node';
5
+ export * from './dist/credentials/KuaimaiCredentials.credentials';
Binary file
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
3
+ <circle cx="9" cy="9" r="2"/>
4
+ <path d="m21 15-3.086-3.086a2 2 0 0 0-1.414-.586H13l-2.5 2.5"/>
5
+ <path d="M3 3l3 3 2-2 4 4"/>
6
+ </svg>
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "n8n-nodes-kuaimai",
3
+ "version": "1.0.0",
4
+ "description": "快麦ERP开放平台的 n8n 节点 - 基于官方API实现订单、商品、库存管理",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "kuaimai",
9
+ "erp",
10
+ "ecommerce"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/gabrielsworld/n8n-nodes-kuaimai",
14
+ "author": {
15
+ "name": "gabrielsworld",
16
+ "email": "gabrielsworld@github.com"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/gabrielsworld/n8n-nodes-kuaimai.git"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "main": "index.js",
26
+ "scripts": {
27
+ "build": "tsc && gulp build:icons",
28
+ "dev": "tsc --watch",
29
+ "format": "prettier nodes credentials --write",
30
+ "lint": "eslint nodes credentials --ext .ts",
31
+ "lintfix": "eslint nodes credentials --ext .ts --fix",
32
+ "prepublishOnly": "npm run build && npm run lint -s",
33
+ "test": "jest",
34
+ "test:watch": "jest --watch",
35
+ "test:coverage": "jest --coverage"
36
+ },
37
+ "n8n": {
38
+ "n8nNodesApiVersion": 1,
39
+ "credentials": [
40
+ "dist/credentials/KuaimaiCredentials.credentials.js"
41
+ ],
42
+ "nodes": [
43
+ "dist/nodes/KuaimaiErp/KuaimaiErp.node.js"
44
+ ]
45
+ },
46
+ "devDependencies": {
47
+ "@types/jest": "^29.5.5",
48
+ "@types/nock": "^10.0.3",
49
+ "@types/node": "^18.16.16",
50
+ "@typescript-eslint/eslint-plugin": "^6.7.0",
51
+ "@typescript-eslint/parser": "^6.7.0",
52
+ "eslint": "^8.49.0",
53
+ "eslint-plugin-n8n-nodes-base": "^1.16.1",
54
+ "gulp": "^4.0.2",
55
+ "jest": "^29.7.0",
56
+ "n8n-workflow": "^1.15.0",
57
+ "nock": "^14.0.10",
58
+ "prettier": "^3.0.3",
59
+ "ts-jest": "^29.1.1",
60
+ "typescript": "^5.2.2"
61
+ },
62
+ "dependencies": {
63
+ "fast-check": "^3.13.2",
64
+ "n8n-core": "^1.15.0"
65
+ }
66
+ }
@@ -0,0 +1,86 @@
1
+ @echo off
2
+ REM 发布 n8n-nodes-kuaimai 到 npm 公共 registry
3
+ REM 使用方法: publish-npm.bat [你的2FA代码]
4
+
5
+ echo ========================================
6
+ echo 发布 n8n-nodes-kuaimai 到 npm
7
+ echo ========================================
8
+ echo.
9
+
10
+ REM 检查是否提供了 2FA 代码
11
+ if "%1"=="" (
12
+ echo [错误] 请提供 2FA 代码作为参数
13
+ echo.
14
+ echo 使用方法:
15
+ echo publish-npm.bat 123456
16
+ echo.
17
+ echo 其中 123456 是你的 npm 2FA 应用中显示的 6 位数字代码
18
+ echo.
19
+ pause
20
+ exit /b 1
21
+ )
22
+
23
+ set OTP_CODE=%1
24
+
25
+ echo [1/5] 检查 npm 登录状态...
26
+ npm whoami
27
+ if errorlevel 1 (
28
+ echo [错误] 未登录 npm,请先运行: npm login
29
+ pause
30
+ exit /b 1
31
+ )
32
+ echo.
33
+
34
+ echo [2/5] 检查 registry 配置...
35
+ npm config get registry
36
+ echo.
37
+
38
+ echo [3/5] 运行构建和检查...
39
+ call npm run build
40
+ if errorlevel 1 (
41
+ echo [错误] 构建失败
42
+ pause
43
+ exit /b 1
44
+ )
45
+ echo.
46
+
47
+ echo [4/5] 正在发布到 npm...
48
+ echo 使用 2FA 代码: %OTP_CODE%
49
+ npm publish --access public --registry=https://registry.npmjs.org/ --otp=%OTP_CODE%
50
+ if errorlevel 1 (
51
+ echo.
52
+ echo [错误] 发布失败
53
+ echo.
54
+ echo 可能的原因:
55
+ echo 1. 2FA 代码已过期(每 30 秒更新一次)
56
+ echo 2. 版本号已存在
57
+ echo 3. 网络问题
58
+ echo.
59
+ echo 解决方案:
60
+ echo - 获取新的 2FA 代码并重新运行
61
+ echo - 或运行: npm version patch 更新版本号
62
+ echo.
63
+ pause
64
+ exit /b 1
65
+ )
66
+ echo.
67
+
68
+ echo [5/5] 验证发布结果...
69
+ timeout /t 5 /nobreak >nul
70
+ echo.
71
+ echo ========================================
72
+ echo 发布成功!
73
+ echo ========================================
74
+ echo.
75
+ echo 包信息:
76
+ npm view n8n-nodes-kuaimai version
77
+ echo.
78
+ echo 访问 npm 页面:
79
+ echo https://www.npmjs.com/package/n8n-nodes-kuaimai
80
+ echo.
81
+ echo 在 n8n 中安装:
82
+ echo 1. 打开 http://localhost:5678
83
+ echo 2. Settings -^> Community nodes -^> Install
84
+ echo 3. 输入: n8n-nodes-kuaimai
85
+ echo.
86
+ pause