summaryrefslogtreecommitdiff
path: root/lib/knox-api/approval/approval.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/knox-api/approval/approval.ts')
-rw-r--r--lib/knox-api/approval/approval.ts603
1 files changed, 603 insertions, 0 deletions
diff --git a/lib/knox-api/approval/approval.ts b/lib/knox-api/approval/approval.ts
new file mode 100644
index 00000000..6a21e113
--- /dev/null
+++ b/lib/knox-api/approval/approval.ts
@@ -0,0 +1,603 @@
+"use server"
+
+// Knox API Approval 서버 액션들
+// 가이드: lib/knox-api/approval/guide.html
+
+// ========== 타입 정의 ==========
+
+// 공통 응답 타입
+export interface BaseResponse {
+ result: string;
+}
+
+// 결재 경로 타입
+export interface ApprovalLine {
+ epId?: string;
+ userId?: string;
+ emailAddress?: string;
+ seq: string;
+ role: string; // 기안(0), 결재(1), 합의(2), 후결(3), 병렬합의(4), 병렬결재(7), 통보(9)
+ aplnStatsCode: string; // 미결(0), 결재(1), 반려(2), 전결(3), 자동결재(5)
+ arbPmtYn: string; // 전결권한여부
+ contentsMdfyPmtYn: string; // 본문수정권한여부
+ aplnMdfyPmtYn: string; // 경로변경권한여부
+ opinion?: string; // 상신의견 (상신자만)
+}
+
+// 결재 상신 요청 타입
+export interface SubmitApprovalRequest {
+ contents: string; // 결재본문
+ contentsType: string; // 본문종류 (TEXT, HTML, MIME)
+ docSecuType: string; // 보안문서타입 (PERSONAL, CONFIDENTIAL, CONFIDENTIAL_STRICT)
+ notifyOption: string; // 통보옵션 (0-3)
+ urgYn: string; // 긴급여부 (Y/N)
+ sbmDt: string; // 상신일시 (YYYYMMDDHHMMSS)
+ timeZone: string; // 타임존 (GMT, GMT+9 등)
+ docMngSaveCode: string; // 문서관리저장코드 (0: 안함, 1: 저장)
+ subject: string; // 결재제목
+ sbmLang: string; // 상신언어 (ko, ja, zh, en)
+ apInfId: string; // 연계ID (32자리 고유값)
+ importantYn?: string; // 중요여부 (Y/N)
+ aplns: ApprovalLine[]; // 결재경로
+ attachments?: File[]; // 첨부파일
+}
+
+// 결재 상신 응답 타입
+export interface SubmitApprovalResponse extends BaseResponse {
+ data: {
+ apInfId: string;
+ };
+}
+
+// 결재 상세 조회 응답 타입
+export interface ApprovalDetailResponse extends BaseResponse {
+ data: {
+ contentsType: string;
+ sbmDt: string;
+ sbmLang: string;
+ apInfId: string;
+ systemId: string;
+ notifyOption: string;
+ urgYn: string;
+ docSecuType: string;
+ status: string; // 암호화실패(-3), 암호화중(-2), 예약상신(-1), 보류(0), 진행중(1), 완결(2), 반려(3), 상신취소(4), 전결(5), 후완결(6)
+ timeZone: string;
+ subject: string;
+ aplns: any[];
+ attachments?: any[];
+ };
+}
+
+// 결재 본문 조회 응답 타입
+export interface ApprovalContentResponse extends BaseResponse {
+ data: {
+ contents: string;
+ contentsType: string;
+ apInfId: string;
+ };
+}
+
+// 결재 상황 조회 요청 타입
+export interface ApprovalStatusRequest {
+ apinfids: { apinfid: string }[];
+}
+
+// 결재 상황 조회 응답 타입
+export interface ApprovalStatusResponse extends BaseResponse {
+ data: {
+ apInfId: string;
+ docChgNum: string;
+ status: string;
+ }[];
+}
+
+// 상신 취소 응답 타입
+export interface CancelApprovalResponse extends BaseResponse {
+ data: {
+ apInfId: string;
+ };
+}
+
+// 개인 결재경로 목록 조회 응답 타입
+export interface OwnApprovalLineListResponse extends BaseResponse {
+ data: any[];
+}
+
+// 개인 결재경로 상세 조회 응답 타입
+export interface OwnApprovalLineDetailResponse extends BaseResponse {
+ data: any;
+}
+
+// 상신함 리스트 조회 응답 타입
+export interface SubmissionListResponse extends BaseResponse {
+ data: any[];
+}
+
+// 연계 이력 조회 응답 타입
+export interface ApprovalHistoryResponse extends BaseResponse {
+ data: any[];
+}
+
+// 연계 ID 조회 응답 타입
+export interface ApprovalIdsResponse extends BaseResponse {
+ data: any[];
+}
+
+// ========== 서버 액션 함수들 ==========
+
+/**
+ * 결재 상신
+ * POST /approval/api/v2.0/approvals/submit
+ */
+export async function submitApproval(
+ request: SubmitApprovalRequest,
+ systemId: string
+): Promise<SubmitApprovalResponse> {
+ try {
+ const formData = new FormData();
+
+ // JSON 데이터 생성
+ const approvalData = {
+ contents: request.contents,
+ contentsType: request.contentsType,
+ docSecuType: request.docSecuType,
+ notifyOption: request.notifyOption,
+ urgYn: request.urgYn,
+ sbmDt: request.sbmDt,
+ timeZone: request.timeZone,
+ docMngSaveCode: request.docMngSaveCode,
+ subject: request.subject,
+ sbmLang: request.sbmLang,
+ apInfId: request.apInfId,
+ importantYn: request.importantYn,
+ aplns: request.aplns
+ };
+
+ formData.append('approval', JSON.stringify(approvalData));
+
+ // 첨부파일 처리
+ if (request.attachments) {
+ request.attachments.forEach((file) => {
+ formData.append('attachments', file);
+ });
+ }
+
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/submit`, {
+ method: 'POST',
+ headers: {
+ 'System-ID': systemId,
+ },
+ body: formData,
+ });
+
+ if (!response.ok) {
+ throw new Error(`결재 상신 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('결재 상신 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 보안 결재 상신
+ * POST /approval/api/v2.0/approvals/secu-submit
+ */
+export async function submitSecurityApproval(
+ request: SubmitApprovalRequest,
+ systemId: string
+): Promise<SubmitApprovalResponse> {
+ try {
+ const formData = new FormData();
+
+ // JSON 데이터 생성
+ const approvalData = {
+ contents: request.contents,
+ contentsType: request.contentsType,
+ docSecuType: request.docSecuType, // CONFIDENTIAL 또는 CONFIDENTIAL_STRICT
+ notifyOption: request.notifyOption,
+ urgYn: request.urgYn,
+ sbmDt: request.sbmDt,
+ timeZone: request.timeZone,
+ docMngSaveCode: request.docMngSaveCode,
+ subject: request.subject,
+ sbmLang: request.sbmLang,
+ apInfId: request.apInfId,
+ importantYn: request.importantYn,
+ aplns: request.aplns
+ };
+
+ formData.append('approval', JSON.stringify(approvalData));
+
+ // 첨부파일 처리
+ if (request.attachments) {
+ request.attachments.forEach((file) => {
+ formData.append('attachments', file);
+ });
+ }
+
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/secu-submit`, {
+ method: 'POST',
+ headers: {
+ 'System-ID': systemId,
+ },
+ body: formData,
+ });
+
+ if (!response.ok) {
+ throw new Error(`보안 결재 상신 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('보안 결재 상신 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 결재 상세 상황 조회
+ * GET /approval/api/v2.0/approvals/{apInfId}/detail
+ */
+export async function getApprovalDetail(
+ apInfId: string,
+ systemId: string
+): Promise<ApprovalDetailResponse> {
+ try {
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/${apInfId}/detail`, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`결재 상세 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('결재 상세 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 결재 본문 조회
+ * GET /approval/api/v2.0/approvals/{apInfId}/content
+ */
+export async function getApprovalContent(
+ apInfId: string,
+ systemId: string
+): Promise<ApprovalContentResponse> {
+ try {
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/${apInfId}/content`, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`결재 본문 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('결재 본문 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 결재 상황 조회
+ * POST /approval/api/v2.0/approvals/status
+ */
+export async function getApprovalStatus(
+ request: ApprovalStatusRequest,
+ systemId: string
+): Promise<ApprovalStatusResponse> {
+ try {
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/status`, {
+ method: 'POST',
+ headers: {
+ 'System-ID': systemId,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(request.apinfids),
+ });
+
+ if (!response.ok) {
+ throw new Error(`결재 상황 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('결재 상황 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 결재 연계 ID 조회
+ * GET /approval/api/v2.0/approvals/apinfids
+ */
+export async function getApprovalIds(
+ systemId: string,
+ apIds?: string[]
+): Promise<ApprovalIdsResponse> {
+ try {
+ let url = `${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/apinfids`;
+
+ if (apIds && apIds.length > 0) {
+ const params = new URLSearchParams();
+ apIds.forEach(id => params.append('apId', id));
+ url += `?${params.toString()}`;
+ }
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`결재 연계 ID 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('결재 연계 ID 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 상신함 리스트 조회
+ * GET /approval/api/v2.0/approvals/submission
+ */
+export async function getSubmissionList(
+ systemId: string,
+ params?: Record<string, string>
+): Promise<SubmissionListResponse> {
+ try {
+ let url = `${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/submission`;
+
+ if (params) {
+ const searchParams = new URLSearchParams(params);
+ url += `?${searchParams.toString()}`;
+ }
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`상신함 리스트 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('상신함 리스트 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 연계 이력 조회
+ * GET /approval/api/v2.0/approvals/apinfidinfos
+ */
+export async function getApprovalHistory(
+ systemId: string,
+ params?: Record<string, string>
+): Promise<ApprovalHistoryResponse> {
+ try {
+ let url = `${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/apinfidinfos`;
+
+ if (params) {
+ const searchParams = new URLSearchParams(params);
+ url += `?${searchParams.toString()}`;
+ }
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`연계 이력 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('연계 이력 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 상신 취소
+ * POST /approval/api/v2.0/approvals/{apInfId}/cancel
+ */
+export async function cancelApproval(
+ apInfId: string,
+ systemId: string
+): Promise<CancelApprovalResponse> {
+ try {
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/${apInfId}/cancel`, {
+ method: 'POST',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`상신 취소 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('상신 취소 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 저장된 결재경로 목록 조회
+ * GET /approval/api/v2.0/approvals/ownaplnlist
+ */
+export async function getOwnApprovalLineList(
+ systemId: string,
+ params?: Record<string, string>
+): Promise<OwnApprovalLineListResponse> {
+ try {
+ let url = `${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/ownaplnlist`;
+
+ if (params) {
+ const searchParams = new URLSearchParams(params);
+ url += `?${searchParams.toString()}`;
+ }
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`저장된 결재경로 목록 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('저장된 결재경로 목록 조회 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 저장된 결재경로 상세 조회
+ * GET /approval/api/v2.0/approvals/{pslAplnId}/ownaplndetail
+ */
+export async function getOwnApprovalLineDetail(
+ pslAplnId: string,
+ systemId: string
+): Promise<OwnApprovalLineDetailResponse> {
+ try {
+ const response = await fetch(`${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/approvals/${pslAplnId}/ownaplndetail`, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`저장된 결재경로 상세 조회 실패: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('저장된 결재경로 상세 조회 오류:', error);
+ throw error;
+ }
+}
+
+// ========== 유틸리티 함수들 ==========
+
+/**
+ * 결재 상신 요청 데이터 생성 도우미
+ */
+export async function createSubmitApprovalRequest(
+ contents: string,
+ subject: string,
+ approvalLines: ApprovalLine[],
+ options: Partial<SubmitApprovalRequest> = {}
+): Promise<SubmitApprovalRequest> {
+ const now = new Date();
+ const sbmDt = now.toISOString().replace(/[-:T]/g, '').slice(0, 14);
+ const apInfId = `${process.env.KNOX_SYSTEM_ID || 'DEFAULT'}${sbmDt}${Math.random().toString(36).substr(2, 9)}`.padEnd(32, '0');
+
+ return {
+ contents,
+ subject,
+ aplns: approvalLines,
+ contentsType: 'TEXT',
+ docSecuType: 'PERSONAL',
+ notifyOption: '0',
+ urgYn: 'N',
+ sbmDt,
+ timeZone: 'GMT+9',
+ docMngSaveCode: '0',
+ sbmLang: 'ko',
+ apInfId,
+ importantYn: 'N',
+ ...options
+ };
+}
+
+/**
+ * 결재 라인 생성 도우미
+ */
+export async function createApprovalLine(
+ userInfo: { epId?: string; userId?: string; emailAddress?: string },
+ role: string,
+ seq: string,
+ options: Partial<ApprovalLine> = {}
+): Promise<ApprovalLine> {
+ return {
+ ...userInfo,
+ seq,
+ role,
+ aplnStatsCode: '0',
+ arbPmtYn: 'Y',
+ contentsMdfyPmtYn: 'Y',
+ aplnMdfyPmtYn: 'Y',
+ ...options
+ };
+}
+
+/**
+ * 결재 상태 문자열 변환
+ */
+export async function getApprovalStatusText(status: string): Promise<string> {
+ const statusMap: Record<string, string> = {
+ '-3': '암호화실패',
+ '-2': '암호화중',
+ '-1': '예약상신',
+ '0': '보류',
+ '1': '진행중',
+ '2': '완결',
+ '3': '반려',
+ '4': '상신취소',
+ '5': '전결',
+ '6': '후완결'
+ };
+
+ return statusMap[status] || '알 수 없음';
+}
+
+/**
+ * 결재 역할 문자열 변환
+ */
+export async function getApprovalRoleText(role: string): Promise<string> {
+ const roleMap: Record<string, string> = {
+ '0': '기안',
+ '1': '결재',
+ '2': '합의',
+ '3': '후결',
+ '4': '병렬합의',
+ '7': '병렬결재',
+ '9': '통보'
+ };
+
+ return roleMap[role] || '알 수 없음';
+}