From 8dbacb8bea16c7da18300de0608d6e1abc6edde1 Mon Sep 17 00:00:00 2001 From: wren <“porlong@qq.com”> Date: Wed, 6 May 2026 11:05:35 +0800 Subject: [PATCH] fix: speed up document type group loading --- app/api/files/files-upload.ts | 82 ++++++++++++++++++++++------ app/routes/document-types._index.tsx | 12 ++-- app/routes/document-types.new.tsx | 6 +- 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/app/api/files/files-upload.ts b/app/api/files/files-upload.ts index e9e6757..be57e86 100644 --- a/app/api/files/files-upload.ts +++ b/app/api/files/files-upload.ts @@ -900,6 +900,67 @@ function dedupeSubtypeGroups(groups: DocumentSubtypeGroup[]): DocumentSubtypeGro return Array.from(groupMap.values()); } +async function fetchAllEvaluationPointGroupRoots(token?: string): Promise { + const headers: Record = {}; + 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, + }); + + return extractApiData(allResponse.data) || []; +} + +function collectSubtypeGroupsFromRoots( + roots: any[], + documentTypeId: number, + entryModuleId?: number | null, +): DocumentSubtypeGroup[] { + return dedupeSubtypeGroups( + roots.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)); + }), + ); +} + +export async function getDocumentSubtypeGroupsMap( + documentTypeIds: number[], + token?: string, +): Promise<{ data: Record; error?: never } | { data?: never; error: string; status?: number }> { + try { + if (!documentTypeIds.length) { + return { data: {} }; + } + + const roots = await fetchAllEvaluationPointGroupRoots(token); + const ids = Array.from(new Set(documentTypeIds.map((id) => Number(id)).filter(Boolean))); + const grouped = Object.fromEntries( + ids.map((documentTypeId) => [documentTypeId, collectSubtypeGroupsFromRoots(roots, documentTypeId)]), + ); + + return { data: grouped }; + } catch (error) { + console.error("批量获取子类型分组失败:", error); + return { + error: error instanceof Error ? error.message : "批量获取子类型分组失败", + status: 500, + }; + } +} + export async function getDocumentSubtypeGroups( documentTypeId: number, token?: string, @@ -911,26 +972,11 @@ export async function getDocumentSubtypeGroups( 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(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)); - }); + const allRoots = await fetchAllEvaluationPointGroupRoots(token); + const matchedFromTree = collectSubtypeGroupsFromRoots(allRoots, documentTypeId, entryModuleId); if (matchedFromTree.length > 0) { - return { data: dedupeSubtypeGroups(matchedFromTree) }; + return { data: matchedFromTree }; } const response = await axios.get(`${API_BASE_URL}/api/v3/evaluation-point-groups/by-document-types`, { diff --git a/app/routes/document-types._index.tsx b/app/routes/document-types._index.tsx index 1fae6ee..7819dc3 100644 --- a/app/routes/document-types._index.tsx +++ b/app/routes/document-types._index.tsx @@ -11,7 +11,7 @@ import { type DocumentType, type EntryModuleOption, } from "~/api/document-types/document-types"; -import { getDocumentSubtypeGroups, type DocumentSubtypeGroup } from "~/api/files/files-upload"; +import { getDocumentSubtypeGroupsMap, type DocumentSubtypeGroup } from "~/api/files/files-upload"; import documentTypesStyles from "~/styles/pages/document-types_index.css?url"; export function links() { @@ -44,17 +44,15 @@ export async function loader({ request }: LoaderFunctionArgs) { ]); const types = typesRes.data?.types || []; - const subtypeGroupEntries = await Promise.all( - types.map(async (type) => { - const groupsRes = await getDocumentSubtypeGroups(type.id, frontendJWT, type.entryModuleId || undefined); - return [type.id, "data" in groupsRes && groupsRes.data ? groupsRes.data : []] as const; - }), + const subtypeGroupsRes = await getDocumentSubtypeGroupsMap( + types.map((type) => type.id), + frontendJWT, ); return { types, entryModules: modulesRes.data || [], - subtypeGroupsByTypeId: Object.fromEntries(subtypeGroupEntries), + subtypeGroupsByTypeId: "data" in subtypeGroupsRes && subtypeGroupsRes.data ? subtypeGroupsRes.data : {}, frontendJWT, }; } catch (error) { diff --git a/app/routes/document-types.new.tsx b/app/routes/document-types.new.tsx index 930ab48..93c199a 100644 --- a/app/routes/document-types.new.tsx +++ b/app/routes/document-types.new.tsx @@ -16,7 +16,7 @@ import { type EntryModuleOption, type RuleSetOption, } from "~/api/document-types/document-types"; -import { getDocumentSubtypeGroups, type DocumentSubtypeGroup } from "~/api/files/files-upload"; +import { getDocumentSubtypeGroupsMap, type DocumentSubtypeGroup } from "~/api/files/files-upload"; import newStyles from "~/styles/pages/document-types_new.css?url"; export function links() { @@ -52,9 +52,9 @@ export async function loader({ request }: LoaderFunctionArgs) { const res = await getDocumentType(parseInt(editId), frontendJWT); editType = res.data || null; if (editType?.id) { - const groupsRes = await getDocumentSubtypeGroups(editType.id, frontendJWT, editType.entryModuleId || undefined); + const groupsRes = await getDocumentSubtypeGroupsMap([editType.id], frontendJWT); if ("data" in groupsRes && groupsRes.data) { - runtimeSubtypeGroups = groupsRes.data; + runtimeSubtypeGroups = groupsRes.data[editType.id] || []; } } }