법인 회원 초대 방법 개선
This commit is contained in:
@@ -2,6 +2,55 @@
|
||||
|
||||
광주은행 엔터프라이즈 API 포털 관리 시스템
|
||||
|
||||
## 프로젝트 클론 (Git Clone)
|
||||
|
||||
아래 스크립트를 복사하여 한 번에 실행하면 모든 프로젝트를 클론합니다:
|
||||
|
||||
```bash
|
||||
# 변수 설정 (환경에 맞게 수정하세요)
|
||||
GIT_URL="ssh://git@192.168.240.178:18081/eapim"
|
||||
PROJECT_HOME="/c/eactive/workspaces/kjb-eapim-devPortal"
|
||||
DEFAULT_BRANCH="jenkins_with_weblogic"
|
||||
|
||||
# 프로젝트 홈 디렉토리 생성 및 이동
|
||||
mkdir -p "$PROJECT_HOME"
|
||||
cd "$PROJECT_HOME"
|
||||
|
||||
# 메인 프로젝트 클론
|
||||
git clone -b "$DEFAULT_BRANCH" "$GIT_URL/kjb-eapim.git" kjb-eapim
|
||||
|
||||
# 의존 모듈 클론 (kjb-eapim 폴더와 동일 레벨)
|
||||
mkdir -p eapim-online
|
||||
git clone -b "$DEFAULT_BRANCH" "$GIT_URL/elink-online-core-jpa.git" eapim-online/elink-online-core-jpa
|
||||
git clone -b "$DEFAULT_BRANCH" "$GIT_URL/elink-portal-common.git" elink-portal-common
|
||||
git clone -b "$DEFAULT_BRANCH" "$GIT_URL/kjb-safedb.git" kjb-safedb
|
||||
|
||||
# 클론 후 폴더 구조 확인
|
||||
echo "=== 프로젝트 구조 ==="
|
||||
ls -la "$PROJECT_HOME"
|
||||
```
|
||||
|
||||
### 클론 후 폴더 구조
|
||||
|
||||
```
|
||||
$PROJECT_HOME/
|
||||
├── kjb-eapim/ # 메인 프로젝트
|
||||
│ └── eapim-portal/ # 이 프로젝트 (Portal)
|
||||
├── eapim-online/ # Gateway 데이터 모델
|
||||
│ └── elink-online-core-jpa/ # JPA 엔티티 모듈
|
||||
├── elink-portal-common/ # 공통 유틸리티
|
||||
└── kjb-safedb/ # SafeDB 암호화 라이브러리
|
||||
```
|
||||
|
||||
### 의존 관계 (settings.gradle 참조)
|
||||
|
||||
```
|
||||
eapim-portal
|
||||
├── elink-online-core-jpa → ../eapim-online/elink-online-core-jpa
|
||||
├── elink-portal-common → ../elink-portal-common
|
||||
└── kjb-safedb → ../kjb-safedb
|
||||
```
|
||||
|
||||
## 프로젝트 개요
|
||||
|
||||
**EAPIM Portal**은 광주은행의 API 서비스를 관리하고, 사용자 등록, API 키 발급, 문서화, 테스트 기능을 제공하는 웹 기반 플랫폼입니다.
|
||||
@@ -136,7 +185,3 @@ docker run -p 8080:8080 eapim-portal:latest
|
||||
- **프로젝트 상세 지침**: `CLAUDE.md` (한글)
|
||||
- **사용자 가이드**: `개발자포탈.md` (한글)
|
||||
- **빌드 스크립트**: `build-gf63.sh`, `deploy_portal.sh`
|
||||
|
||||
## 라이선스
|
||||
|
||||
© 광주은행 (Kwangju Bank)
|
||||
|
||||
@@ -1,99 +1,105 @@
|
||||
flowchart TD
|
||||
Start([법인 관리자가 사용자 초대]) --> GenerateToken[8글자 토큰 생성<br/>예: K7MNPQ2R<br/>UserInvitation 저장]
|
||||
Start([법인 관리자가 사용자 초대]) --> GenerateToken[8글자 토큰 생성<br/>예: K7MNPQ2R<br/>UserInvitation 저장<br/>상태: PENDING]
|
||||
|
||||
GenerateToken --> SendEmail[이메일 발송<br/>- 링크 포함<br/>- 8글자 토큰 표시]
|
||||
|
||||
SendEmail --> UserType{사용자 유형}
|
||||
|
||||
%% ========================================
|
||||
%% 신규 회원 플로우
|
||||
%% ========================================
|
||||
UserType -->|신규 회원<br/>미가입자| NewUserPath{초대 확인 방법}
|
||||
|
||||
NewUserPath -->|폐쇄망 환경| LoginPage1[로그인 페이지 접근]
|
||||
LoginPage1 --> InviteLink1["초대를 받으셨나요?" 링크 클릭]
|
||||
InviteLink1 --> InputCodePage1[초대코드 입력 페이지]
|
||||
InputCodePage1 --> InputCode1[8자리 코드 입력<br/>예: K7MNPQ2R]
|
||||
InputCode1 --> ValidateCode1{코드 유효성 검증}
|
||||
|
||||
ValidateCode1 -->|유효하지 않음| Error1[에러: 유효하지 않은 초대코드]
|
||||
ValidateCode1 -->|유효| SignupPage1[회원가입 페이지<br/>/signup/portalUser?invitation=CODE]
|
||||
|
||||
NewUserPath -->|이메일 링크| EmailLink1[이메일 링크 클릭<br/>/signup/portalUser?invitation=CODE]
|
||||
EmailLink1 --> SignupPage1
|
||||
NewUserPath -->|일반 회원가입| DirectSignup[일반 회원가입 진행<br/>ROLE_USER로 생성]
|
||||
|
||||
SignupPage1 --> RegisterForm[가입 정보 입력<br/>- 이메일 자동 입력됨<br/>- 법인명 표시<br/>- 약관 동의]
|
||||
RegisterForm --> RegisterComplete[회원가입 완료<br/>ROLE_CORP_USER로 생성]
|
||||
EmailLink1 --> SignupPage1[회원가입 페이지<br/>- 이메일 자동 입력됨<br/>- 법인명 표시]
|
||||
SignupPage1 --> RegisterForm[가입 정보 입력<br/>약관 동의]
|
||||
RegisterForm --> RegisterComplete[회원가입 완료<br/>ROLE_CORP_USER로 생성<br/>상태: COMPLETED]
|
||||
RegisterComplete --> NewUserEnd([가입 완료 안내])
|
||||
|
||||
DirectSignup --> Login4[로그인]
|
||||
Login4 --> AfterLogin4[로그인 성공]
|
||||
AfterLogin4 --> CheckInvitation
|
||||
|
||||
%% ========================================
|
||||
%% 기존 회원 플로우
|
||||
%% ========================================
|
||||
UserType -->|기존 회원<br/>ROLE_USER| ExistingUserPath{초대 확인 방법}
|
||||
|
||||
ExistingUserPath -->|폐쇄망 환경| LoginPage2[로그인 페이지 접근]
|
||||
LoginPage2 --> InviteLink2["초대를 받으셨나요?" 링크 클릭]
|
||||
InviteLink2 --> InputCodePage2[초대코드 입력 페이지]
|
||||
InputCodePage2 --> CheckLogin1{로그인 여부}
|
||||
ExistingUserPath -->|이메일 링크<br/>비로그인 상태| EmailLink2[이메일 링크 클릭<br/>/signup/decision?invitation=CODE]
|
||||
ExistingUserPath -->|이메일 링크<br/>로그인 상태| EmailLinkLoggedIn[이메일 링크 클릭<br/>/signup/decision?invitation=CODE]
|
||||
ExistingUserPath -->|직접 로그인| DirectLogin[로그인 페이지에서 로그인]
|
||||
|
||||
CheckLogin1 -->|미로그인| RedirectLogin1[로그인 페이지로 리다이렉트<br/>코드 저장]
|
||||
RedirectLogin1 --> Login1[로그인]
|
||||
Login1 --> AfterLogin1[로그인 성공]
|
||||
|
||||
CheckLogin1 -->|로그인됨| InputCode2[8자리 코드 입력]
|
||||
InputCode2 --> ValidateCode2{코드 유효성 검증}
|
||||
ValidateCode2 -->|유효하지 않음| Error2[에러: 유효하지 않은 초대코드]
|
||||
ValidateCode2 -->|유효| DecisionPage1[초대 수락 페이지<br/>/signup/decision_process]
|
||||
|
||||
ExistingUserPath -->|이메일 링크| EmailLink2[이메일 링크 클릭<br/>/signup/decision?invitation=CODE]
|
||||
%% 비로그인 상태에서 이메일 링크 클릭
|
||||
EmailLink2 --> DecisionPagePublic[초대 수락 페이지<br/>비로그인 상태]
|
||||
DecisionPagePublic --> RedirectLogin2[로그인 유도]
|
||||
RedirectLogin2 --> Login2[로그인]
|
||||
Login2 --> AfterLogin2[로그인 성공]
|
||||
|
||||
ExistingUserPath -->|직접 로그인| DirectLogin[로그인 페이지에서 로그인]
|
||||
%% 로그인 상태에서 이메일 링크 클릭 → 바로 수락 페이지
|
||||
EmailLinkLoggedIn --> DecisionPageDirect[초대 수락 페이지<br/>/signup/decision_process<br/>약관 동의]
|
||||
DecisionPageDirect --> UserDecision
|
||||
|
||||
%% 직접 로그인
|
||||
DirectLogin --> Login3[로그인]
|
||||
Login3 --> AfterLogin3[로그인 성공]
|
||||
|
||||
%% 로그인 후 자동 초대 확인
|
||||
AfterLogin1 --> CheckInvitation[초대 코드 자동 확인<br/>findFirstByInvitationEmailAndStatus]
|
||||
AfterLogin2 --> CheckInvitation
|
||||
%% ========================================
|
||||
%% 로그인 후 초대 확인 (PortalAuthenticationSuccessHandler)
|
||||
%% ========================================
|
||||
AfterLogin2 --> CheckInvitation[PortalAuthenticationSuccessHandler<br/>PENDING 초대 확인<br/>findFirstByInvitationEmailAndStatus]
|
||||
AfterLogin3 --> CheckInvitation
|
||||
|
||||
CheckInvitation --> HasInvitation{초대 코드 존재?}
|
||||
CheckInvitation --> HasInvitation{PENDING 초대 존재?}
|
||||
|
||||
HasInvitation -->|없음| NormalHome[일반 홈페이지]
|
||||
HasInvitation -->|있음| AutoRedirect[자동으로 초대 수락 페이지로 이동<br/>/signup/decision_process]
|
||||
HasInvitation -->|없음| NormalHome[메인 페이지 이동]
|
||||
HasInvitation -->|있음| SaveToSession[세션에 초대 정보 저장<br/>pendingInvitation=true<br/>pendingInvitationToken<br/>pendingInvitationOrgName]
|
||||
|
||||
AutoRedirect --> DecisionPage1
|
||||
DecisionPage1 --> UserDecision{사용자 선택}
|
||||
SaveToSession --> RedirectHome[메인 페이지로 이동]
|
||||
|
||||
UserDecision -->|수락| AcceptProcess[초대 수락 처리<br/>1. PortalOrg 변경<br/>2. ROLE_CORP_USER 전환<br/>3. 약관 재동의]
|
||||
UserDecision -->|거절| RejectProcess[초대 거절 처리<br/>Invitation.status = REJECTED]
|
||||
%% ========================================
|
||||
%% 메인 페이지 팝업 (index.html)
|
||||
%% ========================================
|
||||
RedirectHome --> ShowPopup[팝업 표시<br/>OOO에서 초대가 도착했습니다<br/>초대를 확인하시겠습니까?]
|
||||
|
||||
AcceptProcess --> CompleteInvitation[Invitation.status = COMPLETED]
|
||||
CompleteInvitation --> LogoutPrompt[로그아웃 후 재로그인 안내]
|
||||
ShowPopup --> PopupChoice{사용자 선택}
|
||||
|
||||
PopupChoice -->|확인| GoDecisionPage[초대 수락 페이지 이동<br/>/signup/decision?invitation=TOKEN]
|
||||
PopupChoice -->|취소| StayHome[메인 페이지 유지<br/>다음 로그인 시 팝업 재표시]
|
||||
|
||||
StayHome --> NextLogin[다음 로그인]
|
||||
NextLogin --> CheckInvitation
|
||||
|
||||
GoDecisionPage --> DecisionPage[초대 수락 페이지<br/>/signup/decision_process<br/>약관 동의]
|
||||
|
||||
DecisionPage --> UserDecision{사용자 선택}
|
||||
|
||||
UserDecision -->|수락| AcceptProcess[초대 수락 처리<br/>1. PortalOrg 변경<br/>2. ROLE_CORP_USER 전환<br/>3. 약관 재동의<br/>상태: COMPLETED]
|
||||
UserDecision -->|거절| RejectProcess[초대 거절 처리<br/>상태: REJECTED]
|
||||
|
||||
AcceptProcess --> LogoutPrompt[로그아웃 후 재로그인 안내]
|
||||
LogoutPrompt --> ExistingUserEnd([초대 수락 완료])
|
||||
|
||||
RejectProcess --> RejectEnd([초대 거절 완료])
|
||||
|
||||
%% 에러 처리
|
||||
Error1 --> ErrorEnd([처리 종료])
|
||||
Error2 --> ErrorEnd
|
||||
RejectProcess --> RejectEnd([초대 거절 완료<br/>다음 로그인 시 팝업 안 뜸])
|
||||
|
||||
%% ========================================
|
||||
%% 스타일링
|
||||
%% ========================================
|
||||
style Start fill:#e1f5e1
|
||||
style NewUserEnd fill:#c8e6c9
|
||||
style ExistingUserEnd fill:#c8e6c9
|
||||
style RejectEnd fill:#fff9c4
|
||||
style ErrorEnd fill:#ffcdd2
|
||||
style Error1 fill:#ffcdd2
|
||||
style Error2 fill:#ffcdd2
|
||||
style NormalHome fill:#e3f2fd
|
||||
|
||||
style GenerateToken fill:#bbdefb
|
||||
style AcceptProcess fill:#c5cae9
|
||||
style CheckInvitation fill:#ffe0b2
|
||||
style AutoRedirect fill:#ffe0b2
|
||||
style RejectProcess fill:#ffecb3
|
||||
|
||||
style LoginPage1 fill:#e1bee7
|
||||
style LoginPage2 fill:#e1bee7
|
||||
style InviteLink1 fill:#f8bbd0
|
||||
style InviteLink2 fill:#f8bbd0
|
||||
style InputCodePage1 fill:#f8bbd0
|
||||
style InputCodePage2 fill:#f8bbd0
|
||||
style CheckInvitation fill:#ffe0b2
|
||||
style SaveToSession fill:#ffe0b2
|
||||
style ShowPopup fill:#f8bbd0
|
||||
style PopupChoice fill:#f8bbd0
|
||||
|
||||
style StayHome fill:#e1bee7
|
||||
style NextLogin fill:#e1bee7
|
||||
|
||||
@@ -6,10 +6,17 @@ sequenceDiagram
|
||||
participant MessageService as Message Handler
|
||||
participant Email as Email System
|
||||
actor 초대받은사용자
|
||||
participant AuthHandler as PortalAuthenticationSuccessHandler
|
||||
participant IndexController
|
||||
participant MainPage as 메인 페이지 (index.html)
|
||||
participant UserRegisterController
|
||||
participant UserRegisterFacade
|
||||
participant PortalUserRepo as PortalUser Repository
|
||||
participant PortalOrgRepo as PortalOrg Repository
|
||||
|
||||
%% ========================================
|
||||
%% 1. 초대 발송
|
||||
%% ========================================
|
||||
법인관리자->>Portal: 사용자 이메일 입력<br/>초대 요청
|
||||
Portal->>UserManFacade: sendInvitation(sender, emailAddr)
|
||||
|
||||
@@ -17,7 +24,7 @@ sequenceDiagram
|
||||
alt 사용자가 없거나 이미 법인 사용자
|
||||
UserManFacade-->>Portal: 에러 반환
|
||||
Portal-->>법인관리자: 초대 불가 알림
|
||||
else 일반 사용자 ROLE_USER
|
||||
else 일반 사용자 ROLE_USER 또는 비회원
|
||||
UserManFacade->>UserManFacade: generateEightCharToken()<br/>예: K7MNPQ2R
|
||||
UserManFacade->>UserInvitationRepo: save(UserInvitation)<br/>status: PENDING<br/>expiresOn: 7일 후
|
||||
UserManFacade->>MessageService: publishEvent(UserInvitationEvent)
|
||||
@@ -28,25 +35,74 @@ sequenceDiagram
|
||||
|
||||
Email->>초대받은사용자: 초대 이메일 수신
|
||||
|
||||
alt 폐쇄망 환경
|
||||
초대받은사용자->>Portal: 8글자 토큰 직접 입력<br/>/signup/decision?invitation=K7MNPQ2R
|
||||
else 이메일 링크
|
||||
초대받은사용자->>Portal: 링크 클릭<br/>/signup/decision?invitation=BASE64TOKEN
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 2-A. 로그인 상태에서 이메일 링크 클릭 (바로 수락 페이지)
|
||||
%% ========================================
|
||||
alt 로그인 상태에서 이메일 링크 클릭
|
||||
초대받은사용자->>Portal: 이메일 링크 클릭<br/>/signup/decision?invitation=TOKEN
|
||||
Portal->>UserRegisterController: showDecisionPage(invitationToken)
|
||||
UserRegisterController->>UserRegisterController: decodeInvitationToken()<br/>(Base64 or 직접입력 처리)
|
||||
UserRegisterController->>UserInvitationRepo: findByToken(decodedToken)
|
||||
|
||||
alt 토큰 무효 or 상태가 PENDING 아님
|
||||
UserRegisterController-->>Portal: 에러: 유효하지 않은 초대
|
||||
Portal-->>초대받은사용자: 에러 페이지
|
||||
else 토큰 유효
|
||||
UserRegisterController->>PortalUserRepo: findByEmailAddr()
|
||||
UserRegisterController-->>Portal: 약관 동의 페이지<br/>(법인명, 사용자명 표시)
|
||||
Portal-->>초대받은사용자: 수락/거절 선택 화면
|
||||
UserRegisterController-->>Portal: 약관 동의 페이지
|
||||
Portal-->>초대받은사용자: 초대 수락/거절 화면<br/>(바로 표시, 팝업 없음)
|
||||
Note over 초대받은사용자: 6. 초대 수락/거절 처리로 이동
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 2-B. 로그인 (기존 회원 또는 신규 가입 후)
|
||||
%% ========================================
|
||||
alt 기존 회원 (비로그인 상태)
|
||||
초대받은사용자->>Portal: 로그인
|
||||
else 비회원이 일반 회원가입 후
|
||||
초대받은사용자->>Portal: 회원가입 (ROLE_USER)
|
||||
초대받은사용자->>Portal: 로그인
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 3. 로그인 성공 처리 (AuthenticationSuccessHandler)
|
||||
%% ========================================
|
||||
Portal->>AuthHandler: onAuthenticationSuccess()
|
||||
AuthHandler->>AuthHandler: 이메일 인증, 휴면계정,<br/>비밀번호 만료 체크
|
||||
|
||||
AuthHandler->>UserInvitationRepo: findFirstByInvitationEmailAndStatus<br/>(loginId, PENDING)
|
||||
|
||||
alt PENDING 초대 없음
|
||||
AuthHandler->>Portal: redirect to /
|
||||
Portal-->>초대받은사용자: 메인 페이지 (팝업 없음)
|
||||
else PENDING 초대 있음 & 만료 안됨
|
||||
AuthHandler->>PortalOrgRepo: findById(invitation.orgId)
|
||||
PortalOrgRepo-->>AuthHandler: PortalOrg (기관명)
|
||||
AuthHandler->>AuthHandler: session.setAttribute<br/>("pendingInvitation", true)<br/>("pendingInvitationToken", token)<br/>("pendingInvitationOrgName", orgName)
|
||||
AuthHandler->>Portal: redirect to /
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 4. 메인 페이지 팝업 표시
|
||||
%% ========================================
|
||||
Portal->>IndexController: GET /
|
||||
IndexController->>MainPage: render index.html
|
||||
|
||||
MainPage->>MainPage: 세션에서 pendingInvitation 확인
|
||||
|
||||
alt pendingInvitation == true
|
||||
MainPage-->>초대받은사용자: 팝업 표시<br/>"[기관명]에서 초대가 도착했습니다.<br/>초대를 확인하시겠습니까?"
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 5. 사용자 팝업 선택
|
||||
%% ========================================
|
||||
alt 팝업에서 "확인" 클릭
|
||||
초대받은사용자->>Portal: /signup/decision?invitation=TOKEN
|
||||
Portal->>UserRegisterController: showDecisionPage(invitationToken)
|
||||
UserRegisterController->>UserInvitationRepo: findByToken(decodedToken)
|
||||
UserRegisterController-->>Portal: 약관 동의 페이지
|
||||
Portal-->>초대받은사용자: 초대 수락/거절 화면
|
||||
else 팝업에서 "취소" 클릭
|
||||
Note over 초대받은사용자,MainPage: 메인 페이지 유지<br/>다음 로그인 시 팝업 재표시
|
||||
end
|
||||
|
||||
%% ========================================
|
||||
%% 6. 초대 수락/거절 처리
|
||||
%% ========================================
|
||||
초대받은사용자->>Portal: 수락 or 거절 선택
|
||||
Portal->>UserRegisterController: processInvitation(action)
|
||||
UserRegisterController->>UserRegisterFacade: processInvitation(action, invitation)
|
||||
@@ -57,8 +113,10 @@ sequenceDiagram
|
||||
UserRegisterFacade->>UserInvitationRepo: save(invitation)<br/>status: COMPLETED<br/>completeDate: now()
|
||||
UserRegisterFacade-->>Portal: 수락 성공
|
||||
Portal-->>초대받은사용자: 성공 메시지<br/>로그아웃 후 재로그인 안내
|
||||
Note over 초대받은사용자: 다음 로그인 시<br/>PENDING 초대 없음 → 팝업 안 뜸
|
||||
else 거절
|
||||
UserRegisterFacade->>UserInvitationRepo: save(invitation)<br/>status: REJECTED<br/>completeDate: now()
|
||||
UserRegisterFacade-->>Portal: 거절 처리 완료
|
||||
Portal-->>초대받은사용자: 거절 완료 메시지
|
||||
Note over 초대받은사용자: 다음 로그인 시<br/>REJECTED 상태 → 팝업 안 뜸
|
||||
end
|
||||
|
||||
@@ -1,20 +1,44 @@
|
||||
stateDiagram-v2
|
||||
[*] --> 초대생성: 법인관리자가 초대 시작
|
||||
|
||||
초대생성 --> PENDING: 8글자 토큰 생성<br/>UserInvitation 저장
|
||||
초대생성 --> PENDING: 토큰 생성 및 저장
|
||||
|
||||
PENDING --> 토큰전달: 이메일 발송
|
||||
PENDING --> 이메일발송: 초대 이메일 발송
|
||||
|
||||
토큰전달 --> 사용자접근: 폐쇄망 직접입력 or<br/>이메일 링크 클릭
|
||||
state 로그인후팝업 {
|
||||
이메일발송 --> 로그인: 사용자 로그인
|
||||
로그인 --> 초대확인: AuthSuccessHandler
|
||||
초대확인 --> 세션저장: 세션에 초대 정보 저장
|
||||
세션저장 --> 팝업표시: 메인 페이지 팝업
|
||||
팝업표시 --> 팝업확인: 확인 클릭
|
||||
팝업표시 --> 팝업취소: 취소 클릭
|
||||
팝업취소 --> 로그인: 다음 로그인 시 재표시
|
||||
}
|
||||
|
||||
사용자접근 --> 약관동의: 토큰 유효성 검증 성공
|
||||
사용자접근 --> 에러: 토큰 무효
|
||||
팝업확인 --> 약관동의: 수락 페이지 이동
|
||||
|
||||
약관동의 --> COMPLETED: 사용자 수락<br/>ROLE_CORP_USER 전환<br/>PortalOrg 변경
|
||||
약관동의 --> REJECTED: 사용자 거절
|
||||
약관동의 --> COMPLETED: 수락
|
||||
약관동의 --> REJECTED: 거절
|
||||
|
||||
COMPLETED --> [*]: 초대 완료
|
||||
REJECTED --> [*]: 초대 거절
|
||||
PENDING --> CANCELED: 관리자가 초대 취소
|
||||
PENDING --> CANCELED: 관리자 취소
|
||||
PENDING --> EXPIRED: 만료일 경과
|
||||
CANCELED --> [*]: 초대 취소
|
||||
에러 --> [*]: 처리 종료
|
||||
EXPIRED --> [*]: 초대 만료
|
||||
|
||||
note right of PENDING
|
||||
PENDING 상태일 때만
|
||||
로그인 시 팝업 표시
|
||||
end note
|
||||
|
||||
note right of REJECTED
|
||||
REJECTED 상태가 되면
|
||||
더 이상 팝업 표시 안 함
|
||||
end note
|
||||
|
||||
note right of COMPLETED
|
||||
COMPLETED 상태가 되면
|
||||
더 이상 팝업 표시 안 함
|
||||
ROLE_CORP_USER로 전환됨
|
||||
end note
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class IndexController {
|
||||
|
||||
+12
-4
@@ -1,9 +1,11 @@
|
||||
package com.eactive.apim.portal.config;
|
||||
|
||||
import com.eactive.apim.portal.apps.user.repository.PortalOrgRepository;
|
||||
import com.eactive.apim.portal.apps.user.service.PortalUserLogService;
|
||||
import com.eactive.apim.portal.invitation.entity.UserInvitation;
|
||||
import com.eactive.apim.portal.invitation.entity.UserInvitationEnums.InvitationStatus;
|
||||
import com.eactive.apim.portal.invitation.repository.UserInvitationRepository;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
|
||||
import com.eactive.apim.portal.portaluser.entity.UserPasswordHistory;
|
||||
@@ -38,6 +40,7 @@ public class PortalAuthenticationSuccessHandler implements AuthenticationSuccess
|
||||
private final UserPasswordHistoryRepository passwordHistoryRepository;
|
||||
private final MessageRequestRepository messageRequestRepository;
|
||||
private final UserInvitationRepository userInvitationRepository;
|
||||
private final PortalOrgRepository portalOrgRepository;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
@@ -78,18 +81,23 @@ public class PortalAuthenticationSuccessHandler implements AuthenticationSuccess
|
||||
session.setAttribute("redirectUrl", contextPath + "/new_password");
|
||||
}
|
||||
|
||||
// 초대 코드 확인 - ROLE_USER만 확인
|
||||
// 초대 코드 확인 - ROLE_USER만 확인 (세션에 저장하여 메인 페이지에서 팝업으로 표시)
|
||||
if (user.getRoleCode() == PortalUserEnums.RoleCode.ROLE_USER) {
|
||||
Optional<UserInvitation> pendingInvitation =
|
||||
userInvitationRepository.findFirstByInvitationEmailAndStatus(
|
||||
user.getLoginId(), InvitationStatus.PENDING);
|
||||
|
||||
if (pendingInvitation.isPresent()) {
|
||||
// 만료되지 않은 초대가 있으면 자동으로 초대 수락 페이지로 이동
|
||||
UserInvitation invitation = pendingInvitation.get();
|
||||
if (invitation.getExpiresOn().isAfter(LocalDateTime.now())) {
|
||||
response.sendRedirect(contextPath + "/signup/decision?invitation=" + invitation.getToken());
|
||||
return;
|
||||
// 세션에 초대 정보 저장 (메인 페이지에서 팝업으로 표시)
|
||||
session.setAttribute("pendingInvitation", true);
|
||||
session.setAttribute("pendingInvitationToken", invitation.getToken());
|
||||
// orgId로 기관명 조회
|
||||
String orgName = portalOrgRepository.findById(invitation.getOrgId())
|
||||
.map(PortalOrg::getOrgName)
|
||||
.orElse("알 수 없는 기관");
|
||||
session.setAttribute("pendingInvitationOrgName", orgName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +70,6 @@
|
||||
<img th:src="@{/img/icon/icon_login_join.png}" alt="">
|
||||
<a th:href="@{/signup}">회원가입</a>
|
||||
</li>
|
||||
<li>
|
||||
<span>|</span>
|
||||
</li>
|
||||
<li class="icon_join">
|
||||
<img th:src="@{/img/icon/icon_login_join.png}" alt="">
|
||||
<a th:href="@{/signup/invitation-code}">초대를 받으셨나요?</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="login_list m-only">
|
||||
|
||||
@@ -299,6 +299,20 @@
|
||||
customPopups.hideAlert('#customAlert');
|
||||
window.location.href = redirectUrl;
|
||||
});
|
||||
return; // 다른 체크 중단
|
||||
}
|
||||
|
||||
// 초대 대기 확인 (로그인할 때마다 표시)
|
||||
const pendingInvitation = [[${session.pendingInvitation}]];
|
||||
if (pendingInvitation) {
|
||||
const invitationToken = /*[[${session.pendingInvitationToken}]]*/ '';
|
||||
const orgName = /*[[${session.pendingInvitationOrgName}]]*/ '';
|
||||
|
||||
customPopups.showConfirm(`<strong>${orgName}</strong>에서 초대가 도착했습니다.<br>초대를 확인하시겠습니까?`, function (selection) {
|
||||
if (selection) {
|
||||
window.location.href = '/signup/decision?invitation=' + invitationToken;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user