cyymall-cli 0.1.10 → 0.1.11
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 +1 -1
- package/src/cli.js +353 -321
- package/src/commands/cart.js +89 -51
- package/src/commands/order.js +618 -375
- package/src/commands/product.js +175 -175
package/src/commands/order.js
CHANGED
|
@@ -1,375 +1,618 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const crypto = require("crypto");
|
|
5
|
-
const http = require("../http");
|
|
6
|
-
const config = require("../config");
|
|
7
|
-
const biz = require("../biz");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
r.json
|
|
142
|
-
typeof r.json
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const http = require("../http");
|
|
6
|
+
const config = require("../config");
|
|
7
|
+
const biz = require("../biz");
|
|
8
|
+
const { normalizeAddCartBody } = require("./cart");
|
|
9
|
+
const { resolveProductLine } = require("../cartProduct");
|
|
10
|
+
|
|
11
|
+
function envelope(success, message, data, exitCode) {
|
|
12
|
+
const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
13
|
+
console.log(
|
|
14
|
+
JSON.stringify(
|
|
15
|
+
{
|
|
16
|
+
success,
|
|
17
|
+
code: success ? "OK" : "UPSTREAM_ERROR",
|
|
18
|
+
message,
|
|
19
|
+
data,
|
|
20
|
+
traceId,
|
|
21
|
+
},
|
|
22
|
+
null,
|
|
23
|
+
2,
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
process.exit(exitCode ?? (success ? 0 : 2));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
31
|
+
*/
|
|
32
|
+
async function preSettle(opts) {
|
|
33
|
+
const cfg = config.requireAuthSession();
|
|
34
|
+
let raw = opts.bodyJson;
|
|
35
|
+
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
36
|
+
if (!raw) {
|
|
37
|
+
console.error("cyy: provide --body-file or --body-json");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const url = http.moduleUrl("ORDER", "/app/order/preSettleOrder");
|
|
41
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
42
|
+
const { ok, json } = await http.request(url, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers,
|
|
45
|
+
body: raw,
|
|
46
|
+
});
|
|
47
|
+
const success = ok && biz.isBizSuccess(json);
|
|
48
|
+
envelope(success, success ? "success" : "pre-settle failed", { upstream: json }, success ? 0 : 2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
53
|
+
*/
|
|
54
|
+
async function confirm(opts) {
|
|
55
|
+
const cfg = config.requireAuthSession();
|
|
56
|
+
let raw = opts.bodyJson;
|
|
57
|
+
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
58
|
+
if (!raw) {
|
|
59
|
+
console.error("cyy: provide --body-file or --body-json");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const url = http.moduleUrl("ORDER", "/app/order/confirmOrder");
|
|
63
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
64
|
+
const { ok, json } = await http.request(url, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers,
|
|
67
|
+
body: raw,
|
|
68
|
+
});
|
|
69
|
+
const success = ok && biz.isBizSuccess(json);
|
|
70
|
+
envelope(success, success ? "success" : "confirm failed", { upstream: json }, success ? 0 : 2);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function payUrl(opts) {
|
|
74
|
+
const cfg = config.requireAuthSession();
|
|
75
|
+
const orderId = opts.orderId;
|
|
76
|
+
if (!orderId) {
|
|
77
|
+
console.error("cyy: --order-id required");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
82
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
83
|
+
delete headers["Content-Type"];
|
|
84
|
+
|
|
85
|
+
const { ok, json } = await http.request(url, { method: "GET", headers, body: null });
|
|
86
|
+
|
|
87
|
+
let phone = String(cfg.phone || "");
|
|
88
|
+
let nick = "";
|
|
89
|
+
let img = "";
|
|
90
|
+
if (ok && biz.isBizSuccess(json) && json && typeof json === "object") {
|
|
91
|
+
const d = /** @type {{data?:Record<string,unknown>}} */ (json).data;
|
|
92
|
+
if (d) {
|
|
93
|
+
phone = String(d.memberPhone || phone);
|
|
94
|
+
nick = String(d.memberNick || "");
|
|
95
|
+
img = String(d.headerImg || "");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const siteId = cfg.site_id;
|
|
100
|
+
const shopId = cfg.shop_id;
|
|
101
|
+
const enc = encodeURIComponent;
|
|
102
|
+
const payUrlStr = `https://dhcmall.ifoodbuy.com/H5/#/replacePay?phone=${enc(phone)}&orderid=${enc(String(orderId))}&img=${enc(img)}&nick=${enc(nick)}&siteId=${siteId}&shopId=${shopId}&share=true`;
|
|
103
|
+
|
|
104
|
+
envelope(true, "success", { payUrl: payUrlStr, orderId }, 0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {object} opts
|
|
109
|
+
*/
|
|
110
|
+
async function quick(opts) {
|
|
111
|
+
const cfg = config.requireAuthSession();
|
|
112
|
+
|
|
113
|
+
const keyword = opts.keyword;
|
|
114
|
+
const quantity = Number(opts.quantity || 1);
|
|
115
|
+
const unit = opts.unit || "袋";
|
|
116
|
+
const shopId = Number(opts.shopId ?? cfg.shop_id);
|
|
117
|
+
const siteId = Number(opts.siteId ?? cfg.site_id);
|
|
118
|
+
|
|
119
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
120
|
+
|
|
121
|
+
// 1) Search
|
|
122
|
+
const searchUrl = http.moduleUrl("PRODUCT", "/app/product/getSkuList");
|
|
123
|
+
const searchBody = JSON.stringify({
|
|
124
|
+
pageNum: 1,
|
|
125
|
+
pageSize: 10,
|
|
126
|
+
shopId,
|
|
127
|
+
siteId,
|
|
128
|
+
spuName: keyword,
|
|
129
|
+
stockFlag: "0",
|
|
130
|
+
});
|
|
131
|
+
let r = await http.request(searchUrl, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers,
|
|
134
|
+
body: searchBody,
|
|
135
|
+
});
|
|
136
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
137
|
+
envelope(false, "search failed", { upstream: r.json }, 2);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const list =
|
|
141
|
+
r.json &&
|
|
142
|
+
typeof r.json === "object" &&
|
|
143
|
+
r.json.data &&
|
|
144
|
+
typeof r.json.data === "object"
|
|
145
|
+
? /** @type {{list?:unknown[]}} */ (r.json.data).list
|
|
146
|
+
: [];
|
|
147
|
+
const products = Array.isArray(list) ? list : [];
|
|
148
|
+
if (!products.length) {
|
|
149
|
+
envelope(false, "no products found", {}, 2);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const product = /** @type {Record<string,unknown>} */ (products[0]);
|
|
154
|
+
const skuId = product.id;
|
|
155
|
+
const skuCode = product.skuCode;
|
|
156
|
+
const skuName = product.spuName;
|
|
157
|
+
const stockQty = product.stockAllQuantity ?? 0;
|
|
158
|
+
const promotionList = Array.isArray(product.promotionList) ? product.promotionList : [];
|
|
159
|
+
|
|
160
|
+
let price = 0;
|
|
161
|
+
let unitRate = 1;
|
|
162
|
+
for (const p of promotionList) {
|
|
163
|
+
const row = /** @type {Record<string,unknown>} */ (p);
|
|
164
|
+
if (row.skuSaleUnitName === unit) {
|
|
165
|
+
price = Number(row.salePrice || 0);
|
|
166
|
+
unitRate = Number(row.unitRate ?? 1);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!price) {
|
|
171
|
+
envelope(
|
|
172
|
+
false,
|
|
173
|
+
`unit "${unit}" not found in promotionList`,
|
|
174
|
+
{ availableUnits: promotionList.map((x) => /** @type {{skuSaleUnitName?:string}} */ (x).skuSaleUnitName) },
|
|
175
|
+
2,
|
|
176
|
+
);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 2) Cart
|
|
181
|
+
const cartBody = JSON.stringify({
|
|
182
|
+
cartItemList: [
|
|
183
|
+
{
|
|
184
|
+
skuCode,
|
|
185
|
+
skuName,
|
|
186
|
+
skuQty: quantity,
|
|
187
|
+
skuSaleUnit: unit,
|
|
188
|
+
listPrice: price,
|
|
189
|
+
salePrice: price,
|
|
190
|
+
unitRate,
|
|
191
|
+
selectFlag: true,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
cartModel: "append",
|
|
195
|
+
shopId,
|
|
196
|
+
siteId,
|
|
197
|
+
});
|
|
198
|
+
const cartUrl = http.moduleUrl("ORDER", "/app/order/cart");
|
|
199
|
+
r = await http.request(cartUrl, { method: "POST", headers, body: cartBody });
|
|
200
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
201
|
+
envelope(false, "cart failed", { upstream: r.json }, 2);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const cartData =
|
|
205
|
+
r.json && typeof r.json === "object" && "data" in r.json
|
|
206
|
+
? /** @type {{data?:{cartItemList?:{addTime?:string}[]}}} */ (r.json).data
|
|
207
|
+
: undefined;
|
|
208
|
+
const addTime = cartData?.cartItemList?.[0]?.addTime;
|
|
209
|
+
if (!addTime) {
|
|
210
|
+
envelope(false, "missing addTime from cart response", { upstream: r.json }, 2);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 3) Pre-settle
|
|
215
|
+
const preBody = JSON.stringify({
|
|
216
|
+
headerVO: { shopId, siteId },
|
|
217
|
+
itemVOList: [
|
|
218
|
+
{
|
|
219
|
+
skuId,
|
|
220
|
+
skuCode,
|
|
221
|
+
skuName,
|
|
222
|
+
skuQty: quantity,
|
|
223
|
+
skuSaleUnit: unit,
|
|
224
|
+
salePrice: price,
|
|
225
|
+
listPrice: price,
|
|
226
|
+
unitRate,
|
|
227
|
+
addTime,
|
|
228
|
+
stockQty,
|
|
229
|
+
minSaleUnit: unit,
|
|
230
|
+
selectFlag: true,
|
|
231
|
+
status: 0,
|
|
232
|
+
canBuyFlag: true,
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
balanceFlag: false,
|
|
236
|
+
userPointFlag: false,
|
|
237
|
+
});
|
|
238
|
+
const preUrl = http.moduleUrl("ORDER", "/app/order/preSettleOrder");
|
|
239
|
+
r = await http.request(preUrl, { method: "POST", headers, body: preBody });
|
|
240
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
241
|
+
envelope(false, "pre-settle failed", { upstream: r.json }, 2);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const pdata =
|
|
245
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
246
|
+
? r.json.data
|
|
247
|
+
: null;
|
|
248
|
+
const preSettleId = pdata && /** @type {{preSettleId?:string}} */ (pdata).preSettleId;
|
|
249
|
+
const headerVO = pdata && /** @type {{headerVO?:unknown}} */ (pdata).headerVO;
|
|
250
|
+
const itemVOList = pdata && /** @type {{itemVOList?:unknown}} */ (pdata).itemVOList;
|
|
251
|
+
if (!preSettleId) {
|
|
252
|
+
envelope(false, "missing preSettleId", { upstream: r.json }, 2);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 4) Confirm
|
|
257
|
+
const confirmBody = JSON.stringify({
|
|
258
|
+
preSettleId,
|
|
259
|
+
headerVO,
|
|
260
|
+
itemVOList,
|
|
261
|
+
balanceFlag: false,
|
|
262
|
+
userPointFlag: false,
|
|
263
|
+
validateStockFlag: true,
|
|
264
|
+
repeatFlag: false,
|
|
265
|
+
});
|
|
266
|
+
const confirmUrl = http.moduleUrl("ORDER", "/app/order/confirmOrder");
|
|
267
|
+
r = await http.request(confirmUrl, { method: "POST", headers, body: confirmBody });
|
|
268
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
269
|
+
envelope(false, "confirm failed", { upstream: r.json }, 2);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const orderData =
|
|
273
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
274
|
+
? r.json.data
|
|
275
|
+
: {};
|
|
276
|
+
const orderId = /** @type {{orderId?:string,payAmt?:unknown}} */ (orderData).orderId;
|
|
277
|
+
const payAmt = /** @type {{orderId?:string,payAmt?:unknown}} */ (orderData).payAmt;
|
|
278
|
+
|
|
279
|
+
// 5) Pay URL
|
|
280
|
+
const memberUrl = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
281
|
+
const h2 = /** @type {Record<string,string>} */ ({ ...headers });
|
|
282
|
+
delete h2["Content-Type"];
|
|
283
|
+
const uinfo = await http.request(memberUrl, { method: "GET", headers: h2, body: null });
|
|
284
|
+
let phone = String(cfg.phone || "");
|
|
285
|
+
let nick = "";
|
|
286
|
+
let img = "";
|
|
287
|
+
if (uinfo.ok && biz.isBizSuccess(uinfo.json) && uinfo.json && typeof uinfo.json === "object") {
|
|
288
|
+
const d = /** @type {{data?:Record<string,unknown>}} */ (uinfo.json).data;
|
|
289
|
+
if (d) {
|
|
290
|
+
phone = String(d.memberPhone || phone);
|
|
291
|
+
nick = String(d.memberNick || "");
|
|
292
|
+
img = String(d.headerImg || "");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const enc = encodeURIComponent;
|
|
296
|
+
const payUrlStr = `https://dhcmall.ifoodbuy.com/H5/#/replacePay?phone=${enc(phone)}&orderid=${enc(String(orderId))}&img=${enc(img)}&nick=${enc(nick)}&siteId=${siteId}&shopId=${shopId}&share=true`;
|
|
297
|
+
|
|
298
|
+
envelope(
|
|
299
|
+
true,
|
|
300
|
+
"order placed",
|
|
301
|
+
{
|
|
302
|
+
product: skuName,
|
|
303
|
+
quantity,
|
|
304
|
+
unit,
|
|
305
|
+
orderId,
|
|
306
|
+
payAmt,
|
|
307
|
+
payUrl: payUrlStr,
|
|
308
|
+
},
|
|
309
|
+
0,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 订单分页列表(与 App `OrderQeq` / `NetConfig.QUERY_PAGE` 一致)。
|
|
315
|
+
* @param {{ page?: string, pageSize?: string, status?: string, shopId?: string, objectCode?: string, all?: boolean, shopKeyword?: string }} opts
|
|
316
|
+
*/
|
|
317
|
+
async function list(opts) {
|
|
318
|
+
const cfg = config.requireAuthSession();
|
|
319
|
+
const shopId = opts.shopId != null && opts.shopId !== "" ? Number(opts.shopId) : Number(cfg.shop_id);
|
|
320
|
+
if (!Number.isFinite(shopId)) {
|
|
321
|
+
console.error("cyy: shopId missing (set shop_id in config or pass --shop-id)");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
const pageNum = Math.max(1, parseInt(String(opts.page ?? "1"), 10) || 1);
|
|
325
|
+
const pageSize = Math.max(1, Math.min(100, parseInt(String(opts.pageSize ?? "10"), 10) || 10));
|
|
326
|
+
const objectCode = parseInt(String(opts.objectCode ?? "1"), 10) || 1;
|
|
327
|
+
const status = opts.status != null ? String(opts.status) : "";
|
|
328
|
+
const queryAllFlag = Boolean(opts.all);
|
|
329
|
+
const shopKeyWord = opts.shopKeyword != null ? String(opts.shopKeyword) : "";
|
|
330
|
+
|
|
331
|
+
const bodyObj = {
|
|
332
|
+
pageNum,
|
|
333
|
+
pageSize,
|
|
334
|
+
shopId,
|
|
335
|
+
status,
|
|
336
|
+
queryAllFlag,
|
|
337
|
+
objectCode,
|
|
338
|
+
shopKeyWord,
|
|
339
|
+
};
|
|
340
|
+
const url = http.moduleUrl("DEFAULT", "/order/queryPage");
|
|
341
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
342
|
+
const { ok, json } = await http.request(url, {
|
|
343
|
+
method: "POST",
|
|
344
|
+
headers,
|
|
345
|
+
body: JSON.stringify(bodyObj),
|
|
346
|
+
});
|
|
347
|
+
const success = ok && biz.isBizSuccess(json);
|
|
348
|
+
envelope(success, success ? "success" : "order list failed", { upstream: json }, success ? 0 : 2);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* 取消订单(与 App `OrderReq` / `cancelOrder` 一致)。
|
|
353
|
+
* @param {{ orderId?: string, childId?: string }} opts
|
|
354
|
+
*/
|
|
355
|
+
async function cancel(opts) {
|
|
356
|
+
const cfg = config.requireAuthSession();
|
|
357
|
+
const orderId = opts.orderId;
|
|
358
|
+
if (!orderId) {
|
|
359
|
+
console.error("cyy: --order-id required");
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
const bodyObj = /** @type {Record<string, unknown>} */ ({ orderId: String(orderId) });
|
|
363
|
+
if (opts.childId != null && String(opts.childId) !== "") {
|
|
364
|
+
bodyObj.childId = String(opts.childId);
|
|
365
|
+
}
|
|
366
|
+
const url = http.moduleUrl("ORDER", "/app/order/cancelOrder");
|
|
367
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
368
|
+
const { ok, json } = await http.request(url, {
|
|
369
|
+
method: "POST",
|
|
370
|
+
headers,
|
|
371
|
+
body: JSON.stringify(bodyObj),
|
|
372
|
+
});
|
|
373
|
+
const success = ok && biz.isBizSuccess(json);
|
|
374
|
+
envelope(success, success ? "success" : "cancel order failed", { upstream: json }, success ? 0 : 2);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Multi-SKU checkout: search each keyword → one cart_add → pre_settle (→ optional confirm).
|
|
379
|
+
* @param {{ keywords?: string[], itemsFile?: string, itemsJson?: string, quantity?: string, unit?: string, shopId?: string, siteId?: string, cartModel?: string, preSettleOnly?: boolean }} opts
|
|
380
|
+
*/
|
|
381
|
+
async function checkout(opts) {
|
|
382
|
+
const cfg = config.requireAuthSession();
|
|
383
|
+
const shopId = Number(opts.shopId ?? cfg.shop_id);
|
|
384
|
+
const siteId = Number(opts.siteId ?? cfg.site_id);
|
|
385
|
+
const quantity = Number(opts.quantity || 2);
|
|
386
|
+
const unit = opts.unit || "袋";
|
|
387
|
+
const cartModel = opts.cartModel || "append";
|
|
388
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
389
|
+
|
|
390
|
+
/** @type {{ keyword: string, quantity?: number, unit?: string }[]} */
|
|
391
|
+
let itemSpecs = [];
|
|
392
|
+
if (opts.itemsFile) {
|
|
393
|
+
itemSpecs = JSON.parse(fs.readFileSync(opts.itemsFile, "utf8"));
|
|
394
|
+
} else if (opts.itemsJson) {
|
|
395
|
+
itemSpecs = JSON.parse(opts.itemsJson);
|
|
396
|
+
} else if (opts.keywords && opts.keywords.length) {
|
|
397
|
+
itemSpecs = opts.keywords.map((k) => ({ keyword: k, quantity, unit }));
|
|
398
|
+
}
|
|
399
|
+
if (!itemSpecs.length) {
|
|
400
|
+
console.error("cyy: provide --keyword (repeatable), --items-json, or --items-file");
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const lines = [];
|
|
405
|
+
const cartItemList = [];
|
|
406
|
+
for (const spec of itemSpecs) {
|
|
407
|
+
const kw = spec.keyword;
|
|
408
|
+
if (!kw) continue;
|
|
409
|
+
const line = await resolveProductLine(cfg, {
|
|
410
|
+
keyword: kw,
|
|
411
|
+
shopId,
|
|
412
|
+
siteId,
|
|
413
|
+
quantity: Number(spec.quantity ?? quantity),
|
|
414
|
+
unit: String(spec.unit ?? unit),
|
|
415
|
+
});
|
|
416
|
+
lines.push(line);
|
|
417
|
+
cartItemList.push({
|
|
418
|
+
skuCode: line.skuCode,
|
|
419
|
+
skuName: line.skuName,
|
|
420
|
+
skuQty: line.skuQty,
|
|
421
|
+
skuSaleUnit: line.skuSaleUnit,
|
|
422
|
+
listPrice: line.salePrice,
|
|
423
|
+
salePrice: line.salePrice,
|
|
424
|
+
unitRate: line.unitRate,
|
|
425
|
+
selectFlag: true,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const cartBody = normalizeAddCartBody(
|
|
430
|
+
JSON.stringify({ cartItemList, cartModel, shopId, siteId }),
|
|
431
|
+
cfg,
|
|
432
|
+
);
|
|
433
|
+
const cartUrl = http.moduleUrl("ORDER", "/app/order/cart");
|
|
434
|
+
let r = await http.request(cartUrl, { method: "POST", headers, body: cartBody });
|
|
435
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
436
|
+
envelope(false, "cart failed", { upstream: r.json, lines }, 2);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const cartRows =
|
|
441
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
442
|
+
? /** @type {{cartItemList?:Record<string,unknown>[]}} */ (r.json.data).cartItemList
|
|
443
|
+
: [];
|
|
444
|
+
const byCode = new Map();
|
|
445
|
+
if (Array.isArray(cartRows)) {
|
|
446
|
+
for (const row of cartRows) {
|
|
447
|
+
const code = String(/** @type {{skuCode?:string}} */ (row).skuCode || "");
|
|
448
|
+
if (code) byCode.set(code, row);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const itemVOList = [];
|
|
453
|
+
for (const line of lines) {
|
|
454
|
+
const row = byCode.get(line.skuCode) || {};
|
|
455
|
+
const addTime = row.addTime != null ? String(row.addTime) : "";
|
|
456
|
+
if (!addTime) {
|
|
457
|
+
envelope(false, `missing addTime for ${line.skuCode} after cart`, { upstream: r.json, lines }, 2);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
itemVOList.push({
|
|
461
|
+
skuId: line.skuId,
|
|
462
|
+
skuCode: line.skuCode,
|
|
463
|
+
skuName: line.skuName,
|
|
464
|
+
skuQty: line.skuQty,
|
|
465
|
+
skuSaleUnit: line.skuSaleUnit,
|
|
466
|
+
salePrice: line.salePrice,
|
|
467
|
+
listPrice: line.listPrice,
|
|
468
|
+
unitRate: line.unitRate,
|
|
469
|
+
addTime,
|
|
470
|
+
stockQty: line.stockQty,
|
|
471
|
+
minSaleUnit: line.skuSaleUnit,
|
|
472
|
+
selectFlag: true,
|
|
473
|
+
status: 0,
|
|
474
|
+
canBuyFlag: true,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const preBody = JSON.stringify({
|
|
479
|
+
headerVO: { shopId, siteId },
|
|
480
|
+
itemVOList,
|
|
481
|
+
balanceFlag: false,
|
|
482
|
+
userPointFlag: false,
|
|
483
|
+
});
|
|
484
|
+
const preUrl = http.moduleUrl("ORDER", "/app/order/preSettleOrder");
|
|
485
|
+
r = await http.request(preUrl, { method: "POST", headers, body: preBody });
|
|
486
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
487
|
+
envelope(false, "pre-settle failed", { upstream: r.json, lines }, 2);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const pdata =
|
|
492
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
493
|
+
? r.json.data
|
|
494
|
+
: null;
|
|
495
|
+
const preSettleId = pdata && /** @type {{preSettleId?:string}} */ (pdata).preSettleId;
|
|
496
|
+
const headerVO = pdata && /** @type {{headerVO?:unknown}} */ (pdata).headerVO;
|
|
497
|
+
const payAmt =
|
|
498
|
+
pdata &&
|
|
499
|
+
/** @type {{headerVO?:{payAmt?:unknown,totalPayAmt?:unknown}}} */ (pdata).headerVO &&
|
|
500
|
+
(/** @type {{payAmt?:unknown}} */ (
|
|
501
|
+
/** @type {{headerVO?:{payAmt?:unknown}}} */ (pdata).headerVO
|
|
502
|
+
).payAmt ??
|
|
503
|
+
/** @type {{totalPayAmt?:unknown}} */ (
|
|
504
|
+
/** @type {{headerVO?:{totalPayAmt?:unknown}}} */ (pdata).headerVO
|
|
505
|
+
).totalPayAmt);
|
|
506
|
+
|
|
507
|
+
if (opts.preSettleOnly) {
|
|
508
|
+
envelope(
|
|
509
|
+
true,
|
|
510
|
+
"success",
|
|
511
|
+
{
|
|
512
|
+
lines,
|
|
513
|
+
preSettleId,
|
|
514
|
+
payAmt,
|
|
515
|
+
upstream: r.json,
|
|
516
|
+
hint: "Call order confirm with preSettleId + slim headerVO/itemVOList (skuId per line) after user confirms.",
|
|
517
|
+
},
|
|
518
|
+
0,
|
|
519
|
+
);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (!preSettleId) {
|
|
524
|
+
envelope(false, "missing preSettleId", { upstream: r.json, lines }, 2);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const preItems = pdata && /** @type {{itemVOList?:unknown[]}} */ (pdata).itemVOList;
|
|
529
|
+
const confirmItems = Array.isArray(preItems)
|
|
530
|
+
? preItems.map((it) => {
|
|
531
|
+
const row = /** @type {Record<string,unknown>} */ (it);
|
|
532
|
+
const match = lines.find((l) => l.skuCode === row.skuCode);
|
|
533
|
+
return {
|
|
534
|
+
skuId: match?.skuId ?? row.skuId,
|
|
535
|
+
skuCode: row.skuCode,
|
|
536
|
+
skuQty: row.skuQty,
|
|
537
|
+
skuSaleUnit: row.skuSaleUnit,
|
|
538
|
+
};
|
|
539
|
+
})
|
|
540
|
+
: itemVOList.map((it) => ({
|
|
541
|
+
skuId: it.skuId,
|
|
542
|
+
skuCode: it.skuCode,
|
|
543
|
+
skuQty: it.skuQty,
|
|
544
|
+
skuSaleUnit: it.skuSaleUnit,
|
|
545
|
+
}));
|
|
546
|
+
|
|
547
|
+
const slimHeader =
|
|
548
|
+
headerVO && typeof headerVO === "object"
|
|
549
|
+
? {
|
|
550
|
+
shopId: /** @type {{shopId?:number}} */ (headerVO).shopId ?? shopId,
|
|
551
|
+
siteId: Number(/** @type {{siteId?:unknown}} */ (headerVO).siteId ?? siteId),
|
|
552
|
+
receiverName: /** @type {{receiverName?:string}} */ (headerVO).receiverName,
|
|
553
|
+
receiverPhone: /** @type {{receiverPhone?:string}} */ (headerVO).receiverPhone,
|
|
554
|
+
receiverAddressProvince: /** @type {{receiverAddressProvince?:string}} */ (headerVO)
|
|
555
|
+
.receiverAddressProvince,
|
|
556
|
+
receiverAddressCity: /** @type {{receiverAddressCity?:string}} */ (headerVO).receiverAddressCity,
|
|
557
|
+
receiverAddressArea: /** @type {{receiverAddressArea?:string}} */ (headerVO).receiverAddressArea,
|
|
558
|
+
receiverAddressDetail: /** @type {{receiverAddressDetail?:string}} */ (headerVO)
|
|
559
|
+
.receiverAddressDetail,
|
|
560
|
+
}
|
|
561
|
+
: { shopId, siteId };
|
|
562
|
+
|
|
563
|
+
const confirmBody = JSON.stringify({
|
|
564
|
+
preSettleId,
|
|
565
|
+
headerVO: slimHeader,
|
|
566
|
+
itemVOList: confirmItems,
|
|
567
|
+
balanceFlag: false,
|
|
568
|
+
userPointFlag: false,
|
|
569
|
+
validateStockFlag: true,
|
|
570
|
+
repeatFlag: false,
|
|
571
|
+
});
|
|
572
|
+
const confirmUrl = http.moduleUrl("ORDER", "/app/order/confirmOrder");
|
|
573
|
+
r = await http.request(confirmUrl, { method: "POST", headers, body: confirmBody });
|
|
574
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
575
|
+
envelope(false, "confirm failed", { upstream: r.json, lines, preSettleId, payAmt }, 2);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const orderData =
|
|
580
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
581
|
+
? r.json.data
|
|
582
|
+
: {};
|
|
583
|
+
const orderId = /** @type {{orderId?:string}} */ (orderData).orderId;
|
|
584
|
+
|
|
585
|
+
const memberUrl = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
586
|
+
const h2 = { ...headers };
|
|
587
|
+
delete h2["Content-Type"];
|
|
588
|
+
const uinfo = await http.request(memberUrl, { method: "GET", headers: h2, body: null });
|
|
589
|
+
let phone = String(cfg.phone || "");
|
|
590
|
+
let nick = "";
|
|
591
|
+
let img = "";
|
|
592
|
+
if (uinfo.ok && biz.isBizSuccess(uinfo.json) && uinfo.json && typeof uinfo.json === "object") {
|
|
593
|
+
const d = /** @type {{data?:Record<string,unknown>}} */ (uinfo.json).data;
|
|
594
|
+
if (d) {
|
|
595
|
+
phone = String(d.memberPhone || phone);
|
|
596
|
+
nick = String(d.memberNick || "");
|
|
597
|
+
img = String(d.headerImg || "");
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const enc = encodeURIComponent;
|
|
601
|
+
const payUrlStr = `https://dhcmall.ifoodbuy.com/H5/#/replacePay?phone=${enc(phone)}&orderid=${enc(String(orderId || ""))}&img=${enc(img)}&nick=${enc(nick)}&siteId=${siteId}&shopId=${shopId}&share=true`;
|
|
602
|
+
|
|
603
|
+
envelope(
|
|
604
|
+
true,
|
|
605
|
+
"success",
|
|
606
|
+
{
|
|
607
|
+
lines,
|
|
608
|
+
preSettleId,
|
|
609
|
+
payAmt,
|
|
610
|
+
orderId,
|
|
611
|
+
payUrl: payUrlStr,
|
|
612
|
+
upstream: r.json,
|
|
613
|
+
},
|
|
614
|
+
0,
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
module.exports = { preSettle, confirm, payUrl, quick, checkout, list, cancel };
|