- 项目总群:≥3 人 appchat/create 自动建群(chatid=pgc+projectId),成员 diff 增删同步;DROP project_group_chats;推送改 appchat/send 群发、无群降级逐人 - 任务负责人:tasks.owner_userid(新建/编辑任务通讯录选人),task_set_owner 分配即预建 tgc 任务群发介绍,群主链接只归属负责人 - 任务编辑 task_update:基础字段+截图任务数量/关键词/说明,contents 调平只删 available 尾行 - 删除「一键推送」:task-one-click-push-service 整体移除(recordCardAck 移入 wecom-callback-events) - 负责人后台视角:users.wecom_userid 绑定,lib/owner-scope 行级过滤(undefined=全量/""=空范围/非空=过滤),~30 action 补 managerGate/taskOwnerGate,owner 登录仅见工作台/任务中心/数据回收/复盘报表 - 任务绑定客户群+进群活码:tasks 增 customer_chat_id/name、join_config_id、join_qr_code 4 列,join_way add/update/get/del(换绑走 update 保 config_id 活码不变),后台「客户群」弹窗,portal 认领后扫码自助进群(满员自动开新群) - 迁移 0013/0014/0015;催办支持按任务;企微可信域名 IP 验证文件 - 288 测试全绿;真机全链路验收通过(列群/活码四端点/portal 渲染/解绑清空)
128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
import type { DatabaseClient } from "./database";
|
||
import {
|
||
addTaskJoinWay,
|
||
deleteJoinWay,
|
||
getJoinWayQrCode,
|
||
hasAppCredentials,
|
||
listCustomerGroupChats,
|
||
updateTaskJoinWay,
|
||
type WecomConfig,
|
||
type WecomGroupChat,
|
||
} from "./wecom-client";
|
||
|
||
type FetchLike = typeof fetch;
|
||
|
||
function text(value: unknown) {
|
||
return String(value ?? "").trim();
|
||
}
|
||
|
||
export async function listOwnerCustomerChats(
|
||
config: WecomConfig,
|
||
ownerUserid: string,
|
||
fetchImpl: FetchLike = fetch,
|
||
): Promise<WecomGroupChat[]> {
|
||
if (!hasAppCredentials(config)) {
|
||
throw new Error("企业微信自建应用未配置(WECOM_CORP_ID / WECOM_AGENT_ID / WECOM_SECRET)");
|
||
}
|
||
const owner = text(ownerUserid);
|
||
if (!owner) return [];
|
||
return listCustomerGroupChats(config, fetchImpl, [owner]);
|
||
}
|
||
|
||
export type BindTaskCustomerChatResult = {
|
||
customerChatId: string;
|
||
customerChatName: string;
|
||
joinConfigId: string;
|
||
joinQrCode: string;
|
||
qrError?: string;
|
||
};
|
||
|
||
export async function bindTaskCustomerChat(
|
||
db: DatabaseClient,
|
||
params: { taskId: string; chatId: string; chatName: string },
|
||
config: WecomConfig,
|
||
fetchImpl: FetchLike = fetch,
|
||
): Promise<BindTaskCustomerChatResult> {
|
||
const taskId = text(params.taskId);
|
||
const chatId = text(params.chatId);
|
||
const chatName = text(params.chatName).slice(0, 255);
|
||
if (!taskId) throw new Error("缺少 taskId");
|
||
if (!chatId) throw new Error("请选择要绑定的客户群");
|
||
|
||
const task = await db
|
||
.prepare("SELECT id, name, join_config_id FROM tasks WHERE id = ?")
|
||
.bind(taskId)
|
||
.first<{ id: string; name: string; join_config_id: string | null }>();
|
||
if (!task) throw new Error("任务不存在");
|
||
|
||
const joinInput = {
|
||
chatIdList: [chatId],
|
||
roomBaseName: task.name,
|
||
state: `task-${taskId}`,
|
||
};
|
||
|
||
let configId = text(task.join_config_id);
|
||
if (configId) {
|
||
try {
|
||
await updateTaskJoinWay(config, { ...joinInput, configId }, fetchImpl);
|
||
} catch {
|
||
// 活码配置可能已被企微侧清理,降级为重新创建
|
||
configId = (await addTaskJoinWay(config, joinInput, fetchImpl)).configId;
|
||
}
|
||
} else {
|
||
configId = (await addTaskJoinWay(config, joinInput, fetchImpl)).configId;
|
||
}
|
||
|
||
let joinQrCode = "";
|
||
let qrError: string | undefined;
|
||
try {
|
||
joinQrCode = (await getJoinWayQrCode(config, configId, fetchImpl)).qrCode;
|
||
} catch (error) {
|
||
qrError = `活码二维码获取失败:${error instanceof Error ? error.message : String(error)}`;
|
||
}
|
||
|
||
await db
|
||
.prepare(
|
||
`UPDATE tasks
|
||
SET customer_chat_id = ?, customer_chat_name = ?, join_config_id = ?, join_qr_code = ?
|
||
WHERE id = ?`,
|
||
)
|
||
.bind(chatId, chatName, configId, joinQrCode, taskId)
|
||
.run();
|
||
|
||
return {
|
||
customerChatId: chatId,
|
||
customerChatName: chatName,
|
||
joinConfigId: configId,
|
||
joinQrCode,
|
||
qrError,
|
||
};
|
||
}
|
||
|
||
export async function unbindTaskCustomerChat(
|
||
db: DatabaseClient,
|
||
taskId: string,
|
||
config: WecomConfig,
|
||
fetchImpl: FetchLike = fetch,
|
||
): Promise<void> {
|
||
const id = text(taskId);
|
||
if (!id) throw new Error("缺少 taskId");
|
||
const task = await db
|
||
.prepare("SELECT join_config_id FROM tasks WHERE id = ?")
|
||
.bind(id)
|
||
.first<{ join_config_id: string | null }>();
|
||
if (!task) throw new Error("任务不存在");
|
||
const configId = text(task.join_config_id);
|
||
if (configId) {
|
||
await deleteJoinWay(config, configId, fetchImpl);
|
||
}
|
||
await db
|
||
.prepare(
|
||
`UPDATE tasks
|
||
SET customer_chat_id = '', customer_chat_name = '', join_config_id = '', join_qr_code = ''
|
||
WHERE id = ?`,
|
||
)
|
||
.bind(id)
|
||
.run();
|
||
}
|