cyymall-cli 0.1.10 → 0.1.12
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 +1 -1
- package/package.json +2 -2
- package/src/cartProduct.js +90 -0
- package/src/cli.js +353 -321
- package/src/commands/cart.js +89 -51
- package/src/commands/order.js +632 -375
- package/src/commands/product.js +175 -175
- package/src/Untitled +0 -1
package/src/cli.js
CHANGED
|
@@ -1,321 +1,353 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const pkg = require(path.join(__dirname, "..", "package.json"));
|
|
5
|
-
|
|
6
|
-
const config = require("./config");
|
|
7
|
-
const http = require("./http");
|
|
8
|
-
const apiCall = require("./commands/apiCall");
|
|
9
|
-
const auth = require("./commands/auth");
|
|
10
|
-
const product = require("./commands/product");
|
|
11
|
-
const cart = require("./commands/cart");
|
|
12
|
-
const order = require("./commands/order");
|
|
13
|
-
const serve = require("./commands/serve");
|
|
14
|
-
const shop = require("./commands/shop");
|
|
15
|
-
|
|
16
|
-
const { Command } = require("commander");
|
|
17
|
-
const program = new Command();
|
|
18
|
-
|
|
19
|
-
program
|
|
20
|
-
.name("cyy")
|
|
21
|
-
.description("CyyMall / 菜洋洋商城 CLI (see app-api-cli-spec.md)")
|
|
22
|
-
.version(pkg.version);
|
|
23
|
-
|
|
24
|
-
const cfgCmd = program.command("config").description("Configuration");
|
|
25
|
-
|
|
26
|
-
cfgCmd
|
|
27
|
-
.command("path")
|
|
28
|
-
.description("Print config file path")
|
|
29
|
-
.action(() => {
|
|
30
|
-
console.log(config.getConfigPath());
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
cfgCmd
|
|
34
|
-
.command("show")
|
|
35
|
-
.description("Show config (token masked); includes effective session and env overrides")
|
|
36
|
-
.action(() => {
|
|
37
|
-
const file = config.loadConfig();
|
|
38
|
-
const session = config.getSession();
|
|
39
|
-
const envOverrides = config.getEnvSessionOverrideKeys();
|
|
40
|
-
if (!config.hasAuthToken(session) && !file) {
|
|
41
|
-
console.log(JSON.stringify({ loggedIn: false }, null, 2));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const masked = {
|
|
45
|
-
loggedIn: config.hasAuthToken(session),
|
|
46
|
-
saved: file
|
|
47
|
-
? { ...file, token: config.maskToken(String(file.token || "")) }
|
|
48
|
-
: null,
|
|
49
|
-
effective: {
|
|
50
|
-
...session,
|
|
51
|
-
token: config.maskToken(String(session.token || "")),
|
|
52
|
-
},
|
|
53
|
-
envOverrides: envOverrides.length ? envOverrides : undefined,
|
|
54
|
-
};
|
|
55
|
-
console.log(JSON.stringify(masked, null, 2));
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
function collect(value, previous) {
|
|
59
|
-
return previous.concat([value]);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const api = program.command("api").description("Low-level API calls");
|
|
63
|
-
|
|
64
|
-
api
|
|
65
|
-
.command("call")
|
|
66
|
-
.description("Invoke any mapped endpoint (spec §3)")
|
|
67
|
-
.requiredOption("--method <verb>", "HTTP method: GET, POST, PUT, PATCH, DELETE")
|
|
68
|
-
.requiredOption("--module <name>", "DEFAULT | ORDER | PRODUCT | PLATFORM | PAYMENT | OSS | BIZ")
|
|
69
|
-
.requiredOption("--path <path>", "Path starting with /, e.g. /app/product/getSkuList")
|
|
70
|
-
.option("--body-json <json>", "JSON body string")
|
|
71
|
-
.option("--body-file <file>", "Read JSON body from UTF-8 file")
|
|
72
|
-
.option("--query <pair>", "Repeatable: key=value for query string", collect, [])
|
|
73
|
-
.option("--header <h>", 'Repeatable: "Name: value"', collect, [])
|
|
74
|
-
.option("--bare", "Bootstrap headers only (do not merge saved session)")
|
|
75
|
-
.action(async (opts) => {
|
|
76
|
-
await apiCall.runApiCall({
|
|
77
|
-
...opts,
|
|
78
|
-
noAuth: Boolean(opts.bare),
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const authCmd = program.command("auth").description("Authentication");
|
|
83
|
-
|
|
84
|
-
authCmd
|
|
85
|
-
.command("login")
|
|
86
|
-
.description("SMS login; sends code then waits for code input unless --code is provided")
|
|
87
|
-
.requiredOption("--phone <p>", "Mobile phone")
|
|
88
|
-
.option("--code <code>", "SMS verification code (skip interactive prompt)")
|
|
89
|
-
.action(async (opts) => {
|
|
90
|
-
await auth.login(opts.phone, opts.code);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
authCmd
|
|
94
|
-
.command("send-code")
|
|
95
|
-
.description("Send SMS verification code for login")
|
|
96
|
-
.requiredOption("--phone <p>", "Mobile phone")
|
|
97
|
-
.action(async (opts) => {
|
|
98
|
-
await auth.sendCode(opts.phone);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
authCmd
|
|
102
|
-
.command("whoami")
|
|
103
|
-
.description("GET /member/getInfo/V2 using saved token")
|
|
104
|
-
.action(async () => {
|
|
105
|
-
await auth.whoami();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
authCmd
|
|
109
|
-
.command("import")
|
|
110
|
-
.description(
|
|
111
|
-
"Import external session token into ~/.cyymall/config.json (skip SMS login); optional --no-verify",
|
|
112
|
-
)
|
|
113
|
-
.option("--token <t>", "App session token (appToken / Authorization value)")
|
|
114
|
-
.option("--token-file <file>", "Read token from UTF-8 file (e.g. secret mount)")
|
|
115
|
-
.option("--member-id <id>", "memberId header")
|
|
116
|
-
.option("--shop-id <id>", "shopId header")
|
|
117
|
-
.option("--site-id <id>", "siteId header (default 1 if omitted and not in saved config)")
|
|
118
|
-
.option("--version-code <n>", "version-code header")
|
|
119
|
-
.option("--phone <p>", "Optional phone label for config display")
|
|
120
|
-
.option("--no-verify", "Save without calling GET /member/getInfo/V2")
|
|
121
|
-
.action(async (opts) => {
|
|
122
|
-
await auth.importSession({
|
|
123
|
-
token: opts.token,
|
|
124
|
-
tokenFile: opts.tokenFile,
|
|
125
|
-
memberId: opts.memberId,
|
|
126
|
-
shopId: opts.shopId,
|
|
127
|
-
siteId: opts.siteId,
|
|
128
|
-
versionCode: opts.versionCode,
|
|
129
|
-
phone: opts.phone,
|
|
130
|
-
noVerify: Boolean(opts.noVerify),
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const prod = program.command("product").description("Product helpers");
|
|
135
|
-
|
|
136
|
-
prod
|
|
137
|
-
.command("search")
|
|
138
|
-
.description("POST /mall-product/app/product/getSkuList")
|
|
139
|
-
.requiredOption("--keyword <k>", "spuName keyword")
|
|
140
|
-
.option("--shop-id <id>", "Override shopId (default from session)")
|
|
141
|
-
.option("--site-id <id>", "Override siteId")
|
|
142
|
-
.option("--page <n>", "pageNum", "1")
|
|
143
|
-
.option("--page-size <n>", "pageSize", "10")
|
|
144
|
-
.option("--stock-flag <s>", "stockFlag string (Java SearchCommodityReq)", "0")
|
|
145
|
-
.action(async (opts) => {
|
|
146
|
-
await product.search(opts);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
prod
|
|
150
|
-
.command("category-list")
|
|
151
|
-
.description("POST /app/product/getCategory — category tree (level-1 + children groups)")
|
|
152
|
-
.option("--shop-id <id>", "Override shopId (default from session)")
|
|
153
|
-
.option("--site-id <id>", "Override siteId")
|
|
154
|
-
.action(async (opts) => {
|
|
155
|
-
await product.categoryList(opts);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
prod
|
|
159
|
-
.command("zone-tags")
|
|
160
|
-
.description("POST /app/product/getZoneTagList — zone tags for a second-level group (--group-id)")
|
|
161
|
-
.requiredOption("--group-id <id>", "Second-level group id (children[].id from category-list)")
|
|
162
|
-
.option("--shop-id <id>", "Override shopId")
|
|
163
|
-
.option("--site-id <id>", "Override siteId")
|
|
164
|
-
.action(async (opts) => {
|
|
165
|
-
await product.zoneTags(opts);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
prod
|
|
169
|
-
.command("category-skus")
|
|
170
|
-
.description(
|
|
171
|
-
"POST /app/product/skuListByCategoryTagAll — SKUs under group + optional zone tag (cursor afterKey)",
|
|
172
|
-
)
|
|
173
|
-
.requiredOption("--group-id <id>", "Second-level group id")
|
|
174
|
-
.option("--zone-key <k>", "Zone tag key from zone-tags (required when zone-tags non-empty; App uses first tag, often all)")
|
|
175
|
-
.option("--after-key <k>", "Cursor from previous response data.afterKey")
|
|
176
|
-
.option("--page-size <n>", "pageSize (App TAG_PAGE_SIZE default 20)", "20")
|
|
177
|
-
.option("--shop-id <id>", "Override shopId")
|
|
178
|
-
.option("--site-id <id>", "Override siteId")
|
|
179
|
-
.action(async (opts) => {
|
|
180
|
-
await product.categorySkus(opts);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
prod
|
|
184
|
-
.command("skus-by-category")
|
|
185
|
-
.description(
|
|
186
|
-
"POST /app/product/skuListByCategory — fallback when zone-tags is empty (pageNum pagination)",
|
|
187
|
-
)
|
|
188
|
-
.requiredOption("--group-id <id>", "Second-level group id")
|
|
189
|
-
.option("--page <n>", "pageNum", "1")
|
|
190
|
-
.option("--page-size <n>", "pageSize (App PAGE_SIZE default 10)", "10")
|
|
191
|
-
.option("--shop-id <id>", "Override shopId")
|
|
192
|
-
.option("--site-id <id>", "Override siteId")
|
|
193
|
-
.action(async (opts) => {
|
|
194
|
-
await product.skusByCategory(opts);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const shopCmd = program.command("shop").description("Member shops and delivery sites");
|
|
198
|
-
|
|
199
|
-
shopCmd
|
|
200
|
-
.command("list")
|
|
201
|
-
.description("POST /shop/member/list — paginated shops for current member (GetShopReq)")
|
|
202
|
-
.option("--page <n>", "pageNum", "1")
|
|
203
|
-
.option("--page-size <n>", "pageSize", "20")
|
|
204
|
-
.option("--name <s>", "shopName filter", "")
|
|
205
|
-
.option("--object-code <n>", "objectCode", "3")
|
|
206
|
-
.action(async (opts) => {
|
|
207
|
-
await shop.list(opts);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
shopCmd
|
|
211
|
-
.command("sites")
|
|
212
|
-
.description("GET /shop/member/store/list — sites for a shop")
|
|
213
|
-
.option("--shop-id <id>", "Shop id (default: session shop_id)")
|
|
214
|
-
.option("--site-name <s>", "Optional siteName filter")
|
|
215
|
-
.action(async (opts) => {
|
|
216
|
-
await shop.sites(opts);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
shopCmd
|
|
220
|
-
.command("use")
|
|
221
|
-
.description("Set default shop_id only in ~/.cyymall/config.json (site_id unchanged; validate via shop list)")
|
|
222
|
-
.requiredOption("--shop-id <id>", "Numeric shop id from cyy shop list")
|
|
223
|
-
.action(async (opts) => {
|
|
224
|
-
await shop.useShop(opts);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
shopCmd
|
|
228
|
-
.command("use-site")
|
|
229
|
-
.description("Set default site_id for current session shop_id (validates against shop sites)")
|
|
230
|
-
.requiredOption("--site-id <id>", "Site id from shop sites")
|
|
231
|
-
.action(async (opts) => {
|
|
232
|
-
await shop.useSite(opts);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const cartCmd = program.command("cart").description("Shopping cart");
|
|
236
|
-
|
|
237
|
-
cartCmd
|
|
238
|
-
.command("add")
|
|
239
|
-
.description("POST /mall-order/app/order/cart")
|
|
240
|
-
.option("--body-file <file>", "Cart JSON body")
|
|
241
|
-
.option("--body-json <json>", "Cart JSON string")
|
|
242
|
-
.action(async (opts) => {
|
|
243
|
-
await cart.add(opts);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const orderCmd = program.command("order").description("Order flow");
|
|
247
|
-
|
|
248
|
-
orderCmd
|
|
249
|
-
.command("pre-settle")
|
|
250
|
-
.description("POST /mall-order/app/order/preSettleOrder")
|
|
251
|
-
.option("--body-file <file>", "")
|
|
252
|
-
.option("--body-json <json>", "")
|
|
253
|
-
.action(async (opts) => {
|
|
254
|
-
await order.preSettle(opts);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
orderCmd
|
|
258
|
-
.command("confirm")
|
|
259
|
-
.description("POST /mall-order/app/order/confirmOrder")
|
|
260
|
-
.option("--body-file <file>", "")
|
|
261
|
-
.option("--body-json <json>", "")
|
|
262
|
-
.action(async (opts) => {
|
|
263
|
-
await order.confirm(opts);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
orderCmd
|
|
267
|
-
.command("pay-url")
|
|
268
|
-
.description("Build H5 replacePay URL for order")
|
|
269
|
-
.requiredOption("--order-id <id>", "Order id from confirm response")
|
|
270
|
-
.action(async (opts) => {
|
|
271
|
-
await order.payUrl(opts);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
orderCmd
|
|
275
|
-
.command("list")
|
|
276
|
-
.description("POST /mall-multishop/order/queryPage (OrderQeq)")
|
|
277
|
-
.option("--page <n>", "pageNum", "1")
|
|
278
|
-
.option("--page-size <n>", "pageSize", "10")
|
|
279
|
-
.option("--status <s>", "status filter (empty = all)", "")
|
|
280
|
-
.option("--shop-id <id>", "shopId override")
|
|
281
|
-
.option("--object-code <n>", "objectCode", "1")
|
|
282
|
-
.option("--all", "set queryAllFlag true", false)
|
|
283
|
-
.option("--shop-keyword <s>", "shopKeyWord", "")
|
|
284
|
-
.action(async (opts) => {
|
|
285
|
-
await order.list(opts);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
orderCmd
|
|
289
|
-
.command("cancel")
|
|
290
|
-
.description("POST /mall-order/app/order/cancelOrder")
|
|
291
|
-
.requiredOption("--order-id <id>", "Order id")
|
|
292
|
-
.option("--child-id <id>", "child order id (optional)")
|
|
293
|
-
.action(async (opts) => {
|
|
294
|
-
await order.cancel(opts);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
orderCmd
|
|
298
|
-
.command("quick")
|
|
299
|
-
.description("Search → cart → pre-settle → confirm → pay URL (first search hit)")
|
|
300
|
-
.requiredOption("--keyword <k>", "Product keyword")
|
|
301
|
-
.option("--quantity <n>", "Qty", "1")
|
|
302
|
-
.option("--unit <u>", "袋 | 提 | 箱", "袋")
|
|
303
|
-
.option("--shop-id <id>", "shopId override")
|
|
304
|
-
.option("--site-id <id>", "siteId override")
|
|
305
|
-
.action(async (opts) => {
|
|
306
|
-
await order.quick(opts);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const pkg = require(path.join(__dirname, "..", "package.json"));
|
|
5
|
+
|
|
6
|
+
const config = require("./config");
|
|
7
|
+
const http = require("./http");
|
|
8
|
+
const apiCall = require("./commands/apiCall");
|
|
9
|
+
const auth = require("./commands/auth");
|
|
10
|
+
const product = require("./commands/product");
|
|
11
|
+
const cart = require("./commands/cart");
|
|
12
|
+
const order = require("./commands/order");
|
|
13
|
+
const serve = require("./commands/serve");
|
|
14
|
+
const shop = require("./commands/shop");
|
|
15
|
+
|
|
16
|
+
const { Command } = require("commander");
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.name("cyy")
|
|
21
|
+
.description("CyyMall / 菜洋洋商城 CLI (see app-api-cli-spec.md)")
|
|
22
|
+
.version(pkg.version);
|
|
23
|
+
|
|
24
|
+
const cfgCmd = program.command("config").description("Configuration");
|
|
25
|
+
|
|
26
|
+
cfgCmd
|
|
27
|
+
.command("path")
|
|
28
|
+
.description("Print config file path")
|
|
29
|
+
.action(() => {
|
|
30
|
+
console.log(config.getConfigPath());
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
cfgCmd
|
|
34
|
+
.command("show")
|
|
35
|
+
.description("Show config (token masked); includes effective session and env overrides")
|
|
36
|
+
.action(() => {
|
|
37
|
+
const file = config.loadConfig();
|
|
38
|
+
const session = config.getSession();
|
|
39
|
+
const envOverrides = config.getEnvSessionOverrideKeys();
|
|
40
|
+
if (!config.hasAuthToken(session) && !file) {
|
|
41
|
+
console.log(JSON.stringify({ loggedIn: false }, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const masked = {
|
|
45
|
+
loggedIn: config.hasAuthToken(session),
|
|
46
|
+
saved: file
|
|
47
|
+
? { ...file, token: config.maskToken(String(file.token || "")) }
|
|
48
|
+
: null,
|
|
49
|
+
effective: {
|
|
50
|
+
...session,
|
|
51
|
+
token: config.maskToken(String(session.token || "")),
|
|
52
|
+
},
|
|
53
|
+
envOverrides: envOverrides.length ? envOverrides : undefined,
|
|
54
|
+
};
|
|
55
|
+
console.log(JSON.stringify(masked, null, 2));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function collect(value, previous) {
|
|
59
|
+
return previous.concat([value]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const api = program.command("api").description("Low-level API calls");
|
|
63
|
+
|
|
64
|
+
api
|
|
65
|
+
.command("call")
|
|
66
|
+
.description("Invoke any mapped endpoint (spec §3)")
|
|
67
|
+
.requiredOption("--method <verb>", "HTTP method: GET, POST, PUT, PATCH, DELETE")
|
|
68
|
+
.requiredOption("--module <name>", "DEFAULT | ORDER | PRODUCT | PLATFORM | PAYMENT | OSS | BIZ")
|
|
69
|
+
.requiredOption("--path <path>", "Path starting with /, e.g. /app/product/getSkuList")
|
|
70
|
+
.option("--body-json <json>", "JSON body string")
|
|
71
|
+
.option("--body-file <file>", "Read JSON body from UTF-8 file")
|
|
72
|
+
.option("--query <pair>", "Repeatable: key=value for query string", collect, [])
|
|
73
|
+
.option("--header <h>", 'Repeatable: "Name: value"', collect, [])
|
|
74
|
+
.option("--bare", "Bootstrap headers only (do not merge saved session)")
|
|
75
|
+
.action(async (opts) => {
|
|
76
|
+
await apiCall.runApiCall({
|
|
77
|
+
...opts,
|
|
78
|
+
noAuth: Boolean(opts.bare),
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const authCmd = program.command("auth").description("Authentication");
|
|
83
|
+
|
|
84
|
+
authCmd
|
|
85
|
+
.command("login")
|
|
86
|
+
.description("SMS login; sends code then waits for code input unless --code is provided")
|
|
87
|
+
.requiredOption("--phone <p>", "Mobile phone")
|
|
88
|
+
.option("--code <code>", "SMS verification code (skip interactive prompt)")
|
|
89
|
+
.action(async (opts) => {
|
|
90
|
+
await auth.login(opts.phone, opts.code);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
authCmd
|
|
94
|
+
.command("send-code")
|
|
95
|
+
.description("Send SMS verification code for login")
|
|
96
|
+
.requiredOption("--phone <p>", "Mobile phone")
|
|
97
|
+
.action(async (opts) => {
|
|
98
|
+
await auth.sendCode(opts.phone);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
authCmd
|
|
102
|
+
.command("whoami")
|
|
103
|
+
.description("GET /member/getInfo/V2 using saved token")
|
|
104
|
+
.action(async () => {
|
|
105
|
+
await auth.whoami();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
authCmd
|
|
109
|
+
.command("import")
|
|
110
|
+
.description(
|
|
111
|
+
"Import external session token into ~/.cyymall/config.json (skip SMS login); optional --no-verify",
|
|
112
|
+
)
|
|
113
|
+
.option("--token <t>", "App session token (appToken / Authorization value)")
|
|
114
|
+
.option("--token-file <file>", "Read token from UTF-8 file (e.g. secret mount)")
|
|
115
|
+
.option("--member-id <id>", "memberId header")
|
|
116
|
+
.option("--shop-id <id>", "shopId header")
|
|
117
|
+
.option("--site-id <id>", "siteId header (default 1 if omitted and not in saved config)")
|
|
118
|
+
.option("--version-code <n>", "version-code header")
|
|
119
|
+
.option("--phone <p>", "Optional phone label for config display")
|
|
120
|
+
.option("--no-verify", "Save without calling GET /member/getInfo/V2")
|
|
121
|
+
.action(async (opts) => {
|
|
122
|
+
await auth.importSession({
|
|
123
|
+
token: opts.token,
|
|
124
|
+
tokenFile: opts.tokenFile,
|
|
125
|
+
memberId: opts.memberId,
|
|
126
|
+
shopId: opts.shopId,
|
|
127
|
+
siteId: opts.siteId,
|
|
128
|
+
versionCode: opts.versionCode,
|
|
129
|
+
phone: opts.phone,
|
|
130
|
+
noVerify: Boolean(opts.noVerify),
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const prod = program.command("product").description("Product helpers");
|
|
135
|
+
|
|
136
|
+
prod
|
|
137
|
+
.command("search")
|
|
138
|
+
.description("POST /mall-product/app/product/getSkuList")
|
|
139
|
+
.requiredOption("--keyword <k>", "spuName keyword")
|
|
140
|
+
.option("--shop-id <id>", "Override shopId (default from session)")
|
|
141
|
+
.option("--site-id <id>", "Override siteId")
|
|
142
|
+
.option("--page <n>", "pageNum", "1")
|
|
143
|
+
.option("--page-size <n>", "pageSize", "10")
|
|
144
|
+
.option("--stock-flag <s>", "stockFlag string (Java SearchCommodityReq)", "0")
|
|
145
|
+
.action(async (opts) => {
|
|
146
|
+
await product.search(opts);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
prod
|
|
150
|
+
.command("category-list")
|
|
151
|
+
.description("POST /app/product/getCategory — category tree (level-1 + children groups)")
|
|
152
|
+
.option("--shop-id <id>", "Override shopId (default from session)")
|
|
153
|
+
.option("--site-id <id>", "Override siteId")
|
|
154
|
+
.action(async (opts) => {
|
|
155
|
+
await product.categoryList(opts);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
prod
|
|
159
|
+
.command("zone-tags")
|
|
160
|
+
.description("POST /app/product/getZoneTagList — zone tags for a second-level group (--group-id)")
|
|
161
|
+
.requiredOption("--group-id <id>", "Second-level group id (children[].id from category-list)")
|
|
162
|
+
.option("--shop-id <id>", "Override shopId")
|
|
163
|
+
.option("--site-id <id>", "Override siteId")
|
|
164
|
+
.action(async (opts) => {
|
|
165
|
+
await product.zoneTags(opts);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
prod
|
|
169
|
+
.command("category-skus")
|
|
170
|
+
.description(
|
|
171
|
+
"POST /app/product/skuListByCategoryTagAll — SKUs under group + optional zone tag (cursor afterKey)",
|
|
172
|
+
)
|
|
173
|
+
.requiredOption("--group-id <id>", "Second-level group id")
|
|
174
|
+
.option("--zone-key <k>", "Zone tag key from zone-tags (required when zone-tags non-empty; App uses first tag, often all)")
|
|
175
|
+
.option("--after-key <k>", "Cursor from previous response data.afterKey")
|
|
176
|
+
.option("--page-size <n>", "pageSize (App TAG_PAGE_SIZE default 20)", "20")
|
|
177
|
+
.option("--shop-id <id>", "Override shopId")
|
|
178
|
+
.option("--site-id <id>", "Override siteId")
|
|
179
|
+
.action(async (opts) => {
|
|
180
|
+
await product.categorySkus(opts);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
prod
|
|
184
|
+
.command("skus-by-category")
|
|
185
|
+
.description(
|
|
186
|
+
"POST /app/product/skuListByCategory — fallback when zone-tags is empty (pageNum pagination)",
|
|
187
|
+
)
|
|
188
|
+
.requiredOption("--group-id <id>", "Second-level group id")
|
|
189
|
+
.option("--page <n>", "pageNum", "1")
|
|
190
|
+
.option("--page-size <n>", "pageSize (App PAGE_SIZE default 10)", "10")
|
|
191
|
+
.option("--shop-id <id>", "Override shopId")
|
|
192
|
+
.option("--site-id <id>", "Override siteId")
|
|
193
|
+
.action(async (opts) => {
|
|
194
|
+
await product.skusByCategory(opts);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const shopCmd = program.command("shop").description("Member shops and delivery sites");
|
|
198
|
+
|
|
199
|
+
shopCmd
|
|
200
|
+
.command("list")
|
|
201
|
+
.description("POST /shop/member/list — paginated shops for current member (GetShopReq)")
|
|
202
|
+
.option("--page <n>", "pageNum", "1")
|
|
203
|
+
.option("--page-size <n>", "pageSize", "20")
|
|
204
|
+
.option("--name <s>", "shopName filter", "")
|
|
205
|
+
.option("--object-code <n>", "objectCode", "3")
|
|
206
|
+
.action(async (opts) => {
|
|
207
|
+
await shop.list(opts);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
shopCmd
|
|
211
|
+
.command("sites")
|
|
212
|
+
.description("GET /shop/member/store/list — sites for a shop")
|
|
213
|
+
.option("--shop-id <id>", "Shop id (default: session shop_id)")
|
|
214
|
+
.option("--site-name <s>", "Optional siteName filter")
|
|
215
|
+
.action(async (opts) => {
|
|
216
|
+
await shop.sites(opts);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
shopCmd
|
|
220
|
+
.command("use")
|
|
221
|
+
.description("Set default shop_id only in ~/.cyymall/config.json (site_id unchanged; validate via shop list)")
|
|
222
|
+
.requiredOption("--shop-id <id>", "Numeric shop id from cyy shop list")
|
|
223
|
+
.action(async (opts) => {
|
|
224
|
+
await shop.useShop(opts);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
shopCmd
|
|
228
|
+
.command("use-site")
|
|
229
|
+
.description("Set default site_id for current session shop_id (validates against shop sites)")
|
|
230
|
+
.requiredOption("--site-id <id>", "Site id from shop sites")
|
|
231
|
+
.action(async (opts) => {
|
|
232
|
+
await shop.useSite(opts);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const cartCmd = program.command("cart").description("Shopping cart");
|
|
236
|
+
|
|
237
|
+
cartCmd
|
|
238
|
+
.command("add")
|
|
239
|
+
.description("POST /mall-order/app/order/cart")
|
|
240
|
+
.option("--body-file <file>", "Cart JSON body")
|
|
241
|
+
.option("--body-json <json>", "Cart JSON string")
|
|
242
|
+
.action(async (opts) => {
|
|
243
|
+
await cart.add(opts);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const orderCmd = program.command("order").description("Order flow");
|
|
247
|
+
|
|
248
|
+
orderCmd
|
|
249
|
+
.command("pre-settle")
|
|
250
|
+
.description("POST /mall-order/app/order/preSettleOrder")
|
|
251
|
+
.option("--body-file <file>", "")
|
|
252
|
+
.option("--body-json <json>", "")
|
|
253
|
+
.action(async (opts) => {
|
|
254
|
+
await order.preSettle(opts);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
orderCmd
|
|
258
|
+
.command("confirm")
|
|
259
|
+
.description("POST /mall-order/app/order/confirmOrder")
|
|
260
|
+
.option("--body-file <file>", "")
|
|
261
|
+
.option("--body-json <json>", "")
|
|
262
|
+
.action(async (opts) => {
|
|
263
|
+
await order.confirm(opts);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
orderCmd
|
|
267
|
+
.command("pay-url")
|
|
268
|
+
.description("Build H5 replacePay URL for order")
|
|
269
|
+
.requiredOption("--order-id <id>", "Order id from confirm response")
|
|
270
|
+
.action(async (opts) => {
|
|
271
|
+
await order.payUrl(opts);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
orderCmd
|
|
275
|
+
.command("list")
|
|
276
|
+
.description("POST /mall-multishop/order/queryPage (OrderQeq)")
|
|
277
|
+
.option("--page <n>", "pageNum", "1")
|
|
278
|
+
.option("--page-size <n>", "pageSize", "10")
|
|
279
|
+
.option("--status <s>", "status filter (empty = all)", "")
|
|
280
|
+
.option("--shop-id <id>", "shopId override")
|
|
281
|
+
.option("--object-code <n>", "objectCode", "1")
|
|
282
|
+
.option("--all", "set queryAllFlag true", false)
|
|
283
|
+
.option("--shop-keyword <s>", "shopKeyWord", "")
|
|
284
|
+
.action(async (opts) => {
|
|
285
|
+
await order.list(opts);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
orderCmd
|
|
289
|
+
.command("cancel")
|
|
290
|
+
.description("POST /mall-order/app/order/cancelOrder")
|
|
291
|
+
.requiredOption("--order-id <id>", "Order id")
|
|
292
|
+
.option("--child-id <id>", "child order id (optional)")
|
|
293
|
+
.action(async (opts) => {
|
|
294
|
+
await order.cancel(opts);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
orderCmd
|
|
298
|
+
.command("quick")
|
|
299
|
+
.description("Search → cart → pre-settle → confirm → pay URL (first search hit)")
|
|
300
|
+
.requiredOption("--keyword <k>", "Product keyword")
|
|
301
|
+
.option("--quantity <n>", "Qty", "1")
|
|
302
|
+
.option("--unit <u>", "袋 | 提 | 箱", "袋")
|
|
303
|
+
.option("--shop-id <id>", "shopId override")
|
|
304
|
+
.option("--site-id <id>", "siteId override")
|
|
305
|
+
.action(async (opts) => {
|
|
306
|
+
await order.quick(opts);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
function collectKeywords(value, previous) {
|
|
310
|
+
return previous.concat([value]);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
orderCmd
|
|
314
|
+
.command("checkout")
|
|
315
|
+
.description(
|
|
316
|
+
"Multi-SKU: search keywords → one cart (cartModel append) → pre-settle → confirm (use --pre-settle-only to stop before confirm)",
|
|
317
|
+
)
|
|
318
|
+
.option("--keyword <k>", "Product keyword (repeatable)", collectKeywords, [])
|
|
319
|
+
.option("--items-file <file>", "JSON array [{ keyword, quantity?, unit? }]")
|
|
320
|
+
.option("--items-json <json>", "Same as --items-file inline")
|
|
321
|
+
.option("--quantity <n>", "Default qty per keyword", "2")
|
|
322
|
+
.option("--unit <u>", "Default unit", "袋")
|
|
323
|
+
.option("--cart-model <m>", "append | mix | delete | … (default append)", "append")
|
|
324
|
+
.option("--shop-id <id>", "shopId override")
|
|
325
|
+
.option("--site-id <id>", "siteId override")
|
|
326
|
+
.option("--pre-settle-only", "Stop after pre-settle (no confirm)", false)
|
|
327
|
+
.action(async (opts) => {
|
|
328
|
+
await order.checkout({
|
|
329
|
+
keywords: opts.keyword,
|
|
330
|
+
itemsFile: opts.itemsFile,
|
|
331
|
+
itemsJson: opts.itemsJson,
|
|
332
|
+
quantity: opts.quantity,
|
|
333
|
+
unit: opts.unit,
|
|
334
|
+
cartModel: opts.cartModel,
|
|
335
|
+
shopId: opts.shopId,
|
|
336
|
+
siteId: opts.siteId,
|
|
337
|
+
preSettleOnly: Boolean(opts.preSettleOnly),
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
program
|
|
342
|
+
.command("serve")
|
|
343
|
+
.description("Local HTTP shim: POST /invoke JSON body { method, module, path, body?, query?, noAuth? }")
|
|
344
|
+
.option("-p, --port <n>", "port", "8787")
|
|
345
|
+
.option("--host <h>", "bind address", "127.0.0.1")
|
|
346
|
+
.action((opts) => {
|
|
347
|
+
serve.runServe({ port: opts.port, host: opts.host });
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
program.parseAsync(process.argv).catch((e) => {
|
|
351
|
+
console.error(e);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
});
|