단속정보 등록 시 동일 단속건의 기존 행위자 자동 복사 로직 추가 및 삭제/등록 처리 로직 개선

dev
박성영 2 months ago
parent d91dae85ea
commit 89dd9be7cd

@ -7,6 +7,7 @@ import go.kr.project.crdn.crndRegistAndView.crdnActInfo.model.*;
import go.kr.project.crdn.crndRegistAndView.crdnActInfo.service.CrdnActInfoService;
import go.kr.project.crdn.crndRegistAndView.crdnActInfo.service.CrdnPhotoService;
import go.kr.project.crdn.crndRegistAndView.crdnActrInfo.mapper.CrdnActrInfoMapper;
import go.kr.project.crdn.crndRegistAndView.crdnActrInfo.model.CrdnActrInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -70,6 +71,9 @@ public class CrdnActInfoServiceImpl extends EgovAbstractServiceImpl implements C
if (result > 0) {
log.debug("행위정보 등록 완료: actInfoId={}", vo.getActInfoId());
// 중요한 로직 주석: 동일 단속정보의 다른 불법행위에서 행위자 정보를 조회하여 자동 복사
copyActrInfoFromOtherAct(vo);
// 중요한 로직 주석: 단속 사진이 있는 경우 처리 (CRDN_PHOTO_SE_CD = '1')
if (crdnPhotoFiles != null && !crdnPhotoFiles.isEmpty()) {
int crdnPhotoResult = photoService.insertPhotosWithFiles(crdnPhotoFiles, vo.getActInfoId(), "1", vo);
@ -144,17 +148,27 @@ public class CrdnActInfoServiceImpl extends EgovAbstractServiceImpl implements C
public int deleteActInfo(CrdnActInfoVO vo) {
log.debug("불법위반행위정보 삭제: {}", vo);
// 중요한 로직 주석: 행위정보 삭제 시 관련된 모든 사진도 함께 삭제한다.
// 중요한 로직 주석: 행위정보 삭제 시 해당 행위의 모든 행위자 정보를 먼저 삭제한다.
if (vo.getActInfoId() != null) {
try {
// 중요한 로직 주석: 행위자 정보 삭제
CrdnActrInfoVO actrDeleteVO = CrdnActrInfoVO.builder()
.actInfoId(vo.getActInfoId())
.dltr(vo.getDltr())
.build();
int actrDeleteCount = actrInfoMapper.deleteActrInfoByActInfoId(actrDeleteVO);
log.debug("행위정보 관련 행위자 정보 삭제 완료: actInfoId={}, 삭제 건수={}", vo.getActInfoId(), actrDeleteCount);
// 중요한 로직 주석: 단속 사진 삭제
photoService.deletePhotosByActInfoIdAndCrdnPhotoSecd(vo.getActInfoId(), "1", vo.getDltr());
log.debug("행위정보 단속 사진 삭제 완료: actInfoId={}, crdnPhotoSeCd={}", vo.getActInfoId(), "1");
// 중요한 로직 주석: 조치 사진 삭제
photoService.deletePhotosByActInfoIdAndCrdnPhotoSecd(vo.getActInfoId(), "2", vo.getDltr());
log.debug("행위정보 조치 사진 삭제 완료: actInfoId={}, crdnPhotoSeCd={}", vo.getActInfoId(), "2");
} catch (Exception e) {
log.warn("행위정보 관련 사진 삭제 중 오류 발생: actInfoId={}", vo.getActInfoId(), e);
// 사진 삭제 실패해도 행위정보 삭제는 진행
log.warn("행위정보 관련 행위자/사진 삭제 중 오류 발생: actInfoId={}", vo.getActInfoId(), e);
// 행위자/사진 삭제 실패해도 행위정보 삭제는 진행
}
}
@ -191,6 +205,71 @@ public class CrdnActInfoServiceImpl extends EgovAbstractServiceImpl implements C
return mapper.getAllPstnIdx();
}
/**
* : private
* .
* @param newActInfo VO (actInfoId, crdnYr, crdnNo, sggCd )
*/
private void copyActrInfoFromOtherAct(CrdnActInfoVO newActInfo) {
log.debug("동일 단속정보의 행위자 정보 복사 시작: actInfoId={}, crdnYr={}, crdnNo={}",
newActInfo.getActInfoId(), newActInfo.getCrdnYr(), newActInfo.getCrdnNo());
// 중요한 로직 주석: 현재 사용자 정보를 가져와 등록자로 설정
String userId = SessionUtil.getUserId();
if (userId == null || userId.trim().isEmpty()) {
log.warn("사용자 정보를 가져올 수 없어 행위자 복사를 건너뜁니다.");
return;
}
// 중요한 로직 주석: 동일 단속정보의 다른 불법행위에서 행위자 정보 조회
CrdnActrInfoVO searchVO = CrdnActrInfoVO.builder()
.crdnYr(newActInfo.getCrdnYr())
.crdnNo(newActInfo.getCrdnNo())
.actInfoId(newActInfo.getActInfoId()) // 제외할 행위정보ID (신규 등록한 행위)
.build();
List<CrdnActrInfoVO> existingActrInfoList = actrInfoMapper.selectActrInfoListByOtherAct(searchVO);
if (existingActrInfoList == null || existingActrInfoList.isEmpty()) {
log.debug("복사할 행위자 정보가 없습니다. 단속정보에 첫 번째 불법행위일 수 있습니다.");
return;
}
// 중요한 로직 주석: 중복 방지를 위해 이미 복사된 소유자ID를 추적
Set<String> copiedOwnrIds = new HashSet<>();
int copiedCount = 0;
// 중요한 로직 주석: 조회된 행위자 정보를 신규 불법행위에 복사
for (CrdnActrInfoVO existingActrInfo : existingActrInfoList) {
// 중요한 로직 주석: 이미 복사한 소유자는 건너뛰기
if (copiedOwnrIds.contains(existingActrInfo.getOwnrId())) {
continue;
}
// 중요한 로직 주석: 신규 행위자 정보 생성 (기존 행위자의 소유자ID를 복사)
CrdnActrInfoVO newActrInfo = CrdnActrInfoVO.builder()
.sggCd(newActInfo.getSggCd())
.crdnYr(newActInfo.getCrdnYr())
.crdnNo(newActInfo.getCrdnNo())
.actInfoId(newActInfo.getActInfoId()) // 신규 행위정보ID
.ownrId(existingActrInfo.getOwnrId()) // 기존 소유자ID 복사
.rgtr(userId)
.build();
// 중요한 로직 주석: 행위자 정보 등록
int insertResult = actrInfoMapper.insertActrInfo(newActrInfo);
if (insertResult > 0) {
copiedOwnrIds.add(existingActrInfo.getOwnrId());
copiedCount++;
log.debug("행위자 정보 복사 완료: ownrId={}, 기존 actInfoId={}, 신규 actInfoId={}",
existingActrInfo.getOwnrId(), existingActrInfo.getActInfoId(), newActInfo.getActInfoId());
}
}
log.info("행위자 정보 복사 완료: 총 {}건 복사됨", copiedCount);
}
/**
* ()
* : .
@ -216,16 +295,23 @@ public class CrdnActInfoServiceImpl extends EgovAbstractServiceImpl implements C
throw new MessageException("사용자 정보를 확인할 수 없습니다.");
}
int checkCount = actrInfoMapper.existsActrInfo(actInfoIds);
if( checkCount > 0 ) {
throw new MessageException("행위자 정보 삭제 후 불법행위정보를 삭제 할 수 있습니다.");
}
// 중요한 로직 주석: 강제이행금산출 정보가 있는지 체크
int checkLevyCount = mapper.existsLevyInfoList(actInfoIds);
if( checkLevyCount > 0 ) {
throw new MessageException("이미 등록된 강제이행금산출 정보가 있습니다. \n부과예고에서 강제이행금산출 정보 삭제 후 불법행위정보를 삭제 할 수 있습니다.");
}
// 중요한 로직 주석: 행위정보 삭제 시 해당 행위의 모든 행위자 정보를 먼저 일괄 삭제한다.
try {
java.util.Map<String, Object> paramMap = new java.util.HashMap<>();
paramMap.put("actInfoIds", actInfoIds);
paramMap.put("dltr", userId);
int actrDeleteCount = actrInfoMapper.deleteActrInfoByAct(paramMap);
log.debug("일괄 행위자 정보 삭제 완료: 삭제 건수={}", actrDeleteCount);
} catch (Exception e) {
throw new MessageException("일괄 행위자 정보 삭제 중 오류가 발생했습니다.");
}
// 중요한 로직 주석: 각 행위정보 ID에 대해 유효성 검증 후 논리 삭제 수행

@ -12,6 +12,13 @@ import java.util.List;
@Mapper
public interface CrdnActrInfoMapper {
/**
*
* @param vo
* @return
*/
List<CrdnActrInfoVO> selectActrInfoCheckList(CrdnActrInfoVO vo);
/**
*
* @param vo
@ -49,15 +56,44 @@ public interface CrdnActrInfoMapper {
/**
* () :: count
* @param vo
* @return
* @param actInfoIds ID
* @return
*/
int existsActrInfo(List<String> actInfoIds);
/**
* () ::
* @param vo (delYn, delDt, dltr )
* @param paramMap actInfoIds(ID ), dltr(ID)
* @return
*/
int deleteActrInfoByAct(List<String> actInfoIds);
int deleteActrInfoByAct(java.util.Map<String, Object> paramMap);
/**
* : ID ()
*
* @param vo actInfoId, dltr
* @return
*/
int deleteActrInfoByActInfoId(CrdnActrInfoVO vo);
/**
* : (crdnYr, crdnNo) ID
*
*/
List<String> selectActInfoIdsByCase(CrdnActrInfoVO vo);
/**
* :
* @param vo crdnYr, crdnNo, ownrId, dltr
* @return ()
*/
int deleteActrInfoByCaseAndOwner(CrdnActrInfoVO vo);
/**
* :
*
* @param vo crdnYr, crdnNo, actInfoId( ID)
* @return
*/
List<CrdnActrInfoVO> selectActrInfoListByOtherAct(CrdnActrInfoVO vo);
}

@ -54,7 +54,9 @@ public class CrdnActrInfoServiceImpl extends EgovAbstractServiceImpl implements
// 중요한 로직 주석: 동일한 행위정보ID와 소유자ID에 대한 중복 등록 방지
CrdnActrInfoVO existingRecord = null;
if (vo.getActInfoId() != null && vo.getOwnrId() != null) {
List<CrdnActrInfoVO> existingList = mapper.selectActrInfoList(CrdnActrInfoVO.builder()
List<CrdnActrInfoVO> existingList = mapper.selectActrInfoCheckList(CrdnActrInfoVO.builder()
.crdnYr(vo.getCrdnYr()) // 중복체크 보완: 단속 연도 포함
.crdnNo(vo.getCrdnNo()) // 중복체크 보완: 단속 번호 포함
.actInfoId(vo.getActInfoId())
.ownrId(vo.getOwnrId())
.build());
@ -110,34 +112,38 @@ public class CrdnActrInfoServiceImpl extends EgovAbstractServiceImpl implements
throw new MessageException("사용자 정보를 확인할 수 없습니다.");
}
// 중요한 로직 주석: 각 행위자정보 ID에 대해 유효성 검증 후 논리 삭제 수행
// 중요한 로직 주석: 동일 단속건의 모든 행위에서 동일한 소유자를 함께 삭제하기 위해
// 입력된 actrInfoId들을 (crdnYr, crdnNo, ownrId) 조합으로 유니크 처리하여 중복 삭제를 방지한다.
int deletedCount = 0;
java.util.Set<String> uniqKeys = new java.util.HashSet<>();
for (String actrInfoId : actrInfoIds) {
if (actrInfoId != null && !actrInfoId.trim().isEmpty()) {
// 중요한 로직 주석: 삭제할 데이터 존재 여부 확인
CrdnActrInfoVO existingData = mapper.selectActrInfoByPk(actrInfoId);
if (existingData == null) {
log.warn("삭제할 행위자정보를 찾을 수 없습니다. actrInfoId: {}", actrInfoId);
continue;
}
// 중요한 로직 주석: 삭제 VO 생성 및 삭제자 정보 설정
CrdnActrInfoVO deleteVO = CrdnActrInfoVO.builder()
.actrInfoId(actrInfoId)
.dltr(userId)
.build();
int result = mapper.deleteActrInfo(deleteVO);
if (result > 0) {
deletedCount++;
log.debug("행위자정보 삭제 완료. actrInfoId: {}", actrInfoId);
} else {
log.warn("행위자정보 삭제 실패. actrInfoId: {}", actrInfoId);
}
if (actrInfoId == null || actrInfoId.trim().isEmpty()) {
continue;
}
CrdnActrInfoVO existingData = mapper.selectActrInfoByPk(actrInfoId);
if (existingData == null) {
log.warn("삭제할 행위자정보를 찾을 수 없습니다. actrInfoId: {}", actrInfoId);
continue;
}
String key = existingData.getCrdnYr() + "|" + existingData.getCrdnNo() + "|" + existingData.getOwnrId();
if (uniqKeys.contains(key)) {
continue;
}
uniqKeys.add(key);
// 중요한 로직 주석: 단속건의 모든 행위에서 해당 소유자에 대한 행위자 정보를 일괄 논리삭제
CrdnActrInfoVO deleteByCaseVO = CrdnActrInfoVO.builder()
.crdnYr(existingData.getCrdnYr())
.crdnNo(existingData.getCrdnNo())
.ownrId(existingData.getOwnrId())
.dltr(userId)
.build();
int affected = mapper.deleteActrInfoByCaseAndOwner(deleteByCaseVO);
deletedCount += affected;
log.debug("단속건 전체 행위에서 소유자 삭제 완료. crdnYr: {}, crdnNo: {}, ownrId: {}, 삭제건수: {}", existingData.getCrdnYr(), existingData.getCrdnNo(), existingData.getOwnrId(), affected);
}
log.info("위반행위자정보 일괄 삭제 완료. 요청: {}건, 삭제: {}건", actrInfoIds.size(), deletedCount);
log.info("위반행위자정보 일괄 삭제 완료(전체 행위 동기화). 요청키: {}건, 삭제총건수: {}", uniqKeys.size(), deletedCount);
return deletedCount;
}
@ -150,11 +156,47 @@ public class CrdnActrInfoServiceImpl extends EgovAbstractServiceImpl implements
@Override
@Transactional
public int saveSelectedActrList(List<CrdnActrInfoVO> list) throws Exception {
int savedCount = 0;
// 중요한 로직 주석: 행위자 등록은 "해당 단속건의 모든 행위"에 동일하게 반영되어야 한다.
// 1) 입력 목록이 비어있으면 즉시 종료
if (list == null || list.isEmpty()) {
return 0;
}
// 2) 세션 사용자 설정
String rgtr = SessionUtil.getUserId();
for (CrdnActrInfoVO vo : list) {
vo.setRgtr(rgtr);
savedCount += this.insertActrInfo(vo);
// 3) 기준 단속건 식별 (첫 행 기준) - 프론트에서 동일 단속건만 넘어옴
String crdnYr = list.get(0).getCrdnYr();
String crdnNo = list.get(0).getCrdnNo();
// 방어코드: 필수키 누락 시 예외 처리
if (crdnYr == null || crdnNo == null) {
throw new MessageException("단속 기본정보가 없습니다.");
}
// 4) 단속건에 속한 전체 행위정보 ID 조회
List<String> actInfoIds = mapper.selectActInfoIdsByCase(CrdnActrInfoVO.builder()
.crdnYr(crdnYr)
.crdnNo(crdnNo)
.build());
if (actInfoIds == null || actInfoIds.isEmpty()) {
// 단속건에 행위가 없다면 아무 것도 하지 않음
return 0;
}
int savedCount = 0;
// 5) 입력된 각 소유자(ownrId)에 대해, 단속건의 모든 행위(actInfoId)에 등록
for (CrdnActrInfoVO input : list) {
for (String actInfoId : actInfoIds) {
CrdnActrInfoVO toInsert = CrdnActrInfoVO.builder()
.sggCd(input.getSggCd())
.crdnYr(crdnYr)
.crdnNo(crdnNo)
.actInfoId(actInfoId)
.ownrId(input.getOwnrId())
.rgtr(rgtr)
.build();
savedCount += this.insertActrInfo(toInsert); // 내부에서 중복 체크 수행
}
}
return savedCount;
}

@ -38,7 +38,7 @@
AND ar.CRDN_YR = #{crdnYr}
AND ar.CRDN_NO = #{crdnNo}
AND ar.ACT_INFO_ID = #{actInfoId}
ORDER BY ar.REG_DT ASC, ar.ACTR_INFO_ID ASC
ORDER BY FLNM ASC
<if test='pagingYn != null and pagingYn == "Y"'>
limit #{startIndex}, #{perPage} /* 서버사이드 페이징 처리 */
</if>
@ -56,6 +56,31 @@
AND ar.ACT_INFO_ID = #{actInfoId}
</select>
<!-- 중요한 로직: 동일 단속정보의 다른 불법행위에서 행위자 정보 조회 (신규 불법행위 등록 시 기존 행위자 복사용) -->
<select id="selectActrInfoListByOtherAct" parameterType="CrdnActrInfoVO" resultType="CrdnActrInfoVO">
/* ActrInfoMapper.selectActrInfoListByOtherAct : 동일 단속정보의 다른 불법행위에서 행위자 정보 조회 */
SELECT
ar.ACTR_INFO_ID,
ar.SGG_CD,
ar.CRDN_YR,
ar.CRDN_NO,
ar.ACT_INFO_ID,
ar.OWNR_ID,
o.FLNM,
ECL_DECRYPT(o.RRNO) AS RRNO,
o.OWNR_SE_CD,
o.ADDR AS ADDR,
o.DADDR AS DADDR,
o.ZIP
FROM tb_actr_info ar
INNER JOIN tb_ownr o ON o.OWNR_ID = ar.OWNR_ID
WHERE ar.DEL_YN = 'N'
AND ar.CRDN_YR = #{crdnYr}
AND ar.CRDN_NO = #{crdnNo}
AND ar.ACT_INFO_ID != #{actInfoId}
ORDER BY ar.REG_DT DESC, ar.ACTR_INFO_ID DESC
</select>
<!-- 위반행위자정보 등록 (시컨스 사용) -->
<insert id="insertActrInfo" parameterType="CrdnActrInfoVO">
/* ActrInfoMapper.insertActrInfo : 위반행위자정보 등록 */
@ -140,17 +165,91 @@
</select>
<!-- 위반행위자정보 삭제 (논리삭제) :: 불법행위정보 삭제로 인한 전체 삭제 -->
<update id="deleteActrInfoByAct" parameterType="List">
<update id="deleteActrInfoByAct" parameterType="map">
/* ActrInfoMapper.deleteActrInfoByAct 위반행위자정보 삭제 (논리삭제) :: 불법행위정보 삭제로 인한 전체 삭제 */
UPDATE tb_actr_info SET
DEL_YN = 'Y',
DEL_DT = NOW(),
DLTR = #{dltr}
WHERE ACT_INFO_ID IN
<foreach collection="list" item="item" separator="," open="(" close=")">
<foreach collection="actInfoIds" item="item" separator="," open="(" close=")">
#{item}
</foreach>
AND DEL_YN = 'N'
</update>
<!-- 중요한 로직: 단일 행위정보ID로 행위자정보 삭제 (논리삭제) -->
<update id="deleteActrInfoByActInfoId" parameterType="CrdnActrInfoVO">
/* ActrInfoMapper.deleteActrInfoByActInfoId : 단일 행위정보ID로 행위자정보 삭제 */
UPDATE tb_actr_info SET
DEL_YN = 'Y',
DEL_DT = NOW(),
DLTR = #{dltr}
WHERE ACT_INFO_ID = #{actInfoId}
AND DEL_YN = 'N'
</update>
<!-- 위반행위자정보 중복 체크 목록 조회 -->
<select id="selectActrInfoCheckList" parameterType="CrdnActrInfoVO"
resultType="CrdnActrInfoVO">
/* ActrInfoMapper.selectActrInfoCheckList : 위반행위자정보 목록 조회 */
SELECT
ar.ACTR_INFO_ID,
ar.SGG_CD,
sgg.CD_NM AS SGG_CD_NM,
ar.CRDN_YR,
ar.CRDN_NO,
ar.ACT_INFO_ID,
ar.OWNR_ID,
ar.REG_DT,
ar.RGTR,
regUser.USER_ACNT AS RGTR_ACNT,
regUser.USER_NM AS RGTR_NM,
ar.DEL_YN,
ar.DEL_DT,
ar.DLTR,
o.FLNM,
ECL_DECRYPT(o.RRNO) AS RRNO,
o.OWNR_SE_CD,
ownrSe.CD_NM AS OWNR_SE_CD_NM,
o.ADDR AS ADDR,
o.DADDR AS DADDR,
o.ZIP
FROM tb_actr_info ar
LEFT JOIN tb_cd_detail sgg ON sgg.CD_GROUP_ID = 'ORG_CD' AND sgg.CD_ID = ar.SGG_CD
LEFT JOIN tb_user regUser ON regUser.USER_ID = ar.RGTR
INNER JOIN tb_ownr o ON o.OWNR_ID = ar.OWNR_ID
LEFT JOIN tb_cd_detail ownrSe ON ownrSe.CD_GROUP_ID = 'OWNR_SE_CD' AND ownrSe.CD_ID = o.OWNR_SE_CD
WHERE ar.DEL_YN = 'N'
AND ar.CRDN_YR = #{crdnYr}
AND ar.CRDN_NO = #{crdnNo}
AND ar.ACT_INFO_ID = #{actInfoId}
AND o.OWNR_ID = #{ownrId}
ORDER BY ar.REG_DT ASC, ar.ACTR_INFO_ID ASC
</select>
<!-- 중요로직: 단속건(crdnYr, crdnNo)에 속한 모든 행위정보 ID 조회 -->
<select id="selectActInfoIdsByCase" parameterType="CrdnActrInfoVO" resultType="string">
/* ActrInfoMapper.selectActInfoIdsByCase : 단속건의 모든 행위정보 ID 조회 */
SELECT ACT_INFO_ID
FROM tb_act_info
WHERE DEL_YN = 'N'
AND CRDN_YR = #{crdnYr}
AND CRDN_NO = #{crdnNo}
ORDER BY ACT_INFO_ID
</select>
<!-- 중요로직: 단속건 내 특정 소유자에 대한 행위자 정보를 모든 행위에서 일괄 논리삭제 -->
<update id="deleteActrInfoByCaseAndOwner" parameterType="CrdnActrInfoVO">
/* ActrInfoMapper.deleteActrInfoByCaseAndOwner : 단속건의 특정 소유자 전체 행위에서 삭제 */
UPDATE tb_actr_info
SET DEL_YN = 'Y',
DEL_DT = NOW(),
DLTR = #{dltr}
WHERE DEL_YN = 'N'
AND CRDN_YR = #{crdnYr}
AND CRDN_NO = #{crdnNo}
AND OWNR_ID = #{ownrId}
</update>
</mapper>
Loading…
Cancel
Save