feat: 接入企业微信通知(临期催办 + 群机器人汇总)
新增 lib/wecom-client.ts 与 lib/wecom-notifier-service.ts,支持 群机器人 webhook 与应用消息双通道;scheduler 加入临期 N 天催办 与每日管理员汇总;action 路由补 bind_wecom_external_id 与 send_test_wecom 两个管理端动作,配套测试。
This commit is contained in:
@@ -37,6 +37,13 @@ import {
|
||||
DistributionReleaseError,
|
||||
releaseUnfinishedDistribution,
|
||||
} from "../../../lib/distribution-release-service";
|
||||
import {
|
||||
resolveWecomConfig,
|
||||
sendWecomAppMessage,
|
||||
sendWecomRobotMessage,
|
||||
WecomClientError,
|
||||
type WecomBindings,
|
||||
} from "../../../lib/wecom-client";
|
||||
import { isManagerRequest } from "../../../lib/user-auth";
|
||||
import { extractPublishUrl } from "../../../lib/publish-url";
|
||||
|
||||
@@ -534,6 +541,66 @@ export async function POST(request: Request) {
|
||||
)
|
||||
.bind(exposure, views, distributionId)
|
||||
.run();
|
||||
} else if (body.action === "bind_wecom_external_id") {
|
||||
const partnerId = String(body.partnerId ?? "").trim().slice(0, 80);
|
||||
const externalId = String(body.wecomExternalUserId ?? "")
|
||||
.trim()
|
||||
.slice(0, 128);
|
||||
if (!partnerId) {
|
||||
return Response.json(
|
||||
{ error: "缺少 partnerId" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
await db
|
||||
.prepare(
|
||||
"UPDATE partners SET wecom_external_user_id = ? WHERE id = ?",
|
||||
)
|
||||
.bind(externalId || null, partnerId)
|
||||
.run();
|
||||
return Response.json({
|
||||
partnerId,
|
||||
wecomExternalUserId: externalId || null,
|
||||
});
|
||||
} else if (body.action === "send_test_wecom") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
const partnerId = String(body.partnerId ?? "").trim();
|
||||
let partnerExternalId: string | null = null;
|
||||
if (partnerId) {
|
||||
const row = await db
|
||||
.prepare(
|
||||
"SELECT wecom_external_user_id FROM partners WHERE id = ?",
|
||||
)
|
||||
.bind(partnerId)
|
||||
.first<{ wecom_external_user_id: string | null }>();
|
||||
partnerExternalId = row?.wecom_external_user_id ?? null;
|
||||
}
|
||||
const testContent = `[KOC LOOP 测试] 群机器人连通性正常,时间 ${new Date().toISOString()}`;
|
||||
let robotStatus: "ok" | "skipped" = "skipped";
|
||||
if (wecomConfig.robotWebhook) {
|
||||
await sendWecomRobotMessage(testContent, wecomConfig);
|
||||
robotStatus = "ok";
|
||||
}
|
||||
let appStatus: "ok" | "skipped" | "failed" = "skipped";
|
||||
if (
|
||||
partnerExternalId &&
|
||||
wecomConfig.corpId &&
|
||||
wecomConfig.agentId &&
|
||||
wecomConfig.secret
|
||||
) {
|
||||
const result = await sendWecomAppMessage(
|
||||
[partnerExternalId],
|
||||
testContent,
|
||||
wecomConfig,
|
||||
);
|
||||
appStatus = result.failed > 0 ? "failed" : "ok";
|
||||
}
|
||||
return Response.json({
|
||||
robot: robotStatus,
|
||||
app: appStatus,
|
||||
});
|
||||
} else {
|
||||
return Response.json({ error: "不支持的操作" }, { status: 400 });
|
||||
}
|
||||
@@ -545,7 +612,8 @@ export async function POST(request: Request) {
|
||||
{
|
||||
status:
|
||||
error instanceof FeishuSourceError ||
|
||||
error instanceof DistributionReleaseError
|
||||
error instanceof DistributionReleaseError ||
|
||||
error instanceof WecomClientError
|
||||
? error.status
|
||||
: 500,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user