feat(web): call real docgov lifecycle APIs from action handlers
Replace local lifecycleState mocks; add handleRejectApprove; use holdsDocDuty for chat shortcuts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+199
-66
@@ -49,6 +49,15 @@ import {
|
||||
import { mapApiMembersToProjectMembers } from './lib/projectMemberAdapter';
|
||||
import { mergeWorkflowDocumentsFromServer } from './lib/deliverableSync';
|
||||
import { resolveCurrentDocRole } from './lib/currentDocRole';
|
||||
import { holdsDocDuty } from './lib/docGovActionVisibility';
|
||||
import {
|
||||
claimDocument,
|
||||
submitForReview,
|
||||
reviewDecision,
|
||||
approverDecision,
|
||||
mapLeaseToAstEditLease,
|
||||
type DocGovRecord,
|
||||
} from './api/docgov';
|
||||
import { canAddBlockComment } from './lib/commentPolicy';
|
||||
import {
|
||||
pollUntilPostTaskServerDoc,
|
||||
@@ -2934,8 +2943,18 @@ export default function App() {
|
||||
currentDocRole === 'author';
|
||||
const isReviewPassShortcut =
|
||||
(text.includes('通过') || text.includes('同意')) &&
|
||||
((docAST.lifecycleState === 'reviewing' && currentDocRole === 'reviewer') ||
|
||||
(docAST.lifecycleState === 'approving' && currentDocRole === 'approver'));
|
||||
((docAST.lifecycleState === 'reviewing' &&
|
||||
holdsDocDuty(currentUser.id, 'reviewer', {
|
||||
authorId: docAST.authorId,
|
||||
reviewerId: docAST.reviewerId,
|
||||
approverId: docAST.approverId,
|
||||
})) ||
|
||||
(docAST.lifecycleState === 'approving' &&
|
||||
holdsDocDuty(currentUser.id, 'approver', {
|
||||
authorId: docAST.authorId,
|
||||
reviewerId: docAST.reviewerId,
|
||||
approverId: docAST.approverId,
|
||||
})));
|
||||
const sendActiveDoc =
|
||||
deliverables.find((d) => d.docId === activeDocId) ||
|
||||
visibleDeliverables.find((d) => d.docId === activeDocId);
|
||||
@@ -3192,8 +3211,13 @@ export default function App() {
|
||||
}
|
||||
|
||||
if (text.includes('通过') || text.includes('同意')) {
|
||||
if (docAST.lifecycleState === 'reviewing' && currentDocRole === 'reviewer') {
|
||||
handlePassReview();
|
||||
const duties = {
|
||||
authorId: docAST.authorId,
|
||||
reviewerId: docAST.reviewerId,
|
||||
approverId: docAST.approverId,
|
||||
};
|
||||
if (docAST.lifecycleState === 'reviewing' && holdsDocDuty(currentUser.id, 'reviewer', duties)) {
|
||||
void handlePassReview();
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
@@ -3208,8 +3232,8 @@ export default function App() {
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if (docAST.lifecycleState === 'approving' && currentDocRole === 'approver') {
|
||||
handleApprove();
|
||||
if (docAST.lifecycleState === 'approving' && holdsDocDuty(currentUser.id, 'approver', duties)) {
|
||||
void handleApprove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3328,80 +3352,189 @@ export default function App() {
|
||||
setDocAST({ ...docAST });
|
||||
};
|
||||
|
||||
// 法定生命周期流转
|
||||
const handleClaim = () => {
|
||||
docAST.isClaimed = true;
|
||||
setDocAST({ ...docAST });
|
||||
// 法定生命周期流转(真实 docgov HTTP)
|
||||
const applyDocGovRecord = (doc: DocGovRecord) => {
|
||||
const nextState = (doc.current_state || docAST.lifecycleState) as DocLifecycleState;
|
||||
const nextClaimed = doc.author_claimed ?? docAST.isClaimed;
|
||||
const lease = mapLeaseToAstEditLease(doc);
|
||||
const nextAst: DocumentAST = {
|
||||
...docAST,
|
||||
lifecycleState: nextState,
|
||||
isClaimed: nextClaimed,
|
||||
authorId: doc.author_id ?? docAST.authorId,
|
||||
author: doc.author_name || docAST.author,
|
||||
reviewerId: doc.reviewer_id ?? docAST.reviewerId,
|
||||
reviewer: doc.reviewer_name || docAST.reviewer,
|
||||
approverId: doc.approver_id ?? docAST.approverId,
|
||||
approver: doc.approver_name || docAST.approver,
|
||||
dutyPolicy: doc.duty_policy || docAST.dutyPolicy,
|
||||
contentSha256: doc.content_sha256 || docAST.contentSha256,
|
||||
editLease: lease ?? docAST.editLease,
|
||||
};
|
||||
setDocAST(nextAst);
|
||||
setDeliverables((prev) =>
|
||||
prev.map((d) => (d.docId === activeDocId ? { ...d, isClaimed: true } : d))
|
||||
prev.map((d) =>
|
||||
d.docId === activeDocId || d.docId === doc.doc_id
|
||||
? {
|
||||
...d,
|
||||
lifecycleState: nextState,
|
||||
isClaimed: nextClaimed,
|
||||
authorId: doc.author_id ?? d.authorId,
|
||||
author: doc.author_name || d.author,
|
||||
reviewerId: doc.reviewer_id ?? d.reviewerId,
|
||||
reviewer: doc.reviewer_name || d.reviewer,
|
||||
approverId: doc.approver_id ?? d.approverId,
|
||||
approver: doc.approver_name || d.approver,
|
||||
dutyPolicy: doc.duty_policy || d.dutyPolicy,
|
||||
editLease: lease ?? d.editLease,
|
||||
}
|
||||
: d,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubmitReview = () => {
|
||||
docAST.lifecycleState = 'reviewing';
|
||||
setDocAST({ ...docAST });
|
||||
setDeliverables((prev) =>
|
||||
prev.map((d) => (d.docId === activeDocId ? { ...d, lifecycleState: 'reviewing' } : d))
|
||||
);
|
||||
};
|
||||
|
||||
const handlePassReview = () => {
|
||||
docAST.lifecycleState = 'approving';
|
||||
setDocAST({ ...docAST });
|
||||
setDeliverables((prev) =>
|
||||
prev.map((d) => (d.docId === activeDocId ? { ...d, lifecycleState: 'approving' } : d))
|
||||
);
|
||||
};
|
||||
|
||||
const handleRejectReview = (reason: string) => {
|
||||
docAST.lifecycleState = 'drafting';
|
||||
setDocAST({ ...docAST });
|
||||
setDeliverables((prev) =>
|
||||
prev.map((d) => (d.docId === activeDocId ? { ...d, lifecycleState: 'drafting' } : d))
|
||||
);
|
||||
|
||||
const pushDocGovError = (actionLabel: string, err: unknown) => {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'rej-' + Date.now(),
|
||||
id: 'docgov-err-' + Date.now(),
|
||||
sessionId: activeSessionId,
|
||||
role: 'system',
|
||||
content: `⚠️ 李主管(审核负责人)驳回了《${docAST.title}》,已退回编制阶段。驳回原因:${reason}`,
|
||||
content: `⚠️ ${actionLabel}失败:${message}`,
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const handleApprove = () => {
|
||||
docAST.lifecycleState = 'approved';
|
||||
setDocAST({ ...docAST });
|
||||
setDeliverables((prev) =>
|
||||
prev.map((d) => (d.docId === activeDocId ? { ...d, lifecycleState: 'approved' } : d))
|
||||
);
|
||||
const handleClaim = async () => {
|
||||
try {
|
||||
const doc = await claimDocument({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
user_role: 'author',
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
} catch (err) {
|
||||
pushDocGovError('认领文档', err);
|
||||
}
|
||||
};
|
||||
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'appr-' + Date.now(),
|
||||
sessionId: activeSessionId,
|
||||
role: 'assistant',
|
||||
authorName: '水木东方协同参谋',
|
||||
intentTag: '🏆 终审批准生效',
|
||||
content: `🎉 祝贺!王总监(批准人)已终审批准《${docAST.title}》!系统已自动对该受控交付物进行微粒度章节切片与向量化,成功注入全局标杆案例经验库!`,
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
recommendedCases: [
|
||||
{
|
||||
docId: docAST.docId,
|
||||
taskKey: docAST.taskKey,
|
||||
artifactTitle: docAST.title,
|
||||
sectionTitle: '终审已生效标杆案例',
|
||||
similarityScore: 1.0,
|
||||
keyRegulations: ['GB 9706.1-2020', 'YY 0569-2011', 'IEC 60601-1-2'],
|
||||
summarySnippet: '本交付物已通过编制、审核、批准法定三级签署闭环,作为往期标杆经验向全平台开放自推荐。',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const handleSubmitReview = async () => {
|
||||
try {
|
||||
const doc = await submitForReview({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
} catch (err) {
|
||||
pushDocGovError('提交送审', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePassReview = async () => {
|
||||
try {
|
||||
const doc = await reviewDecision({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
action: 'approve',
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
} catch (err) {
|
||||
pushDocGovError('审核通过', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRejectReview = async (reason: string) => {
|
||||
try {
|
||||
const doc = await reviewDecision({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
action: 'reject',
|
||||
reason,
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'rej-' + Date.now(),
|
||||
sessionId: activeSessionId,
|
||||
role: 'system',
|
||||
content: `⚠️ ${currentUser.name}(审核)驳回了《${docAST.title}》,已退回编制阶段。驳回原因:${reason}`,
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
},
|
||||
]);
|
||||
} catch (err) {
|
||||
pushDocGovError('审核驳回', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleApprove = async () => {
|
||||
try {
|
||||
const doc = await approverDecision({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
action: 'approve',
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'appr-' + Date.now(),
|
||||
sessionId: activeSessionId,
|
||||
role: 'assistant',
|
||||
authorName: '水木东方协同参谋',
|
||||
intentTag: '🏆 终审批准生效',
|
||||
content: `🎉 祝贺!${currentUser.name}(批准人)已终审批准《${docAST.title}》!系统已自动对该受控交付物进行微粒度章节切片与向量化,成功注入全局标杆案例经验库!`,
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
recommendedCases: [
|
||||
{
|
||||
docId: docAST.docId,
|
||||
taskKey: docAST.taskKey,
|
||||
artifactTitle: docAST.title,
|
||||
sectionTitle: '终审已生效标杆案例',
|
||||
similarityScore: 1.0,
|
||||
keyRegulations: ['GB 9706.1-2020', 'YY 0569-2011', 'IEC 60601-1-2'],
|
||||
summarySnippet:
|
||||
'本交付物已通过编制、审核、批准法定三级签署闭环,作为往期标杆经验向全平台开放自推荐。',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
} catch (err) {
|
||||
pushDocGovError('终审批准', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRejectApprove = async (reason: string) => {
|
||||
try {
|
||||
const doc = await approverDecision({
|
||||
doc_id: docAST.docId,
|
||||
user_id: currentUser.id,
|
||||
user_name: currentUser.name,
|
||||
action: 'reject',
|
||||
reason,
|
||||
rollback_to: 'drafting',
|
||||
});
|
||||
applyDocGovRecord(doc);
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'rej-appr-' + Date.now(),
|
||||
sessionId: activeSessionId,
|
||||
role: 'system',
|
||||
content: `⚠️ ${currentUser.name}(批准人)驳回了《${docAST.title}》,已退回编制阶段。驳回原因:${reason}`,
|
||||
timestamp: new Date().toTimeString().substring(0, 5),
|
||||
},
|
||||
]);
|
||||
} catch (err) {
|
||||
pushDocGovError('批准驳回', err);
|
||||
}
|
||||
};
|
||||
|
||||
// 导出 DOCX (Word 渲染器)
|
||||
@@ -4206,7 +4339,7 @@ export default function App() {
|
||||
onPassReview={handlePassReview}
|
||||
onRejectReview={handleRejectReview}
|
||||
onApprove={handleApprove}
|
||||
onRejectApprove={handleRejectReview}
|
||||
onRejectApprove={handleRejectApprove}
|
||||
onShareToProject={handleShareToProject}
|
||||
onRevokeShare={handleRevokeShare}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user