feat: 项目负责人+内部任务群+portal 内部身份领取+群主链接归属
- 迁移 0012:projects.owner_userid / tasks.task_chat_id / claims.owner_partner_id - lib/task-group-service.ts:懒建 appchat 任务群(首个内部群主领取时创建,成员 diff 补员),播报/卡片文案 - wecom-client 新增 appchat create/update/get/send 封装 - OAuth ticket 增可选 uid(v:1 兼容),内部成员领取按 wecom_userid 解析群主身份 - 外部 OAuth 领取人 upsert 打 partner_type='group_owner' - portal 链接带 ?owner= 穿透 OAuth 往返,claims 记录归属群主 - 后台任务卡「群主链接」面板:复制链接/推送内部群主/任务群状态 - 催办与超期升级同步播报进任务群
This commit is contained in:
@@ -731,6 +731,7 @@ export async function POST(request: Request) {
|
||||
name: body.name,
|
||||
brand: body.brand,
|
||||
mainGroupMemberIds: body.mainGroupMemberIds,
|
||||
ownerUserid: body.ownerUserid,
|
||||
note: body.note,
|
||||
}),
|
||||
});
|
||||
@@ -814,6 +815,85 @@ export async function POST(request: Request) {
|
||||
return Response.json({ refresh: status });
|
||||
} else if (body.action === "account_field_refresh_status") {
|
||||
return Response.json({ refresh: getAccountFieldRefreshStatus() });
|
||||
} else if (body.action === "task_group_links") {
|
||||
const taskId = String(body.taskId ?? "").trim();
|
||||
const task = await db
|
||||
.prepare(
|
||||
"SELECT id, name, share_token, task_chat_id FROM tasks WHERE id = ?",
|
||||
)
|
||||
.bind(taskId)
|
||||
.first<{
|
||||
id: string;
|
||||
name: string;
|
||||
share_token: string | null;
|
||||
task_chat_id: string;
|
||||
}>();
|
||||
if (!task) return Response.json({ error: "任务不存在" }, { status: 404 });
|
||||
const portalUrl = String(env.KOC_PORTAL_URL ?? "").replace(/\/$/, "");
|
||||
const owners = await listGroupOwnerPartners(db);
|
||||
return Response.json({
|
||||
taskName: task.name,
|
||||
taskChatId: task.task_chat_id ?? "",
|
||||
claimUrl: task.share_token
|
||||
? `${portalUrl}/?task=${task.share_token}`
|
||||
: "",
|
||||
owners: task.share_token
|
||||
? owners.map((owner) => ({
|
||||
partnerId: owner.id,
|
||||
name: owner.name,
|
||||
wecomUserid: owner.wecom_userid,
|
||||
isInternal: Boolean(owner.wecom_userid),
|
||||
link: `${portalUrl}/?task=${task.share_token}&owner=${owner.id}`,
|
||||
}))
|
||||
: [],
|
||||
});
|
||||
} else if (body.action === "task_group_push") {
|
||||
const taskId = String(body.taskId ?? "").trim();
|
||||
const task = await db
|
||||
.prepare(
|
||||
"SELECT id, name, share_token FROM tasks WHERE id = ?",
|
||||
)
|
||||
.bind(taskId)
|
||||
.first<{ id: string; name: string; share_token: string | null }>();
|
||||
if (!task) return Response.json({ error: "任务不存在" }, { status: 404 });
|
||||
if (!task.share_token) {
|
||||
return Response.json({ error: "任务缺少领取链接" }, { status: 400 });
|
||||
}
|
||||
const owners = (await listGroupOwnerPartners(db)).filter(
|
||||
(owner) => owner.wecom_userid,
|
||||
);
|
||||
if (owners.length === 0) {
|
||||
return Response.json(
|
||||
{ error: "没有可推送的内部群主,请先在后台登记群主 userid" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
const portalUrl = String(env.KOC_PORTAL_URL ?? "").replace(/\/$/, "");
|
||||
const wecomConfig = resolveWecomConfig(env as unknown as WecomBindings);
|
||||
let sent = 0;
|
||||
let failed = 0;
|
||||
const failList: string[] = [];
|
||||
for (const owner of owners) {
|
||||
const link = `${portalUrl}/?task=${task.share_token}&owner=${owner.id}`;
|
||||
try {
|
||||
const result = await sendWecomAppMessage(
|
||||
[owner.wecom_userid],
|
||||
`【任务派发】${task.name}\n请把领取链接转发给你的 KOC(链接已绑定你的群主身份):\n${link}`,
|
||||
wecomConfig,
|
||||
);
|
||||
if (result.skipped) {
|
||||
failed += 1;
|
||||
failList.push(owner.name);
|
||||
} else {
|
||||
sent += result.sent;
|
||||
failed += result.failed;
|
||||
}
|
||||
} catch {
|
||||
failed += 1;
|
||||
failList.push(owner.name);
|
||||
}
|
||||
}
|
||||
return Response.json({ sent, failed, failList });
|
||||
} else if (body.action === "wecom_group_chats") {
|
||||
const groups = await listGroupChatRows(db);
|
||||
return Response.json({ groups });
|
||||
|
||||
Reference in New Issue
Block a user