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<User, Long, JpaRepository<User, Long>> 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<User, Long> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, String> 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<String, Object> token = (Map<String, Object>) 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<String, String> 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<String, Object> profile = new HashMap<>();
|
Gson gson = new Gson();
|
Object socialProfile = gson.fromJson(response, Object.class);
|
// 해당 소셜에서 받은 사용자 정보를 분석한다.
|
this.parserUserProfile(socialType, profile, (Map<String, Object>) 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<String, Object> profile, Map<String, Object> socialProfile, String accessToken) {
|
String email = "";
|
String name = "";
|
String profilePath = "";
|
Map<String, Object> response;
|
|
switch (socialType) {
|
case GOOGLE:
|
List<Map<String, Object>> emails = (List<Map<String, Object>>) socialProfile.get("emails");
|
Map<String, Object> 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<String, Object>) socialProfile.get("response");
|
email = MapUtil.getString(response, "email");
|
name = MapUtil.getString(response, "name");
|
break;
|
|
case KAKAO:
|
response = (Map<String, Object>) socialProfile.get("properties");
|
name = MapUtil.getString(response, "nickname");
|
Map<String, Object> emailMap = (Map<String, Object>) 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<String, Object> 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<SessionInformation> 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<UserVo> findUser(Map<String, Object> 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<Map<String, Object>> 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<String, Object> resJsonData) {
|
Long loginId = this.webAppUtil.getLoginId();
|
Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<User> findByIdIn(List<Long> userIds) {
|
return this.userRepository.findByIdIn(userIds);
|
}
|
|
// 관리자 목록을 가져온다.
|
@Override
|
@Transactional(readOnly = true)
|
public List<User> 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<String, Object> resJsonData, UserCondition userCondition) {
|
ProjectRole projectRole = this.projectRoleService.findByProjectIdAndRoleType(userCondition.getProjectId(), ProjectRole.TYPE_DEFAULT);
|
List<ProjectRoleUser> projectRoleUsers = this.projectRoleUserService.findByProjectRoleId(projectRole.getId());
|
List<UserVo> 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<String, Object> 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<String, Object> 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<Department> 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<String> findByReservationNotifyTime() {
|
Map<String, Object> 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<Map<String, Object>> users = this.userMapper.findByReservationNotifyTime(conditions);
|
List<String> results = Lists.newArrayList();
|
|
for (Map<String, Object> user : users) {
|
results.add(MapUtil.getString(user, "account"));
|
}
|
|
return results;
|
}
|
|
// 이메일 알림 예정 시간을 실시간으로 설정한 사용자를 조회한다.
|
@Override
|
@Transactional(readOnly = true)
|
public List<String> findByRealTimeNotifyTime() {
|
Map<String, Object> conditions = new HashMap<>();
|
|
List<Map<String, Object>> users = this.userMapper.findByRealTimeNotifyTime(conditions);
|
List<String> results = Lists.newArrayList();
|
|
for (Map<String, Object> user : users) {
|
results.add(MapUtil.getString(user, "account"));
|
}
|
|
return results;
|
}
|
|
// 프로젝트에 참여하는 사용자 정보를 조회한다.
|
@Override
|
@Transactional(readOnly = true)
|
public List<Map<String, Object>> 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<User> joinUsers = this.userRepository.findJoinDay(yesterdayFrom, todayTo);
|
List<UserVo> 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<User> activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
|
|
Map<String, Object> userMap = new HashMap<>();
|
userMap.put("today", DateUtil.convertDateToYYYYMMDD(new Date()));
|
userMap.put("totalUserCount", activeUsers.size());
|
userMap.put("newUserCount", joinUsers.size());
|
userMap.put("joinUsers", userVos);
|
|
String[] sendEmails = this.userJoinStatisticsEmail.replaceAll("\\p{Z}", "").split(",");
|
List<String> 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<User> activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
|
|
Map<String, Object> 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()));
|
|
String[] sendEmails = this.totalStatisticsEmail.replaceAll("\\p{Z}", "").split(",");
|
List<String> 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<UserVo> findByAllWorkspace(Map<String, Object> resJsonData, UserCondition condition, Pageable pageable) {
|
|
condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
|
condition.setPageSize(pageable.getPageSize());
|
|
// 해당 업무공간에 참여하고 있는 사용자는 제외한다.
|
List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceId(this.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
|
|
for (UserWorkspace userWorkspace : userWorkspaces) {
|
condition.addExcludeIds(userWorkspace.getUser().getId());
|
}
|
|
List<Map<String, Object>> results = this.userMapper.findByAllWorkspace(condition);
|
Long totalUsersCount = this.userMapper.countByAllWorkspace(condition);
|
|
// 검색 결과를 UserVo 로 변환한다.
|
return this.convertUserVoToMap(results, totalUsersCount, pageable, resJsonData);
|
}
|
|
// 검색 결과를 UserVo 로 변환한다.
|
private List<UserVo> convertUserVoToMap(List<Map<String, Object>> results, Long totalUsersCount, Pageable pageable, Map<String, Object> resJsonData) {
|
List<UserVo> userVos = Lists.newArrayList();
|
|
for (Map<String, Object> 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<User> users = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
|
List<UserVo> 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<User> 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<Map<String, Object>> userMaps = this.userMapper.findEvent();
|
List<UserVo> userVos = Lists.newArrayList();
|
|
for (Map<String, Object> 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);
|
}*/
|
}
|