Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 178 additions & 41 deletions chrome-extension/background.js

Large diffs are not rendered by default.

138 changes: 104 additions & 34 deletions chrome-extension/boss-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
let stopRequestedRunId = "";
let activeScanRunId = "";
let activeScanPromise = null;
const deliveryExecutions = new Map();

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (!isCurrentContentInstance()) return;
Expand Down Expand Up @@ -115,7 +116,7 @@
}
if (message?.type === "BOSS_DELIVER_ONE") {
prepareStandaloneDelivery();
deliverOne(message.task, message).then(sendResponse).catch((error) => {
executeDeliveryOnce(message.task, () => deliverOne(message.task, message)).then(sendResponse).catch((error) => {
postProgress(message, "error", error.message || String(error), {
operation: "deliver",
stage: "error"
Expand Down Expand Up @@ -2751,10 +2752,9 @@
});
await waitForPage();
if (!isSameBossJobUrl(window.location.href, task.url)) {
return {
success: false,
message: "Boss投递需要先由扩展后台打开岗位详情页,请刷新扩展和页面后重试。"
};
const failure = classifyDeliveryFailure("Boss投递需要先由扩展后台打开岗位详情页,请刷新扩展和页面后重试。");
await postDeliveryResult(task, false, failure, "PRE_ACTION_ERROR");
return { success: false, outcome: "FAILED", evidence: "PRE_ACTION_ERROR", message: failure.failureReason, failureType: failure.failureType };
}
return deliverOnCurrentPage(task, message);
}
Expand All @@ -2771,30 +2771,44 @@
}
};

deliverOnCurrentPage(message.task, message, respondOnce).then((result) => {
respondOnce(result);
executeDeliveryOnce(message.task, () => deliverOnCurrentPage(message.task, message)).then((result) => {
respondOnce({ ...result, persisted: true });
}).catch((error) => {
postProgress(message, "error", error.message || String(error), {
operation: "deliver",
stage: "error"
});
respondOnce({ success: false, message: error.message || String(error) });
respondOnce({ success: false, outcome: "UNKNOWN", evidence: "NO_CONFIRMATION", persisted: false, message: error.message || String(error) });
});
}

async function deliverOnCurrentPage(task, message, earlyRespond) {
if (!task?.url || !task?.id) {
return { success: false, message: "投递任务缺少岗位链接或ID" };
return { success: false, outcome: "FAILED", evidence: "PRE_ACTION_ERROR", message: "投递任务缺少岗位链接或ID" };
}
await waitForPage();
if (!isSameBossJobUrl(window.location.href, task.url)) {
return { success: false, message: "当前Boss页面不是目标岗位详情页,已取消投递。" };
const failure = classifyDeliveryFailure("当前Boss页面不是目标岗位详情页,已取消投递。");
await postDeliveryResult(task, false, failure, "PRE_ACTION_ERROR");
return { success: false, outcome: "FAILED", evidence: "PRE_ACTION_ERROR", message: failure.failureReason, failureType: failure.failureType };
}
if (message?.respectScanStop && isStopRequested(message?.runId)) {
stopRequested = true;
return { success: false, message: "Boss扫描已停止" };
const failure = classifyDeliveryFailure("Boss扫描已停止,未执行投递");
await postDeliveryResult(task, false, failure, "PRE_ACTION_ERROR");
return { success: false, outcome: "FAILED", evidence: "PRE_ACTION_ERROR", message: failure.failureReason, failureType: failure.failureType };
}
await sleep(1500);
if (detectBossDeliveryStatus(document)) {
const messageText = "Boss岗位页面已显示沟通或投递状态";
await postDeliveryResult(task, true, messageText, "PLATFORM_STATUS_TEXT");
return {
success: true,
outcome: "CONFIRMED",
evidence: "PLATFORM_STATUS_TEXT",
message: messageText
};
}
postProgress(message, "info", `Boss Chrome正在当前详情页投递:${task.companyName || ""} ${task.jobName || ""}`.trim(), {
operation: "deliver",
stage: "submitting",
Expand Down Expand Up @@ -2822,12 +2836,12 @@
}
if (!chatButton) {
const failure = classifyDeliveryFailure("未找到立即沟通按钮");
await postDeliveryResult(task, false, failure);
await postDeliveryResult(task, false, failure, "PRE_ACTION_ERROR");
postProgress(message, "warning", `Boss Chrome投递失败:${failure.failureReason}`, {
operation: "deliver",
stage: "error"
});
return { success: false, message: failure.failureReason, failureType: failure.failureType };
return { success: false, outcome: "FAILED", evidence: "PRE_ACTION_ERROR", message: failure.failureReason, failureType: failure.failureType };
}
postProgress(message, "info", "Boss Chrome已找到沟通入口,准备点击立即沟通。", {
operation: "deliver",
Expand All @@ -2836,9 +2850,9 @@
clickElement(chatButton);
const successMessage = favoriteButton ? "Boss岗位已点击感兴趣并立即沟通" : "Boss岗位已点击立即沟通";
const deliveryCheck = await waitForDeliveryOpened(beforeUrl, task, 9000);
if (!deliveryCheck.success) {
if (deliveryCheck.outcome === "FAILED") {
const failure = classifyDeliveryFailure(deliveryCheck.message);
await postDeliveryResult(task, false, failure);
await postDeliveryResult(task, false, failure, deliveryCheck.evidence || "PLATFORM_ERROR");
postProgress(message, "warning", `Boss Chrome投递失败:${failure.failureReason}`, {
operation: "deliver",
stage: "error"
Expand All @@ -2847,14 +2861,28 @@
}
const greetingResult = await sendConfiguredGreeting(task, message);
const finalMessage = greetingResult?.sent ? `${successMessage},已发送开场白` : successMessage;
await postDeliveryResult(task, true, finalMessage);
earlyRespond?.({ success: true, message: finalMessage, early: true });
postProgress(message, "success", buildDeliverySuccessMessage(favoriteButton, greetingResult), {
const confirmed = deliveryCheck.outcome === "CONFIRMED";
await postDeliveryResult(
task,
confirmed ? true : null,
confirmed ? finalMessage : `${finalMessage},但未检测到明确平台成功状态`,
deliveryCheck.evidence || (confirmed ? "PLATFORM_STATUS_TEXT" : "CHAT_SURFACE_ONLY")
);
const result = {
success: confirmed,
outcome: confirmed ? "CONFIRMED" : "UNKNOWN",
evidence: deliveryCheck.evidence || (confirmed ? "PLATFORM_STATUS_TEXT" : "CHAT_SURFACE_ONLY"),
message: confirmed ? finalMessage : `${finalMessage},结果待人工确认`
};
earlyRespond?.({ ...result, early: true });
postProgress(message, confirmed ? "success" : "warning", confirmed
? buildDeliverySuccessMessage(favoriteButton, greetingResult)
: `${buildDeliverySuccessMessage(favoriteButton, greetingResult)}未检测到明确平台成功状态,已标记待确认。`, {
operation: "deliver",
stage: "complete",
saved: 1
saved: confirmed ? 1 : 0
});
return { success: true, message: finalMessage };
return result;
}

async function sendConfiguredGreeting(task, message) {
Expand Down Expand Up @@ -2948,6 +2976,8 @@
async function deliverBatch(tasks, message) {
let success = 0;
let failed = 0;
let unknown = 0;
const results = [];
postProgress(message, "info", `Boss Chrome批量投递开始,共 ${tasks.length} 个待确认岗位。`, {
operation: "deliver",
stage: "received",
Expand All @@ -2964,28 +2994,48 @@
keywordTotal: tasks.length,
saved: success
});
const result = await deliverOne(task, message).catch(async (error) => {
const result = await executeDeliveryOnce(task, () => deliverOne(task, message))
.then((value) => ({ ...value, persisted: true }))
.catch(async (error) => {
const failure = classifyDeliveryFailure(error.message || String(error));
await postDeliveryResult(task, false, failure).catch(() => {});
return { success: false, message: failure.failureReason, failureType: failure.failureType };
let persisted = false;
await postDeliveryResult(task, null, failure.failureReason, "NO_CONFIRMATION")
.then(() => { persisted = true; })
.catch(() => {});
return { success: false, outcome: "UNKNOWN", evidence: "NO_CONFIRMATION", persisted, message: failure.failureReason, failureType: failure.failureType };
});
if (result.success) success += 1;
const outcome = result?.outcome || (result?.success ? "CONFIRMED" : "FAILED");
if (outcome === "CONFIRMED") success += 1;
else if (outcome === "UNKNOWN") unknown += 1;
else failed += 1;
results.push({ id: task?.id, requestKey: task?.requestKey, outcome, evidence: result?.evidence || "", persisted: result?.persisted === true, message: result?.message || "" });
}
postProgress(message, failed ? "warning" : "success", `Boss批量投递完成:成功${success},失败${failed}`, {
postProgress(message, failed || unknown ? "warning" : "success", `Boss批量投递完成:已确认${success},待确认${unknown},失败${failed}`, {
operation: "deliver",
stage: "complete",
keywordTotal: tasks.length,
saved: success
});
return { success: true, message: `Boss批量投递完成:成功${success},失败${failed}`, successCount: success, failedCount: failed };
return {
success: failed === 0 && unknown === 0,
partial: success > 0 && (failed > 0 || unknown > 0),
message: `Boss批量投递完成:已确认${success},待确认${unknown},失败${failed}`,
successCount: success,
unknownCount: unknown,
failedCount: failed,
results
};
}

async function postDeliveryResult(task, success, message) {
const failure = success ? null : normalizeFailurePayload(message);
async function postDeliveryResult(task, success, message, evidence) {
const failure = success === false ? normalizeFailurePayload(message) : null;
const outcome = success === true ? "CONFIRMED" : success === false ? "FAILED" : "UNKNOWN";
await callBossLocalApi("delivery-result", {
requestKey: task.requestKey,
outcome,
evidence: evidence || (outcome === "CONFIRMED" ? "PLATFORM_STATUS_TEXT" : outcome === "FAILED" ? "PLATFORM_ERROR" : "NO_CONFIRMATION"),
success,
message: success ? message : failure.failureReason,
message: success === true ? message : failure?.failureReason || String(message || ""),
failureType: failure?.failureType,
failureReason: failure?.failureReason
}, {
Expand Down Expand Up @@ -3588,20 +3638,40 @@
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const failure = detectDeliveryFailure("");
if (failure) return { success: false, message: failure };
if (failure) return { success: false, outcome: "FAILED", evidence: "PLATFORM_ERROR", message: failure };
if (detectBossDeliveryStatus(document)) {
return { success: true, outcome: "CONFIRMED", evidence: "PLATFORM_STATUS_TEXT", message: "Boss页面已显示沟通或投递状态" };
}
if (isBossChatPage(window.location.href)) {
return { success: true, message: "已进入Boss沟通页" };
return { success: false, outcome: "UNKNOWN", evidence: "CHAT_SURFACE_ONLY", message: "已进入Boss沟通页,但未显示明确成功状态" };
}
if (findChatInput()) {
return { success: true, message: "已打开Boss聊天窗口" };
return { success: false, outcome: "UNKNOWN", evidence: "CHAT_SURFACE_ONLY", message: "已打开Boss聊天窗口,但未显示明确成功状态" };
}
const continueButton = findBossDeliverButton(["继续沟通", "已沟通"], []);
if (continueButton && (!isSameBossJobUrl(beforeUrl, window.location.href) || isSameBossJobUrl(window.location.href, task.url))) {
return { success: true, message: "Boss沟通状态已更新" };
return { success: true, outcome: "CONFIRMED", evidence: "PLATFORM_STATUS_TEXT", message: "Boss沟通状态已更新" };
}
await sleep(300);
}
return { success: false, message: detectDeliveryFailure("点击立即沟通后未出现聊天窗口或沟通页") };
const failure = detectDeliveryFailure("");
if (failure) return { success: false, outcome: "FAILED", evidence: "PLATFORM_ERROR", message: failure };
return { success: false, outcome: "UNKNOWN", evidence: "NO_CONFIRMATION", message: "点击立即沟通后未出现明确平台结果" };
}

function executeDeliveryOnce(task, action) {
const requestKey = compact(task?.requestKey || "");
if (!requestKey) {
return Promise.resolve({ success: false, outcome: "FAILED", message: "投递任务缺少 requestKey,已拒绝执行" });
}
const existing = deliveryExecutions.get(requestKey);
if (existing) return existing;
if (deliveryExecutions.size >= 200) {
deliveryExecutions.delete(deliveryExecutions.keys().next().value);
}
const execution = Promise.resolve().then(action);
deliveryExecutions.set(requestKey, execution);
return execution;
}

function detectDeliveryFailure(fallback) {
Expand Down
75 changes: 75 additions & 0 deletions chrome-extension/tests/background-tab-routing.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,81 @@ test("allows numeric Zhilian delivery result IDs and rejects invalid or unknown
assert.equal(urls.length, 1);
});

test("treats HTTP 200 business rejection as a failed local API request", async () => {
const { context } = loadBackground({
tabs: [],
fetchImpl: async () => ({
ok: true,
status: 200,
async text() { return JSON.stringify({ success: false, message: "状态已变化" }); }
})
});

const result = await context.requestLocalApi("/api/boss/jobs/1/delivery-result", {
operation: "delivery-result",
method: "POST",
body: { requestKey: "request-1", outcome: "CONFIRMED", evidence: "PLATFORM_STATUS_TEXT" }
});

assert.equal(result.success, false);
assert.equal(result.errorType, "BUSINESS_REJECTED");
assert.equal(result.message, "状态已变化");
});

test("records an empty Boss chat-page response as unknown instead of confirmed", async () => {
const requests = [];
const { context } = loadBackground({
tabs: [{ id: 7, windowId: 1, url: "https://www.zhipin.com/web/geek/chat", status: "complete" }],
fetchImpl: async (url, options) => {
requests.push({ url, body: JSON.parse(options.body) });
return { ok: true, status: 200, async text() { return '{"success":true}'; } };
}
});

const result = await context.inferBossDeliveryAfterEmptyResponse(7, {
id: 99,
requestKey: "request-99"
});

assert.equal(result.success, false);
assert.equal(result.outcome, "UNKNOWN");
assert.equal(result.persisted, true);
assert.equal(requests.length, 1);
assert.equal(requests[0].body.requestKey, "request-99");
assert.equal(requests[0].body.outcome, "UNKNOWN");
assert.equal(requests[0].body.evidence, "CHAT_SURFACE_ONLY");
});

test("does not upgrade legacy success booleans to confirmed without explicit evidence", () => {
const { context } = loadBackground({ tabs: [] });

assert.equal(context.deliveryOutcomeOf({ success: true }), "UNKNOWN");
assert.equal(context.deliveryOutcomeOf({ outcome: "CONFIRMED" }), "UNKNOWN");
assert.equal(context.deliveryOutcomeOf({
outcome: "CONFIRMED",
evidence: "PLATFORM_STATUS_TEXT"
}), "CONFIRMED");
});

test("surfaces delivery-result persistence failure instead of reporting a stored outcome", async () => {
const { context } = loadBackground({
tabs: [],
fetchImpl: async () => ({
ok: false,
status: 500,
async text() { return JSON.stringify({ success: false, message: "database unavailable" }); }
})
});

await assert.rejects(
context.recordBossDeliveryResponse(
{ id: 77, requestKey: "request-77" },
{ outcome: "UNKNOWN", evidence: "NO_CONFIRMATION", message: "result uncertain" }
),
/database unavailable/
);
});

test("rejects forged Zhilian senders before any local API request", async () => {
let fetchCalls = 0;
const { runtimeMessageListener } = loadBackground({
Expand Down
46 changes: 46 additions & 0 deletions chrome-extension/tests/delivery-state-safety.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");

const EXTENSION_DIR = path.resolve(__dirname, "..");

function source(file) {
return fs.readFileSync(path.join(EXTENSION_DIR, file), "utf8");
}

test("Boss and Zhilian callbacks carry request identity, outcome and evidence", () => {
for (const file of ["boss-content.js", "zhilian-content.js"]) {
const text = source(file);
assert.match(text, /requestKey:\s*task\.requestKey/);
assert.match(text, /outcome,/);
assert.match(text, /evidence:/);
assert.match(text, /executeDeliveryOnce\(message\.task/);
}
});

test("uncertain delivery paths remain unknown and batch responses expose per-row results", () => {
const background = source("background.js");
const boss = source("boss-content.js");
const zhilian = source("zhilian-content.js");

assert.match(background, /Boss已进入沟通页,但未收到明确平台成功状态/);
assert.doesNotMatch(background, /Boss已进入沟通页,按成功处理/);
assert.match(boss, /outcome:\s*"UNKNOWN"/);
assert.match(zhilian, /outcome:\s*"UNKNOWN"/);
for (const text of [background, boss, zhilian]) {
assert.match(text, /unknownCount:/);
assert.match(text, /results/);
assert.match(text, /persisted/);
}
});

test("delivery-result persistence is explicit so the frontend can compensate failed callbacks", () => {
const background = source("background.js");
const boss = source("boss-content.js");
const zhilian = source("zhilian-content.js");

assert.match(background, /persisted:\s*result\?\.persisted\s*===\s*true/);
assert.match(boss, /persisted:\s*false/);
assert.match(zhilian, /persisted:\s*false/);
});
Loading
Loading