feat: add group repair and dispatch rejection flows
This commit is contained in:
@@ -4175,6 +4175,50 @@ export async function confirmDispatchPlan(input: {
|
||||
});
|
||||
}
|
||||
|
||||
export async function rejectDispatchPlan(input: {
|
||||
groupProjectId: string;
|
||||
planId: string;
|
||||
rejectedBy: string;
|
||||
}) {
|
||||
const result = await mutateState((state) => {
|
||||
const groupProjectId = input.groupProjectId.trim();
|
||||
if (!groupProjectId) throw new Error("PROJECT_NOT_FOUND");
|
||||
const groupProject = state.projects.find((item) => item.id === groupProjectId);
|
||||
if (!groupProject) throw new Error("PROJECT_NOT_FOUND");
|
||||
if (!groupProject.isGroup) throw new Error("PROJECT_NOT_GROUP_CHAT");
|
||||
requireDispatchActorSession(state, input.rejectedBy);
|
||||
|
||||
const plan = state.dispatchPlans.find((item) => item.planId === input.planId);
|
||||
if (!plan) throw new Error("DISPATCH_PLAN_NOT_FOUND");
|
||||
if (plan.groupProjectId !== groupProjectId) {
|
||||
throw new Error("DISPATCH_PLAN_PROJECT_MISMATCH");
|
||||
}
|
||||
if (plan.status === "dispatched") {
|
||||
throw new Error("DISPATCH_PLAN_ALREADY_DISPATCHED");
|
||||
}
|
||||
if (plan.status !== "rejected") {
|
||||
plan.status = "rejected";
|
||||
}
|
||||
groupProject.approvalState = "rejected";
|
||||
const notice =
|
||||
pushProjectLedgerMessage(state, groupProjectId, {
|
||||
sender: "master",
|
||||
senderLabel: "主 Agent",
|
||||
body: "已拒绝主 Agent 推荐,本次不会下发到任何线程。",
|
||||
kind: "system_notice",
|
||||
}) ?? null;
|
||||
|
||||
return {
|
||||
plan: { ...plan },
|
||||
notice: notice ? { ...notice } : null,
|
||||
};
|
||||
});
|
||||
|
||||
publishBossEvent("project.messages.updated", { projectId: input.groupProjectId });
|
||||
publishBossEvent("conversation.updated", { projectId: input.groupProjectId });
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function createDispatchExecutionsFromPlan(input: {
|
||||
planId: string;
|
||||
confirmedBy: string;
|
||||
@@ -6266,17 +6310,13 @@ export async function createIndependentGroupChat(input: {
|
||||
return project;
|
||||
}
|
||||
|
||||
function createGroupChatFromProjectIds(
|
||||
function resolveGroupChatThreadProjects(
|
||||
state: BossState,
|
||||
input: {
|
||||
requestedProjectIds: string[];
|
||||
createdBy: string;
|
||||
defaultRiskLevel?: Project["riskLevel"];
|
||||
},
|
||||
requestedProjectIds: string[],
|
||||
) {
|
||||
const memberProjects: Project[] = [];
|
||||
const seenProjectIds = new Set<string>();
|
||||
for (const projectId of input.requestedProjectIds) {
|
||||
for (const projectId of requestedProjectIds) {
|
||||
if (!projectId || seenProjectIds.has(projectId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -6291,6 +6331,77 @@ function createGroupChatFromProjectIds(
|
||||
}
|
||||
memberProjects.push(memberProject);
|
||||
}
|
||||
return memberProjects;
|
||||
}
|
||||
|
||||
export async function replaceGroupChatMembers(input: {
|
||||
projectId: string;
|
||||
memberProjectIds: string[];
|
||||
requestedBy: string;
|
||||
}) {
|
||||
const result = await mutateState((state) => {
|
||||
const groupProject = state.projects.find((item) => item.id === input.projectId);
|
||||
if (!groupProject) {
|
||||
throw new Error("PROJECT_NOT_FOUND");
|
||||
}
|
||||
if (!groupProject.isGroup) {
|
||||
throw new Error("PROJECT_NOT_GROUP_CHAT");
|
||||
}
|
||||
|
||||
const memberProjects = resolveGroupChatThreadProjects(state, input.memberProjectIds);
|
||||
if (memberProjects.length < 2) {
|
||||
throw new Error("GROUP_CHAT_REQUIRES_AT_LEAST_TWO_THREADS");
|
||||
}
|
||||
|
||||
const now = nowIso();
|
||||
groupProject.groupMembers = memberProjects.map((memberProject) => ({
|
||||
projectId: memberProject.id,
|
||||
deviceId: memberProject.deviceIds[0] ?? memberProject.id,
|
||||
threadId: memberProject.threadMeta.threadId,
|
||||
threadDisplayName: memberProject.threadMeta.threadDisplayName,
|
||||
folderName: memberProject.threadMeta.folderName,
|
||||
}));
|
||||
groupProject.deviceIds = dedupeStrings(groupProject.groupMembers.map((member) => member.deviceId));
|
||||
groupProject.threadMeta.activityIconCount = Math.max(1, groupProject.groupMembers.length);
|
||||
groupProject.threadMeta.folderName = "群聊";
|
||||
groupProject.threadMeta.updatedAt = now;
|
||||
groupProject.updatedAt = now;
|
||||
groupProject.lastMessageAt = now;
|
||||
groupProject.approvalState = "not_required";
|
||||
const memberLabel = memberProjects
|
||||
.map((project) => project.threadMeta.threadDisplayName || project.name)
|
||||
.join("、");
|
||||
pushProjectLedgerMessage(state, groupProject.id, {
|
||||
sender: "master",
|
||||
senderLabel: "主 Agent",
|
||||
body: `已更新群成员:${memberLabel}`,
|
||||
kind: "system_notice",
|
||||
sentAt: now,
|
||||
});
|
||||
|
||||
return {
|
||||
project: { ...groupProject },
|
||||
groupMembers: groupProject.groupMembers.map((member) => ({ ...member })),
|
||||
};
|
||||
});
|
||||
|
||||
publishBossEvent("project.messages.updated", { projectId: input.projectId });
|
||||
publishBossEvent("conversation.updated", {
|
||||
projectId: input.projectId,
|
||||
note: `group members updated by ${input.requestedBy}`,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function createGroupChatFromProjectIds(
|
||||
state: BossState,
|
||||
input: {
|
||||
requestedProjectIds: string[];
|
||||
createdBy: string;
|
||||
defaultRiskLevel?: Project["riskLevel"];
|
||||
},
|
||||
) {
|
||||
const memberProjects = resolveGroupChatThreadProjects(state, input.requestedProjectIds);
|
||||
if (memberProjects.length < 2) {
|
||||
throw new Error("GROUP_CHAT_REQUIRES_AT_LEAST_TWO_THREADS");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user