diff --git a/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp b/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
index 95ddbbb..ad1005e 100644
--- a/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
+++ b/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
@@ -29,6 +29,16 @@
// 원본 행 데이터를 ID로 조회하기 위한 맵
var gridRowDataMap = {};
+ // 휴대폰번호 중복 조회 모드 여부
+ var isDupView = false;
+
+ // 현재 검색조건 + 중복 조회 모드 플래그를 합쳐 LIST 요청 파라미터를 생성
+ function buildListPostData() {
+ var postData = getSearchForJqgrid("cmd", "LIST");
+ postData.onlyDuplicateMobile = isDupView;
+ return postData;
+ }
+
/* 선택된 사용자 삭제 - 상태에 따라 Soft/Hard Delete 자동 분기 */
function deleteSelectedUsers() {
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
@@ -166,6 +176,13 @@
$('#grid').jqGrid('setColProp', 'approvalStatus', {editoptions: {value: select_approvalStatus}});
$('#grid').jqGrid('setColProp', 'userStatus', {editoptions: {value: select_userStatus}});
+
+ // 휴대폰번호 중복 허용안함(기능 ON) + 중복 사용자 존재 시 배너 노출
+ if (json.mobileDuplicateCheckEnabled && json.mobileDuplicateUserCount > 0) {
+ $("#dupMobileCount").text(json.mobileDuplicateUserCount);
+ $("#dupMobileBanner").show();
+ }
+
$('#grid').trigger('reloadGrid');
},
@@ -176,7 +193,7 @@
}
$(document).ready(function () {
- var gridPostData = getSearchForJqgrid("cmd", "LIST");
+ var gridPostData = buildListPostData();
$('#grid').jqGrid({
datatype: "json",
@@ -188,7 +205,7 @@
'<%= localeMessage.getString("portalUser.orgName") %>',
'<%= localeMessage.getString("portalUser.name") %>',
'<%= localeMessage.getString("portalUser.userId") %>',
- <%--'<%= localeMessage.getString("portalUser.mobile") %>',--%>
+ '<%= localeMessage.getString("portalUser.mobileNumber") %>',
'<%= localeMessage.getString("portalUser.roleName") %>',
'<%= localeMessage.getString("portalUser.userStatus") %>',
'<%= localeMessage.getString("portalUser.createOn") %>',
@@ -201,7 +218,7 @@
{name: 'portalOrgUIs.orgName', align: 'center', width: "120"},
{name: 'userName', align: 'center', width: "80" },
{name: 'loginId', align: 'center', width: "150" },
- // {name: 'mobileNumber', align: 'center', width: "100" },
+ {name: 'mobileNumber', align: 'center', width: "110", hidden: true},
{name: 'roleCodeDescription', align: 'center', width: "80"},
{name: 'userStatusDescription', align: 'center', width: "50"},
{name: 'createdDate', align: 'center', width: "100", formatter: timeStampFormat},
@@ -274,9 +291,20 @@
init();
$("#btn_search").click(function () {
- var postData = getSearchForJqgrid("cmd", "LIST");
+ $("#grid").setGridParam({url: url, postData: buildListPostData(), page: 1}).trigger("reloadGrid");
+ });
- $("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
+ // 중복 휴대폰번호 사용자만 보기 / 전체 보기 토글
+ $("#btn_dup_toggle").click(function () {
+ isDupView = !isDupView;
+ if (isDupView) {
+ $(this).text("전체 보기");
+ $("#grid").jqGrid('showCol', 'mobileNumber');
+ } else {
+ $(this).text("중복만 보기");
+ $("#grid").jqGrid('hideCol', 'mobileNumber');
+ }
+ $("#grid").setGridParam({url: url, postData: buildListPostData(), page: 1}).trigger("reloadGrid");
});
$("#btn_new").click(function () {
@@ -334,6 +362,11 @@
<% } %>
+
+ ⚠ 중복 휴대폰번호 사용자 0건 발견
+
+
+
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserService.java
index 965f0c7..1566095 100644
--- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserService.java
+++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserService.java
@@ -8,12 +8,14 @@ import com.eactive.apim.portal.template.entity.QMessageRequest;
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
+import com.querydsl.jpa.JPAExpressions;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
import java.util.Optional;
@Service
@@ -54,9 +56,40 @@ public class PortalUserService extends AbstractEMSDataSerivce duplicateGroupCounts = getJPAQueryFactory()
+ .select(qPortalUser.count())
+ .from(qPortalUser)
+ .where(qPortalUser.mobileNumber.isNotNull()
+ .and(qPortalUser.userStatus.ne(PortalUserEnums.UserStatus.REMOVED)))
+ .groupBy(qPortalUser.mobileNumber)
+ .having(qPortalUser.count().gt(1L))
+ .fetch();
+ return duplicateGroupCounts.stream().mapToLong(Long::longValue).sum();
+ }
+
public boolean existsByLoginId(String loginId) {
BooleanExpression predicate = QPortalUser.portalUser.loginId.eq(loginId);
return repository.exists(predicate);
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserUISearch.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserUISearch.java
index 4ecbdaa..bde84f2 100644
--- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserUISearch.java
+++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portaluser/PortalUserUISearch.java
@@ -23,4 +23,7 @@ public class PortalUserUISearch {
private boolean includeRemoved;
+ /** true 인 경우 휴대폰번호가 중복(2명 이상 공유)되는 사용자만 조회 */
+ private boolean onlyDuplicateMobile;
+
}
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java
index 2669a8f..0130117 100644
--- a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java
@@ -102,6 +102,12 @@ public class PortalUserManController extends BaseAnnotationController {
resultMap.put("userStatusRows", userStatusList);
resultMap.put("roleCodeRows", roleCodeList);
resultMap.put("isProdServer", SystemUtil.isProdServer());
+
+ // 휴대폰번호 중복 허용안함 여부 및 중복 사용자 수 (배너 노출용)
+ Map mobileDuplicateInfo = portalUserManService.getMobileDuplicateInfo();
+ resultMap.put("mobileDuplicateCheckEnabled", mobileDuplicateInfo.get("enabled"));
+ resultMap.put("mobileDuplicateUserCount", mobileDuplicateInfo.get("count"));
+
return ResponseEntity.ok(resultMap);
}
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java
index 75b9197..d04655a 100644
--- a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java
@@ -4,6 +4,7 @@ import com.eactive.apim.portal.file.entity.FileDetail;
import com.eactive.apim.portal.file.entity.FileInfo;
import com.eactive.apim.portal.file.service.FileService;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
+import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
import com.eactive.apim.portal.portaluser.entity.PortalUserPrivacyAgreement;
@@ -31,7 +32,9 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@Service
@Transactional(transactionManager = "transactionManagerForEMS")
@@ -39,6 +42,11 @@ import java.util.List;
@Slf4j
public class PortalUserManService extends BaseService {
+ /** PortalProperty 그룹명 */
+ private static final String PORTAL_PROPERTY_GROUP = "Portal";
+ /** 사용자 별 휴대폰 번호 중복 허용 프로퍼티 키 (Y:허용, N:허용안함) */
+ private static final String PROP_MOBILE_DUPLICATE_ALLOW = "user.mobile.duplicate.allow";
+
private final PortalUserService portalUserService;
private final PortalUserUIMapper portalUserUIMapper;
private final LocaleMessage localeMessage;
@@ -50,6 +58,7 @@ public class PortalUserManService extends BaseService {
private final PortalUserTermsService portalUserTermsService;
private final ApprovalService approvalService;
private final PortalInquiryService portalInquiryService;
+ private final PortalPropertyService portalPropertyService;
public Page selectList(Pageable pageable, PortalUserUISearch portalUserUISearch) {
@@ -57,6 +66,11 @@ public class PortalUserManService extends BaseService {
return portalUser.map(entity -> {
PortalUserUI ui = convertToUIWithMaskOption(entity, false);
+ // 목록의 휴대폰번호는 항상 마스킹 (중복 조회 모드에서만 컬럼이 노출되며 개인정보 보호)
+ if (ui != null && StringUtils.isNotBlank(ui.getMobileNumber())) {
+ ui.setMobileNumber(MaskingUtils.maskPhoneNumber(ui.getMobileNumber()));
+ }
+
if (entity.getPortalOrg() != null) {
setPortalOrgUI(entity, ui);
}
@@ -64,6 +78,28 @@ public class PortalUserManService extends BaseService {
});
}
+ /**
+ * 사용자 별 휴대폰 번호 중복 허용 프로퍼티(Portal/user.mobile.duplicate.allow)를 확인한다.
+ * 프로퍼티가 없으면 기본값 'N'(허용안함)으로 생성하며, '허용안함'일 때 중복 휴대폰번호 사용자 수를 함께 반환한다.
+ *
+ * @return enabled(boolean): 중복 조회 기능 활성 여부, count(long): 중복 휴대폰번호 사용자 수
+ */
+ public Map getMobileDuplicateInfo() {
+ String allowValue = portalPropertyService.getOrCreateProperty(
+ PORTAL_PROPERTY_GROUP,
+ PROP_MOBILE_DUPLICATE_ALLOW,
+ "N",
+ "사용자 별 휴대폰 번호 중복 허용 여부 (Y:허용, N:허용안함). 허용안함일 때 사용자 관리 목록에 중복 휴대폰번호 사용자 조회 기능을 노출");
+
+ boolean checkEnabled = "N".equalsIgnoreCase(allowValue);
+ long duplicateUserCount = checkEnabled ? portalUserService.countDuplicateMobileUsers() : 0L;
+
+ Map info = new HashMap<>();
+ info.put("enabled", checkEnabled);
+ info.put("count", duplicateUserCount);
+ return info;
+ }
+
public PortalUserUI selectDetail(String id, boolean isMasked) {
PortalUser portalUser = portalUserService.getById(id);
PortalUserUI portalUserUI = convertToUIWithMaskOption(portalUser, isMasked);