From 97f6cd77be2e35e0abfeffae294566f1a7f72a32 Mon Sep 17 00:00:00 2001 From: chpark Date: Tue, 10 Feb 2026 17:01:35 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=B0=EC=9E=AC=20=EA=B1=B4=EC=88=98=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=B0=EC=9E=AC=20=EC=83=81=EC=84=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=95=84=EB=A7=88=EB=9E=80=EC=8A=A4=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/approval/amaranthApprovalDetail.jsp | 247 +++++++++++ .../WEB-INF/view/approval/approvalList.jsp | 289 +++++++------ WebContent/WEB-INF/view/main/header.jsp | 10 +- .../pms/api/AmaranthApprovalApiClient.java | 279 ++++++++++++ .../pms/controller/ApprovalController.java | 39 ++ src/com/pms/service/ApprovalService.java | 400 ++++++++++++++++++ 6 files changed, 1118 insertions(+), 146 deletions(-) create mode 100644 WebContent/WEB-INF/view/approval/amaranthApprovalDetail.jsp diff --git a/WebContent/WEB-INF/view/approval/amaranthApprovalDetail.jsp b/WebContent/WEB-INF/view/approval/amaranthApprovalDetail.jsp new file mode 100644 index 0000000..b90705e --- /dev/null +++ b/WebContent/WEB-INF/view/approval/amaranthApprovalDetail.jsp @@ -0,0 +1,247 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> + + + + +<%=Constants.SYSTEM_NAME%> - 결재 문서 상세 + + + + + + +
+
+
+

+ 결재 문서 상세 +

