package kr.wisestone.owl.service.impl; import com.amazonaws.services.s3.AmazonS3; import com.google.common.collect.Lists; import com.google.gson.Gson; import kr.wisestone.owl.config.security.service.UserSecurityService; import kr.wisestone.owl.constant.Constants; import kr.wisestone.owl.constant.MngPermission; import kr.wisestone.owl.constant.MsgConstants; import kr.wisestone.owl.domain.*; import kr.wisestone.owl.domain.enumType.EmailType; import kr.wisestone.owl.domain.enumType.SocialType; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.mapper.UserMapper; import kr.wisestone.owl.repository.DepartmentRepository; import kr.wisestone.owl.repository.UserRepository; import kr.wisestone.owl.service.*; import kr.wisestone.owl.util.*; import kr.wisestone.owl.vo.*; import kr.wisestone.owl.web.condition.UserCondition; import kr.wisestone.owl.web.form.UserForm; import kr.wisestone.owl.web.view.ExcelView; import org.apache.commons.validator.routines.EmailValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; import java.util.regex.Pattern; @Service public class UserServiceImpl extends AbstractServiceImpl> implements UserService { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private UserRepository userRepository; @Autowired private DepartmentRepository departmentRepository; @Autowired private UserLevelService userLevelService; @Autowired private DepartmentService departmentService; @Autowired private SystemRoleService systemRoleService; @Autowired private SystemEmailService systemEmailService; @Autowired private SimpMessagingTemplate simpMessagingTemplate; @Autowired private UserMapper userMapper; @Autowired private WorkspaceService workspaceService; @Autowired private UserWorkspaceService userWorkspaceService; @Autowired private ProjectRoleUserService projectRoleUserService; @Autowired private ProjectRoleService projectRoleService; @Autowired private ProjectService projectService; @Autowired private IssueTypeService issueTypeService; @Autowired private UserSecurityService userSecurityService; @Autowired private UserInviteService userInviteService; @Autowired private UserWithDrawService userWithDrawService; @Autowired private AttachedFileService attachedFileService; @Autowired private UserDepartmentService userDepartmentService; @Autowired private IssueService issueService; @Autowired private SessionRegistry sessionRegistry; @Value("${OAuth.google.clientId}") private String googleClientId; @Value("${OAuth.google.clientSecret}") private String googleClientSecret; @Value("${OAuth.google.redirectUri}") private String googleClientRedirectUri; @Value("${OAuth.naver.clientId}") private String naverClientId; @Value("${OAuth.naver.clientSecret}") private String naverClientSecret; @Value("${OAuth.kakao.clientId}") private String kakaoClientId; @Value("${OAuth.kakao.clientSecret}") private String kakaoClientSecret; @Value("${OAuth.facebook.clientId}") private String facebookClientId; @Value("${OAuth.facebook.clientSecret}") private String facebookClientSecret; @Value("${OAuth.facebook.redirectUri}") private String facebookClientRedirectUri; @Value("${OAuth.common.state}") private String oAuthState; @Value("${use.aws}") private boolean bUseAWS; @Value("${owl.license.key}") private String owlLicenseKey; @Autowired private AmazonS3 amazonS3; @Value("${aws.bucket.name}") private String bucketName; @Value("${aws.s3.url}") private String awsS3Url; @Value("${profile.file.path}") private String profileFolder; @Value("${user.join.statistics.email}") private String userJoinStatisticsEmail; // 사용자 가입 정보 담당자에게 알림 @Value("${total.statistics.email}") private String totalStatisticsEmail; // 프로젝트, 이슈, 사용자수 알림 @Value("${email.joinEmail}") private boolean bJoinEmail; @Autowired private ExcelView excelView; @Autowired private PasswordEncoder passwordEncoder; @Override protected JpaRepository getRepository() { return this.userRepository; } // 사용자 가입 후 업무 공간를 생성한다. @Override @Transactional public User addUser(UserForm userForm, MultipartFile profile) { User user = ConvertUtil.copyProperties(userForm, User.class, "socialType"); // edit by zenith at 20200722 // form 의 license를 확인하고 정상적이면, 관리자로 입력한다. Integer validAdmin = this.verifyLicenseKey(userForm.getLicensekey()); // 소셜 회원 가입일 때 소셜 정보를 저장 if (userForm.getSocialType() != null) { user.setSocialType(SocialType.valueOf(userForm.getSocialType())); } // 이름 유효성 검사 this.verifyUserName(user.getName()); // 계정 유효성 검사 this.verifyAccount(user.getId(), user.getAccount()); // 비밀번호 유효성 검사 this.verifyPassword(user.getPassword()); // 연락처 유효성 검사 this.verifyPhone(user.getPhone()); user.addSystemRole(this.systemRoleService.findByRoleType(Permission.ROLE_TYPE_WORKSPACE_MANAGER)); // 이메일 정보 암호화 user.setAccount(CommonUtil.encryptAES128(userForm.getAccount())); user.setPassword(this.passwordEncoder.encode(user.getPassword())); //this.userLevelRepository.saveAndFlush(user); //userLevelRepository.saveAndFlush(user); // 프로필 파일이 유효한지 체크한다. this.verifyProfile(profile); // 프로필을 선택하지 않았으면 시스템 프로필이 기본 선택된다. this.changeProfile(user, profile); // 사용자 기본 언어를 서버 언어로 설정한다. LocaleContextHolder.setLocale(CommonUtil.getUserLanguage(user.getLanguage())); // 업무 공간 명이 비어있을 경우 시스템에서 만들어준다. if (StringUtils.isEmpty(userForm.getWorkspaceName())) { userForm.setWorkspaceName(user.getName() + this.messageAccessor.message("common.sWorkspace")); } if(validAdmin == 0) { // 라이센스 미입력 일반 사용자 // 업무 공간를 가져온다. Workspace primaryWorkspace = this.workspaceService.getPrimaryWorkspace(); user.setLastWorkspaceId(primaryWorkspace.getId()); this.userInviteService.includePrimaryWorkspace(user, primaryWorkspace); UserLevel userLevel = this.userLevelService.getBasicUserLevel(); user.setUserLevel(userLevel); // edit by zenith for permission at 20200803 /*user.setPermission(MngPermission.USER_PERMISSION_MNG_NONE);*/ } else if(validAdmin == 1) { // 라이센스 입력 관리자 /*DepartmentForm departmentForm = new DepartmentForm(); departmentForm.setDepartmentName("부서 없음"); Department department = this.departmentService.add(departmentForm); this.departmentRepository.saveAndFlush(department);*/ Workspace primaryWorkspace = this.workspaceService.getPrimaryWorkspace(); UserLevel userLevel = this.userLevelService.addSuperUserLevel(); user.setUserLevel(userLevel); this.userLevelService.addNormalAdminUserLevel(); this.userLevelService.addNormalUserLevel(); if(primaryWorkspace == null || primaryWorkspace.getName() != userForm.getWorkspaceName()) { // 업무 공간를 생성한다. 가입한 사용자는 업무 공간의 주인이다. Workspace workspace = this.workspaceService.addWorkspace(userForm.getWorkspaceName()); this.userWorkspaceService.addUserWorkspace(user, workspace, true, true); // 기본으로 제공되는 프로젝트를 생성한다. this.projectService.addDefaultProject(user, workspace); // 기본으로 제공되는 프로젝트를 이슈 유형의 사용 프로젝트로 설정 this.issueTypeService.addDefaultUsedProject(workspace); user.setLastWorkspaceId(workspace.getId()); // edit by zenith for permission at 20200803 //user.setPermission(MngPermission.makeAllPermission()); } else { this.userInviteService.includePrimaryWorkspace(user, primaryWorkspace); user.setLastWorkspaceId(primaryWorkspace.getId()); /*user.setPermission(MngPermission.makeSubAllPermission());*/ } } this.userRepository.saveAndFlush(user); // 이메일 알림 예정 시간이 공백이면 디폴트 이메일 알림 예정 시간으로 설정한다. user.setReservationNotifyTime(User.DEFAULT_RESERVATION_NOTIFY_TIME); // 초대받은 메일이 있는 사용자가 가입했으면 초대했을 때 선택한 프로젝트에 참여 시킨다. this.userInviteService.checkInviteUser(user); Map userMap = new HashMap<>(); userMap.put("name", user.getName()); userMap.put("account", CommonUtil.decryptAES128(user.getAccount())); userMap.put("registerDate", DateUtil.convertDateToYYYYMMDD(user.getRegisterDate())); // 회원 가입 알림 메일 전송 if(bJoinEmail) { this.systemEmailService.directEmail(new String[]{user.getAccount()}, EmailType.WORKSPACE_JOIN, userMap, null); } return user; } // 이름 유효성 검사 private void verifyUserName(String userName) { if (StringUtils.isEmpty(userName)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_NAME)); } if (userName.length() > 50) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NAME_LENGTH_EXCESS)); } } // 계정 유효성 검사 private void verifyAccount(Long id, String account) { if (StringUtils.isEmpty(account)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_EMAIL)); } else { if (!EmailValidator.getInstance().isValid(account)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL)); } // 중복 여부 체크 if (id == null) { if (this.findByAccount(CommonUtil.encryptAES128(account)) != null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_USED_EMAIL)); } } else { if (this.userRepository.findByIdNotAndAccount(id, CommonUtil.encryptAES128(account)) != null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_USED_EMAIL)); } } // 회원 탈퇴 기록 조회 this.checkWithDrawAccount(CommonUtil.encryptAES128(account)); } } // 회원 탈퇴 기록 조회 private void checkWithDrawAccount(String account) { if (this.userWithDrawService.findByAccount(account) != null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_WITH_DRAW_EXIST)); } } // 비밀번호 유효성 검사 private void verifyPassword(String password) { if (StringUtils.isEmpty(password)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_PASSWORD)); } // 사용자가 입력할 수 있는 비밀번호는 20글자이지만 MD5 암호화를 거치기 때문에 Table 컬럼 길이 100으로 한다. if (password.length() > 200) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_MAX_LENGTH_OUT)); } } // 연락처 유효성 검사 private void verifyPhone(String phone) { if (!StringUtils.isEmpty(phone)) { if (phone.length() > 20) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PHONE_MAX_LENGTH_OUT)); } if (!Pattern.matches("^[0-9]*$", phone)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PHONE_ONLY_NUMBER)); } } } // 라이센스키 유효성 검사 private Integer verifyLicenseKey(String licensekey) { Integer validAdminUser = 0; // 0 : 일반사용자, 1: 관리자, -1 : 비정상 라이센스입력 if (StringUtils.isEmpty(licensekey)) { return validAdminUser; } String inputLicenseKey = StringUtils.trimWhitespace(licensekey); if(owlLicenseKey.compareTo(inputLicenseKey) == 0) { validAdminUser = 1; } else { validAdminUser = -1; throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_LICENSEKEY)); } return validAdminUser; } // 프로필 검사 private void verifyProfile(MultipartFile profile) { if (profile == null) { return; } // 업로드 프로필 확장자 체크 this.checkAcceptFile(profile); // 업로드 프로필 사진 용량 체크 this.checkProfileSize(profile); } // 프로필 파일 용량 체크 private void checkProfileSize(MultipartFile profile) { if (profile.getSize() > 10485760) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_SIZE_NOT_ALLOW)); } } // 프로필 파일 확장자 체크 private void checkAcceptFile(MultipartFile profile) { boolean result = false; String check = profile.getOriginalFilename().substring(profile.getOriginalFilename().lastIndexOf(".")); if (check.equalsIgnoreCase(".jpg") || check.equalsIgnoreCase(".png")) { result = true; } if (!result) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_UPLOAD_FILE_TYPE_NOT_ALLOW)); } } // 프로필 파일은 10M 이하만 허용한다. private void changeProfile(User user, MultipartFile profile) { // 프로필을 선택하지 않았으면 시스템 프로필이 기본 선택된다. String filePath = User.DEFAULT_PROFILE; if (profile != null) { // 10MB 제한 if (profile.getSize() > 10 * 1024 * 1024) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_SIZE_NOT_ALLOW)); } try { Map fileMap = CommonUtil.makeFileMap(profile); // 프로필 업로드 String awsKey = this.attachedFileService.uploadFile(fileMap, this.profileFolder, null, 1, 1); user.setAwsKey(awsKey); // owlKey 저장 filePath = this.awsS3Url + this.bucketName + this.profileFolder + "/" + awsKey; } catch (Exception e) { log.debug(e.getMessage()); } } // oAuth2.0으로 연동했을 때 가져온 프로필 정보를 기본 프로필로 저장한다. if (user.getProfile() == null) { user.setProfile(filePath); } this.userRepository.saveAndFlush(user); } // 사용자 정보를 수정한다. @Override @Transactional public void modifyUser(UserForm userForm, MultipartFile profile) { // 로그인한 사용자와 수정하려는 사용자 정보가 일치하는지 확인 this.verifyUserId(userForm.getId()); // 이름 유효성 검사 this.verifyUserName(userForm.getName()); // 연락처 유효성 검사 this.verifyPhone(userForm.getPhone()); // 프로필 파일이 유효한지 체크한다. this.verifyProfile(profile); User user = this.getUser(userForm.getId()); // 폼 정보를 user 객체에 복사한다. ConvertUtil.copyProperties(userForm, user, "id", "profile", "account", "password"); // 이메일 알림 예정 시간이 공백이면 디폴트 이메일 알림 예정 시간으로 설정한다. if (StringUtils.isEmpty(user.getReservationNotifyTime())) { user.setReservationNotifyTime(User.DEFAULT_RESERVATION_NOTIFY_TIME); } else { user.setReservationNotifyTime(userForm.getReservationNotifyTime()); } // 프로필을 변경하지 않은 경우 - profile 이 올라오지 않는다. // 프로필을 변경한 경우 - profile 이 올라온다. if (profile != null) { // 기존 프로필 삭제. 단, 기본 제공되는 프로필일때는 삭제하지 않는다. if (!user.getProfile().contains(User.DEFAULT_PROFILE)) { // 업로드한 프로필일 경우 if (!StringUtils.isEmpty(user.getAwsKey())) { this.removeProfile(user.getAwsKey()); } } // 프로필 등록 user.setProfile(null); this.changeProfile(user, profile); } this.userRepository.saveAndFlush(user); SecurityUtils.setUserToSession(user); } // 등록한 프로필 파일을 삭제한다. private void removeProfile(String awsKey) { try { this.attachedFileService.removeFile(awsKey, -1L); } catch (Exception e) { log.debug("프로필 삭제 에러 :" + e.getMessage()); } } // 비밀번호를 수정한다. @Override @Transactional public void modifyPassword(UserForm userForm) { // 로그인한 사용자와 수정하려는 사용자 정보가 일치하는지 확인 this.verifyUserId(userForm.getId()); User user = this.getUser(userForm.getId()); // 비밀번호 유효성 체크 this.verifyPassword(userForm.getPassword()); // 비밀번호 변경 유효성 체크 this.verifyModifyPassword(userForm.getCurrentPassword(), user.getPassword(), userForm.getPassword(), userForm.getPasswordConfirm()); user.setPassword(this.passwordEncoder.encode(userForm.getPassword())); this.userRepository.saveAndFlush(user); } // 비밀번호 변경 유효성 체크 private void verifyModifyPassword(String currentPassword, String oldPassword, String newPassword, String newPasswordConfirm) { // 현재 비밀번호 확인 if (!this.passwordEncoder.matches(currentPassword, oldPassword)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_CURRENT_PASSWORD)); } // 변경하려는 비밀번호가 현재 비밀번호와 같으면 안됨 if (newPassword.equals(currentPassword)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_SAME_NEW_PASSWORD)); } if (!newPassword.equals(newPasswordConfirm)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_NOT_SAME_CONFIRM_PASSWORD)); } } // 사용자 상세 정보를 조회한다. @Override @Transactional(readOnly = true) public void detailUser(Map resJsonData, UserCondition userCondition) { UserVo userVo = new UserVo(); if (userCondition.getId() != null) { // 로그인한 사용자와 수정하려는 사용자 정보가 일치하는지 확인 this.verifyUserId(userCondition.getId()); User user = this.getUser(userCondition.getId()); userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); Map projectManagerYN = this.projectRoleUserService.findProjectManager((userCondition.getId())); if(projectManagerYN != null){ userVo.setProjectManagerYN(true); } } resJsonData.put(Constants.RES_KEY_CONTENTS, userVo); } // 로그인한 사용자와 수정하려는 사용자 정보가 일치하는지 확인 private void verifyUserId(Long id) { if (!this.webAppUtil.getLoginId().equals(id)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_MODIFY_SELF)); } } // 계정명으로 사용자를 찾는다 - security, 소셜 연동 여부 확인에서 사용 @Override @Transactional(readOnly = true) public User findByAccount(String account) { return this.userRepository.findByAccount(account); } // 업무 공간의 관리자를 찾는다. @Override @Transactional(readOnly = true) public User findByWorkspaceIdAndManagerYn(Workspace workspace) { return this.userRepository.findByWorkspaceIdAndManagerYn(workspace.getId(), true); } // OAuth 인증 @Override @Transactional public ModelAndView getOAuthToken(String code, String state, SocialType socialType, HttpServletRequest request) { // 응답받은 state 값이 내가 설정한 값과 같은지 확인 this.checkOAuthState(state); // 소셜 유형에 따라 token url 정보를 가져온다. String targetUrl = this.getOAuthTokenUrl(socialType); HttpURLConnection conn = null; BufferedWriter writer = null; String response = ""; try { URL url = new URL(targetUrl); conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 소셜 유형에 따라 token parameter 를 설정한다. Map postDataParams = new HashMap<>(); this.setOAuthTokenClientAuthorizationCode(postDataParams, socialType, code); writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8")); writer.write(CommonUtil.getPostDataString(postDataParams)); writer.flush(); response = CommonUtil.getStringToInputStream(conn.getResponseCode(), conn.getInputStream()); } catch (IOException e) { log.error("[UserServiceImpl.getOAuthToken()] => " + e.getMessage()); } finally { this.deAllocationMemory(writer, conn); } if (!StringUtils.isEmpty(response)) { Gson gson = new Gson(); Map token = (Map) gson.fromJson(response, Object.class); String accessToken = MapUtil.getString(token, "access_token"); return this.getProfileAndUserSocialConfirm(socialType, accessToken, request); } return new ModelAndView("/views/login/socialFail.html"); } // 응답받은 state 값이 내가 설정한 값과 같은지 확인 private void checkOAuthState(String responseState) { if (!this.oAuthState.equals(responseState)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.OAUTH_STATE_VALUE_NOT_EQUAL)); } } // 소셜 유형에 따라 token url 정보를 가져온다. private String getOAuthTokenUrl(SocialType socialType) { String targetUrl = ""; switch (socialType) { case GOOGLE: targetUrl = "https://www.googleapis.com/oauth2/v4/token"; break; case NAVER: targetUrl = "https://nid.naver.com/oauth2.0/token"; break; case KAKAO: targetUrl = "https://kauth.kakao.com/oauth/token"; break; case FACEBOOK: targetUrl = "https://graph.facebook.com/v3.1/oauth/access_token"; break; } return targetUrl; } // 소셜 유형에 따라 token parameter 를 설정한다. private void setOAuthTokenClientAuthorizationCode(Map postDataParams, SocialType socialType, String code) { switch (socialType) { case GOOGLE: postDataParams.put("client_id", this.googleClientId); postDataParams.put("client_secret", this.googleClientSecret); postDataParams.put("redirect_uri", this.googleClientRedirectUri); break; case NAVER: postDataParams.put("client_id", this.naverClientId); postDataParams.put("client_secret", this.naverClientSecret); break; case KAKAO: postDataParams.put("client_id", this.kakaoClientId); postDataParams.put("client_secret", this.kakaoClientSecret); break; case FACEBOOK: postDataParams.put("client_id", this.facebookClientId); postDataParams.put("client_secret", this.facebookClientSecret); postDataParams.put("redirect_uri", this.facebookClientRedirectUri); break; } postDataParams.put("code", code); postDataParams.put("grant_type", "authorization_code"); postDataParams.put("state", this.oAuthState); } // 메모리 해제 private void deAllocationMemory(BufferedWriter writer, HttpURLConnection conn) { try { if (writer != null) { writer.close(); } } catch (IOException e) { log.error("[UserServiceImpl.getOAuthToken().writer.close()] => " + e.getMessage()); } try { if (conn != null) { conn.disconnect(); } } catch (Exception e) { log.error("[UserServiceImpl.getOAuthToken().conn.disconnect()] => " + e.getMessage()); } } // 프로필 정보를 얻고 해당 사용자가 이미 소셜 연동이 되었는지 확인 후 연동이 안되어 있으면 사용자를 추가한다. private ModelAndView getProfileAndUserSocialConfirm(SocialType socialType, String accessToken, HttpServletRequest request) { HttpURLConnection conn = null; String apiUrl = this.getOAuthUserProfileUrl(socialType); String response = ""; String modelViewName; // 페이스북만 이렇게... if (socialType.equals(SocialType.FACEBOOK)) { apiUrl += "?fields=id%2Cname%2Cemail%2Cpicture&access_token=" + accessToken; } try { URL url = new URL(apiUrl); conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("Accept", "application/json"); conn.setRequestProperty("Authorization", "Bearer " + accessToken); conn.setRequestMethod("GET"); conn.connect(); response = CommonUtil.getStringToInputStream(conn.getResponseCode(), conn.getInputStream()); } catch (IOException e) { log.error("[UserServiceImpl.getProfileAndUserSocialConfirm()] => " + e.getMessage()); } finally { if (conn != null) { conn.disconnect(); } } if (StringUtils.isEmpty(response)) { modelViewName = "/views/login/socialFail.html"; } else { Map profile = new HashMap<>(); Gson gson = new Gson(); Object socialProfile = gson.fromJson(response, Object.class); // 해당 소셜에서 받은 사용자 정보를 분석한다. this.parserUserProfile(socialType, profile, (Map) socialProfile, accessToken); // 사용자가 이미 소셜연동이 되어있는지 확인하고 정보가 없을 경우 사용자를 추가한다. modelViewName = this.checkUserSocialConnect(profile, request, socialType); } return new ModelAndView(modelViewName); } // 카카오톡 로그인에서 실패할 경우 앱 연동 정보를 삭제 요청한다. private void unlinkKaKao(String accessToken) { try { URL url = new URL("https://kapi.kakao.com/v1/user/unlink"); HttpURLConnection unlinkConnect = (HttpURLConnection) url.openConnection(); unlinkConnect.setRequestProperty("Accept", "application/json"); unlinkConnect.setRequestProperty("Authorization", "Bearer " + accessToken); unlinkConnect.setRequestMethod("POST"); unlinkConnect.connect(); CommonUtil.getStringToInputStream(unlinkConnect.getResponseCode(), unlinkConnect.getInputStream()); } catch (IOException e) { log.error("[UserServiceImpl.unlinkSocial()] => " + e.getMessage()); } } // 소셜에서 개인 정보를 가져오기 위한 url 정보 private String getOAuthUserProfileUrl(SocialType socialType) { String apiUrl = ""; switch (socialType) { case GOOGLE: apiUrl = "https://www.googleapis.com/plus/v1/people/me"; break; case NAVER: apiUrl = "https://openapi.naver.com/v1/nid/me"; break; case KAKAO: apiUrl = "https://kapi.kakao.com/v2/user/me"; break; case FACEBOOK: apiUrl = "https://graph.facebook.com/v3.1/me"; break; } return apiUrl; } // 해당 소셜에서 받은 사용자 정보를 분석한다. private void parserUserProfile(SocialType socialType, Map profile, Map socialProfile, String accessToken) { String email = ""; String name = ""; String profilePath = ""; Map response; switch (socialType) { case GOOGLE: List> emails = (List>) socialProfile.get("emails"); Map emailInfo = emails.get(0); email = MapUtil.getString(emailInfo, "value"); name = MapUtil.getString(socialProfile, "displayName"); profile.put("language", MapUtil.getString(socialProfile, "language")); break; case NAVER: response = (Map) socialProfile.get("response"); email = MapUtil.getString(response, "email"); name = MapUtil.getString(response, "name"); break; case KAKAO: response = (Map) socialProfile.get("properties"); name = MapUtil.getString(response, "nickname"); Map emailMap = (Map) socialProfile.get("kakao_account"); if (MapUtil.getBoolean(emailMap, "has_email")) { email = MapUtil.getString(emailMap, "email"); } if (StringUtils.isEmpty(email)) { // 카카오톡 로그인에서 실패할 경우 앱 연동 정보를 삭제 요청한다. this.unlinkKaKao(accessToken); } break; case FACEBOOK: email = MapUtil.getString(socialProfile, "email"); name = MapUtil.getString(socialProfile, "name"); break; } profile.put("email", email); profile.put("name", name); profile.put("profile", profilePath); } // 사용자가 이미 소셜연동이 되어있는지 확인하고 정보가 없을 경우 사용자를 추가한다. private String checkUserSocialConnect(Map profile, HttpServletRequest request, SocialType socialType) { String email = MapUtil.getString(profile, "email"); User user = this.findByAccount(CommonUtil.encryptAES128(email)); String modelViewName; // 카카오, 페이스북, 네이버, 구글에서 공통적으로 이메일을 받지 못할 경우 여기서 에러 처리해준다. if (StringUtils.isEmpty(email)) { modelViewName = "/views/login/socialFailNotEmail.html"; return modelViewName; } try { // 회원 탈퇴 기록 조회 this.checkWithDrawAccount(CommonUtil.encryptAES128(email)); } catch (Exception e) { modelViewName = "/views/login/withDrawSocialAccount.html"; return modelViewName; } if (user == null) { String name = MapUtil.getString(profile, "name"); String language = MapUtil.getString(profile, "language"); String profilePath = MapUtil.getString(profile, "profile"); UserForm userForm = new UserForm(); userForm.setName(name); userForm.setAccount(email); if (StringUtils.isEmpty(language)) { userForm.setLanguage(User.DEFAULT_LANGUAGE); } else { switch (language) { case "ko": userForm.setLanguage(language); break; case "en": userForm.setLanguage(language); break; case "ja": userForm.setLanguage(language); break; case "vi": userForm.setLanguage(language); break; default: userForm.setLanguage(User.DEFAULT_LANGUAGE); } } userForm.setStatus(User.USER_STATUS_ACTIVE); String tempPassword = CommonUtil.randomStringMaker(); userForm.setPassword(CommonUtil.encryptionSha512(tempPassword)); // 비밀번호를 랜덤으로 만들어준다. userForm.setWorkspaceName(name + this.messageAccessor.message("common.sWorkspace")); // 의 업무공간 // 소셜 정보 셋팅 if (socialType != null) { userForm.setSocialType(socialType.toString()); } if (!StringUtils.isEmpty(profilePath)) { userForm.setProfile(profilePath); } // 사용자 생성 this.addUser(userForm, null); // 자동 로그인 this.autoLogin(CommonUtil.encryptAES128(userForm.getAccount()), request); modelViewName = "redirect:/#/dashboards/dashboard"; } else { // 이미 존재하면서 연동 정보가 있을 경우 자동 로그인 if (user.getSocialType() != null) { if (user.getSocialType().equals(socialType)) { this.autoLogin(user.getAccount(), request); modelViewName = "redirect:/#/dashboards/dashboard"; } else { // 이미 존재하면서 연동 정보가 없을 경우 modelViewName = "/views/login/socialConnect.html"; } } else { // 이미 존재하면서 연동 정보가 없을 경우 modelViewName = "/views/login/socialConnect.html"; } } return modelViewName; } // 자동 로그인 @Override @Transactional(readOnly = true) public void autoLogin(String email, HttpServletRequest request) { UserDetails userDetails = this.userSecurityService.loadUserByUsername(email); List sessionInformationList = this.sessionRegistry.getAllSessions(userDetails, false); for (SessionInformation sessionInformation : sessionInformationList) { String targetAccount = (String) sessionInformation.getPrincipal(); if (targetAccount.equals(userDetails.getUsername())) { sessionInformation.expireNow(); break; } } try { log.warn("자동 로그인 수행 전"); SecurityUtils.autoLogin(userDetails); log.warn("자동 로그인 수행 후"); } catch (Exception ex) { log.error(ex.getMessage()); } } // 사용자 목록을 가져온다. @Override @Transactional(readOnly = true) public List findUser(Map resJsonData, UserCondition condition, Pageable pageable) { Long loginId = this.webAppUtil.getLoginId(); condition.setWorkspaceId(this.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); condition.setPage(pageable.getPageNumber() * pageable.getPageSize()); condition.setPageSize(pageable.getPageSize()); condition.setLoginUserId(loginId); List> results = this.userMapper.find(condition); Long totalUsersCount = this.userMapper.count(condition); return this.convertUserVoToMap(results, totalUsersCount, pageable, resJsonData); } // 사용자 정보를 가져온다. @Override @Transactional(readOnly = true) public void findMyLevelAndDepartment(Map resJsonData) { Long loginId = this.webAppUtil.getLoginId(); Map result = this.userMapper.findByMyLevelAndDepartment(loginId); resJsonData.put(Constants.RES_KEY_CONTENTS, result); } // 사용자 아이디로 사용자를 조회한다. @Override @Transactional(readOnly = true) public User getUser(Long id) { if (id == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST)); } User user = this.findOne(id); if (user == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST)); } return user; } // 사용자 이메일로 현재 비밀번호를 알려준다. @Override @Transactional public void returnEmailPassword(UserForm userForm) { User user = this.userRepository.findByAccount(userForm.getAccount()); if (user == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST)); } // 소셜 계정으로 회원 가입한 사용자는 비밀번호 찾기 기능을 제공하지 않는다. this.checkOAuthJoinUser(user); String tempPassword = CommonUtil.randomStringMaker(); user.setPassword(this.passwordEncoder.encode(CommonUtil.encryptionSha512(tempPassword))); // 비밀번호를 랜덤으로 만들어준다. this.userRepository.saveAndFlush(user); Map params = new HashMap<>(); params.put("name", user.getName()); params.put("account", CommonUtil.decryptAES128(user.getAccount())); params.put("password", tempPassword); // 비밀번호 찾기 이메일 전송 this.systemEmailService.directEmail(new String[]{user.getAccount()}, EmailType.USER_SEARCH_PASSWORD, params, null); } // 소셜 계정으로 회원 가입한 사용자는 비밀번호 찾기 기능을 제공하지 않는다. private void checkOAuthJoinUser(User user) { if (user.getSocialType() != null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER)); } } // 마지막으로 선택한 업무 공간 정보를 저장한다. @Override @Transactional public void updateLastWorkspace(Map resJsonData, UserForm userForm) { User user = this.getUser(this.webAppUtil.getLoginId()); if (userForm.getLastWorkspaceId() != null) { user.setLastWorkspaceId(userForm.getLastWorkspaceId()); } this.userRepository.saveAndFlush(user); // 세션 업데이트 SecurityUtils.setUserToSession(user); // 클라이언트의 사용자 정보 업데이트 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); if (user.getSocialType() != null) { userVo.setSocialType(user.getSocialType().toString()); } userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); resJsonData.put(Constants.RES_KEY_CONTENTS, userVo); } // 마지막으로 선택한 로그인 정보를 저장한다. @Override @Transactional public void updateLastLogin() { User user = this.getUser(this.webAppUtil.getLoginId()); user.setLastLoginDate(new Date()); this.userRepository.saveAndFlush(user); } // 삭제 할 등급명을 유저가 사용하고 있는지 확인 @Override public boolean useUserLevel(Long levelId) { return this.userMapper.findByLevelId(levelId) > 0; } // 마지막으로 선택한 프로젝트 정보를 저장한다. @Override @Transactional public void updateLastProject(Map resJsonData, UserForm userForm) { User user = this.getUser(this.webAppUtil.getLoginId()); if (userForm.getLastProjectId() != null) { user.setLastProjectId(userForm.getLastProjectId()); } this.userRepository.saveAndFlush(user); // 세션 업데이트 SecurityUtils.setUserToSession(user); // 클라이언트의 사용자 정보 업데이트 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); if (user.getSocialType() != null) { userVo.setSocialType(user.getSocialType().toString()); } userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); resJsonData.put(Constants.RES_KEY_CONTENTS, userVo); } // 마지막으로 선택한 이슈 타입 정보를 저장한다. @Override @Transactional public void updateLastIssueType(Map resJsonData, UserForm userForm) { User user = this.getUser(this.webAppUtil.getLoginId()); if (userForm.getLastIssueTypeId() != null) { user.setLastIssueTypeId(userForm.getLastIssueTypeId()); } this.userRepository.saveAndFlush(user); // 세션 업데이트 SecurityUtils.setUserToSession(user); // 클라이언트의 사용자 정보 업데이트 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); if (user.getSocialType() != null) { userVo.setSocialType(user.getSocialType().toString()); } userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); resJsonData.put(Constants.RES_KEY_CONTENTS, userVo); } // 특정 아이디에 해당하는 사용자 목록을 가져온다. @Override @Transactional(readOnly = true) public List findByIdIn(List userIds) { return this.userRepository.findByIdIn(userIds); } // 관리자 목록을 가져온다. @Override @Transactional(readOnly = true) public List findAdmin() { return this.userRepository.findAdmin(MngPermission.makeAllPermission()); } // 해당 사용자가 관리하는 업무 공간을 마지막으로 접근한 업무 공간 정보로 업데이트한다. @Override @Transactional public void updateLastDefaultWorkspace(Workspace workspace, User user) { if (user.getLastWorkspaceId().equals(workspace.getId())) { // 해당 사용자가 관리하는 업무 공간을 마지막 접근 업무 공간로 변경한다. this.updateLastMyWorkspace(user); } else { // 알림은 표시하지 않고 화면상에서 해당 업무 공간가 안보이게 한다. this.simpMessagingTemplate.convertAndSendToUser(user.getAccount(), "/notification/workspace-update", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspace.getName())); } } // 해당 사용자가 관리하는 업무 공간를 마지막 접근 업무 공간로 변경한다. @Override @Transactional public void updateLastMyWorkspace(User user) { UserWorkspace userManagerWorkspaceConnect = this.userWorkspaceService.findMyWorkspace(user.getId()); if(userManagerWorkspaceConnect != null) { Workspace userManagerWorkspace = userManagerWorkspaceConnect.getWorkspace(); user.setLastWorkspaceId(userManagerWorkspace.getId()); this.userRepository.saveAndFlush(user); } } // 프로젝트에 참여하는 일반 사용자 목록을 가져온다. @Override @Transactional(readOnly = true) public void findProjectMember(Map resJsonData, UserCondition userCondition) { ProjectRole projectRole = this.projectRoleService.findByProjectIdAndRoleType(userCondition.getProjectId(), ProjectRole.TYPE_DEFAULT); List projectRoleUsers = this.projectRoleUserService.findByProjectRoleId(projectRole.getId()); List userVos = Lists.newArrayList(); for (ProjectRoleUser projectRoleUser : projectRoleUsers) { UserVo userVo = ConvertUtil.copyProperties(projectRoleUser.getUser(), UserVo.class, "password"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVos.add(userVo); } resJsonData.put(Constants.RES_KEY_CONTENTS, userVos); } // 회원 탈퇴 @Override @Transactional public void withDrawUser() { User user = this.getUser(this.webAppUtil.getLoginId()); // 이메일 발송에 사용할 정보 미리 추출 Map userMap = new HashMap<>(); userMap.put("name", user.getName()); userMap.put("account", user.getAccount()); UserWorkspace userWorkspace = this.userWorkspaceService.findMyWorkspace(user.getId()); if (userWorkspace != null) { Workspace myWorkspace = userWorkspace.getWorkspace(); // 사용자가 관리하는 업무 공간에 있는 정보를 삭제한다. this.workspaceService.removeWorkspace(myWorkspace, user); } // 참여하고 있는 다른 업무 공간에서 담당자 정보를 삭제한다. this.removeOtherWorkspaceAssignee(user); // 회원 탈퇴 정보를 저장한다. this.userWithDrawService.addUserWithDraw(user); // 사용자 개인 정보를 마스킹/암호화 처리한다. this.initPrivateUser(user); UserCondition userCondition = new UserCondition(); userCondition.setId(user.getId()); userCondition.setAccount(user.getAccount()); // 회원 탈퇴를 진행하면 연관 테이블 정보를 삭제한다. this.userMapper.deleteCascadeUser(userCondition); // 회원 탈퇴 알림 메일 전송 this.systemEmailService.directEmail(new String[]{MapUtil.getString(userMap, "account")}, EmailType.USER_WITH_DRAW, userMap, null); } // 사용자 개인 정보를 마스킹 처리한다. private void initPrivateUser(User user) { // 프로필 초기화 user.setProfile(User.DEFAULT_PROFILE); // 프로필이 올라가 있을 경우 삭제해준다. if (!StringUtils.isEmpty(user.getAwsKey())) { this.removeProfile(user.getAwsKey()); } user.setName(CommonUtil.maskingName(user.getName())); user.setStatus(User.USER_STATUS_DEL); this.userRepository.saveAndFlush(user); } // 참여하고 있는 업무 공간에서 프로젝트/이슈 담당자 정보를 삭제한다. private void removeOtherWorkspaceAssignee(User user) { for (UserWorkspace userWorkspace : user.getUserWorkspaces()) { // 참여하고 있는 모든 업무 공간 - 자신이 관리하는 업무 공간은 제외 if (!userWorkspace.getManagerYn()) { Workspace workspace = userWorkspace.getWorkspace(); // 해당 업무 공간에 존재하는 모든 프로젝트에서 제외 this.projectRoleUserService.withDrawWorkspaceManagerModifyProjectRole(workspace, user); } } } // 업무 공간에서 제외되거나 업무 공간 삭제시 참여 사용자들의 마지막 접근 업무 공간 정보로 세션 업데이트하기 위해 사용 @Override @Transactional(readOnly = true) public void updateUserSession() { User user = this.getUser(this.webAppUtil.getLoginId()); // 세션 업데이트 SecurityUtils.setUserToSession(user); } // 사용자의 현재 세션 정보를 가져온다. @Override @Transactional(readOnly = true) public User getUserSession(Map resJsonData, HttpServletRequest httpServletRequest) { User user = this.getUser(this.webAppUtil.getLoginId()); UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); // user에서 Permission 값을 가져와서 userVo 에 setPermission 함 userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); UserLevel userLevel = user.getUserLevel(); if (userLevel != null) { userVo.setLevelName(userLevel.getLevelName()); } List departments = this.userDepartmentService.findDepartment(user.getId()); if (departments != null) { userVo.setDepartmentVos(ConvertUtil.convertObjectsToClasses(departments, DepartmentVo.class)); } resJsonData.put(Constants.RES_KEY_CONTENTS, userVo); // 사용자 세션 정보를 분석해서 로그에 남긴다. log.info(ElasticSearchUtil.makeUserSessionHistoryMessage(httpServletRequest, userVo)); return user; } // 해당 시간에 이메일 알림 예정 시간으로 설정한 사용자를 조회한다. @Override @Transactional(readOnly = true) public List findByReservationNotifyTime() { Map conditions = new HashMap<>(); Calendar startCal = Calendar.getInstance(); startCal.setTime(new Date()); startCal.add(Calendar.MINUTE, -3); // 3분전으로.. Calendar endCal = Calendar.getInstance(); endCal.setTime(new Date()); endCal.add(Calendar.MINUTE, 27); // 27분 뒤로.. conditions.put("startTime", DateUtil.convertDateToStr(startCal.getTime(), "HH:mm")); conditions.put("endTime", DateUtil.convertDateToStr(endCal.getTime(), "HH:mm")); List> users = this.userMapper.findByReservationNotifyTime(conditions); List results = Lists.newArrayList(); for (Map user : users) { results.add(MapUtil.getString(user, "account")); } return results; } // 이메일 알림 예정 시간을 실시간으로 설정한 사용자를 조회한다. @Override @Transactional(readOnly = true) public List findByRealTimeNotifyTime() { Map conditions = new HashMap<>(); List> users = this.userMapper.findByRealTimeNotifyTime(conditions); List results = Lists.newArrayList(); for (Map user : users) { results.add(MapUtil.getString(user, "account")); } return results; } // 프로젝트에 참여하는 사용자 정보를 조회한다. @Override @Transactional(readOnly = true) public List> findProjectMember(Project project) { UserCondition userCondition = new UserCondition(); userCondition.setProjectId(project.getId()); return this.userMapper.findProjectMember(userCondition); } // 사용자가 선택한 언어를 저장한다. @Override @Transactional public void updateLanguage(String language) { // 로그인 전일 때는 업데이트 하지 않는다. - 회원 가입 if (this.webAppUtil.getLoginId() == null) { return; } User user = this.getUser(this.webAppUtil.getLoginId()); switch (language) { case "ko_KR": user.setLanguage("ko"); break; case "en_US": user.setLanguage("en"); break; case "ja_JP": user.setLanguage("ja"); break; case "vi_VN": user.setLanguage("vi"); break; default: user.setLanguage("ko"); } this.userRepository.saveAndFlush(user); this.updateUserSession(); } // 사용자 회원 가입 정보를 OWL ITS 관련자들에게 메일로 통지해준다. @Override @Transactional(readOnly = true) public void sendUserJoinStatisticsEmail() { String today = DateUtil.convertDateToYYYYMMDD(new Date()); Date yesterdayFrom = DateUtil.convertStrToDate(DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), -1)) + " 17:00:00"); Date todayTo = DateUtil.convertStrToDate(today + " 23:59:59"); List joinUsers = this.userRepository.findJoinDay(yesterdayFrom, todayTo); List userVos = Lists.newArrayList(); for (User user : joinUsers) { UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVos.add(userVo); } List activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE); Map userMap = new HashMap<>(); userMap.put("today", DateUtil.convertDateToYYYYMMDD(new Date())); userMap.put("totalUserCount", activeUsers.size()); userMap.put("newUserCount", joinUsers.size()); userMap.put("joinUsers", userVos); if (this.userJoinStatisticsEmail != null && !this.userJoinStatisticsEmail.equals("")) { String[] sendEmails = this.userJoinStatisticsEmail.replaceAll("\\p{Z}", "").split(","); List encryptSendEmail = Lists.newArrayList(); for (String sendEmail : sendEmails) { encryptSendEmail.add(CommonUtil.encryptAES128(sendEmail)); } // 결제 성공 메일 발송 this.systemEmailService.directEmail(encryptSendEmail.toArray(new String[encryptSendEmail.size()]), EmailType.USER_JOIN_STATISTICS, userMap, null); } } // 전체 사용자, 프로젝트 수, 이슈 수를 이메일로 보낸다. @Override @Transactional(readOnly = true) public void sendTotalStatisticsEmail() { String today = DateUtil.convertDateToYYYYMMDD(new Date()); long issueCount = this.issueService.count(); long projectCount = this.projectService.count(); List activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE); Map userMap = new HashMap<>(); userMap.put("today", DateUtil.convertDateToYYYYMMDD(new Date())); userMap.put("issueCount", CommonUtil.getDecimalFormat(issueCount)); userMap.put("projectCount", CommonUtil.getDecimalFormat(projectCount)); userMap.put("userCount", CommonUtil.getDecimalFormat(activeUsers.size())); if (this.totalStatisticsEmail != null && !this.totalStatisticsEmail.equals("")) { String[] sendEmails = this.totalStatisticsEmail.replaceAll("\\p{Z}", "").split(","); List encryptSendEmail = Lists.newArrayList(); for (String sendEmail : sendEmails) { encryptSendEmail.add(CommonUtil.encryptAES128(sendEmail)); } // 결제 성공 메일 발송 this.systemEmailService.directEmail(encryptSendEmail.toArray(new String[encryptSendEmail.size()]), EmailType.TOTAL_STATISTICS, userMap, null); } } // 모든 업무 공간의 사용자 목록을 가져온다. @Override @Transactional(readOnly = true) public List findByAllWorkspace(Map resJsonData, UserCondition condition, Pageable pageable) { condition.setPage(pageable.getPageNumber() * pageable.getPageSize()); condition.setPageSize(pageable.getPageSize()); // 해당 업무공간에 참여하고 있는 사용자는 제외한다. List userWorkspaces = this.userWorkspaceService.findByWorkspaceId(this.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); for (UserWorkspace userWorkspace : userWorkspaces) { condition.addExcludeIds(userWorkspace.getUser().getId()); } List> results = this.userMapper.findByAllWorkspace(condition); Long totalUsersCount = this.userMapper.countByAllWorkspace(condition); // 검색 결과를 UserVo 로 변환한다. return this.convertUserVoToMap(results, totalUsersCount, pageable, resJsonData); } // 검색 결과를 UserVo 로 변환한다. private List convertUserVoToMap(List> results, Long totalUsersCount, Pageable pageable, Map resJsonData) { List userVos = Lists.newArrayList(); for (Map result : results) { UserVo userVo = ConvertUtil.convertMapToClass(result, UserVo.class); userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVos.add(userVo); } int totalPage = (int) Math.ceil((totalUsersCount - 1) / pageable.getPageSize()) + 1; resJsonData.put(Constants.RES_KEY_CONTENTS, userVos); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), totalPage, totalUsersCount)); return userVos; } // 사용자 목록을 엑셀로 다운로드 한다. @Override @Transactional public ModelAndView downloadExcel(Model model) { List users = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE); List userVos = Lists.newArrayList(); for (User user : users) { UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); userVo.setAccount(CommonUtil.decryptAES128(user.getAccount())); userVos.add(userVo); } ExportExcelVo excelInfo = new ExportExcelVo(); excelInfo.setFileName(this.messageAccessor.message("사용자 목록")); excelInfo.addAttrInfos(new ExportExcelAttrVo("id", "고유번호", 6, ExportExcelAttrVo.ALIGN_CENTER)); excelInfo.addAttrInfos(new ExportExcelAttrVo("account", "계정", 6, ExportExcelAttrVo.ALIGN_CENTER)); excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "이름", 6, ExportExcelAttrVo.ALIGN_LEFT)); excelInfo.addAttrInfos(new ExportExcelAttrVo("phone", "전화번호", 20, ExportExcelAttrVo.ALIGN_CENTER)); // 관리자 excelInfo.setDatas(userVos); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } // 사용자 패스워드를 삭제하고 사용자 계정을 복호화한다. @Override @Transactional(readOnly = true) public UserVo removeSensitiveUser(Long userId) { User register = this.getUser(userId); UserVo userVo = ConvertUtil.copyProperties(register, UserVo.class, "password"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVo.setByName(userVo.getName() + "(" + userVo.getAccount() + ")"); return userVo; } /*// 전체 패스워드 업데이트 진행 @Override @Transactional public void updatePassword() { List users = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE); users.parallelStream().forEach(user -> { if (this.passwordEncoder.upgradeEncoding(user.getPassword())) { user.setPassword(this.passwordEncoder.encode(user.getPassword())); } }); this.redisConnectionFactory.getConnection().flushAll(); this.userRepository.saveAll(users); }*/ /*// 이벤트 당첨자 목록을 엑셀로 다운로드 한다. @Override @Transactional public ModelAndView downloadExcelEvent(Model model) { List> userMaps = this.userMapper.findEvent(); List userVos = Lists.newArrayList(); for (Map userMap : userMaps) { UserVo userVo = ConvertUtil.convertMapToClass(userMap, UserVo.class); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVos.add(userVo); } ExportExcelVo excelInfo = new ExportExcelVo(); excelInfo.setFileName(this.messageAccessor.message("이벤트 당첨자 목록")); excelInfo.addAttrInfos(new ExportExcelAttrVo("account", "계정", 6, ExportExcelAttrVo.ALIGN_CENTER)); excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "이름", 6, ExportExcelAttrVo.ALIGN_LEFT)); excelInfo.addAttrInfos(new ExportExcelAttrVo("phone", "전화번호", 20, ExportExcelAttrVo.ALIGN_CENTER)); excelInfo.setDatas(userVos); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); }*/ }