oz-request 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/package.json +16 -0
- package/src/index.js +391 -0
- package/src/utils.js +160 -0
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "oz-request",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "oz-request",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"author": "Your Name",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"https-proxy-agent": "^7.0.2",
|
|
11
|
+
"tough-cookie": "^4.1.3"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=14.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
// 全局环境设置
|
|
2
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
3
|
+
require('dns').setDefaultResultOrder('ipv4first');
|
|
4
|
+
|
|
5
|
+
const http2 = require('http2');
|
|
6
|
+
const tls = require('tls');
|
|
7
|
+
const { CookieJar } = require('tough-cookie');
|
|
8
|
+
const { URL } = require('url');
|
|
9
|
+
const crypto = require('crypto');
|
|
10
|
+
const tough = require('tough-cookie');
|
|
11
|
+
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
12
|
+
|
|
13
|
+
// ---------- 工具函数 ----------
|
|
14
|
+
const getRandomNum = (min, max) => {
|
|
15
|
+
min = Math.ceil(min);
|
|
16
|
+
max = Math.floor(max);
|
|
17
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const stopFn = (s = 1) => {
|
|
21
|
+
return new Promise(resolve => {
|
|
22
|
+
setTimeout(resolve, 1000 * s);
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function getWindowsChromeUA() {
|
|
27
|
+
const versions = {
|
|
28
|
+
chrome: Math.floor(Math.random() * 20) + 130,
|
|
29
|
+
windows: ['10.0', '11.0'][Math.floor(Math.random() * 2)]
|
|
30
|
+
};
|
|
31
|
+
return `Mozilla/5.0 (Windows NT ${versions.windows}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${versions.chrome}.0.0.0 Safari/537.36`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------- Cookie 会话管理器 ----------
|
|
35
|
+
const pendingCreates = new Map();
|
|
36
|
+
|
|
37
|
+
async function getOrCreateJar(cookieStr) {
|
|
38
|
+
const key = cookieStr;
|
|
39
|
+
if (pendingCreates.has(key)) {
|
|
40
|
+
return pendingCreates.get(key);
|
|
41
|
+
}
|
|
42
|
+
const createPromise = (async () => {
|
|
43
|
+
const jar = new CookieJar();
|
|
44
|
+
await initSession(jar, cookieStr);
|
|
45
|
+
pendingCreates.delete(key);
|
|
46
|
+
return jar;
|
|
47
|
+
})();
|
|
48
|
+
pendingCreates.set(key, createPromise);
|
|
49
|
+
return createPromise;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function initSession(jar, initialCookies) {
|
|
53
|
+
initialCookies = initialCookies.replaceAll('__Secure', 'mySecure');
|
|
54
|
+
for (const cookieStr of initialCookies.split('; ')) {
|
|
55
|
+
const cookie = tough.Cookie.parse(cookieStr);
|
|
56
|
+
if (cookie) {
|
|
57
|
+
await new Promise(resolve =>
|
|
58
|
+
jar.setCookie(cookie, 'https://seller.ozon.ru', resolve)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function serializeCookies(jar) {
|
|
65
|
+
let cookies = await new Promise(resolve =>
|
|
66
|
+
jar.getCookies('https://seller.ozon.ru', (err, cookies) => {
|
|
67
|
+
if (err) return resolve([]);
|
|
68
|
+
const cookieMap = new Map();
|
|
69
|
+
cookies.forEach(cookie => {
|
|
70
|
+
cookieMap.set(cookie.key, cookie.cookieString());
|
|
71
|
+
});
|
|
72
|
+
resolve(Array.from(cookieMap.values()).join('; '));
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
cookies = cookies.replaceAll('mySecure', '__Secure');
|
|
76
|
+
return cookies;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------- 工厂函数 ----------
|
|
80
|
+
function createOzonClient(options) {
|
|
81
|
+
const { proxyList, tlsFingerprint = null } = options;
|
|
82
|
+
|
|
83
|
+
if (!proxyList || !Array.isArray(proxyList) || proxyList.length === 0) {
|
|
84
|
+
throw new Error('proxyList must be a non-empty array of proxy URLs');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const DEFAULT_FINGERPRINT = {
|
|
88
|
+
cipherSuites: [
|
|
89
|
+
'TLS_AES_128_GCM_SHA256',
|
|
90
|
+
'TLS_AES_256_GCM_SHA384',
|
|
91
|
+
'TLS_CHACHA20_POLY1305_SHA256',
|
|
92
|
+
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
|
93
|
+
'ECDHE-RSA-AES128-GCM-SHA256',
|
|
94
|
+
'ECDHE-ECDSA-AES256-GCM-SHA384',
|
|
95
|
+
'ECDHE-RSA-AES256-GCM-SHA384',
|
|
96
|
+
'ECDHE-ECDSA-CHACHA20-POLY1305',
|
|
97
|
+
'ECDHE-RSA-CHACHA20-POLY1305',
|
|
98
|
+
'ECDHE-RSA-AES128-SHA',
|
|
99
|
+
'ECDHE-RSA-AES256-SHA',
|
|
100
|
+
'AES128-GCM-SHA256',
|
|
101
|
+
'AES256-GCM-SHA384',
|
|
102
|
+
'AES128-SHA',
|
|
103
|
+
'AES256-SHA'
|
|
104
|
+
].join(':'),
|
|
105
|
+
ellipticCurves: ['X25519', 'secp256r1', 'secp384r1'],
|
|
106
|
+
signatureAlgorithms: [
|
|
107
|
+
'ecdsa_secp256r1_sha256',
|
|
108
|
+
'rsa_pss_rsae_sha256',
|
|
109
|
+
'rsa_pkcs1_sha256',
|
|
110
|
+
'ecdsa_secp384r1_sha384',
|
|
111
|
+
'rsa_pss_rsae_sha384',
|
|
112
|
+
'rsa_pkcs1_sha384',
|
|
113
|
+
'rsa_pss_rsae_sha512',
|
|
114
|
+
'rsa_pkcs1_sha512'
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const activeFingerprint = tlsFingerprint || DEFAULT_FINGERPRINT;
|
|
119
|
+
|
|
120
|
+
function createBrowserSocket(hostname) {
|
|
121
|
+
return tls.connect({
|
|
122
|
+
host: hostname,
|
|
123
|
+
port: 443,
|
|
124
|
+
ciphers: activeFingerprint.cipherSuites,
|
|
125
|
+
ALPNProtocols: ['h2'],
|
|
126
|
+
servername: hostname,
|
|
127
|
+
ecdhCurve: activeFingerprint.ellipticCurves.join(':'),
|
|
128
|
+
signatureAlgorithms: activeFingerprint.signatureAlgorithms.join(':'),
|
|
129
|
+
minVersion: 'TLSv1.2',
|
|
130
|
+
maxVersion: 'TLSv1.3'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 核心请求函数,增加 customHeaders 参数
|
|
135
|
+
async function cloudRequest({
|
|
136
|
+
url,
|
|
137
|
+
data,
|
|
138
|
+
count307,
|
|
139
|
+
jar,
|
|
140
|
+
customHeaders = {}
|
|
141
|
+
}) {
|
|
142
|
+
const parsedUrl = new URL(url);
|
|
143
|
+
const hostname = parsedUrl.hostname;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const socket = createBrowserSocket(hostname);
|
|
147
|
+
await new Promise((resolve, reject) => {
|
|
148
|
+
socket.once('secureConnect', resolve);
|
|
149
|
+
socket.once('error', reject);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const ipIndex = getRandomNum(0, proxyList.length - 1);
|
|
153
|
+
const proxyUrl = proxyList[ipIndex];
|
|
154
|
+
const agent = new HttpsProxyAgent(proxyUrl, {
|
|
155
|
+
rejectUnauthorized: false,
|
|
156
|
+
keepAlive: true,
|
|
157
|
+
lookup: (hostname, options, callback) => {
|
|
158
|
+
require('dns').resolve4(hostname, (err, addresses) => {
|
|
159
|
+
if (err) return callback(err);
|
|
160
|
+
callback(null, addresses[0], 4);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const client = http2.connect(`https://${hostname}`, {
|
|
166
|
+
createConnection: () => socket,
|
|
167
|
+
settings: {
|
|
168
|
+
headerTableSize: 65536,
|
|
169
|
+
enablePush: false,
|
|
170
|
+
initialWindowSize: 6291456,
|
|
171
|
+
maxFrameSize: 16384,
|
|
172
|
+
maxHeaderListSize: 262144
|
|
173
|
+
},
|
|
174
|
+
agent,
|
|
175
|
+
protocol: 'h2'
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
client.on('error', err => {
|
|
179
|
+
console.error('HTTP/2连接错误:', err);
|
|
180
|
+
client.destroy();
|
|
181
|
+
throw new Error('HTTP/2连接错误');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const cookiesStr = await serializeCookies(jar);
|
|
185
|
+
|
|
186
|
+
let sellerIdMatch =
|
|
187
|
+
cookiesStr.match(/sc_company_id=(\d+)/) ||
|
|
188
|
+
cookiesStr.match(/contentId=(\d+)/);
|
|
189
|
+
let sellerId = sellerIdMatch ? sellerIdMatch[1] : '';
|
|
190
|
+
|
|
191
|
+
const userAgent = getWindowsChromeUA(); // 保留 UA 生成,但未使用?原始代码中 headers 里没有显式使用 UA,可忽略
|
|
192
|
+
|
|
193
|
+
// 默认 headers
|
|
194
|
+
const baseHeaders = {
|
|
195
|
+
':method': 'POST',
|
|
196
|
+
':path': parsedUrl.pathname + parsedUrl.search,
|
|
197
|
+
':scheme': 'https',
|
|
198
|
+
':authority': parsedUrl.hostname,
|
|
199
|
+
accept: 'application/json, text/plain, */*',
|
|
200
|
+
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
201
|
+
'content-type': 'application/json',
|
|
202
|
+
cookie: cookiesStr,
|
|
203
|
+
origin: 'https://seller.ozon.ru',
|
|
204
|
+
'sec-ch-ua':
|
|
205
|
+
'"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
|
|
206
|
+
'sec-ch-ua-mobile': '?0',
|
|
207
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
208
|
+
'sec-fetch-dest': 'empty',
|
|
209
|
+
'sec-fetch-mode': 'cors',
|
|
210
|
+
'sec-fetch-site': 'same-origin',
|
|
211
|
+
Referer: 'https://seller.ozon.ru/app/products/create',
|
|
212
|
+
'X-O3-App-Name': 'seller-ui',
|
|
213
|
+
'X-O3-Company-Id': sellerId + '',
|
|
214
|
+
'X-O3-Language': 'zh-Hans',
|
|
215
|
+
'X-O3-Page-Type': 'products-other',
|
|
216
|
+
Origin: 'https://seller.ozon.ru',
|
|
217
|
+
'x-forwarded-for': proxyUrl.split('@')[1].split(':')[0],
|
|
218
|
+
'x-real-ip': proxyUrl.split('@')[1].split(':')[0],
|
|
219
|
+
via: '1.1 proxy-server'
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// 合并外部 headers(外部优先)
|
|
223
|
+
const headers = { ...baseHeaders, ...customHeaders };
|
|
224
|
+
|
|
225
|
+
// 强制添加随机 traceid 和 spanid,且不允许外部覆盖(确保每次请求唯一)
|
|
226
|
+
headers['x-b3-traceid'] = crypto.randomBytes(8).toString('hex');
|
|
227
|
+
headers['x-b3-spanid'] = crypto.randomBytes(4).toString('hex');
|
|
228
|
+
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
let responseData = Buffer.alloc(0);
|
|
231
|
+
let responseHeaders = {};
|
|
232
|
+
|
|
233
|
+
const req = client.request(headers);
|
|
234
|
+
const timeout = setTimeout(() => {
|
|
235
|
+
req.close();
|
|
236
|
+
client.close();
|
|
237
|
+
reject(new Error('请求超时'));
|
|
238
|
+
}, 30000);
|
|
239
|
+
|
|
240
|
+
req.on('response', headers => {
|
|
241
|
+
responseHeaders = headers;
|
|
242
|
+
if (headers['set-cookie']) {
|
|
243
|
+
const setCookies = Array.isArray(headers['set-cookie'])
|
|
244
|
+
? headers['set-cookie']
|
|
245
|
+
: [headers['set-cookie']];
|
|
246
|
+
setCookies.forEach(cookie => {
|
|
247
|
+
const storedCookie = cookie.replace('__Secure', 'mySecure');
|
|
248
|
+
jar.setCookieSync(storedCookie, 'https://seller.ozon.ru');
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
req.on('data', chunk => {
|
|
254
|
+
responseData = Buffer.concat([responseData, chunk]);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
req.on('end', async () => {
|
|
258
|
+
clearTimeout(timeout);
|
|
259
|
+
client.close();
|
|
260
|
+
|
|
261
|
+
if (responseHeaders[':status'] === 307) {
|
|
262
|
+
const redirectUrl = responseHeaders.location;
|
|
263
|
+
console.log('进入307');
|
|
264
|
+
count307++;
|
|
265
|
+
if (count307 >= 5) {
|
|
266
|
+
console.log('307上限,结束');
|
|
267
|
+
resolve('change_cookie');
|
|
268
|
+
} else {
|
|
269
|
+
resolve(
|
|
270
|
+
cloudRequest({
|
|
271
|
+
url: redirectUrl,
|
|
272
|
+
data,
|
|
273
|
+
count307,
|
|
274
|
+
jar,
|
|
275
|
+
customHeaders // 透传外部 headers
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
if (responseHeaders['content-type']?.includes('text/html')) {
|
|
281
|
+
return resolve({ error_type: 'abt_data' });
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const res = JSON.parse(responseData.toString());
|
|
285
|
+
if (!res.items) {
|
|
286
|
+
console.log('业务错误,重试!');
|
|
287
|
+
resolve('change_cookie');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const newCookie = await serializeCookies(jar);
|
|
291
|
+
res.newCookie = newCookie;
|
|
292
|
+
resolve(res);
|
|
293
|
+
} catch (e) {
|
|
294
|
+
console.log(e);
|
|
295
|
+
console.log('请求错误,成功捕获,进行重试');
|
|
296
|
+
resolve('change_cookie');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
req.on('error', err => {
|
|
302
|
+
clearTimeout(timeout);
|
|
303
|
+
client.close();
|
|
304
|
+
console.error('请求错误:', err);
|
|
305
|
+
reject(err);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
req.write(JSON.stringify(data));
|
|
309
|
+
req.end();
|
|
310
|
+
});
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error('请求过程中发生错误:', error);
|
|
313
|
+
if (
|
|
314
|
+
error.message.includes('ECONNRESET') ||
|
|
315
|
+
error.message.includes('HTTP/2连接错误')
|
|
316
|
+
) {
|
|
317
|
+
return 'change_cookie';
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 对外暴露的函数,接收 customHeaders
|
|
324
|
+
async function fetchOzonDataCore({
|
|
325
|
+
url,
|
|
326
|
+
reqParmas,
|
|
327
|
+
originalCookie,
|
|
328
|
+
headers = {}
|
|
329
|
+
}) {
|
|
330
|
+
return new Promise(resolve => {
|
|
331
|
+
let tryTimes = 0;
|
|
332
|
+
const core = async () => {
|
|
333
|
+
let response;
|
|
334
|
+
try {
|
|
335
|
+
const jar = await getOrCreateJar(originalCookie);
|
|
336
|
+
response = await cloudRequest({
|
|
337
|
+
url,
|
|
338
|
+
data: reqParmas,
|
|
339
|
+
count307: 0,
|
|
340
|
+
jar,
|
|
341
|
+
customHeaders: headers // 传递外部 headers
|
|
342
|
+
});
|
|
343
|
+
if (response.error_type == 'abt_data') {
|
|
344
|
+
console.log('机器人拦截');
|
|
345
|
+
tryTimes++;
|
|
346
|
+
if (tryTimes >= 5) {
|
|
347
|
+
resolve({ fail: true, err_msg: '机器人拦截' });
|
|
348
|
+
} else {
|
|
349
|
+
await stopFn(0.2);
|
|
350
|
+
core();
|
|
351
|
+
}
|
|
352
|
+
} else if (response == 'change_cookie') {
|
|
353
|
+
tryTimes++;
|
|
354
|
+
if (tryTimes >= 5) {
|
|
355
|
+
resolve({ fail: true, err_msg: '请切换cookie' });
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
await stopFn(0.2);
|
|
359
|
+
core();
|
|
360
|
+
} else if (!response || response?.error) {
|
|
361
|
+
tryTimes++;
|
|
362
|
+
if (tryTimes >= 5) {
|
|
363
|
+
resolve({ fail: true, err_msg: '请切换cookie' });
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
console.log('正常是cookie失效,重新获取新的');
|
|
367
|
+
await stopFn(0.2);
|
|
368
|
+
core();
|
|
369
|
+
} else {
|
|
370
|
+
resolve(response);
|
|
371
|
+
}
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.log('错误2: 重试');
|
|
374
|
+
console.log(error);
|
|
375
|
+
tryTimes++;
|
|
376
|
+
if (tryTimes >= 5) {
|
|
377
|
+
resolve({ fail: true, err_msg: '请求发生错误' });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
await stopFn(0.2);
|
|
381
|
+
core();
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
core();
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return fetchOzonDataCore;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
module.exports = createOzonClient;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// 获取随机数(min 到 max之间的随机数,包含边界值)
|
|
2
|
+
const getRandomNum = (min, max) => {
|
|
3
|
+
min = Math.ceil(min);
|
|
4
|
+
max = Math.floor(max);
|
|
5
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// 暂停函数
|
|
9
|
+
const stopFn = (s = 1) => {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
resolve();
|
|
13
|
+
}, 1000 * s);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// 数组根据key,进行去重
|
|
18
|
+
const arrObjUni = (array, key) => {
|
|
19
|
+
const _set = [...new Set(array.map(e => e[key]))];
|
|
20
|
+
let deArray = [];
|
|
21
|
+
_set.map(item => {
|
|
22
|
+
deArray.push(array[array.findIndex(val => val[key] === item)]);
|
|
23
|
+
});
|
|
24
|
+
return deArray;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//处理属性,优先取外部的值,然后封装为老数据格式
|
|
28
|
+
const transformAttributes = (
|
|
29
|
+
newAttrs,
|
|
30
|
+
baseItem,
|
|
31
|
+
{ length, width, height, weight, barcode }
|
|
32
|
+
) => {
|
|
33
|
+
const attrs = newAttrs.map(attr => {
|
|
34
|
+
const { attribute_id, values = [] } = attr;
|
|
35
|
+
|
|
36
|
+
let value = '';
|
|
37
|
+
let collection = [];
|
|
38
|
+
const dictionary_value_id = values[0]?.dictionary_value_id || '';
|
|
39
|
+
|
|
40
|
+
// 多值 → collection
|
|
41
|
+
if (
|
|
42
|
+
values.length > 1 ||
|
|
43
|
+
(dictionary_value_id !== '0' &&
|
|
44
|
+
attribute_id !== '8229' &&
|
|
45
|
+
attribute_id !== '85')
|
|
46
|
+
) {
|
|
47
|
+
//8229特殊属性,不确定还有其他的没有
|
|
48
|
+
collection = values.map(v => v.value).filter(Boolean);
|
|
49
|
+
} else {
|
|
50
|
+
// 单值 → value
|
|
51
|
+
const v = values[0];
|
|
52
|
+
const val = v.value || '';
|
|
53
|
+
value = val;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
key: attribute_id,
|
|
58
|
+
value,
|
|
59
|
+
collection,
|
|
60
|
+
complex: [],
|
|
61
|
+
complex_collection: []
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
//变化完后去把内部值替换成外部的,没有就新建
|
|
66
|
+
const outerMap = {
|
|
67
|
+
4180: {
|
|
68
|
+
value: baseItem?.variant_name || '',
|
|
69
|
+
type: 'value'
|
|
70
|
+
},
|
|
71
|
+
8229: {
|
|
72
|
+
value: baseItem?.description_type_name || '',
|
|
73
|
+
type: 'value'
|
|
74
|
+
},
|
|
75
|
+
4194: {
|
|
76
|
+
value: baseItem?.main_image || '',
|
|
77
|
+
type: 'value'
|
|
78
|
+
},
|
|
79
|
+
4195: {
|
|
80
|
+
value: baseItem?.secondary_images || [],
|
|
81
|
+
type: 'arr'
|
|
82
|
+
},
|
|
83
|
+
9024: {
|
|
84
|
+
value: barcode || '',
|
|
85
|
+
type: 'value'
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (baseItem?.brand_name) {
|
|
90
|
+
outerMap['85'] = {
|
|
91
|
+
value: baseItem?.brand_name || '',
|
|
92
|
+
type: 'value'
|
|
93
|
+
};
|
|
94
|
+
outerMap['31'] = {
|
|
95
|
+
value: baseItem?.brand_name || '',
|
|
96
|
+
type: 'value'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//不知道会不会外部没有,里面有,保险起见!
|
|
101
|
+
if (length && width && height && weight) {
|
|
102
|
+
outerMap['9454'] = {
|
|
103
|
+
value: length + '',
|
|
104
|
+
type: 'value'
|
|
105
|
+
};
|
|
106
|
+
outerMap['9455'] = {
|
|
107
|
+
value: width + '',
|
|
108
|
+
type: 'value'
|
|
109
|
+
};
|
|
110
|
+
outerMap['9456'] = {
|
|
111
|
+
value: height + '',
|
|
112
|
+
type: 'value'
|
|
113
|
+
};
|
|
114
|
+
outerMap['4497'] = {
|
|
115
|
+
value: weight + '',
|
|
116
|
+
type: 'value'
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// 遍历attrs,根据key_id,替换value
|
|
120
|
+
attrs.forEach(item => {
|
|
121
|
+
const { key } = item;
|
|
122
|
+
if (outerMap[key] && outerMap[key].type === 'value') {
|
|
123
|
+
item.value = outerMap[key].value;
|
|
124
|
+
outerMap[key].isReplace = true;
|
|
125
|
+
} else if (outerMap[key] && outerMap[key].type === 'arr') {
|
|
126
|
+
item.collection = outerMap[key].value;
|
|
127
|
+
outerMap[key].isReplace = true;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
//对没没有替换的属性,添加到attrs中
|
|
132
|
+
for (const [key, value] of Object.entries(outerMap)) {
|
|
133
|
+
if (!value.isReplace) {
|
|
134
|
+
const obj = {
|
|
135
|
+
key: key,
|
|
136
|
+
complex: [],
|
|
137
|
+
complex_collection: []
|
|
138
|
+
};
|
|
139
|
+
value.type === 'value'
|
|
140
|
+
? (obj.value = value.value)
|
|
141
|
+
: (obj.collection = value.value);
|
|
142
|
+
attrs.push(obj);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return attrs;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const getCompanyIdByCookie = cookie => {
|
|
149
|
+
const match = cookie?.match(/sc_company_id=([^;]+)/);
|
|
150
|
+
const companyId = match ? match[1] : null;
|
|
151
|
+
return companyId;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
stopFn,
|
|
156
|
+
arrObjUni,
|
|
157
|
+
getRandomNum,
|
|
158
|
+
transformAttributes,
|
|
159
|
+
getCompanyIdByCookie
|
|
160
|
+
};
|