/ clis / jd / add-cart.js
add-cart.js
 1  import { AuthRequiredError } from '@jackwener/opencli/errors';
 2  import { cli, Strategy } from '@jackwener/opencli/registry';
 3  import { clampInt, normalizeNumericId } from '../_shared/common.js';
 4  cli({
 5      site: 'jd',
 6      name: 'add-cart',
 7      description: '京东加入购物车',
 8      domain: 'item.jd.com',
 9      strategy: Strategy.COOKIE,
10      args: [
11          { name: 'sku', positional: true, required: true, help: '商品 SKU ID' },
12          { name: 'num', type: 'int', default: 1, help: '数量' },
13          { name: 'dry-run', type: 'bool', default: false, help: '仅预览,不实际加入购物车' },
14      ],
15      columns: ['status', 'title', 'price', 'sku'],
16      navigateBefore: false,
17      func: async (page, kwargs) => {
18          const sku = normalizeNumericId(kwargs.sku, 'sku', '100291143898');
19          const num = clampInt(kwargs.num, 1, 1, 99);
20          const dryRun = !!kwargs['dry-run'];
21          await page.goto(`https://item.jd.com/${sku}.html`);
22          await page.wait(4);
23          const info = await page.evaluate(`
24        (() => {
25          const text = document.body?.innerText || '';
26          const titleMatch = document.title.match(/^【[^】]*】(.+?)【/);
27          const title = titleMatch ? titleMatch[1].trim() : document.title.split('-')[0].trim();
28          const priceMatch = text.match(/¥([\\d,.]+)/);
29          const price = priceMatch ? '¥' + priceMatch[1] : '';
30          return { title, price };
31        })()
32      `);
33          if (dryRun) {
34              return [{
35                      status: 'dry-run',
36                      title: (info?.title || '').slice(0, 80),
37                      price: info?.price || '',
38                      sku,
39                  }];
40          }
41          await page.goto(`https://cart.jd.com/gate.action?pid=${sku}&pcount=${num}&ptype=1`);
42          await page.wait(4);
43          const result = await page.evaluate(`
44        (() => {
45          const url = location.href;
46          const text = document.body?.innerText || '';
47          if (text.includes('已成功加入') || text.includes('商品已成功') || url.includes('addtocart')) {
48            return 'success';
49          }
50          if (text.includes('请登录') || text.includes('login') || url.includes('login')) {
51            return 'login_required';
52          }
53          return 'page:' + url.substring(0, 60) + ' | ' + text.substring(0, 100);
54        })()
55      `);
56          if (result === 'login_required') {
57              throw new AuthRequiredError('jd add-cart requires a logged-in JD session');
58          }
59          let status = '? 未知';
60          if (result === 'success')
61              status = '✓ 已加入购物车';
62          else
63              status = '? ' + result;
64          return [{
65                  status,
66                  title: (info?.title || '').slice(0, 80),
67                  price: info?.price || '',
68                  sku,
69              }];
70      },
71  });