summaryrefslogtreecommitdiff
path: root/lib/swp/api-client.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-17 20:47:44 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-17 20:47:44 +0900
commite6403e9610253b2bfc7a46e470c236e2ed10c626 (patch)
tree05f7e458ac8b0f3bef56553e4cfbd666f5134042 /lib/swp/api-client.ts
parentf56eb799ecdcd2baae1db26460cd1deff0a69eee (diff)
(김준회) swp: VxDCS 와 동일하게 변경, Complete된 리스트 중 없는 건이 있으면 DOCUMENT LIST에서 보여줌
Diffstat (limited to 'lib/swp/api-client.ts')
-rw-r--r--lib/swp/api-client.ts307
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;
+}
+