From 812a10b0044c5d9fcb54cd76e5e0bb6a14b3fa52 Mon Sep 17 00:00:00 2001 From: thkim Date: Fri, 14 Nov 2025 18:24:38 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EA=B8=B0=EA=B4=80=20?= =?UTF-8?q?=ED=86=B5=EA=B3=84=20=EA=B8=B0=EB=8A=A5=20=EC=95=88=EC=A0=95?= =?UTF-8?q?=ED=99=94=20=EA=B1=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DrillingStatisticsController.java | 160 ++++++ .../service/DrillingStatisticsMapper.java | 33 ++ .../service/DrillingStatisticsService.java | 27 + .../impl/DrillingStatisticsServiceImpl.java | 35 ++ .../statistics/DrillingStatisticsMapper.xml | 107 ++++ .../drilling/statistics/drilling_notice.jsp | 43 +- .../statistics/drilling_statistics.jsp | 539 +++++++++++++----- 7 files changed, 764 insertions(+), 180 deletions(-) diff --git a/src/main/java/geoinfo/drilling/statistics/DrillingStatisticsController.java b/src/main/java/geoinfo/drilling/statistics/DrillingStatisticsController.java index d595ed98..3163f937 100644 --- a/src/main/java/geoinfo/drilling/statistics/DrillingStatisticsController.java +++ b/src/main/java/geoinfo/drilling/statistics/DrillingStatisticsController.java @@ -4,14 +4,17 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import org.json.simple.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -21,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; +import geoinfo.drilling.input.service.DrillingInputService; import geoinfo.drilling.inquiry.service.DrillingInquiryService; import geoinfo.drilling.statistics.service.DrillingStatisticsService; import geoinfo.util.MyUtil; @@ -33,6 +37,9 @@ public class DrillingStatisticsController { @Autowired DrillingInquiryService drillingInquiryService; + @Autowired + DrillingInputService drillingInputService; + @Autowired DrillingStatisticsService drillingStatisticsService; @@ -87,4 +94,157 @@ public class DrillingStatisticsController { return null; } + + + /** + * [신규] 통계 페이지 - 프로젝트 상태 파이 차트 데이터 조회 + * @param session + * @return + */ + @RequestMapping(value = "/drilling/statistics/project-status-chart.do", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public ResponseEntity getProjectStatusChartData( HttpServletRequest request, HttpSession session ) { + + JSONObject response = new JSONObject(); + + try { + // 세션에서 사용자 정보 (Box 또는 VO)를 가져옵니다. + // "sessionInfo"는 예시이며, 실제 세션에 저장된 Key를 사용해야 합니다. + // DrillingInquiryController의 로직을 참고하여 사용자 조직 코드를 가져옵니다. + String userId = MyUtil.getStringFromObject(request.getSession().getAttribute("USERID")); + + if (userId == null) { + response.put("resultCode", 401); + response.put("resultMessage", "로그인이 필요합니다."); + return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED); + } + + HashMap spGetMasterCompanyDistrictParams = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId); + + String masterCompanyOCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gl") ); + String masterCompanyTwCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gm") ); + String masterCompanyThCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gs") ); + String masterCompanyName = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gf") ); + + + HashMap params = new HashMap<>(); + + // DrillingInquiryController에서 사용하는 파라미터와 동일하게 설정 + // (예: O_CODE, TW_CODE, TH_CODE 등) + params.put("masterCompanyOCode", masterCompanyOCode); + params.put("masterCompanyTwCode", masterCompanyTwCode); + params.put("masterCompanyThCode", masterCompanyThCode); + // params.put("USERID", sessionInfo.getString("USERID")); + + List> chartData = drillingStatisticsService.getProjectStatusCounts(params); + + response.put("resultCode", 200); + response.put("datas", chartData); + + } catch (Exception e) { + response.put("resultCode", 500); + response.put("resultMessage", "차트 데이터 조회 중 오류가 발생했습니다: " + e.getMessage()); + e.printStackTrace(); + } + + return new ResponseEntity<>(response, HttpStatus.OK); + } + + + /** + * [신규] 통계 페이지 - 용역사별 성과 현황 + * @param session + * @return + */ + @RequestMapping(value = "/drilling/statistics/company-performance.do", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public ResponseEntity getCompanyPerformanceStats( HttpServletRequest request, HttpSession session) { + + JSONObject response = new JSONObject(); + + try { + + String userId = MyUtil.getStringFromObject(request.getSession().getAttribute("USERID")); + + if (userId == null) { + response.put("resultCode", 401); + response.put("resultMessage", "로그인이 필요합니다."); + return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED); + } + + HashMap spGetMasterCompanyDistrictParams = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId); + + String masterCompanyOCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gl") ); + String masterCompanyTwCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gm") ); + String masterCompanyThCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gs") ); + String masterCompanyName = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gf") ); + + HashMap params = new HashMap<>(); + + // DrillingInquiryController의 발주기관 필터링 로직과 동일하게 파라미터 설정 + params.put("masterCompanyOCode", masterCompanyOCode); + params.put("masterCompanyTwCode", masterCompanyTwCode); + params.put("masterCompanyThCode", masterCompanyThCode); + + List> statsData = drillingStatisticsService.getCompanyPerformanceStats(params); + + response.put("resultCode", 200); + response.put("datas", statsData); + + } catch (Exception e) { + response.put("resultCode", 500); + response.put("resultMessage", "성과 현황 조회 중 오류가 발생했습니다: " + e.getMessage()); + e.printStackTrace(); + } + + return new ResponseEntity<>(response, HttpStatus.OK); + } + + + /** + * 통계 페이지 - 데이터 품질 현황 (수정 요청 횟수) + * @param request + * @param session + * @return + */ + @RequestMapping(value = "/drilling/statistics/data-quality-stats.do", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public ResponseEntity getDataQualityStats(HttpServletRequest request, HttpSession session) { + + JSONObject response = new JSONObject(); + + try { + // 세션에서 사용자 정보 (USERID) + String userId = MyUtil.getStringFromObject(request.getSession().getAttribute("USERID")); + if (userId == null) { + response.put("resultCode", 401); + response.put("resultMessage", "로그인이 필요합니다."); + return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED); + } + + // 세션 사용자의 조직 코드 조회 + HashMap spGetMasterCompanyDistrictParams = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId); + String masterCompanyOCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gl") ); + String masterCompanyTwCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gm") ); + String masterCompanyThCode = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gs") ); + + HashMap params = new HashMap<>(); + params.put("masterCompanyOCode", masterCompanyOCode); + params.put("masterCompanyTwCode", masterCompanyTwCode); + params.put("masterCompanyThCode", masterCompanyThCode); + + // Service 호출 + HashMap statsData = drillingStatisticsService.getDataQualityStats(params); + + response.put("resultCode", 200); + response.put("datas", statsData); // { "projects": [...], "companies": [...] } + + } catch (Exception e) { + response.put("resultCode", 500); + response.put("resultMessage", "데이터 품질 현황 조회 중 오류가 발생했습니다: " + e.getMessage()); + e.printStackTrace(); + } + + return new ResponseEntity<>(response, HttpStatus.OK); + } } diff --git a/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsMapper.java b/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsMapper.java index e2434fdd..668a527e 100644 --- a/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsMapper.java +++ b/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsMapper.java @@ -11,4 +11,37 @@ import egovframework.rte.psl.dataaccess.util.EgovMap; public interface DrillingStatisticsMapper { public List selectConstructSiteHistList(HashMap params) throws SQLException; public Long selectConstructSiteHistListCnt(HashMap params) throws SQLException; + + /** + * 소속 기관의 프로젝트 상태별 건수 조회 (XML 매핑) + * @param params + * @return + * @throws Exception + */ + List> selectProjectStatusCounts(HashMap params) throws Exception; + + /** + * 소속 기관의 용역사(조사기관)별 성과 통계 조회 (XML 매핑) + * @param params + * @return + * @throws Exception + */ + List> selectCompanyPerformanceStats(HashMap params) throws Exception; + + /** + * 수정 요청이 많은 프로젝트 Top 5 조회 (XML 매핑) + * @param params + * @return + * @throws Exception + */ + List> selectRevisionCountByProject(HashMap params) throws Exception; + + /** + * 용역사(시공사)별 누적 수정 요청 횟수 조회 (XML 매핑) + * @param params + * @return + * @throws Exception + */ + List> selectRevisionCountByCompany(HashMap params) throws Exception; + } \ No newline at end of file diff --git a/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsService.java b/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsService.java index e11edcfd..b51e5fc7 100644 --- a/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsService.java +++ b/src/main/java/geoinfo/drilling/statistics/service/DrillingStatisticsService.java @@ -13,4 +13,31 @@ import egovframework.rte.psl.dataaccess.util.EgovMap; public interface DrillingStatisticsService { public JSONObject getConstructSiteHistList(HttpServletRequest request, HashMap params) throws Exception; + + /** + * 소속 기관의 프로젝트 상태별 건수 조회 + * @param params + * @return + * @throws Exception + */ + List> getProjectStatusCounts(HashMap params) throws Exception; + + + /** + * 소속 기관의 용역사(조사기관)별 성과 통계 조회 + * @param params + * @return + * @throws Exception + */ + List> getCompanyPerformanceStats(HashMap params) throws Exception; + + + /** + * 데이터 품질(수정 요청 횟수) 통계 조회 + * @param params + * @return projects (Top 5) 와 companies (전체) 리스트가 담긴 Map + * @throws Exception + */ + HashMap getDataQualityStats(HashMap params) throws Exception; + } diff --git a/src/main/java/geoinfo/drilling/statistics/service/impl/DrillingStatisticsServiceImpl.java b/src/main/java/geoinfo/drilling/statistics/service/impl/DrillingStatisticsServiceImpl.java index 506034bd..c3bef637 100644 --- a/src/main/java/geoinfo/drilling/statistics/service/impl/DrillingStatisticsServiceImpl.java +++ b/src/main/java/geoinfo/drilling/statistics/service/impl/DrillingStatisticsServiceImpl.java @@ -94,6 +94,41 @@ public class DrillingStatisticsServiceImpl implements DrillingStatisticsService throw new Exception("이력 조회 중 오류가 발생하였습니다."); } } + + /** + * 소속 기관의 프로젝트 상태별 건수 조회 + */ + @Override + public List> getProjectStatusCounts(HashMap params) throws Exception { + return drillingStatisticsMapper.selectProjectStatusCounts(params); + } + + /** + * 소속 기관의 용역사(조사기관)별 성과 통계 조회 + */ + @Override + public List> getCompanyPerformanceStats(HashMap params) throws Exception { + return drillingStatisticsMapper.selectCompanyPerformanceStats(params); + } + + /** + * 데이터 품질(수정 요청 횟수) 통계 조회 + */ + @Override + public HashMap getDataQualityStats(HashMap params) throws Exception { + + // 1. 수정 요청 많은 프로젝트 Top 5 + List> projectStats = drillingStatisticsMapper.selectRevisionCountByProject(params); + + // 2. 용역사별 누적 수정 요청 횟수 + List> companyStats = drillingStatisticsMapper.selectRevisionCountByCompany(params); + + HashMap result = new HashMap<>(); + result.put("projects", projectStats); + result.put("companies", companyStats); + + return result; + } } diff --git a/src/main/resources/egovframework/sqlmap/mapper/drilling/statistics/DrillingStatisticsMapper.xml b/src/main/resources/egovframework/sqlmap/mapper/drilling/statistics/DrillingStatisticsMapper.xml index 59354ccb..0caaf81d 100644 --- a/src/main/resources/egovframework/sqlmap/mapper/drilling/statistics/DrillingStatisticsMapper.xml +++ b/src/main/resources/egovframework/sqlmap/mapper/drilling/statistics/DrillingStatisticsMapper.xml @@ -56,5 +56,112 @@ AND csi.MASTER_COMPANY_TH_CODE = #{masterCompanyThCode} + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_notice.jsp b/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_notice.jsp index 1d0c008a..deae4c02 100644 --- a/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_notice.jsp +++ b/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_notice.jsp @@ -63,54 +63,47 @@ function getStatusInfo(statusCode) { }; switch (String(statusCode)) { - case '0': + case '0': // Gray status.name = '미입력'; - // 아이콘: 마이너스 (MinusCircle) - 비어있거나 시작 안 함 status.icon = ''; - status.bgColor = 'bg-gray-500'; // 회색 (중립) + status.bgColor = 'bg-gray-500'; status.textColor = 'text-gray-600'; - break; // - case '1': + break; + case '1': // Blue status.name = '입력 중'; - // 아이콘: 연필 (Pencil) - 수정/작성 중 (기존과 동일) status.icon = ''; - status.bgColor = 'bg-blue-500'; // 파란색 (진행중) + status.bgColor = 'bg-blue-500'; status.textColor = 'text-blue-600'; break; - case '2': + case '2': // Orange status.name = '검수 준비 대기중'; - // 아이콘: 시계 (Clock) - 대기 중 status.icon = ''; - status.bgColor = 'bg-yellow-500'; // 노란색 (대기) - status.textColor = 'text-yellow-600'; - break; // - case '3': + status.bgColor = 'bg-orange-500'; // [수정] (Orange) + status.textColor = 'text-orange-600'; // [수정] + break; + case '3': // Yellow status.name = '검수중'; - // 아이콘: 돋보기 (Search) - 검토 중 status.icon = ''; - status.bgColor = 'bg-yellow-500'; // 노란색 (대기/검토) + status.bgColor = 'bg-yellow-500'; // (Yellow) status.textColor = 'text-yellow-600'; break; - case '4': + case '4': // Red status.name = '수정 요청'; - // 아이콘: 느낌표 (ExclamationCircle) - 주의/수정 필요 status.icon = ''; - status.bgColor = 'bg-red-500'; // 빨간색 (거절/오류) + status.bgColor = 'bg-red-500'; status.textColor = 'text-red-600'; break; - case '5': + case '5': // Green status.name = '검수 완료'; - // 아이콘: 체크 (CheckCircle) - 성공 (기존과 동일) status.icon = ''; - status.bgColor = 'bg-green-500'; // 초록색 (완료/성공) + status.bgColor = 'bg-green-500'; status.textColor = 'text-green-600'; break; - case '6': + case '6': // Teal status.name = '등록 완료'; - // 아이콘: 체크 (CheckCircle) - 성공 (기존과 동일) status.icon = ''; - status.bgColor = 'bg-green-500'; // 초록색 (완료/성공) - status.textColor = 'text-green-600'; + status.bgColor = 'bg-teal-500'; // [수정] (Teal) + status.textColor = 'text-teal-600'; // [수정] break; } return status; diff --git a/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_statistics.jsp b/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_statistics.jsp index 6aab8dbd..fb76537d 100644 --- a/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_statistics.jsp +++ b/src/main/webapp/WEB-INF/views/drilling/statistics/drilling_statistics.jsp @@ -25,10 +25,12 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe + + - + @@ -307,56 +545,47 @@ function getStatusInfo(statusCode) { }; switch (String(statusCode)) { - case '0': - status.name = '미입력'; - // 아이콘: 마이너스 (MinusCircle) - 비어있거나 시작 안 함 - status.icon = ''; - status.bgColor = 'bg-gray-500'; // 회색 (중립) - status.textColor = 'text-gray-600'; - break; // - case '1': - status.name = '입력 중'; - // 아이콘: 연필 (Pencil) - 수정/작성 중 (기존과 동일) - status.icon = ''; - status.bgColor = 'bg-blue-500'; // 파란색 (진행중) - status.textColor = 'text-blue-600'; - break; - case '2': - status.name = '검수 준비 대기중'; - // 아이콘: 시계 (Clock) - 대기 중 - status.icon = ''; - status.bgColor = 'bg-yellow-500'; // 노란색 (대기) - status.textColor = 'text-yellow-600'; - break; // - case '3': - status.name = '검수중'; - // 아이콘: 돋보기 (Search) - 검토 중 - status.icon = ''; - status.bgColor = 'bg-yellow-500'; // 노란색 (대기/검토) - status.textColor = 'text-yellow-600'; - break; - case '4': - status.name = '수정 요청'; - // 아이콘: 느낌표 (ExclamationCircle) - 주의/수정 필요 - status.icon = ''; - status.bgColor = 'bg-red-500'; // 빨간색 (거절/오류) - status.textColor = 'text-red-600'; - break; - case '5': - status.name = '검수 완료'; - // 아이콘: 체크 (CheckCircle) - 성공 (기존과 동일) - status.icon = ''; - status.bgColor = 'bg-green-500'; // 초록색 (완료/성공) - status.textColor = 'text-green-600'; - break; - case '6': - status.name = '등록 완료'; - // 아이콘: 체크 (CheckCircle) - 성공 (기존과 동일) - status.icon = ''; - status.bgColor = 'bg-green-500'; // 초록색 (완료/성공) - status.textColor = 'text-green-600'; - break; - } + case '0': // Gray + status.name = '미입력'; + status.icon = ''; + status.bgColor = 'bg-gray-500'; + status.textColor = 'text-gray-600'; + break; + case '1': // Blue + status.name = '입력 중'; + status.icon = ''; + status.bgColor = 'bg-blue-500'; + status.textColor = 'text-blue-600'; + break; + case '2': // Orange + status.name = '검수 준비 대기중'; + status.icon = ''; + status.bgColor = 'bg-orange-500'; // (Orange) + status.textColor = 'text-orange-600'; + break; + case '3': // Yellow + status.name = '검수중'; + status.icon = ''; + status.bgColor = 'bg-yellow-500'; // (Yellow) + status.textColor = 'text-yellow-600'; + break; + case '4': // Red + status.name = '수정 요청'; + status.icon = ''; + break; + case '5': // Green + status.name = '검수 완료'; + status.icon = ''; + status.bgColor = 'bg-green-500'; + status.textColor = 'text-green-600'; + break; + case '6': // Teal + status.name = '등록 완료'; + status.icon = ''; + status.bgColor = 'bg-teal-500'; // (Teal) + status.textColor = 'text-teal-600'; + break; + } return status; }