Commit 428a3d3f by yexing

[yx] update

parent a4d4f159
log
.vscode
old
.
后台WFS导出各种报告
#### 软件打包
```bash
pip install ordered-set # 加速编译
pip install nuitka
pip install zstandard # onefile时压缩文件
# 打包命令
python -m nuitka --mingw64 --standalone --onefile --jobs=64 --output-dir=out main.py
node_modules
pnpm-lock.yaml
hook
(function () {
'use strict';
const MONTHS = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function getValue(key, str, sep = ';') {
const items = (str || document.cookie).split(sep);
for (let item of items) {
item = item.trim();
if (item.startsWith(key)) {
let value = item.substring(key.length + 1, item.length);
console.log(`已找到${key}: ${value}`);
return value;
}
}
console.warn(`未找到${key}`);
return '';
}
const replacer = (value) => {
if (value === null || value === undefined) {
return '';
}
if (typeof value == 'string') {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
};
function JSON2CSV(json) {
const cols = Object.keys(json[0]);
let csv = cols.join(',') + '\r\n';
json.forEach(row => {
const values = cols.map(col => replacer(row[col]));
csv += values.join(',') + '\r\n';
});
return csv;
}
function downloadFile(fn, blob) {
// const url = `data:text/csv;charset=utf8,\ufeff${encodeURIComponent(data)}`;
// const blob = new Blob([`\ufeff${data}`], { type: "text/csv;charset=utf8" });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fn;
a.click();
a.remove();
URL.revokeObjectURL(url);
}
function fmt1(d1) {
const dt1 = new Date(d1);
return `${MONTHS[dt1.getMonth()]} ${dt1.getDate()},${dt1.getFullYear()}`;
}
function fmt2(d1) {
const dt1 = new Date(d1);
const date = dt1.getDate().toString().padStart(2, '0');
return `${MONTHS[dt1.getMonth()]} ${date}, ${dt1.getFullYear()}`;
}
function fmt3(d1) {
d1 = d1.replaceAll('-', '');
return d1.slice(4) + d1.slice(0, 4);
}
function ffmt1(d1, d2) {
const dt1 = new Date(d1);
const dt2 = new Date(d2);
return `${dt1.getMonth() + 1}${dt1.getDate()}-${dt2.getDate()}`;
}
const catchError = (reason) => {
console.error(reason);
};
const headers = Object.freeze({
"wm_aurora.market": "US",
});
const DELAY = 2000;
class Task {
constructor(names, params, options) {
this.names = names;
this.params = params;
this.options = options || {};
}
send() {
if (!(this.uri && this.scid)) {
console.warn(`未设置 ${this}`);
return;
}
const url = new URL(`${this.uri}${this.names.apiName || ''}`);
const method = this.options.method || 'GET';
const callback = this.options.callback;
const desc = this.names.desc || this.desc;
let ext = this.options.ext || 'csv';
let body = this.options.body;
body = (body && JSON.stringify(body)) || null;
url.search = new URLSearchParams(this.params).toString();
console.log(`${method} ${url}`);
fetch(url, {
body,
method,
headers: this.options.headers || headers,
...(this.options.requestInit || {})
}).then(async (response) => {
console.log(response);
if (callback) {
return callback.call(this, response);
}
if (ext == 'xlsx') {
const json = await response.json();
const csv = JSON2CSV(json);
ext = 'csv';
return new Blob([csv], { type: "text/csv" });
} else {
return response.blob();
}
}).then((blob) => {
const fn = `${this.scid} ${this.names.showName.toLowerCase()} ${desc}.${ext}`;
console.log(`下载/上传: ${fn}`);
// uploadFile(fn, data);
downloadFile(fn, blob);
}).catch(catchError);
}
}
const inventoryHealthCallback = async function (response) {
let data = await response.json();
const url = data.downloadUrl;
if (!url) {
throw new Error(`downloadUrl 获取失败: ${JSON.stringify(data)}`);
}
console.log(`GET ${url}`);
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
return response.blob();
};
const campaignsCallback = async function (response) {
let data = await response.json();
const waitDone = async () => {
try {
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
data = await response.json();
const item = data.response.filter((item) => item.download.endsWith(snapshotId))[0];
if (!item) {
throw new Error(JSON.stringify(data));
}
await sleep(DELAY);
const sf = item.requestedOn.replace(/[\s:-]/g, '_');
url = `${item.download}?fileName=${this.names.fileName}_${sf}.csv&lang=en-US`;
console.log(`GET ${url}`);
response = await fetch(url, { headers } );
console.log(response);
return response.blob();
} catch (e) {
console.log(`重试请求, ${e}`);
return waitDone();
}
};
const snapshotId = data.response && data.response.snapshotId;
if (!snapshotId) {
throw new Error(`snapshotId 获取失败: ${JSON.stringify(data)}`);
}
console.log(`snapshotId: ${snapshotId}`);
let url = new URL(`${this.uri}${this.names.apiName}`);
const advertiserId = this.options.body.advertiserId;
url.search = new URLSearchParams({
request: `/v1/snapshot/report?advertiserId=${advertiserId}&jobStatus=pending,processing,done`
}).toString();
console.log(`GET ${url}`);
return waitDone();
};
const reportRequestsCallback = async function (response) {
let data = await response.json();
const requestId = data.requestId;
if (!requestId) {
throw new Error(`requestId 获取失败: ${JSON.stringify(data)}`);
}
console.log(`requestId: ${requestId}`);
let url = new URL(`${this.uri}${this.names.apiName2}`);
url.search = new URLSearchParams({
"reportType": "ORDER",
"reportVersion": "v1",
requestId
}).toString();
console.log(`GET ${url}`);
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
return response.blob();
};
const getPeriods = async () => {
const url = "https://seller.walmart.com/aurora/v1/auroraPaymentsSettlementService/payment/settlementPeriods";
console.log(`GET ${url}`);
let response = await fetch(url, { headers });
let data = await response.json();
response = data.payload && data.payload.response;
if (!response) {
throw new Error(`response 获取失败: ${JSON.stringify(data)}`);
}
const periods = response.slice(1, 4);
console.log(`periods: ${JSON.stringify(periods)}`);
return periods;
};
const createCampaignsTask = function (showName, bodyArgs, request) {
return new Task({
apiName: "campaigns",
showName: "广告表",
fileName: showName.replaceAll(' ', '')
}, {
request: request || "/v1/snapshot/report"
}, {
method: 'POST',
body: {
"advertiserId": localStorage.getItem("advertiserId"),
"format": "gzip",
...bodyArgs,
},
callback: campaignsCallback
});
};
const FormURI = {
Payment: "https://seller.walmart.com/aurora/v1/report/reconreport/",
Order: "https://seller.walmart.com/aurora/v1/reports/",
Advertiser: "https://advertising.walmart.com/sp/api/",
};
async function createTasks(formURI, fromDate, toDate) {
const uri = new URL(formURI);
if (location.host != uri.host) {
console.warn("不支持当前网站");
return [];
}
Task.prototype.uri = uri;
Task.prototype.scid = getValue('scid');
if (fromDate && toDate) {
Task.prototype.desc = ffmt1(fromDate, toDate);
}
switch (formURI) {
case FormURI.Payment:
const periods = await getPeriods();
await sleep(DELAY);
let tasks = [];
for (let period of periods) {
fromDate = period.payPeriodFromDate;
toDate = period.payPeriodToDate;
const desc = `${fmt1(fromDate)}-${fmt1(toDate)}`;
const reportDate = fmt3(period.settleDate);
tasks = tasks.concat([
new Task({
apiName: "v1/reconFile",
showName: "Payments new report",
desc
}, {
reportDate
}, {
ext: "zip"
}),
new Task({
apiName: "reconFile",
showName: "Payments legacy report",
desc
}, {
reportDate
}, {
ext: "zip"
})
]);
}
return tasks;
case FormURI.Order:
return [
new Task({
apiName: "reportRequests",
apiName2: "downloadReport",
showName: "订单表"
}, {
"reportType": "ORDER",
"reportVersion": "v1",
"isDSV": "false",
"bizRole": "MP"
}, {
method: 'POST',
body: {
"rowFilters": [
{
"type": "enumFilter",
"values": [
"All"
],
"columnName": "orderGroups"
},
{
"type": "enumFilter",
"values": [
"Seller"
],
"columnName": "fulfilmentTypes"
},
{
"type": "rangeFilter",
"columnName": "orderDate",
"from": fromDate,
"to": toDate
}
],
"additionalInfo": {
"reportSummary": `Seller Fulfilled - ${fmt2(fromDate)} to ${fmt2(toDate)}`
},
"excludeColumns": [
"Order Type",
"Service PO#",
"Service Status",
"Total Service Cost",
"Service Scheduled Date"
]
},
callback: reportRequestsCallback,
requestInit: {
serviceName: "get_orders_asyncInitiateDownload"
}
}),
];
case FormURI.Advertiser:
Task.prototype.scid = getValue(
"partner_id",
decodeURIComponent(getValue('adtech-w-dest')),
"&"
);
return [
createCampaignsTask("Keyword Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "keyword",
"attributionWindow": "days3",
"extraFields": ["noDate"],
}),
createCampaignsTask("Item Keyword Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "itemKeyword",
"attributionWindow": "days3",
"extraFields": ["noDate"],
}),
createCampaignsTask("Item Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "adItem",
"attributionWindow": "days3",
"extraFields": ["noDate", "splitAttribution"],
}),
createCampaignsTask("Keyword Recommendations", {
"recommendationType":"keywordRecommendations"
}, "/v1/snapshot/recommendations"),
];
default:
return [
new Task({
apiName: "salesReport",
showName: "Orders"
}, {
fromDate,
toDate,
}, { ext: 'xlsx' }),
new Task({
apiName: "salesReport",
showName: "Orders",
sheetName: "Multchannel_Fulfillment_Details"
}, {
fromDate,
toDate,
type: "MCS"
}, { ext: 'xlsx' }),
new Task({
apiName: "returnsReport",
showName: "Customer returns"
}, {
fromDate: `${fromDate}T00:00:00`,
toDate: `${toDate}T23:59:59`,
}),
new Task({
apiName: "inventoryReconciliation",
showName: "Inventory reconciliation"
}, {
fromDate,
toDate,
}),
new Task({
apiName: "feeDetailReport",
showName: "Settlement"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "storageFeeReport",
showName: "Storage"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "inboundTransportationReport",
showName: "Inbound transportation"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "inventoryHealthReport",
showName: "Inventory health"
}, {}, { callback: inventoryHealthCallback }),
];
}
}
async function run(sn) {
let tasks = await createTasks(FormURI.Order, "2025-02-01", "2025-02-10");
tasks = tasks;
let moment = 0;
tasks.forEach((task) => {
sleep(moment).then(() => task.send());
moment += 2000;
});
}
{
run();
}
})();
!function(){"use strict";const e=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function t(e){return new Promise((t=>setTimeout(t,e)))}function o(e,t,o=";"){const r=(t||document.cookie).split(o);for(let t of r)if(t=t.trim(),t.startsWith(e)){let o=t.substring(e.length+1,t.length);return console.log(`已找到${e}: ${o}`),o}return console.warn(`未找到${e}`),""}function r(t){const o=new Date(t);return`${e[o.getMonth()]} ${o.getDate()},${o.getFullYear()}`}function a(t){const o=new Date(t),r=o.getDate().toString().padStart(2,"0");return`${e[o.getMonth()]} ${r}, ${o.getFullYear()}`}const n=e=>{console.error(e)},s=Object.freeze({"wm_aurora.market":"US"}),i=2e3;class c{constructor(e,t,o){this.names=e,this.params=t,this.options=o||{}}send(){if(!this.uri||!this.scid)return void console.warn(`未设置 ${this}`);const e=new URL(`${this.uri}${this.names.apiName||""}`),t=this.options.method||"GET",o=this.options.callback,r=this.names.desc||this.desc;let a=this.options.ext||"csv",i=this.options.body;i=i&&JSON.stringify(i)||null,e.search=new URLSearchParams(this.params).toString(),console.log(`${t} ${e}`),fetch(e,{body:i,method:t,headers:this.options.headers||s,...this.options.requestInit||{}}).then((async e=>{if(console.log(e),o)return o.call(this,e);if("xlsx"==a){const t=function(e){const t=Object.keys(e[0]);let o=t.join(",")+"\r\n";return e.forEach((e=>{const r=t.map((t=>{return null==(o=e[t])?"":"string"==typeof o?`"${o.replace(/"/g,'""')}"`:o;var o}));o+=r.join(",")+"\r\n"})),o}(await e.json());return a="csv",new Blob([t],{type:"text/csv"})}return e.blob()})).then((e=>{const t=`${this.scid} ${this.names.showName.toLowerCase()} ${r}.${a}`;console.log(`下载/上传: ${t}`),function(e,t){const o=URL.createObjectURL(t),r=document.createElement("a");r.href=o,r.download=e,r.click(),r.remove(),URL.revokeObjectURL(o)}(t,e)})).catch(n)}}const l=async function(e){let o=await e.json();const r=o.downloadUrl;if(!r)throw new Error(`downloadUrl 获取失败: ${JSON.stringify(o)}`);return console.log(`GET ${r}`),await t(i),e=await fetch(r,{headers:s}),console.log(e),e.blob()},p=async function(e){let o=await e.json();const r=async()=>{try{await t(i),e=await fetch(n,{headers:s}),console.log(e),o=await e.json();const r=o.response.filter((e=>e.download.endsWith(a)))[0];if(!r)throw new Error(JSON.stringify(o));await t(i);const c=r.requestedOn.replace(/[\s:-]/g,"_");return n=`${r.download}?fileName=${this.names.fileName}_${c}.csv&lang=en-US`,console.log(`GET ${n}`),e=await fetch(n,{headers:s}),console.log(e),e.blob()}catch(e){return console.log(`重试请求, ${e}`),r()}},a=o.response&&o.response.snapshotId;if(!a)throw new Error(`snapshotId 获取失败: ${JSON.stringify(o)}`);console.log(`snapshotId: ${a}`);let n=new URL(`${this.uri}${this.names.apiName}`);const c=this.options.body.advertiserId;return n.search=new URLSearchParams({request:`/v1/snapshot/report?advertiserId=${c}&jobStatus=pending,processing,done`}).toString(),console.log(`GET ${n}`),r()},d=async function(e){let o=await e.json();const r=o.requestId;if(!r)throw new Error(`requestId 获取失败: ${JSON.stringify(o)}`);console.log(`requestId: ${r}`);let a=new URL(`${this.uri}${this.names.apiName2}`);return a.search=new URLSearchParams({reportType:"ORDER",reportVersion:"v1",requestId:r}).toString(),console.log(`GET ${a}`),await t(i),e=await fetch(a,{headers:s}),console.log(e),e.blob()},m=function(e,t,o){return new c({apiName:"campaigns",showName:"广告表",fileName:e.replaceAll(" ","")},{request:o||"/v1/snapshot/report"},{method:"POST",body:{advertiserId:localStorage.getItem("advertiserId"),format:"gzip",...t},callback:p})},h="https://seller.walmart.com/aurora/v1/report/reconreport/",u="https://seller.walmart.com/aurora/v1/reports/",w="https://advertising.walmart.com/sp/api/";async function f(e,n,p){const f=new URL(e);if(location.host!=f.host)return console.warn("不支持当前网站"),[];switch(c.prototype.uri=f,c.prototype.scid=o("scid"),n&&p&&(c.prototype.desc=function(e,t){const o=new Date(e),r=new Date(t);return`${o.getMonth()+1}${o.getDate()}-${r.getDate()}`}(n,p)),e){case h:const e=await(async()=>{const e="https://seller.walmart.com/aurora/v1/auroraPaymentsSettlementService/payment/settlementPeriods";console.log(`GET ${e}`);let t=await fetch(e,{headers:s}),o=await t.json();if(t=o.payload&&o.payload.response,!t)throw new Error(`response 获取失败: ${JSON.stringify(o)}`);const r=t.slice(1,4);return console.log(`periods: ${JSON.stringify(r)}`),r})();await t(i);let f=[];for(let t of e){n=t.payPeriodFromDate,p=t.payPeriodToDate;const e=`${r(n)}-${r(p)}`,o=(y=(y=t.settleDate).replaceAll("-","")).slice(4)+y.slice(0,4);f=f.concat([new c({apiName:"v1/reconFile",showName:"Payments new report",desc:e},{reportDate:o},{ext:"zip"}),new c({apiName:"reconFile",showName:"Payments legacy report",desc:e},{reportDate:o},{ext:"zip"})])}return f;case u:return[new c({apiName:"reportRequests",apiName2:"downloadReport",showName:"订单表"},{reportType:"ORDER",reportVersion:"v1",isDSV:"false",bizRole:"MP"},{method:"POST",body:{rowFilters:[{type:"enumFilter",values:["All"],columnName:"orderGroups"},{type:"enumFilter",values:["Seller"],columnName:"fulfilmentTypes"},{type:"rangeFilter",columnName:"orderDate",from:n,to:p}],additionalInfo:{reportSummary:`Seller Fulfilled - ${a(n)} to ${a(p)}`},excludeColumns:["Order Type","Service PO#","Service Status","Total Service Cost","Service Scheduled Date"]},callback:d,requestInit:{serviceName:"get_orders_asyncInitiateDownload"}})];case w:return c.prototype.scid=o("partner_id",decodeURIComponent(o("adtech-w-dest")),"&"),[m("Keyword Performance",{startDate:n,endDate:p,reportType:"keyword",attributionWindow:"days3",extraFields:["noDate"]}),m("Item Keyword Performance",{startDate:n,endDate:p,reportType:"itemKeyword",attributionWindow:"days3",extraFields:["noDate"]}),m("Item Performance",{startDate:n,endDate:p,reportType:"adItem",attributionWindow:"days3",extraFields:["noDate","splitAttribution"]}),m("Keyword Recommendations",{recommendationType:"keywordRecommendations"},"/v1/snapshot/recommendations")];default:return[new c({apiName:"salesReport",showName:"Orders"},{fromDate:n,toDate:p},{ext:"xlsx"}),new c({apiName:"salesReport",showName:"Orders",sheetName:"Multchannel_Fulfillment_Details"},{fromDate:n,toDate:p,type:"MCS"},{ext:"xlsx"}),new c({apiName:"returnsReport",showName:"Customer returns"},{fromDate:`${n}T00:00:00`,toDate:`${p}T23:59:59`}),new c({apiName:"inventoryReconciliation",showName:"Inventory reconciliation"},{fromDate:n,toDate:p}),new c({apiName:"feeDetailReport",showName:"Settlement"},{startDate:n,endDate:p}),new c({apiName:"storageFeeReport",showName:"Storage"},{startDate:n,endDate:p}),new c({apiName:"inboundTransportationReport",showName:"Inbound transportation"},{startDate:n,endDate:p}),new c({apiName:"inventoryHealthReport",showName:"Inventory health"},{},{callback:l})]}var y}!async function(){let e=await f(u,"2025-02-01","2025-02-10"),o=0;e.forEach((e=>{t(o).then((()=>e.send())),o+=2e3}))}()}();
//# sourceMappingURL=app.min.js.map
{"version":3,"file":"app.min.js","sources":["../src/util.js","../src/base.js","../src/main.js"],"sourcesContent":["const MONTHS = [\r\n \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \r\n \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"\r\n];\r\nexport function sleep(ms) {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\nexport function getValue(key, str, sep = ';') {\r\n const items = (str || document.cookie).split(sep);\r\n for (let item of items) {\r\n item = item.trim();\r\n if (item.startsWith(key)) {\r\n let value = item.substring(key.length + 1, item.length);\r\n console.log(`已找到${key}: ${value}`);\r\n return value;\r\n }\r\n }\r\n console.warn(`未找到${key}`);\r\n return '';\r\n}\r\nconst replacer = (value) => {\r\n if (value === null || value === undefined) {\r\n return '';\r\n }\r\n if (typeof value == 'string') {\r\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\r\n }\r\n return value;\r\n}\r\nexport function JSON2CSV(json) {\r\n const cols = Object.keys(json[0]);\r\n let csv = cols.join(',') + '\\r\\n';\r\n json.forEach(row => {\r\n const values = cols.map(col => replacer(row[col]));\r\n csv += values.join(',') + '\\r\\n';\r\n });\r\n return csv;\r\n}\r\nexport function downloadFile(fn, blob) {\r\n // const url = `data:text/csv;charset=utf8,\\ufeff${encodeURIComponent(data)}`;\r\n // const blob = new Blob([`\\ufeff${data}`], { type: \"text/csv;charset=utf8\" });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = fn;\r\n a.click();\r\n a.remove();\r\n URL.revokeObjectURL(url);\r\n}\r\nexport function uploadFile(fn, data) {\r\n const url = ''; // ?\r\n fetch(url, {\r\n method: \"POST\",\r\n body: {\r\n data,\r\n fileName: fn\r\n },\r\n })\r\n .then((response) => console.log(`上传成功: ${fn}, ${response}`))\r\n .catch((reason) => console.error(`上传失败: ${fn}, by ${reason}`));\r\n}\r\nexport function fmt1(d1) {\r\n const dt1 = new Date(d1);\r\n return `${MONTHS[dt1.getMonth()]} ${dt1.getDate()},${dt1.getFullYear()}`;\r\n}\r\nexport function fmt2(d1) {\r\n const dt1 = new Date(d1);\r\n const date = dt1.getDate().toString().padStart(2, '0');\r\n return `${MONTHS[dt1.getMonth()]} ${date}, ${dt1.getFullYear()}`;\r\n}\r\nexport function fmt3(d1) {\r\n d1 = d1.replaceAll('-', '');\r\n return d1.slice(4) + d1.slice(0, 4);\r\n}\r\nexport function ffmt1(d1, d2) {\r\n const dt1 = new Date(d1);\r\n const dt2 = new Date(d2);\r\n return `${dt1.getMonth() + 1}月${dt1.getDate()}-${dt2.getDate()}`;\r\n}\r\n","import { sleep, getValue, ffmt1, fmt2, fmt3, fmt1, uploadFile, JSON2CSV, downloadFile } from \"./util.js\";\r\n\r\nconst catchError = (reason) => {\r\n console.error(reason);\r\n}\r\nconst headers = Object.freeze({\r\n \"wm_aurora.market\": \"US\",\r\n});\r\nconst DELAY = 2000;\r\nclass Task {\r\n constructor(names, params, options) {\r\n this.names = names;\r\n this.params = params;\r\n this.options = options || {};\r\n }\r\n send() {\r\n if (!(this.uri && this.scid)) {\r\n console.warn(`未设置 ${this}`);\r\n return;\r\n }\r\n const url = new URL(`${this.uri}${this.names.apiName || ''}`);\r\n const method = this.options.method || 'GET';\r\n const callback = this.options.callback;\r\n const desc = this.names.desc || this.desc;\r\n let ext = this.options.ext || 'csv';\r\n let body = this.options.body;\r\n body = (body && JSON.stringify(body)) || null;\r\n\r\n url.search = new URLSearchParams(this.params).toString();\r\n console.log(`${method} ${url}`);\r\n fetch(url, {\r\n body,\r\n method,\r\n headers: this.options.headers || headers,\r\n ...(this.options.requestInit || {})\r\n }).then(async (response) => {\r\n console.log(response);\r\n if (callback) {\r\n return callback.call(this, response);\r\n }\r\n if (ext == 'xlsx') {\r\n const json = await response.json();\r\n const csv = JSON2CSV(json);\r\n ext = 'csv';\r\n return new Blob([csv], { type: \"text/csv\" });\r\n } else {\r\n return response.blob();\r\n }\r\n }).then((blob) => {\r\n const fn = `${this.scid} ${this.names.showName.toLowerCase()} ${desc}.${ext}`;\r\n console.log(`下载/上传: ${fn}`);\r\n // uploadFile(fn, data);\r\n downloadFile(fn, blob);\r\n }).catch(catchError);\r\n }\r\n}\r\nconst inventoryHealthCallback = async function (response) {\r\n let data = await response.json();\r\n const url = data.downloadUrl;\r\n if (!url) {\r\n throw new Error(`downloadUrl 获取失败: ${JSON.stringify(data)}`);\r\n }\r\n console.log(`GET ${url}`);\r\n await sleep(DELAY);\r\n response = await fetch(url, { headers });\r\n console.log(response);\r\n return response.blob();\r\n}\r\nconst campaignsCallback = async function (response) {\r\n let data = await response.json();\r\n const waitDone = async () => {\r\n try {\r\n await sleep(DELAY);\r\n response = await fetch(url, { headers });\r\n console.log(response);\r\n data = await response.json();\r\n const item = data.response.filter((item) => item.download.endsWith(snapshotId))[0];\r\n if (!item) {\r\n throw new Error(JSON.stringify(data));\r\n }\r\n\r\n await sleep(DELAY);\r\n const sf = item.requestedOn.replace(/[\\s:-]/g, '_');\r\n url = `${item.download}?fileName=${this.names.fileName}_${sf}.csv&lang=en-US`;\r\n console.log(`GET ${url}`);\r\n response = await fetch(url, { headers } );\r\n console.log(response);\r\n return response.blob();\r\n } catch (e) {\r\n console.log(`重试请求, ${e}`);\r\n return waitDone();\r\n }\r\n }\r\n const snapshotId = data.response && data.response.snapshotId;\r\n if (!snapshotId) {\r\n throw new Error(`snapshotId 获取失败: ${JSON.stringify(data)}`);\r\n }\r\n console.log(`snapshotId: ${snapshotId}`);\r\n let url = new URL(`${this.uri}${this.names.apiName}`);\r\n const advertiserId = this.options.body.advertiserId;\r\n url.search = new URLSearchParams({\r\n request: `/v1/snapshot/report?advertiserId=${advertiserId}&jobStatus=pending,processing,done`\r\n }).toString();\r\n console.log(`GET ${url}`);\r\n return waitDone();\r\n}\r\nconst reportRequestsCallback = async function (response) {\r\n let data = await response.json();\r\n const requestId = data.requestId;\r\n if (!requestId) {\r\n throw new Error(`requestId 获取失败: ${JSON.stringify(data)}`);\r\n }\r\n console.log(`requestId: ${requestId}`);\r\n let url = new URL(`${this.uri}${this.names.apiName2}`);\r\n url.search = new URLSearchParams({\r\n \"reportType\": \"ORDER\",\r\n \"reportVersion\": \"v1\",\r\n requestId\r\n }).toString();\r\n console.log(`GET ${url}`);\r\n await sleep(DELAY);\r\n response = await fetch(url, { headers });\r\n console.log(response);\r\n return response.blob();\r\n}\r\nconst getPeriods = async () => {\r\n const url = \"https://seller.walmart.com/aurora/v1/auroraPaymentsSettlementService/payment/settlementPeriods\";\r\n console.log(`GET ${url}`);\r\n let response = await fetch(url, { headers });\r\n let data = await response.json();\r\n response = data.payload && data.payload.response;\r\n if (!response) {\r\n throw new Error(`response 获取失败: ${JSON.stringify(data)}`);\r\n }\r\n const periods = response.slice(1, 4);\r\n console.log(`periods: ${JSON.stringify(periods)}`);\r\n return periods;\r\n}\r\nconst createCampaignsTask = function (showName, bodyArgs, request) {\r\n return new Task({\r\n apiName: \"campaigns\",\r\n showName: \"广告表\",\r\n fileName: showName.replaceAll(' ', '')\r\n }, {\r\n request: request || \"/v1/snapshot/report\"\r\n }, {\r\n method: 'POST',\r\n body: {\r\n \"advertiserId\": localStorage.getItem(\"advertiserId\"),\r\n \"format\": \"gzip\",\r\n ...bodyArgs,\r\n },\r\n callback: campaignsCallback\r\n });\r\n}\r\nexport const FormURI = {\r\n WFS: \"https://seller.walmart.com/aurora/v1/wfs/reports/\",\r\n Payment: \"https://seller.walmart.com/aurora/v1/report/reconreport/\",\r\n Order: \"https://seller.walmart.com/aurora/v1/reports/\",\r\n Advertiser: \"https://advertising.walmart.com/sp/api/\",\r\n}\r\nexport async function createTasks(formURI, fromDate, toDate) {\r\n const uri = new URL(formURI || FormURI.WFS);\r\n if (location.host != uri.host) {\r\n console.warn(\"不支持当前网站\");\r\n return [];\r\n }\r\n Task.prototype.uri = uri;\r\n Task.prototype.scid = getValue('scid');\r\n if (fromDate && toDate) {\r\n Task.prototype.desc = ffmt1(fromDate, toDate);\r\n }\r\n switch (formURI) {\r\n case FormURI.Payment:\r\n const periods = await getPeriods();\r\n await sleep(DELAY);\r\n let tasks = [];\r\n for (let period of periods) {\r\n fromDate = period.payPeriodFromDate;\r\n toDate = period.payPeriodToDate;\r\n const desc = `${fmt1(fromDate)}-${fmt1(toDate)}`;\r\n const reportDate = fmt3(period.settleDate);\r\n tasks = tasks.concat([\r\n new Task({\r\n apiName: \"v1/reconFile\",\r\n showName: \"Payments new report\",\r\n desc\r\n }, {\r\n reportDate\r\n }, {\r\n ext: \"zip\"\r\n }),\r\n new Task({\r\n apiName: \"reconFile\",\r\n showName: \"Payments legacy report\",\r\n desc\r\n }, {\r\n reportDate\r\n }, {\r\n ext: \"zip\"\r\n })\r\n ]);\r\n }\r\n return tasks;\r\n case FormURI.Order:\r\n return [\r\n new Task({\r\n apiName: \"reportRequests\",\r\n apiName2: \"downloadReport\",\r\n showName: \"订单表\"\r\n }, {\r\n \"reportType\": \"ORDER\",\r\n \"reportVersion\": \"v1\",\r\n \"isDSV\": \"false\",\r\n \"bizRole\": \"MP\"\r\n }, {\r\n method: 'POST',\r\n body: {\r\n \"rowFilters\": [\r\n {\r\n \"type\": \"enumFilter\",\r\n \"values\": [\r\n \"All\"\r\n ],\r\n \"columnName\": \"orderGroups\"\r\n },\r\n {\r\n \"type\": \"enumFilter\",\r\n \"values\": [\r\n \"Seller\"\r\n ],\r\n \"columnName\": \"fulfilmentTypes\"\r\n },\r\n {\r\n \"type\": \"rangeFilter\",\r\n \"columnName\": \"orderDate\",\r\n \"from\": fromDate,\r\n \"to\": toDate\r\n }\r\n ],\r\n \"additionalInfo\": {\r\n \"reportSummary\": `Seller Fulfilled - ${fmt2(fromDate)} to ${fmt2(toDate)}`\r\n },\r\n \"excludeColumns\": [\r\n \"Order Type\",\r\n \"Service PO#\",\r\n \"Service Status\",\r\n \"Total Service Cost\",\r\n \"Service Scheduled Date\"\r\n ]\r\n },\r\n callback: reportRequestsCallback,\r\n requestInit: {\r\n serviceName: \"get_orders_asyncInitiateDownload\"\r\n }\r\n }),\r\n ];\r\n case FormURI.Advertiser:\r\n Task.prototype.scid = getValue(\r\n \"partner_id\",\r\n decodeURIComponent(getValue('adtech-w-dest')),\r\n \"&\"\r\n );\r\n return [\r\n createCampaignsTask(\"Keyword Performance\", {\r\n \"startDate\": fromDate,\r\n \"endDate\": toDate,\r\n \"reportType\": \"keyword\",\r\n \"attributionWindow\": \"days3\",\r\n \"extraFields\": [\"noDate\"],\r\n }),\r\n createCampaignsTask(\"Item Keyword Performance\", {\r\n \"startDate\": fromDate,\r\n \"endDate\": toDate,\r\n \"reportType\": \"itemKeyword\",\r\n \"attributionWindow\": \"days3\",\r\n \"extraFields\": [\"noDate\"],\r\n }),\r\n createCampaignsTask(\"Item Performance\", {\r\n \"startDate\": fromDate,\r\n \"endDate\": toDate,\r\n \"reportType\": \"adItem\",\r\n \"attributionWindow\": \"days3\",\r\n \"extraFields\": [\"noDate\", \"splitAttribution\"],\r\n }),\r\n createCampaignsTask(\"Keyword Recommendations\", {\r\n \"recommendationType\":\"keywordRecommendations\"\r\n }, \"/v1/snapshot/recommendations\"),\r\n ];\r\n default:\r\n return [\r\n new Task({\r\n apiName: \"salesReport\",\r\n showName: \"Orders\"\r\n }, {\r\n fromDate,\r\n toDate,\r\n }, { ext: 'xlsx' }),\r\n new Task({\r\n apiName: \"salesReport\",\r\n showName: \"Orders\",\r\n sheetName: \"Multchannel_Fulfillment_Details\"\r\n }, {\r\n fromDate,\r\n toDate,\r\n type: \"MCS\"\r\n }, { ext: 'xlsx' }),\r\n \r\n new Task({\r\n apiName: \"returnsReport\",\r\n showName: \"Customer returns\"\r\n }, {\r\n fromDate: `${fromDate}T00:00:00`,\r\n toDate: `${toDate}T23:59:59`,\r\n }),\r\n new Task({\r\n apiName: \"inventoryReconciliation\",\r\n showName: \"Inventory reconciliation\"\r\n }, {\r\n fromDate,\r\n toDate,\r\n }),\r\n new Task({\r\n apiName: \"feeDetailReport\",\r\n showName: \"Settlement\"\r\n }, {\r\n startDate: fromDate,\r\n endDate: toDate,\r\n }),\r\n new Task({\r\n apiName: \"storageFeeReport\",\r\n showName: \"Storage\"\r\n }, {\r\n startDate: fromDate,\r\n endDate: toDate,\r\n }),\r\n new Task({\r\n apiName: \"inboundTransportationReport\",\r\n showName: \"Inbound transportation\"\r\n }, {\r\n startDate: fromDate,\r\n endDate: toDate,\r\n }),\r\n \r\n new Task({\r\n apiName: \"inventoryHealthReport\",\r\n showName: \"Inventory health\"\r\n }, {}, { callback: inventoryHealthCallback }),\r\n ];\r\n }\r\n}\r\n","import { sleep } from \"./util.js\";\r\nimport { createTasks, FormURI } from \"./base.js\";\r\n\r\nconst TEST_FLAG = true;\r\n\r\nasync function run(sn) {\r\n let tasks = await createTasks(FormURI.Order, \"2025-02-01\", \"2025-02-10\");\r\n tasks = typeof sn == 'number' ? [tasks.at(sn)] : tasks;\r\n let moment = 0;\r\n tasks.forEach((task) => {\r\n sleep(moment).then(() => task.send());\r\n moment += 2000;\r\n });\r\n}\r\nif (TEST_FLAG) {\r\n run();\r\n}\r\n"],"names":["MONTHS","sleep","ms","Promise","resolve","setTimeout","getValue","key","str","sep","items","document","cookie","split","item","trim","startsWith","value","substring","length","console","log","warn","fmt1","d1","dt1","Date","getMonth","getDate","getFullYear","fmt2","date","toString","padStart","catchError","reason","error","headers","Object","freeze","DELAY","Task","constructor","names","params","options","this","send","uri","scid","url","URL","apiName","method","callback","desc","ext","body","JSON","stringify","search","URLSearchParams","fetch","requestInit","then","async","response","call","csv","json","cols","keys","join","forEach","row","values","map","col","replacer","replace","JSON2CSV","Blob","type","blob","fn","showName","toLowerCase","createObjectURL","a","createElement","href","download","click","remove","revokeObjectURL","downloadFile","catch","inventoryHealthCallback","data","downloadUrl","Error","campaignsCallback","waitDone","filter","endsWith","snapshotId","sf","requestedOn","fileName","e","advertiserId","request","reportRequestsCallback","requestId","apiName2","reportType","reportVersion","createCampaignsTask","bodyArgs","replaceAll","localStorage","getItem","format","FormURI","createTasks","formURI","fromDate","toDate","location","host","prototype","d2","dt2","ffmt1","periods","payload","slice","getPeriods","tasks","period","payPeriodFromDate","payPeriodToDate","reportDate","settleDate","concat","isDSV","bizRole","rowFilters","columnName","from","to","additionalInfo","reportSummary","excludeColumns","serviceName","decodeURIComponent","startDate","endDate","attributionWindow","extraFields","recommendationType","sheetName","moment","task","run"],"mappings":"yBAAA,MAAMA,EAAS,CACX,MAAO,MAAO,MAAO,MAAO,MAAO,MACnC,MAAO,MAAO,MAAO,MAAO,MAAO,OAEhC,SAASC,EAAMC,GAClB,OAAO,IAAIC,SAASC,GAAYC,WAAWD,EAASF,IACxD,CACO,SAASI,EAASC,EAAKC,EAAKC,EAAM,KACrC,MAAMC,GAASF,GAAOG,SAASC,QAAQC,MAAMJ,GAC7C,IAAK,IAAIK,KAAQJ,EAEb,GADAI,EAAOA,EAAKC,OACRD,EAAKE,WAAWT,GAAM,CACtB,IAAIU,EAAQH,EAAKI,UAAUX,EAAIY,OAAS,EAAGL,EAAKK,QAEhD,OADAC,QAAQC,IAAI,MAAMd,MAAQU,KACnBA,CACX,CAGJ,OADAG,QAAQE,KAAK,MAAMf,KACZ,EACX,CA0CO,SAASgB,EAAKC,GACjB,MAAMC,EAAM,IAAIC,KAAKF,GACrB,MAAO,GAAGxB,EAAOyB,EAAIE,eAAeF,EAAIG,aAAaH,EAAII,eAC7D,CACO,SAASC,EAAKN,GACjB,MAAMC,EAAM,IAAIC,KAAKF,GACfO,EAAON,EAAIG,UAAUI,WAAWC,SAAS,EAAG,KAClD,MAAO,GAAGjC,EAAOyB,EAAIE,eAAeI,MAASN,EAAII,eACrD,CCnEA,MAAMK,EAAcC,IAChBf,QAAQgB,MAAMD,EAAO,EAEnBE,EAAUC,OAAOC,OAAO,CAC1B,mBAAoB,OAElBC,EAAQ,IACd,MAAMC,EACFC,WAAAA,CAAYC,EAAOC,EAAQC,GACvBC,KAAKH,MAAQA,EACbG,KAAKF,OAASA,EACdE,KAAKD,QAAUA,GAAW,CAAE,CAChC,CACAE,IAAAA,GACI,IAAMD,KAAKE,MAAOF,KAAKG,KAEnB,YADA7B,QAAQE,KAAK,OAAOwB,QAGxB,MAAMI,EAAM,IAAIC,IAAI,GAAGL,KAAKE,MAAMF,KAAKH,MAAMS,SAAW,MAClDC,EAASP,KAAKD,QAAQQ,QAAU,MAChCC,EAAWR,KAAKD,QAAQS,SACxBC,EAAOT,KAAKH,MAAMY,MAAQT,KAAKS,KACrC,IAAIC,EAAMV,KAAKD,QAAQW,KAAO,MAC1BC,EAAOX,KAAKD,QAAQY,KACxBA,EAAQA,GAAQC,KAAKC,UAAUF,IAAU,KAEzCP,EAAIU,OAAS,IAAIC,gBAAgBf,KAAKF,QAAQZ,WAC9CZ,QAAQC,IAAI,GAAGgC,KAAUH,KACzBY,MAAMZ,EAAK,CACPO,OACAJ,SACAhB,QAASS,KAAKD,QAAQR,SAAWA,KAC7BS,KAAKD,QAAQkB,aAAe,CAAE,IACnCC,MAAKC,UAEJ,GADA7C,QAAQC,IAAI6C,GACRZ,EACA,OAAOA,EAASa,KAAKrB,KAAMoB,GAE/B,GAAW,QAAPV,EAAe,CACf,MACMY,EDbf,SAAkBC,GACrB,MAAMC,EAAOhC,OAAOiC,KAAKF,EAAK,IAC9B,IAAID,EAAME,EAAKE,KAAK,KAAO,OAK3B,OAJAH,EAAKI,SAAQC,IACT,MAAMC,EAASL,EAAKM,KAAIC,IAAOC,OAZ/B7D,OADUA,EAa8ByD,EAAIG,IAXrC,GAES,iBAAT5D,EACA,IAAIA,EAAM8D,QAAQ,KAAM,SAE5B9D,EAPOA,KAauC,IACjDmD,GAAOO,EAAOH,KAAK,KAAO,MAAM,IAE7BJ,CACX,CCK4BY,OADOd,EAASG,QAG5B,OADAb,EAAM,MACC,IAAIyB,KAAK,CAACb,GAAM,CAAEc,KAAM,YACnC,CACI,OAAOhB,EAASiB,MACpB,IACDnB,MAAMmB,IACL,MAAMC,EAAK,GAAGtC,KAAKG,QAAQH,KAAKH,MAAM0C,SAASC,iBAAiB/B,KAAQC,IACxEpC,QAAQC,IAAI,UAAU+D,KDZ3B,SAAsBA,EAAID,GAG7B,MAAMjC,EAAMC,IAAIoC,gBAAgBJ,GAC1BK,EAAI7E,SAAS8E,cAAc,KACjCD,EAAEE,KAAOxC,EACTsC,EAAEG,SAAWP,EACbI,EAAEI,QACFJ,EAAEK,SACF1C,IAAI2C,gBAAgB5C,EACxB,CCIY6C,CAAaX,EAAID,EAAK,IACvBa,MAAM9D,EACb,EAEJ,MAAM+D,EAA0BhC,eAAgBC,GAC5C,IAAIgC,QAAahC,EAASG,OAC1B,MAAMnB,EAAMgD,EAAKC,YACjB,IAAKjD,EACD,MAAM,IAAIkD,MAAM,qBAAqB1C,KAAKC,UAAUuC,MAMxD,OAJA9E,QAAQC,IAAI,OAAO6B,WACbjD,EAAMuC,GACZ0B,QAAiBJ,MAAMZ,EAAK,CAAEb,YAC9BjB,QAAQC,IAAI6C,GACLA,EAASiB,MACpB,EACMkB,EAAoBpC,eAAgBC,GACtC,IAAIgC,QAAahC,EAASG,OAC1B,MAAMiC,EAAWrC,UACb,UACUhE,EAAMuC,GACZ0B,QAAiBJ,MAAMZ,EAAK,CAAEb,YAC9BjB,QAAQC,IAAI6C,GACZgC,QAAahC,EAASG,OACtB,MAAMvD,EAAOoF,EAAKhC,SAASqC,QAAQzF,GAASA,EAAK6E,SAASa,SAASC,KAAa,GAChF,IAAK3F,EACD,MAAM,IAAIsF,MAAM1C,KAAKC,UAAUuC,UAG7BjG,EAAMuC,GACZ,MAAMkE,EAAK5F,EAAK6F,YAAY5B,QAAQ,UAAW,KAK/C,OAJA7B,EAAM,GAAGpC,EAAK6E,qBAAqB7C,KAAKH,MAAMiE,YAAYF,mBAC1DtF,QAAQC,IAAI,OAAO6B,KACnBgB,QAAiBJ,MAAMZ,EAAK,CAAEb,YAC9BjB,QAAQC,IAAI6C,GACLA,EAASiB,MACnB,CAAC,MAAO0B,GAEL,OADAzF,QAAQC,IAAI,SAASwF,KACdP,GACX,GAEEG,EAAaP,EAAKhC,UAAYgC,EAAKhC,SAASuC,WAClD,IAAKA,EACD,MAAM,IAAIL,MAAM,oBAAoB1C,KAAKC,UAAUuC,MAEvD9E,QAAQC,IAAI,eAAeoF,KAC3B,IAAIvD,EAAM,IAAIC,IAAI,GAAGL,KAAKE,MAAMF,KAAKH,MAAMS,WAC3C,MAAM0D,EAAehE,KAAKD,QAAQY,KAAKqD,aAKvC,OAJA5D,EAAIU,OAAS,IAAIC,gBAAgB,CAC7BkD,QAAS,oCAAoCD,wCAC9C9E,WACHZ,QAAQC,IAAI,OAAO6B,KACZoD,GACX,EACMU,EAAyB/C,eAAgBC,GAC3C,IAAIgC,QAAahC,EAASG,OAC1B,MAAM4C,EAAYf,EAAKe,UACvB,IAAKA,EACD,MAAM,IAAIb,MAAM,mBAAmB1C,KAAKC,UAAUuC,MAEtD9E,QAAQC,IAAI,cAAc4F,KAC1B,IAAI/D,EAAM,IAAIC,IAAI,GAAGL,KAAKE,MAAMF,KAAKH,MAAMuE,YAU3C,OATAhE,EAAIU,OAAS,IAAIC,gBAAgB,CAC7BsD,WAAc,QACdC,cAAiB,KACjBH,cACDjF,WACHZ,QAAQC,IAAI,OAAO6B,WACbjD,EAAMuC,GACZ0B,QAAiBJ,MAAMZ,EAAK,CAAEb,YAC9BjB,QAAQC,IAAI6C,GACLA,EAASiB,MACpB,EAcMkC,EAAsB,SAAUhC,EAAUiC,EAAUP,GACtD,OAAO,IAAItE,EAAK,CACZW,QAAS,YACTiC,SAAU,MACVuB,SAAUvB,EAASkC,WAAW,IAAK,KACpC,CACCR,QAASA,GAAW,uBACrB,CACC1D,OAAQ,OACRI,KAAM,CACFqD,aAAgBU,aAAaC,QAAQ,gBACrCC,OAAU,UACPJ,GAEPhE,SAAU+C,GAElB,EACasB,EAEA,2DAFAA,EAGF,gDAHEA,EAIG,0CAET1D,eAAe2D,EAAYC,EAASC,EAAUC,GACjD,MAAM/E,EAAM,IAAIG,IAAI0E,GACpB,GAAIG,SAASC,MAAQjF,EAAIiF,KAErB,OADA7G,QAAQE,KAAK,WACN,GAOX,OALAmB,EAAKyF,UAAUlF,IAAMA,EACrBP,EAAKyF,UAAUjF,KAAO3C,EAAS,QAC3BwH,GAAYC,IACZtF,EAAKyF,UAAU3E,KDhGhB,SAAe/B,EAAI2G,GACtB,MAAM1G,EAAM,IAAIC,KAAKF,GACf4G,EAAM,IAAI1G,KAAKyG,GACrB,MAAO,GAAG1G,EAAIE,WAAa,KAAKF,EAAIG,aAAawG,EAAIxG,WACzD,CC4F8ByG,CAAMP,EAAUC,IAElCF,GACJ,KAAKF,EACD,MAAMW,OAjDCrE,WACf,MAAMf,EAAM,iGACZ9B,QAAQC,IAAI,OAAO6B,KACnB,IAAIgB,QAAiBJ,MAAMZ,EAAK,CAAEb,YAC9B6D,QAAahC,EAASG,OAE1B,GADAH,EAAWgC,EAAKqC,SAAWrC,EAAKqC,QAAQrE,UACnCA,EACD,MAAM,IAAIkC,MAAM,kBAAkB1C,KAAKC,UAAUuC,MAErD,MAAMoC,EAAUpE,EAASsE,MAAM,EAAG,GAElC,OADApH,QAAQC,IAAI,YAAYqC,KAAKC,UAAU2E,MAChCA,CAAO,EAsCgBG,SAChBxI,EAAMuC,GACZ,IAAIkG,EAAQ,GACZ,IAAK,IAAIC,KAAUL,EAAS,CACxBR,EAAWa,EAAOC,kBAClBb,EAASY,EAAOE,gBAChB,MAAMtF,EAAO,GAAGhC,EAAKuG,MAAavG,EAAKwG,KACjCe,GD9GlBtH,GADiBA,EC+GmBmH,EAAOI,YD9GnCxB,WAAW,IAAK,KACdiB,MAAM,GAAKhH,EAAGgH,MAAM,EAAG,GC8GrBE,EAAQA,EAAMM,OAAO,CACjB,IAAIvG,EAAK,CACLW,QAAS,eACTiC,SAAU,sBACV9B,QACD,CACCuF,cACD,CACCtF,IAAK,QAET,IAAIf,EAAK,CACLW,QAAS,YACTiC,SAAU,yBACV9B,QACD,CACCuF,cACD,CACCtF,IAAK,SAGjB,CACA,OAAOkF,EACX,KAAKf,EACD,MAAO,CACH,IAAIlF,EAAK,CACLW,QAAS,iBACT8D,SAAU,iBACV7B,SAAU,OACX,CACC8B,WAAc,QACdC,cAAiB,KACjB6B,MAAS,QACTC,QAAW,MACZ,CACC7F,OAAQ,OACRI,KAAM,CACF0F,WAAc,CACV,CACIjE,KAAQ,aACRP,OAAU,CACN,OAEJyE,WAAc,eAElB,CACIlE,KAAQ,aACRP,OAAU,CACN,UAEJyE,WAAc,mBAElB,CACIlE,KAAQ,cACRkE,WAAc,YACdC,KAAQvB,EACRwB,GAAMvB,IAGdwB,eAAkB,CACdC,cAAiB,sBAAsB1H,EAAKgG,SAAgBhG,EAAKiG,MAErE0B,eAAkB,CACd,aACA,cACA,iBACA,qBACA,2BAGRnG,SAAU0D,EACVjD,YAAa,CACT2F,YAAa,uCAI7B,KAAK/B,EAMD,OALAlF,EAAKyF,UAAUjF,KAAO3C,EAClB,aACAqJ,mBAAmBrJ,EAAS,kBAC5B,KAEG,CACH+G,EAAoB,sBAAuB,CACvCuC,UAAa9B,EACb+B,QAAW9B,EACXZ,WAAc,UACd2C,kBAAqB,QACrBC,YAAe,CAAC,YAEpB1C,EAAoB,2BAA4B,CAC5CuC,UAAa9B,EACb+B,QAAW9B,EACXZ,WAAc,cACd2C,kBAAqB,QACrBC,YAAe,CAAC,YAEpB1C,EAAoB,mBAAoB,CACpCuC,UAAa9B,EACb+B,QAAW9B,EACXZ,WAAc,SACd2C,kBAAqB,QACrBC,YAAe,CAAC,SAAU,sBAE9B1C,EAAoB,0BAA2B,CAC3C2C,mBAAqB,0BACtB,iCAEX,QACI,MAAO,CACH,IAAIvH,EAAK,CACLW,QAAS,cACTiC,SAAU,UACX,CACCyC,WACAC,UACD,CAAEvE,IAAK,SACV,IAAIf,EAAK,CACLW,QAAS,cACTiC,SAAU,SACV4E,UAAW,mCACZ,CACCnC,WACAC,SACA7C,KAAM,OACP,CAAE1B,IAAK,SAEV,IAAIf,EAAK,CACLW,QAAS,gBACTiC,SAAU,oBACX,CACCyC,SAAU,GAAGA,aACbC,OAAQ,GAAGA,eAEf,IAAItF,EAAK,CACLW,QAAS,0BACTiC,SAAU,4BACX,CACCyC,WACAC,WAEJ,IAAItF,EAAK,CACLW,QAAS,kBACTiC,SAAU,cACX,CACCuE,UAAW9B,EACX+B,QAAS9B,IAEb,IAAItF,EAAK,CACLW,QAAS,mBACTiC,SAAU,WACX,CACCuE,UAAW9B,EACX+B,QAAS9B,IAEb,IAAItF,EAAK,CACLW,QAAS,8BACTiC,SAAU,0BACX,CACCuE,UAAW9B,EACX+B,QAAS9B,IAGb,IAAItF,EAAK,CACLW,QAAS,wBACTiC,SAAU,oBACX,GAAI,CAAE/B,SAAU2C,KDrR5B,IAAczE,CCwRrB,ECzVAyC,iBACI,IAAIyE,QAAcd,EAAYD,EAAe,aAAc,cAEvDuC,EAAS,EACbxB,EAAMjE,SAAS0F,IACXlK,EAAMiK,GAAQlG,MAAK,IAAMmG,EAAKpH,SAC9BmH,GAAU,GAAI,GAEtB,CAEIE"}
\ No newline at end of file
{
"name": "WalmartExports",
"description": "导出沃尔玛报表",
"version": "1.0.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js",
"type": "module"
},
"permissions": [
"tabs",
"activeTab",
"scripting",
"storage",
"notifications",
"alarms"
],
"action": {
"default_title": "WalmartExports",
"default_popup": "popup.html",
"default_icon": {
"16": "icon.png",
"32": "icon.png",
"48": "icon.png",
"128": "icon.png"
}
},
"icons": {
"16": "icon.png",
"32": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"web_accessible_resources": [
{
"resources": ["src/main.js"],
"matches": ["<all_urls>"]
}
],
"host_permissions": [
"https://seller.walmart.com/*"
],
"content_scripts": [
{
"matches": [
"https://seller.walmart.com/*"
],
"js": [
"dist/bundle.js"
]
}
]
}
\ No newline at end of file
{
"$schema": "https://json.schemastore.org/jsconfig",
"type": "module",
"scripts": {
"build": "rollup -c"
},
"devDependencies": {
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"rollup": "^4.34.8"
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WalmartExports</title>
</head>
<body>
</body>
</html>
\ No newline at end of file
import babel from "@rollup/plugin-babel";
import nodeResolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";
export default [{
input: "src/main.js",
output: {
file: "dist/app.js",
format: "iife",
name: "G",
},
}, {
input: "src/main.js",
output: {
file: "dist/app.min.js",
format: "iife",
name: "G",
sourcemap: true
},
plugins: [
nodeResolve(),
commonjs(),
babel({
babelHelpers: "bundled",
}),
terser()
]
}]
\ No newline at end of file
import { sleep, getValue, ffmt1, fmt2, fmt3, fmt1, uploadFile, JSON2CSV, downloadFile } from "./util.js";
const catchError = (reason) => {
console.error(reason);
}
const headers = Object.freeze({
"wm_aurora.market": "US",
});
const DELAY = 2000;
class Task {
constructor(names, params, options) {
this.names = names;
this.params = params;
this.options = options || {};
}
send() {
if (!(this.uri && this.scid)) {
console.warn(`未设置 ${this}`);
return;
}
const url = new URL(`${this.uri}${this.names.apiName || ''}`);
const method = this.options.method || 'GET';
const callback = this.options.callback;
const desc = this.names.desc || this.desc;
let ext = this.options.ext || 'csv';
let body = this.options.body;
body = (body && JSON.stringify(body)) || null;
url.search = new URLSearchParams(this.params).toString();
console.log(`${method} ${url}`);
fetch(url, {
body,
method,
headers: this.options.headers || headers,
...(this.options.requestInit || {})
}).then(async (response) => {
console.log(response);
if (callback) {
return callback.call(this, response);
}
if (ext == 'xlsx') {
const json = await response.json();
const csv = JSON2CSV(json);
ext = 'csv';
return new Blob([csv], { type: "text/csv" });
} else {
return response.blob();
}
}).then((blob) => {
const fn = `${this.scid} ${this.names.showName.toLowerCase()} ${desc}.${ext}`;
console.log(`下载/上传: ${fn}`);
// uploadFile(fn, data);
downloadFile(fn, blob);
}).catch(catchError);
}
}
const inventoryHealthCallback = async function (response) {
let data = await response.json();
const url = data.downloadUrl;
if (!url) {
throw new Error(`downloadUrl 获取失败: ${JSON.stringify(data)}`);
}
console.log(`GET ${url}`);
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
return response.blob();
}
const campaignsCallback = async function (response) {
let data = await response.json();
const waitDone = async () => {
try {
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
data = await response.json();
const item = data.response.filter((item) => item.download.endsWith(snapshotId))[0];
if (!item) {
throw new Error(JSON.stringify(data));
}
await sleep(DELAY);
const sf = item.requestedOn.replace(/[\s:-]/g, '_');
url = `${item.download}?fileName=${this.names.fileName}_${sf}.csv&lang=en-US`;
console.log(`GET ${url}`);
response = await fetch(url, { headers } );
console.log(response);
return response.blob();
} catch (e) {
console.log(`重试请求, ${e}`);
return waitDone();
}
}
const snapshotId = data.response && data.response.snapshotId;
if (!snapshotId) {
throw new Error(`snapshotId 获取失败: ${JSON.stringify(data)}`);
}
console.log(`snapshotId: ${snapshotId}`);
let url = new URL(`${this.uri}${this.names.apiName}`);
const advertiserId = this.options.body.advertiserId;
url.search = new URLSearchParams({
request: `/v1/snapshot/report?advertiserId=${advertiserId}&jobStatus=pending,processing,done`
}).toString();
console.log(`GET ${url}`);
return waitDone();
}
const reportRequestsCallback = async function (response) {
let data = await response.json();
const requestId = data.requestId;
if (!requestId) {
throw new Error(`requestId 获取失败: ${JSON.stringify(data)}`);
}
console.log(`requestId: ${requestId}`);
let url = new URL(`${this.uri}${this.names.apiName2}`);
url.search = new URLSearchParams({
"reportType": "ORDER",
"reportVersion": "v1",
requestId
}).toString();
console.log(`GET ${url}`);
await sleep(DELAY);
response = await fetch(url, { headers });
console.log(response);
return response.blob();
}
const getPeriods = async () => {
const url = "https://seller.walmart.com/aurora/v1/auroraPaymentsSettlementService/payment/settlementPeriods";
console.log(`GET ${url}`);
let response = await fetch(url, { headers });
let data = await response.json();
response = data.payload && data.payload.response;
if (!response) {
throw new Error(`response 获取失败: ${JSON.stringify(data)}`);
}
const periods = response.slice(1, 4);
console.log(`periods: ${JSON.stringify(periods)}`);
return periods;
}
const createCampaignsTask = function (showName, bodyArgs, request) {
return new Task({
apiName: "campaigns",
showName: "广告表",
fileName: showName.replaceAll(' ', '')
}, {
request: request || "/v1/snapshot/report"
}, {
method: 'POST',
body: {
"advertiserId": localStorage.getItem("advertiserId"),
"format": "gzip",
...bodyArgs,
},
callback: campaignsCallback
});
}
export const FormURI = {
WFS: "https://seller.walmart.com/aurora/v1/wfs/reports/",
Payment: "https://seller.walmart.com/aurora/v1/report/reconreport/",
Order: "https://seller.walmart.com/aurora/v1/reports/",
Advertiser: "https://advertising.walmart.com/sp/api/",
}
export async function createTasks(formURI, fromDate, toDate) {
const uri = new URL(formURI || FormURI.WFS);
if (location.host != uri.host) {
console.warn("不支持当前网站");
return [];
}
Task.prototype.uri = uri;
Task.prototype.scid = getValue('scid');
if (fromDate && toDate) {
Task.prototype.desc = ffmt1(fromDate, toDate);
}
switch (formURI) {
case FormURI.Payment:
const periods = await getPeriods();
await sleep(DELAY);
let tasks = [];
for (let period of periods) {
fromDate = period.payPeriodFromDate;
toDate = period.payPeriodToDate;
const desc = `${fmt1(fromDate)}-${fmt1(toDate)}`;
const reportDate = fmt3(period.settleDate);
tasks = tasks.concat([
new Task({
apiName: "v1/reconFile",
showName: "Payments new report",
desc
}, {
reportDate
}, {
ext: "zip"
}),
new Task({
apiName: "reconFile",
showName: "Payments legacy report",
desc
}, {
reportDate
}, {
ext: "zip"
})
]);
}
return tasks;
case FormURI.Order:
return [
new Task({
apiName: "reportRequests",
apiName2: "downloadReport",
showName: "订单表"
}, {
"reportType": "ORDER",
"reportVersion": "v1",
"isDSV": "false",
"bizRole": "MP"
}, {
method: 'POST',
body: {
"rowFilters": [
{
"type": "enumFilter",
"values": [
"All"
],
"columnName": "orderGroups"
},
{
"type": "enumFilter",
"values": [
"Seller"
],
"columnName": "fulfilmentTypes"
},
{
"type": "rangeFilter",
"columnName": "orderDate",
"from": fromDate,
"to": toDate
}
],
"additionalInfo": {
"reportSummary": `Seller Fulfilled - ${fmt2(fromDate)} to ${fmt2(toDate)}`
},
"excludeColumns": [
"Order Type",
"Service PO#",
"Service Status",
"Total Service Cost",
"Service Scheduled Date"
]
},
callback: reportRequestsCallback,
}),
];
case FormURI.Advertiser:
Task.prototype.scid = getValue(
"partner_id",
decodeURIComponent(getValue('adtech-w-dest')),
"&"
);
return [
createCampaignsTask("Keyword Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "keyword",
"attributionWindow": "days3",
"extraFields": ["noDate"],
}),
createCampaignsTask("Item Keyword Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "itemKeyword",
"attributionWindow": "days3",
"extraFields": ["noDate"],
}),
createCampaignsTask("Item Performance", {
"startDate": fromDate,
"endDate": toDate,
"reportType": "adItem",
"attributionWindow": "days3",
"extraFields": ["noDate", "splitAttribution"],
}),
createCampaignsTask("Keyword Recommendations", {
"recommendationType":"keywordRecommendations"
}, "/v1/snapshot/recommendations"),
];
default:
return [
new Task({
apiName: "salesReport",
showName: "Orders"
}, {
fromDate,
toDate,
}, { ext: 'xlsx' }),
new Task({
apiName: "salesReport",
showName: "Orders",
sheetName: "Multchannel_Fulfillment_Details"
}, {
fromDate,
toDate,
type: "MCS"
}, { ext: 'xlsx' }),
new Task({
apiName: "returnsReport",
showName: "Customer returns"
}, {
fromDate: `${fromDate}T00:00:00`,
toDate: `${toDate}T23:59:59`,
}),
new Task({
apiName: "inventoryReconciliation",
showName: "Inventory reconciliation"
}, {
fromDate,
toDate,
}),
new Task({
apiName: "feeDetailReport",
showName: "Settlement"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "storageFeeReport",
showName: "Storage"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "inboundTransportationReport",
showName: "Inbound transportation"
}, {
startDate: fromDate,
endDate: toDate,
}),
new Task({
apiName: "inventoryHealthReport",
showName: "Inventory health"
}, {}, { callback: inventoryHealthCallback }),
];
}
}
import { sleep } from "./util.js";
import { createTasks, FormURI } from "./base.js";
const TEST_FLAG = true;
async function run(sn) {
let tasks = await createTasks(FormURI.Order, "2025-02-01", "2025-02-10");
tasks = typeof sn == 'number' ? [tasks.at(sn)] : tasks;
let moment = 0;
tasks.forEach((task) => {
sleep(moment).then(() => task.send());
moment += 2000;
});
}
if (TEST_FLAG) {
run();
}
const MONTHS = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function getValue(key, str, sep = ';') {
const items = (str || document.cookie).split(sep);
for (let item of items) {
item = item.trim();
if (item.startsWith(key)) {
let value = item.substring(key.length + 1, item.length);
console.log(`已找到${key}: ${value}`);
return value;
}
}
console.warn(`未找到${key}`);
return '';
}
const replacer = (value) => {
if (value === null || value === undefined) {
return '';
}
if (typeof value == 'string') {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
}
export function JSON2CSV(json) {
const cols = Object.keys(json[0]);
let csv = cols.join(',') + '\r\n';
json.forEach(row => {
const values = cols.map(col => replacer(row[col]));
csv += values.join(',') + '\r\n';
});
return csv;
}
export function downloadFile(fn, blob) {
// const url = `data:text/csv;charset=utf8,\ufeff${encodeURIComponent(data)}`;
// const blob = new Blob([`\ufeff${data}`], { type: "text/csv;charset=utf8" });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fn;
a.click();
a.remove();
URL.revokeObjectURL(url);
}
export function uploadFile(fn, data) {
const url = ''; // ?
fetch(url, {
method: "POST",
body: {
data,
fileName: fn
},
})
.then((response) => console.log(`上传成功: ${fn}, ${response}`))
.catch((reason) => console.error(`上传失败: ${fn}, by ${reason}`));
}
export function fmt1(d1) {
const dt1 = new Date(d1);
return `${MONTHS[dt1.getMonth()]} ${dt1.getDate()},${dt1.getFullYear()}`;
}
export function fmt2(d1) {
const dt1 = new Date(d1);
const date = dt1.getDate().toString().padStart(2, '0');
return `${MONTHS[dt1.getMonth()]} ${date}, ${dt1.getFullYear()}`;
}
export function fmt3(d1) {
d1 = d1.replaceAll('-', '');
return d1.slice(4) + d1.slice(0, 4);
}
export function ffmt1(d1, d2) {
const dt1 = new Date(d1);
const dt2 = new Date(d2);
return `${dt1.getMonth() + 1}${dt1.getDate()}-${dt2.getDate()}`;
}
[[tool.uv.index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
import platform
import subprocess
import time
import os
import json
import uuid
import requests
import hashlib
import traceback
from typing import Callable, List, Tuple
from DrissionPage import Chromium
from DrissionPage.items import MixTab
from DrissionPage.errors import ElementNotFoundError
from .const import BROWSER_PATH, DRIVER_PATH, SOCKET_PORT, logger
class SuperBrowserBridge:
def __init__(self, user_info: dict):
self.user_info = user_info
self.browser_list: list = []
self.driver: Chromium = None
self.tab: MixTab = None
self._is_windows: bool = platform.system() == 'Windows'
self._is_mac: bool = platform.system() == 'Darwin'
self._opt_id: int = 0
self._opts: List[Tuple[int, Callable]] = []
def add_opt(self, opt: Callable):
"""添加自动化操作"""
self._opt_id += 1
self._opts.append((self._opt_id, opt))
def encrypt_sha1(self, fpath: str) -> str:
with open(fpath, 'rb') as f:
return hashlib.new('sha1', f.read()).hexdigest()
def download_file(self, url, save_path):
# 发送GET请求获取文件内容
response = requests.get(url, stream=True)
# 检查请求是否成功
if response.status_code == 200:
# 创建一个本地文件并写入下载的内容(如果文件已存在,将被覆盖)
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
logger.info(f"文件已成功下载并保存到:{save_path}")
else:
logger.error(f"下载失败,响应状态码为:{response.status_code}")
def download_driver(self):
if self._is_windows:
config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/exe_32/config.json"
elif self._is_mac:
arch = platform.machine()
if arch == 'x86_64':
config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/mac/x64/config.json"
elif arch == 'arm64':
config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/mac/arm64/config.json"
else:
return
else:
return
response = requests.get(config_url)
# 检查请求是否成功
if response.status_code == 200:
# 获取文本内容
txt_content = response.text
config = json.loads(txt_content)
else:
logger.error(f"下载驱动失败,状态码:{response.status_code}")
exit()
if not os.path.exists(DRIVER_PATH):
os.makedirs(DRIVER_PATH)
# 获取文件夹中所有chromedriver文件
driver_list = [filename for filename in os.listdir(DRIVER_PATH) if filename.startswith('chromedriver')]
for item in config:
filename = item['name']
if self._is_windows:
filename = filename + ".exe"
local_file_path = os.path.join(DRIVER_PATH, filename)
if filename in driver_list:
# 判断sha1是否一致
file_sha1 = self.encrypt_sha1(local_file_path)
if file_sha1 == item['sha1']:
logger.info(f"驱动{filename}已存在,sha1校验通过...")
else:
logger.info(f"驱动{filename}的sha1不一致,重新下载...")
self.download_file(item['url'], local_file_path)
# mac首次下载修改文件权限
if self._is_mac:
cmd = ['chmod', '+x', local_file_path]
subprocess.Popen(cmd)
else:
logger.info(f"驱动{filename}不存在,开始下载...")
self.download_file(item['url'], local_file_path)
# mac首次下载修改文件权限
if self._is_mac:
cmd = ['chmod', '+x', local_file_path]
subprocess.Popen(cmd)
def exit(self):
"""关闭客户端"""
data = {"action": "exit", "requestId": str(uuid.uuid4())}
data.update(self.user_info)
logger.info('browser exit ...' + json.dumps(data, ensure_ascii=False))
self.send_http(data)
def update_core(self):
"""
下载所有内核,打开店铺前调用,需客户端版本5.285.7以上
因为http有超时时间,所以这个action适合循环调用,直到返回成功
"""
data = {
"action": "updataCore",
"requestId": str(uuid.uuid4()),
}
data.update(self.user_info)
while True:
result = self.send_http(data)
logger.info(result)
if result is None:
logger.info("等待客户端启动...")
time.sleep(2)
continue
if result.get("statusCode") is None or result.get("statusCode") == -10003:
logger.info("当前版本不支持此接口,请升级客户端")
return
elif result.get("statusCode") == 0:
logger.info("更新内核完成")
return
else:
logger.info(f"等待更新内核: {json.dumps(result)}")
time.sleep(2)
def send_http(self, data):
try:
url = 'http://127.0.0.1:{}'.format(SOCKET_PORT)
response = requests.post(url, json.dumps(data).encode('utf-8'), timeout=120)
return json.loads(response.text)
except Exception as err:
logger.error(err)
def kill_process(self):
if self._is_windows:
os.system('taskkill /f /t /im SuperBrowser.exe')
elif self._is_mac:
os.system('killall ziniao')
time.sleep(3)
def start_browser(self):
try:
if self._is_windows:
cmd = [BROWSER_PATH, '--run_type=web_driver', '--ipc_type=http', '--port=' + str(SOCKET_PORT)]
elif self._is_mac:
cmd = ['open', '-a', BROWSER_PATH, '--args', '--run_type=web_driver', '--ipc_type=http',
'--port=' + str(SOCKET_PORT)]
else:
logger.warning('platform not supported')
exit()
subprocess.Popen(cmd)
time.sleep(5)
except Exception as e:
logger.error('start browser process failed')
logger.info('browser launch')
def get_browser_list(self) -> list:
request_id = str(uuid.uuid4())
data = {
"action": "getBrowserList",
"requestId": request_id
}
data.update(self.user_info)
r = self.send_http(data)
if str(r.get("statusCode")) == "0":
logger.debug(r)
return r.get("browserList")
elif str(r.get("statusCode")) == "-10003":
logger.error(f"login Err {json.dumps(r, ensure_ascii=False)}")
exit()
else:
logger.error(f"Fail {json.dumps(r, ensure_ascii=False)} ")
exit()
def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0, isHeadless=0, cookieTypeSave=0, jsInfo=""):
request_id = str(uuid.uuid4())
data = {
"action": "startBrowser"
, "isWaitPluginUpdate": 0
, "isHeadless": isHeadless
, "requestId": request_id
, "isWebDriverReadOnlyMode": isWebDriverReadOnlyMode
, "cookieTypeLoad": 0
, "cookieTypeSave": cookieTypeSave
, "runMode": "1"
, "isLoadUserPlugin": False
, "pluginIdType": 1
, "privacyMode": isprivacy
}
data.update(self.user_info)
if store_info.isdigit():
data["browserId"] = store_info
else:
data["browserOauth"] = store_info
if len(str(jsInfo)) > 2:
data["injectJsInfo"] = json.dumps(jsInfo)
r = self.send_http(data)
if str(r.get("statusCode")) == "0":
return r
elif str(r.get("statusCode")) == "-10003":
logger.error(f"login Err {json.dumps(r, ensure_ascii=False)}")
exit()
else:
logger.error(f"Fail {json.dumps(r, ensure_ascii=False)} ")
exit()
def close_store(self, browser_oauth):
request_id = str(uuid.uuid4())
data = {
"action": "stopBrowser"
, "requestId": request_id
, "duplicate": 0
, "browserOauth": browser_oauth
}
data.update(self.user_info)
r = self.send_http(data)
if str(r.get("statusCode")) == "0":
return r
elif str(r.get("statusCode")) == "-10003":
logger.info(f"login Err {json.dumps(r, ensure_ascii=False)}")
exit()
else:
logger.info(f"Fail {json.dumps(r, ensure_ascii=False)} ")
exit()
def get_driver(self, open_ret_json):
core_type = open_ret_json.get('core_type')
if core_type == 'Chromium' or core_type == 0:
major = open_ret_json.get('core_version').split('.')[0]
if self._is_windows:
chrome_driver_path = os.path.join(DRIVER_PATH, 'chromedriver%s.exe') % major
else:
chrome_driver_path = os.path.join(DRIVER_PATH, 'chromedriver%s') % major
logger.info(f"chrome_driver_path: {chrome_driver_path}")
port = open_ret_json.get('debuggingPort')
self.driver = Chromium(port)
self.tab = self.driver.latest_tab
logger.info('webdriver 初始化完毕')
else:
return None
def _run_task(self, browser):
"""
打开一个店铺运行脚本
:param browser: 店铺信息
"""
# 如果要指定店铺ID, 获取方法:登录紫鸟客户端->账号管理->选择对应的店铺账号->点击"查看账号"进入账号详情页->账号名称后面的ID即为店铺ID
store_id = browser.get('browserOauth')
store_name = browser.get("browserName")
# 打开店铺
logger.info(f"=====打开店铺:{store_name}=====")
ret_json: dict = self.open_store(store_id)
logger.info(ret_json)
store_id = ret_json.get("browserOauth")
if store_id is None:
store_id = ret_json.get("browserId")
# 使用驱动实例开启会话
self.get_driver(ret_json)
if self.driver is None:
logger.info(f"=====关闭店铺:{store_name}=====")
self.close_store(store_id)
return
# 获取ip检测页地址
ip_check_url = ret_json.get("ipDetectionPage")
if not ip_check_url:
logger.error("ip检测页地址为空,请升级紫鸟浏览器到最新版")
self.driver.quit()
logger.info(f"=====关闭店铺:{store_name}=====")
self.close_store(store_id)
exit()
# 执行脚本
try:
ip_usable = self.open_ip_check(ip_check_url)
if ip_usable:
logger.info("ip检测通过,打开店铺平台主页")
self.tab.get(ret_json.get("launcherPage"))
# 打开店铺平台主页后进行后续自动化操作
for i, opt in self._opts:
if not callable(opt):
logger.error(f"{i}号任务不可调用")
continue
logger.info(f"开始执行{i}号任务")
try:
opt(self.tab, self.driver) # 运行
logger.info(f"{i}号任务执行完毕")
except Exception as e:
logger.error(f"{i}号任务执行失败:{e}")
else:
logger.error("ip检测不通过,请检查")
except:
logger.error("脚本运行异常:" + traceback.format_exc())
finally:
self.driver.quit()
logger.info(f"=====关闭店铺:{store_name}=====")
self.close_store(store_id)
def open_ip_check(self, ip_check_url):
"""
打开ip检测页检测ip是否正常
:param driver: driver实例
:param ip_check_url ip检测页地址
:return 检测结果
"""
try:
self.tab.get(ip_check_url)
self.tab.ele('//button[contains(@class, "styles_btn--success")]')
return True
except ElementNotFoundError:
logger.info("未找到ip检测成功元素")
return False
except Exception as e:
logger.info("ip检测异常:" + traceback.format_exc())
return False
def init(self):
self.download_driver()
self.kill_process()
self.start_browser()
self.update_core()
self.browser_list = self.get_browser_list()
if not self.browser_list:
logger.error("browser list is empty")
exit()
def run(self, sn: int = None):
browsers = self.browser_list if sn is None else [self.browser_list[sn]]
for browser in browsers:
self._run_task(browser)
\ No newline at end of file
import os
from tool.log import create_logger
BROWSER_PATH = os.environ.get(
'BROWSER_PATH',
r"D:\SuperBrowser\starter.exe"
)
SOCKET_PORT = os.environ.get(
'SOCKET_PORT',
16851
)
DRIVER_PATH = os.environ.get(
'DRIVER_PATH',
r"D:\webdriver"
)
EXPORT_PATH = os.environ.get(
'EXPORT_PATH',
r"E:\wfs_export_data"
)
USER_INFO = {
"company": "深圳市泰极电子商务有限公司",
"username": "Leshiliuzong",
"password": "leshi2023"
}
WFS_HEADERS = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"wm_aurora.market": "US",
}
logger = create_logger("wfs_export")
from DrissionPage import Chromium
from DrissionPage.items import MixTab
from .browser import SuperBrowserBridge
from .const import USER_INFO, logger
class WFSExport:
def __init__(self, start_date: str, end_date: str):
self._start_date = start_date
self._end_date = end_date
self.bridge = SuperBrowserBridge(USER_INFO)
self.bridge.init()
self.cookies: dict = None
self.uri: str = None
def download_report(self, names: dict, params: dict, ext: str = 'csv'):
pass
def download_reports(self, tab: MixTab, driver: Chromium):
"""下载报表"""
self.uri = "https://seller.walmart.com/aurora/v1/wfs/reports"
tab.run_js()
self.download_report({
"apiName": "salesReport",
"showName": "Orders"
}, {
"fromDate": self._start_date,
"toDate": self._end_date,
}, 'xlsx')
self.download_report({
"apiName": "salesReport",
"showName": "Orders",
"sheetName": "Multchannel_Fulfillment_Details"
}, {
"fromDate": self._start_date,
"toDate": self._end_date,
"type": "MCS"
}, 'xlsx')
self.download_report({
"apiName": "returnsReport",
"showName": "Customer returns"
}, {
"fromDate": f"{self._start_date}T00:00:00",
"toDate": f"{self._end_date}T23:59:59",
})
self.download_report({
"apiName": "poAudit",
"showName": "Inbound receipts"
}, {
"fromDate": self._start_date,
"toDate": self._end_date,
"gtin": ""
})
self.download_report({
"apiName": "inventoryReconciliation",
"showName": "Inventory reconciliation"
}, {
"fromDate": self._start_date,
"toDate": self._end_date,
})
breakpoint()
# self.download_report({
# "apiName": "inventoryHealthReport",
# "showName": "Inventory health"
# }, None)
self.download_report({
"apiName": "feeDetailReport",
"showName": "Settlement"
}, {
"startDate": self._start_date,
"endDate": self._end_date,
})
self.download_report({
"apiName": "storageFeeReport",
"showName": "Storage"
}, {
"startDate": self._start_date,
"endDate": self._end_date,
})
def download_payments(self, tab: MixTab, driver: Chromium):
breakpoint()
def pass_test(self, tab: MixTab, driver: Chromium):
"""人机检测"""
breakpoint()
logger.info("尝试通过人机检测")
# tab.actions.move_to("#px-captcha").hold().release()
tab.ele("@text()=登录").click()
tab.wait.ele_displayed('@text()=WFS')
cookies = tab.cookies()
logger.debug(f"获取到cookies: {cookies.as_str()}")
self.cookies = cookies.as_dict()
def run(self):
try:
self.bridge.browser_list = [
browser for browser in self.bridge.browser_list
if browser['platform_name'] == '沃尔玛-全球'
]
self.bridge.add_opt(self.pass_test)
self.bridge.add_opt(self.download_reports)
self.bridge.add_opt(self.download_payments)
self.bridge.run(sn=1)
finally:
self.bridge.exit()
\ No newline at end of file
from src.export import WFSExport
def test_WFSExport():
WFSExport("2025-02-01", "2025-02-10").run()
if __name__ == '__main__':
test_WFSExport()
import requests
import json
headers = {
"authority": "seller.walmart.com",
"accept": "application/json",
"accept-language": "zh-CN,zh;q=0.9",
"content-type": "application/json",
"origin": "https://seller.walmart.com",
"referer": "https://seller.walmart.com/orders/manage-orders?orderGroups=All",
"sec-ch-ua": "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
"wm_aurora.locale": "en-US",
"wm_aurora.market": "US",
"wm_aurora.timezone": "-08:00",
"wm_aurora.tmxevent": "LOGIN",
"wm_aurora.tmxid": "vn69bwdaeabumw_i2lachl3c2nef-_yze7a1",
"wm_qos.correlation_id": "35572f2f-dd03-4450-a9a1-fdde70ebd3f4",
"wm_svc.name": "API",
"x-xsrf-token": "aa71d953c3b0dba3f30c18b523f565d55dc8ceea47e529bdc291d5bec71a1780"
}
cookies = {
"vtc": "eoOI4d3iNX05_z5UzKUFL0",
"TS0194e2a6": "01c5a4e2f9216f40ab23fb6ea75116986e8e40ee3e753eff233455fb59ceed474c29820b01ce9a60e82fb7f3b5ce3d873b53d235fd",
"TS011714b6": "01c5a4e2f9216f40ab23fb6ea75116986e8e40ee3e753eff233455fb59ceed474c29820b01ce9a60e82fb7f3b5ce3d873b53d235fd",
"XSRF-T2_MART": "US",
"_auth": "MTAyOTYyMDE44s/dK5SjffV4NFYBHwXAYdf7fowbQMxi/sekxkoojDAFvLInAueipbLaayshq/oE0m7pAl6tjfRutM6x1y3fq0iczhE3eZLB+mqAX3wP6R8ylqRdv3lzJC3CZ4gZyQvZ/z17NuP/cUbgGcA/2mdWr+jqZcc1mwrl3Dk1tUjq0mNvgCN1P5vyVcaA3y4pdG/Ry4pYDqhCKojR30JMYUyqv3WXa8PLY3Qx1eDvqRu5/ebaG42uOA92ynUyMEYYNGY8jaSE5ElcV2HHAOuNeUoAZjxnbRtfyanLF89tf0/W/mncKMVjc0h4IWCgeE2p+5icHo/pfkPYuEdSOYqdcaOtOlMHT9XE74SfVuE58Y+HidXYStryyZPuCyS+2o5lbBdPbnZmSjB0E4oMPQMkeJVnwjt0sOVPDkHKdQ2+ZQv+E8mIQ61FuRdBQLhowDo9xCOD",
"JSESSIONID": "f8e041aa-5c2d-49f1-a060-82707e45d93b",
"XSRF-TOKEN": "aa71d953c3b0dba3f30c18b523f565d55dc8ceea47e529bdc291d5bec71a1780",
"SC_EXP_FLAVOUR": "aurora",
"scid": "10002545660",
"SC_SELLER_FLAVOUR": "allen",
"isSeller": "true",
"SC_GLOBAL_NAV_ENABLED": "true",
"SC_MULTIBOX": "true",
"SC_TAX_PROFILE_FLAVOUR": "aurora",
"SC_INSURANCE_FLAVOUR": "aurora",
"SC_PAYMENT_INFO_FLAVOUR": "aurora",
"appstore.seller": "true",
"SC_SELLER_SETTINGS": "aurora",
"isbm.enabled.seller": "true",
"SC_WFS_FLAVOUR": "aurora",
"IS_MLMQ": "aurora",
"ORDERSV2_REVAMP_SELLERS": "aurora",
"sc.manage.items.rollout": "true",
"sc.activity.feeds.rollout": "true",
"sc.pt.spec.rollout": "true",
"sc.ioh.rollout": "true",
"sc.wfs.multiBox.rollout": "true",
"sc.items.aurora.migration": "true",
"SC_RATINGS_REVIEWS": "aurora",
"SC_ANALYTICS_OVERVIEW_WIDGET": "true",
"SHOW_SALES_BY_DEPARTMENT_WIDGET": "true",
"pxcts": "22c525dc-f00e-11ef-9b4a-4d039f64f67f",
"_pxvid": "22c519a0-f00e-11ef-9b4a-b98748f7bdc6",
"_gcl_au": "1.1.1151528653.1740112911",
"hubspotutk": "10d191df307906711e90e07a8938f14e",
"__hssrc": "1",
"QuantumMetricUserID": "5144e6e94f4fa4d356f2afa9f7a20bb6",
"TS015a1969": "019f81911db180d6d5f4f2af2fe76b861a9a5523a31745d303ab54ce04c91311b799bf562ad022e7da084034cdb0ac79adeb151ef3",
"_px3": "b55322a25048af8e7437f436257006e0ff263176a9da673ce54e68a1f611b1f8:oJG59NZKwOJA85UC42Im3DuMrwAk/oRzIQeo3UK6B/SoUHVMx3JdAXZb75f3j9TlrpNCxMlgvdOJ5RZmG3Un2w==:1000:gnHjrnAHSHoqMUCZf/b39TuoSCFbK0t9ymgVePLXkIm80UfTl0d+QmJSgc5UPcIqWtNAq2gUj41d7d8+qwWv1cGRRZ4hMnSXbByZOFRmpYewatmoZ5KlF8VdLzz2LeakLEmlYjnXyaDg7uD8DiNdRYtM47XdiHdVrc0KHgdNpN3vo8Bhk1xvDPw5yLwGzxKa8e6pN5y2RhMG8C1myTNkUHuAWBde1u4hcuegr3XsmGY=",
"mp_706c95f0b1efdbcfcce0f666821c2237_mixpanel": "%7B%22distinct_id%22%3A%20%22%24device%3A19526f537568a8-02c94e61d98269-26031151-1fa400-19526f537568a8%22%2C%22%24device_id%22%3A%20%2219526f537568a8-02c94e61d98269-26031151-1fa400-19526f537568a8%22%2C%22%24initial_referrer%22%3A%20%22https%3A%2F%2Flogin.account.wal-mart.com%2F%22%2C%22%24initial_referring_domain%22%3A%20%22login.account.wal-mart.com%22%2C%22__mps%22%3A%20%7B%7D%2C%22__mpso%22%3A%20%7B%22%24initial_referrer%22%3A%20%22https%3A%2F%2Flogin.account.wal-mart.com%2F%22%2C%22%24initial_referring_domain%22%3A%20%22login.account.wal-mart.com%22%7D%2C%22__mpus%22%3A%20%7B%7D%2C%22__mpa%22%3A%20%7B%7D%2C%22__mpu%22%3A%20%7B%7D%2C%22__mpr%22%3A%20%5B%5D%2C%22__mpap%22%3A%20%5B%5D%7D",
"ak_bmsc": "7FDBAA3D96AB94FCEF621B55ECD088C6~000000000000000000000000000000~YAAQJ/EPF4V2dhqVAQAAWneNJxph/DdrGilMksMs/9QM31rRA8vSkJfUkuwQEw/kwQr38I22qXDMjSlEWMkyoQrHqCF+fxAMO7TyoRkmfIElnEui9AqZSfYKoxcylfBZNo1Cvkj2gtG1mjaW3svHV7ix9jK0WAHQgzXUykAR6e4PnokazwmAWSvW3MEcZSlCrDNrYl61bj6W1iKkC9YJ9AkOI/vi6so1yIChJk2skib4pvVXk7QLPt/0A53rHKDMFG2iumV8FV9B37SUuAjpsAB1+2YAm+U+ttfbBQIJq+QUlc8enAjo9hVthow0glJEf5wD+ZaamudYCnORXwsczbWExTOR2m4qh8EzIZnGpN3KrJD6qnxW8Ob8o7r3x9E3Ii98yBkuqmRstLk=",
"OptanonConsent": "isGpcEnabled=0&datestamp=Fri+Feb+21+2025+16%3A09%3A01+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=202408.1.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=eb7ba610-87bd-4528-8132-ae77bbc49b09&interactionCount=1&isAnonUser=1&landingPath=NotLandingPage&groups=C0007%3A1%2CC0008%3A1%2CC0009%3A1%2CC0010%3A1&AwaitingReconsent=false&geolocation=CN%3BGD",
"OptanonAlertBoxClosed": "2025-02-21T08:09:01.049Z",
"__hstc": "195562739.10d191df307906711e90e07a8938f14e.1740112929843.1740118745664.1740125347091.4",
"QuantumMetricSessionID": "f9f306d4e929cb3700e94f0a072b2861",
"TS01817376": "01c5a4e2f9c1a6d129c120bb7986b8d553d53e6f689c8fa63f253768fd3bc7a16a8f8082e9723546639931da058ce98f550dfa6160",
"TSe809d73e027": "08cb8c7367ab2000040f38398513604f685537475ac784eed3c4f985e0ef5da596be4cdbad8232550829840af01130006415c7094da1d6b6087c8b11bdcb3fe06250b68b563213e124a5da29d814a0b59ec36e946dc06b26b071051e8ada25cb",
"TS24c05192027": "0800b316f6ab20009a0b71d1380323b79612e0bef09bd834b332a05228734c6e8e72d9921dd29a310887d174d7113000bba3769a5777b0dcc9685a6c6473c30c3f2301a75ce80ff91e5508b63d9b63f35bbc0c394618c8c7ab38d213fec5e718",
"bm_sv": "D51A2B8AAD9B515AFEC13194B6257681~YAAQu6cpFx9Kxf+UAQAA3O3uJxpZnsVkdfi46RdWaq7to15rt75WFQ5JaMMU9JamTYeAADeuLBTAUyIV3QHjs/h6ORt0DLF5duorw/iTsF31WrhSNvb0mYJWSJ9ylK7Bcx2fLWj7TqsvTDlutw+MXot/fX3l6pT/kZltXKtGaiify9buWNh+g4h9tkowiSDb09tUD4wIGJBK5m/dS5yKO4/9UXVlbvfRmoEnxGkJkoGLAdBq4DORlzRiVe5OSLELamU=~1"
}
url = "https://seller.walmart.com/aurora/v1/reports/reportRequests"
params = {
"reportType": "ORDER",
"reportVersion": "v1",
"isDSV": "false",
"bizRole": "MP"
}
data = {
"rowFilters": [
{
"type": "enumFilter",
"values": [
"All"
],
"columnName": "orderGroups"
},
{
"type": "enumFilter",
"values": [
"Seller"
],
"columnName": "fulfilmentTypes"
},
{
"type": "rangeFilter",
"columnName": "orderDate",
"from": "2025-02-10",
"to": "2025-02-20"
}
],
"additionalInfo": {
"reportSummary": "Seller Fulfilled - Feb 10, 2025 to Feb 20, 2025"
},
"excludeColumns": [
"Order Type",
"Service PO#",
"Service Status",
"Total Service Cost",
"Service Scheduled Date"
]
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, params=params, data=data)
print(response.text)
print(response)
from datetime import datetime
DATEFMT = "%Y-%m-%d"
def fmt1(d1: str, d2: str):
"""x月x-x号"""
dt1 = datetime.strptime(d1, DATEFMT)
dt2 = datetime.strptime(d2, DATEFMT)
return f"{dt1.month}月{dt1.day}-{dt2.day}日"
import logging
import sys
from datetime import datetime
from pathlib import Path
def create_logger(name: str):
today = datetime.now().strftime("%Y-%m-%d")
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
path = Path(__file__).parent.parent / 'log' / f"{today}_{name}.log"
fhdlr = logging.FileHandler(path, encoding='utf8')
shdlr = logging.StreamHandler(sys.stdout)
fmt = logging.Formatter('%(asctime)s | %(levelname)s | %(module)s:%(funcName)s:%(lineno)d - %(message)s')
fhdlr.setFormatter(fmt)
shdlr.setFormatter(fmt)
logger.addHandler(fhdlr)
logger.addHandler(shdlr)
return logger
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment