재부과 이력 조회 기능 추가: 팝업 화면 및 관련 API, 서비스, DAO 구현

부과, 재부과 시 crdn_no 시컨스 기능 수정
dev
박성영 1 month ago
parent 28effbfd9f
commit 72557d1c20

@ -0,0 +1,7 @@
CREATE SEQUENCE seq_crdn_no
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 999999
CACHE 1000
NOCYCLE;

@ -3,8 +3,8 @@ echo ----------------------------------------
echo UbiService.bat
echo ----------------------------------------
echo.
set JAVA_DIR=D:\Java\jdk1.8.0_271
set UBISERVICE_DIR=D:\Work\git\IBMS_NEW\src\main\UbiService
set JAVA_DIR=D:\DEV\.jdks\jdk1.8.0_271
set UBISERVICE_DIR=D:\workspace\git\IBMS_NEW\src\main\UbiService
set PROPERTY_DIR=%UBISERVICE_DIR%
set FONT_DIR=%UBISERVICE_DIR%\fonts\
set XMS=512M

@ -27,8 +27,8 @@
<Resources>
<Resource id="UBIHTML"
isLocalFile="true"
jrfDir="D:/Work/git/IBMS_NEW/src/main/webapp/ubi4/work/"
resultDir="D:/Work/git/IBMS_NEW/src/main/UbiService/results/"
jrfDir="D:\workspace\git\IBMS_NEW\src\main\webapp\ubi4\work"
resultDir="D:\workspace\git\IBMS_NEW\src\main\UbiService\results"
ubiserverUrl="http://localhost:8080/UbiServer"
servletRoot="http://localhost:8080"
fileUrl="http://localhost:8080/ubi4"

@ -0,0 +1,101 @@
package go.kr.project.crdn.crndRegistAndView.main.controller;
import egovframework.constant.TilesConstants;
import egovframework.util.ApiResponseUtil;
import go.kr.project.crdn.crndRegistAndView.main.model.CrdnRelevyHistoryVO;
import go.kr.project.crdn.crndRegistAndView.main.service.CrdnRelevyHistoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
/**
* packageName : go.kr.project.crdn.crndRegistAndView.main.controller
* fileName : CrdnRelevyHistController
* author :
* date : 2025-01-30
* description :
* :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2025-01-30
*/
@Controller
@RequestMapping("/crdn/crndRegistAndView/crdnRelevyHistory")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "재부과 이력 조회", description = "재부과 이력 조회 관련 API")
public class CrdnRelevyHistoryController {
private final CrdnRelevyHistoryService crdnRelevyHistoryService;
/**
* .
*
* @param crdnYr
* @param crdnNo
* @return
*/
@GetMapping("/relevyHistoryPopup.do")
@Operation(summary = "재부과 이력 팝업", description = "재부과 이력 팝업 화면을 제공합니다.")
public ModelAndView relevyHistPopup(
@Parameter(description = "단속 연도") @RequestParam String crdnYr,
@Parameter(description = "단속 번호") @RequestParam String crdnNo) {
log.debug("재부과 이력 팝업 요청 - 단속연도: {}, 단속번호: {}", crdnYr, crdnNo);
ModelAndView mav = new ModelAndView("crdn/crndRegistAndView/main/crdnRelevyHistory/relevyHistoryPopup" + TilesConstants.POPUP);
// 기본 파라미터 설정
mav.addObject("crdnYr", crdnYr);
mav.addObject("crdnNo", crdnNo);
CrdnRelevyHistoryVO paramVO = new CrdnRelevyHistoryVO();
paramVO.setCrdnYr(crdnYr);
paramVO.setCrdnNo(crdnNo);
// 단속 기본 정보 조회 (이력이 없어도 주소 정보를 표시하기 위해)
CrdnRelevyHistoryVO crdnBasicInfo = crdnRelevyHistoryService.selectCrdnBasicInfo(paramVO);
mav.addObject("crdnBasicInfo", crdnBasicInfo);
return mav;
}
/**
* 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 CrdnRelevyHistoryVO paramVO) {
log.debug("재부과 chain 목록 조회 - crdnYr: {}, crdnNo: {}, frstCrdnYr: {}, frstCrdnNo: {}",
paramVO.getCrdnYr(), paramVO.getCrdnNo(), paramVO.getFrstCrdnYr(), paramVO.getFrstCrdnNo());
// 재부과 chain 목록 조회
List<CrdnRelevyHistoryVO> list = crdnRelevyHistoryService.selectRelevyChainList(paramVO);
// 총 개수 설정
paramVO.setTotalCount(list.size());
return ApiResponseUtil.successWithGrid(list, paramVO);
}
}

@ -0,0 +1,38 @@
package go.kr.project.crdn.crndRegistAndView.main.mapper;
import go.kr.project.crdn.crndRegistAndView.main.model.CrdnRelevyHistoryVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* packageName : go.kr.project.crdn.crndRegistAndView.main.mapper
* fileName : CrdnRelevyHistMapper
* author :
* date : 2025-01-30
* description : MyBatis
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2025-01-30
*/
@Mapper
public interface CrdnRelevyHistoryMapper {
/**
* .
*
* @param vo (, )
* @return
*/
CrdnRelevyHistoryVO selectCrdnBasicInfo(CrdnRelevyHistoryVO vo);
/**
* chain . ( chain)
*
* @param vo VO (crdnYr, crdnNo, frstCrdnYr, frstCrdnNo )
* @return chain
*/
List<CrdnRelevyHistoryVO> selectRelevyChainList(CrdnRelevyHistoryVO vo);
}

@ -38,10 +38,9 @@ public interface CrdnRelevyMapper {
/**
* .
* @param crdnYr
* @return (10 LPAD )
*/
String selectNextCrdnNo(@Param("crdnYr") String crdnYr);
String selectNextCrdnNo();
/**
* .

@ -0,0 +1,188 @@
package go.kr.project.crdn.crndRegistAndView.main.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import go.kr.project.common.model.PagingVO;
import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
/**
* packageName : go.kr.project.crdn.crndRegistAndView.main.model
* fileName : CrdnRelevyHistVO
* author :
* date : 2025-01-30
* description : Value Object
* : (tb_crdn) VO
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2025-01-30
*/
@EqualsAndHashCode(callSuper=true)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class CrdnRelevyHistoryVO extends PagingVO {
// ==================== 기본 테이블 컬럼 ====================
/** 단속 연도 */
private String crdnYr;
/** 단속 번호 */
private String crdnNo;
/** 시군구 코드(그룹코드:ORG_CD, 로그인한 사용자정보에서 ORG_CD 가져온다.) */
private String sggCd;
/** 지역 구분 코드 */
private String rgnSeCd;
/** 적발 방법 코드 */
private String dsclMthdCd;
/** 적발 일자 */
private String dsclYmd;
/** 조사원 */
private String exmnr;
/** 비고 */
private String rmrk;
/** 처분 사전 시작 일자 */
private String dspsBfhdBgngYmd;
/** 처분 사전 종료 일자 */
private String dspsBfhdEndYmd;
/** 시정 명령 시작 일자 */
private String crcCmdBgngYmd;
/** 시정 명령 종료 일자 */
private String crcCmdEndYmd;
/** 시정 촉구 시작 일자 */
private String crcUrgBgngYmd;
/** 시정 촉구 종료 일자 */
private String crcUrgEndYmd;
/** 부과 예고 시작 일자 */
private String levyPrvntcBgngYmd;
/** 부과 예고 종료 일자 */
private String levyPrvntcEndYmd;
/** 부과 시작 일자 */
private String levyBgngYmd;
/** 부과 종료 일자 */
private String levyEndYmd;
/** 납부 촉구 시작 일자 */
private String payUrgBgngYmd;
/** 납부 촉구 종료 일자 */
private String payUrgEndYmd;
/** 최초 단속 연도 */
private String frstCrdnYr;
/** 최초 단속 번호 */
private String frstCrdnNo;
/** 재부과 여부 */
private String relevyYn;
/** 가중 부과 대상 여부 */
private String agrvtnLevyTrgtYn;
/** 단속 처리 상태 코드 */
private String crdnPrcsSttsCd;
/** 단속 처리 일자 */
private String crdnPrcsYmd;
/** 법정동 읍면동 코드 */
private String stdgEmdCd;
/** 행위 유형 코드 */
private String actTypeCd;
/** 용도 지수 코드 */
private String usgIdxCd;
/** 등록 일시 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime regDt;
/** 등록자 */
private String rgtr;
/** 등록자 계정 */
private String rgtrAcnt;
/** 등록자 사용자명 */
private String rgtrNm;
/** 삭제 여부 */
private String delYn;
/** 삭제 일시 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime delDt;
/** 삭제자 */
private String dltr;
// ==================== 조인 컬럼 (코드명) ====================
/** 시군구 코드명 */
private String sggCdNm;
/** 지역 구분 코드명 */
private String rgnSeCdNm;
/** 적발 방법 코드명 */
private String dsclMthdCdNm;
/** 단속 처리 상태 코드명 */
private String crdnPrcsSttsCdNm;
/** 소유자명들 (쉼표로 구분) */
private String ownrNams;
/** 행위자명들 (쉼표로 구분) */
private String actrNams;
/** 법정동 읍면동 코드명 */
private String stdgEmdCdNm;
/** 행위 유형 코드명 */
private String actTypeCdNm;
/** 용도 지수 코드명 */
private String usgIdxCdNm;
/** 지번 전체 주소 */
private String lotnoWholAddr;
/** 지번 주소 */
private String lotnoAddr;
/** 지번 본번 */
private String lotnoMno;
/** 지번 부번 */
private String lotnoSno;
/** 우편번호 */
private String zip;
}

@ -0,0 +1,36 @@
package go.kr.project.crdn.crndRegistAndView.main.service;
import go.kr.project.crdn.crndRegistAndView.main.model.CrdnRelevyHistoryVO;
import java.util.List;
/**
* packageName : go.kr.project.crdn.crndRegistAndView.main.service
* fileName : CrdnRelevyHistService
* author :
* date : 2025-01-30
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2025-01-30
*/
public interface CrdnRelevyHistoryService {
/**
* .
*
* @param vo (, )
* @return
*/
CrdnRelevyHistoryVO selectCrdnBasicInfo(CrdnRelevyHistoryVO vo);
/**
* chain .
*
* @param vo (, , , )
* @return chain
*/
List<CrdnRelevyHistoryVO> selectRelevyChainList(CrdnRelevyHistoryVO vo);
}

@ -108,7 +108,7 @@ public class CrdnRegistAndViewServiceImpl extends EgovAbstractServiceImpl implem
}
// 년도별 시퀀스 존재 여부 확인 및 생성
ensureSequenceExists(vo.getCrdnYr() == null ? LocalDate.now().getYear() + "" : vo.getCrdnYr());
//ensureSequenceExists(vo.getCrdnYr() == null ? LocalDate.now().getYear() + "" : vo.getCrdnYr());
vo.setCrdnPrcsSttsCd(CrdnPrcsSttsConstants.CRDN_PRCS_STTS_CD_10_CRDN);
// 등록자 정보 설정 및 등록 수행

@ -0,0 +1,55 @@
package go.kr.project.crdn.crndRegistAndView.main.service.impl;
import go.kr.project.crdn.crndRegistAndView.main.mapper.CrdnRelevyHistoryMapper;
import go.kr.project.crdn.crndRegistAndView.main.model.CrdnRelevyHistoryVO;
import go.kr.project.crdn.crndRegistAndView.main.service.CrdnRelevyHistoryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* packageName : go.kr.project.crdn.crndRegistAndView.main.service.impl
* fileName : CrdnRelevyHistServiceImpl
* author :
* date : 2025-01-30
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2025-01-30
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class CrdnRelevyHistoryServiceImpl implements CrdnRelevyHistoryService {
private final CrdnRelevyHistoryMapper mapper;
/**
* .
*
* @param vo (, )
* @return
*/
@Override
public CrdnRelevyHistoryVO selectCrdnBasicInfo(CrdnRelevyHistoryVO vo) {
log.debug("단속 기본 정보 조회 - 단속연도: {}, 단속번호: {}", vo.getCrdnYr(), vo.getCrdnNo());
return mapper.selectCrdnBasicInfo(vo);
}
/**
* chain .
*
* @param vo (, , , )
* @return chain
*/
@Override
public List<CrdnRelevyHistoryVO> selectRelevyChainList(CrdnRelevyHistoryVO vo) {
log.debug("재부과 chain 목록 조회 - 단속연도: {}, 단속번호: {}, 최초단속연도: {}, 최초단속번호: {}",
vo.getCrdnYr(), vo.getCrdnNo(), vo.getFrstCrdnYr(), vo.getFrstCrdnNo());
return mapper.selectRelevyChainList(vo);
}
}

@ -117,12 +117,12 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
String currentYear = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy"));
// 년도별 시퀀스 존재 여부 확인 및 생성
ensureSequenceExists(currentYear);
//ensureSequenceExists(currentYear);
// 2. 신규 단속 번호 생성 (현재 년도 기준)
relevyVO.setNewCrdnYr(currentYear);
String newCrdnNo = relevyMapper.selectNextCrdnNo(currentYear);
relevyVO.setNewCrdnNo(newCrdnNo);
//String newCrdnNo = relevyMapper.selectNextCrdnNo();
relevyVO.setNewCrdnNo(relevyVO.getSrcCrdnNo()); //단속번호를 기존번호에서 복사한다. 일관된 번호 유지를 위해
// 3. 기본 단속 정보 복사 (tb_crdn)
int result = relevyMapper.insertCrdnBasicInfo(relevyVO);
@ -150,7 +150,7 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
log.info("생성된 newActInfoId: {}, 원본 srcActInfoId: {}", newActInfoId, srcActInfo.getSrcActInfoId());
srcActInfo.setActInfoId(newActInfoId);
srcActInfo.setNewCrdnYr(currentYear);
srcActInfo.setNewCrdnNo(newCrdnNo);
srcActInfo.setNewCrdnNo(relevyVO.getSrcCrdnNo());
srcActInfo.setPstnInfoId(nextPstnInfoId);
srcActInfo.setRgtr(relevyVO.getRgtr());
relevyMapper.insertActInfoOne(srcActInfo);
@ -166,7 +166,7 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
log.info("행위사진 insert - newActInfoId: {}, 원본 SN: {}", newActInfoId, srcPhoto.getCrdnPhotoSn());
srcPhoto.setActInfoId(newActInfoId);
srcPhoto.setCrdnYr(currentYear);
srcPhoto.setCrdnNo(newCrdnNo);
srcPhoto.setCrdnNo(relevyVO.getSrcCrdnNo());
srcPhoto.setCrdnPhotoNm(newPhotoNm);
srcPhoto.setActnInfoId(null); // 행위사진은 ACTN_INFO_ID 없음
srcPhoto.setRgtr(relevyVO.getRgtr());
@ -182,7 +182,7 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
String newActnInfoId = relevyMapper.selectNextActnInfoId();
srcActnInfo.setActnInfoId(newActnInfoId);
srcActnInfo.setNewCrdnYr(currentYear);
srcActnInfo.setNewCrdnNo(newCrdnNo);
srcActnInfo.setNewCrdnNo(relevyVO.getSrcCrdnNo());
srcActnInfo.setActInfoId(newActInfoId);
srcActnInfo.setRgtr(relevyVO.getRgtr());
relevyMapper.insertActnInfoOne(srcActnInfo);
@ -198,7 +198,7 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
log.info("조치사진 insert - newActInfoId: {}, 원본 SN: {}", newActInfoId, srcPhoto.getCrdnPhotoSn());
srcPhoto.setActInfoId(newActInfoId);
srcPhoto.setCrdnYr(currentYear);
srcPhoto.setCrdnNo(newCrdnNo);
srcPhoto.setCrdnNo(relevyVO.getSrcCrdnNo());
srcPhoto.setCrdnPhotoNm(newPhotoNm);
srcPhoto.setActnInfoId(newActnInfoId); // 조치사진은 ACTN_INFO_ID 필수
srcPhoto.setRgtr(relevyVO.getRgtr());
@ -214,19 +214,19 @@ public class CrdnRelevyServiceImpl implements CrdnRelevyService {
String newActrInfoId = relevyMapper.selectNextActrInfoId();
srcActrInfo.setActrInfoId(newActrInfoId);
srcActrInfo.setNewCrdnYr(currentYear);
srcActrInfo.setNewCrdnNo(newCrdnNo);
srcActrInfo.setNewCrdnNo(relevyVO.getSrcCrdnNo());
srcActrInfo.setActInfoId(newActInfoId);
srcActrInfo.setRgtr(relevyVO.getRgtr());
relevyMapper.insertActrInfoOne(srcActrInfo);
}
}
log.info("재부과 처리 완료: 신규 단속번호={}", newCrdnNo);
log.info("재부과 처리 완료: 신규 단속번호={}", relevyVO.getSrcCrdnNo());
// 결과 반환
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("newCrdnYr", currentYear);
resultMap.put("newCrdnNo", newCrdnNo);
resultMap.put("newCrdnNo", relevyVO.getSrcCrdnNo());
resultMap.put("message", "재부과가 성공적으로 처리되었습니다.");
return resultMap;

@ -35,6 +35,8 @@
<!-- 30일 보관 -->
<maxHistory>${MAX_HISTORY}</maxHistory>
<cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 로그 파일 정리 기능을 활성화하여 서버 시작 시 로그 파일을 정리함, 실서버에서는 굳이 필요할까 함 -->
<!-- 전체 로그 파일의 최대 크기 (예: 3GB) -->
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<!-- 디렉토리가 없으면 생성 -->
<prudent>true</prudent>

@ -348,7 +348,7 @@
DEL_YN
) VALUES (
#{crdnYr},
LPAD(NEXTVAL(seq_crdn_no_${crdnYr}), 6, '0'),
LPAD(NEXTVAL(seq_crdn_no), 6, '0'),
#{sggCd},
#{rgnSeCd},
#{dsclMthdCd},
@ -416,13 +416,13 @@
AND CRDN_NO = #{crdnNo}
</select>
<!-- 시퀀스 존재 여부 확인 -->
<!-- 시퀀스 존재 여부 확인 - 로직변경 현재사용안함 -->
<select id="checkSequenceExists" parameterType="String" resultType="String">
/* CrdnRegistAndViewMapper.checkSequenceExists : 시퀀스 존재 여부 확인 */
SHOW CREATE SEQUENCE ${sequenceName}
</select>
<!-- 시퀀스 생성 -->
<!-- 시퀀스 생성 - 로직변경 현재사용안함 -->
<update id="createSequence" parameterType="String">
/* CrdnRegistAndViewMapper.createSequence : 년도별 시퀀스 생성 */
CREATE SEQUENCE IF NOT EXISTS ${sequenceName}

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="go.kr.project.crdn.crndRegistAndView.main.mapper.CrdnRelevyHistoryMapper">
<!-- ==================== 재부과 이력 관련 쿼리 ==================== -->
<!-- ==================== 단속 기본 정보 조회 쿼리 ==================== -->
<select id="selectCrdnBasicInfo" parameterType="CrdnRelevyHistoryVO" resultType="CrdnRelevyHistoryVO">
/* CrdnRelevyHistMapper.selectCrdnBasicInfo : 단속 기본 정보 및 위치 정보 조회 */
SELECT
c.CRDN_YR,
c.CRDN_NO,
c.DSCL_YMD,
c.EXMNR,
c.RGN_SE_CD,
rgnSeCd.CD_NM as RGN_SE_CD_NM,
c.SGG_CD,
sggCd.CD_NM as SGG_CD_NM,
p.STDG_EMD_CD,
stdgEmdCd.CD_NM as STDG_EMD_CD_NM,
p.LOTNO_ADDR,
p.LOTNO_MNO,
p.LOTNO_SNO,
c.FRST_CRDN_YR,
c.FRST_CRDN_NO
FROM tb_crdn c
INNER JOIN tb_pstn_info p ON p.CRDN_YR = c.CRDN_YR AND p.CRDN_NO = c.CRDN_NO AND p.DEL_YN = 'N'
LEFT JOIN tb_cd_detail rgnSeCd ON rgnSeCd.CD_GROUP_ID = 'RGN_SE_CD' AND rgnSeCd.CD_ID = c.RGN_SE_CD
LEFT JOIN tb_cd_detail sggCd ON sggCd.CD_GROUP_ID = 'SGG_CD' AND sggCd.CD_ID = c.SGG_CD
LEFT JOIN tb_cd_detail stdgEmdCd ON stdgEmdCd.CD_GROUP_ID = 'STDG_EMD_CD' AND stdgEmdCd.CD_ID = p.STDG_EMD_CD
WHERE c.CRDN_YR = #{crdnYr}
AND c.CRDN_NO = #{crdnNo}
AND c.DEL_YN = 'N'
</select>
<!-- 중요로직: 재부과 chain 목록 조회 - 최초 단속부터 자기 자신 포함한 모든 재부과 이력 -->
<select id="selectRelevyChainList" parameterType="CrdnRelevyHistoryVO" resultType="CrdnRelevyHistoryVO">
/* CrdnRelevyHistMapper.selectRelevyChainList : 재부과 chain 목록 조회 */
SELECT
c.CRDN_YR, /* 단속 연도 */
c.CRDN_NO, /* 단속 번호 */
c.SGG_CD, /* 시군구 코드 */
sgg.CD_NM AS SGG_CD_NM,
c.RGN_SE_CD, /* 지역 구분 코드 */
rgn.CD_NM AS RGN_SE_CD_NM,
c.DSCL_MTHD_CD, /* 단속 방법 코드 */
dscl.CD_NM AS DSCL_MTHD_CD_NM,
c.DSCL_YMD, /* 적발 일자 */
c.EXMNR, /* 조사원 */
c.RMRK, /* 비고 */
c.DSPS_BFHD_BGNG_YMD, /* 사전처분 시작일 */
c.DSPS_BFHD_END_YMD, /* 사전처분 종료일 */
c.CRC_CMD_BGNG_YMD, /* 시정명령 시작일 */
c.CRC_CMD_END_YMD, /* 시정명령 종료일 */
c.CRC_URG_BGNG_YMD, /* 시정촉구 시작일 */
c.CRC_URG_END_YMD, /* 시정촉구 종료일 */
c.LEVY_PRVNTC_BGNG_YMD, /* 부과예고 시작일 */
c.LEVY_PRVNTC_END_YMD, /* 부과예고 종료일 */
c.LEVY_BGNG_YMD, /* 부과 시작일 */
c.LEVY_END_YMD, /* 부과 종료일 */
c.PAY_URG_BGNG_YMD, /* 납부촉구 시작일 */
c.PAY_URG_END_YMD, /* 납부촉구 종료일 */
c.FRST_CRDN_YR, /* 최초 단속 연도 */
c.FRST_CRDN_NO, /* 최초 단속 번호 */
c.RELEVY_YN, /* 재과 여부 */
c.AGRVTN_LEVY_TRGT_YN, /* 가중 부과 대상 여부 */
c.CRDN_PRCS_STTS_CD, /* 단속 처리 상태 코드 */
stts.CD_NM AS CRDN_PRCS_STTS_CD_NM,
c.CRDN_PRCS_YMD, /* 단속 처리 일자 */
c.REG_DT,
c.RGTR,
u.USER_ACNT AS RGTR_ACNT,
u.USER_NM AS RGTR_NM,
p.LOTNO_WHOL_ADDR, /* 지번 전체 주소 */
p.STDG_EMD_CD, /* 법정동 읍면동 코드 */
emd.CD_NM AS STDG_EMD_CD_NM,
p.ZIP,
(SELECT GROUP_CONCAT(DISTINCT o2.FLNM SEPARATOR ', ')
FROM tb_ownr_info oi2
LEFT JOIN tb_ownr o2 ON o2.OWNR_ID = oi2.OWNR_ID AND o2.DEL_YN = 'N'
WHERE oi2.CRDN_YR = c.CRDN_YR
AND oi2.CRDN_NO = c.CRDN_NO
AND oi2.DEL_YN = 'N') AS OWNR_NAMS,
(SELECT GROUP_CONCAT(DISTINCT o2.FLNM SEPARATOR ', ')
FROM tb_actr_info ai
LEFT JOIN tb_ownr o2 ON o2.OWNR_ID = ai.OWNR_ID AND o2.DEL_YN = 'N'
WHERE ai.CRDN_YR = c.CRDN_YR
AND ai.CRDN_NO = c.CRDN_NO
AND ai.DEL_YN = 'N') AS ACTR_NAMS,
a.ACT_TYPE_CD, /* 행위 유형 코드 */
CASE WHEN (SELECT COUNT(1) FROM tb_act_info a2 WHERE a2.CRDN_YR = a.CRDN_YR AND a2.CRDN_NO = a.CRDN_NO AND a2.DEL_YN='N') > 1 THEN
CONCAT(act.VLTN_BDST, ' 등 ', (SELECT COUNT(1) FROM tb_act_info a2 WHERE a2.CRDN_YR = a.CRDN_YR AND a2.CRDN_NO = a.CRDN_NO AND a2.DEL_YN='N'), '건')
ELSE act.VLTN_BDST END ACT_TYPE_CD_NM,
a.USG_IDX_CD, /* 용도 지수 코드 */
usg.USG_NM AS USG_IDX_CD_NM
FROM tb_crdn c
LEFT JOIN tb_cd_detail sgg ON sgg.CD_GROUP_ID = 'ORG_CD' AND sgg.CD_ID = c.SGG_CD
LEFT JOIN tb_cd_detail rgn ON rgn.CD_GROUP_ID = 'RGN_SE_CD' AND rgn.CD_ID = c.RGN_SE_CD
LEFT JOIN tb_cd_detail dscl ON dscl.CD_GROUP_ID = 'DSCL_MTHD_CD' AND dscl.CD_ID = c.DSCL_MTHD_CD
LEFT JOIN tb_cd_detail stts ON stts.CD_GROUP_ID = 'CRDN_PRCS_STTS_CD' AND stts.CD_ID = c.CRDN_PRCS_STTS_CD
LEFT JOIN tb_user u ON u.USER_ID = c.RGTR AND u.USE_YN = 'Y'
LEFT JOIN tb_pstn_info p ON p.CRDN_YR = c.CRDN_YR AND p.CRDN_NO = c.CRDN_NO AND p.DEL_YN = 'N'
LEFT JOIN tb_cd_detail emd ON emd.CD_GROUP_ID = 'STDG_EMD_CD' AND emd.CD_ID = p.STDG_EMD_CD
LEFT Join tb_act_info a ON a.CRDN_YR = c.CRDN_YR and a.CRDN_NO = c.CRDN_NO AND a.DEL_YN = 'N' AND a.ACT_NO = (SELECT MIN(a1.ACT_NO) FROM tb_act_info a1 WHERE a1.CRDN_YR = a.CRDN_YR AND a1.CRDN_NO = a.CRDN_NO AND a1.DEL_YN='N')
LEFT JOIN tb_act_type act ON act.ACT_TYPE_CD = a.ACT_TYPE_CD
LEFT JOIN tb_usg_idx usg ON usg.USG_IDX_CD = a.USG_IDX_CD
WHERE c.DEL_YN = 'N'
AND (
(c.FRST_CRDN_YR = #{frstCrdnYr} AND c.FRST_CRDN_NO = #{frstCrdnNo})
or
(c.CRDN_YR = #{frstCrdnYr} AND c.CRDN_NO = #{frstCrdnNo})
)
ORDER BY c.CRDN_YR ASC, c.CRDN_NO ASC
</select>
</mapper>

@ -97,9 +97,10 @@
AND c.CRDN_NO = #{crdnNo}
</select>
<select id="selectNextCrdnNo" parameterType="string" resultType="string">
/* CrdnRelevyMapper.selectNextCrdnNo : 다음 단속번호 조회 */
SELECT LPAD(NEXTVAL(seq_crdn_no_${crdnYr}), 6, '0') AS NEXT_CRDN_NO
<select id="selectNextCrdnNo" resultType="string">
/* CrdnRelevyMapper.selectNextCrdnNo : 다음 단속번호 조회, 현재는 년도가 아닌 단일 시컨스 사용!! */
<!-- SELECT LPAD(NEXTVAL(seq_crdn_no_${crdnYr}), 6, '0') AS NEXT_CRDN_NO*/ -->
SELECT LPAD(NEXTVAL(seq_crdn_no), 6, '0') AS NEXT_CRDN_NO
</select>
<insert id="insertCrdnBasicInfo" parameterType="CrdnRelevyVO">

@ -40,12 +40,12 @@
<td>${crdnBasicInfo.stdgEmdCdNm}</td>
<td class="text-left">
${crdnBasicInfo.lotnoAddr}
<c:if test="${not empty crdnBasicInfo.lotnoMno}">
<%--<c:if test="${not empty crdnBasicInfo.lotnoMno}">
${crdnBasicInfo.lotnoMno}
<c:if test="${not empty crdnBasicInfo.lotnoSno}">
-${crdnBasicInfo.lotnoSno}
</c:if>
</c:if>
</c:if>--%>
</td>
</c:when>
<c:otherwise>

@ -0,0 +1,306 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="dateUtil" uri="http://egovframework.go.kr/functions/date-util" %>
<!-- 재부과 이력 팝업 -->
<div class="popup_wrap">
<div class="popup_inner">
<div class="popup_tit">
<h2 class="tit" id="popupTitle">
재부과 이력
</h2>
<a href="#" class="pop-x-btn modalclose" id="btnCloseTop"></a>
</div>
<div class="popup_con">
<div class="cash_tables">
<table>
<colgroup>
<col style="width: 120px;" />
<col style="width: 120px;" />
<col style="width: 150px;" />
<col />
</colgroup>
<thead>
<tr>
<th>단속 연도</th>
<th>단속 번호</th>
<th>법정동</th>
<th>위치 주소</th>
</tr>
</thead>
<tbody>
<tr>
<td>${crdnYr}</td>
<td>${crdnNo}</td>
<c:choose>
<c:when test="${not empty crdnBasicInfo}">
<td>${crdnBasicInfo.stdgEmdCdNm}</td>
<td class="text-left">${crdnBasicInfo.lotnoAddr}</td>
</c:when>
<c:otherwise>
<td>-</td>
<td>-</td>
</c:otherwise>
</c:choose>
</tr>
</tbody>
</table>
</div>
<!-- 재부과 이력 그리드 -->
<div class="row" style="margin-top: 20px;">
<div class="col-sm-12">
<div class="box_column">
<ul class="box_title">
<li class="tit">재부과 이력 목록 (최초 단속부터 선택된 단속까지)</li>
</ul>
<div class="containers">
<div id="gridRelevyHist"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 팝업 버튼 -->
<div class="popup_foot">
<button type="button" class="newbtns bg2 modalclose" id="btnClose">닫기</button>
</div>
</div>
</div>
<script type="text/javascript">
/**
* 재부과 이력 팝업 스크립트
* 중요한 로직 주석: 재부과 이력을 조회하여 표시하는 팝업
*/
(function(window, $) {
'use strict';
var RelevyHistPopup = {
/**
* 현재 단속 정보
*/
currentCrdnYr: '${crdnYr}',
currentCrdnNo: '${crdnNo}',
currentFrstCrdnYr: '${crdnBasicInfo.frstCrdnYr != null ? crdnBasicInfo.frstCrdnYr : crdnYr}',
currentFrstCrdnNo: '${crdnBasicInfo.frstCrdnNo != null ? crdnBasicInfo.frstCrdnNo : crdnNo}',
/**
* 그리드 관련 객체
*/
grid: {
/**
* 그리드 인스턴스
*/
instance: null,
/**
* 그리드 설정 초기화
* @returns {Object} 그리드 설정 객체
*/
initConfig: function() {
// 데이터 소스 설정
var dataSource = this.createDataSource();
// 그리드 설정 객체 생성
var gridConfig = new XitTuiGridConfig();
// 기본 설정
gridConfig.setOptDataSource(dataSource); // 데이터소스 연결
gridConfig.setOptGridId('gridRelevyHist'); // 그리드를 출력할 Element ID
gridConfig.setOptGridHeight(400); // 그리드 높이(단위: px)
gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px)
gridConfig.setOptRowHeaderType(''); // 행 첫번째 셀 타입 비활성화
gridConfig.setOptUseClientSort(true); // 클라이언트 정렬 사용
gridConfig.setOptColumns(this.getGridColumns());
return gridConfig;
},
/**
* 그리드 컬럼 정의
* @returns {Array} 그리드 컬럼 배열
*/
getGridColumns: function() {
return [
{ 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: 'dspsBfhdBgngYmd', align: 'center', width: 120,
formatter: function (e) {
return e.value ? moment(e.value).format('YYYY-MM-DD') : '';
}
},
{ header: '시정명령 일자', name: 'crcCmdBgngYmd', align: 'center', width: 120,
formatter: function (e) {
return e.value ? moment(e.value).format('YYYY-MM-DD') : '';
}
},
{ header: '시정촉구 일자', name: 'crcUrgBgngYmd', align: 'center', width: 120,
formatter: function (e) {
return e.value ? moment(e.value).format('YYYY-MM-DD') : '';
}
},
{ 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') : '';
}
},
{ header: '납부촉구 일자', name: 'payUrgBgngYmd', align: 'center', width: 120,
formatter: function (e) {
return e.value ? moment(e.value).format('YYYY-MM-DD') : '';
}
},
{ header: '등록일시', name: 'regDt', align: 'center', width: 150 },
{ header: '등록자', name: 'rgtrNm', align: 'center', width: 100 }
];
},
/**
* 데이터 소스 생성
* @returns {Object} 데이터 소스 설정 객체
*/
createDataSource: function() {
var self = RelevyHistPopup;
return {
api: {
readData: {
url: '<c:url value="/crdn/crndRegistAndView/crdnRelevyHistory/relevyChain.ajax"/>',
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;
}
};
},
/**
* 그리드 인스턴스 생성
*/
create: function() {
var gridConfig = this.initConfig();
var Grid = tui.Grid;
this.instance = gridConfig.instance(Grid);
// 그리드 테마 설정
Grid.applyTheme('striped');
this.gridBindEvents();
},
/**
* 그리드 이벤트 바인딩
*/
gridBindEvents: function() {
var self = this;
// 행 더블클릭 이벤트 - 단속 상세 화면으로 이동
this.instance.on('dblclick', function(ev) {
var rowKey = ev.rowKey;
var rowData = self.instance.getRow(rowKey);
if (rowData) {
var detailUrl = buildUrlWithParamCondAndMultipleKeys(
null,
{"crdnYr": rowData.crdnYr, "crdnNo": rowData.crdnNo},
"<c:url value="/crdn/crndRegistAndView/detailView.do"/>"
);
window.open(detailUrl, 'crdnDetailView');
}
});
}
},
/**
* 이벤트 초기화
*/
eventBindEvents: function() {
var self = this;
// 닫기 버튼 이벤트 바인딩
$('.modalclose').on('click', function(e) {
e.preventDefault();
self.cancel();
});
},
/**
* 팝업 취소 처리
* 중요한 로직 주석: 팝업창을 닫는 기능을 제공합니다.
*/
cancel: function() {
window.close();
},
/**
* 초기화
*/
init: function() {
// 그리드 생성
this.grid.create();
// 이벤트 핸들러 설정
this.eventBindEvents();
// 데이터 로드
this.grid.instance.readData(1);
}
};
// 초기화 실행
$(document).ready(function() {
RelevyHistPopup.init();
});
})(window, jQuery);
</script>

@ -369,7 +369,16 @@
width: 120,
align: 'center',
formatter: function(value) {
return '<span class="btn_unlock badge success" style="cursor:pointer;">이행이력</span>';
return '<span class="btn_unlock badge success" style="cursor:pointer;">이행 이력</span>';
}
},
{
header: '재부과 이력',
name: 'relevyHistoryBtn',
width: 120,
align: 'center',
formatter: function(value) {
return '<span class="btn_unlock badge success" style="cursor:pointer;">재부과 이력</span>';
}
},
{ header: '처분사전 일자', name: 'dspsBfhdBgngYmd', align: 'center', width: 120,
@ -494,6 +503,13 @@
CrdnRegistAndViewList.openImpltTaskHistoryPopup(rowData.crdnYr, rowData.crdnNo);
}
}
if (ev.columnName === 'relevyHistoryBtn') {
var rowKey = ev.rowKey;
var rowData = self.instance.getRow(rowKey);
if (rowData) {
CrdnRegistAndViewList.openRelevyHistoryPopup(rowData.crdnYr, rowData.crdnNo);
}
}
});
// 행 더블클릭 이벤트 - detailView 페이지를 새 탭으로 열기
@ -603,6 +619,19 @@
openPopup(url, 1000, 800, 'impltTaskHistoryPopup');
},
/**
* 재부과 이력 팝업을 엽니다.
*
* @param crdnYr 단속 연도
* @param crdnNo 단속 번호
*/
openRelevyHistoryPopup: function(crdnYr, crdnNo) {
var url = '<c:url value="/crdn/crndRegistAndView/crdnRelevyHistory/relevyHistoryPopup.do"/>?crdnYr=' +
encodeURIComponent(crdnYr) + '&crdnNo=' +
encodeURIComponent(crdnNo);
openPopup(url, 1400, 800, 'relevyHistoryPopup');
},
/**
* 조치처리상태를 확인하고 미조치(코드 1)인 경우에만 콜백 함수를 실행합니다.
* 중요로직: 팝업 호출 전 미조치(코드 1) 여부를 확인하는 공통 함수

Loading…
Cancel
Save