아마란스 결재 반영영

This commit is contained in:
2026-02-23 17:27:51 +09:00
parent 34b233c7a2
commit 0a6ca61a12
8 changed files with 428 additions and 40 deletions

View File

@@ -1779,6 +1779,7 @@ public class ApprovalService {
* @return fullUrl이 포함된 JSON 문자열
*/
public String getAmaranthSsoUrl(HttpServletRequest request, Map paramMap){
SqlSession sqlSession = null;
try {
HttpSession session = request.getSession();
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
@@ -1787,13 +1788,15 @@ public class ApprovalService {
}
String empSeq = CommonUtils.checkNull(person.getEmpseq());
String loginId = CommonUtils.checkNull(person.getUserId());
System.out.println("=== Amaranth SSO - 사용자 정보 ===");
System.out.println("userId: " + person.getUserId());
System.out.println("userId(loginId): " + loginId);
System.out.println("userName: " + person.getUserName());
System.out.println("empSeq: [" + empSeq + "]");
if(empSeq.isEmpty()){
return "{\"resultCode\":-1,\"resultMsg\":\"empSeq 정보가 없습니다. (userId: " + person.getUserId() + ") 관리자에게 문의하세요.\"}";
return "{\"resultCode\":-1,\"resultMsg\":\"empSeq 정보가 없습니다. (userId: " + loginId + ") 관리자에게 문의하세요.\"}";
}
// 파라미터 추출
@@ -1809,25 +1812,38 @@ public class ApprovalService {
String approKey = "UB_" + java.util.UUID.randomUUID().toString();
System.out.println("=== Amaranth SSO URL 생성 ===");
System.out.println("empSeq: " + empSeq);
System.out.println("empSeq: " + empSeq + ", loginId: " + loginId);
System.out.println("targetType: " + targetType + ", targetObjId: " + targetObjId);
System.out.println("outProcessCode: " + outProcessCode + ", formId: " + formId);
System.out.println("approKey: " + approKey);
// API 호출
// API 호출 (loginId 파라미터 추가)
com.pms.api.AmaranthApprovalApiClient apiClient = new com.pms.api.AmaranthApprovalApiClient();
String apiResponse = apiClient.getSsoUrl(
AMARANTH_BASE_URL, empSeq, outProcessCode, formId,
approKey, approvalTitle, "W", compSeq, deptSeq
approKey, approvalTitle, "W", compSeq, deptSeq, loginId
);
System.out.println("SSO API 응답: " + apiResponse);
// 응답에서 fullUrl 추출하여 approKey와 함께 반환
// 응답에서 fullUrl 추출
String fullUrl = extractJsonStringValue(apiResponse, "fullUrl");
String resultCode = extractJsonStringValue(apiResponse, "resultCode");
if("0".equals(resultCode) && !fullUrl.isEmpty()){
// approKey → targetObjId 매핑을 DB에 저장 (콜백/컨텐츠 조회 시 사용)
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
Map<String, Object> mappingParam = new HashMap();
mappingParam.put("approKey", approKey);
mappingParam.put("targetType", targetType);
mappingParam.put("targetObjId", targetObjId);
mappingParam.put("approvalTitle", approvalTitle);
mappingParam.put("writer", loginId);
sqlSession.insert("approval.insertAmaranthApproval", mappingParam);
sqlSession.commit();
System.out.println("Amaranth 결재 매핑 DB 저장 완료 - approKey: " + approKey);
StringBuilder result = new StringBuilder();
result.append("{\"resultCode\":0,\"resultMsg\":\"SUCCESS\",\"resultData\":{");
result.append("\"fullUrl\":\"").append(escapeJsonValue(fullUrl)).append("\",");
@@ -1842,9 +1858,12 @@ public class ApprovalService {
}
} catch(Exception e){
if(sqlSession != null) sqlSession.rollback();
System.err.println("Amaranth SSO URL 생성 오류: " + e.getMessage());
e.printStackTrace();
return "{\"resultCode\":-1,\"resultMsg\":\"" + escapeJsonValue(e.getMessage()) + "\"}";
} finally {
if(sqlSession != null) sqlSession.close();
}
}
@@ -1862,6 +1881,7 @@ public class ApprovalService {
String docTitle = CommonUtils.checkNull(paramMap.get("docTitle"));
String userId = CommonUtils.checkNull(paramMap.get("userId"));
String processId = CommonUtils.checkNull(paramMap.get("processId"));
String appCancelYn = CommonUtils.checkNull(paramMap.get("appCancelYn"), "0");
System.out.println("=== Amaranth 결재 콜백 수신 ===");
System.out.println("approkey: " + approkey);
@@ -1870,14 +1890,97 @@ public class ApprovalService {
System.out.println("docTitle: " + docTitle);
System.out.println("userId: " + userId);
System.out.println("processId: " + processId);
System.out.println("appCancelYn: " + appCancelYn);
System.out.println("전체 파라미터: " + paramMap);
// TODO: approkey로 우리 시스템의 대상 문서를 찾아서 상태 업데이트
// 예: approval 테이블에서 approkey로 조회 → targetObjId의 상태 변경
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
// approkey로 매핑 정보 조회
Map<String, Object> searchParam = new HashMap();
searchParam.put("approKey", approkey);
Map<String, Object> mappingInfo = sqlSession.selectOne("approval.selectAmaranthApprovalByApproKey", searchParam);
if(mappingInfo == null){
System.err.println("콜백 처리 실패: approkey에 해당하는 매핑 정보 없음 - " + approkey);
return "{\"resultCode\":\"SUCCESS\",\"resultMessage\":\"성공하였습니다.\"}";
}
String targetType = CommonUtils.checkNull(mappingInfo.get("target_type"));
String targetObjId = CommonUtils.checkNull(mappingInfo.get("target_objid"));
System.out.println("매핑 정보 - targetType: " + targetType + ", targetObjId: " + targetObjId);
// Amaranth 결재 매핑 테이블 상태 업데이트
String internalStatus = convertDocStsToInternalStatus(docSts, appCancelYn);
Map<String, Object> updateParam = new HashMap();
updateParam.put("approKey", approkey);
updateParam.put("amaranthDocId", docId);
updateParam.put("docSts", docSts);
updateParam.put("status", internalStatus);
sqlSession.update("approval.updateAmaranthApprovalByCallback", updateParam);
// 대상 문서(품의서 등)의 상태 업데이트
if(!targetObjId.isEmpty()){
String proposalStatus = convertDocStsToProposalStatus(docSts, appCancelYn);
if(!proposalStatus.isEmpty()){
Map<String, Object> statusParam = new HashMap();
statusParam.put("targetObjId", targetObjId);
statusParam.put("status", proposalStatus);
if("PROPOSAL".equals(targetType)){
sqlSession.update("approval.changeProposalApprovalStatus", statusParam);
System.out.println("품의서 상태 변경 - targetObjId: " + targetObjId + "" + proposalStatus);
} else if("SALES_REQUEST".equals(targetType)){
sqlSession.update("approval.salesRequestApprovalStatus", statusParam);
System.out.println("구매요청 상태 변경 - targetObjId: " + targetObjId + "" + proposalStatus);
}
}
}
sqlSession.commit();
System.out.println("콜백 처리 완료 - docSts: " + docSts + " → internalStatus: " + internalStatus);
} catch(Exception e){
if(sqlSession != null) sqlSession.rollback();
System.err.println("콜백 처리 중 오류: " + e.getMessage());
e.printStackTrace();
} finally {
if(sqlSession != null) sqlSession.close();
}
return "{\"resultCode\":\"SUCCESS\",\"resultMessage\":\"성공하였습니다.\"}";
}
/**
* Amaranth docSts → 내부 상태 변환
* 10:임시보관, 20:상신, 30:진행, 90:종결, 100:반려, 110:보류
*/
private String convertDocStsToInternalStatus(String docSts, String appCancelYn){
if("1".equals(appCancelYn)) return "create";
if("10".equals(docSts)) return "create";
if("20".equals(docSts)) return "inProcess";
if("30".equals(docSts)) return "inProcess";
if("90".equals(docSts)) return "complete";
if("100".equals(docSts)) return "reject";
if("110".equals(docSts)) return "hold";
return "inProcess";
}
/**
* Amaranth docSts → 품의서(SALES_REQUEST_MASTER) STATUS 변환
*/
private String convertDocStsToProposalStatus(String docSts, String appCancelYn){
if("1".equals(appCancelYn)) return "create";
if("20".equals(docSts)) return "approvalRequest";
if("30".equals(docSts)) return "approvalRequest";
if("90".equals(docSts)) return "approvalComplete";
if("100".equals(docSts)) return "reject";
if("110".equals(docSts)) return "approvalRequest";
return "";
}
/**
* Amaranth10 결재작성 시 본문 내용 조회 (Binding WebAPI / WebAPI)
* 결재 작성 화면이 열릴 때 Amaranth10에서 호출
@@ -1894,15 +1997,187 @@ public class ApprovalService {
System.out.println("docId: " + docId);
System.out.println("전체 파라미터: " + paramMap);
// TODO: approkey로 대상 문서 데이터를 조회하여 본문 HTML 또는 Binding JSON 구성
// Binding WebAPI 방식일 경우 ITEMS/TABLE 구조의 JSON 반환
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
// approkey로 매핑 정보 조회
Map<String, Object> searchParam = new HashMap();
searchParam.put("approKey", approkey);
Map<String, Object> mappingInfo = sqlSession.selectOne("approval.selectAmaranthApprovalByApproKey", searchParam);
String title = "결재 문서";
String contentsHtml = "<p>연동 데이터를 찾을 수 없습니다.</p>";
if(mappingInfo != null){
String targetType = CommonUtils.checkNull(mappingInfo.get("target_type"));
String targetObjId = CommonUtils.checkNull(mappingInfo.get("target_objid"));
title = CommonUtils.checkNull(mappingInfo.get("approval_title"), "결재 문서");
System.out.println("매핑 정보 - targetType: " + targetType + ", targetObjId: " + targetObjId);
if("PROPOSAL".equals(targetType) && !targetObjId.isEmpty()){
// 품의서 데이터 조회
Map<String, Object> proposalParam = new HashMap();
proposalParam.put("PROPOSAL_OBJID", targetObjId);
Map<String, Object> proposalInfo = sqlSession.selectOne("salesMng.getProposalInfo", proposalParam);
if(proposalInfo != null){
proposalInfo = CommonUtils.toUpperCaseMapKey(proposalInfo);
contentsHtml = buildProposalContentsHtml(proposalInfo, sqlSession, targetObjId);
String proposalNo = CommonUtils.checkNull(proposalInfo.get("PROPOSAL_NO"));
if(!proposalNo.isEmpty()){
title = "품의서 결재 - " + proposalNo;
}
}
}
}
// UTF-8 인코딩 처리 (contentsEnc=U 설정됨)
String encodedTitle = java.net.URLEncoder.encode(title, "UTF-8");
String encodedContents = java.net.URLEncoder.encode(contentsHtml, "UTF-8");
StringBuilder result = new StringBuilder();
result.append("{\"resultCode\":0,\"resultMessage\":\"SUCCESS\",\"resultData\":{");
result.append("\"title\":\"").append(escapeJsonValue(encodedTitle)).append("\",");
result.append("\"contents\":\"").append(escapeJsonValue(encodedContents)).append("\"");
result.append("}}");
return result.toString();
} catch(Exception e){
System.err.println("결재 본문 조회 오류: " + e.getMessage());
e.printStackTrace();
StringBuilder result = new StringBuilder();
result.append("{\"resultCode\":0,\"resultMessage\":\"SUCCESS\",\"resultData\":{");
result.append("\"title\":\"결재 문서\",");
result.append("\"contents\":\"<p>본문 조회 중 오류가 발생했습니다.</p>\"");
result.append("}}");
return result.toString();
} finally {
if(sqlSession != null) sqlSession.close();
}
}
/**
* 품의서 데이터를 HTML 본문으로 구성
*/
private String buildProposalContentsHtml(Map proposalInfo, SqlSession sqlSession, String targetObjId){
StringBuilder html = new StringBuilder();
StringBuilder result = new StringBuilder();
result.append("{\"resultCode\":0,\"resultMessage\":\"SUCCESS\",\"resultData\":{");
result.append("\"title\":\"결재 문서\",");
result.append("\"contents\":\"<p>본문 내용</p>\"");
result.append("}}");
return result.toString();
String proposalNo = CommonUtils.checkNull(proposalInfo.get("PROPOSAL_NO"));
String projectNumber = CommonUtils.checkNull(proposalInfo.get("PROJECT_NUMBER"));
String projectName = CommonUtils.checkNull(proposalInfo.get("PROJECT_NAME"));
String purchaseTypeName = CommonUtils.checkNull(proposalInfo.get("PURCHASE_TYPE_NAME"));
String orderTypeName = CommonUtils.checkNull(proposalInfo.get("ORDER_TYPE_NAME"));
String productName = CommonUtils.checkNull(proposalInfo.get("PRODUCT_NAME_TITLE"));
String customerName = CommonUtils.checkNull(proposalInfo.get("PROJECT_CUSTOMER_NAME"));
String writerName = CommonUtils.checkNull(proposalInfo.get("WRITER_NAME"));
String regdate = CommonUtils.checkNull(proposalInfo.get("REGDATE_TITLE"));
String remark = CommonUtils.checkNull(proposalInfo.get("REMARK"));
String totalAmount = CommonUtils.checkNull(proposalInfo.get("TOTAL_AMOUNT"));
html.append("<div style='font-family:맑은 고딕,Malgun Gothic,sans-serif;font-size:10pt;'>");
html.append("<table cellspacing='0' cellpadding='5' style='border-collapse:collapse;width:100%;'>");
html.append("<colgroup><col style='width:120px;'/><col style='width:200px;'/><col style='width:120px;'/><col style='width:200px;'/></colgroup>");
html.append("<tbody>");
html.append("<tr>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>품의서 No</th>");
html.append("<td style='border:1px solid #333;text-align:left;padding-left:8px;'>").append(escapeHtml(proposalNo)).append("</td>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>작성일</th>");
html.append("<td style='border:1px solid #333;text-align:center;'>").append(escapeHtml(regdate)).append("</td>");
html.append("</tr>");
html.append("<tr>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>프로젝트번호</th>");
html.append("<td style='border:1px solid #333;text-align:left;padding-left:8px;'>").append(escapeHtml(projectNumber)).append("</td>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>프로젝트명</th>");
html.append("<td style='border:1px solid #333;text-align:left;padding-left:8px;'>").append(escapeHtml(projectName)).append("</td>");
html.append("</tr>");
html.append("<tr>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>구매유형</th>");
html.append("<td style='border:1px solid #333;text-align:center;'>").append(escapeHtml(purchaseTypeName)).append("</td>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>주문유형</th>");
html.append("<td style='border:1px solid #333;text-align:center;'>").append(escapeHtml(orderTypeName)).append("</td>");
html.append("</tr>");
html.append("<tr>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>제품구분</th>");
html.append("<td style='border:1px solid #333;text-align:center;'>").append(escapeHtml(productName)).append("</td>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>고객사</th>");
html.append("<td style='border:1px solid #333;text-align:left;padding-left:8px;'>").append(escapeHtml(customerName)).append("</td>");
html.append("</tr>");
html.append("<tr>");
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>작성자</th>");
html.append("<td style='border:1px solid #333;text-align:center;'>").append(escapeHtml(writerName)).append("</td>");
if(!totalAmount.isEmpty()){
html.append("<th style='border:1px solid #333;background-color:#E8E8E8;text-align:center;font-weight:bold;'>합계금액</th>");
html.append("<td style='border:1px solid #333;text-align:right;padding-right:8px;'>").append(escapeHtml(totalAmount)).append("</td>");
} else {
html.append("<td style='border:1px solid #333;' colspan='2'></td>");
}
html.append("</tr>");
html.append("</tbody></table>");
// 품의서 품목 리스트 조회
try {
Map<String, Object> partParam = new HashMap();
partParam.put("PROPOSAL_OBJID", targetObjId);
List<Map> partList = sqlSession.selectList("salesMng.getProposalPartList", partParam);
if(partList != null && !partList.isEmpty()){
html.append("<br/>");
html.append("<table cellspacing='0' cellpadding='4' style='border-collapse:collapse;width:100%;'>");
html.append("<thead><tr>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>No</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>품번</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>품명</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>규격</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>수량</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>단가</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>금액</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>거래처</th>");
html.append("</tr></thead><tbody>");
int idx = 1;
for(Map partInfo : partList){
html.append("<tr>");
html.append("<td style='border:1px solid #ccc;text-align:center;font-size:9pt;'>").append(idx++).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:left;padding-left:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("PART_NO")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:left;padding-left:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("PART_NAME")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:left;padding-left:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("SPEC")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:right;padding-right:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("QTY")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:right;padding-right:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("UNIT_PRICE")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:right;padding-right:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("TOTAL_PRICE")))).append("</td>");
html.append("<td style='border:1px solid #ccc;text-align:left;padding-left:4px;font-size:9pt;'>").append(escapeHtml(CommonUtils.checkNull(partInfo.get("VENDOR_NAME")))).append("</td>");
html.append("</tr>");
}
html.append("</tbody></table>");
}
} catch(Exception e){
System.err.println("품의서 품목 리스트 조회 오류: " + e.getMessage());
}
// 비고
if(!remark.isEmpty()){
html.append("<br/><p><strong>비고:</strong> ").append(escapeHtml(remark)).append("</p>");
}
html.append("</div>");
return html.toString();
}
/**
* HTML 이스케이프 (XSS 방지)
*/
private String escapeHtml(String value){
if(value == null || value.isEmpty()) return "";
return value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;");
}
/**