diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/service/impl/CrdnActInfoServiceImpl.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/service/impl/CrdnActInfoServiceImpl.java index dd6eaf0..95b930b 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/service/impl/CrdnActInfoServiceImpl.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/service/impl/CrdnActInfoServiceImpl.java @@ -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 existingActrInfoList = actrInfoMapper.selectActrInfoListByOtherAct(searchVO); + + if (existingActrInfoList == null || existingActrInfoList.isEmpty()) { + log.debug("복사할 행위자 정보가 없습니다. 단속정보에 첫 번째 불법행위일 수 있습니다."); + return; + } + + // 중요한 로직 주석: 중복 방지를 위해 이미 복사된 소유자ID를 추적 + Set 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 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에 대해 유효성 검증 후 논리 삭제 수행 diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/mapper/CrdnActrInfoMapper.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/mapper/CrdnActrInfoMapper.java index 5db3866..15a1fde 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/mapper/CrdnActrInfoMapper.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/mapper/CrdnActrInfoMapper.java @@ -12,6 +12,13 @@ import java.util.List; @Mapper public interface CrdnActrInfoMapper { + /** + * 위반행위자정보 목록 조회 + * @param vo 검색조건 및 페이징 정보 + * @return 위반행위자정보 목록 + */ + List selectActrInfoCheckList(CrdnActrInfoVO vo); + /** * 위반행위자정보 목록 조회 * @param vo 검색조건 및 페이징 정보 @@ -49,15 +56,44 @@ public interface CrdnActrInfoMapper { /** * 위반행위자정보 삭제 (논리삭제) :: 불법행위정보 삭제로 인한 전체 삭제 전 count 체크 - * @param vo 삭제할 위반행위자정보 조회 - * @return 삭제 결과 + * @param actInfoIds 행위 정보 ID 목록 + * @return 존재 개수 */ int existsActrInfo(List actInfoIds); /** * 위반행위자정보 삭제 (논리삭제) :: 불법행위정보 삭제로 인한 전체 삭제 - * @param vo 삭제할 위반행위자정보 (delYn, delDt, dltr 포함) + * @param paramMap actInfoIds(행위정보ID 목록), dltr(삭제자ID) 포함 * @return 삭제 결과 */ - int deleteActrInfoByAct(List actInfoIds); + int deleteActrInfoByAct(java.util.Map paramMap); + + /** + * 중요한 로직: 단일 행위정보ID로 행위자정보 삭제 (논리삭제) + * 행위정보 삭제 시 해당 행위의 모든 행위자 정보를 함께 삭제 + * @param vo actInfoId, dltr 필수 + * @return 삭제 결과 + */ + int deleteActrInfoByActInfoId(CrdnActrInfoVO vo); + + /** + * 중요한 로직: 단속건(crdnYr, crdnNo)에 속한 모든 행위정보 ID 조회 + * 모든 행위의 행위자를 동일하게 유지하기 위해 필요 + */ + List selectActInfoIdsByCase(CrdnActrInfoVO vo); + + /** + * 중요한 로직: 단속건 내 특정 소유자에 대한 행위자 정보를 모든 행위에서 일괄 논리삭제 + * @param vo crdnYr, crdnNo, ownrId, dltr 필수 + * @return 삭제(갱신) 건수 + */ + int deleteActrInfoByCaseAndOwner(CrdnActrInfoVO vo); + + /** + * 중요한 로직: 동일 단속정보의 다른 불법행위에서 행위자 정보 조회 + * 신규 불법행위 등록 시 기존 행위자 정보를 복사하기 위해 사용 + * @param vo crdnYr, crdnNo, actInfoId(제외할 행위정보ID) 필수 + * @return 행위자 정보 목록 + */ + List selectActrInfoListByOtherAct(CrdnActrInfoVO vo); } \ No newline at end of file diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/service/impl/CrdnActrInfoServiceImpl.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/service/impl/CrdnActrInfoServiceImpl.java index fc0dec9..5c837c0 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/service/impl/CrdnActrInfoServiceImpl.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActrInfo/service/impl/CrdnActrInfoServiceImpl.java @@ -54,7 +54,9 @@ public class CrdnActrInfoServiceImpl extends EgovAbstractServiceImpl implements // 중요한 로직 주석: 동일한 행위정보ID와 소유자ID에 대한 중복 등록 방지 CrdnActrInfoVO existingRecord = null; if (vo.getActInfoId() != null && vo.getOwnrId() != null) { - List existingList = mapper.selectActrInfoList(CrdnActrInfoVO.builder() + List 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 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 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 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; } diff --git a/src/main/resources/mybatis/mapper/crdn/crndRegistAndView/crdnActrInfo/CrdnActrInfoMapper_maria.xml b/src/main/resources/mybatis/mapper/crdn/crndRegistAndView/crdnActrInfo/CrdnActrInfoMapper_maria.xml index d7b1a64..2c2f794 100644 --- a/src/main/resources/mybatis/mapper/crdn/crndRegistAndView/crdnActrInfo/CrdnActrInfoMapper_maria.xml +++ b/src/main/resources/mybatis/mapper/crdn/crndRegistAndView/crdnActrInfo/CrdnActrInfoMapper_maria.xml @@ -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 limit #{startIndex}, #{perPage} /* 서버사이드 페이징 처리 */ @@ -56,6 +56,31 @@ AND ar.ACT_INFO_ID = #{actInfoId} + + + /* ActrInfoMapper.insertActrInfo : 위반행위자정보 등록 */ @@ -140,17 +165,91 @@ - + /* ActrInfoMapper.deleteActrInfoByAct 위반행위자정보 삭제 (논리삭제) :: 불법행위정보 삭제로 인한 전체 삭제 */ UPDATE tb_actr_info SET DEL_YN = 'Y', DEL_DT = NOW(), DLTR = #{dltr} WHERE ACT_INFO_ID IN - + #{item} AND DEL_YN = 'N' + + + /* 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' + + + + + + + + + + + /* 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} + + \ No newline at end of file