feat: 数据回流通知(提交/采集结果推运营群,默认关闭)
This commit is contained in:
@@ -27,6 +27,7 @@ export type RuntimeEnv = {
|
||||
WECOM_CALLBACK_TOKEN?: string;
|
||||
WECOM_CALLBACK_ENCODING_AES_KEY?: string;
|
||||
WECOM_CALLBACK_ENABLED?: string;
|
||||
WECOM_FEEDBACK_ENABLED?: string;
|
||||
SEED_DEMO_DATA?: string;
|
||||
ENABLE_SCHEDULER?: string;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,10 @@ import {
|
||||
type WecomBindings,
|
||||
} from "./wecom-client";
|
||||
import { runDueSoonWecomNotifications } from "./wecom-notifier-service";
|
||||
import {
|
||||
isWecomFeedbackEnabled,
|
||||
notifyCollectionFinished,
|
||||
} from "./wecom-feedback-service";
|
||||
|
||||
declare global {
|
||||
var __kocLoopScheduler: ScheduledTask | undefined;
|
||||
@@ -28,19 +32,25 @@ async function runDailyJob() {
|
||||
);
|
||||
const collections = await runScheduledCollections(db, Date.now(), config);
|
||||
const accounts = await backfillAccountProfiles(db, config, 10);
|
||||
const wecomConfig = resolveWecomConfig(env as unknown as WecomBindings);
|
||||
let wecom: Awaited<ReturnType<typeof runDueSoonWecomNotifications>> | null =
|
||||
null;
|
||||
if (isEnabled(env.WECOM_NOTIFY_ENABLED, true)) {
|
||||
const wecomConfig = resolveWecomConfig(env as unknown as WecomBindings);
|
||||
if (
|
||||
wecomConfig.robotWebhook ||
|
||||
(wecomConfig.corpId && wecomConfig.agentId && wecomConfig.secret)
|
||||
) {
|
||||
try {
|
||||
wecom = await runDueSoonWecomNotifications(db, wecomConfig);
|
||||
} catch (error) {
|
||||
console.error("[KOC LOOP] wecom notify failed", error);
|
||||
}
|
||||
if (
|
||||
isEnabled(env.WECOM_NOTIFY_ENABLED, true) &&
|
||||
(wecomConfig.robotWebhook ||
|
||||
(wecomConfig.corpId && wecomConfig.agentId && wecomConfig.secret))
|
||||
) {
|
||||
try {
|
||||
wecom = await runDueSoonWecomNotifications(db, wecomConfig);
|
||||
} catch (error) {
|
||||
console.error("[KOC LOOP] wecom notify failed", error);
|
||||
}
|
||||
}
|
||||
if (isWecomFeedbackEnabled()) {
|
||||
try {
|
||||
await notifyCollectionFinished(collections, wecomConfig);
|
||||
} catch (error) {
|
||||
console.error("[KOC LOOP] wecom collection feedback failed", error);
|
||||
}
|
||||
}
|
||||
console.info("[KOC LOOP] daily scheduler completed", {
|
||||
|
||||
107
lib/wecom-feedback-service.ts
Normal file
107
lib/wecom-feedback-service.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type { DatabaseClient } from "./database";
|
||||
import { getRuntimeEnv, isEnabled } from "./runtime-env";
|
||||
import { sendWecomRobotMessage, type WecomConfig } from "./wecom-client";
|
||||
|
||||
type FetchLike = typeof fetch;
|
||||
|
||||
export function isWecomFeedbackEnabled() {
|
||||
return isEnabled(getRuntimeEnv().WECOM_FEEDBACK_ENABLED, false);
|
||||
}
|
||||
|
||||
type SubmittedRow = {
|
||||
claimant_name: string | null;
|
||||
partner_name: string | null;
|
||||
task_name: string;
|
||||
content_title: string | null;
|
||||
publish_url: string | null;
|
||||
};
|
||||
|
||||
function truncate(value: string, max: number) {
|
||||
return value.length > max ? `${value.slice(0, max)}…` : value;
|
||||
}
|
||||
|
||||
export function buildPublishSubmittedMessage(
|
||||
row: SubmittedRow,
|
||||
kind: "publish" | "screenshot",
|
||||
): string {
|
||||
const partner = row.partner_name || row.claimant_name || "未知合作方";
|
||||
if (kind === "screenshot") {
|
||||
return [
|
||||
"【KOC 已提交截图】",
|
||||
`合作方:${partner}`,
|
||||
`任务:${truncate(row.task_name, 24)}`,
|
||||
`内容:${truncate(row.content_title ?? "", 24)}`,
|
||||
"请到后台「数据回收」页查看与审核。",
|
||||
].join("\n");
|
||||
}
|
||||
return [
|
||||
"【KOC 已提交发布】",
|
||||
`合作方:${partner}`,
|
||||
`任务:${truncate(row.task_name, 24)}`,
|
||||
`内容:${truncate(row.content_title ?? "", 24)}`,
|
||||
`链接:${row.publish_url ?? ""}`,
|
||||
"链接已入库,采集计划将按日自动执行。",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export async function notifyPartnerSubmitted(
|
||||
db: DatabaseClient,
|
||||
params: { distributionId: string; kind: "publish" | "screenshot" },
|
||||
config: WecomConfig,
|
||||
fetchImpl: FetchLike = fetch,
|
||||
): Promise<boolean> {
|
||||
if (!config.robotWebhook) return false;
|
||||
const distributionId = String(params.distributionId ?? "").trim();
|
||||
if (!distributionId) return false;
|
||||
const row = await db
|
||||
.prepare(
|
||||
`SELECT d.claimant_name, d.publish_url, t.name AS task_name,
|
||||
c.title AS content_title, p.name AS partner_name
|
||||
FROM distributions d
|
||||
JOIN tasks t ON t.id = d.task_id
|
||||
LEFT JOIN contents c ON c.id = d.content_id
|
||||
LEFT JOIN partners p ON p.id = d.partner_id
|
||||
WHERE d.id = ?`,
|
||||
)
|
||||
.bind(distributionId)
|
||||
.first<SubmittedRow>();
|
||||
if (!row) return false;
|
||||
const message = buildPublishSubmittedMessage(row, params.kind);
|
||||
await sendWecomRobotMessage(message, config, fetchImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function buildCollectionFinishedMessage(summary: {
|
||||
currentDate: string;
|
||||
dueTasks: number;
|
||||
attempted: number;
|
||||
succeeded: number;
|
||||
failed: number;
|
||||
}): string {
|
||||
const lines = [
|
||||
`【数据采集日报】${summary.currentDate}`,
|
||||
`应采集任务 ${summary.dueTasks} 个,执行 ${summary.attempted} 条:成功 ${summary.succeeded}、失败 ${summary.failed}。`,
|
||||
];
|
||||
if (summary.failed > 0) {
|
||||
lines.push("有采集失败,请到后台「数据回收」页查看并重试。");
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export async function notifyCollectionFinished(
|
||||
summary: {
|
||||
currentDate: string;
|
||||
dueTasks: number;
|
||||
attempted: number;
|
||||
succeeded: number;
|
||||
failed: number;
|
||||
},
|
||||
config: WecomConfig,
|
||||
fetchImpl: FetchLike = fetch,
|
||||
): Promise<boolean> {
|
||||
if (!config.robotWebhook) return false;
|
||||
if (summary.attempted <= 0 && summary.failed <= 0) return false;
|
||||
const message = buildCollectionFinishedMessage(summary);
|
||||
await sendWecomRobotMessage(message, config, fetchImpl);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user