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 @@ <% } %> + + 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);