Compare commits
16 Commits
1b8f7a8ef1
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cdb08a26f | |||
| a1be6d2b9c | |||
| 3c9c757962 | |||
| 4749a570e7 | |||
| a4976fdfa5 | |||
| d86b169a6c | |||
| 665aa5d44c | |||
| 61086f419f | |||
| b14b139abd | |||
| f9ad183d03 | |||
| dc9c4d5977 | |||
| ae35b2e75b | |||
| 975533a790 | |||
| b92639bc04 | |||
| ebdeab8204 | |||
| 99dac967bc |
+146
@@ -0,0 +1,146 @@
|
|||||||
|
package com.eactive.apim.portal.agreements.service;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||||
|
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약관 종류 운영 설정 조회. PTL_PROPERTY(그룹 {@code Portal})로 관리하며 admin·portal 이 같은 값을 본다.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <caption>PTL_PROPERTY (group = Portal)</caption>
|
||||||
|
* <tr><th>{@code portal.terms.enabled-types}</th>
|
||||||
|
* <td>시스템에서 사용할 약관 종류. 쉼표 구분, 순서 무의미</td></tr>
|
||||||
|
* <tr><th>{@code portal.terms.display-types}</th>
|
||||||
|
* <td>약관 페이지에 노출할 약관 종류. 쉼표 구분, <b>기재 순서가 노출 순서</b></td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* <p>값은 관리 콘솔의 '약관 목록 > 약관 종류 관리' 화면에서 설정한다. 프로퍼티가 없으면
|
||||||
|
* {@link #DEFAULT_TYPES} 를 사용하며 <b>조회만으로 행을 만들지는 않는다</b>
|
||||||
|
* (PTL_PROPERTY 는 (그룹, 이름) 유니크 제약이 없어 동시 요청이 중복 행을 만들 수 있다).
|
||||||
|
* 알 수 없는 코드는 무시하므로 enum 항목이 빠져도 화면이 깨지지 않는다.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgreementTypeConfigService {
|
||||||
|
|
||||||
|
public static final String PROPERTY_GROUP = "Portal";
|
||||||
|
public static final String ENABLED_TYPES_KEY = "portal.terms.enabled-types";
|
||||||
|
public static final String DISPLAY_TYPES_KEY = "portal.terms.display-types";
|
||||||
|
public static final String AGREE_ALL_ENABLED_KEY = "portal.terms.agree-all-enabled";
|
||||||
|
|
||||||
|
/** '전체 동의' 기본값 — 미설정 시 항목별로 끝까지 읽고 개별 동의하는 방식 */
|
||||||
|
public static final boolean DEFAULT_AGREE_ALL_ENABLED = false;
|
||||||
|
|
||||||
|
/** 프로퍼티 미설정 시 기본값 — 현재 운영중인 약관 종류 */
|
||||||
|
public static final List<AgreementType> DEFAULT_TYPES = Collections.unmodifiableList(Arrays.asList(
|
||||||
|
AgreementType.TERMS_OF_USE,
|
||||||
|
AgreementType.PRIVACY_COLLECT,
|
||||||
|
AgreementType.NOTIFICATION_CONSENT));
|
||||||
|
|
||||||
|
private final PortalPropertyService portalPropertyService;
|
||||||
|
|
||||||
|
/** 시스템에서 사용하는 약관 종류 */
|
||||||
|
public Set<AgreementType> getEnabledTypes() {
|
||||||
|
Set<AgreementType> enabled = EnumSet.noneOf(AgreementType.class);
|
||||||
|
enabled.addAll(readTypes(readGroupProperties(), ENABLED_TYPES_KEY));
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 약관 페이지에 노출할 약관 종류(노출 순서대로). 사용하지 않는 종류는 제외된다. */
|
||||||
|
public List<AgreementType> getDisplayTypes() {
|
||||||
|
Map<String, String> properties = readGroupProperties();
|
||||||
|
List<AgreementType> enabled = readTypes(properties, ENABLED_TYPES_KEY);
|
||||||
|
|
||||||
|
List<AgreementType> display = new ArrayList<>();
|
||||||
|
for (AgreementType type : readTypes(properties, DISPLAY_TYPES_KEY)) {
|
||||||
|
if (enabled.contains(type)) {
|
||||||
|
display.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 시스템에서 사용하는 약관 종류인지 여부 */
|
||||||
|
public boolean isEnabled(AgreementType type) {
|
||||||
|
return getEnabledTypes().contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 약관 페이지에 노출(배치)된 약관 종류인지 여부. 사용하지 않는 종류는 항상 {@code false}. */
|
||||||
|
public boolean isDisplayed(AgreementType type) {
|
||||||
|
return getDisplayTypes().contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회원가입 약관 동의 화면에서 '전체 동의'를 허용할지 여부.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code false} — 항목을 펼쳐 끝까지 읽어야 개별 동의할 수 있고, '전체 동의' 체크박스는 노출하지 않는다</li>
|
||||||
|
* <li>{@code true} — '전체 동의' 체크박스를 노출하고, 항목별 스크롤 없이 바로 동의할 수 있다</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public boolean isAgreeAllEnabled() {
|
||||||
|
return parseBoolean(readGroupProperties().get(AGREE_ALL_ENABLED_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@code true/false} 외에 레거시 {@code Y/N}, {@code 1/0} 표기도 허용한다. */
|
||||||
|
private boolean parseBoolean(String value) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return DEFAULT_AGREE_ALL_ENABLED;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
if ("true".equalsIgnoreCase(trimmed) || "Y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ("false".equalsIgnoreCase(trimmed) || "N".equalsIgnoreCase(trimmed) || "0".equals(trimmed)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.warn("PTL_PROPERTY {}/{} 값을 해석할 수 없어 기본값({})을 사용합니다. : {}",
|
||||||
|
PROPERTY_GROUP, AGREE_ALL_ENABLED_KEY, DEFAULT_AGREE_ALL_ENABLED, trimmed);
|
||||||
|
return DEFAULT_AGREE_ALL_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Portal} 그룹 프로퍼티 전체를 읽는다.
|
||||||
|
*
|
||||||
|
* <p>단건 조회({@code findById})는 중복 행에서 예외가 나므로 그룹 단위로 읽는다.
|
||||||
|
*/
|
||||||
|
private Map<String, String> readGroupProperties() {
|
||||||
|
return portalPropertyService.getPortalPropertiesAsMap(PROPERTY_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 프로퍼티 값을 약관 종류 목록으로 변환. 미설정이면 기본값, 알 수 없는 코드는 무시한다. */
|
||||||
|
private List<AgreementType> readTypes(Map<String, String> properties, String propertyName) {
|
||||||
|
String value = properties.get(propertyName);
|
||||||
|
if (value == null) {
|
||||||
|
return new ArrayList<>(DEFAULT_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AgreementType> types = new ArrayList<>();
|
||||||
|
for (String code : value.split(",")) {
|
||||||
|
String trimmed = code.trim();
|
||||||
|
if (trimmed.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
types.add(AgreementType.valueOf(trimmed));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.warn("PTL_PROPERTY {}/{} 에 알 수 없는 약관 종류 코드가 있어 무시합니다. : {}",
|
||||||
|
PROPERTY_GROUP, propertyName, trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(new LinkedHashSet<>(types));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import org.springframework.data.domain.Page;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@EMSDataSource
|
@EMSDataSource
|
||||||
public interface CredentialRepository extends BaseRepository<Credential, String> {
|
public interface CredentialRepository extends BaseRepository<Credential, String> {
|
||||||
@@ -19,6 +20,9 @@ public interface CredentialRepository extends BaseRepository<Credential, String>
|
|||||||
|
|
||||||
List<Credential> findAllByOrgid(String orgid);
|
List<Credential> findAllByOrgid(String orgid);
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
long deleteByOrgid(String orgid);
|
||||||
|
|
||||||
Optional<Credential> findByClientidAndOrgid(String clientid, String orgid);
|
Optional<Credential> findByClientidAndOrgid(String clientid, String orgid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public interface AppRequestRepository extends BaseRepository<AppRequest, String>
|
|||||||
|
|
||||||
Optional<AppRequest> findByIdAndOrgAndTypeIsIn(String id, PortalOrg org, List<AppRequestType> types);
|
Optional<AppRequest> findByIdAndOrgAndTypeIsIn(String id, PortalOrg org, List<AppRequestType> types);
|
||||||
|
|
||||||
|
/** 테스트 재실행 정리처럼 법인 내 동일 클라이언트명의 신청을 좁게 조회한다. */
|
||||||
|
List<AppRequest> findAllByOrgAndClientName(PortalOrg org, String clientName);
|
||||||
|
|
||||||
List<AppRequest> findAllByClientIdsContainsAndTypeIsIn(String clientId, List<AppRequestType> types);
|
List<AppRequest> findAllByClientIdsContainsAndTypeIsIn(String clientId, List<AppRequestType> types);
|
||||||
|
|
||||||
List<AppRequest> findAllByOrgAndStatus(PortalOrg org, ApprovalStatus status);
|
List<AppRequest> findAllByOrgAndStatus(PortalOrg org, ApprovalStatus status);
|
||||||
|
|||||||
+22
-10
@@ -95,16 +95,15 @@ public class InternalApiTokenService {
|
|||||||
*/
|
*/
|
||||||
public boolean matches(String presented) {
|
public boolean matches(String presented) {
|
||||||
String expected = ensureToken();
|
String expected = ensureToken();
|
||||||
if (expected == null) {
|
return tokenMatches(presented, expected);
|
||||||
log.error("내부 API 토큰({}/{})을 확보하지 못해 요청을 거부합니다.", PROP_GROUP, PROP_TOKEN);
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
if (isBlank(presented)) {
|
* admin 등 토큰을 생성하면 안 되는 애플리케이션용 검증.
|
||||||
return false;
|
* 프로퍼티가 없으면 생성하지 않고 거부한다.
|
||||||
}
|
*/
|
||||||
return MessageDigest.isEqual(
|
public boolean matchesReadOnly(String presented) {
|
||||||
presented.trim().getBytes(StandardCharsets.UTF_8),
|
return tokenMatches(presented, findToken());
|
||||||
expected.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 기동 시 1회 호출 — 헤더명/토큰 프로퍼티를 미리 만들어 둔다. (포탈 전용) */
|
/** 기동 시 1회 호출 — 헤더명/토큰 프로퍼티를 미리 만들어 둔다. (포탈 전용) */
|
||||||
@@ -138,6 +137,19 @@ public class InternalApiTokenService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean tokenMatches(String presented, String expected) {
|
||||||
|
if (expected == null) {
|
||||||
|
log.error("내부 API 토큰({}/{})을 확보하지 못해 요청을 거부합니다.", PROP_GROUP, PROP_TOKEN);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isBlank(presented)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MessageDigest.isEqual(
|
||||||
|
presented.trim().getBytes(StandardCharsets.UTF_8),
|
||||||
|
expected.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
/** JVM 당 1회만 생성되는 난수 토큰 (DB 에 값이 없을 때의 기본값). */
|
/** JVM 당 1회만 생성되는 난수 토큰 (DB 에 값이 없을 때의 기본값). */
|
||||||
private String localToken() {
|
private String localToken() {
|
||||||
String token = generatedToken.get();
|
String token = generatedToken.get();
|
||||||
|
|||||||
+4
@@ -17,6 +17,10 @@ public interface UserInvitationRepository extends JpaRepository<UserInvitation,
|
|||||||
|
|
||||||
List<UserInvitation> findByStatus(InvitationStatus status);
|
List<UserInvitation> findByStatus(InvitationStatus status);
|
||||||
|
|
||||||
|
long deleteByOrgId(String orgId);
|
||||||
|
|
||||||
|
long deleteByInvitationMobile(String invitationMobile);
|
||||||
|
|
||||||
Optional<UserInvitation> findFirstByInvitationEmailAndStatus(String email, InvitationStatus status);
|
Optional<UserInvitation> findFirstByInvitationEmailAndStatus(String email, InvitationStatus status);
|
||||||
|
|
||||||
Optional<UserInvitation> findFirstByInvitationEmailAndOrgIdAndStatus(String email, String orgId, InvitationStatus status);
|
Optional<UserInvitation> findFirstByInvitationEmailAndOrgIdAndStatus(String email, String orgId, InvitationStatus status);
|
||||||
|
|||||||
+1
@@ -30,6 +30,7 @@ public class PortalPropertyService extends AbstractDataService<PortalPropertyGro
|
|||||||
.orElse(new PortalPropertyGroup());
|
.orElse(new PortalPropertyGroup());
|
||||||
|
|
||||||
return portalPropertyGroup.getPortalProperties().stream()
|
return portalPropertyGroup.getPortalProperties().stream()
|
||||||
|
.filter(portalProperty -> portalProperty.getPropertyValue() != null)
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
portalProperty -> portalProperty.getId().getPropertyName(),
|
portalProperty -> portalProperty.getId().getPropertyName(),
|
||||||
PortalProperty::getPropertyValue,
|
PortalProperty::getPropertyValue,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.eactive.apim.portal.portaluser.entity;
|
package com.eactive.apim.portal.portaluser.entity;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
|
||||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.hibernate.annotations.Comment;
|
import org.hibernate.annotations.Comment;
|
||||||
@@ -40,6 +41,7 @@ public class UserRoleHistory implements Serializable, com.eactive.eai.data.Data
|
|||||||
@Column(name = "id", columnDefinition = "number(19)")
|
@Column(name = "id", columnDefinition = "number(19)")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Convert(converter = PersonalDataEncryptConverter.class)
|
||||||
@Column(name = "user_id", length = 255, nullable = false)
|
@Column(name = "user_id", length = 255, nullable = false)
|
||||||
@Comment("대상 사용자 ID(loginId)")
|
@Comment("대상 사용자 ID(loginId)")
|
||||||
private String userId;
|
private String userId;
|
||||||
@@ -56,6 +58,7 @@ public class UserRoleHistory implements Serializable, com.eactive.eai.data.Data
|
|||||||
@Comment("변경 유형(MANAGER_ASSIGN/MANAGER_REVOKE/ORG_REMOVE 등)")
|
@Comment("변경 유형(MANAGER_ASSIGN/MANAGER_REVOKE/ORG_REMOVE 등)")
|
||||||
private String changeType;
|
private String changeType;
|
||||||
|
|
||||||
|
@Convert(converter = PersonalDataEncryptConverter.class)
|
||||||
@Column(name = "changed_by", length = 255, nullable = false)
|
@Column(name = "changed_by", length = 255, nullable = false)
|
||||||
@Comment("변경 수행자(loginId)")
|
@Comment("변경 수행자(loginId)")
|
||||||
private String changedBy;
|
private String changedBy;
|
||||||
@@ -65,6 +68,7 @@ public class UserRoleHistory implements Serializable, com.eactive.eai.data.Data
|
|||||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||||
private LocalDateTime changeDate;
|
private LocalDateTime changeDate;
|
||||||
|
|
||||||
|
@Convert(converter = PersonalDataEncryptConverter.class)
|
||||||
@Column(name = "created_by", length = 255, nullable = false)
|
@Column(name = "created_by", length = 255, nullable = false)
|
||||||
@Comment("생성자")
|
@Comment("생성자")
|
||||||
private String createdBy;
|
private String createdBy;
|
||||||
|
|||||||
+5
@@ -3,6 +3,7 @@ package com.eactive.apim.portal.portaluser.repository;
|
|||||||
import com.eactive.apim.portal.portaluser.entity.PortalUserPrivacyAgreement;
|
import com.eactive.apim.portal.portaluser.entity.PortalUserPrivacyAgreement;
|
||||||
import com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
import com.eactive.eai.rms.data.EMSDataSource;
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -15,4 +16,8 @@ public interface PortalUserPrivacyAgreementRepository extends BaseRepository<Por
|
|||||||
List<PortalUserPrivacyAgreement> findAllByCreatedBy(String createdBy);
|
List<PortalUserPrivacyAgreement> findAllByCreatedBy(String createdBy);
|
||||||
|
|
||||||
// boolean existsByCreatedByAndTermsTypeAndTermsVersionGreaterThanEqual(String userId, String termsType, int latestVersion);
|
// boolean existsByCreatedByAndTermsTypeAndTermsVersionGreaterThanEqual(String userId, String termsType, int latestVersion);
|
||||||
|
|
||||||
|
/** createdBy = PortalUser.id. */
|
||||||
|
@Transactional
|
||||||
|
long deleteByCreatedBy(String createdBy);
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,8 @@ public interface PortalUserRepository extends BaseRepository<PortalUser, String>
|
|||||||
|
|
||||||
long countByPortalOrg(PortalOrg portalOrg);
|
long countByPortalOrg(PortalOrg portalOrg);
|
||||||
|
|
||||||
|
List<PortalUser> findAllByPortalOrg_Id(String orgId);
|
||||||
|
|
||||||
Optional<PortalUser> findByUserName(String userName);
|
Optional<PortalUser> findByUserName(String userName);
|
||||||
|
|
||||||
// 암복
|
// 암복
|
||||||
|
|||||||
+5
@@ -5,6 +5,7 @@ import com.eactive.eai.rms.data.EMSDataSource;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -15,4 +16,8 @@ public interface UserPasswordHistoryRepository extends JpaRepository<UserPasswor
|
|||||||
List<UserPasswordHistory> findRecentPasswordsByUserId(@Param("loginId") String loginId);
|
List<UserPasswordHistory> findRecentPasswordsByUserId(@Param("loginId") String loginId);
|
||||||
|
|
||||||
Optional<UserPasswordHistory> findTopByUserIdOrderByChangeDateDesc(String loginId);
|
Optional<UserPasswordHistory> findTopByUserIdOrderByChangeDateDesc(String loginId);
|
||||||
|
|
||||||
|
/** userId = PortalUser.id (loginId 아님 — UserRoleHistory.userId 와 반대 시맨틱). */
|
||||||
|
@Transactional
|
||||||
|
long deleteByUserId(String userId);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -3,6 +3,7 @@ package com.eactive.apim.portal.portaluser.repository;
|
|||||||
import com.eactive.apim.portal.portaluser.entity.UserRoleHistory;
|
import com.eactive.apim.portal.portaluser.entity.UserRoleHistory;
|
||||||
import com.eactive.eai.rms.data.EMSDataSource;
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -10,4 +11,8 @@ import java.util.List;
|
|||||||
public interface UserRoleHistoryRepository extends JpaRepository<UserRoleHistory, Long> {
|
public interface UserRoleHistoryRepository extends JpaRepository<UserRoleHistory, Long> {
|
||||||
|
|
||||||
List<UserRoleHistory> findByUserIdOrderByChangeDateDesc(String userId);
|
List<UserRoleHistory> findByUserIdOrderByChangeDateDesc(String userId);
|
||||||
|
|
||||||
|
/** userId = PortalUser.loginId (id 아님 — UserPasswordHistory.userId 와 반대 시맨틱). */
|
||||||
|
@Transactional
|
||||||
|
long deleteByUserId(String userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,23 +11,22 @@ public enum MessageCode {
|
|||||||
|
|
||||||
// 사용자 이벤트 영역
|
// 사용자 이벤트 영역
|
||||||
USER_VERIFICATION_EMAIL("이메일 주소 인증", null, Channels.EMAIL),
|
USER_VERIFICATION_EMAIL("이메일 주소 인증", null, Channels.EMAIL),
|
||||||
USER_VERIFICATION_MOBILEPHONE("휴대폰 번호 인증", null, Channels.SMS),
|
USER_VERIFICATION_MOBILEPHONE("휴대폰 번호 인증", "JJB_85959019001", Channels.SMS),
|
||||||
|
|
||||||
USER_PASSWORD_RESET("비밀번호 재설정", null, Channels.SMS),
|
USER_PASSWORD_CHANGED("사용자 비밀번호 변경", "JJB_85959019003", Channels.SMS),
|
||||||
USER_PASSWORD_CHANGED("사용자 비밀번호 변경", null, Channels.SMS),
|
|
||||||
|
|
||||||
USER_ACCOUNT_LOCKED("계정 잠금 알림", null, Channels.SMS),
|
USER_ACCOUNT_LOCKED("계정 잠금 알림", "JJB_85959019002", Channels.SMS),
|
||||||
|
|
||||||
// 법인관리자 이벤트 영역
|
// 법인관리자 이벤트 영역
|
||||||
USER_INVITATION("법인 이용자 이메일 초대", null, Channels.SMS),
|
USER_INVITATION("법인 이용자 이메일 초대", "JJB_85959019006", Channels.SMS),
|
||||||
USER_INVITATION_CANCELED("법인 이용자 이메일 초대 취소", null, Channels.SMS),
|
USER_INVITATION_CANCELED("법인 이용자 이메일 초대 취소", "JJB_85959019007", Channels.SMS),
|
||||||
|
|
||||||
MANAGER_WITH_ORG_REGISTER_APPROVED("법인관리자 및 법인 등록 승인", null, Channels.SMS),
|
MANAGER_WITH_ORG_REGISTER_APPROVED("법인관리자 및 법인 등록 승인", "JJB_85959019004", Channels.SMS),
|
||||||
MANAGER_WITH_ORG_REGISTER_REJECTED("법인관리자 및 법인 등록 거절", null, Channels.SMS),
|
MANAGER_WITH_ORG_REGISTER_REJECTED("법인관리자 및 법인 등록 거절", "JJB_85959019005", Channels.SMS),
|
||||||
|
|
||||||
// 관리자 영역
|
// 관리자 영역
|
||||||
APP_APPROVE("앱 사용 승인", null, Channels.SMS),
|
APP_APPROVE("앱 사용 승인", "JJB_85959019008", Channels.SMS),
|
||||||
APP_REJECTED("앱 사용 거절", null, Channels.SMS),
|
APP_REJECTED("앱 사용 거절", "JJB_85959019009", Channels.SMS),
|
||||||
|
|
||||||
// Q&A 등록/댓글 등록 알림 (DJBank 커스텀 — portal-admin 수신)
|
// Q&A 등록/댓글 등록 알림 (DJBank 커스텀 — portal-admin 수신)
|
||||||
INQUIRY_CREATED("Q&A 등록 알림", null, Channels.SWING),
|
INQUIRY_CREATED("Q&A 등록 알림", null, Channels.SWING),
|
||||||
@@ -37,9 +36,10 @@ public enum MessageCode {
|
|||||||
// 제주은행
|
// 제주은행
|
||||||
API_STATUS_CHANGED("API 상태 변화", null, Channels.SWING),
|
API_STATUS_CHANGED("API 상태 변화", null, Channels.SWING),
|
||||||
INFLOW_TOKEN_FAILED("유량제어 토큰 획득 실패", null, Channels.SWING),
|
INFLOW_TOKEN_FAILED("유량제어 토큰 획득 실패", null, Channels.SWING),
|
||||||
ADMIN_VERIFICATION_MOBILEPHONE("관리자포탈 휴대폰 번호 인증", null, Channels.SMS),
|
ADMIN_VERIFICATION_MOBILEPHONE("관리자포탈 휴대폰 번호 인증", "JJB_85999019001", Channels.SMS),
|
||||||
|
|
||||||
// 레거시/미사용 — Event 핸들러 클래스 호환을 위해 유지 (실제 발송 없음)
|
// 레거시/미사용 — Event 핸들러 클래스 호환을 위해 유지 (실제 발송 없음)
|
||||||
|
USER_PASSWORD_RESET("비밀번호 재설정", null, Channels.SMS),
|
||||||
API_APPROVED("API 승인 알림", null, Channels.NONE),
|
API_APPROVED("API 승인 알림", null, Channels.NONE),
|
||||||
APP_REGISTER_REQUEST("앱 등록 요청", null, Channels.NONE),
|
APP_REGISTER_REQUEST("앱 등록 요청", null, Channels.NONE),
|
||||||
APP_REGISTER_REJECTED("앱 등록 거절", null, Channels.NONE),
|
APP_REGISTER_REJECTED("앱 등록 거절", null, Channels.NONE),
|
||||||
|
|||||||
@@ -79,9 +79,16 @@ public class MessageRequest implements Serializable {
|
|||||||
@Column(name = "request_status")
|
@Column(name = "request_status")
|
||||||
private String requestStatus = "PENDING"; // PENDING, SENT, FAILED
|
private String requestStatus = "PENDING"; // PENDING, SENT, FAILED
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(name = "response_data")
|
||||||
|
private String responseData;
|
||||||
|
|
||||||
@Column(name = "eai_interface_id")
|
@Column(name = "eai_interface_id")
|
||||||
private String eaiInterfaceId;
|
private String eaiInterfaceId;
|
||||||
|
|
||||||
|
@Column(name = "eai_tx_id")
|
||||||
|
private String eaiTxId;
|
||||||
|
|
||||||
@Column(name = "service_id")
|
@Column(name = "service_id")
|
||||||
private String serviceId;
|
private String serviceId;
|
||||||
|
|
||||||
|
|||||||
@@ -37,22 +37,24 @@ public class MessageSendService {
|
|||||||
private final PortalPropertyService portalPropertyService;
|
private final PortalPropertyService portalPropertyService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void sendMessage(MessageCode messageCode, MessageRecipient recipient, Map<String, String> messageParams) {
|
public List<MessageRequest> sendMessage(MessageCode messageCode, MessageRecipient recipient, Map<String, String> messageParams) {
|
||||||
processMessage(messageCode.name(), recipient, messageParams);
|
return processMessage(messageCode, recipient, messageParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMessage(String messageCode, MessageRecipient recipient, Map<String, String> messageParams) {
|
private List<MessageRequest> processMessage(MessageCode messageCode, MessageRecipient recipient, Map<String, String> messageParams) {
|
||||||
|
|
||||||
Optional<MessageTemplate> template = messageTemplateRepository.findById(messageCode.toUpperCase());
|
Optional<MessageTemplate> template = messageTemplateRepository.findById(messageCode.name().toUpperCase());
|
||||||
|
|
||||||
|
List<MessageRequest> created = new ArrayList<>();
|
||||||
if (template.isPresent()) {
|
if (template.isPresent()) {
|
||||||
List<MessageRecipient> recipients = prepareRecipients(template.get(), recipient);
|
List<MessageRecipient> recipients = prepareRecipients(template.get(), recipient);
|
||||||
for (MessageRecipient user : recipients) {
|
for (MessageRecipient user : recipients) {
|
||||||
processSingleRecipient(user, template.get(), messageParams);
|
created.addAll(processSingleRecipient(user, template.get(), messageParams, messageCode));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("메세지 템플릿이 존재 하지 않음 - {}", messageCode);
|
log.warn("메세지 템플릿이 존재 하지 않음 - {}", messageCode.name());
|
||||||
}
|
}
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MessageRecipient> prepareRecipients(MessageTemplate template, MessageRecipient recipient) {
|
private List<MessageRecipient> prepareRecipients(MessageTemplate template, MessageRecipient recipient) {
|
||||||
@@ -70,15 +72,18 @@ public class MessageSendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void processSingleRecipient(MessageRecipient user, MessageTemplate template, Map<String, String> messageParams) {
|
public List<MessageRequest> processSingleRecipient(MessageRecipient user, MessageTemplate template, Map<String, String> messageParams, MessageCode messageCode) {
|
||||||
Map<String, String> portalProperties = portalPropertyService.getPortalPropertiesAsMap("Portal");
|
Map<String, String> portalProperties = portalPropertyService.getPortalPropertiesAsMap("Portal");
|
||||||
|
|
||||||
updateMessageParams(user, messageParams);
|
updateMessageParams(user, messageParams);
|
||||||
|
|
||||||
String subject = buildMessage(template.getSubjectTemplate(), messageParams);
|
String subject = buildMessage(template.getSubjectTemplate(), messageParams);
|
||||||
|
|
||||||
|
List<MessageRequest> created = new ArrayList<>();
|
||||||
|
|
||||||
if (template.getEnableSms().equalsIgnoreCase("Y")) {
|
if (template.getEnableSms().equalsIgnoreCase("Y")) {
|
||||||
String smsEaiInterfaceId = portalProperties.get("djb.ums.sms.if_id");
|
String smsEaiInterfaceId = portalProperties.get("djb.ums.sms.if_id");
|
||||||
|
String smsEaiTxId = portalProperties.get("djb.ums.sms.tx_id");
|
||||||
MessageRequest smsRequest = new MessageRequest();
|
MessageRequest smsRequest = new MessageRequest();
|
||||||
smsRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode().toUpperCase()));
|
smsRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode().toUpperCase()));
|
||||||
smsRequest.setSubject(subject);
|
smsRequest.setSubject(subject);
|
||||||
@@ -88,15 +93,17 @@ public class MessageSendService {
|
|||||||
smsRequest.setUsername(user.getUsername());
|
smsRequest.setUsername(user.getUsername());
|
||||||
smsRequest.setPhone(user.getPhone());
|
smsRequest.setPhone(user.getPhone());
|
||||||
smsRequest.setEaiInterfaceId(smsEaiInterfaceId);
|
smsRequest.setEaiInterfaceId(smsEaiInterfaceId);
|
||||||
smsRequest.setServiceId(portalProperties.get("djb.ums.sms.tx_id"));
|
smsRequest.setEaiTxId(smsEaiTxId);
|
||||||
|
smsRequest.setServiceId(messageCode.getServiceId());
|
||||||
smsRequest.setUserId(user.getUserId());
|
smsRequest.setUserId(user.getUserId());
|
||||||
smsRequest.setMessageType("SMS");
|
smsRequest.setMessageType("SMS");
|
||||||
messageRequestRepository.save(smsRequest);
|
created.add(messageRequestRepository.save(smsRequest));
|
||||||
logger.debug(smsRequest.toString());
|
logger.debug(smsRequest.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.getEnableEmail().equalsIgnoreCase("Y")) {
|
if (template.getEnableEmail().equalsIgnoreCase("Y")) {
|
||||||
String mailEaiInterfaceId = portalProperties.get("djb.ums.email.if_id");
|
String mailEaiInterfaceId = portalProperties.get("djb.ums.email.if_id");
|
||||||
|
String mailEaiTxId = portalProperties.get("djb.ums.email.tx_id");
|
||||||
MessageRequest emailRequest = new MessageRequest();
|
MessageRequest emailRequest = new MessageRequest();
|
||||||
emailRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode().toUpperCase()));
|
emailRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode().toUpperCase()));
|
||||||
emailRequest.setSubject(subject);
|
emailRequest.setSubject(subject);
|
||||||
@@ -106,15 +113,17 @@ public class MessageSendService {
|
|||||||
emailRequest.setUsername(resolveEmailUsername(user));
|
emailRequest.setUsername(resolveEmailUsername(user));
|
||||||
emailRequest.setEmail(user.getUserId());
|
emailRequest.setEmail(user.getUserId());
|
||||||
emailRequest.setEaiInterfaceId(mailEaiInterfaceId);
|
emailRequest.setEaiInterfaceId(mailEaiInterfaceId);
|
||||||
emailRequest.setServiceId(portalProperties.get("djb.ums.email.tx_id"));
|
emailRequest.setEaiTxId(mailEaiTxId);
|
||||||
|
emailRequest.setServiceId(messageCode.getServiceId());
|
||||||
emailRequest.setMessageType("EMAIL");
|
emailRequest.setMessageType("EMAIL");
|
||||||
emailRequest.setUserId(user.getUserId());
|
emailRequest.setUserId(user.getUserId());
|
||||||
messageRequestRepository.save(emailRequest);
|
created.add(messageRequestRepository.save(emailRequest));
|
||||||
logger.debug(emailRequest.toString());
|
logger.debug(emailRequest.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.getEnableMessenger().equalsIgnoreCase("Y")) {
|
if (template.getEnableMessenger().equalsIgnoreCase("Y")) {
|
||||||
String swingEaiInterfaceId = portalProperties.get("djb.ums.messenger.if_id");
|
String swingEaiInterfaceId = portalProperties.get("djb.ums.messenger.if_id");
|
||||||
|
String swingEaiTxId = portalProperties.get("djb.ums.messenger.tx_id");
|
||||||
MessageRequest messengerRequest = new MessageRequest();
|
MessageRequest messengerRequest = new MessageRequest();
|
||||||
messengerRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode()));
|
messengerRequest.setMessageCode(MessageCode.valueOf(template.getMessageCode()));
|
||||||
messengerRequest.setSubject(subject);
|
messengerRequest.setSubject(subject);
|
||||||
@@ -122,14 +131,17 @@ public class MessageSendService {
|
|||||||
messengerRequest.setMessage(messengerTemplate);
|
messengerRequest.setMessage(messengerTemplate);
|
||||||
messengerRequest.setRequestDate(LocalDateTime.now());
|
messengerRequest.setRequestDate(LocalDateTime.now());
|
||||||
messengerRequest.setUsername(user.getUsername());
|
messengerRequest.setUsername(user.getUsername());
|
||||||
messengerRequest.setMessengerId(user.getUserId());
|
messengerRequest.setMessengerId(user.getMessengerId());
|
||||||
messengerRequest.setEaiInterfaceId(swingEaiInterfaceId);
|
messengerRequest.setEaiInterfaceId(swingEaiInterfaceId);
|
||||||
messengerRequest.setServiceId(portalProperties.get("djb.ums.messenger.tx_id"));
|
messengerRequest.setEaiTxId(swingEaiTxId);
|
||||||
|
messengerRequest.setServiceId(messageCode.getServiceId());
|
||||||
messengerRequest.setMessageType("MESSENGER");
|
messengerRequest.setMessageType("MESSENGER");
|
||||||
messengerRequest.setUserId(user.getUserId());
|
messengerRequest.setUserId(user.getUserId());
|
||||||
messageRequestRepository.save(messengerRequest);
|
created.add(messageRequestRepository.save(messengerRequest));
|
||||||
logger.debug(messengerRequest.toString());
|
logger.debug(messengerRequest.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import com.eactive.apim.portal.user.entity.UserLog;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
public interface UserLogRepository extends JpaRepository<UserLog, Long> {
|
public interface UserLogRepository extends JpaRepository<UserLog, Long> {
|
||||||
|
|
||||||
Optional<UserLog> findFirstByLoginIdAndSuccessOrderByLoginTimeDesc(@Param("loginId") String loginId,@Param("success") boolean success);
|
Optional<UserLog> findFirstByLoginIdAndSuccessOrderByLoginTimeDesc(@Param("loginId") String loginId,@Param("success") boolean success);
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
long deleteByLoginId(String loginId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user