Update NewAddToCart_Web.js
This commit is contained in:
@@ -5,7 +5,9 @@
|
|||||||
* 流程:
|
* 流程:
|
||||||
* 1. GET 读取远程当前组(服务端加锁)
|
* 1. GET 读取远程当前组(服务端加锁)
|
||||||
* 2. 执行加购
|
* 2. 执行加购
|
||||||
* 3. 加购完成后 GET clear(服务端释放锁 + 弹出该组)
|
* 3. POST /surge/commit 提交结果:
|
||||||
|
* - remaining 为空 → 服务端弹出当前组(全部成功)
|
||||||
|
* - remaining 非空 → 服务端用失败的 product 更新当前组,释放锁(部分失败,下次只重试失败部分)
|
||||||
* 4. 若远程无数据,回退到本地 XboxProductList
|
* 4. 若远程无数据,回退到本地 XboxProductList
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,8 +17,7 @@ const FRIENDLY_NAME = `cart-${MARKET}`;
|
|||||||
const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" };
|
const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" };
|
||||||
|
|
||||||
const REMOTE_READ_URL = 'https://cc.dragonisheep.com/surge?token=xbox123';
|
const REMOTE_READ_URL = 'https://cc.dragonisheep.com/surge?token=xbox123';
|
||||||
const REMOTE_CLEAR_URL = 'https://cc.dragonisheep.com/surge/clear?token=xbox123';
|
const REMOTE_COMMIT_URL = 'https://cc.dragonisheep.com/surge/commit?token=xbox123';
|
||||||
const REMOTE_UNLOCK_URL = 'https://cc.dragonisheep.com/surge/unlock?token=xbox123';
|
|
||||||
const LOCAL_KEY = 'XboxProductList';
|
const LOCAL_KEY = 'XboxProductList';
|
||||||
|
|
||||||
const MUID = $persistentStore.read("cart-x-authorization-muid");
|
const MUID = $persistentStore.read("cart-x-authorization-muid");
|
||||||
@@ -28,7 +29,7 @@ const successKeys = [];
|
|||||||
let currentIndex = 0;
|
let currentIndex = 0;
|
||||||
let productList = [];
|
let productList = [];
|
||||||
let sourceLabel = "";
|
let sourceLabel = "";
|
||||||
let useRemote = false; // 是否使用了远程数据(决定加购后是否发 clear)
|
let useRemote = false;
|
||||||
|
|
||||||
function log(type, message, detail = "") {
|
function log(type, message, detail = "") {
|
||||||
const icon = type === "success" ? "✅" : (type === "error" ? "❌" : "ℹ️");
|
const icon = type === "success" ? "✅" : (type === "error" ? "❌" : "ℹ️");
|
||||||
@@ -74,45 +75,54 @@ function finalizeAndClean() {
|
|||||||
const successCount = results.success.length;
|
const successCount = results.success.length;
|
||||||
const failureCount = results.failure.length;
|
const failureCount = results.failure.length;
|
||||||
|
|
||||||
// 清理本地 XboxProductList 中已成功的 key(仅本地模式)
|
const showResult = () => {
|
||||||
let remainingCount = 0;
|
|
||||||
if (!useRemote) {
|
|
||||||
try {
|
|
||||||
let storeObj; try { storeObj = JSON.parse($persistentStore.read(LOCAL_KEY) || "{}"); } catch { storeObj = {}; }
|
|
||||||
for (const k of successKeys) {
|
|
||||||
if (k && Object.prototype.hasOwnProperty.call(storeObj, k)) delete storeObj[k];
|
|
||||||
}
|
|
||||||
remainingCount = Object.keys(storeObj).filter(k => /^product\d+$/.test(k)).length;
|
|
||||||
$persistentStore.write(JSON.stringify(storeObj), LOCAL_KEY);
|
|
||||||
log("info", "本地清理完成", `剩余: ${remainingCount}`);
|
|
||||||
} catch (e) { log("error", "清理异常", String(e)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
const doFinish = () => {
|
|
||||||
$notification.post(
|
$notification.post(
|
||||||
"🛒 Xbox 加购完成",
|
"🛒 Xbox 加购完成",
|
||||||
`成功: ${successCount} / 失败: ${failureCount}`,
|
`成功: ${successCount} / 失败: ${failureCount}`,
|
||||||
`来源: ${sourceLabel}${!useRemote ? ` | 剩余本地: ${remainingCount}` : ''}`
|
`来源: ${sourceLabel}`
|
||||||
);
|
);
|
||||||
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Xbox Cart</title></head><body style="font-family:sans-serif;padding:20px;"><h3>执行结果: 成功 ${successCount} / 失败 ${failureCount} | 来源: ${sourceLabel}</h3><div style="background:#f9f9f9;padding:10px;">${logBuffer.join("")}</div></body></html>`;
|
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Xbox Cart</title></head><body style="font-family:sans-serif;padding:20px;"><h3>执行结果: 成功 ${successCount} / 失败 ${failureCount} | 来源: ${sourceLabel}</h3><div style="background:#f9f9f9;padding:10px;">${logBuffer.join("")}</div></body></html>`;
|
||||||
$done({ response: { status: 200, headers: { "Content-Type": "text/html;charset=utf-8" }, body: html } });
|
$done({ response: { status: 200, headers: { "Content-Type": "text/html;charset=utf-8" }, body: html } });
|
||||||
};
|
};
|
||||||
|
|
||||||
if (useRemote) {
|
if (useRemote) {
|
||||||
if (failureCount === 0) {
|
// 构建失败的 product 对象(重新编号 product1, product2...)
|
||||||
// 全部成功:发 clear,释放服务端锁并弹出该组
|
const failedProducts = {};
|
||||||
log("info", "加购全部成功,通知服务端 clear");
|
let fi = 1;
|
||||||
$httpClient.post({ url: REMOTE_CLEAR_URL, headers: { 'Content-Type': 'application/json' }, body: '{}' }, () => doFinish());
|
for (const item of productList) {
|
||||||
} else {
|
if (results.failure.includes(item.productId)) {
|
||||||
// 有失败:释放锁但不弹组,保留服务端数据,下次可重试
|
failedProducts[`product${fi++}`] = {
|
||||||
log("info", `有 ${failureCount} 个失败,释放锁并保留服务端数据以便重试`);
|
ProductId: item.productId,
|
||||||
$httpClient.post({ url: REMOTE_UNLOCK_URL, headers: { 'Content-Type': 'application/json' }, body: '{}' }, () => {
|
SkuId: item.skuId,
|
||||||
$notification.post("⚠️ 部分失败", `${failureCount} 个加购失败`, "服务端数据已保留,可重新执行");
|
AvailabilityId: item.availabilityId
|
||||||
doFinish();
|
};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logMsg = failureCount === 0
|
||||||
|
? "全部成功,提交 commit(弹出当前组)"
|
||||||
|
: `${failureCount} 个失败,提交 commit(只保留失败的 product,其余组不变)`;
|
||||||
|
log("info", logMsg);
|
||||||
|
|
||||||
|
// 提交结果到服务端
|
||||||
|
$httpClient.post({
|
||||||
|
url: REMOTE_COMMIT_URL,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ remaining: failedProducts })
|
||||||
|
}, () => showResult());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
doFinish();
|
// 本地模式:清理已成功的 key
|
||||||
|
try {
|
||||||
|
let storeObj; try { storeObj = JSON.parse($persistentStore.read(LOCAL_KEY) || "{}"); } catch { storeObj = {}; }
|
||||||
|
for (const k of successKeys) {
|
||||||
|
if (k && Object.prototype.hasOwnProperty.call(storeObj, k)) delete storeObj[k];
|
||||||
|
}
|
||||||
|
const remainingCount = Object.keys(storeObj).filter(k => /^product\d+$/.test(k)).length;
|
||||||
|
$persistentStore.write(JSON.stringify(storeObj), LOCAL_KEY);
|
||||||
|
log("info", "本地清理完成", `剩余: ${remainingCount}`);
|
||||||
|
} catch (e) { log("error", "清理异常", String(e)); }
|
||||||
|
showResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,11 +136,15 @@ function startTask() {
|
|||||||
if (productList.length === 0) {
|
if (productList.length === 0) {
|
||||||
log("info", "列表为空");
|
log("info", "列表为空");
|
||||||
$notification.post("⚠️ Xbox 脚本", "无需执行", `来源: ${sourceLabel} | 列表为空`);
|
$notification.post("⚠️ Xbox 脚本", "无需执行", `来源: ${sourceLabel} | 列表为空`);
|
||||||
// 远程模式下列表为空也要释放锁
|
|
||||||
if (useRemote) {
|
if (useRemote) {
|
||||||
$httpClient.post({ url: REMOTE_CLEAR_URL, headers: { 'Content-Type': 'application/json' }, body: '{}' }, () => $done({}));
|
// 远程为空也要提交 commit 释放锁
|
||||||
|
$httpClient.post({
|
||||||
|
url: REMOTE_COMMIT_URL,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ remaining: {} })
|
||||||
|
}, () => $done({}));
|
||||||
} else {
|
} else {
|
||||||
finalizeAndClean();
|
$done({});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -166,8 +180,7 @@ function sendRequest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========================= 主流程 =========================
|
// ========================= 主流程 =========================
|
||||||
// ★ 服务端有锁机制,客户端不再需要时间锁
|
// 服务端有锁机制,客户端无需时间锁
|
||||||
// 第一步:尝试从远程读取当前组(服务端此时会加锁)
|
|
||||||
$httpClient.get(REMOTE_READ_URL, (error, response, data) => {
|
$httpClient.get(REMOTE_READ_URL, (error, response, data) => {
|
||||||
let remoteGroup = null;
|
let remoteGroup = null;
|
||||||
let groupIndex = null;
|
let groupIndex = null;
|
||||||
@@ -175,13 +188,11 @@ $httpClient.get(REMOTE_READ_URL, (error, response, data) => {
|
|||||||
if (!error && data) {
|
if (!error && data) {
|
||||||
try {
|
try {
|
||||||
const payload = JSON.parse((data || '').trim() || '{}');
|
const payload = JSON.parse((data || '').trim() || '{}');
|
||||||
// 服务端有锁且有数据时返回 currentGroup
|
|
||||||
if (payload.ok && payload.currentGroup) {
|
if (payload.ok && payload.currentGroup) {
|
||||||
const keys = Object.keys(payload.currentGroup);
|
const keys = Object.keys(payload.currentGroup);
|
||||||
if (keys.length > 0) {
|
if (keys.length > 0) {
|
||||||
remoteGroup = payload.currentGroup;
|
remoteGroup = payload.currentGroup;
|
||||||
groupIndex = payload.currentGroupIndex;
|
groupIndex = payload.currentGroupIndex;
|
||||||
log("info", "使用远程待同步 Product", `第 ${groupIndex} 组,共 ${keys.length} 个`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@@ -190,13 +201,13 @@ $httpClient.get(REMOTE_READ_URL, (error, response, data) => {
|
|||||||
if (remoteGroup) {
|
if (remoteGroup) {
|
||||||
useRemote = true;
|
useRemote = true;
|
||||||
sourceLabel = `远程第 ${groupIndex} 组`;
|
sourceLabel = `远程第 ${groupIndex} 组`;
|
||||||
|
log("info", "使用远程待同步 Product", `第 ${groupIndex} 组,共 ${Object.keys(remoteGroup).length} 个`);
|
||||||
productList = parseProductList(JSON.stringify(remoteGroup));
|
productList = parseProductList(JSON.stringify(remoteGroup));
|
||||||
startTask();
|
startTask();
|
||||||
} else {
|
} else {
|
||||||
// 远程无数据(队列为空或被锁)或连接失败,回退到本地
|
|
||||||
useRemote = false;
|
useRemote = false;
|
||||||
const localRaw = $persistentStore.read(LOCAL_KEY) || "{}";
|
|
||||||
sourceLabel = "本地";
|
sourceLabel = "本地";
|
||||||
|
const localRaw = $persistentStore.read(LOCAL_KEY) || "{}";
|
||||||
productList = parseProductList(localRaw);
|
productList = parseProductList(localRaw);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
log("info", "远程队列为空,使用本地 Product");
|
log("info", "远程队列为空,使用本地 Product");
|
||||||
|
|||||||
Reference in New Issue
Block a user