7 Commits

Author SHA1 Message Date
Rinjae ca44e8a967 - PortalProperty 설명 자동 채우기 로직 추가: 비어있을 때만 적용
- isBlank 유틸 메서드 추가
2026-07-03 16:55:05 +09:00
Rinjae ce3efa299e - StartupInfoPrinter 클래스 추가 - 서비스 시작 시 배너 출력
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
- MessageCode 채널 필드 @Deprecated 추가 및 기본값 설정
- MessageCode 신규 생성자 추가 - channel 파라미터 제거
2026-07-01 10:37:08 +09:00
eastargh ad2ecb0b82 Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/elink-portal-common.git
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
2026-06-30 15:50:50 +09:00
eastargh f7bff90844 UMS발송시 user_id 추가 2026-06-30 15:50:25 +09:00
Rinjae 658958c9ef - 법인 탈퇴 시 민감정보 삭제 로직 추가
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
- CSS 수정: 폰트 크기 제거 및 스타일 변경 적용
- 예상완료일 요일 계산 로직 추가
2026-06-30 15:38:40 +09:00
Rinjae ea031f3755 Merge remote-tracking branch 'origin/master'
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
2026-06-29 18:39:25 +09:00
Rinjae 320e0d6011 전화번호 정규화 유틸 추가 및 적용:
- PhoneNumberUtil 도입: 한국번호 정규화/숫자 추출 지원
- 유틸 적용: 모든 저장 및 조회에 정규형 강제
- 초대 매칭 오류 수정: 휴대폰 형식 차이 대응
2026-06-29 18:26:15 +09:00
10 changed files with 176 additions and 5 deletions
@@ -77,5 +77,21 @@ public class Approval implements Serializable, com.eactive.eai.data.Data {
private LocalDateTime approvalDate;
@Column(name = "expect_end_date")
private String expectEndDate; //예상완료일
private String expectEndDate; //예상완료일 (yyyyMMdd)
/** 예상완료일(yyyyMMdd)의 한글 요일. 값이 없거나 형식이 맞지 않으면 빈 문자열. (예: "토") */
@Transient
public String getExpectEndDateDayOfWeek() {
if (expectEndDate == null || expectEndDate.length() < 8) {
return "";
}
try {
return java.time.LocalDate
.parse(expectEndDate.substring(0, 8), java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"))
.getDayOfWeek()
.getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.KOREAN);
} catch (Exception e) {
return "";
}
}
}
@@ -0,0 +1,80 @@
package com.eactive.apim.portal.common.util;
/**
* 한국 전화번호/연락처 정규화 유틸 (portal·admin 공유).
*
* 전화번호 저장은 본 유틸로 하이픈 구분 정규형으로 통일한다. 저장 형식이 경로별로
* 달라(가입=원본, 초대=하이픈, SSO=숫자만) 휴대폰 기반 조회/초대 매칭이 어긋나는 것을 막는다.
* 암호화 컨버터가 붙은 필드는 {@code @PrePersist}/{@code @PreUpdate} 에서 본 메서드로 평문을
* 정규화한 뒤 암호화되므로 암복호화에 영향이 없다.
*/
public final class PhoneNumberUtil {
private PhoneNumberUtil() {
}
/**
* 한국 전화번호를 하이픈 구분 정규형으로 변환한다.
* 숫자만 추출 후 prefix/길이로 포맷하며, 해당 규칙에 맞지 않는 값은 원본을 그대로 반환한다.
* 멱등(이미 정규형이면 동일 결과)이며 null/blank 는 원본을 반환한다.
*
* <pre>
* 휴대폰 01X 11자리 → XXX-XXXX-XXXX (010-1234-5678)
* 휴대폰 01X 10자리 → XXX-XXX-XXXX (011-123-4567)
* 서울 02 9자리 → 02-XXX-XXXX (02-123-4567)
* 서울 02 10자리 → 02-XXXX-XXXX (02-1234-5678)
* 지역/070 0XX 10자리 → 0XX-XXX-XXXX (031-234-5678)
* 지역/070 0XX 11자리 → 0XX-XXXX-XXXX (070-1234-5678)
* 안심 050X 12자리 → 0XXX-XXXX-XXXX (0507-1234-5678)
* 대표 15/16/18XX 8자리 → XXXX-XXXX (1588-1234)
* 그 외/null → 원본 그대로
* </pre>
*/
public static String normalize(String raw) {
if (raw == null) {
return null;
}
String digits = raw.replaceAll("[^0-9]", "");
int len = digits.length();
if (len == 0) {
return raw;
}
// 대표번호 15XX/16XX/18XX (8자리, 지역번호 없음)
if (len == 8 && (digits.startsWith("15") || digits.startsWith("16") || digits.startsWith("18"))) {
return digits.substring(0, 4) + "-" + digits.substring(4);
}
// 안심번호 050X (12자리)
if (len == 12 && digits.startsWith("050")) {
return digits.substring(0, 4) + "-" + digits.substring(4, 8) + "-" + digits.substring(8);
}
// 서울 02
if (digits.startsWith("02")) {
if (len == 9) {
return "02-" + digits.substring(2, 5) + "-" + digits.substring(5);
}
if (len == 10) {
return "02-" + digits.substring(2, 6) + "-" + digits.substring(6);
}
return raw;
}
// 휴대폰/지역/070 등 0XX
if (digits.startsWith("0")) {
if (len == 10) {
return digits.substring(0, 3) + "-" + digits.substring(3, 6) + "-" + digits.substring(6);
}
if (len == 11) {
return digits.substring(0, 3) + "-" + digits.substring(3, 7) + "-" + digits.substring(7);
}
return raw;
}
return raw;
}
/**
* 숫자만 추출(발송 게이트웨이 전달·등가 비교용). null 은 그대로 반환.
*/
public static String digitsOnly(String raw) {
return raw == null ? null : raw.replaceAll("[^0-9]", "");
}
}
@@ -1,5 +1,6 @@
package com.eactive.apim.portal.invitation.entity;
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
import com.eactive.apim.portal.invitation.entity.UserInvitationEnums.InvitationStatus;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
@@ -62,4 +63,11 @@ public class UserInvitation {
@Comment("만료일시")
private LocalDateTime expiresOn;
/** 저장 시 초대 휴대폰을 하이픈 구분 정규형으로 통일 */
@PrePersist
@PreUpdate
private void normalizePhones() {
this.invitationMobile = PhoneNumberUtil.normalize(this.invitationMobile);
}
}
@@ -1,6 +1,7 @@
package com.eactive.apim.portal.portalorg.entity;
import com.eactive.apim.portal.common.entity.Auditable;
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
import lombok.Data;
@@ -95,4 +96,30 @@ public class PortalOrg extends Auditable implements Serializable, com.eactive.ea
@Column(name = "reverse_proxy_path", length = 255)
@Comment("리버스 프록시 경로")
private String reverseProxyPath;
/** 저장 시 전화번호를 하이픈 구분 정규형으로 통일 */
@PrePersist
@PreUpdate
private void normalizePhones() {
this.orgPhoneNumber = PhoneNumberUtil.normalize(this.orgPhoneNumber);
this.scPhoneNumber = PhoneNumberUtil.normalize(this.scPhoneNumber);
}
/**
* 법인 반려/삭제(탈퇴) 시 사업자등록번호·법인등록번호 등 민감/개인정보 필드를 제거한다.
* orgName·orgStatus·approvalStatus 등 상태값은 호출 측에서 별도 설정한다.
*/
public void clearSensitiveDataOnRemoval() {
this.corpRegNo = null;
this.compRegNo = null;
this.orgCode = null;
this.compRegFile = null;
this.ceoName = null;
this.orgAddr = null;
this.orgPhoneNumber = null;
this.scPhoneNumber = null;
this.orgSectors = null;
this.orgIndustryType = null;
this.serviceName = null;
}
}
@@ -51,13 +51,24 @@ public class PortalPropertyService extends AbstractDataService<PortalPropertyGro
Optional<PortalProperty> existingProperty = portalPropertyRepository.findById(propertyId);
if (existingProperty.isPresent()) {
return existingProperty.get().getPropertyValue();
PortalProperty property = existingProperty.get();
// 설명(property_desc)이 비어 있으면 코드가 넘긴 description 으로 채운다.
// (값이 있으면 관리자 편집분 보존 — 덮어쓰지 않음)
if (isBlank(property.getPropertyDesc()) && !isBlank(description)) {
property.setPropertyDesc(description);
portalPropertyRepository.save(property);
}
return property.getPropertyValue();
}
// 프로퍼티가 없으면 기본값으로 생성
return createDefaultProperty(groupName, propertyName, defaultValue, description);
}
private static boolean isBlank(String s) {
return s == null || s.trim().isEmpty();
}
/**
* 기본 프로퍼티를 DB에 생성
*/
@@ -1,6 +1,7 @@
package com.eactive.apim.portal.portaluser.entity;
import com.eactive.apim.portal.common.entity.Auditable;
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.ApprovalStatus;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.RoleCode;
@@ -98,4 +99,12 @@ public class PortalUser extends Auditable implements Serializable, com.eactive.e
@Column(name = "withdrawal_reason", length=200)
@Comment("회원탈퇴사유")
private String withdrawalReason;
/** 저장 시 전화번호를 하이픈 구분 정규형으로 통일 */
@PrePersist
@PreUpdate
private void normalizePhones() {
this.phoneNumber = PhoneNumberUtil.normalize(this.phoneNumber);
this.mobileNumber = PhoneNumberUtil.normalize(this.mobileNumber);
}
}
@@ -60,7 +60,8 @@ public enum MessageCode {
@Getter
private final String serviceId;
@Getter
private final int channels;
@Deprecated // 삭제 예정
private int channels = 0;
public static Optional<MessageCode> findServiceId(String code) {
@@ -72,6 +73,11 @@ public enum MessageCode {
return Optional.empty();
}
MessageCode(String description, String serviceId) {
this.description = description;
this.serviceId = serviceId;
// this.channels = channel.getValue();
}
MessageCode(String description, String serviceId, Channels channel) {
this.description = description;
@@ -59,8 +59,11 @@ public class MessageRequest implements Serializable {
@Convert(converter = PersonalDataEncryptConverter.class)
private String phone;
@Column(name = "user_id")
private String userId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@JoinColumn(name = "user_id", insertable = false, updatable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@NotFound(action = NotFoundAction.IGNORE)
@Comment("발송 대상 사용자")
private PortalUser user;
@@ -80,5 +83,4 @@ public class MessageRequest implements Serializable {
@Column(name = "service_id")
private String serviceId;
}
@@ -89,6 +89,7 @@ public class MessageSendService {
smsRequest.setPhone(user.getPhone());
smsRequest.setEaiInterfaceId(smsEaiInterfaceId);
smsRequest.setServiceId(portalProperties.get("ums.sms.tx_id"));
smsRequest.setUserId(user.getUserId());
if ("ADMIN_VERIFICATION_MOBILEPHONE".equals(template.getMessageCode().toUpperCase())) {
smsRequest.setMessageType("KAKAO");
} else {
@@ -111,6 +112,7 @@ public class MessageSendService {
emailRequest.setEaiInterfaceId(mailEaiInterfaceId);
emailRequest.setServiceId(portalProperties.get("ums.email.tx_id"));
emailRequest.setMessageType("EMAIL");
emailRequest.setUserId(user.getUserId());
messageRequestRepository.save(emailRequest);
logger.debug(emailRequest.toString());
}
@@ -128,6 +130,7 @@ public class MessageSendService {
messengerRequest.setEaiInterfaceId(swingEaiInterfaceId);
messengerRequest.setServiceId(portalProperties.get("ums.messenger.tx_id"));
messengerRequest.setMessageType("MESSENGER");
messengerRequest.setUserId(user.getUserId());
messageRequestRepository.save(messengerRequest);
logger.debug(messengerRequest.toString());
}
@@ -1,5 +1,6 @@
package com.eactive.apim.portal.user.entity;
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import com.eactive.eai.data.converter.StringTrimConverter;
@@ -139,4 +140,12 @@ public class UserInfo extends AbstractEntity<String> implements Serializable {
public @NonNull String getId() {
return userid;
}
/** 저장 시 전화번호를 하이픈 구분 정규형으로 통일 */
@PrePersist
@PreUpdate
private void normalizePhones() {
this.cphnno = PhoneNumberUtil.normalize(this.cphnno);
this.ofctelno = PhoneNumberUtil.normalize(this.ofctelno);
}
}