feat: 任务发布到企微客户群(企业群发)与资源库单条新增
- 任务中心新增「发布到企微群」:同步客户群清单(wecom_group_chats)、 按群主分组创建企业群发任务(add_msg_template)、发送记录落库 wecom_group_pushes,迁移 mysql/0009;lib/wecom-client.ts 补 listCustomerGroupChats/createGroupMsgTemplate - KOC 资源库支持单条新增:app/api/resources-insert + lib/resource-write, 拆出资源写入公共逻辑供导入复用;0008 补合作方外部联系人字段 - 环境变量示例补企微凭证与 SEED_DEMO_DATA;next.config 增加 allowedDevOrigins;CLAUDE.md 补充项目说明 - .gitignore 排除 .codegraph/ 与 .ipynb_checkpoints/
This commit is contained in:
@@ -41,9 +41,17 @@ import {
|
||||
resolveWecomConfig,
|
||||
sendWecomAppMessage,
|
||||
sendWecomRobotMessage,
|
||||
listExternalContacts,
|
||||
WecomClientError,
|
||||
type WecomBindings,
|
||||
} from "../../../lib/wecom-client";
|
||||
import { runDueSoonWecomNotifications } from "../../../lib/wecom-notifier-service";
|
||||
import {
|
||||
listGroupChatRows,
|
||||
listGroupPushes,
|
||||
pushTaskToGroupChats,
|
||||
syncGroupChats,
|
||||
} from "../../../lib/wecom-group-push-service";
|
||||
import { isManagerRequest } from "../../../lib/user-auth";
|
||||
import { extractPublishUrl } from "../../../lib/publish-url";
|
||||
|
||||
@@ -546,21 +554,32 @@ export async function POST(request: Request) {
|
||||
const externalId = String(body.wecomExternalUserId ?? "")
|
||||
.trim()
|
||||
.slice(0, 128);
|
||||
const wecomName = String(body.wecomName ?? "").trim().slice(0, 120);
|
||||
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();
|
||||
if (wecomName) {
|
||||
await db
|
||||
.prepare(
|
||||
"UPDATE partners SET wecom_external_user_id = ?, wecom_name = ? WHERE id = ?",
|
||||
)
|
||||
.bind(externalId || null, wecomName, partnerId)
|
||||
.run();
|
||||
} else {
|
||||
await db
|
||||
.prepare(
|
||||
"UPDATE partners SET wecom_external_user_id = ? WHERE id = ?",
|
||||
)
|
||||
.bind(externalId || null, partnerId)
|
||||
.run();
|
||||
}
|
||||
return Response.json({
|
||||
partnerId,
|
||||
wecomExternalUserId: externalId || null,
|
||||
wecomName,
|
||||
});
|
||||
} else if (body.action === "send_test_wecom") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
@@ -601,6 +620,72 @@ export async function POST(request: Request) {
|
||||
robot: robotStatus,
|
||||
app: appStatus,
|
||||
});
|
||||
} else if (body.action === "wecom_status") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
return Response.json({
|
||||
robotConfigured: Boolean(wecomConfig.robotWebhook),
|
||||
appConfigured: Boolean(
|
||||
wecomConfig.corpId && wecomConfig.agentId && wecomConfig.secret,
|
||||
),
|
||||
dueDays: wecomConfig.dueDays,
|
||||
});
|
||||
} else if (body.action === "trigger_wecom_due_soon") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
const summary = await runDueSoonWecomNotifications(
|
||||
db,
|
||||
wecomConfig,
|
||||
);
|
||||
return Response.json(summary);
|
||||
} else if (body.action === "wecom_list_external_contacts") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
const contacts = await listExternalContacts(wecomConfig);
|
||||
return Response.json({ contacts });
|
||||
} else if (body.action === "wecom_sync_group_chats") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
const groups = await syncGroupChats(db, wecomConfig);
|
||||
return Response.json({ groups, syncedCount: groups.length });
|
||||
} else if (body.action === "wecom_push_task_to_groups") {
|
||||
const wecomConfig = resolveWecomConfig(
|
||||
env as unknown as WecomBindings,
|
||||
);
|
||||
const summary = await pushTaskToGroupChats(
|
||||
db,
|
||||
{
|
||||
taskId: String(body.taskId ?? ""),
|
||||
chatIds: body.chatIds,
|
||||
text: body.text,
|
||||
},
|
||||
wecomConfig,
|
||||
);
|
||||
const failCount = summary.results.reduce(
|
||||
(total, item) => total + item.failList.length,
|
||||
0,
|
||||
);
|
||||
const groupCount = summary.results.reduce(
|
||||
(total, item) => total + item.chatCount,
|
||||
0,
|
||||
);
|
||||
return Response.json({
|
||||
results: summary.results,
|
||||
groupCount,
|
||||
failCount,
|
||||
hint: "群发任务已创建,群主需在企微客户端「群发助手」点击发送后,消息才会送达客户群",
|
||||
});
|
||||
} else if (body.action === "wecom_task_group_pushes") {
|
||||
const taskId = String(body.taskId ?? "").trim();
|
||||
const pushes = await listGroupPushes(db, taskId || undefined);
|
||||
return Response.json({ pushes });
|
||||
} else if (body.action === "wecom_group_chats") {
|
||||
const groups = await listGroupChatRows(db);
|
||||
return Response.json({ groups });
|
||||
} else {
|
||||
return Response.json({ error: "不支持的操作" }, { status: 400 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user