@besile/scm-cli 2026.3.31 → 2026.3.33
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.zh.md +1 -1
- package/package.json +1 -1
- package/src/cmd/auth/login.js +4 -1
- package/src/internal/auth/token-store.js +37 -0
- package/src/internal/index.js +1 -0
- package/src/shortcuts/material-orders/list.js +28 -28
- package/src/shortcuts/production-orders/list.js +4 -4
- package/src/shortcuts/production-orders/query.js +36 -36
- package/src/shortcuts/qc-orders/list.js +34 -34
package/README.zh.md
CHANGED
package/package.json
CHANGED
package/src/cmd/auth/login.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* scm-cli auth login command
|
|
7
7
|
*/
|
|
8
|
-
import { scmClient, loadConfig, createLogger } from '../../internal/index.js';
|
|
8
|
+
import { scmClient, loadConfig, createLogger, saveToken } from '../../internal/index.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Execute login
|
|
@@ -75,6 +75,9 @@ export function loginCommand(program) {
|
|
|
75
75
|
console.log('正在登录 SCM...');
|
|
76
76
|
const result = await doLogin(opts.openId, opts.mobile, opts.password);
|
|
77
77
|
|
|
78
|
+
// Save token to cache
|
|
79
|
+
await saveToken(opts.openId, result);
|
|
80
|
+
|
|
78
81
|
console.log('\n✅ 登录成功!');
|
|
79
82
|
console.log('───────────────');
|
|
80
83
|
console.log(`用户: ${result.username}`);
|
|
@@ -86,6 +86,43 @@ export async function checkToken(openId) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* 保存 Token 到缓存
|
|
91
|
+
*
|
|
92
|
+
* @param {string} openId - 飞书用户 openId
|
|
93
|
+
* @param {Object} tokenInfo - { access_token, expires_in, user_id, username }
|
|
94
|
+
*/
|
|
95
|
+
export async function saveToken(openId, tokenInfo) {
|
|
96
|
+
const { readFileSync, writeFileSync, mkdirSync } = await import('node:fs');
|
|
97
|
+
const { dirname } = await import('node:path');
|
|
98
|
+
|
|
99
|
+
// Ensure directory exists
|
|
100
|
+
const dir = dirname(TOKEN_FILE);
|
|
101
|
+
mkdirSync(dir, { recursive: true });
|
|
102
|
+
|
|
103
|
+
// Load existing tokens
|
|
104
|
+
let tokens = {};
|
|
105
|
+
try {
|
|
106
|
+
const existing = readFileSync(TOKEN_FILE, 'utf-8');
|
|
107
|
+
tokens = JSON.parse(existing);
|
|
108
|
+
} catch {
|
|
109
|
+
// File doesn't exist yet, start fresh
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const expiresIn = tokenInfo.expires_in || tokenInfo.expiresIn || 12 * 3600;
|
|
113
|
+
const now = Date.now() / 1000;
|
|
114
|
+
|
|
115
|
+
tokens[openId] = {
|
|
116
|
+
access_token: tokenInfo.access_token || tokenInfo.accessToken,
|
|
117
|
+
expires_at: now + expiresIn,
|
|
118
|
+
user_id: tokenInfo.user_id || tokenInfo.userId || '',
|
|
119
|
+
username: tokenInfo.username || '',
|
|
120
|
+
last_login: now,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2), 'utf-8');
|
|
124
|
+
}
|
|
125
|
+
|
|
89
126
|
/**
|
|
90
127
|
* Token 文件路径
|
|
91
128
|
*/
|
package/src/internal/index.js
CHANGED
|
@@ -15,57 +15,57 @@ export const MaterialOrdersList = {
|
|
|
15
15
|
scopes: [],
|
|
16
16
|
hasFormat: true,
|
|
17
17
|
flags: [
|
|
18
|
-
{ name: '
|
|
19
|
-
{ name: '
|
|
20
|
-
{ name: '
|
|
18
|
+
{ name: 'openId', type: 'string', required: true, description: '飞书用户 openId' },
|
|
19
|
+
{ name: 'pageNo', type: 'number', default: 1, description: '页码' },
|
|
20
|
+
{ name: 'pageSize', type: 'number', default: 20, description: '每页条数' },
|
|
21
21
|
// 订单信息
|
|
22
|
-
{ name: '
|
|
22
|
+
{ name: 'orderNo', type: 'string', description: '物料订单号' },
|
|
23
23
|
{ name: 'status', type: 'string', description: '物料订单状态: 0-待采购|1-采购中|2-部分到货|3-已到货|4-已取消' },
|
|
24
|
-
{ name: '
|
|
24
|
+
{ name: 'cooperationMode', type: 'string', description: '合作模式: 0-CMT|1-FOB' },
|
|
25
25
|
// 商品信息
|
|
26
|
-
{ name: '
|
|
27
|
-
{ name: '
|
|
28
|
-
{ name: '
|
|
26
|
+
{ name: 'goodsFullCode', type: 'string', description: '大货款号' },
|
|
27
|
+
{ name: 'goodsBatch', type: 'string', description: '大货批次' },
|
|
28
|
+
{ name: 'goodsType', type: 'string', description: '大货类型: 0-首单|1-加单|2-翻单' },
|
|
29
29
|
// 供应商
|
|
30
|
-
{ name: '
|
|
30
|
+
{ name: 'supplierName', type: 'string', description: '供应商名称' },
|
|
31
31
|
// 人员
|
|
32
|
-
{ name: '
|
|
33
|
-
{ name: '
|
|
34
|
-
{ name: '
|
|
32
|
+
{ name: 'purchaserIdList', type: 'string', description: '采购员 ID 列表 (逗号分隔)' },
|
|
33
|
+
{ name: 'pdUserIdList', type: 'string', description: 'PD ID 列表 (逗号分隔)' },
|
|
34
|
+
{ name: 'designerName', type: 'string', description: '设计师名称' },
|
|
35
35
|
// 时间
|
|
36
36
|
{ name: 'type', type: 'string', description: '时间查询类型: 0-上市|1-下单|2-交期|3-调整|4-关单|5-生产下单|6-工厂接单|7-计划到货|8-实际到货|9-需求到货' },
|
|
37
|
-
{ name: '
|
|
38
|
-
{ name: '
|
|
37
|
+
{ name: 'startTime', type: 'string', description: '开始时间 (ISO 8601)' },
|
|
38
|
+
{ name: 'endTime', type: 'string', description: '结束时间 (ISO 8601)' },
|
|
39
39
|
],
|
|
40
40
|
|
|
41
41
|
async execute(internal, runtime) {
|
|
42
42
|
const f = runtime.flags;
|
|
43
|
-
const openId = f['
|
|
43
|
+
const openId = f['openId'];
|
|
44
44
|
const token = await scmClient.getToken(openId, null, null);
|
|
45
45
|
|
|
46
46
|
const filter = {
|
|
47
|
-
pageNo: parseInt(f['
|
|
48
|
-
pageSize: Math.min(parseInt(f['
|
|
47
|
+
pageNo: parseInt(f['pageNo']) || 1,
|
|
48
|
+
pageSize: Math.min(parseInt(f['pageSize']) || 20, 100),
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
// 订单信息
|
|
52
|
-
if (f['
|
|
52
|
+
if (f['orderNo']) filter.orderNo = f['orderNo'];
|
|
53
53
|
if (f['status'] !== undefined) filter.status = parseInt(f['status']);
|
|
54
|
-
if (f['
|
|
54
|
+
if (f['cooperationMode'] !== undefined) filter.cooperationMode = parseInt(f['cooperationMode']);
|
|
55
55
|
// 商品信息
|
|
56
|
-
if (f['
|
|
57
|
-
if (f['
|
|
58
|
-
if (f['
|
|
56
|
+
if (f['goodsFullCode']) filter.goodsFullCode = f['goodsFullCode'];
|
|
57
|
+
if (f['goodsBatch']) filter.goodsBatch = f['goodsBatch'];
|
|
58
|
+
if (f['goodsType'] !== undefined) filter.goodsType = parseInt(f['goodsType']);
|
|
59
59
|
// 供应商
|
|
60
|
-
if (f['
|
|
60
|
+
if (f['supplierName']) filter.supplierName = f['supplierName'];
|
|
61
61
|
// 人员
|
|
62
|
-
if (f['
|
|
63
|
-
if (f['
|
|
64
|
-
if (f['
|
|
62
|
+
if (f['purchaserIdList']) filter.purchaserIdList = f['purchaserIdList'].split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
63
|
+
if (f['pdUserIdList']) filter.pdUserIdList = f['pdUserIdList'].split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
64
|
+
if (f['designerName']) filter.designerName = f['designerName'];
|
|
65
65
|
// 时间
|
|
66
66
|
if (f['type'] !== undefined) filter.type = parseInt(f['type']);
|
|
67
|
-
if (f['
|
|
68
|
-
if (f['
|
|
67
|
+
if (f['startTime']) filter.startTime = f['startTime'];
|
|
68
|
+
if (f['endTime']) filter.endTime = f['endTime'];
|
|
69
69
|
|
|
70
70
|
const body = { domain: 'goods_material', action: 'query', filter };
|
|
71
71
|
const endpoint = '/admin-api/api/ai-scm/execute';
|
|
@@ -18,13 +18,13 @@ export const ProductionOrdersList = {
|
|
|
18
18
|
scopes: [],
|
|
19
19
|
hasFormat: true,
|
|
20
20
|
flags: [
|
|
21
|
-
{ name: '
|
|
22
|
-
{ name: '
|
|
23
|
-
{ name: '
|
|
21
|
+
{ name: 'openId', type: 'string', required: true, description: '飞书用户 openId' },
|
|
22
|
+
{ name: 'pageNo', type: 'number', default: 1, description: '页码,从 1 开始' },
|
|
23
|
+
{ name: 'pageSize', type: 'number', default: 20, description: '每页条数,最大 100' },
|
|
24
24
|
],
|
|
25
25
|
|
|
26
26
|
async execute(internal, runtime) {
|
|
27
|
-
const { '
|
|
27
|
+
const { 'openId': openId, 'pageNo': pageNo, 'pageSize': pageSize } = runtime.flags;
|
|
28
28
|
const token = await scmClient.getToken(openId, null, null);
|
|
29
29
|
|
|
30
30
|
const body = {
|
|
@@ -18,62 +18,62 @@ export const ProductionOrdersQuery = {
|
|
|
18
18
|
scopes: [],
|
|
19
19
|
hasFormat: true,
|
|
20
20
|
flags: [
|
|
21
|
-
{ name: '
|
|
22
|
-
{ name: '
|
|
23
|
-
{ name: '
|
|
21
|
+
{ name: 'openId', type: 'string', required: true, description: '飞书用户 openId' },
|
|
22
|
+
{ name: 'pageNo', type: 'number', default: 1, description: '页码' },
|
|
23
|
+
{ name: 'pageSize', type: 'number', default: 20, description: '每页条数' },
|
|
24
24
|
// 订单状态
|
|
25
|
-
{ name: '
|
|
26
|
-
{ name: '
|
|
27
|
-
{ name: '
|
|
28
|
-
{ name: '
|
|
29
|
-
{ name: '
|
|
25
|
+
{ name: 'prodStatus', type: 'string', description: '生产订单状态: -1-全部|0-待确定|1-待同步|2-在途中|3-已完成|100-已取消' },
|
|
26
|
+
{ name: 'confirmSupplier', type: 'string', description: '确认供应商: 0-未确认|1-已确认' },
|
|
27
|
+
{ name: 'supplierOrderStatus', type: 'string', description: '供应商订单状态' },
|
|
28
|
+
{ name: 'quotationStatus', type: 'string', description: '报价单状态' },
|
|
29
|
+
{ name: 'washStatus', type: 'string', description: '洗唛订单状态' },
|
|
30
30
|
// 商品信息
|
|
31
|
-
{ name: '
|
|
32
|
-
{ name: '
|
|
33
|
-
{ name: '
|
|
31
|
+
{ name: 'goodsFullCode', type: 'string', description: '大货款号' },
|
|
32
|
+
{ name: 'goodsBatch', type: 'string', description: '大货批次' },
|
|
33
|
+
{ name: 'goodsType', type: 'string', description: '大货类型: 0-首单|1-加单|2-翻单' },
|
|
34
34
|
// 供应商
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: '
|
|
35
|
+
{ name: 'supplierName', type: 'string', description: '供应商名称' },
|
|
36
|
+
{ name: 'cooperationMode', type: 'string', description: '合作模式: 0-CMT|1-FOB' },
|
|
37
37
|
// 人员
|
|
38
|
-
{ name: '
|
|
38
|
+
{ name: 'designerName', type: 'string', description: '设计师名称' },
|
|
39
39
|
// 时间
|
|
40
40
|
{ name: 'type', type: 'string', description: '时间类型: 0-上市|1-下单|2-交期...' },
|
|
41
|
-
{ name: '
|
|
42
|
-
{ name: '
|
|
41
|
+
{ name: 'startTime', type: 'string', description: '开始时间 (ISO 8601)' },
|
|
42
|
+
{ name: 'endTime', type: 'string', description: '结束时间 (ISO 8601)' },
|
|
43
43
|
// 状态标识
|
|
44
|
-
{ name: '
|
|
44
|
+
{ name: 'qcInspection', type: 'string', description: 'QC 质检结果' },
|
|
45
45
|
// 分类
|
|
46
|
-
{ name: '
|
|
46
|
+
{ name: 'saleArea', type: 'string', description: '销售地区 ID 列表(逗号分隔)' },
|
|
47
47
|
],
|
|
48
48
|
|
|
49
49
|
async execute(internal, runtime) {
|
|
50
50
|
const f = runtime.flags;
|
|
51
|
-
const openId = f['
|
|
51
|
+
const openId = f['openId'];
|
|
52
52
|
|
|
53
53
|
const token = await scmClient.getToken(openId, null, null);
|
|
54
54
|
|
|
55
55
|
const filter = {
|
|
56
|
-
pageNo: parseInt(f['
|
|
57
|
-
pageSize: Math.min(parseInt(f['
|
|
56
|
+
pageNo: parseInt(f['pageNo']) || 1,
|
|
57
|
+
pageSize: Math.min(parseInt(f['pageSize']) || 20, 100),
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
// Optional filters
|
|
61
|
-
if (f['
|
|
62
|
-
if (f['
|
|
63
|
-
if (f['
|
|
64
|
-
if (f['
|
|
65
|
-
if (f['
|
|
66
|
-
if (f['
|
|
67
|
-
if (f['
|
|
68
|
-
if (f['
|
|
69
|
-
if (f['
|
|
70
|
-
if (f['
|
|
71
|
-
if (f['
|
|
61
|
+
if (f['prodStatus'] !== undefined) filter.prodStatus = parseInt(f['prodStatus']);
|
|
62
|
+
if (f['confirmSupplier'] !== undefined) filter.confirmSupplier = parseInt(f['confirmSupplier']);
|
|
63
|
+
if (f['supplierOrderStatus'] !== undefined) filter.supplierOrderStatus = parseInt(f['supplierOrderStatus']);
|
|
64
|
+
if (f['quotationStatus'] !== undefined) filter.quotationStatus = parseInt(f['quotationStatus']);
|
|
65
|
+
if (f['washStatus'] !== undefined) filter.washStatus = parseInt(f['washStatus']);
|
|
66
|
+
if (f['goodsFullCode']) filter.goodsFullCode = f['goodsFullCode'];
|
|
67
|
+
if (f['goodsBatch']) filter.goodsBatch = f['goodsBatch'];
|
|
68
|
+
if (f['goodsType'] !== undefined) filter.goodsType = parseInt(f['goodsType']);
|
|
69
|
+
if (f['supplierName']) filter.supplierName = f['supplierName'];
|
|
70
|
+
if (f['cooperationMode'] !== undefined) filter.cooperationMode = parseInt(f['cooperationMode']);
|
|
71
|
+
if (f['designerName']) filter.designerName = f['designerName'];
|
|
72
72
|
if (f['type'] !== undefined) filter.type = parseInt(f['type']);
|
|
73
|
-
if (f['
|
|
74
|
-
if (f['
|
|
75
|
-
if (f['
|
|
76
|
-
if (f['
|
|
73
|
+
if (f['startTime']) filter.startTime = f['startTime'];
|
|
74
|
+
if (f['endTime']) filter.endTime = f['endTime'];
|
|
75
|
+
if (f['qcInspection'] !== undefined) filter.qcInspection = parseInt(f['qcInspection']);
|
|
76
|
+
if (f['saleArea']) filter.saleArea = f['saleArea'].split(',').map(Number);
|
|
77
77
|
|
|
78
78
|
const body = { domain: 'production_order', action: 'query', filter };
|
|
79
79
|
|
|
@@ -15,59 +15,59 @@ export const QcOrdersList = {
|
|
|
15
15
|
scopes: [],
|
|
16
16
|
hasFormat: true,
|
|
17
17
|
flags: [
|
|
18
|
-
{ name: '
|
|
19
|
-
{ name: '
|
|
20
|
-
{ name: '
|
|
18
|
+
{ name: 'openId', type: 'string', required: true, description: '飞书用户 openId' },
|
|
19
|
+
{ name: 'pageNo', type: 'number', default: 1, description: '页码' },
|
|
20
|
+
{ name: 'pageSize', type: 'number', default: 10, description: '每页条数' },
|
|
21
21
|
// 订单状态
|
|
22
|
-
{ name: '
|
|
22
|
+
{ name: 'qcOrderStatus', type: 'string', description: 'QC 订单状态: -1-全部|0-待分配|1-已分配|2-已完成|3-已取消' },
|
|
23
23
|
// 商品信息
|
|
24
|
-
{ name: '
|
|
25
|
-
{ name: '
|
|
26
|
-
{ name: '
|
|
24
|
+
{ name: 'goodsFullCode', type: 'string', description: '大货款号' },
|
|
25
|
+
{ name: 'goodsType', type: 'string', description: '大货类型: 1-大货|2-翻单|3-补料' },
|
|
26
|
+
{ name: 'goodsGradeList', type: 'string', description: '商品分级列表 (逗号分隔)' },
|
|
27
27
|
// 供应商
|
|
28
|
-
{ name: '
|
|
28
|
+
{ name: 'supplierName', type: 'string', description: '供应商名称' },
|
|
29
29
|
// 人员
|
|
30
|
-
{ name: '
|
|
31
|
-
{ name: '
|
|
32
|
-
{ name: '
|
|
33
|
-
{ name: '
|
|
34
|
-
{ name: '
|
|
35
|
-
{ name: '
|
|
30
|
+
{ name: 'midManager', type: 'string', description: '中查管理人员' },
|
|
31
|
+
{ name: 'checkGoods', type: 'string', description: '查货人员' },
|
|
32
|
+
{ name: 'tailManager', type: 'string', description: '尾查管理人员' },
|
|
33
|
+
{ name: 'qcUserIdList', type: 'string', description: 'QC 用户 ID 列表 (逗号分隔)' },
|
|
34
|
+
{ name: 'prodManagerIdList', type: 'string', description: '生产经理 ID 列表 (逗号分隔)' },
|
|
35
|
+
{ name: 'purchaserIdList', type: 'string', description: '采购员 ID 列表 (逗号分隔)' },
|
|
36
36
|
// 时间
|
|
37
|
-
{ name: '
|
|
38
|
-
{ name: '
|
|
39
|
-
{ name: '
|
|
37
|
+
{ name: 'timeType', type: 'string', description: '时间类型: 0-预约验货|1-下单|2-接单|3-分配|4-完成|5-交期' },
|
|
38
|
+
{ name: 'startTime', type: 'string', description: '开始时间 (ISO 8601)' },
|
|
39
|
+
{ name: 'endTime', type: 'string', description: '结束时间 (ISO 8601)' },
|
|
40
40
|
],
|
|
41
41
|
|
|
42
42
|
async execute(internal, runtime) {
|
|
43
43
|
const f = runtime.flags;
|
|
44
|
-
const openId = f['
|
|
44
|
+
const openId = f['openId'];
|
|
45
45
|
const token = await scmClient.getToken(openId, null, null);
|
|
46
46
|
|
|
47
47
|
const filter = {
|
|
48
|
-
pageNo: parseInt(f['
|
|
49
|
-
pageSize: Math.min(parseInt(f['
|
|
48
|
+
pageNo: parseInt(f['pageNo']) || 1,
|
|
49
|
+
pageSize: Math.min(parseInt(f['pageSize']) || 10, 100),
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// 订单状态
|
|
53
|
-
if (f['
|
|
53
|
+
if (f['qcOrderStatus'] !== undefined) filter.qcOrderStatus = parseInt(f['qcOrderStatus']);
|
|
54
54
|
// 商品信息
|
|
55
|
-
if (f['
|
|
56
|
-
if (f['
|
|
57
|
-
if (f['
|
|
55
|
+
if (f['goodsFullCode']) filter.goodsFullCode = f['goodsFullCode'];
|
|
56
|
+
if (f['goodsType'] !== undefined) filter.goodsType = parseInt(f['goodsType']);
|
|
57
|
+
if (f['goodsGradeList']) filter.goodsGradeList = f['goodsGradeList'].split(',').map(s => s.trim());
|
|
58
58
|
// 供应商
|
|
59
|
-
if (f['
|
|
59
|
+
if (f['supplierName']) filter.supplierName = f['supplierName'];
|
|
60
60
|
// 人员
|
|
61
|
-
if (f['
|
|
62
|
-
if (f['
|
|
63
|
-
if (f['
|
|
64
|
-
if (f['
|
|
65
|
-
if (f['
|
|
66
|
-
if (f['
|
|
61
|
+
if (f['midManager']) filter.midManager = f['midManager'];
|
|
62
|
+
if (f['checkGoods']) filter.checkGoods = f['checkGoods'];
|
|
63
|
+
if (f['tailManager']) filter.tailManager = f['tailManager'];
|
|
64
|
+
if (f['qcUserIdList']) filter.qcUserIdList = f['qcUserIdList'].split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
65
|
+
if (f['prodManagerIdList']) filter.prodManagerIdList = f['prodManagerIdList'].split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
66
|
+
if (f['purchaserIdList']) filter.purchaserIdList = f['purchaserIdList'].split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
67
67
|
// 时间
|
|
68
|
-
if (f['
|
|
69
|
-
if (f['
|
|
70
|
-
if (f['
|
|
68
|
+
if (f['timeType'] !== undefined) filter.timeType = parseInt(f['timeType']);
|
|
69
|
+
if (f['startTime']) filter.startTime = f['startTime'];
|
|
70
|
+
if (f['endTime']) filter.endTime = f['endTime'];
|
|
71
71
|
|
|
72
72
|
const body = { domain: 'qc_order', action: 'query', filter };
|
|
73
73
|
const endpoint = '/admin-api/api/ai-scm/execute';
|