feat: align frontend document and rule management flows
This commit is contained in:
+160
-14
@@ -151,6 +151,21 @@ export interface DocumentType {
|
||||
ruleSetIds?: number[];
|
||||
}
|
||||
|
||||
export interface DocumentSubtypeGroup {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
documentTypeId: number;
|
||||
documentTypeName?: string | null;
|
||||
rootGroupId?: number | null;
|
||||
rootGroupName?: string | null;
|
||||
entryModuleName?: string | null;
|
||||
entryModuleId?: number | null;
|
||||
isDefault?: boolean;
|
||||
displayName?: string;
|
||||
displayHint?: string;
|
||||
}
|
||||
|
||||
export interface UploadErrorDetails {
|
||||
title: string;
|
||||
summary: string;
|
||||
@@ -211,6 +226,7 @@ export interface UploadResult {
|
||||
fileName: string;
|
||||
fileSize: number;
|
||||
typeId: number;
|
||||
groupId?: number | null;
|
||||
region: string;
|
||||
processingStatus: string;
|
||||
duplicateUpload: boolean;
|
||||
@@ -236,6 +252,7 @@ interface NewUploadResponse {
|
||||
fileId: number;
|
||||
typeId: number;
|
||||
typeCode: string;
|
||||
groupId?: number | null;
|
||||
region: string;
|
||||
fileName: string;
|
||||
ossUrl: string;
|
||||
@@ -373,7 +390,7 @@ export function buildUploadErrorDetails(
|
||||
detailLines,
|
||||
actionLines: [
|
||||
'到“系统设置 / 文档类型管理”检查该文档类型是否绑定了正确的规则集。',
|
||||
'到“规则管理 / 规则集管理”确认对应规则集的可用规则数是否正常。',
|
||||
'到“规则管理”确认对应规则集的可用规则数是否正常。',
|
||||
'如果首页入口也异常,请同时到“系统设置 / 入口模块管理”检查入口模块绑定。',
|
||||
],
|
||||
rawMessage: message,
|
||||
@@ -395,7 +412,7 @@ export function buildUploadErrorDetails(
|
||||
summary: '当前上传入口关联的规则集不可用,文件无法开始审核。',
|
||||
detailLines,
|
||||
actionLines: [
|
||||
'到“规则管理 / 规则集管理”检查对应规则集是否存在、可用规则数是否正常。',
|
||||
'到“规则管理”检查对应规则集是否存在、可用规则数是否正常。',
|
||||
'如文档类型绑定了错误的规则集,请到“系统设置 / 文档类型管理”修正绑定关系。',
|
||||
],
|
||||
rawMessage: message,
|
||||
@@ -595,15 +612,12 @@ export async function appendContractAttachments(
|
||||
formData.append('files', file);
|
||||
});
|
||||
|
||||
// 添加其他参数
|
||||
formData.append('merge_mode', mergeMode);
|
||||
formData.append('is_reprocess', isReprocess.toString());
|
||||
if (remark) {
|
||||
formData.append('remark', remark);
|
||||
}
|
||||
|
||||
// 新链路仅保留附件追加;mergeMode / remark 在后端暂不消费,但继续保留函数签名兼容旧页面调用。
|
||||
void mergeMode;
|
||||
void remark;
|
||||
|
||||
// 构建请求URL
|
||||
const uploadUrl = `${UPLOAD_URL}/contracts/${documentId}/append_attachments`;
|
||||
const uploadUrl = `${API_BASE_URL}/api/documents/${documentId}/attachments`;
|
||||
console.log('【合同附件追加】准备发送请求到服务器:', uploadUrl);
|
||||
|
||||
// 设置请求头
|
||||
@@ -627,11 +641,28 @@ export async function appendContractAttachments(
|
||||
const result = response.data;
|
||||
console.log('【合同附件追加】服务器返回结果:', result);
|
||||
|
||||
if (result.success) {
|
||||
return { data: result.result };
|
||||
} else {
|
||||
return { error: result.error || '附件追加失败' };
|
||||
if (result?.data) {
|
||||
if (isReprocess) {
|
||||
await axios.post(
|
||||
`${API_BASE_URL}/api/audit/run`,
|
||||
{
|
||||
documentId,
|
||||
force: true,
|
||||
speed: 'normal',
|
||||
},
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
success: true,
|
||||
result: result.data,
|
||||
error: null,
|
||||
}
|
||||
};
|
||||
}
|
||||
return { error: result?.message || result?.msg || '附件追加失败' };
|
||||
|
||||
} catch (error) {
|
||||
console.error('【合同附件追加】上传过程中发生错误:', error);
|
||||
@@ -646,8 +677,10 @@ export async function uploadDocumentToServer(
|
||||
fileName: string,
|
||||
fileType: string,
|
||||
typeId: number,
|
||||
groupId?: number | null,
|
||||
region: string = "default",
|
||||
createdBy?: number,
|
||||
attachments?: File[],
|
||||
autoRun: boolean = true,
|
||||
speed: string = "normal",
|
||||
jwtToken?: string,
|
||||
@@ -657,6 +690,12 @@ export async function uploadDocumentToServer(
|
||||
const blob = new Blob([binaryData], { type: fileType });
|
||||
formData.append("file", blob, fileName);
|
||||
formData.append("typeId", String(typeId));
|
||||
if (groupId) {
|
||||
formData.append("groupId", String(groupId));
|
||||
}
|
||||
(attachments || []).forEach((attachment) => {
|
||||
formData.append("attachments", attachment);
|
||||
});
|
||||
formData.append("region", region);
|
||||
formData.append("fileRole", "primary");
|
||||
if (createdBy !== undefined) {
|
||||
@@ -689,6 +728,7 @@ export async function uploadDocumentToServer(
|
||||
fileName: uploadData.fileName,
|
||||
fileSize: binaryData.byteLength,
|
||||
typeId: uploadData.typeId,
|
||||
groupId: uploadData.groupId,
|
||||
region: uploadData.region,
|
||||
processingStatus: uploadData.processingStatus,
|
||||
duplicateUpload: uploadData.duplicateUpload,
|
||||
@@ -819,6 +859,112 @@ export async function getDocumentTypes(token?: string): Promise<{data: DocumentT
|
||||
}
|
||||
}
|
||||
|
||||
function mapSubtypeChild(child: any, root?: any): DocumentSubtypeGroup {
|
||||
const rawName = typeof child.name === "string" ? child.name.trim() : "";
|
||||
const rawCode = typeof child.code === "string" ? child.code.trim() : "";
|
||||
const isDefault = rawName === "通用" || rawCode.endsWith(".default");
|
||||
const entryModuleId = child.entry_module_id ?? root?.entry_module_id ?? null;
|
||||
const entryModuleName = child.entry_module_name ?? root?.entry_module_name ?? null;
|
||||
const rootGroupName = root?.name ?? null;
|
||||
|
||||
return {
|
||||
id: child.id,
|
||||
name: child.name,
|
||||
code: child.code,
|
||||
documentTypeId: child.document_type_id,
|
||||
documentTypeName: child.document_type_name,
|
||||
rootGroupId: root?.id ?? null,
|
||||
rootGroupName,
|
||||
entryModuleId: typeof entryModuleId === "number" ? entryModuleId : null,
|
||||
entryModuleName,
|
||||
isDefault,
|
||||
displayName: isDefault ? `默认子类型(${rawName || "通用"})` : rawName || child.name,
|
||||
displayHint: [rootGroupName, child.document_type_name, entryModuleName, rawCode].filter(Boolean).join(" · "),
|
||||
};
|
||||
}
|
||||
|
||||
function dedupeSubtypeGroups(groups: DocumentSubtypeGroup[]): DocumentSubtypeGroup[] {
|
||||
const groupMap = new Map<number, DocumentSubtypeGroup>();
|
||||
groups.forEach((group) => {
|
||||
const existing = groupMap.get(group.id);
|
||||
if (!existing) {
|
||||
groupMap.set(group.id, group);
|
||||
return;
|
||||
}
|
||||
const currentScore = (group.rootGroupName ? 2 : 0) + (group.entryModuleId ? 1 : 0);
|
||||
const existingScore = (existing.rootGroupName ? 2 : 0) + (existing.entryModuleId ? 1 : 0);
|
||||
if (currentScore > existingScore) {
|
||||
groupMap.set(group.id, group);
|
||||
}
|
||||
});
|
||||
return Array.from(groupMap.values());
|
||||
}
|
||||
|
||||
export async function getDocumentSubtypeGroups(
|
||||
documentTypeId: number,
|
||||
token?: string,
|
||||
entryModuleId?: number | null,
|
||||
): Promise<{ data: DocumentSubtypeGroup[]; error?: never } | { data?: never; error: string; status?: number }> {
|
||||
try {
|
||||
const headers: Record<string, string> = {};
|
||||
if (token) {
|
||||
headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const allResponse = await axios.get(`${API_BASE_URL}/api/v3/evaluation-point-groups/all`, {
|
||||
params: {
|
||||
include_disabled: false,
|
||||
with_rule_count: false,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
const allRoots = extractApiData<any[]>(allResponse.data) || [];
|
||||
const matchedFromTree = allRoots.flatMap((root: any) => {
|
||||
if (!Array.isArray(root?.children)) return [];
|
||||
if (entryModuleId && Number(root?.entry_module_id || 0) !== Number(entryModuleId)) {
|
||||
return [];
|
||||
}
|
||||
return root.children
|
||||
.filter((child: any) => Number(child?.document_type_id || 0) === Number(documentTypeId))
|
||||
.map((child: any) => mapSubtypeChild(child, root));
|
||||
});
|
||||
|
||||
if (matchedFromTree.length > 0) {
|
||||
return { data: dedupeSubtypeGroups(matchedFromTree) };
|
||||
}
|
||||
|
||||
const response = await axios.get(`${API_BASE_URL}/api/v3/evaluation-point-groups/by-document-types`, {
|
||||
params: {
|
||||
document_type_ids: String(documentTypeId),
|
||||
include_disabled: false,
|
||||
with_rule_count: false,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
const roots = extractApiData<any[]>(response.data) || [];
|
||||
const filteredRoots = entryModuleId
|
||||
? roots.filter((root: any) => Number(root?.entry_module_id || 0) === Number(entryModuleId))
|
||||
: roots;
|
||||
const fallbackRoots = filteredRoots.length > 0 ? filteredRoots : roots;
|
||||
const groups = dedupeSubtypeGroups(
|
||||
fallbackRoots.flatMap((root: any) =>
|
||||
Array.isArray(root?.children)
|
||||
? root.children
|
||||
.filter((child: any) => Number(child?.document_type_id || 0) === Number(documentTypeId))
|
||||
.map((child: any) => mapSubtypeChild(child, root))
|
||||
: [],
|
||||
),
|
||||
);
|
||||
return { data: groups };
|
||||
} catch (error) {
|
||||
console.error("获取子类型分组失败:", error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : "获取子类型分组失败",
|
||||
status: 500,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文档的状态
|
||||
* @param documentIds 文档ID列表
|
||||
|
||||
Reference in New Issue
Block a user