fix: restore safe top actions and home group chat entry

This commit is contained in:
kris
2026-03-29 18:44:53 +08:00
parent e9ab62e94d
commit c6e8d19ee5
22 changed files with 467 additions and 127 deletions

View File

@@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import { createIndependentGroupChat } from "@/lib/boss-data";
export async function POST(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const body = (await request.json()) as {
memberProjectIds?: string[];
};
try {
const project = await createIndependentGroupChat({
memberProjectIds: Array.isArray(body.memberProjectIds)
? body.memberProjectIds.filter((memberProjectId) => typeof memberProjectId === "string")
: [],
createdBy: session.account,
});
return NextResponse.json({ ok: true, project });
} catch (error) {
return NextResponse.json(
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
{ status: 400 },
);
}
}

View File

@@ -4411,84 +4411,129 @@ export async function createProjectGroupChat(input: {
const project = await mutateState((state) => {
const source = state.projects.find((item) => item.id === input.sourceProjectId);
if (!source) throw new Error("GROUP_CHAT_SOURCE_NOT_FOUND");
const requestedProjectIds = [input.sourceProjectId, ...input.memberProjectIds];
const memberProjects: Project[] = [];
const seenProjectIds = new Set<string>();
for (const projectId of requestedProjectIds) {
if (seenProjectIds.has(projectId)) {
continue;
}
seenProjectIds.add(projectId);
const memberProject = state.projects.find((item) => item.id === projectId);
if (!memberProject) {
throw new Error("GROUP_CHAT_MEMBER_NOT_FOUND");
}
memberProjects.push(memberProject);
}
if (memberProjects.length < 2) {
throw new Error("GROUP_CHAT_REQUIRES_AT_LEAST_TWO_THREADS");
}
const now = nowIso();
const projectId = randomToken("project");
const threadId = randomToken("thread");
const threadDisplayName = source.threadMeta.threadDisplayName ?? source.name;
const folderName = source.threadMeta.folderName ?? (source.isGroup ? "群聊" : source.name);
const 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,
}));
const nextProject = normalizeProject({
id: projectId,
name: threadDisplayName,
pinned: false,
systemPinned: false,
deviceIds: dedupeStrings(groupMembers.map((member) => member.deviceId)),
preview: `已创建群聊《${threadDisplayName}`,
updatedAt: now,
lastMessageAt: now,
isGroup: true,
unreadCount: 0,
riskLevel: source.riskLevel,
threadMeta: {
projectId,
threadId,
threadDisplayName,
folderName,
activityIconCount: Math.max(1, memberProjects.length),
updatedAt: now,
},
groupMembers,
createdByAgent: true,
collaborationMode: "development",
approvalState: "not_required",
messages: [
{
id: randomToken("msg"),
sender: "master",
senderLabel: input.createdBy || "群聊创建",
body: `已由 ${input.createdBy || "系统"} 创建群聊《${threadDisplayName}》。`,
sentAt: now,
kind: "text",
},
],
goals: [],
versions: [],
return createGroupChatFromProjectIds(state, {
requestedProjectIds: [input.sourceProjectId, ...input.memberProjectIds],
createdBy: input.createdBy,
defaultRiskLevel: source.riskLevel,
});
state.projects.unshift(nextProject);
return nextProject;
});
publishBossEvent("project.messages.updated", { projectId: project.id });
publishBossEvent("conversation.updated", { projectId: project.id });
return project;
}
export async function createIndependentGroupChat(input: {
memberProjectIds: string[];
createdBy: string;
}) {
const project = await mutateState((state) =>
createGroupChatFromProjectIds(state, {
requestedProjectIds: input.memberProjectIds,
createdBy: input.createdBy,
}),
);
publishBossEvent("project.messages.updated", { projectId: project.id });
publishBossEvent("conversation.updated", { projectId: project.id });
return project;
}
function createGroupChatFromProjectIds(
state: BossState,
input: {
requestedProjectIds: string[];
createdBy: string;
defaultRiskLevel?: Project["riskLevel"];
},
) {
const memberProjects: Project[] = [];
const seenProjectIds = new Set<string>();
for (const projectId of input.requestedProjectIds) {
if (!projectId || seenProjectIds.has(projectId)) {
continue;
}
seenProjectIds.add(projectId);
const memberProject = state.projects.find((item) => item.id === projectId);
if (!memberProject) {
throw new Error("GROUP_CHAT_MEMBER_NOT_FOUND");
}
memberProjects.push(memberProject);
}
if (memberProjects.length < 2) {
throw new Error("GROUP_CHAT_REQUIRES_AT_LEAST_TWO_THREADS");
}
const now = nowIso();
const projectId = randomToken("project");
const threadId = randomToken("thread");
const threadDisplayName = buildAutoGroupChatName(memberProjects);
const folderName = "群聊";
const 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,
}));
const seedProject = memberProjects[0];
const nextProject = normalizeProject({
id: projectId,
name: threadDisplayName,
pinned: false,
systemPinned: false,
deviceIds: dedupeStrings(groupMembers.map((member) => member.deviceId)),
preview: `已创建群聊《${threadDisplayName}`,
updatedAt: now,
lastMessageAt: now,
isGroup: true,
unreadCount: 0,
riskLevel: input.defaultRiskLevel ?? seedProject?.riskLevel ?? "normal",
threadMeta: {
projectId,
threadId,
threadDisplayName,
folderName,
activityIconCount: Math.max(1, memberProjects.length),
updatedAt: now,
},
groupMembers,
createdByAgent: true,
collaborationMode: "development",
approvalState: "not_required",
messages: [
{
id: randomToken("msg"),
sender: "master",
senderLabel: input.createdBy || "群聊创建",
body: `已由 ${input.createdBy || "系统"} 创建群聊《${threadDisplayName}》。`,
sentAt: now,
kind: "text",
},
],
goals: [],
versions: [],
});
state.projects.unshift(nextProject);
return nextProject;
}
function buildAutoGroupChatName(memberProjects: Project[]) {
const titles = memberProjects
.map((project) => project.threadMeta.threadDisplayName || project.name)
.filter((title) => typeof title === "string" && title.trim().length > 0);
if (titles.length === 0) {
return "新群聊";
}
if (titles.length === 1) {
return titles[0];
}
if (titles.length === 2) {
return `${titles[0]}${titles[1]}`;
}
return `${titles[0]}${titles[1]}${titles.length}个线程`;
}
export async function appendProjectMessage(payload: {
projectId: string;
sender?: MessageSender;