diff --git a/src/main/java/egovframework/util/DateUtil.java b/src/main/java/egovframework/util/DateUtil.java index 4a77f5b..7343d3b 100644 --- a/src/main/java/egovframework/util/DateUtil.java +++ b/src/main/java/egovframework/util/DateUtil.java @@ -60,7 +60,7 @@ public class DateUtil { public static String getCurrentDateAddMonths(String pattern, int months) { LocalDate now = LocalDate.now(); return now.plusMonths(months).format(DateTimeFormatter.ofPattern(pattern)); - } + } /** * 현재 날짜와 시간을 지정된 형식으로 반환 @@ -301,6 +301,31 @@ public class DateUtil { return date.plusYears(years); } + /** + * 현재 날짜에 지정된 년/월/일을 더하거나 뺀 날짜를 지정된 형식으로 반환 + * + * @param pattern 날짜 형식 (예: "yyyy-MM-dd", "yyyyMMdd") + * @param type 년월일 구분 (Y: 년, M: 월, D: 일) + * @param amount 더하거나 뺄 값 (양수: 더하기, 음수: 빼기) + * @return 계산된 날짜 + */ + public static String getCurrentDateAdd(String pattern, String type, int amount) { + LocalDate now = LocalDate.now(); + LocalDate result; + + if ("Y".equalsIgnoreCase(type) || "year".equalsIgnoreCase(type)) { + result = now.plusYears(amount); + } else if ("M".equalsIgnoreCase(type) || "month".equalsIgnoreCase(type)) { + result = now.plusMonths(amount); + } else if ("D".equalsIgnoreCase(type) || "day".equalsIgnoreCase(type)) { + result = now.plusDays(amount); + } else { + result = now; + } + + return result.format(DateTimeFormatter.ofPattern(pattern)); + } + /** * 두 날짜 사이의 일수 계산 * @param startDate 시작 날짜 diff --git a/src/main/java/go/kr/project/levy/levyRelevy/controller/LevyRelevyController.java b/src/main/java/go/kr/project/levy/levyRelevy/controller/LevyRelevyController.java index 230e900..bc0c0b9 100644 --- a/src/main/java/go/kr/project/levy/levyRelevy/controller/LevyRelevyController.java +++ b/src/main/java/go/kr/project/levy/levyRelevy/controller/LevyRelevyController.java @@ -124,4 +124,31 @@ public class LevyRelevyController { return ApiResponseUtil.successWithGrid(list, paramVO); } + /** + * 재부과 chain 목록을 조회하는 AJAX 메소드 + * 선택된 단속의 최초 단속부터 자기 자신을 제외한 chain 목록을 조회합니다. + * + * @param paramVO 조회 조건을 담은 VO 객체 (crdnYr, crdnNo, frstCrdnYr, frstCrdnNo 필수) + * @return 재부과 chain 목록과 성공 상태를 담은 ResponseEntity 객체 + */ + @Operation(summary = "재부과 chain 목록 조회 (AJAX)", description = "재부과 chain 목록을 조회하고 JSON 형식으로 반환합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "재부과 chain 목록 조회 성공"), + @ApiResponse(responseCode = "400", description = "재부과 chain 목록 조회 실패"), + @ApiResponse(description = "오류로 인한 실패") + }) + @PostMapping("/relevyChain.ajax") + public ResponseEntity relevyChainAjax(@ModelAttribute LevyRelevyVO paramVO) { + log.debug("재부과 chain 목록 조회 - crdnYr: {}, crdnNo: {}, frstCrdnYr: {}, frstCrdnNo: {}", + paramVO.getCrdnYr(), paramVO.getCrdnNo(), paramVO.getFrstCrdnYr(), paramVO.getFrstCrdnNo()); + + // 재부과 chain 목록 조회 + List list = service.selectRelevyChainList(paramVO); + + // 총 개수 설정 + paramVO.setTotalCount(list.size()); + + return ApiResponseUtil.successWithGrid(list, paramVO); + } + } \ No newline at end of file diff --git a/src/main/java/go/kr/project/levy/levyRelevy/mapper/LevyRelevyMapper.java b/src/main/java/go/kr/project/levy/levyRelevy/mapper/LevyRelevyMapper.java index 8a0461b..10d121b 100644 --- a/src/main/java/go/kr/project/levy/levyRelevy/mapper/LevyRelevyMapper.java +++ b/src/main/java/go/kr/project/levy/levyRelevy/mapper/LevyRelevyMapper.java @@ -34,5 +34,11 @@ public interface LevyRelevyMapper { */ int selectListTotalCount(LevyRelevyVO vo); + /** + * 재부과 chain 목록을 조회한다. (최초 단속부터 자기 자신 제외한 chain) + * @param vo 조회 조건을 담은 VO 객체 (crdnYr, crdnNo, frstCrdnYr, frstCrdnNo 필수) + * @return 재부과 chain 목록 + */ + List selectRelevyChainList(LevyRelevyVO vo); } \ No newline at end of file diff --git a/src/main/java/go/kr/project/levy/levyRelevy/service/LevyRelevyService.java b/src/main/java/go/kr/project/levy/levyRelevy/service/LevyRelevyService.java index 5754a04..fa3e163 100644 --- a/src/main/java/go/kr/project/levy/levyRelevy/service/LevyRelevyService.java +++ b/src/main/java/go/kr/project/levy/levyRelevy/service/LevyRelevyService.java @@ -33,5 +33,12 @@ public interface LevyRelevyService { */ int selectListTotalCount(LevyRelevyVO vo); + /** + * 재부과 chain 목록을 조회합니다. (최초 단속부터 자기 자신 제외한 chain) + * + * @param vo 조회 조건을 담은 VO 객체 (crdnYr, crdnNo, frstCrdnYr, frstCrdnNo 필수) + * @return 재부과 chain 목록 + */ + List selectRelevyChainList(LevyRelevyVO vo); } \ No newline at end of file diff --git a/src/main/java/go/kr/project/levy/levyRelevy/service/impl/LevyRelevyServiceImpl.java b/src/main/java/go/kr/project/levy/levyRelevy/service/impl/LevyRelevyServiceImpl.java index 9e006b8..43a2569 100644 --- a/src/main/java/go/kr/project/levy/levyRelevy/service/impl/LevyRelevyServiceImpl.java +++ b/src/main/java/go/kr/project/levy/levyRelevy/service/impl/LevyRelevyServiceImpl.java @@ -51,4 +51,15 @@ public class LevyRelevyServiceImpl extends EgovAbstractServiceImpl implements Le return mapper.selectListTotalCount(vo); } + /** + * 재부과 chain 목록을 조회합니다. (최초 단속부터 자기 자신 제외한 chain) + * + * @param vo 조회 조건을 담은 VO 객체 (crdnYr, crdnNo, frstCrdnYr, frstCrdnNo 필수) + * @return 재부과 chain 목록 + */ + @Override + public List selectRelevyChainList(LevyRelevyVO vo) { + return mapper.selectRelevyChainList(vo); + } + } \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/levy/levyRelevy/RevyRelevyMapper_maria.xml b/src/main/resources/mybatis/mapper/levy/levyRelevy/RevyRelevyMapper_maria.xml index f048cb9..90b72df 100644 --- a/src/main/resources/mybatis/mapper/levy/levyRelevy/RevyRelevyMapper_maria.xml +++ b/src/main/resources/mybatis/mapper/levy/levyRelevy/RevyRelevyMapper_maria.xml @@ -134,4 +134,85 @@ + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tlds/date-format-functions.tld b/src/main/webapp/WEB-INF/tlds/date-format-functions.tld index 462cc48..c51504c 100644 --- a/src/main/webapp/WEB-INF/tlds/date-format-functions.tld +++ b/src/main/webapp/WEB-INF/tlds/date-format-functions.tld @@ -60,4 +60,10 @@ java.lang.String formatDateString(java.lang.String) + + getCurrentDateAdd + egovframework.util.DateUtil + java.lang.String getCurrentDateAdd(java.lang.String, java.lang.String, int) + + diff --git a/src/main/webapp/WEB-INF/views/levy/levyRelevy/list.jsp b/src/main/webapp/WEB-INF/views/levy/levyRelevy/list.jsp index 72ddd4f..ada7e9d 100644 --- a/src/main/webapp/WEB-INF/views/levy/levyRelevy/list.jsp +++ b/src/main/webapp/WEB-INF/views/levy/levyRelevy/list.jsp @@ -4,6 +4,27 @@ <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%@ taglib prefix="dateUtil" uri="http://egovframework.go.kr/functions/date-util" %> +<%-- + +재부과 관련 기초 그리드 조회 프로그램 +src/main/webapp/WEB-INF/views/levy/levyRelevy/list.jsp +src/main/java/go/kr/project/levy/levyRelevy/controller/LevyRelevyController.java +src/main/java/go/kr/project/levy/levyRelevy/mapper/LevyRelevyMapper.java +src/main/java/go/kr/project/levy/levyRelevy/model/LevyRelevyVO.java +src/main/java/go/kr/project/levy/levyRelevy/service/impl/LevyRelevyServiceImpl.java +src/main/java/go/kr/project/levy/levyRelevy/service/LevyRelevyService.java +src/main/resources/mybatis/mapper/levy/levyRelevy/RevyRelevyMapper_maria.xml + +tb_crdn.FRST_CRDN_YR:최초 단속 연도 +tb_crdn.FRST_CRDN_NO:최초 단속 번호 +tb_crdn.RELEVY_TRGT_CRDN_YR:재부과 대상 단속 연도,자신의 바로 위 부모 +tb_crdn.RELEVY_TRGT_CRDN_NO:재부과 대상 단속 번호,자신의 바로 위 부모 + +구현 대상, 일부 기능은 구현 완료, 하위 그리드에 대한 내용은 구현안되어 있음 +1. 이메뉴는 재부과 대상(schCrdnYr : 올해-1년) 메인 리스트를 보여준뒤, focus 가 되면 +2. 하위 그리드(현재 구현안되어 있음)에 최초 단속부터, 자기 자신을 제외한 chain 단속(재부과) 목록을 부여줘야해 +--%> +
@@ -20,7 +41,7 @@
  • 단속 년도
  • - +
  • 단속 번호
  • @@ -110,6 +131,18 @@
+
+
+
+
    +
  • 재부과 이력 (선택된 단속의 최초 단속부터 현재까지)
  • +
+
+
+
+
+
+
@@ -407,6 +440,18 @@ this.instance.on('focusChange', function(ev) { LevyRelevyList.selectedRow = self.instance.getRow(ev.rowKey); + + // 하위 그리드에 재부과 chain 데이터 로드 + if (LevyRelevyList.selectedRow) { + // 파라미터 저장 + LevyRelevyList.gridDetail.currentCrdnYr = LevyRelevyList.selectedRow.crdnYr; + LevyRelevyList.gridDetail.currentCrdnNo = LevyRelevyList.selectedRow.crdnNo; + LevyRelevyList.gridDetail.currentFrstCrdnYr = LevyRelevyList.selectedRow.frstCrdnYr; + LevyRelevyList.gridDetail.currentFrstCrdnNo = LevyRelevyList.selectedRow.frstCrdnNo; + + // 그리드 데이터 요청 + LevyRelevyList.gridDetail.instance.readData(1); + } }); // 행 선택 이벤트 @@ -439,6 +484,160 @@ }, + /** + * 하위 그리드 관련 객체 (재부과 chain) + */ + gridDetail: { + /** + * 그리드 인스턴스 + */ + instance: null, + + /** + * 현재 선택된 행의 정보 저장 + */ + currentCrdnYr: null, + currentCrdnNo: null, + currentFrstCrdnYr: null, + currentFrstCrdnNo: null, + + /** + * 그리드 설정 초기화 + * @returns {Object} 그리드 설정 객체 + */ + initConfig: function() { + // 데이터 소스 설정 + var dataSource = this.createDataSource(); + + // 그리드 설정 객체 생성 + var gridConfig = new XitTuiGridConfig(); + + // 기본 설정 + gridConfig.setOptDataSource(dataSource); // 데이터소스 연결 + gridConfig.setOptGridId('gridDetail'); // 그리드를 출력할 Element ID + gridConfig.setOptGridHeight(200); // 그리드 높이(단위: px) + gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px) + gridConfig.setOptRowHeaderType(''); // 행 첫번째 셀 타입 비활성화 + gridConfig.setOptUseClientSort(true); // 클라이언트 정렬 사용 + gridConfig.setOptColumns(this.getGridColumns()); + + return gridConfig; + }, + + /** + * 데이터 소스 생성 + * @returns {Object} 데이터 소스 설정 객체 + */ + createDataSource: function() { + var self = this; + return { + api: { + readData: { + url: '', + method: 'POST', + contentType: 'application/x-www-form-urlencoded', + processData: true + } + }, + initialRequest: false, // 초기 데이터 요청 여부 + serializer: function(params) { + var defaultParams = $.param(params); + var searchParams = $.param({ + crdnYr: self.currentCrdnYr, + crdnNo: self.currentCrdnNo, + frstCrdnYr: self.currentFrstCrdnYr, + frstCrdnNo: self.currentFrstCrdnNo + }); + return defaultParams + '&' + searchParams; + } + }; + }, + + /** + * 그리드 컬럼 정의 + * @returns {Array} 그리드 컬럼 배열 + */ + getGridColumns: function() { + return [ + { + header: '순번', + name: '_rowNum', + align: 'center', + width: 60, + sortable: false, + formatter: function(e) { + return e.row.rowKey + 1; + } + }, + { header: '단속년도', name: 'crdnYr', align: 'center', width: 80 }, + { header: '단속번호', name: 'crdnNo', align: 'center', width: 90 }, + { header: '법정동', name: 'stdgEmdCdNm', align: 'center', width: 90 }, + { header: '지역구분', name: 'rgnSeCdNm', align: 'center', width: 100 }, + { header: '적발방법', name: 'dsclMthdCdNm', align: 'center', width: 120 }, + { + header: '적발일자', + name: 'dsclYmd', + align: 'center', + width: 100, + formatter: function (e) { + return e.value ? moment(e.value).format('YYYY-MM-DD') : ''; + } + }, + { header: '조사원', name: 'exmnr', align: 'left', width: 130 }, + { + header: '재부과여부', + name: 'relevyYn', + align: 'center', + width: 80, + formatter: function(e) { + return e.value === 'Y' ? '재부과' : '일반'; + } + }, + { header: '진행단계', name: 'crdnPrcsSttsCdNm', align: 'center', width: 100 }, + { + header: '위치', + name: 'lotnoWholAddr', + align: 'left', + minWidth: 250, + formatter: function(e) { + return e.value; + } + }, + { + header: '부과예고 일자', + name: 'levyPrvntcBgngYmd', + align: 'center', + width: 120, + formatter: function (e) { + return e.value ? moment(e.value).format('YYYY-MM-DD') : ''; + } + }, + { + header: '부과 일자', + name: 'levyBgngYmd', + align: 'center', + width: 120, + formatter: function (e) { + return e.value ? moment(e.value).format('YYYY-MM-DD') : ''; + } + } + ]; + }, + + /** + * 그리드 인스턴스 생성 + */ + create: function() { + var gridConfig = this.initConfig(); + var Grid = tui.Grid; + this.instance = gridConfig.instance(Grid); + + // 그리드 테마 설정 + Grid.applyTheme('striped'); + }, + + }, + /** * 목록 현재 페이징 새로고침 */ @@ -531,10 +730,13 @@ * 모듈 초기화 */ init: function() { - - // 그리드 생성 + + // 메인 그리드 생성 this.grid.create(); + // 하위 그리드 생성 (재부과 chain) + this.gridDetail.create(); + // 이벤트 핸들러 설정 this.eventBindEvents();