Files
koc-loop/tests/pool-partner-default.test.mjs
ABAPPLO 87f4d4066d feat: KOC详情弹窗 + 任务中心单入口改版 + 资源池默认合作方/采集兜底
KOC 详情弹窗(合作方/数据回收/工作台三处入口):
- 新增 KocDetailModal:基本信息 / 绑定KOC账号画像 / 参与任务记录 / 履约漏斗
- 工作台最近分发、回收队列账号名、截图回收合作方名、合作方页名称、分发详情头部合作方均可点开
- 分发表行点击仍打开分发详情(stopPropagation 隔离)

任务中心单入口改版(对齐"单任务双阶段"模型):
- 任务卡发布/回收面板整块可点,全卡仅保留一个「查看明细」,回收面板重复按钮移除
- 详情页自述身份:动态 label(任务详情·分发与回收一体/仅分发)+ 阶段徽章(含回收·截止/未开通回收)
- 内容分发表新增阶段筛选 chips(全部/待发布/已发布待数据/待收截图,未开通回收不显示截图项),筛选落空给专用空态文案
- 数据回收页入口文案改为「选择一个任务进入回收队列」,全站不再区分发布/回收两种任务类型

资源池 KOC 默认加入合作方 + 采集兜底:
- partners 新增 released_account_id(mysql/0023):显式解绑/被顶替的账号不再被默认合作方同步回填
- 任务未设置采集计划时,默认按发布时间起连续采集 7 天,等待文案显示「等待D1 · 默认7天」
2026-10-12 11:11:31 +08:00

127 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
const resourceWrite = readFileSync(
new URL("../lib/resource-write.ts", import.meta.url),
"utf8",
);
const bootstrapRoute = readFileSync(
new URL("../app/api/bootstrap/route.ts", import.meta.url),
"utf8",
);
const resourcesImport = readFileSync(
new URL("../app/api/resources-import/route.ts", import.meta.url),
"utf8",
);
const resourcesInsert = readFileSync(
new URL("../app/api/resources-insert/route.ts", import.meta.url),
"utf8",
);
const actionRoute = readFileSync(
new URL("../app/api/action/route.ts", import.meta.url),
"utf8",
);
const migration = readFileSync(
new URL("../mysql/0023_partner_released_account.sql", import.meta.url),
"utf8",
);
function section(source, action) {
const start = source.indexOf(`"${action}"`);
assert.ok(start >= 0, `action ${action} 不存在于路由源码`);
const next = source.indexOf("} else if (body.action ===", start);
return source.slice(start, next === -1 ? undefined : next);
}
test("ensurePartnersForPoolAccounts 为资源池未绑定账号创建默认合作方", () => {
assert.match(resourceWrite, /export async function ensurePartnersForPoolAccounts/);
assert.match(
resourceWrite,
/NOT EXISTS \(SELECT 1 FROM partners p WHERE p\.account_id = a\.id\)/,
"只为尚未绑定合作方的资源账号建档",
);
assert.match(
resourceWrite,
/INSERT OR IGNORE INTO partners/,
"并发同步幂等:主键冲突直接忽略",
);
assert.match(resourceWrite, /'external_koc'/, "默认合作方为外部KOC类型");
assert.match(
resourceWrite,
/partner-koc-\$\{hashText/,
"合作方 id 由账号身份派生,与领取流程的前缀约定一致",
);
});
test("默认合作方同步尊重运营显式解绑(released_account_id 不回填)", () => {
assert.match(
resourceWrite,
/NOT EXISTS \(\s*SELECT 1 FROM partners r WHERE r\.released_account_id = a\.id\s*\)/,
"被显式解绑/顶替过的资源账号不再自动加入合作方",
);
assert.match(migration, /ADD COLUMN `released_account_id`/);
assert.match(migration, /partners_released_account_idx/);
});
test("空壳默认合作方的名称随昵称补全刷新且不碰有业绩合作方", () => {
assert.match(resourceWrite, /async function refreshPoolPartnerNames/);
assert.match(
resourceWrite,
/NOT EXISTS \(SELECT 1 FROM claims c WHERE c\.partner_id = p\.id\)/,
"有领取记录的合作方不刷新名称",
);
assert.match(
resourceWrite,
/NOT EXISTS \(SELECT 1 FROM distributions d WHERE d\.partner_id = p\.id\)/,
"有分发记录的合作方不刷新名称",
);
assert.match(
resourceWrite,
/p\.wecom_external_user_id IS NULL OR p\.wecom_external_user_id = ''/,
"已绑企微的合作方不刷新名称",
);
});
test("bootstrap 在返回面板数据前同步默认合作方", () => {
const syncIndex = bootstrapRoute.indexOf("await ensurePartnersForPoolAccounts");
const dashboardIndex = bootstrapRoute.indexOf("await getDashboardData(");
assert.ok(syncIndex >= 0, "bootstrap 需要调用默认合作方同步");
assert.ok(
dashboardIndex > syncIndex,
"同步必须发生在 getDashboardData 之前,首次加载即可见",
);
});
test("资源导入与手动新增后都会触发默认合作方同步", () => {
assert.match(
resourcesImport,
/runInBackground\(\s*ensurePartnersForPoolAccounts\(\)/,
"批量导入 commit 后同步",
);
assert.match(
resourcesInsert,
/runInBackground\(\s*ensurePartnersForPoolAccounts\(\)/,
"手动新增后同步",
);
});
test("partner_bind_account 换绑时可顶替空壳占用方", () => {
const body = section(actionRoute, "partner_bind_account");
assert.match(body, /isShellAccountHolder/, "占用方为空壳时判断是否可顶替");
assert.match(
body,
/UPDATE partners SET account_id = \?, released_account_id = '' WHERE id = \?/,
"绑定目标时清掉自身的释放标记",
);
const helper = actionRoute.slice(
actionRoute.indexOf("async function isShellAccountHolder"),
);
assert.match(helper, /SELECT 1 FROM claims WHERE partner_id = \? LIMIT 1/);
assert.match(
helper,
/SELECT 1 FROM distributions WHERE partner_id = \? LIMIT 1/,
"有领取或分发记录的占用方不可顶替,仍走人工解绑",
);
});