diff --git a/kjb-docs/biz-reg-no-duplicate-check-plan.md b/kjb-docs/biz-reg-no-duplicate-check-plan.md new file mode 100644 index 0000000..7a8e070 --- /dev/null +++ b/kjb-docs/biz-reg-no-duplicate-check-plan.md @@ -0,0 +1,113 @@ +# 사업자등록번호 중복 체크 구현 작업 계획서 + +## 1. 현황 분석 + +### 문제점 +- 법인 등록 시 사업자등록번호(compRegNo) 중복 검증이 없음 +- `PortalOrgRepository.findByCompRegNo()` 메서드는 존재하나 사용되지 않음 +- 동일한 사업자등록번호로 여러 법인이 등록될 수 있는 상태 + +### 검증 시점 +1. **실시간 검증**: 사용자가 사업자등록번호 입력 시 `/check-business-number` API 호출 +2. **등록 시 검증**: 실제 법인 생성 시점 (`registerOrgFromDTOWithFile`) + +--- + +## 2. 수정 대상 파일 + +| 파일 경로 | 수정 내용 | +|-----------|-----------| +| `src/main/java/.../apps/user/service/PortalOrgService.java` | 사업자등록번호 중복 체크 메서드 추가 | +| `src/main/java/.../apps/user/facade/OrgRegisterFacadeImpl.java` | `checkBusinessNumber()` 중복 체크 로직 추가, 등록 전 중복 체크 추가 | + +--- + +## 3. 구현 상세 + +### 3.1 PortalOrgService.java + +```java +// 추가할 메서드 +public boolean existsByCompRegNo(String compRegNo) { + return portalOrgRepository.findByCompRegNo(compRegNo).isPresent(); +} +``` + +### 3.2 OrgRegisterFacadeImpl.java + +#### checkBusinessNumber 메서드 수정 +- 형식 검증 + 중복 검증 동시 수행 +- 중복 시 "이미 등록된 사업자등록번호입니다." 메시지 반환 + +```java +@Override +public ResponseEntity checkBusinessNumber(String compRegNo) { + // 1. 형식 검증 + boolean isValidFormat = validationService.isValidBusinessNumber(compRegNo); + if (!isValidFormat) { + return ResponseEntity.ok(new ValidationResponse(false, "유효하지 않은 사업자 등록번호입니다.")); + } + + // 2. 중복 검증 + boolean isDuplicate = portalOrgService.existsByCompRegNo(compRegNo); + if (isDuplicate) { + return ResponseEntity.ok(new ValidationResponse(false, "이미 등록된 사업자등록번호입니다.")); + } + + return ResponseEntity.ok(new ValidationResponse(true, "사용 가능한 사업자 등록번호입니다.")); +} +``` + +#### 법인 생성 메서드들에 중복 체크 추가 +- `registerNewCorporateUser()` +- `convertExistingUserToCorporate()` +- `registerNewCorporateUserWithExistingData()` + +각 메서드에서 `portalOrgService.registerOrgFromDTOWithFile()` 호출 전 중복 체크 수행 + +--- + +## 4. 검증 흐름 + +``` +[사용자 입력] + ↓ +[실시간 검증] /check-business-number API + ├── 형식 검증 (10자리 숫자) + └── 중복 검증 (DB 조회) + ↓ +[등록 요청] + ↓ +[등록 시 검증] registerOrgFromDTOWithFile 호출 전 + └── 중복 체크 (이중 방어) + ↓ +[법인 생성] +``` + +--- + +## 5. 기존 중복 데이터 처리 + +- 기존에 중복 등록된 데이터는 그대로 유지 +- 신규 등록 시에만 중복 체크 적용 +- 필요시 관리자 화면에서 중복 데이터 조회/정리 기능 별도 개발 가능 + +--- + +## 6. 테스트 시나리오 + +| 시나리오 | 예상 결과 | +|----------|-----------| +| 형식이 잘못된 사업자등록번호 입력 | "유효하지 않은 사업자 등록번호입니다." | +| 이미 등록된 사업자등록번호 입력 | "이미 등록된 사업자등록번호입니다." | +| 사용 가능한 사업자등록번호 입력 | "사용 가능한 사업자 등록번호입니다." | +| 중복된 번호로 법인 등록 시도 | 등록 실패, 에러 메시지 표시 | + +--- + +## 7. 작업 완료 + +- [x] PortalOrgService에 existsByCompRegNo 메서드 추가 +- [x] OrgRegisterFacadeImpl.checkBusinessNumber 수정 +- [x] 법인 등록 메서드들에 중복 체크 로직 추가 +- [x] 빌드 검증 완료 diff --git a/kjb-docs/mermaid/invite-state.mermaid b/kjb-docs/mermaid/invite-state.mermaid index 1699fec..e9a551c 100644 --- a/kjb-docs/mermaid/invite-state.mermaid +++ b/kjb-docs/mermaid/invite-state.mermaid @@ -15,10 +15,10 @@ stateDiagram-v2 팝업취소 --> 로그인: 다음 로그인 시 재표시 } - 팝업확인 --> 약관동의: 수락 페이지 이동 + 팝업확인 --> 초대수락: 수락 페이지 이동 - 약관동의 --> COMPLETED: 수락 - 약관동의 --> REJECTED: 거절 + 초대수락 --> COMPLETED: 수락 + 초대수락 --> REJECTED: 거절 COMPLETED --> [*]: 초대 완료 REJECTED --> [*]: 초대 거절 diff --git a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java index b23c960..5d93a91 100644 --- a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java +++ b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java @@ -181,6 +181,11 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade { PortalOrgRegistrationDTO orgDTO, FileInfo uploadedFile) { + // 사업자등록번호 중복 체크 (이중 방어) + if (portalOrgService.existsByCompRegNo(orgDTO.getCompRegNo())) { + return new ValidationResponse(false, "이미 등록된 사업자등록번호입니다."); + } + // PortalOrgService를 통한 기관 등록 PortalOrg newOrg = portalOrgService.registerOrgFromDTOWithFile(orgDTO, uploadedFile); @@ -218,6 +223,11 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade { PortalOrgRegistrationDTO orgDTO, FileInfo uploadedFile) { + // 사업자등록번호 중복 체크 (이중 방어) + if (portalOrgService.existsByCompRegNo(orgDTO.getCompRegNo())) { + return new ValidationResponse(false, "이미 등록된 사업자등록번호입니다."); + } + // PortalOrgService를 통한 기관 등록 PortalOrg newOrg = portalOrgService.registerOrgFromDTOWithFile(orgDTO, uploadedFile); @@ -243,6 +253,11 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade { String newEmail, FileInfo uploadedFile) { + // 사업자등록번호 중복 체크 (이중 방어) + if (portalOrgService.existsByCompRegNo(orgDTO.getCompRegNo())) { + return new ValidationResponse(false, "이미 등록된 사업자등록번호입니다."); + } + // PortalOrgService를 통한 기관 등록 PortalOrg newOrg = portalOrgService.registerOrgFromDTOWithFile(orgDTO, uploadedFile); @@ -285,9 +300,19 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade { @Override public ResponseEntity checkBusinessNumber(String compRegNo) { - boolean isValid = validationService.isValidBusinessNumber(compRegNo); - String message = isValid ? "사용 가능한 사업자 등록번호입니다." : "유효하지 않은 사업자 등록번호입니다."; - return ResponseEntity.ok(new ValidationResponse(isValid, message)); + // 1. 형식 검증 + boolean isValidFormat = validationService.isValidBusinessNumber(compRegNo); + if (!isValidFormat) { + return ResponseEntity.ok(new ValidationResponse(false, "유효하지 않은 사업자 등록번호입니다.")); + } + + // 2. 중복 검증 + boolean isDuplicate = portalOrgService.existsByCompRegNo(compRegNo); + if (isDuplicate) { + return ResponseEntity.ok(new ValidationResponse(false, "이미 등록된 사업자등록번호입니다.")); + } + + return ResponseEntity.ok(new ValidationResponse(true, "사용 가능한 사업자 등록번호입니다.")); } @Override diff --git a/src/main/java/com/eactive/apim/portal/apps/user/service/PortalOrgService.java b/src/main/java/com/eactive/apim/portal/apps/user/service/PortalOrgService.java index ee2cae8..bab11aa 100644 --- a/src/main/java/com/eactive/apim/portal/apps/user/service/PortalOrgService.java +++ b/src/main/java/com/eactive/apim/portal/apps/user/service/PortalOrgService.java @@ -58,4 +58,9 @@ public class PortalOrgService { org.setOrgStatus(OrgStatus.READY); org.setApprovalStatus(ApprovalStatus.PENDING); } + + // 사업자등록번호 중복 체크 + public boolean existsByCompRegNo(String compRegNo) { + return portalOrgRepository.findByCompRegNo(compRegNo).isPresent(); + } } \ No newline at end of file