사용자관리 비밀번호 변경 완료

This commit is contained in:
hehihoho3@gmail.com 2025-04-03 17:25:23 +09:00
parent 483ebb1235
commit 7c9f240ace
26 changed files with 760 additions and 347 deletions

View File

@ -1,47 +1,39 @@
package com.itn.admin.cmn.config;
import com.itn.admin.itn.user.mapper.domain.UserVO;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
public class CustomUserDetails implements UserDetails {
private final String username;
private final String password;
private final String userId;
private final String id;
private final Collection<? extends GrantedAuthority> authorities;
private final UserVO user;
public CustomUserDetails(String username, String password, String userId, String id, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.userId = userId;
this.id = id;
this.authorities = authorities;
public CustomUserDetails(UserVO user) {
this.user = user;
}
public String getUserId() {
return userId;
}
public String getId() {
return id;
public UserVO getUser() {
return this.user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
return Collections.singletonList(new SimpleGrantedAuthority(user.getRole().name()));
}
@Override
public String getPassword() {
return password;
return user.getPassword();
}
@Override
public String getUsername() {
return username;
return user.getUserId(); // userName으로 바꿔도 무관
}
@Override
@ -61,6 +53,11 @@ public class CustomUserDetails implements UserDetails {
@Override
public boolean isEnabled() {
return true;
return "Y".equalsIgnoreCase(Optional.ofNullable(user.getActiveYn()).orElse("N"));
}
// 도메인 정보 쉽게 접근할 있게 추가적인 getter
public String getId() {
return user.getUniqId();
}
}

View File

@ -2,35 +2,39 @@ package com.itn.admin.cmn.config;
import com.itn.admin.itn.user.mapper.UserMapper;
import com.itn.admin.itn.user.mapper.domain.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
@Slf4j
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
// @Autowired
// private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
UserVO user = userMapper.getUserById(userId);
UserVO user = userMapper.getLoadUserByUsername(userId);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
log.info("DB Password: {}", user.getPassword());
// log.info("Encoded password: {}", passwordEncoder.encode("내가 입력한 비밀번호"));
// log.info("match? {}", passwordEncoder.matches("내가 입력한 비밀번호", user.getPassword()));
// 사용자 권한을 SimpleGrantedAuthority로 변환
GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole().name());
Collection<? extends GrantedAuthority> authorities = Collections.singletonList(authority);
// CustomUserDetails 객체 생성
return new CustomUserDetails(user.getUserId(), user.getPassword(), user.getUserId(), user.getUniqId(), authorities);
return new CustomUserDetails(user);
}
}

View File