+
+
+
+
문서 정보를 조회중입니다...
+
+
+
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/WebContent/WEB-INF/view/approval/approvalList.jsp b/WebContent/WEB-INF/view/approval/approvalList.jsp index 71cb689..be4d6c3 100644 --- a/WebContent/WEB-INF/view/approval/approvalList.jsp +++ b/WebContent/WEB-INF/view/approval/approvalList.jsp @@ -8,58 +8,159 @@ <%=Constants.SYSTEM_NAME%> - +<%-- [주석 처리] 기존 JSTL 페이징 변수 - Amaranth API AJAX 방식으로 변경 +--%> @@ -83,33 +184,17 @@ function openApprovalPop(){ - - - <%-- - --%>
- + - + - + - ~ + ~ - - - - - - - -
@@ -119,20 +204,20 @@ function openApprovalPop(){ - + - - + + No - 결재번호 - 대상구분 + 문서번호 + 양식명 제목 - 상신일 - 상신자 + 기안일 + 기안자 상태 @@ -142,101 +227,23 @@ function openApprovalPop(){ - + - - + + - - - - - ${item.RNUM} - ${item.ROUTE_NO} - ${item.TARGET_NAME} - ${item.APPROVAL_TITLE} - ${item.ROUTE_REGDATE} - ${item.WRITER_DEPT_NAME} ${item.WRITER_USER_NAME} - - - - 결재중 - - - 반려 - - - 결재완료 - - - 결재취소 - - - ${item.STATUS} - - - - - - - - - 조회된 정보가 없습니다. - - - + + + 조회중... + +
- - <%-- - nPage ${nPage} - prevPage ${prevPage} - maxPage ${maxPage} - nextPage ${nextPage} - totalCount ${totalCount} - --%> - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
prevprev${nPage}${status.index}nextnext
-

총 ${totalCount}건

+
-
diff --git a/WebContent/WEB-INF/view/main/header.jsp b/WebContent/WEB-INF/view/main/header.jsp index 207889d..e448d21 100644 --- a/WebContent/WEB-INF/view/main/header.jsp +++ b/WebContent/WEB-INF/view/main/header.jsp @@ -142,9 +142,9 @@ $(function(){ fn_goMyTaskMyApproval(); }); - //결재건수 세팅 - fn_setApprovalCnt(); - setInterval(fn_setApprovalCnt, 60000); //refresh + //결재건수 세팅 (주석 해제 시 Amaranth10 결재 건수 조회 활성화) + //fn_setApprovalCnt(); + //setInterval(fn_setApprovalCnt, 60000); //refresh //setTimeout(() => fn_setApprovalCnt(), 10000); $(".blink_none").children("span").children("img").attr({src:"/images/bell.png"}); @@ -411,8 +411,8 @@ function fn_setApprovalCnt(){ - - 결재 + Q&A diff --git a/src/com/pms/api/AmaranthApprovalApiClient.java b/src/com/pms/api/AmaranthApprovalApiClient.java index f1d4097..9cc4338 100644 --- a/src/com/pms/api/AmaranthApprovalApiClient.java +++ b/src/com/pms/api/AmaranthApprovalApiClient.java @@ -403,6 +403,285 @@ public class AmaranthApprovalApiClient { } } + /** + * 결재 문서 목록 조회 - 서버 인증 방식 + * @param baseUrl API 서버의 기본 URL + * @param empSeq 사원 시퀀스 + * @param compSeq 회사 시퀀스 + * @param menuId 결재함 메뉴 ID (미결: 1001000 등) + * @param fromDt 조회 시작일 (YYYY-MM-DD) + * @param toDt 조회 종료일 (YYYY-MM-DD) + * @param keyWord 검색어 + * @param page 페이지 번호 + * @param pageSize 페이지 사이즈 + * @return API 응답 결과 (JSON 문자열) + */ + public String getApprovalDocList(String baseUrl, String empSeq, String compSeq, + String menuId, String fromDt, String toDt, String keyWord, + String page, String pageSize) throws Exception { + + System.setProperty("https.protocols", "TLSv1.2"); + + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return null; } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + } + }; + + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { + public boolean verify(String hostname, javax.net.ssl.SSLSession session) { return true; } + }); + + String urlPath = "/apiproxy/api99u02A02"; + String cleanBaseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + String fullUrl = cleanBaseUrl + urlPath; + + URL url = new URL(fullUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(30000); + connection.setReadTimeout(30000); + connection.setInstanceFollowRedirects(false); + + try { + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("callerName", CALLER_NAME); + connection.setRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); + + String transactionId = generateTransactionId(); + connection.setRequestProperty("transaction-id", transactionId); + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + connection.setRequestProperty("timestamp", timestamp); + + connection.setRequestProperty("groupSeq", GROUP_SEQ); + + String wehagoSign = generateWehagoSign(ACCESS_TOKEN, transactionId, timestamp, urlPath, HASH_KEY); + connection.setRequestProperty("wehago-sign", wehagoSign); + + // 요청 본문 작성 + StringBuilder body = new StringBuilder(); + body.append("{"); + body.append("\"header\":{"); + body.append("\"empSeq\":\"").append(escapeJson(empSeq)).append("\","); + body.append("\"groupSeq\":\"").append(escapeJson(GROUP_SEQ)).append("\""); + body.append("},"); + body.append("\"body\":{"); + body.append("\"menuId\":\"").append(escapeJson(menuId)).append("\","); + if (fromDt != null && !fromDt.isEmpty()) { + body.append("\"fromDt\":\"").append(escapeJson(fromDt)).append("\","); + } + if (toDt != null && !toDt.isEmpty()) { + body.append("\"toDt\":\"").append(escapeJson(toDt)).append("\","); + } + body.append("\"pageSize\":\"").append(escapeJson(pageSize != null ? pageSize : "30")).append("\","); + body.append("\"keyWord\":\"").append(escapeJson(keyWord != null ? keyWord : "")).append("\","); + body.append("\"page\":\"").append(escapeJson(page != null ? page : "1")).append("\","); + body.append("\"sort\":\"10\","); + body.append("\"langCode\":\"kr\","); + body.append("\"searchKind\":\"1\","); + body.append("\"companyInfo\":{"); + body.append("\"compSeq\":\"").append(escapeJson(compSeq)).append("\""); + body.append("}"); + body.append("}"); + body.append("}"); + + String requestBody = body.toString(); + + System.out.println("=== Amaranth 결재 문서 목록 조회 ==="); + System.out.println("URL: " + fullUrl); + System.out.println("Request Body: " + requestBody); + + connection.setDoOutput(true); + connection.setDoInput(true); + + OutputStreamWriter writer = new OutputStreamWriter( + connection.getOutputStream(), StandardCharsets.UTF_8); + writer.write(requestBody); + writer.flush(); + writer.close(); + + int responseCode = connection.getResponseCode(); + + BufferedReader reader = null; + StringBuilder response = new StringBuilder(); + + try { + if (responseCode >= 200 && responseCode < 300) { + reader = new BufferedReader(new InputStreamReader( + connection.getInputStream(), StandardCharsets.UTF_8)); + } else { + java.io.InputStream errorStream = connection.getErrorStream(); + if (errorStream != null) { + reader = new BufferedReader(new InputStreamReader( + errorStream, StandardCharsets.UTF_8)); + } else { + throw new Exception("결재 문서 목록 조회 실패: HTTP " + responseCode); + } + } + + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } finally { + if (reader != null) reader.close(); + } + + System.out.println("Response Code: " + responseCode); + + if (responseCode >= 200 && responseCode < 300) { + return response.toString(); + } else { + throw new Exception("결재 문서 목록 조회 실패: HTTP " + responseCode + " - " + response.toString()); + } + + } finally { + connection.disconnect(); + } + } + + /** + * 결재 문서 상세 조회 - 서버 인증 방식 + * @param baseUrl API 서버 기본 URL + * @param empSeq 사원 시퀀스 + * @param compSeq 회사 시퀀스 + * @param deptSeq 부서 시퀀스 + * @param docId 문서 ID + * @return API 응답 결과 (JSON 문자열) + */ + public String getApprovalDocDetail(String baseUrl, String empSeq, String compSeq, + String deptSeq, String docId) throws Exception { + + System.setProperty("https.protocols", "TLSv1.2"); + + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return null; } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + } + }; + + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { + public boolean verify(String hostname, javax.net.ssl.SSLSession session) { return true; } + }); + + String urlPath = "/apiproxy/api99u02A04"; + String cleanBaseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + String fullUrl = cleanBaseUrl + urlPath; + + URL url = new URL(fullUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(30000); + connection.setReadTimeout(30000); + connection.setInstanceFollowRedirects(false); + + try { + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("callerName", CALLER_NAME); + connection.setRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); + + String transactionId = generateTransactionId(); + connection.setRequestProperty("transaction-id", transactionId); + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + connection.setRequestProperty("timestamp", timestamp); + + connection.setRequestProperty("groupSeq", GROUP_SEQ); + + String wehagoSign = generateWehagoSign(ACCESS_TOKEN, transactionId, timestamp, urlPath, HASH_KEY); + connection.setRequestProperty("wehago-sign", wehagoSign); + + // 요청 본문 작성 + StringBuilder body = new StringBuilder(); + body.append("{"); + body.append("\"header\":{"); + body.append("\"pId\":\"\","); + body.append("\"tId\":\"\","); + body.append("\"groupSeq\":\"").append(escapeJson(GROUP_SEQ)).append("\","); + body.append("\"empSeq\":\"").append(escapeJson(empSeq)).append("\""); + body.append("},"); + body.append("\"body\":{"); + body.append("\"migYn\":\"0\","); + body.append("\"langCode\":\"kr\","); + body.append("\"spMigYn\":\"0\","); + body.append("\"docId\":\"").append(escapeJson(docId)).append("\","); + body.append("\"spDocId\":\"\","); + body.append("\"companyInfo\":{"); + body.append("\"compSeq\":\"").append(escapeJson(compSeq)).append("\","); + body.append("\"deptSeq\":\"").append(escapeJson(deptSeq)).append("\""); + body.append("}"); + body.append("}"); + body.append("}"); + + String requestBody = body.toString(); + + System.out.println("=== Amaranth 결재 문서 상세 조회 ==="); + System.out.println("URL: " + fullUrl); + System.out.println("docId: " + docId); + System.out.println("Request Body: " + requestBody); + + connection.setDoOutput(true); + connection.setDoInput(true); + + OutputStreamWriter writer = new OutputStreamWriter( + connection.getOutputStream(), StandardCharsets.UTF_8); + writer.write(requestBody); + writer.flush(); + writer.close(); + + int responseCode = connection.getResponseCode(); + + BufferedReader reader = null; + StringBuilder response = new StringBuilder(); + + try { + if (responseCode >= 200 && responseCode < 300) { + reader = new BufferedReader(new InputStreamReader( + connection.getInputStream(), StandardCharsets.UTF_8)); + } else { + java.io.InputStream errorStream = connection.getErrorStream(); + if (errorStream != null) { + reader = new BufferedReader(new InputStreamReader( + errorStream, StandardCharsets.UTF_8)); + } else { + throw new Exception("결재 문서 상세 조회 실패: HTTP " + responseCode); + } + } + + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } finally { + if (reader != null) reader.close(); + } + + System.out.println("Response Code: " + responseCode); + + if (responseCode >= 200 && responseCode < 300) { + return response.toString(); + } else { + throw new Exception("결재 문서 상세 조회 실패: HTTP " + responseCode + " - " + response.toString()); + } + + } finally { + connection.disconnect(); + } + } + /** * loginId 암호화 (AES128 CBC PKCS5Padding) */ diff --git a/src/com/pms/controller/ApprovalController.java b/src/com/pms/controller/ApprovalController.java index a6fe717..3a4170c 100644 --- a/src/com/pms/controller/ApprovalController.java +++ b/src/com/pms/controller/ApprovalController.java @@ -47,6 +47,45 @@ public class ApprovalController { return "/approval/approvalList"; } + /** + * Amaranth10 전자결재 문서 목록 조회 (AJAX 전용) + * approvalList.jsp에서만 사용하는 전용 엔드포인트 + * @param request + * @param paramMap + * @return JSON 결과 + */ + @RequestMapping("/approval/getAmaranthApprovalDocList.do") + public String getAmaranthApprovalDocList(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{ + String jsonResult = approvalService.getAmaranthApprovalDocListJson(request, paramMap); + request.setAttribute("RESULT", jsonResult); + return "/ajax/ajaxResult"; + } + + /** + * Amaranth10 결재 문서 상세 팝업 페이지 + * approvalList.jsp에서 문서번호 클릭 시 호출 + * @param request + * @param paramMap docId, deptSeq 필수 + * @return 상세 팝업 JSP + */ + @RequestMapping("/approval/amaranthApprovalDetail.do") + public String amaranthApprovalDetail(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{ + return "/approval/amaranthApprovalDetail"; + } + + /** + * Amaranth10 결재 문서 상세 데이터 조회 (AJAX 전용) + * @param request + * @param paramMap docId, deptSeq 필수 + * @return JSON 결과 + */ + @RequestMapping("/approval/getAmaranthApprovalDocDetail.do") + public String getAmaranthApprovalDocDetail(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{ + String jsonResult = approvalService.getAmaranthApprovalDocDetail(request, paramMap); + request.setAttribute("RESULT", jsonResult); + return "/ajax/ajaxResult"; + } + /** * 결재 상신 Form * @param request diff --git a/src/com/pms/service/ApprovalService.java b/src/com/pms/service/ApprovalService.java index 0ebf9dc..9295d23 100644 --- a/src/com/pms/service/ApprovalService.java +++ b/src/com/pms/service/ApprovalService.java @@ -1228,6 +1228,406 @@ public class ApprovalService { return count; } + /** + * Amaranth10 전자결재 문서 목록 조회 - JSON 문자열 반환 (AJAX 전용) + * approvalList.jsp 전용 엔드포인트에서 호출 + * @param request HttpServletRequest + * @param paramMap 검색 파라미터 + * @return JSON 문자열 + */ + public String getAmaranthApprovalDocListJson(HttpServletRequest request, Map paramMap){ + Map resultMap = getAmaranthApprovalDocList(request, paramMap); + return buildApprovalDocListJson(resultMap); + } + + /** + * Amaranth10 결재 문서 상세 조회 + * @param request HttpServletRequest + * @param paramMap docId, deptSeq 필수 + * @return API 응답 JSON 원본 (contents HTML 포함) + */ + public String getAmaranthApprovalDocDetail(HttpServletRequest request, Map paramMap){ + try { + // 세션에서 사원 정보 가져오기 + HttpSession session = request.getSession(); + PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String empSeq = CommonUtils.checkNull(person.getEmpseq()); + String compSeq = "1000"; + + String docId = CommonUtils.checkNull(paramMap.get("docId")); + String deptSeq = CommonUtils.checkNull(paramMap.get("deptSeq")); + + System.out.println("=== Amaranth 결재 문서 상세 조회 ==="); + System.out.println("empSeq: " + empSeq + ", docId: " + docId + ", deptSeq: " + deptSeq); + + if(empSeq == null || empSeq.isEmpty()){ + System.err.println("empSeq가 비어있습니다."); + return "{\"resultCode\":-1,\"resultMsg\":\"empSeq가 비어있습니다.\"}"; + } + if(docId == null || docId.isEmpty()){ + System.err.println("docId가 비어있습니다."); + return "{\"resultCode\":-1,\"resultMsg\":\"docId가 비어있습니다.\"}"; + } + + // API 호출 + com.pms.api.AmaranthApprovalApiClient apiClient = new com.pms.api.AmaranthApprovalApiClient(); + String baseUrl = "https://erp.rps-korea.com"; + + String apiResponse = apiClient.getApprovalDocDetail(baseUrl, empSeq, compSeq, deptSeq, docId); + + System.out.println("상세 조회 API 응답 길이: " + (apiResponse != null ? apiResponse.length() : 0) + " bytes"); + + return apiResponse; + + } catch(Exception e){ + System.err.println("Amaranth 결재 문서 상세 조회 오류: " + e.getMessage()); + e.printStackTrace(); + return "{\"resultCode\":-1,\"resultMsg\":\"" + escapeJsonValue(e.getMessage()) + "\"}"; + } + } + + /** + * 결재 문서 목록 결과를 JSON 문자열로 변환 + */ + private String buildApprovalDocListJson(Map resultMap){ + StringBuilder json = new StringBuilder(); + json.append("{"); + + // 페이징 정보 + json.append("\"totalCount\":").append(resultMap.get("TOTAL_COUNT")).append(","); + json.append("\"maxPage\":").append(resultMap.get("MAX_PAGE_SIZE")).append(","); + json.append("\"nextPage\":").append(resultMap.get("NEXT_PAGE")).append(","); + json.append("\"prevPage\":").append(resultMap.get("PREV_PAGE")).append(","); + + // 문서 리스트 + json.append("\"list\":["); + ArrayList> list = (ArrayList>) resultMap.get("LIST"); + if(list != null){ + for(int i = 0; i < list.size(); i++){ + if(i > 0) json.append(","); + HashMap doc = list.get(i); + json.append("{"); + json.append("\"RNUM\":\"").append(escapeJsonValue(String.valueOf(doc.get("RNUM")))).append("\","); + json.append("\"DOC_ID\":\"").append(escapeJsonValue(String.valueOf(doc.get("DOC_ID")))).append("\","); + json.append("\"DOC_TITLE\":\"").append(escapeJsonValue(String.valueOf(doc.get("DOC_TITLE")))).append("\","); + json.append("\"FORM_NAME\":\"").append(escapeJsonValue(String.valueOf(doc.get("FORM_NAME")))).append("\","); + json.append("\"EMP_NAME\":\"").append(escapeJsonValue(String.valueOf(doc.get("EMP_NAME")))).append("\","); + json.append("\"DEPT_NAME\":\"").append(escapeJsonValue(String.valueOf(doc.get("DEPT_NAME")))).append("\","); + json.append("\"DOC_STS_NAME\":\"").append(escapeJsonValue(String.valueOf(doc.get("DOC_STS_NAME")))).append("\","); + json.append("\"REP_DT\":\"").append(escapeJsonValue(String.valueOf(doc.get("REP_DT")))).append("\","); + json.append("\"DOC_STS\":\"").append(escapeJsonValue(String.valueOf(doc.get("DOC_STS")))).append("\","); + json.append("\"DEPT_SEQ\":\"").append(escapeJsonValue(String.valueOf(doc.get("DEPT_SEQ")))).append("\""); + json.append("}"); + } + } + json.append("]"); + json.append("}"); + + return json.toString(); + } + + /** + * JSON 값 이스케이프 (서비스 내부용) + */ + private String escapeJsonValue(String value){ + if(value == null || "null".equals(value)) return ""; + return value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t"); + } + + /** + * Amaranth10 전자결재 문서 목록 조회 (서버 인증 방식) + * @param request HttpServletRequest + * @param paramMap 검색 파라미터 (search_approvalTitle, search_fromDate, search_toDate, page 등) + * @return 결재 문서 목록 및 페이징 정보 + */ + public Map getAmaranthApprovalDocList(HttpServletRequest request, Map paramMap){ + Map resultMap = new HashMap(); + ArrayList> resultList = new ArrayList(); + + try{ + // 세션에서 사원 정보 가져오기 + HttpSession session = request.getSession(); + PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String empSeq = CommonUtils.checkNull(person.getEmpseq()); + String compSeq = "1000"; // 회사 시퀀스 + + System.out.println("=== Amaranth 결재 문서 목록 조회 ==="); + System.out.println("empSeq: " + empSeq); + + if(empSeq == null || empSeq.isEmpty()){ + System.err.println("empSeq가 비어있습니다. LoginId 업데이트 배치를 먼저 실행하세요."); + resultMap.put("LIST", resultList); + resultMap.put("TOTAL_COUNT", 0); + return resultMap; + } + + // 검색 조건 매핑 + String keyWord = CommonUtils.checkNull(paramMap.get("search_approvalTitle")); + String fromDt = CommonUtils.checkNull(paramMap.get("search_fromDate")); + String toDt = CommonUtils.checkNull(paramMap.get("search_toDate")); + String page = CommonUtils.checkNull(paramMap.get("page"), "1"); + String pageSize = "20"; + + // menuId: 기안함(1000400) - 결재 전체 목록 조회용 + String menuId = CommonUtils.checkNull(paramMap.get("menuId"), "1000400"); + + // API 호출 + com.pms.api.AmaranthApprovalApiClient apiClient = new com.pms.api.AmaranthApprovalApiClient(); + String baseUrl = "https://erp.rps-korea.com"; + + String apiResponse = apiClient.getApprovalDocList( + baseUrl, empSeq, compSeq, menuId, fromDt, toDt, keyWord, page, pageSize); + + System.out.println("API 응답 길이: " + (apiResponse != null ? apiResponse.length() : 0) + " bytes"); + + // JSON 파싱 + resultList = parseApprovalDocList(apiResponse); + int totalCnt = extractTotalCnt(apiResponse); + + // 번호 매기기 (RNUM) + int startNum = (Integer.parseInt(page) - 1) * Integer.parseInt(pageSize); + for(int i = 0; i < resultList.size(); i++){ + resultList.get(i).put("RNUM", String.valueOf(startNum + i + 1)); + } + + // 페이징 정보 설정 + int currentPage = Integer.parseInt(page); + int pageSizeInt = Integer.parseInt(pageSize); + int maxPage = (int) Math.ceil((double) totalCnt / pageSizeInt); + if(maxPage < 1) maxPage = 1; + + resultMap.put("LIST", resultList); + resultMap.put("TOTAL_COUNT", totalCnt); + resultMap.put("MAX_PAGE_SIZE", maxPage); + resultMap.put("NEXT_PAGE", Math.min(currentPage + 1, maxPage)); + resultMap.put("PREV_PAGE", Math.max(currentPage - 1, 1)); + + System.out.println("결재 문서 수: " + resultList.size() + ", 전체: " + totalCnt); + + }catch(Exception e){ + System.err.println("Amaranth 결재 문서 목록 조회 오류: " + e.getMessage()); + e.printStackTrace(); + resultMap.put("LIST", resultList); + resultMap.put("TOTAL_COUNT", 0); + resultMap.put("MAX_PAGE_SIZE", 1); + resultMap.put("NEXT_PAGE", 1); + resultMap.put("PREV_PAGE", 1); + } + + return resultMap; + } + + /** + * API 응답에서 eaDocList 배열 파싱 + * 응답 구조: {"resultData":{"eaDocList":[{...},{...}],"totalCnt":94}} + */ + private ArrayList> parseApprovalDocList(String jsonResponse){ + ArrayList> list = new ArrayList(); + + try { + // eaDocList 배열 찾기 + int eaDocListIndex = jsonResponse.indexOf("\"eaDocList\""); + if(eaDocListIndex == -1){ + System.err.println("eaDocList를 찾을 수 없습니다."); + return list; + } + + // eaDocList 다음의 [ 찾기 + int arrayStart = jsonResponse.indexOf("[", eaDocListIndex); + if(arrayStart == -1){ + System.err.println("eaDocList 배열 시작을 찾을 수 없습니다."); + return list; + } + + // 대응하는 ] 찾기 + int bracketCount = 0; + int arrayEnd = -1; + for(int i = arrayStart; i < jsonResponse.length(); i++){ + char c = jsonResponse.charAt(i); + if(c == '[') bracketCount++; + else if(c == ']') bracketCount--; + + if(bracketCount == 0){ + arrayEnd = i; + break; + } + } + + if(arrayEnd == -1){ + System.err.println("eaDocList 배열 끝을 찾을 수 없습니다."); + return list; + } + + String arrayStr = jsonResponse.substring(arrayStart + 1, arrayEnd); + + // 각 문서 객체 파싱 + int searchFrom = 0; + while(searchFrom < arrayStr.length()){ + int objStart = arrayStr.indexOf("{", searchFrom); + if(objStart == -1) break; + + // 중첩 { } 처리 + int braceCount = 0; + int objEnd = -1; + for(int i = objStart; i < arrayStr.length(); i++){ + char c = arrayStr.charAt(i); + if(c == '{') braceCount++; + else if(c == '}') braceCount--; + + if(braceCount == 0){ + objEnd = i; + break; + } + } + + if(objEnd == -1) break; + + String objStr = arrayStr.substring(objStart, objEnd + 1); + HashMap doc = parseDocObject(objStr); + if(!doc.isEmpty()){ + list.add(doc); + } + + searchFrom = objEnd + 1; + } + + System.out.println("파싱된 문서 수: " + list.size()); + + } catch (Exception e) { + System.err.println("eaDocList 파싱 오류: " + e.getMessage()); + e.printStackTrace(); + } + + return list; + } + + /** + * 개별 문서 JSON 객체 파싱 + */ + private HashMap parseDocObject(String objStr){ + HashMap doc = new HashMap(); + + try { + doc.put("DOC_ID", extractJsonStringValue(objStr, "docId")); + doc.put("DOC_TITLE", extractJsonStringValue(objStr, "docTitle")); + doc.put("FORM_NAME", extractJsonStringValue(objStr, "formName")); + doc.put("EMP_NAME", extractJsonStringValue(objStr, "empName")); + doc.put("DEPT_NAME", extractJsonStringValue(objStr, "deptName")); + doc.put("DOC_STS", extractJsonStringValue(objStr, "docSts")); + doc.put("DOC_STS_NAME", extractJsonStringValue(objStr, "docStsName")); + doc.put("POSITION_NAME", extractJsonStringValue(objStr, "positionName")); + doc.put("DUTY_NAME", extractJsonStringValue(objStr, "dutyName")); + doc.put("LINE_NAME", extractJsonStringValue(objStr, "lineName")); + doc.put("ATTACH_CNT", extractJsonStringValue(objStr, "attachCnt")); + doc.put("COMMENT_CNT", extractJsonStringValue(objStr, "commentCnt")); + doc.put("EMERGENCY_FLAG", extractJsonStringValue(objStr, "emergencyFlag")); + doc.put("DELAY_FLAG", extractJsonStringValue(objStr, "delayFlag")); + doc.put("READ_YN", extractJsonStringValue(objStr, "readYN")); + doc.put("APP_YN", extractJsonStringValue(objStr, "appYN")); + doc.put("COMP_SEQ", extractJsonStringValue(objStr, "compSeq")); + doc.put("EMP_SEQ", extractJsonStringValue(objStr, "empSeq")); + doc.put("DEPT_SEQ", extractJsonStringValue(objStr, "deptSeq")); + + // 상신일(repDt) 포맷팅: YYYYMMDDHHMISS → YYYY-MM-DD + String repDt = extractJsonStringValue(objStr, "repDt"); + if(repDt != null && repDt.length() >= 8){ + doc.put("REP_DT", repDt.substring(0, 4) + "-" + repDt.substring(4, 6) + "-" + repDt.substring(6, 8)); + } else { + doc.put("REP_DT", repDt); + } + + // 생성일(createdDt) 포맷팅 + String createdDt = extractJsonStringValue(objStr, "createdDt"); + if(createdDt != null && createdDt.length() >= 8){ + doc.put("CREATED_DT", createdDt.substring(0, 4) + "-" + createdDt.substring(4, 6) + "-" + createdDt.substring(6, 8)); + } else { + doc.put("CREATED_DT", createdDt); + } + + } catch (Exception e) { + System.err.println("문서 객체 파싱 오류: " + e.getMessage()); + } + + return doc; + } + + /** + * JSON 문자열에서 특정 키의 문자열 값 추출 + */ + private String extractJsonStringValue(String json, String key){ + String searchKey = "\"" + key + "\""; + int keyIndex = json.indexOf(searchKey); + if(keyIndex == -1) return ""; + + int colonIndex = json.indexOf(":", keyIndex + searchKey.length()); + if(colonIndex == -1) return ""; + + // 콜론 뒤의 공백 건너뛰기 + int valueStart = colonIndex + 1; + while(valueStart < json.length() && json.charAt(valueStart) == ' '){ + valueStart++; + } + + if(valueStart >= json.length()) return ""; + + // null 값 체크 + if(json.substring(valueStart).startsWith("null")){ + return ""; + } + + // 숫자 값 체크 (따옴표 없는 경우) + char firstChar = json.charAt(valueStart); + if(firstChar != '"'){ + // 숫자나 boolean 값 + int valueEnd = valueStart; + while(valueEnd < json.length()){ + char c = json.charAt(valueEnd); + if(c == ',' || c == '}' || c == ']') break; + valueEnd++; + } + return json.substring(valueStart, valueEnd).trim(); + } + + // 문자열 값 (따옴표 있는 경우) + int quoteStart = valueStart; // 이미 '"' 위치 + int quoteEnd = json.indexOf("\"", quoteStart + 1); + if(quoteEnd == -1) return ""; + + return json.substring(quoteStart + 1, quoteEnd); + } + + /** + * API 응답에서 totalCnt 추출 + */ + private int extractTotalCnt(String jsonResponse){ + try { + String searchKey = "\"totalCnt\""; + int keyIndex = jsonResponse.indexOf(searchKey); + if(keyIndex == -1) return 0; + + int colonIndex = jsonResponse.indexOf(":", keyIndex + searchKey.length()); + if(colonIndex == -1) return 0; + + int valueStart = colonIndex + 1; + while(valueStart < jsonResponse.length() && jsonResponse.charAt(valueStart) == ' '){ + valueStart++; + } + + int valueEnd = valueStart; + while(valueEnd < jsonResponse.length()){ + char c = jsonResponse.charAt(valueEnd); + if(c == ',' || c == '}' || c == ']') break; + valueEnd++; + } + + String cntStr = jsonResponse.substring(valueStart, valueEnd).trim(); + return Integer.parseInt(cntStr); + } catch (Exception e) { + System.err.println("totalCnt 추출 오류: " + e.getMessage()); + return 0; + } + } + public ArrayList getApprovalLine(HttpServletRequest request, Map paramMap){ ArrayList> resultList = new ArrayList(); SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();