import { isManagerRequest, managerForbidden } from "../../../lib/user-auth"; import { parseStoredDate } from "../../../lib/date-utils"; import { ensureSchema, getRawDb } from "../../../lib/mvp-db"; import { buildRecoveryWorkbook, type RecoveryWorkbookRow, } from "../../../lib/recovery-workbook"; export const runtime = "edge"; type AccountRow = { id: string; platform: string; public_account_id: string; nickname: string; profile_url: string; ip_location: string; followers: number; post_count: number; first_seen_at: string; last_seen_at: string; }; type CooperationRow = { account_id: string; partner_name: string; delegation_bundle_id: string | null; }; function formatExportDate(value: string) { const date = parseStoredDate(value); if (Number.isNaN(date.getTime())) return value; return new Intl.DateTimeFormat("zh-CN", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false, }).format(date); } function exactArrayBuffer(bytes: Uint8Array) { const copy = new Uint8Array(bytes.byteLength); copy.set(bytes); return copy.buffer; } export async function POST(request: Request) { if (!(await isManagerRequest(request))) return managerForbidden(); try { const body = (await request.json()) as { accountIds?: unknown }; if (!Array.isArray(body.accountIds)) { return Response.json({ error: "缺少需要导出的账号" }, { status: 400 }); } const accountIds = [ ...new Set( body.accountIds .filter((value): value is string => typeof value === "string") .map((value) => value.trim()) .filter(Boolean), ), ].slice(0, 5000); if (accountIds.length === 0) { return Response.json({ error: "当前筛选结果为空" }, { status: 400 }); } await ensureSchema(); const db = getRawDb(); const [accountResult, cooperationResult] = await Promise.all([ db.prepare("SELECT * FROM accounts ORDER BY last_seen_at DESC").all(), db .prepare( `SELECT d.account_id, p.name AS partner_name, d.delegation_bundle_id FROM distributions d JOIN partners p ON p.id = d.partner_id WHERE d.account_id IS NOT NULL`, ) .all(), ]); const accountIdSet = new Set(accountIds); const order = new Map(accountIds.map((id, index) => [id, index])); const accounts = accountResult.results .filter((account) => accountIdSet.has(account.id)) .sort( (left, right) => (order.get(left.id) ?? Number.MAX_SAFE_INTEGER) - (order.get(right.id) ?? Number.MAX_SAFE_INTEGER), ); if (accounts.length === 0) { return Response.json({ error: "没有找到可导出的账号" }, { status: 404 }); } const cooperationByAccount = new Map(); for (const cooperation of cooperationResult.results) { if (!accountIdSet.has(cooperation.account_id)) continue; const current = cooperationByAccount.get(cooperation.account_id) ?? []; current.push(cooperation); cooperationByAccount.set(cooperation.account_id, current); } const headers = [ "序号", "平台", "账号名称", "小红书号/抖音号", "账号主页", "IP地", "粉丝数", "合作发布数", "历史合作来源", "资源归属", "首次合作时间", "最近合作时间", ]; const rows: RecoveryWorkbookRow[] = accounts.map((account, index) => { const cooperation = cooperationByAccount.get(account.id) ?? []; const sources = [...new Set(cooperation.map((item) => item.partner_name))]; const partnerManagedOnly = cooperation.some((item) => item.delegation_bundle_id) && cooperation.every((item) => item.delegation_bundle_id); return { cells: [ index + 1, account.platform, account.nickname, account.public_account_id || "待识别", account.profile_url || "", account.ip_location || "待识别", account.followers, account.post_count, sources.join("、"), partnerManagedOnly ? "合作社资源 · 不可直联" : "可直联", formatExportDate(account.first_seen_at), formatExportDate(account.last_seen_at), ], images: [], hyperlinks: account.profile_url ? [{ column: 4, url: account.profile_url }] : [], }; }); const workbook = buildRecoveryWorkbook({ sheetName: "KOC资源库", headers, columnWidths: [ 9, 12, 22, 22, 44, 14, 14, 14, 32, 22, 21, 21, ], rows, }); const date = new Intl.DateTimeFormat("zh-CN", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", }) .format(new Date()) .replace(/\D/g, ""); const fileName = `KOC资源库-${date}.xlsx`; return new Response(exactArrayBuffer(workbook), { headers: { "Cache-Control": "private, no-store", "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Content-Disposition": `attachment; filename="koc-resources.xlsx"; filename*=UTF-8''${encodeURIComponent(fileName)}`, "X-Content-Type-Options": "nosniff", }, }); } catch (error) { return Response.json( { error: error instanceof Error ? error.message : "导出失败" }, { status: 500 }, ); } }