diff options
Diffstat (limited to 'lib/swp/api-client.ts')
| -rw-r--r-- | lib/swp/api-client.ts | 307 |
1 files changed, 303 insertions, 4 deletions
diff --git a/lib/swp/api-client.ts b/lib/swp/api-client.ts index 39ce02b0..17cfbb7c 100644 --- a/lib/swp/api-client.ts +++ b/lib/swp/api-client.ts @@ -15,6 +15,7 @@ * ## 주요 API * - `fetchGetVDRDocumentList`: 문서 마스터 조회 (필터링 지원) * - `fetchGetExternalInboxList`: 파일 정보 조회 (업로드된 파일 포함) + * - `fetchGetRevTreeCompleteList`: 문서 리비전 트리 조회 (NEW) * - `fetchGetActivityFileList`: Rev-Activity-File 계층 구조 조회 * - `callSaveInBoxList`: 파일 업로드 메타데이터 등록 * - `callSaveInBoxListCancelStatus`: Standby 파일 취소 @@ -28,16 +29,17 @@ * vndrCd: "SE00100" * }); * - * // 파일 목록 조회 - * const files = await fetchGetExternalInboxList({ - * projNo: "SN2190", - * vndrCd: "SE00100" + * // 리비전 트리 조회 + * const tree = await fetchGetRevTreeCompleteList({ + * proj_no: "SN2190", + * doc_no: "C168-SH-SBN08-XG-20118-01" * }); * ``` * * @see lib/swp/document-service.ts - 비즈니스 로직 레이어 * @see lib/swp/vendor-actions.ts - 서버 액션 (권한 체크) * @see lib/swp/README.md - 전체 시스템 문서 + * @see lib/swp/REVISION_TREE_USAGE.md - 리비전 트리 API 활용 가이드 */ // ============================================================================ @@ -147,6 +149,7 @@ export interface SwpFileApiResponse { STAT: string | null; STAT_NM: string | null; IDX: string | null; + NOTE: string | null; } // ============================================================================ @@ -454,6 +457,96 @@ export async function analyzeSwpData( } // ============================================================================ +// 서버 액션: 리비전 트리 조회 (GetRevTreeCompleteList) +// ============================================================================ + +/** + * 리비전 트리 조회 필터 + */ +export interface GetRevTreeCompleteListFilter { + proj_no: string; // 필수 (예: "SN2190") + doc_no: string; // 필수 (예: "C168-SH-SBN08-XG-20118-01") + stat?: string; // 상태 코드 필터 (선택) + lang_gb?: string; // 언어 구분 (선택) +} + +/** + * 리비전 트리 노드 API 응답 + * + * 트리 구조: + * - Root: 문서 자체 + * - Revision: 문서의 버전 (01, 02, 03...) + * - Activity: 각 리비전에 속한 송수신 활동 + */ +export interface RevTreeNodeApiResponse { + // 노드 식별 정보 + SearchType: "Root" | "Revision" | "Activity"; // 노드 타입 + NodeName: string; // 노드 이름 (Activity의 경우 ACTV_NO) + ParentName: string | null; // 부모 노드 이름 + SearchName: string; // 검색용 이름 + + // Revision 정보 (SearchType이 "Revision"일 때) + Stage: string; // IFA, IFC 등 + + // Activity 정보 (SearchType이 "Activity"일 때) + InOut: string; // "IN"=수신, "OUT"=송신 + StatusCode: string; // R00=Ready, S00=Ready, S30=Completed + StatusName: string; // 상태명 + TransmittalNo: string; // 송장번호 + RefActivityNo: string; // 참조 Activity 번호 + + // 메타 정보 + CreateDate: string; + CreateEmpNo: string; + ModifyDate: string; + ModifyEmpNo: string; +} + +/** + * 리비전 트리 조회 (GetRevTreeCompleteList) + * + * 문서의 전체 리비전 히스토리와 각 리비전별 Activity 목록을 트리 구조로 조회합니다. + * + * @param filter 조회 필터 + * @returns 트리 구조의 노드 배열 + * + * @example + * ```typescript + * const tree = await fetchGetRevTreeCompleteList({ + * proj_no: "SN2190", + * doc_no: "C168-SH-SBN08-XG-20118-01" + * }); + * + * // Root 노드 찾기 + * const root = tree.find(n => n.SearchType === "Root"); + * + * // Revision 목록 + * const revisions = tree.filter(n => n.SearchType === "Revision"); + * + * // 특정 Revision의 Activity 목록 + * const rev04Activities = tree.filter(n => + * n.SearchType === "Activity" && n.ParentName === "3" // Rev 04 + * ); + * ``` + */ +export async function fetchGetRevTreeCompleteList( + filter: GetRevTreeCompleteListFilter +): Promise<RevTreeNodeApiResponse[]> { + const body = { + proj_no: filter.proj_no, + doc_no: filter.doc_no, + stat: filter.stat || "", + lang_gb: filter.lang_gb || "", + }; + + return callSwpApi<RevTreeNodeApiResponse>( + "GetRevTreeCompleteList", + body, + "GetRevTreeCompleteListResult" + ); +} + +// ============================================================================ // 서버 액션: Activity 및 파일 리스트 조회 (GetActivityFileList) // ============================================================================ @@ -546,3 +639,209 @@ export async function callSaveInBoxListCancelStatus( return result; } +// ============================================================================ +// 유틸리티: GetRevTreeCompleteList + GetActivityFileList 연동 +// ============================================================================ + +/** + * 리비전 트리 구조 파싱 결과 + */ +export interface ParsedRevisionTree { + docNo: string; + revisions: Array<{ + revSeq: string; // "0", "1", "2", "3" (내부 시퀀스) + revNo: string; // "01", "02", "03", "04" (표시용) + stage: string; // "IFA", "IFC" + activities: Array<{ + actvNo: string; // NodeName (예: "SHIK2017022008520078313") + inOut: "IN" | "OUT"; + statusCode: string; + statusName: string; + transmittalNo: string; + refActivityNo: string; + createDate: string; + createEmpNo: string; + }>; + }>; +} + +/** + * 리비전 트리를 구조화된 형태로 파싱 + * + * @param tree GetRevTreeCompleteList 응답 + * @returns 파싱된 리비전 트리 + */ +export async function parseRevisionTree( + tree: RevTreeNodeApiResponse[] +): Promise<ParsedRevisionTree> { + const root = tree.find((n) => n.SearchType === "Root"); + if (!root) { + throw new Error("Root 노드를 찾을 수 없습니다"); + } + + // Revision 목록 + const revisions = tree + .filter((n) => n.SearchType === "Revision") + .map((rev) => { + // 해당 Revision의 Activity 목록 + const activities = tree + .filter( + (n) => n.SearchType === "Activity" && n.ParentName === rev.NodeName + ) + .map((act) => ({ + actvNo: act.NodeName, + inOut: act.InOut as "IN" | "OUT", + statusCode: act.StatusCode, + statusName: act.StatusName, + transmittalNo: act.TransmittalNo, + refActivityNo: act.RefActivityNo.trim(), + createDate: act.CreateDate, + createEmpNo: act.CreateEmpNo, + })); + + return { + revSeq: rev.NodeName, // "0", "1", "2", "3" + revNo: rev.SearchName, // "01", "02", "03", "04" + stage: rev.Stage, + activities, + }; + }); + + return { + docNo: root.NodeName, + revisions, + }; +} + +/** + * 특정 문서의 모든 리비전과 파일 정보를 조회 + * + * GetRevTreeCompleteList로 트리 구조를 가져온 후, + * 각 Activity의 NodeName(ACTV_NO)을 사용하여 GetActivityFileList를 호출합니다. + * + * ⚠️ 주의: Activity가 많을 경우 API 호출이 많아질 수 있습니다. + * + * @param projNo 프로젝트 번호 + * @param docNo 문서 번호 + * @returns 리비전 트리 + 각 Activity의 파일 목록 + * + * @example + * ```typescript + * const fullData = await fetchDocumentRevisionTreeWithFiles( + * "SN2190", + * "C168-SH-SBN08-XG-20118-01" + * ); + * + * console.log(`문서: ${fullData.docNo}`); + * fullData.revisions.forEach(rev => { + * console.log(`\nRevision ${rev.revNo} (${rev.stage})`); + * rev.activities.forEach(act => { + * console.log(` Activity: ${act.actvNo} (${act.inOut})`); + * console.log(` 파일 ${act.files.length}개`); + * }); + * }); + * ``` + */ +export async function fetchDocumentRevisionTreeWithFiles( + projNo: string, + docNo: string +): Promise<{ + docNo: string; + revisions: Array<{ + revSeq: string; + revNo: string; + stage: string; + activities: Array<{ + actvNo: string; + inOut: "IN" | "OUT"; + statusCode: string; + statusName: string; + transmittalNo: string; + files: ActivityFileApiResponse[]; + }>; + }>; +}> { + console.log(`[SWP API] 문서 ${docNo} 전체 히스토리 조회 시작`); + + // 1단계: 리비전 트리 조회 + const tree = await fetchGetRevTreeCompleteList({ + proj_no: projNo, + doc_no: docNo, + }); + + const parsed = await parseRevisionTree(tree); + + // 2단계: 각 Activity의 파일 목록 조회 + // Activity가 많을 경우 병렬 처리 고려 (단, API 부하 주의) + const revisionsWithFiles = await Promise.all( + parsed.revisions.map(async (rev) => { + const activitiesWithFiles = await Promise.all( + rev.activities.map(async (act) => { + try { + // GetActivityFileList는 rev_seq가 필요할 수 있음 + const files = await fetchGetActivityFileList({ + proj_no: projNo, + doc_no: docNo, + rev_seq: rev.revSeq, + }); + + // Activity 번호로 필터링 + const activityFiles = files.filter( + (f) => f.ACTV_NO === act.actvNo + ); + + return { + ...act, + files: activityFiles, + }; + } catch (error) { + console.error( + `Activity ${act.actvNo} 파일 조회 실패:`, + error + ); + return { + ...act, + files: [], + }; + } + }) + ); + + return { + ...rev, + activities: activitiesWithFiles, + }; + }) + ); + + console.log(`[SWP API] 조회 완료: ${revisionsWithFiles.length}개 리비전`); + + return { + docNo: parsed.docNo, + revisions: revisionsWithFiles, + }; +} + +/** + * 특정 Revision의 Activity 목록만 조회 (경량 버전) + * + * @param projNo 프로젝트 번호 + * @param docNo 문서 번호 + * @param revNo Revision 번호 (예: "04") + * @returns 해당 Revision의 Activity 목록 + */ +export async function fetchRevisionActivities( + projNo: string, + docNo: string, + revNo: string +): Promise<ParsedRevisionTree["revisions"][0] | null> { + const tree = await fetchGetRevTreeCompleteList({ + proj_no: projNo, + doc_no: docNo, + }); + + const parsed = await parseRevisionTree(tree); + + return parsed.revisions.find((r) => r.revNo === revNo) || null; +} + |