@ -1,9 +1,12 @@
package com.itn.admin.cmn.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itn.admin.itn.user.mapper.UserMapper;
import com.itn.admin.itn.user.mapper.domain.UserVO;
import com.itn.admin.itn.user.service.UserService;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@ -22,6 +25,7 @@ import org.springframework.security.web.session.SessionInformationExpiredStrateg
import java.net.URL;
import java.io.PrintWriter;
@Slf4j
@Configuration // Spring의 설정 클래스임을 나타냄
@EnableWebSecurity // Spring Security의 보안 기능을 활성화
public class SecurityConfig {
@ -31,16 +35,23 @@ public class SecurityConfig {
// 사용자 관련 비즈니스 로직을 처리하는 서비스
private final UserService userService;
private final UserMapper userMapper;
// 생성자를 통해 의존성 주입
public SecurityConfig(CustomUserDetailsService customUserDetailsService, UserService userService) {
public SecurityConfig(CustomUserDetailsService customUserDetailsService, UserService userService, UserMapper userMapper) {
this.customUserDetailsService = customUserDetailsService;
this.userService = userService;
this.userMapper = userMapper;
}
// 비밀번호 암호화를 위한 BCryptPasswordEncoder 생성
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
log.info(" + UserRestController 등록된 PasswordEncoder instance: {}", encoder);
return encoder;
}
// Spring Security의 보안 필터 체인을 설정하는 핵심 메서드
@ -130,6 +141,24 @@ public class SecurityConfig {
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
return (request, response, exception) -> {
// 실패 원인을 콘솔에 출력 (디버깅용)
String username = request.getParameter("username"); // 입력한 ID
String rawPassword = request.getParameter("password"); // 입력한 비번
log.info("로그인 실패 - 입력한 ID: [{}]", username);
log.info("로그인 실패 - 입력한 비번(raw): [{}]", rawPassword);
// DB 비밀번호 가져오기 (직접 조회)
UserVO user = userMapper.getLoadUserByUsername(username); // userMapper로 직접 조회해도 OK
if (user != null) {
log.info("로그인 실패 - DB 비번(encoded): [{}]", user.getPassword());
// 비번 매칭 테스트
boolean match = passwordEncoder().matches(rawPassword, user.getPassword());
log.info("비밀번호 일치 여부: [{}]", match);
} else {
log.warn("로그인 실패 - 아이디에 해당하는 사용자 없음");
}
System.out.println("Authentication failed. Exception: " + exception.getMessage());
response.setStatus(HttpStatus.UNAUTHORIZED.value()); // HTTP 상태 코드 401 설정
response.sendRedirect("/user/login?error=true"); // 로그인 페이지로 리다이렉트 (에러 표시)

View File

@ -41,7 +41,7 @@ public class UserInterceptor implements HandlerInterceptor {
if (principal instanceof UserVO) { // principal이 UserVO 타입인 경우
UserVO loggedInUser = (UserVO) principal; // principal 객체를 UserVO 타입으로 변환
id = loggedInUser.getUserId(); // UserVO 객체에서 userId 값을 가져옴
userName = loggedInUser.getUsername(); // UserVO 객체에서 username 값을 가져옴
userName = loggedInUser.getUserName(); // UserVO 객체에서 username 값을 가져옴
} else if (principal instanceof User) { // principal이 User 타입인 경우
User loggedInUser = (User) principal; // principal 객체를 User 타입으로 변환
id = loggedInUser.getUsername(); // User 객체에서 username 값을 가져옴 (userId로 사용)

View File

@ -3,6 +3,7 @@ package com.itn.admin.cmn.util.scheduled;
import com.itn.admin.commute.mapper.domain.CommuteVO;
import com.itn.admin.commute.service.CommuteService;
import com.itn.admin.etc.crawling.morak.service.MorakService;
import com.itn.admin.itn.commute.service.ItnCommuteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
@ -31,6 +32,9 @@ public class ScheduledTasks {
@Autowired
private CommuteService commuteService;
@Autowired
private ItnCommuteService itnCommuteService;
// @Scheduled(cron = "*/50 * * * * MON-FRI")
@Scheduled(cron = "0 40 10 * * MON-FRI")
public void noonJob() throws IOException {
@ -40,6 +44,6 @@ public class ScheduledTasks {
@Scheduled(cron = "0 0 10 * * MON-FRI")
public void commuteJob() throws IOException {
CommuteVO commuteVO = new CommuteVO();
commuteService.transfer(commuteVO);
itnCommuteService.transfer(commuteVO);
}
}

View File

@ -1,10 +1,16 @@
package com.itn.admin.cmn.util.thymeleafUtils;
import com.itn.admin.itn.code.mapper.domain.CodeDetailVO;
import com.itn.admin.itn.code.mapper.domain.CodeVO;
import com.itn.admin.itn.code.server.CodeDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import java.util.List;
// Tag Code Utils
@Component
public class TCodeUtils {
@ -17,5 +23,9 @@ public class TCodeUtils {
};
return codeDetailService.getCodeName(codeGroupId, codeValue);
}
public List<CodeDetailVO> getCodeList(String codeGroupId) {
return codeDetailService.getDetailsByGroupId(codeGroupId);
}
}

View File

@ -1,9 +1,11 @@
package com.itn.admin.cmn.vo;
import lombok.*;
import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Getter
@Setter
public class CmnVO {
@ -17,6 +19,11 @@ public class CmnVO {
private int totalPageCount; // 페이지
private int startPage; // 페이지네이션의 시작 페이지 번호
private int endPage; // 페이지네이션의 페이지 번호
private String frstRegisterId;
private String frstRegistPnttm;
private String lastUpdusrId;
private String lastUpdtPnttm;
// 페이징을 위한 offset과 limit을 계산하는 메서드
public void calculatePaging(int totalRecordCount) {

View File

@ -11,6 +11,5 @@ public interface CommuteService {
Map<String, Object> getList(CommuteVO commuteVO);
Map<String, Object> transfer(CommuteVO commuteVO);
}

View File

@ -39,15 +39,9 @@ public class CommuteServiceImpl implements CommuteService {
@Autowired
CommuteMapper commuteMapper;
@Autowired
ItnCommuteMapper itnCommuteMapper;
@Autowired
HolidayMapper holidayMapper;
@Autowired
CodeDetailService codeDetailService;
@Autowired
private UserMapper userMapper;
@ -70,141 +64,6 @@ public class CommuteServiceImpl implements CommuteService {
@Override
@Transactional
public Map<String, Object> transfer(CommuteVO commuteVO) {
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchYear());
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchMonth());
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchDay());
if(StringUtils.isEmpty(commuteVO.getSearchYear())){
Map<String,String> map = DateUtils.getPreviousBusinessDay();
commuteVO.setSearchYear(map.get("year"));
commuteVO.setSearchMonth(map.get("month"));
commuteVO.setSearchDay(map.get("day"));
}
// 년월일로 select 조건 : 관리자콘솔 정보 select를 위함
CommuteVO searchVO = getSearchVO(commuteVO);
// 임직원 정보 select
List<UserVO> userAllVO = userMapper.findByBiostarIdIsNotNull();
List<HolidayVO> holidayList = holidayMapper.selectHolidaysByDateRange(DateUtils.getYesterdayDate(commuteVO));
String workDt = searchVO.getStartDate().split(" ")[0];
workDt = DateUtils.getFormatToStandardDate(workDt);
Integer groupId = itnCommuteMapper.findByCommuteGroupIdFromItnCommuteWhereWordDt(workDt);
// groupId가 없으면 groupId insert
if(groupId == null) {
ItnCommuteGroupVO itnGroupVO = new ItnCommuteGroupVO();
itnGroupVO.setWorkDt(workDt);
itnCommuteMapper.insertCommuteGroup(itnGroupVO);
groupId = itnGroupVO.getCommuteGroupId();
}
List<ItnCommuteVO> itnCommuteList = new ArrayList<>();
for(UserVO userVO : userAllVO){
ItnCommuteVO itnCommuteVO = new ItnCommuteVO();
itnCommuteVO.setCommuteGroupId(groupId);
String biostarId = userVO.getBiostarId();
searchVO.setBiostarId(biostarId);
commuteVO = commuteMapper.findByUsridAndSrvdtBetween(searchVO);
// 휴가 유형 확인
Optional<HolidayVO> holidayOpt = holidayList.stream()
.filter(h -> h.getUserId().equals(userVO.getGwId()))
.findFirst();
String holidayType = holidayOpt.map(h -> HolidayAnalyzer.determineHolidayType(h)).orElse("");
itnCommuteVO.setUniqId(userVO.getUniqId());
itnCommuteVO.setWorkDt(workDt);
itnCommuteVO.setHoliCode(holidayType);
if(commuteVO == null){
itnCommuteList.add(itnCommuteVO);
continue;
}
// 지각 체크;
itnCommuteVO.setStartTime(commuteVO.getFirstActivityTime().split(" ")[1]);
itnCommuteVO.setStartRslt(this.getLateChk(commuteVO.getFirstActivityTime()));
// 조기퇴근 체크
itnCommuteVO.setEndTime(commuteVO.getLastActivityTime().split(" ")[1]);
itnCommuteVO.setEndRslt(this.getLeaveWorkEarly(commuteVO.getLastActivityTime()));
itnCommuteList.add(itnCommuteVO);
}
log.info("itnCommuteList.size() :: [{}]", itnCommuteList.size());
itnCommuteMapper.upsertCommuteList(itnCommuteList);
/*
List<CommuteVO> commuteList = makeList(commuteVO);
String startDate = commuteVO.getStartDate();
String workDt = startDate.split(" ")[0];
workDt = DateUtils.getFormatToStandardDate(workDt);
Integer groupId = itnCommuteMapper.findByCommuteGroupIdFromItnCommuteWhereWordDt(workDt);
// groupId가 없으면 groupId insert
if(groupId == null) {
ItnCommuteGroupVO itnGroupVO = new ItnCommuteGroupVO();
itnGroupVO.setWorkDt(workDt);
itnCommuteMapper.insertCommuteGroup(itnGroupVO);
groupId = itnGroupVO.getCommuteGroupId();
}
System.out.println("groupId : "+groupId);
List<ItnCommuteVO> itnCommuteList = new ArrayList<>();
for(CommuteVO vo : commuteList) {
ItnCommuteVO itnCommuteVO = new ItnCommuteVO();
itnCommuteVO.setCommuteGroupId(groupId);
itnCommuteVO.setUniqId(vo.getUniqId());
itnCommuteVO.setWorkDt(workDt);
itnCommuteVO.setUserName(vo.getUsrid());
itnCommuteVO.setUserRank(vo.getPstn());
itnCommuteVO.setStartTime(vo.getFirstActivityTime());
itnCommuteVO.setStartRslt(vo.getFirstActivityTimeMemo());
itnCommuteVO.setEndTime(vo.getLastActivityTime());
itnCommuteVO.setEndRslt(vo.getLastActivityTimeMemo());
itnCommuteList.add(itnCommuteVO);
}
itnCommuteMapper.upsertCommuteList(itnCommuteList);
*/
// controller에 return
Map<String, Object> map = new HashMap<String, Object>();
// map.put("resultList", commuteList);
map.put("commuteVO", commuteVO);
return map;
}
public List<CommuteVO> makeList(CommuteVO commuteVO){
@ -249,7 +108,7 @@ public class CommuteServiceImpl implements CommuteService {
log.info("matchedUser : [{}] : [{}]", matchedUser, t.getUsrid());
if( matchedUser != null ){
t.setUsrid(matchedUser.getUsername());
t.setUsrid(matchedUser.getUserName());
t.setPstn(matchedUser.getUserRank());
t.setUniqId(matchedUser.getUniqId());
}
@ -259,6 +118,7 @@ public class CommuteServiceImpl implements CommuteService {
t.setLastActivityTime(t.getLastActivityTime().split(" ")[1]);
});
commuteList.forEach(t -> log.info("userVO : [{}][{}]", t.getBiostarId(), t) );
// 출근안한사람 체크하기
for (UserEnum user : UserEnum.values()) {
@ -279,6 +139,7 @@ public class CommuteServiceImpl implements CommuteService {
commuteTempVO.setFirstActivityTime("-"); // 기본값 설정
commuteTempVO.setLastActivityTime("-"); // 기본값 설정
commuteList.add(commuteTempVO); // 수정된 리스트에 추가
log.info(" : commuteTempVO : [{}]", commuteTempVO);
}
}
@ -286,7 +147,7 @@ public class CommuteServiceImpl implements CommuteService {
commuteList.forEach(commute -> System.out.println(commute.toString()));
commuteList.removeIf(t -> "유인식".equals(t.getUsrid())
commuteList.removeIf(t -> "&@~Z/Uti3tzksl96ByRRZT7AQ==".equals(t.getUsrid())
|| "itn6".equals(t.getUsrid())
);

View File

@ -3,6 +3,7 @@ package com.itn.admin.commute.web;
import com.itn.admin.cmn.msg.RestResponse;
import com.itn.admin.commute.mapper.domain.CommuteVO;
import com.itn.admin.commute.service.CommuteService;
import com.itn.admin.itn.commute.service.ItnCommuteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
@ -25,15 +26,22 @@ public class CommuteRestController {
private CommuteService commuteService;
private ItnCommuteService itnCommuteService;
@Autowired
public void setCommuteService(CommuteService commuteService) {
this.commuteService = commuteService;
}
@Autowired
public void setItnCommuteService(ItnCommuteService itnCommuteService) {
this.itnCommuteService = itnCommuteService;
}
@GetMapping(value = "/api/commute/transfer")
public ResponseEntity<RestResponse> list(@ModelAttribute("commuteVO") CommuteVO commuteVO, Model model) {
// 서비스 메서드 호출
Map<String, Object> resultMap = commuteService.transfer(commuteVO);
Map<String, Object> resultMap = itnCommuteService.transfer(commuteVO);
return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK,"성공적으로 조회했습니다.",resultMap));
/*
*/
@ -77,7 +85,7 @@ public class CommuteRestController {
// commuteService.transfer 호출
try {
Map<String, Object> dailyResult = commuteService.transfer(commuteVO);
Map<String, Object> dailyResult = itnCommuteService.transfer(commuteVO);
resultMap.put(formattedDate, dailyResult);
log.info(":: Processed date :: [{}] - Result: [{}]", formattedDate, dailyResult);
} catch (Exception e) {
@ -140,7 +148,7 @@ public class CommuteRestController {
// commuteService.transfer 호출
try {
Map<String, Object> dailyResult = commuteService.transfer(commuteVO);
Map<String, Object> dailyResult = itnCommuteService.transfer(commuteVO);
resultMap.put(formattedDate, dailyResult);
log.info(":: Processed date :: [{}] - Result: [{}]", formattedDate, dailyResult);
} catch (Exception e) {

View File

@ -6,7 +6,7 @@ import lombok.*;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CodeVO {
public class CodeVO extends CodeDetailVO {
private String codeGroupId;
private String codeGroupName;
private String description;

View File

@ -2,6 +2,7 @@ package com.itn.admin.itn.code.server;
import com.itn.admin.cmn.msg.RestResponse;
import com.itn.admin.itn.code.mapper.domain.CodeDetailVO;
import com.itn.admin.itn.code.mapper.domain.CodeVO;
import java.util.List;
@ -24,4 +25,5 @@ public interface CodeDetailService {
void deleteCodeDetail(String codeGroupId, String codeId);
String getCodeName(String codeGroupId, String codeId);
}

View File

@ -6,6 +6,7 @@ import com.itn.admin.cmn.config.SecurityUtil;
import com.itn.admin.cmn.msg.RestResponse;
import com.itn.admin.itn.code.mapper.CodeDetailMapper;
import com.itn.admin.itn.code.mapper.domain.CodeDetailVO;
import com.itn.admin.itn.code.mapper.domain.CodeVO;
import com.itn.admin.itn.code.server.CodeDetailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View File

@ -1,5 +1,6 @@
package com.itn.admin.itn.commute.service;
import com.itn.admin.commute.mapper.domain.CommuteVO;
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
import com.itn.admin.itn.commute.mapper.domain.ItnCommuteBackVO;
@ -12,4 +13,6 @@ public interface ItnCommuteService {
Map<String, Object> getList(ItnCommuteBackVO itnCommuteBackVO);
Map<String, Object> getCommuteList(ItnCommuteVO itnCommuteVO);
Map<String, Object> transfer(CommuteVO commuteVO);
}

View File

@ -1,7 +1,13 @@
package com.itn.admin.itn.commute.service.impl;
import com.itn.admin.cmn.util.DateUtils;
import com.itn.admin.commute.mapper.CommuteMapper;
import com.itn.admin.commute.mapper.domain.CommuteVO;
import com.itn.admin.commute.mapper.domain.ItnCommuteGroupVO;
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
import com.itn.admin.commute.service.HolidayAnalyzer;
import com.itn.admin.gw.holiday.mapper.HolidayMapper;
import com.itn.admin.gw.holiday.mapper.domain.HolidayVO;
import com.itn.admin.itn.code.mapper.domain.CodeDetailVO;
import com.itn.admin.itn.code.server.CodeDetailService;
import com.itn.admin.itn.commute.mapper.ItnCommuteMapper;
@ -13,19 +19,20 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.*;
@Slf4j
@Service
public class ItnCommuteServiceImpl implements ItnCommuteService {
@Autowired
CommuteMapper commuteMapper;
@Autowired
ItnCommuteMapper itnCommuteMapper;
@ -33,6 +40,9 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
@Autowired
CodeDetailService codeDetailService;
@Autowired
HolidayMapper holidayMapper;
@Autowired
private UserMapper userMapper;
@ -89,6 +99,142 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
}
@Override
@Transactional
public Map<String, Object> transfer(CommuteVO commuteVO) {
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchYear());
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchMonth());
log.info(" :: commuteVO.getSearchYear() :: [{}]", commuteVO.getSearchDay());
if(StringUtils.isEmpty(commuteVO.getSearchYear())){
Map<String,String> map = DateUtils.getPreviousBusinessDay();
commuteVO.setSearchYear(map.get("year"));
commuteVO.setSearchMonth(map.get("month"));
commuteVO.setSearchDay(map.get("day"));
}
// 년월일로 select 조건 : 관리자콘솔 정보 select를 위함
CommuteVO searchVO = getSearchVO(commuteVO);
// 임직원 정보 select
List<UserVO> userAllVO = userMapper.findByBiostarIdIsNotNull();
List<HolidayVO> holidayList = holidayMapper.selectHolidaysByDateRange(DateUtils.getYesterdayDate(commuteVO));
String workDt = searchVO.getStartDate().split(" ")[0];
workDt = DateUtils.getFormatToStandardDate(workDt);
Integer groupId = itnCommuteMapper.findByCommuteGroupIdFromItnCommuteWhereWordDt(workDt);
// groupId가 없으면 groupId insert
if(groupId == null) {
ItnCommuteGroupVO itnGroupVO = new ItnCommuteGroupVO();
itnGroupVO.setWorkDt(workDt);
itnCommuteMapper.insertCommuteGroup(itnGroupVO);
groupId = itnGroupVO.getCommuteGroupId();
}
List<ItnCommuteVO> itnCommuteList = new ArrayList<>();
for(UserVO userVO : userAllVO){
ItnCommuteVO itnCommuteVO = new ItnCommuteVO();
itnCommuteVO.setCommuteGroupId(groupId);
String biostarId = userVO.getBiostarId();
searchVO.setBiostarId(biostarId);
commuteVO = commuteMapper.findByUsridAndSrvdtBetween(searchVO);
// 휴가 유형 확인
Optional<HolidayVO> holidayOpt = holidayList.stream()
.filter(h -> h.getUserId().equals(userVO.getGwId()))
.findFirst();
String holidayType = holidayOpt.map(h -> HolidayAnalyzer.determineHolidayType(h)).orElse("");
itnCommuteVO.setUniqId(userVO.getUniqId());
itnCommuteVO.setWorkDt(workDt);
itnCommuteVO.setHoliCode(holidayType);
if(commuteVO == null){
itnCommuteList.add(itnCommuteVO);
continue;
}
// 지각 체크;
itnCommuteVO.setStartTime(commuteVO.getFirstActivityTime().split(" ")[1]);
itnCommuteVO.setStartRslt(this.getLateChk(commuteVO.getFirstActivityTime()));
// 조기퇴근 체크
itnCommuteVO.setEndTime(commuteVO.getLastActivityTime().split(" ")[1]);
itnCommuteVO.setEndRslt(this.getLeaveWorkEarly(commuteVO.getLastActivityTime()));
itnCommuteList.add(itnCommuteVO);
}
log.info("itnCommuteList.size() :: [{}]", itnCommuteList.size());
itnCommuteMapper.upsertCommuteList(itnCommuteList);
/*
List<CommuteVO> commuteList = makeList(commuteVO);
String startDate = commuteVO.getStartDate();
String workDt = startDate.split(" ")[0];
workDt = DateUtils.getFormatToStandardDate(workDt);
Integer groupId = itnCommuteMapper.findByCommuteGroupIdFromItnCommuteWhereWordDt(workDt);
// groupId가 없으면 groupId insert
if(groupId == null) {
ItnCommuteGroupVO itnGroupVO = new ItnCommuteGroupVO();
itnGroupVO.setWorkDt(workDt);
itnCommuteMapper.insertCommuteGroup(itnGroupVO);
groupId = itnGroupVO.getCommuteGroupId();
}
System.out.println("groupId : "+groupId);
List<ItnCommuteVO> itnCommuteList = new ArrayList<>();
for(CommuteVO vo : commuteList) {
ItnCommuteVO itnCommuteVO = new ItnCommuteVO();
itnCommuteVO.setCommuteGroupId(groupId);
itnCommuteVO.setUniqId(vo.getUniqId());
itnCommuteVO.setWorkDt(workDt);
itnCommuteVO.setUserName(vo.getUsrid());
itnCommuteVO.setUserRank(vo.getPstn());
itnCommuteVO.setStartTime(vo.getFirstActivityTime());
itnCommuteVO.setStartRslt(vo.getFirstActivityTimeMemo());
itnCommuteVO.setEndTime(vo.getLastActivityTime());
itnCommuteVO.setEndRslt(vo.getLastActivityTimeMemo());
itnCommuteList.add(itnCommuteVO);
}
itnCommuteMapper.upsertCommuteList(itnCommuteList);
*/
// controller에 return
Map<String, Object> map = new HashMap<String, Object>();
// map.put("resultList", commuteList);
map.put("commuteVO", commuteVO);
return map;
}
private String addSeconds(String startTimeStr) {
@ -117,6 +263,58 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
}
// 조기 퇴근 여부를 확인하는 함수
private String getLeaveWorkEarly(String p_lastActivityTime) {
// 마지막 활동 시간을 LocalTime으로 변환
LocalTime activityTime = parseActivityTime(p_lastActivityTime);
// 기준 시간(18:30) 설정
LocalTime checkTime = LocalTime.of(18, 30);
// 활동 시간이 기준 시간보다 이전이면 조기 퇴근
if (activityTime.isBefore(checkTime)) {
return "70"; //""조기퇴근";
}
// 그렇지 않으면 문자열 반환
return "";
}
// 지각 여부를 확인하는 함수
private static String getLateChk(String p_firstActivityTime) {
// 활동 시간을 LocalTime으로 변환
LocalTime activityTime = parseActivityTime(p_firstActivityTime);
// 기준 시간(09:30) 설정
LocalTime checkTime = LocalTime.of(9, 30);
// 활동 시간이 기준 시간보다 이후면 지각
if (activityTime.isAfter(checkTime)) {
return "60"; // 지각 60 반환 (공통코드 COMMUTE)
}
// 그렇지 않으면 문자열 반환
return "";
}
// 입력된 시간 문자열을 LocalTime 객체로 변환하는 함수
private static LocalTime parseActivityTime(String activityTimeString) {
// "yyyy-MM-dd HH:mm:ss" 패턴의 포매터 생성
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 문자열을 LocalDateTime으로 파싱
LocalDateTime dateTime = LocalDateTime.parse(activityTimeString, formatter);
// LocalDateTime에서 시간 부분만 추출하여 반환
return dateTime.toLocalTime();
}
private static CommuteVO getSearchVO(CommuteVO commuteVO) {
commuteVO.setStartDate(commuteVO.getSearchYear()+"-"+ commuteVO.getSearchMonth()+"-"+ commuteVO.getSearchDay()+" 06:00:00");
commuteVO.setEndDate(commuteVO.getSearchYear()+"-"+ commuteVO.getSearchMonth()+"-"+ commuteVO.getSearchDay()+" 23:59:59");
// 테이블명 생성
// biostar(관리자콘솔) 테이블명이 "t_lg년월" 이루어져 있음.
String tableNmM = commuteVO.getSearchMonth().length() <2
? "0"+ commuteVO.getSearchMonth()
: commuteVO.getSearchMonth();
commuteVO.setTableNm("t_lg"+ commuteVO.getSearchYear()+tableNmM);
return commuteVO;
}
}

View File

@ -3,6 +3,7 @@ package com.itn.admin.itn.mjon.spam.mapper.domain;
import com.itn.admin.cmn.vo.CmnVO;
import com.itn.admin.itn.code.mapper.domain.CodeDetailVO;
import lombok.*;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
import java.time.LocalDateTime;
@ -22,7 +23,7 @@ import java.util.List;
*/
@NoArgsConstructor
@AllArgsConstructor
@Builder
@SuperBuilder
@Getter
@Setter
@ToString
@ -38,10 +39,6 @@ public class SpamVO extends CmnVO implements Serializable {
private String chcKeywords; // 스팸에 해당하는 단어
private LocalDateTime timestamp; // 요청 시간
private String categ; // 스팸종류
private String frstRegisterId; // 최초등록자ID
private LocalDateTime frstRegistPnttm; // 최초등록일자
private String lastUpdusrId; // 최종수정자ID
private LocalDateTime lastUpdtPnttm; // 최종수정일자
private List<CodeDetailVO> CodeDetailList; // 최종수정일자

View File

@ -4,24 +4,26 @@ import com.itn.admin.itn.user.mapper.domain.UserVO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.core.annotation.Order;
import java.util.List;
@Mapper
public interface UserMapper {
// UserVO save(UserVO userVO);
@Select("SELECT uniq_id, user_id AS userId, user_pw AS password, user_name AS username, role FROM users WHERE user_id = #{userId}")
UserVO getUserById(String userId);
UserVO getLoadUserByUsername(String userId);
@Select("SELECT uniq_id, user_id AS userId, user_pw AS password, user_name AS username, role FROM users WHERE uniq_id = #{uniqId}")
@Select("SELECT * FROM users WHERE uniq_id = #{uniqId}")
UserVO findById(String uniqId);
@Insert("INSERT INTO users (uniq_id, user_id, user_pw, user_name, role) VALUES (#{uniqId}, #{userId}, #{password}, #{username}, #{role})")
void save(UserVO userVO);
@Select("SELECT * FROM users order by active_yn desc")
List<UserVO> findAll();
List<UserVO> findByBiostarIdIsNotNull();
void updateRole(UserVO user);
@ -33,4 +35,9 @@ public interface UserMapper {
/// 진행중
@Insert("INSERT INTO request_logs (uniqId, FRST_REGIST_PNTTM) VALUES (#{uniqId}, now())")
void requestLogs(String uniqId, String requestURL);
void updateUserInfo(UserVO userVO);
void changepassword(UserVO userVO);
}

View File

@ -1,9 +1,8 @@
package com.itn.admin.itn.user.mapper.domain;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import com.itn.admin.cmn.vo.CmnVO;
import lombok.*;
import lombok.experimental.SuperBuilder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@ -14,8 +13,10 @@ import java.util.Collections;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@ToString
public class UserVO implements UserDetails {
public class UserVO extends CmnVO {
private String uniqId;
private String userId;
private String password;
@ -23,44 +24,10 @@ public class UserVO implements UserDetails {
private String userRank;
private String gwId;
private String biostarId;
private String deptCd;
private String activeYn;
private Role role; // 단일 역할로 변경
private String lastUpdtPnttm;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority(role.name()));
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
private String hireDate; // 입사일
private String resignDate; // 퇴사일
}

View File

@ -18,4 +18,10 @@ public interface UserService {
void loginLog(String id);
void requestLogs(String id, String requestURL);
RestResponse findByUniqId(String uniqId);
RestResponse updateUserInfo(UserVO userVO);
RestResponse changepassword(UserVO userVO);
}

View File

@ -84,5 +84,37 @@ public class UserServiceImpl implements UserService {
// userMapper.requestLogs(id, requestURL);
}
@Override
public RestResponse findByUniqId(String uniqId) {
UserVO user = userMapper.findById(uniqId);
return RestResponse.builder()
.status(HttpStatus.OK) // 200 성공
.data(user)
// .msg("수정되었습니다.")
.build();
}
@Override
public RestResponse updateUserInfo(UserVO userVO) {
userMapper.updateUserInfo(userVO);
return RestResponse.builder()
.status(HttpStatus.OK) // 200 성공
.data(userVO.getUserName())
.msg("수정되었습니다.")
.build();
}
@Override
public RestResponse changepassword(UserVO userVO) {
userMapper.changepassword(userVO);
return RestResponse.builder()
.status(HttpStatus.OK) // 200 성공
.data(userVO.getUserName())
.msg("수정되었습니다.")
.build();
}
}

View File

@ -1,10 +1,15 @@
package com.itn.admin.itn.user.web;
import com.itn.admin.cmn.config.CustomUserDetails;
import com.itn.admin.cmn.msg.RestResponse;
import com.itn.admin.itn.code.mapper.domain.CodeVO;
import com.itn.admin.itn.user.mapper.domain.Role;
import com.itn.admin.itn.user.mapper.domain.UserVO;
import com.itn.admin.itn.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -12,18 +17,54 @@ import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Slf4j
@RestController
public class UserRestController {
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
private final PasswordEncoder passwordEncoder;
public UserRestController(PasswordEncoder passwordEncoder) {
log.info(" + UserRestController 등록된 PasswordEncoder instance: {}", passwordEncoder);
this.passwordEncoder = passwordEncoder;
}
@PostMapping("/user/updateRole")
@ResponseBody
public ResponseEntity<?> updateRole(@RequestParam("uniqId") String uniqId, @RequestParam("role") Role role) {
return ResponseEntity.ok(userService.updateRole(uniqId, role));
}
// 특정 코드 그룹을 ID로 가져오는 메서드
@GetMapping("/api/admin/user/{uniqId}")
public ResponseEntity<?> findByUniqId(@PathVariable String uniqId) {
log.info("getCodeGroupById uniqId={}", uniqId);
return ResponseEntity.ok(userService.findByUniqId(uniqId));
}
// 코드 그룹 수정 메서드
@PutMapping("/api/admin/user/{uniqId}")
public ResponseEntity<RestResponse> updateUserInfo(@PathVariable String uniqId, @RequestBody UserVO userVO) {
return ResponseEntity.ok().body(userService.updateUserInfo(userVO));
}
// 코드 그룹 수정 메서드
@PostMapping("/api/admin/user/{uniqId}/change-password")
public ResponseEntity<RestResponse> changePassword(@PathVariable String uniqId,
@RequestBody UserVO userVO,
@AuthenticationPrincipal CustomUserDetails loginUser) {
String newPassword = passwordEncoder.encode(userVO.getPassword());
UserVO setUserVO = UserVO.builder()
.uniqId(uniqId)
.password(newPassword)
.lastUpdusrId(loginUser.getUser().getUserName())
.build();
return ResponseEntity.ok().body(userService.changepassword(setUserVO));
}
}

View File

@ -5,23 +5,6 @@
<mapper namespace="com.itn.admin.itn.user.mapper.UserMapper">
<select id="findAll" resultType="userVO">
SELECT
uniq_id
, user_id
, user_pw as password
, user_name
, user_rank
, role
, gw_id
, biostar_id
, FRST_REGISTER_ID
, FRST_REGIST_PNTTM
, LAST_UPDUSR_ID
, LAST_UPDT_PNTTM
FROM
users
</select>
<select id="findByBiostarIdIsNotNull" resultType="userVO">
SELECT
@ -42,10 +25,61 @@
where biostar_id is not null
and gw_id is not null
and active_yn = 'Y'
and uniq_id != 'ITN_a1b2c3d4e5' /*대표님 제외*/
</select>
<select id="getLoadUserByUsername" resultType="userVO">
SELECT uniq_id
, user_id
, user_pw AS password
, user_name
, user_rank
, dept_cd
, role
, gw_id AS gwId
, biostar_id
, active_yn
, hire_date
, FRST_REGISTER_ID
, FRST_REGIST_PNTTM
, LAST_UPDUSR_ID
, LAST_UPDT_PNTTM
FROM users
WHERE user_id = #{userId}
</select>
<update id="updateRole" parameterType="userVO">
UPDATE users SET role = #{role} WHERE uniq_id = #{uniqId}
</update>
<update id="updateUserInfo" parameterType="userVO">
UPDATE users SET
user_name = #{username}
, user_rank = #{userRank}
, dept_cd = #{deptCd}
<if test="role != null and role != ''">
,role = #{role}
</if>
<if test="gwId != null and gwId != ''">
,gw_id = #{gwId}
</if>
<if test="biostarId != null and biostarId != ''">
,biostar_id = #{biostarId}
</if>
, active_yn = #{activeYn}
, hire_date = #{hireDate}
, resign_date = #{resignDate}
, LAST_UPDT_PNTTM = now()
WHERE uniq_id = #{uniqId}
</update>
<update id="changepassword" parameterType="userVO">
UPDATE users SET
user_pw = #{password}
, LAST_UPDUSR_ID = #{lastUpdusrId}
, LAST_UPDT_PNTTM = now()
WHERE uniq_id = #{uniqId}
</update>
</mapper>

View File

@ -40,6 +40,14 @@
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
<!-- Add icons to the links using the .nav-icon class
with font-awesome or any other icon font library -->
<li class="nav-item">
<a th:href="@{/commute/list_temp}" class="nav-link">
<!-- <i class="far fa-circle nav-icon"></i>-->
<i class="far fa-clock nav-icon"></i>
<p>출퇴근 관리</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link" >
<i class="nav-icon fas fa-cogs"></i>
@ -48,15 +56,6 @@
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a th:href="@{/commute/list_temp}" class="nav-link">
<!-- <i class="far fa-circle nav-icon"></i>-->
<i class="far fa-clock nav-icon"></i>
<p>출퇴근 관리</p>
</a>
</li>
</ul>
<ul class="nav nav-treeview">
<li class="nav-item">
<a th:href="@{/itn/commute/list_temp}" class="nav-link">

View File

@ -22,7 +22,9 @@
<div th:replace="~{fragments/top_nav :: topFragment}"></div>
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4" th:insert="~{fragments/mainsidebar :: sidebarFragment}"></aside>
<aside class="main-sidebar sidebar-dark-primary elevation-4"
th:insert="~{fragments/mainsidebar :: sidebarFragment}">
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">

View File

@ -180,7 +180,7 @@
</div>
<!-- /.card-header -->
<div class="card-body">
<form id="searchForm" method="GET" th:action="@{/commute/list}">
<form id="searchForm" method="GET" th:action="@{/itn/commute/list}">
<div class="row top_row">
<div class="col-sm-2">
<div class="form-group">

View File

@ -13,9 +13,16 @@
<link rel="stylesheet" th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet" th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet" th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
<script>
</script>
<style>
.cursor-pointer {
cursor: pointer;
}
.cursor-pointer:hover {
text-decoration: underline;
color: #007bff;
}
</style>
</head>
<body layout:fragment="body">
@ -60,66 +67,50 @@
</div>
<!-- /.card-header -->
<div class="card-body">
<!--<form th:id="searchForm" th:name="searchForm" th:action="@{/commute/list}" >
<div class="row">
<div class="col-sm-6" style="max-width: 10%;">
&lt;!&ndash; select &ndash;&gt;
<div class="form-group">
<label></label>
<select th:name="searchYear" th:id="searchYear" class="form-control">
<option th:each="year : ${#numbers.sequence(2023, 2022 + 40)}"
th:value="${year}"
th:text="${year}"
th:selected="${year.toString() == commuteVO.searchYear}"></option>
</select>
</div>
</div>
<div class="col-sm-6" style="max-width: 10%;">
<div class="form-group">
<label></label>
<select th:name="searchMonth" th:id="searchMonth" class="form-control">
<option th:each="month : ${#numbers.sequence(1, 12)}"
th:value="${month}"
th:text="${month}"
th:selected="${month.toString() == commuteVO.searchMonth}"></option>
</select>
</div>
</div>
<div class="col-sm-6" style="max-width: 10%;">
<div class="form-group">
<label></label>
<select th:name="searchDay" th:id="searchDay" class="form-control">
<option th:each="day : ${#numbers.sequence(1, 31)}"
th:value="${day}"
th:text="${day}"
th:selected="${day.toString() == commuteVO.searchDay}"></option>
</select>
</div>
</div>
</div>
</form>-->
<table id="commuteTb" class="table table-bordered table-striped">
<table id="commuteTb" class="table table-bordered table-striped" style="width: 100%;">
<thead>
<tr>
<th></th>
<th>고유ID</th>
<th>id</th>
<th>이름</th>
<th>권한</th>
<th>마지막업뎃일시</th>
<th style="width: 5px;"></th>
<th style="width: auto;">고유ID</th>
<th style="width: auto;">id</th>
<th style="width: auto;">이름</th>
<th style="width: auto;">권한</th>
<th style="width: 100px;">비밀번호 변경</th>
<th style="width: 160px;">마지막업뎃일시</th>
</tr>
</thead>
<tbody>
<tr th:each="row, stat : ${list}">
<td th:text="${stat.count}"></td>
<td th:text="${row.uniqId}"></td>
<td th:text="${row.userId}"></td>
<td th:text="${row.username}"></td>
<td th:text="${stat.count}"/>
<td th:text="${row.uniqId}"/>
<!--<td th:text="${row.userId}"/>
-->
<td th:text="${row.userId}"
th:onclick="editUserInfo([[${row.uniqId}]]);"
class="cursor-pointer"/>
<td>
<span th:text="${row.userName}"
th:onclick="editUserInfo([[${row.uniqId}]]);"
class="cursor-pointer"/>
<span class="badge badge-danger ml-2" th:if="${row.activeYn == 'N'}">퇴사</span>
</td>
<td>
<select th:data-uniqid="${row.uniqId}" onchange="updateRole(this)">
<option th:each="role : ${roles}" th:value="${role}" th:text="${role}" th:selected="${role == row.role}"></option>
<option th:each="role : ${roles}"
th:value="${role}"
th:text="${role}"
th:selected="${role == row.role}">
</option>
</select>
</td>
<td class="text-center">
<button class="btn btn-sm btn-warning"
th:onclick="changePassword([[${row.uniqId}]]);">
비밀번호 변경
</button>
</td>
<td th:text="${row.lastUpdtPnttm}"></td>
</tr>
</tbody>
@ -130,6 +121,7 @@
<th>id</th>
<th>이름</th>
<th>권한</th>
<th>비밀번호 변경</th>
<th>마지막업뎃일시</th>
</tr>
</tfoot>
@ -149,7 +141,99 @@
</div>
<!-- 공통 코드 그룹 수정 모달 -->
<div class="modal fade" id="editUserInfoModal" tabindex="-1" role="dialog" aria-labelledby="editUserInfoModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editUserInfoModalLabel">사용자 정보 수정</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="editUserInfoForm">
<div class="form-group">
<label for="uniqId">고유 ID</label>
<input type="text" class="form-control" id="uniqId" name="uniqId" readonly>
</div>
<div class="form-group">
<label for="userId">ID</label>
<input type="text" class="form-control" id="userId" name="userId" readonly>
</div>
<div class="form-group">
<label for="userName">이름</label>
<input type="text" class="form-control" id="userName" name="userName">
</div>
<div class="form-group">
<label for="userRank">직급</label>
<input type="text" class="form-control" id="userRank" name="userRank" placeholder="직급을 입력해 주세요.">
</div>
<div class="form-group">
<label for="deptCd">부서명</label>
<select class="form-control" id="deptCd" name="deptCd">
<option value="" th:selected>-- 선택 --</option>
<option th:each="code : ${@TCodeUtils.getCodeList('DEPT')}"
th:value="${code.codeId}"
th:text="${code.codeName}">
</option>
</select>
</div>
<div class="form-group">
<label for="activeYn">재직여부</label>
<select class="form-control" id="activeYn" name="activeYn">
<option value="Y">재직</option>
<option value="N">퇴사</option>
</select>
</div>
<div class="form-group">
<label for="hireDate">입사일</label>
<input type="date" id="hireDate" name="hireDate" class="form-control" required>
</div>
<div class="form-group">
<label for="resignDate">퇴사일</label>
<input type="date" id="resignDate" name="resignDate" class="form-control" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" onclick="updateUserInfo()">수정</button>
</div>
</div>
</div>
</div>
<!-- 비밀번호 변경 모달 -->
<div class="modal fade" id="changePasswordModal" tabindex="-1" role="dialog" aria-labelledby="changePasswordModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="changePasswordModalLabel">비밀번호 변경</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="changePasswordForm">
<input type="hidden" id="changePwUniqId" name="uniqId">
<div class="form-group">
<label for="newPassword">새 비밀번호</label>
<input type="password" class="form-control" id="newPassword" name="newPassword" required>
</div>
<div class="form-group">
<label for="confirmPassword">비밀번호 확인</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" onclick="submitPasswordChange()">변경</button>
</div>
</div>
</div>
</div>
<!-- /.content-wrapper -->
<footer class="main-footer"
@ -179,24 +263,128 @@
<script th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script>
$(function () {
const commonExportOptions = {
columns: ':visible',
format: {
body: function (data, row, column, node) {
if ($(node).find('select').length) {
return $(node).find('select option:selected').text();
}
// 태그 제거: 보이는 텍스트만 반환
return $(node).text();
}
}
};
$("#commuteTb").DataTable({
"responsive": true
, "lengthChange": false
, "autoWidth": false
, "pageLength": 20
, "buttons": ["copy", { extend: 'csv', charset: 'UTF-8', bom: true }
, "excel", "pdf", "print", "colvis"]
// , "buttons": ["copy", { extend: 'csv', charset: 'UTF-8', bom: true }
// , "excel", "pdf", "print", "colvis"]
, "buttons": [
{
extend: 'copy',
charset: 'UTF-8',
exportOptions: commonExportOptions
},
{
extend: 'csv',
charset: 'UTF-8',
bom: true,
exportOptions: commonExportOptions
},
{
extend: 'excel',
charset: 'UTF-8',
exportOptions: commonExportOptions
},
{
extend: 'pdf',
charset: 'UTF-8',
exportOptions: commonExportOptions
},
{
extend: 'print',
charset: 'UTF-8',
exportOptions: commonExportOptions
},
"colvis"]
}).buttons().container().appendTo('#commuteTb_wrapper .col-md-6:eq(0)');
});
$( document ).ready( function() {
// $('#searchDay, #searchMonth, #searchYear').on('change', function (){
// $('#searchForm').submit();
// });
} );
window.editUserInfo = function(uniqId) {
console.log('uniqId : ', uniqId);
$.ajax({
url: '/api/admin/user/' + uniqId,
type: 'GET',
success: function(data) {
console.log('data :: ', data);
var dataInfo = data.data
var modalId = '#editUserInfoModal';
$(modalId+' #uniqId').val(dataInfo.uniqId);
$(modalId+' #userId').val(dataInfo.userId);
$(modalId+' #userName').val(dataInfo.username);
$(modalId+' #userRank').val(dataInfo.userRank);
$(modalId+' #deptCd').val(dataInfo.deptCd);
$(modalId+' #hireDate').val(dataInfo.hireDate);
$(modalId+' #resignDate').val(dataInfo.resignDate);
$(modalId+' #activeYn').val(dataInfo.activeYn);
$(modalId).modal('show'); // 모달 표시
},
error: function(xhr) {
alert('정보를 불러오는 데 실패했습니다.');
}
});
}
// 코드 그룹 수정 함수
window.updateUserInfo = function() {
var formId = "#editUserInfoForm";
// value chk
if($(formId+' #activeYn').val() == 'N' && !$('#resignDate').val()){
alert('퇴사자를 처리할 때 퇴사일을 입력해 주세요.');
return false;
}
var formDataArray = $(formId).serializeArray();
var formData = {};
// 배열 데이터를 JSON 객체로 변환
$.each(formDataArray, function(_, field) {
formData[field.name] = field.value;
});
$.ajax({
url: '/api/admin/user/' + formData.uniqId,
type: 'PUT',
data: JSON.stringify(formData),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function(data) {
$('#editUserInfoModal').modal('hide');
fn_successAlert("성공", data.data +'(이)가 '+data.msg);
location.reload();
},
error: function(xhr) {
alert('코드 그룹 수정 중 오류가 발생했습니다.');
console.log('xhr : ', xhr);
}
});
};
});
function updateRole(selectElement) {
var uniqId = $(selectElement).data("uniqid");
@ -225,27 +413,44 @@
});
}
function fn_successAlert(uniqId, role, msg){
$(document).Toasts('create', {
class: 'bg-info',
title: msg,
subtitle: '',
autohide : true,
delay: 3000,
body: uniqId+'의 권한을 \''+ role+'\'으로 수정하였습니다.'
})
window.changePassword = function (uniqId) {
$('#changePwUniqId').val(uniqId);
$('#changePasswordForm')[0].reset();
$('#changePasswordModal').modal('show');
}
function fn_failedAlert(){
$(document).Toasts('create', {
class: 'bg-danger',
title: '실패',
subtitle: '',
autohide : true,
delay: 3000,
body: '관리자에게 문의해 주세요.'
})
window.submitPasswordChange = function () {
const uniqId = $('#changePwUniqId').val();
const pw1 = $('#newPassword').val();
const pw2 = $('#confirmPassword').val();
if (!pw1 || !pw2) {
alert("비밀번호를 입력해주세요.");
return;
}
if (pw1 !== pw2) {
alert("비밀번호가 일치하지 않습니다.");
return;
}
// 서버로 전송
$.ajax({
url: '/api/admin/user/' + uniqId + '/change-password',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ password: pw1 }),
success: function (response) {
alert('비밀번호가 변경되었습니다.');
$('#changePasswordModal').modal('hide');
},
error: function () {
alert('비밀번호 변경 실패. 다시 시도해주세요.');
}
});
}
</script>
</body>