|
|
|
@ -18,12 +18,13 @@ |
|
|
|
import { useEffect, useRef, useState } from "react"; |
|
|
|
import { message } from "antd"; |
|
|
|
import { |
|
|
|
getSuperAgentSessionSectionDirect, |
|
|
|
getSuperAgentSessionSectionsDirect, |
|
|
|
updateSuperAgentSessionSectionDirect, |
|
|
|
} from "../actions/api"; |
|
|
|
getArtifact, |
|
|
|
getArtifactChapter, |
|
|
|
listArtifactChapters, |
|
|
|
listArtifacts, |
|
|
|
updateArtifactChapter, |
|
|
|
} from "../actions/agentResources"; |
|
|
|
import { defaultCatalog, defaultScoreItems, TASK_STAGE } from "../constants"; |
|
|
|
import { htmlToMarkdown } from "../../bookWriter/utils/htmlToMarkdown"; |
|
|
|
import { exportToWord } from "../../aiTenderDocument/utils/exportToWord"; |
|
|
|
import { |
|
|
|
buildBodyExportContent, |
|
|
|
@ -202,6 +203,7 @@ const useSuperAgentWorkspace = ({ |
|
|
|
workspaceOpen, |
|
|
|
setWorkspaceOpen, |
|
|
|
activeSessionId, |
|
|
|
activeConversationId, |
|
|
|
currentUserId, |
|
|
|
taskStage, |
|
|
|
actions, |
|
|
|
@ -225,6 +227,7 @@ const useSuperAgentWorkspace = ({ |
|
|
|
const sectionDetailLoadedIdsRef = useRef(new Set()); |
|
|
|
const [workspaceSectionsLoading, setWorkspaceSectionsLoading] = useState(false); |
|
|
|
const activeSessionIdRef = useRef(""); |
|
|
|
const artifactContextRef = useRef({ artifactId: "", version: 0 }); |
|
|
|
activeSessionIdRef.current = String(activeSessionId || ""); |
|
|
|
|
|
|
|
const isCurrentSession = (sessionId) => |
|
|
|
@ -350,14 +353,15 @@ const useSuperAgentWorkspace = ({ |
|
|
|
}) => { |
|
|
|
const sectionId = getSectionId(section) || `section_${index + 1}`; |
|
|
|
try { |
|
|
|
const artifactId = section.artifactId || section.artifact_id || artifactContextRef.current.artifactId; |
|
|
|
const artifactVersion = Number( |
|
|
|
section.artifactVersion || section.artifact_version || artifactContextRef.current.version |
|
|
|
); |
|
|
|
if (!artifactId || !artifactVersion) return section; |
|
|
|
const requestKey = `${sessionId}_${sectionId}_${currentUserId}`; |
|
|
|
let sectionRequest = sectionDetailRequestMap.get(requestKey); |
|
|
|
if (forceFresh || !sectionRequest) { |
|
|
|
sectionRequest = getSuperAgentSessionSectionDirect({ |
|
|
|
sessionId, |
|
|
|
sectionId, |
|
|
|
userId: currentUserId, |
|
|
|
}).finally(() => { |
|
|
|
sectionRequest = getArtifactChapter(artifactId, artifactVersion, sectionId).finally(() => { |
|
|
|
sectionDetailRequestMap.delete(requestKey); |
|
|
|
}); |
|
|
|
if (!forceFresh) { |
|
|
|
@ -366,11 +370,15 @@ const useSuperAgentWorkspace = ({ |
|
|
|
} |
|
|
|
const sectionResult = await sectionRequest; |
|
|
|
const sectionPayload = sectionResult?.data || sectionResult || {}; |
|
|
|
const sectionData = sectionPayload.section || sectionPayload; |
|
|
|
const sectionData = sectionPayload.chapter || sectionPayload; |
|
|
|
return { |
|
|
|
...section, |
|
|
|
...sectionData, |
|
|
|
id: sectionId, |
|
|
|
artifactId, |
|
|
|
artifactVersion, |
|
|
|
chapterVersion: Number(sectionData.version || section.chapterVersion || 0), |
|
|
|
etag: sectionResult?.headers?.get?.("ETag") || section.etag || "", |
|
|
|
title: |
|
|
|
sectionData.title || |
|
|
|
section.title || |
|
|
|
@ -399,13 +407,44 @@ const useSuperAgentWorkspace = ({ |
|
|
|
forceFresh = false, |
|
|
|
loadDetails = true, |
|
|
|
}) => { |
|
|
|
const sectionsResult = await getSuperAgentSessionSectionsDirect({ |
|
|
|
sessionId, |
|
|
|
userId: currentUserId, |
|
|
|
includeContent: 0, |
|
|
|
}); |
|
|
|
const sectionsData = sectionsResult?.data || sectionsResult; |
|
|
|
const sectionList = getSessionSectionList(sectionsData); |
|
|
|
const resourceConversationId = String( |
|
|
|
activeConversationId || workspaceData?.conversationId || "" |
|
|
|
); |
|
|
|
if (!resourceConversationId) return fallbackSections; |
|
|
|
const artifactPage = await listArtifacts(resourceConversationId, { limit: 100 }); |
|
|
|
const artifactItems = Array.isArray(artifactPage?.items) ? artifactPage.items : []; |
|
|
|
const selectedArtifact = artifactItems |
|
|
|
.filter((item) => item?.id && Number(item.version) > 0) |
|
|
|
.sort((left, right) => |
|
|
|
String(right.updated_at || right.updatedAt || "").localeCompare( |
|
|
|
String(left.updated_at || left.updatedAt || "") |
|
|
|
) |
|
|
|
)[0]; |
|
|
|
if (!selectedArtifact) return fallbackSections; |
|
|
|
const artifactDetail = await getArtifact( |
|
|
|
String(selectedArtifact.id), |
|
|
|
Number(selectedArtifact.version) |
|
|
|
); |
|
|
|
artifactContextRef.current = { |
|
|
|
artifactId: String(selectedArtifact.id), |
|
|
|
version: Number(selectedArtifact.version), |
|
|
|
title: artifactDetail?.title || selectedArtifact.title || "", |
|
|
|
}; |
|
|
|
const chapterResponse = await listArtifactChapters( |
|
|
|
artifactContextRef.current.artifactId, |
|
|
|
artifactContextRef.current.version |
|
|
|
); |
|
|
|
const sectionList = (chapterResponse?.items || []).map((chapter, index) => ({ |
|
|
|
...chapter, |
|
|
|
id: chapter.outline_node_id, |
|
|
|
section_id: chapter.outline_node_id, |
|
|
|
title: chapter.title || `正文${index + 1}`, |
|
|
|
content: chapter.content || "", |
|
|
|
artifactId: String(chapter.artifact_id || artifactContextRef.current.artifactId), |
|
|
|
artifactVersion: Number(chapter.artifact_version || artifactContextRef.current.version), |
|
|
|
chapterVersion: Number(chapter.version || 0), |
|
|
|
etag: `"${Number(chapter.version || 1)}"`, |
|
|
|
})); |
|
|
|
const sourceSections = sectionList.length |
|
|
|
? mergeSectionContentList(fallbackSections, sectionList) |
|
|
|
: fallbackSections; |
|
|
|
@ -1035,6 +1074,40 @@ const useSuperAgentWorkspace = ({ |
|
|
|
sectionBaselineContentRef.current.clear(); |
|
|
|
}; |
|
|
|
|
|
|
|
const refreshSectionAfterConflict = async (sessionId, section) => { |
|
|
|
try { |
|
|
|
const refreshedSection = await loadSessionSectionDetail({ |
|
|
|
sessionId, |
|
|
|
section, |
|
|
|
forceFresh: true, |
|
|
|
}); |
|
|
|
const sectionId = getSectionId(section, ""); |
|
|
|
dirtySectionIdsRef.current.delete(sectionId); |
|
|
|
setWorkspaceBodyList((previousList) => |
|
|
|
mergeSectionContentList(previousList, [refreshedSection]) |
|
|
|
); |
|
|
|
setWorkspaceData((previousData) => { |
|
|
|
if (!previousData || previousData.sessionId !== sessionId) return previousData; |
|
|
|
const nextSections = mergeSectionContentList(previousData.sections, [refreshedSection]); |
|
|
|
return { |
|
|
|
...previousData, |
|
|
|
sections: nextSections, |
|
|
|
payload: { |
|
|
|
...(previousData.payload || {}), |
|
|
|
sections: nextSections, |
|
|
|
chapters: nextSections, |
|
|
|
}, |
|
|
|
}; |
|
|
|
}); |
|
|
|
} catch (refreshError) { |
|
|
|
superAgentWarn("workspace.conflict", "refresh_failed", { |
|
|
|
sessionId, |
|
|
|
sectionId: getSectionId(section, ""), |
|
|
|
error: refreshError?.message, |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!activeSessionId) return; |
|
|
|
closeWorkspace(); |
|
|
|
@ -1164,18 +1237,28 @@ const useSuperAgentWorkspace = ({ |
|
|
|
const savedIds = new Set(); |
|
|
|
for (const item of sectionsToPersist) { |
|
|
|
const sectionId = getSectionId(item, ""); |
|
|
|
const content = htmlToMarkdown(item.content || ""); |
|
|
|
const content = String(item.content || ""); |
|
|
|
try { |
|
|
|
if (!sectionId) throw new Error("章节缺少 section_id"); |
|
|
|
await updateSuperAgentSessionSectionDirect({ |
|
|
|
sessionId, |
|
|
|
const artifactId = item.artifactId || item.artifact_id || artifactContextRef.current.artifactId; |
|
|
|
const artifactVersion = Number( |
|
|
|
item.artifactVersion || item.artifact_version || artifactContextRef.current.version |
|
|
|
); |
|
|
|
const etag = item.etag || `"${Number(item.chapterVersion || item.version || 1)}"`; |
|
|
|
if (!artifactId || !artifactVersion) throw new Error("章节缺少 artifact 版本信息"); |
|
|
|
const savedResult = await updateArtifactChapter( |
|
|
|
artifactId, |
|
|
|
artifactVersion, |
|
|
|
sectionId, |
|
|
|
userId: currentUserId, |
|
|
|
{ |
|
|
|
title: item.title || item.name || undefined, |
|
|
|
content, |
|
|
|
status: "edited", |
|
|
|
title: item.title || item.name || "", |
|
|
|
order: getSectionOrder(item, savedCount + 1), |
|
|
|
}); |
|
|
|
}, |
|
|
|
etag |
|
|
|
); |
|
|
|
const savedData = savedResult?.data || {}; |
|
|
|
item.chapterVersion = Number(savedData.version || item.chapterVersion || 0); |
|
|
|
item.etag = savedResult?.headers?.get?.("ETag") || `"${item.chapterVersion}"`; |
|
|
|
savedCount += 1; |
|
|
|
savedIds.add(sectionId); |
|
|
|
rememberSavedSectionContent(sessionId, item); |
|
|
|
@ -1186,6 +1269,10 @@ const useSuperAgentWorkspace = ({ |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|
failedCount += 1; |
|
|
|
if (Number(error?.status) === 412) { |
|
|
|
await refreshSectionAfterConflict(sessionId, item); |
|
|
|
message.warning("章节已被智能体更新,请刷新后重新编辑"); |
|
|
|
} |
|
|
|
superAgentWarn("workspace.save", "sections.persist.item_failed", { |
|
|
|
traceId, |
|
|
|
sessionId, |
|
|
|
@ -1456,15 +1543,25 @@ const useSuperAgentWorkspace = ({ |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await updateSuperAgentSessionSectionDirect({ |
|
|
|
sessionId, |
|
|
|
const artifactId = artifactContextRef.current.artifactId; |
|
|
|
const artifactVersion = artifactContextRef.current.version; |
|
|
|
const currentSection = (workspaceData?.sections || []).find( |
|
|
|
(item) => getSectionId(item, "") === sectionId |
|
|
|
); |
|
|
|
const chapterId = currentSection?.artifactId || currentSection?.artifact_id || artifactId; |
|
|
|
const chapterVersion = Number( |
|
|
|
currentSection?.artifactVersion || currentSection?.artifact_version || artifactVersion |
|
|
|
); |
|
|
|
const etag = currentSection?.etag || `"${Number(currentSection?.chapterVersion || currentSection?.version || 1)}"`; |
|
|
|
if (!chapterId || !chapterVersion) throw new Error("章节缺少 artifact 版本信息"); |
|
|
|
const savedResult = await updateArtifactChapter( |
|
|
|
chapterId, |
|
|
|
chapterVersion, |
|
|
|
sectionId, |
|
|
|
userId: currentUserId, |
|
|
|
content: htmlToMarkdown(content || ""), |
|
|
|
status: "edited", |
|
|
|
title: title || "", |
|
|
|
order, |
|
|
|
}); |
|
|
|
{ title: title || undefined, content: String(content || "") }, |
|
|
|
etag |
|
|
|
); |
|
|
|
const savedData = savedResult?.data || {}; |
|
|
|
rememberSavedSectionContent(sessionId, { |
|
|
|
id: sectionId, |
|
|
|
content, |
|
|
|
@ -1473,6 +1570,10 @@ const useSuperAgentWorkspace = ({ |
|
|
|
id: sectionId, |
|
|
|
section_id: sectionId, |
|
|
|
sectionId, |
|
|
|
artifactId: chapterId, |
|
|
|
artifactVersion: chapterVersion, |
|
|
|
chapterVersion: Number(savedData.version || currentSection?.chapterVersion || 0), |
|
|
|
etag: savedResult?.headers?.get?.("ETag") || `"${Number(savedData.version || currentSection?.chapterVersion || 1)}"`, |
|
|
|
content, |
|
|
|
status: "edited", |
|
|
|
title: title || "", |
|
|
|
@ -1557,7 +1658,12 @@ const useSuperAgentWorkspace = ({ |
|
|
|
sectionId, |
|
|
|
error, |
|
|
|
}); |
|
|
|
if (Number(error?.status) === 412) { |
|
|
|
await refreshSectionAfterConflict(sessionId, { id: sectionId }); |
|
|
|
message.warning("章节已被智能体更新,请刷新后重新编辑"); |
|
|
|
} else { |
|
|
|
message.error(error?.message || "章节保存失败,请稍后重试"); |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
}; |
|
|
|
|