출퇴근관리 게시판 수정 중
This commit is contained in:
parent
ccc1aa283e
commit
483ebb1235
13
pom.xml
13
pom.xml
@ -175,6 +175,19 @@
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 -->
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.jdbc</groupId>
|
||||
<artifactId>ojdbc8</artifactId>
|
||||
<version>21.17.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.oracle.database.nls/orai18n -->
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.nls</groupId>
|
||||
<artifactId>orai18n</artifactId>
|
||||
<version>21.17.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@EnableScheduling
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.itn.admin.itn.commute.mapper") // 패키지 경로를 정확히 지정
|
||||
@MapperScan("com.itn.admin.itn.**.mapper") // 패키지 경로를 정확히 지정
|
||||
//@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
|
||||
public class ItnAdminApplication {
|
||||
|
||||
|
||||
48
src/main/java/com/itn/admin/cmn/config/GwDatabaseConfig.java
Normal file
48
src/main/java/com/itn/admin/cmn/config/GwDatabaseConfig.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.itn.admin.cmn.config;
|
||||
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
|
||||
@Configuration
|
||||
@MapperScan(value = "com.itn.admin.gw.holiday.mapper", sqlSessionFactoryRef = "factory3")
|
||||
class GwDatabaseConfig {
|
||||
|
||||
private final String GW_DATA_SOURCE = "GwDatabase";
|
||||
|
||||
// A database DataSource
|
||||
@Bean(GW_DATA_SOURCE)
|
||||
@ConfigurationProperties(prefix = "spring.gw.datasource")
|
||||
public DataSource CommuteDataSource() {
|
||||
return DataSourceBuilder.create()
|
||||
// .type(HikariDataSource.class)
|
||||
.build();
|
||||
}
|
||||
|
||||
// SqlSessionTemplate 에서 사용할 SqlSession 을 생성하는 Factory
|
||||
@Bean(name = "factory3")
|
||||
public SqlSessionFactory CommuteSqlSessionFactory(@Qualifier(GW_DATA_SOURCE) DataSource dataSource) throws Exception {
|
||||
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
|
||||
sqlSessionFactory.setDataSource(dataSource);
|
||||
sqlSessionFactory.setTypeAliasesPackage("com.itn.admin.gw");
|
||||
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/gw/**/*Mapper.xml"));
|
||||
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
|
||||
return sqlSessionFactory.getObject();
|
||||
}
|
||||
|
||||
// DataSource 에서 Transaction 관리를 위한 Manager 클래스 등록
|
||||
@Bean(name = "sqlSession3")
|
||||
public SqlSessionTemplate sqlSession(SqlSessionFactory sqlSessionFactory) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
@ -22,125 +22,147 @@ import org.springframework.security.web.session.SessionInformationExpiredStrateg
|
||||
import java.net.URL;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@Configuration // Spring의 설정 클래스임을 나타냄
|
||||
@EnableWebSecurity // Spring Security의 웹 보안 기능을 활성화
|
||||
public class SecurityConfig {
|
||||
|
||||
// 사용자 정보를 로드하는 커스텀 서비스 (UserDetailsService 구현체)
|
||||
private final CustomUserDetailsService customUserDetailsService;
|
||||
// 사용자 관련 비즈니스 로직을 처리하는 서비스
|
||||
private final UserService userService;
|
||||
|
||||
// 생성자를 통해 의존성 주입
|
||||
public SecurityConfig(CustomUserDetailsService customUserDetailsService, UserService userService) {
|
||||
this.customUserDetailsService = customUserDetailsService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
// 비밀번호 암호화를 위한 BCryptPasswordEncoder 빈 생성
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
// Spring Security의 보안 필터 체인을 설정하는 핵심 메서드
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// CSRF(Cross-Site Request Forgery) 보호 비활성화 (주로 API 서버나 폼 기반 인증에서 사용)
|
||||
.csrf((csrfConfig) -> csrfConfig.disable())
|
||||
// X-Frame-Options 헤더 비활성화 (iframe 사용 허용)
|
||||
.headers((headerConfig) -> headerConfig.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable()))
|
||||
// HTTP 요청에 대한 권한 설정
|
||||
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
|
||||
.requestMatchers("/user/register").permitAll()
|
||||
.requestMatchers("/accessDenied").permitAll()
|
||||
.requestMatchers("/static/**", "/plugins/**", "/dist/**").permitAll()
|
||||
.requestMatchers("/api/**").permitAll()
|
||||
.requestMatchers("/admin/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated() // 모든 요청에 대해 인증 요구
|
||||
.requestMatchers("/user/register").permitAll() // 회원가입 페이지는 인증 없이 접근 가능
|
||||
.requestMatchers("/accessDenied").permitAll() // 접근 거부 페이지는 인증 없이 접근 가능
|
||||
.requestMatchers("/static/**", "/plugins/**", "/dist/**").permitAll() // 정적 리소스는 인증 없이 접근 가능
|
||||
.requestMatchers("/api/**").permitAll() // API 엔드포인트는 인증 없이 접근 가능
|
||||
.requestMatchers("/admin/**").hasRole("ADMIN") // /admin/** 경로는 ADMIN 역할 필요
|
||||
.anyRequest().authenticated() // 나머지 모든 요청은 인증 필요
|
||||
)
|
||||
// 예외 처리 설정
|
||||
.exceptionHandling((exceptionConfig) -> exceptionConfig
|
||||
.authenticationEntryPoint(customUnauthorizedEntryPoint())
|
||||
.accessDeniedHandler(customAccessDeniedHandler())
|
||||
.authenticationEntryPoint(customUnauthorizedEntryPoint()) // 인증 실패 시 처리
|
||||
.accessDeniedHandler(customAccessDeniedHandler()) // 권한 부족 시 처리
|
||||
)
|
||||
// 폼 로그인 설정
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.loginPage("/user/login")
|
||||
.usernameParameter("username")
|
||||
.passwordParameter("password")
|
||||
.loginProcessingUrl("/login/login-proc")
|
||||
.successHandler(customAuthenticationSuccessHandler())
|
||||
.failureHandler(customAuthenticationFailureHandler())
|
||||
.permitAll()
|
||||
.loginPage("/user/login") // 커스텀 로그인 페이지 경로
|
||||
.usernameParameter("username") // 로그인 폼의 사용자 이름 파라미터 이름
|
||||
.passwordParameter("password") // 로그인 폼의 비밀번호 파라미터 이름
|
||||
.loginProcessingUrl("/login/login-proc") // 로그인 요청을 처리하는 URL
|
||||
.successHandler(customAuthenticationSuccessHandler()) // 로그인 성공 시 핸들러
|
||||
.failureHandler(customAuthenticationFailureHandler()) // 로그인 실패 시 핸들러
|
||||
.permitAll() // 로그인 페이지는 누구나 접근 가능
|
||||
)
|
||||
// 로그아웃 설정
|
||||
.logout((logoutConfig) -> logoutConfig
|
||||
.logoutUrl("/logout")
|
||||
.logoutSuccessUrl("/user/login")
|
||||
.invalidateHttpSession(true) // 세션 무효화
|
||||
.deleteCookies("JSESSIONID") // 쿠키 삭제
|
||||
.permitAll()
|
||||
.logoutUrl("/logout") // 로그아웃 요청 URL
|
||||
.logoutSuccessUrl("/user/login") // 로그아웃 성공 시 리다이렉트 URL
|
||||
.invalidateHttpSession(true) // 로그아웃 시 세션 무효화
|
||||
.deleteCookies("JSESSIONID") // JSESSIONID 쿠키 삭제
|
||||
.permitAll() // 로그아웃은 누구나 요청 가능
|
||||
)
|
||||
// 세션 관리 설정
|
||||
.sessionManagement((sessionManagement) -> sessionManagement
|
||||
.sessionFixation().migrateSession() // 세션 고정 공격 방지
|
||||
.invalidSessionUrl("/user/login?invalidSession=true") // 세션이 유효하지 않을 때 리다이렉트할 URL
|
||||
.maximumSessions(1) // 동시 세션 수 제한
|
||||
.maxSessionsPreventsLogin(false) // 최대 세션 수 초과 시 추가 로그인을 방지
|
||||
.expiredSessionStrategy(sessionExpiredStrategy()) // 세션 만료 전략 설정
|
||||
.sessionFixation().migrateSession() // 세션 고정 공격 방지 (새 세션 생성 후 기존 데이터 복사)
|
||||
.invalidSessionUrl("/user/login?invalidSession=true") // 세션이 유효하지 않을 때 리다이렉트
|
||||
.maximumSessions(1) // 한 사용자당 최대 동시 세션 수 제한 (1개)
|
||||
.maxSessionsPreventsLogin(false) // 최대 세션 초과 시 새 로그인 허용 (기존 세션 만료)
|
||||
.expiredSessionStrategy(sessionExpiredStrategy()) // 세션 만료 시 처리 전략
|
||||
)
|
||||
// 사용자 정보 로드를 위한 서비스 설정
|
||||
.userDetailsService(customUserDetailsService);
|
||||
|
||||
// 설정을 기반으로 SecurityFilterChain 객체 생성 및 반환
|
||||
return http.build();
|
||||
}
|
||||
|
||||
// 세션 만료 시 커스텀 전략을 정의하는 빈
|
||||
@Bean
|
||||
public SessionInformationExpiredStrategy sessionExpiredStrategy() {
|
||||
return new CustomSessionExpiredStrategy("/user/login?expired");
|
||||
return new CustomSessionExpiredStrategy("/user/login?expired"); // 세션 만료 시 리다이렉트 경로 지정
|
||||
}
|
||||
|
||||
// 로그인 성공 시 동작을 커스터마이징하는 핸들러
|
||||
@Bean
|
||||
public AuthenticationSuccessHandler customAuthenticationSuccessHandler() {
|
||||
return (request, response, authentication) -> {
|
||||
// 디버깅 정보를 콘솔에 출력
|
||||
// 인증된 사용자 정보를 가져옴
|
||||
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
|
||||
// String userId = userDetails.getUserId();
|
||||
|
||||
// 개발이 아니면 로그 등록하게 수정 20240522
|
||||
// 요청 URL의 호스트를 추출 (디버깅용)
|
||||
String host_url = new URL(request.getRequestURL().toString()).getHost();
|
||||
System.out.println("host_url : "+ host_url);
|
||||
if(!"localhost".equals(host_url)){
|
||||
System.out.println("host_url : " + host_url);
|
||||
|
||||
// 로컬 환경이 아닌 경우 로그인 로그를 기록 (2024-05-22 수정)
|
||||
if (!"localhost".equals(host_url)) {
|
||||
String id = userDetails.getId();
|
||||
userService.loginLog(id);
|
||||
userService.loginLog(id); // 로그인 기록 저장
|
||||
}
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
response.sendRedirect("/");
|
||||
response.setStatus(HttpStatus.OK.value()); // HTTP 상태 코드 200 설정
|
||||
response.sendRedirect("/"); // 루트 경로로 리다이렉트
|
||||
};
|
||||
}
|
||||
|
||||
// 로그인 실패 시 동작을 커스터마이징하는 핸들러
|
||||
@Bean
|
||||
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
|
||||
return (request, response, exception) -> {
|
||||
// 디버깅 정보를 콘솔에 출력
|
||||
// 실패 원인을 콘솔에 출력 (디버깅용)
|
||||
System.out.println("Authentication failed. Exception: " + exception.getMessage());
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.sendRedirect("/user/login?error=true");
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value()); // HTTP 상태 코드 401 설정
|
||||
response.sendRedirect("/user/login?error=true"); // 로그인 페이지로 리다이렉트 (에러 표시)
|
||||
};
|
||||
}
|
||||
|
||||
// 인증되지 않은 사용자가 보호된 리소스에 접근 시 처리
|
||||
@Bean
|
||||
public AuthenticationEntryPoint customUnauthorizedEntryPoint() {
|
||||
return (request, response, authException) -> {
|
||||
response.sendRedirect("/user/login");
|
||||
response.sendRedirect("/user/login"); // 로그인 페이지로 리다이렉트
|
||||
};
|
||||
}
|
||||
|
||||
// 권한이 부족한 경우 처리
|
||||
@Bean
|
||||
public AccessDeniedHandler customAccessDeniedHandler() {
|
||||
return (request, response, accessDeniedException) -> {
|
||||
response.sendRedirect("/accessDenied");
|
||||
response.sendRedirect("/accessDenied"); // 접근 거부 페이지로 리다이렉트
|
||||
};
|
||||
}
|
||||
|
||||
// 세션 이벤트 발행을 위한 빈 (세션 생성/소멸 감지)
|
||||
@Bean
|
||||
public HttpSessionEventPublisher httpSessionEventPublisher() {
|
||||
return new HttpSessionEventPublisher();
|
||||
}
|
||||
|
||||
// 에러 응답 객체를 정의하는 내부 클래스
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public static class ErrorResponse {
|
||||
private final HttpStatus status;
|
||||
private final String message;
|
||||
private final HttpStatus status; // HTTP 상태 코드
|
||||
private final String message; // 에러 메시지
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package com.itn.admin.cmn.util;
|
||||
|
||||
import com.itn.admin.commute.mapper.domain.CommuteVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@ -9,7 +12,11 @@ import java.util.Map;
|
||||
public class DateUtils {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 전 영업일을 계산하여 년, 월, 일을 Map 형태로 반환하는 메서드.
|
||||
* 현재 날짜에서 하루를 빼고, 주말(토요일 또는 일요일)인 경우 금요일로 조정함.
|
||||
* @return Map<String,String> - "year", "month", "day" 키로 구성된 날짜 정보
|
||||
*/
|
||||
public static Map<String,String> getPreviousBusinessDay(){
|
||||
Map<String,String> map = new HashMap<>();
|
||||
|
||||
@ -32,9 +39,9 @@ public class DateUtils {
|
||||
|
||||
|
||||
// 결과 출력
|
||||
System.out.println("Year: " + year);
|
||||
System.out.println("Month: " + month);
|
||||
System.out.println("Day: " + day);
|
||||
// System.out.println("Year: " + year);
|
||||
// System.out.println("Month: " + month);
|
||||
// System.out.println("Day: " + day);
|
||||
|
||||
map.put("year",year);
|
||||
map.put("month",month);
|
||||
@ -52,4 +59,21 @@ public class DateUtils {
|
||||
// 원하는 형식으로 변환
|
||||
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
}
|
||||
public static String getYesterdayDate(CommuteVO commuteVO) {
|
||||
// commuteVO에서 년, 월, 일을 가져옴
|
||||
String year = commuteVO.getSearchYear();
|
||||
String month = commuteVO.getSearchMonth();
|
||||
String day = commuteVO.getSearchDay();
|
||||
|
||||
// commuteVO의 값으로 LocalDate 생성
|
||||
LocalDate date = LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day));
|
||||
|
||||
// 어제 날짜 계산
|
||||
// LocalDate yesterday = date.minusDays(5);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
|
||||
System.out.println("date.format(formatter) :: "+ date.format(formatter));
|
||||
return date.format(formatter);
|
||||
// return yesterday.format(formatter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -34,7 +34,6 @@ public class ScheduledTasks {
|
||||
// @Scheduled(cron = "*/50 * * * * MON-FRI")
|
||||
@Scheduled(cron = "0 40 10 * * MON-FRI")
|
||||
public void noonJob() throws IOException {
|
||||
System.out.println("It's noon!");
|
||||
morakService.morakMenu();
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package com.itn.admin.cmn.util.thymeleafUtils;
|
||||
|
||||
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;
|
||||
|
||||
@Component
|
||||
public class TCodeUtils {
|
||||
|
||||
@Autowired
|
||||
private CodeDetailService codeDetailService;
|
||||
|
||||
public String getCodeName(String codeGroupId, String codeValue) {
|
||||
if(StringUtils.isEmpty(codeValue)){
|
||||
return codeValue;
|
||||
};
|
||||
return codeDetailService.getCodeName(codeGroupId, codeValue);
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,4 +20,6 @@ import java.util.List;
|
||||
public interface CommuteMapper {
|
||||
|
||||
List<CommuteVO> findAll(CommuteVO commuteVO);
|
||||
|
||||
CommuteVO findByUsridAndSrvdtBetween(CommuteVO commuteVO);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ public class CommuteVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int evtlguid;
|
||||
private String biostarId;
|
||||
private String srvdt;
|
||||
private int devdt;
|
||||
private int devuid;
|
||||
@ -67,4 +68,11 @@ public class CommuteVO implements Serializable {
|
||||
private String searchDay;
|
||||
|
||||
private String tableNm;
|
||||
|
||||
private String uniqId;
|
||||
|
||||
|
||||
private String div1;
|
||||
private String div2;
|
||||
private String div3;
|
||||
}
|
||||
|
||||
@ -16,8 +16,9 @@ public class ItnCommuteVO implements Serializable {
|
||||
|
||||
private Integer commuteId; // 아이디
|
||||
private Integer commuteGroupId; // 그룹 아이디
|
||||
private String name; // 이름
|
||||
private String pstn; // 직위
|
||||
private String uniqId;
|
||||
private String userName; // 이름
|
||||
private String userRank; // 직위
|
||||
private String category; // 구분
|
||||
private String workDt; // 근무일자
|
||||
private String startTime; // 출근시간
|
||||
@ -27,6 +28,7 @@ public class ItnCommuteVO implements Serializable {
|
||||
private String transferDt; // 이관일시
|
||||
private String approver; // 승인자
|
||||
private String approverDt; // 승인일시
|
||||
private String holiCode; // 승인일시
|
||||
|
||||
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ public enum UserEnum {
|
||||
,user8("&@~Z/Uti3tzksl96ByRRZT7AQ==","유인식", "대표")
|
||||
,user9("&@~zO4oUG4dNtiVfnDdfCWM2A==","장영익", "실장")
|
||||
,user10("&@~O+736xoPn0FzMuPQufff1w==","김상훈", "본부장")
|
||||
,user11("&@~E8RB3p27IfRVEhNefMu2Vw==","김보미", "대리")
|
||||
// ,user11("&@~E8RB3p27IfRVEhNefMu2Vw==","김보미", "대리")
|
||||
,user12("&@~47amAycYJ4ZT8BZDi6a2sA==","이설희", "주임")
|
||||
,user13("&@~KPBL+GIy7i2agV7V57MZWg==","정다은", "대리")
|
||||
,user15("&@~S6vaRrMJmeRjp0T8z+/ybg==","강민경", "팀장")
|
||||
|
||||
@ -13,5 +13,4 @@ public interface CommuteService {
|
||||
|
||||
Map<String, Object> transfer(CommuteVO commuteVO);
|
||||
|
||||
Map<String, Object> getCommuteList(ItnCommuteVO itnCommuteVO);
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package com.itn.admin.commute.service;
|
||||
|
||||
import com.itn.admin.gw.holiday.mapper.domain.HolidayVO;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class HolidayAnalyzer {
|
||||
|
||||
// 휴가 유형 상수
|
||||
private static final int FULL_DAY_MINUTES = 480; // 연차
|
||||
private static final int HALF_DAY_MINUTES = 240; // 반차
|
||||
private static final int WORK_START = 510; // 08:30
|
||||
private static final int WORK_END = 1050; // 17:30
|
||||
private static final int HALF_DAY_BREAK = 810; // 13:30
|
||||
|
||||
|
||||
|
||||
// 휴가 유형 판단 메서드
|
||||
public static String determineHolidayType(HolidayVO holi) {
|
||||
// Long 타입을 int로 변환
|
||||
int reqCnt = holi.getReqCnt().intValue();
|
||||
int startTm = holi.getStartTm().intValue();
|
||||
int endTm = holi.getEndTm().intValue();
|
||||
|
||||
// 날짜 차이 계산
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
|
||||
LocalDate startDate = LocalDate.parse(holi.getStartDt(), formatter);
|
||||
LocalDate endDate = LocalDate.parse(holi.getEndDt(), formatter);
|
||||
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1; // 포함된 날짜 수
|
||||
|
||||
// 1일 신청
|
||||
if (daysBetween == 1) {
|
||||
if (reqCnt == FULL_DAY_MINUTES) {
|
||||
return "30"; // "연차";
|
||||
} else if (reqCnt == HALF_DAY_MINUTES) {
|
||||
if (startTm == WORK_START && endTm == HALF_DAY_BREAK) {
|
||||
return "10"; // "오후 반차";
|
||||
} else if (startTm == HALF_DAY_BREAK && endTm == WORK_END) {
|
||||
return "20"; // "오전 반차";
|
||||
} else {
|
||||
return "반차 (시간 확인 필요: " + startTm + " ~ " + endTm + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2일 이상 신청
|
||||
else if (daysBetween > 1) {
|
||||
// 하루 평균 요청 시간 계산
|
||||
int avgMinutesPerDay = reqCnt / (int) daysBetween;
|
||||
|
||||
if (avgMinutesPerDay == FULL_DAY_MINUTES) {
|
||||
return "30"; // "연차 " + daysBetween + "일";
|
||||
} else if (avgMinutesPerDay == HALF_DAY_MINUTES) {
|
||||
if (startTm == WORK_START && endTm == HALF_DAY_BREAK) {
|
||||
return "10"; // "오후 반차 " + daysBetween + "일";
|
||||
} else if (startTm == HALF_DAY_BREAK && endTm == WORK_END) {
|
||||
return "20"; // "오전 반차 " + daysBetween + "일";
|
||||
} else {
|
||||
return "반차 (시간 확인 필요: " + startTm + " ~ " + endTm + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "기타 (" + reqCnt + "분, " + daysBetween + "일)";
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,13 +7,19 @@ import com.itn.admin.commute.mapper.domain.ItnCommuteGroupVO;
|
||||
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
|
||||
import com.itn.admin.commute.mapper.domain.UserEnum;
|
||||
import com.itn.admin.commute.service.CommuteService;
|
||||
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;
|
||||
import com.itn.admin.itn.user.mapper.UserMapper;
|
||||
import com.itn.admin.itn.user.mapper.domain.UserVO;
|
||||
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.text.Collator;
|
||||
import java.time.DayOfWeek;
|
||||
@ -36,9 +42,15 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
@Autowired
|
||||
ItnCommuteMapper itnCommuteMapper;
|
||||
|
||||
@Autowired
|
||||
HolidayMapper holidayMapper;
|
||||
|
||||
@Autowired
|
||||
CodeDetailService codeDetailService;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
|
||||
private static final int PAGE_SIZE = 5;
|
||||
|
||||
@ -55,38 +67,95 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getCommuteList(ItnCommuteVO itnCommuteVO) {
|
||||
// controller에 return
|
||||
|
||||
if(StringUtils.isEmpty(itnCommuteVO.getWorkDt())){
|
||||
|
||||
|
||||
@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();
|
||||
itnCommuteVO.setWorkDt(DateUtils.getFormatToStandardDate(map.get("year") +"-"+map.get("month")+"-"+map.get("day")));
|
||||
commuteVO.setSearchYear(map.get("year"));
|
||||
commuteVO.setSearchMonth(map.get("month"));
|
||||
commuteVO.setSearchDay(map.get("day"));
|
||||
}
|
||||
|
||||
log.info(":: rsnCode01List :: ");
|
||||
List<CodeDetailVO> codeList = codeDetailService.getDetailsByGroupId("COMMUTE");
|
||||
// 년월일로 select 조건 : 관리자콘솔 정보 select를 위함
|
||||
CommuteVO searchVO = getSearchVO(commuteVO);
|
||||
|
||||
List<ItnCommuteVO> itnCommuteList = itnCommuteMapper.findByWorkDtFromItnCommute(itnCommuteVO.getWorkDt());
|
||||
// 임직원 정보 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);
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
Map<String, Object> returnMap = new HashMap<String, Object>();
|
||||
returnMap.put("itnCommuteList", itnCommuteList);
|
||||
returnMap.put("codeList", codeList);
|
||||
return returnMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> transfer(CommuteVO commuteVO) {
|
||||
List<CommuteVO> commuteList = makeList(commuteVO);
|
||||
|
||||
String startDate = commuteVO.getStartDate();
|
||||
|
||||
|
||||
System.out.println("startDate :: "+startDate);
|
||||
String workDt = startDate.split(" ")[0];
|
||||
|
||||
workDt = DateUtils.getFormatToStandardDate(workDt);
|
||||
@ -109,10 +178,11 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
for(CommuteVO vo : commuteList) {
|
||||
ItnCommuteVO itnCommuteVO = new ItnCommuteVO();
|
||||
itnCommuteVO.setCommuteGroupId(groupId);
|
||||
itnCommuteVO.setUniqId(vo.getUniqId());
|
||||
itnCommuteVO.setWorkDt(workDt);
|
||||
|
||||
itnCommuteVO.setName(vo.getUsrid());
|
||||
itnCommuteVO.setPstn(vo.getPstn());
|
||||
itnCommuteVO.setUserName(vo.getUsrid());
|
||||
itnCommuteVO.setUserRank(vo.getPstn());
|
||||
|
||||
itnCommuteVO.setStartTime(vo.getFirstActivityTime());
|
||||
itnCommuteVO.setStartRslt(vo.getFirstActivityTimeMemo());
|
||||
@ -125,11 +195,12 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
|
||||
itnCommuteMapper.upsertCommuteList(itnCommuteList);
|
||||
|
||||
*/
|
||||
|
||||
// controller에 return
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
map.put("resultList", commuteList);
|
||||
// map.put("resultList", commuteList);
|
||||
map.put("commuteVO", commuteVO);
|
||||
return map;
|
||||
}
|
||||
@ -139,38 +210,25 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
public List<CommuteVO> makeList(CommuteVO commuteVO){
|
||||
|
||||
|
||||
if(StringUtils.isNotEmpty(commuteVO.getSearchYear())){
|
||||
if(StringUtils.isEmpty(commuteVO.getSearchYear())){
|
||||
|
||||
}else{
|
||||
Map<String,String> map = DateUtils.getPreviousBusinessDay();
|
||||
|
||||
|
||||
commuteVO.setSearchYear(map.get("year"));
|
||||
commuteVO.setSearchMonth(map.get("month"));
|
||||
commuteVO.setSearchDay(map.get("day"));
|
||||
}
|
||||
|
||||
CommuteVO searchVO = getSearchVO(commuteVO);
|
||||
|
||||
List<CommuteVO> commuteList = commuteMapper.findAll(searchVO);
|
||||
|
||||
commuteVO.setStartDate(commuteVO.getSearchYear()+"-"+commuteVO.getSearchMonth()+"-"+commuteVO.getSearchDay()+" 06:00:00");
|
||||
commuteVO.setEndDate(commuteVO.getSearchYear()+"-"+commuteVO.getSearchMonth()+"-"+commuteVO.getSearchDay()+" 23:59:59");
|
||||
|
||||
// 테이블명 생성
|
||||
String tableNmM = commuteVO.getSearchMonth().length() <2
|
||||
? "0"+commuteVO.getSearchMonth()
|
||||
: commuteVO.getSearchMonth();
|
||||
|
||||
commuteVO.setTableNm("t_lg"+commuteVO.getSearchYear()+tableNmM);
|
||||
|
||||
List<CommuteVO> commuteList = new ArrayList<>();
|
||||
try {
|
||||
// commuteList = commuteMapper.findAll(commuteVO);
|
||||
commuteList = new ArrayList<>(commuteMapper.findAll(commuteVO));
|
||||
}catch (Exception e){
|
||||
// 월별로 테이블이 생성되는데
|
||||
// 없는 테이블을 select하면 Exception이 발생함
|
||||
// 해당 exception 방지용 try-catch
|
||||
}
|
||||
List<HolidayVO> holidayList = holidayMapper.selectHolidaysByDateRange(DateUtils.getYesterdayDate(commuteVO));
|
||||
log.info("holidayList.size() :: [{}] : "+holidayList.size());
|
||||
holidayList.forEach(holidayVO -> {
|
||||
log.info("holidayVO : [{}]",holidayVO);
|
||||
});
|
||||
List<UserVO> userAllVO = userMapper.findByBiostarIdIsNotNull();
|
||||
userAllVO.forEach(userVO -> log.info("userVO : [{}][{}]", userVO.getBiostarId(), userVO) );
|
||||
|
||||
commuteList.stream().forEach(t->{
|
||||
|
||||
@ -183,21 +241,25 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
|
||||
|
||||
if(StringUtils.isNotEmpty(t.getUsrid())){
|
||||
try {
|
||||
UserEnum userEnum = UserEnum.valueOfLabel(t.getUsrid());
|
||||
t.setUsrid(userEnum.userName());
|
||||
t.setPstn(userEnum.pstn());
|
||||
}catch (Exception e){
|
||||
t.setUsrid("힣등록해야함");
|
||||
}
|
||||
}
|
||||
});
|
||||
UserVO matchedUser = userAllVO.stream()
|
||||
.filter(user -> user.getBiostarId().equals(t.getUsrid()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
log.info("===============================================");
|
||||
log.info("matchedUser : [{}] : [{}]", matchedUser, t.getUsrid());
|
||||
|
||||
commuteList.forEach(t-> {
|
||||
if( matchedUser != null ){
|
||||
t.setUsrid(matchedUser.getUsername());
|
||||
t.setPstn(matchedUser.getUserRank());
|
||||
t.setUniqId(matchedUser.getUniqId());
|
||||
}
|
||||
|
||||
}
|
||||
t.setFirstActivityTime(t.getFirstActivityTime().split(" ")[1]);
|
||||
t.setLastActivityTime(t.getLastActivityTime().split(" ")[1]);
|
||||
});
|
||||
|
||||
|
||||
// 출근안한사람 체크하기
|
||||
for (UserEnum user : UserEnum.values()) {
|
||||
// commuteList에서 해당 userName을 가진 CommuteVO 객체가 있는지 검사
|
||||
@ -220,6 +282,8 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
commuteList.forEach(commute -> System.out.println(commute.toString()));
|
||||
|
||||
commuteList.removeIf(t -> "유인식".equals(t.getUsrid())
|
||||
@ -229,6 +293,24 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
return commuteList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static String getRandomTime(String start, String end) {
|
||||
// 문자열을 LocalTime으로 변환
|
||||
LocalTime startTime = LocalTime.parse(start, DateTimeFormatter.ofPattern("HH:mm:ss"));
|
||||
@ -248,39 +330,43 @@ public class CommuteServiceImpl implements CommuteService {
|
||||
return randomTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
|
||||
}
|
||||
|
||||
private String getLeaveWorkEarly(String p_lastActivityTime) {
|
||||
// DateTimeFormatter를 사용하여 String을 LocalDateTime으로 파싱
|
||||
// 입력된 시간 문자열을 LocalTime 객체로 변환하는 함수
|
||||
private static LocalTime parseActivityTime(String activityTimeString) {
|
||||
// "yyyy-MM-dd HH:mm:ss" 패턴의 포매터 생성
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
LocalDateTime firstActivityTime = LocalDateTime.parse(p_lastActivityTime, formatter);
|
||||
// 문자열을 LocalDateTime으로 파싱
|
||||
LocalDateTime dateTime = LocalDateTime.parse(activityTimeString, formatter);
|
||||
// LocalDateTime에서 시간 부분만 추출하여 반환
|
||||
return dateTime.toLocalTime();
|
||||
}
|
||||
|
||||
// 체크하고자 하는 시간 설정 (09:30)
|
||||
// 조기 퇴근 여부를 확인하는 함수
|
||||
private String getLeaveWorkEarly(String p_lastActivityTime) {
|
||||
// 마지막 활동 시간을 LocalTime으로 변환
|
||||
LocalTime activityTime = parseActivityTime(p_lastActivityTime);
|
||||
// 기준 시간(18:30) 설정
|
||||
LocalTime checkTime = LocalTime.of(18, 30);
|
||||
|
||||
// firstActivityTime의 시간 부분만 추출
|
||||
LocalTime activityTime = firstActivityTime.toLocalTime();
|
||||
|
||||
// activityTime이 checkTime보다 이후인지 확인
|
||||
// 활동 시간이 기준 시간보다 이전이면 조기 퇴근
|
||||
if (activityTime.isBefore(checkTime)) {
|
||||
return "조기퇴근";
|
||||
return "70"; //""조기퇴근";
|
||||
}
|
||||
// 그렇지 않으면 빈 문자열 반환
|
||||
return "";
|
||||
}
|
||||
|
||||
// 지각 여부를 확인하는 함수
|
||||
private static String getLateChk(String p_firstActivityTime) {
|
||||
// DateTimeFormatter를 사용하여 String을 LocalDateTime으로 파싱
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
LocalDateTime firstActivityTime = LocalDateTime.parse(p_firstActivityTime, formatter);
|
||||
|
||||
// 체크하고자 하는 시간 설정 (09:30)
|
||||
// 첫 활동 시간을 LocalTime으로 변환
|
||||
LocalTime activityTime = parseActivityTime(p_firstActivityTime);
|
||||
// 기준 시간(09:30) 설정
|
||||
LocalTime checkTime = LocalTime.of(9, 30);
|
||||
|
||||
// firstActivityTime의 시간 부분만 추출
|
||||
LocalTime activityTime = firstActivityTime.toLocalTime();
|
||||
|
||||
// activityTime이 checkTime보다 이후인지 확인
|
||||
// 활동 시간이 기준 시간보다 이후면 지각
|
||||
if (activityTime.isAfter(checkTime)) {
|
||||
return "지각";
|
||||
return "60"; // 지각 시 60 반환 (공통코드 COMMUTE)
|
||||
}
|
||||
// 그렇지 않으면 빈 문자열 반환
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ public class CommuteController {
|
||||
this.commuteService = commuteService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/commute/list")
|
||||
@GetMapping(value = "/commute/list_temp")
|
||||
public String list(@ModelAttribute("commuteVO") CommuteVO commuteVO, Model model) {
|
||||
|
||||
|
||||
@ -32,21 +32,7 @@ public class CommuteController {
|
||||
model.addAttribute("list", resultMap.get("resultList"));
|
||||
model.addAttribute("commuteVO", resultMap.get("commuteVO"));
|
||||
|
||||
return "commute/list_backup";
|
||||
return "commute/list_temp";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/commute/list_test")
|
||||
public String list_test(@ModelAttribute ItnCommuteVO itnCommuteVO, Model model) {
|
||||
// commuteVO.getWorkDt()를 사용하여 workDt 값에 접근할 수 있습니다.
|
||||
|
||||
|
||||
|
||||
|
||||
Map<String, Object> resultMap = commuteService.getCommuteList(itnCommuteVO);
|
||||
//
|
||||
model.addAttribute("list", resultMap.get("itnCommuteList"));
|
||||
model.addAttribute("codeList", resultMap.get("codeList"));
|
||||
|
||||
return "commute/list";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@ -13,9 +14,12 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class CommuteRestController {
|
||||
|
||||
@ -28,35 +32,142 @@ public class CommuteRestController {
|
||||
|
||||
@GetMapping(value = "/api/commute/transfer")
|
||||
public ResponseEntity<RestResponse> list(@ModelAttribute("commuteVO") CommuteVO commuteVO, Model model) {
|
||||
// 시작 날짜 (2024년 8월 1일)
|
||||
// LocalDate startDate = LocalDate.of(2024, 8, 1);
|
||||
// 오늘 날짜
|
||||
// LocalDate endDate = LocalDate.now();
|
||||
|
||||
// 날짜 형식
|
||||
// DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("yyyy");
|
||||
// DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MM");
|
||||
// DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("dd");
|
||||
|
||||
// 날짜 반복 (하루씩 증가)
|
||||
// for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
||||
// // 현재 날짜를 파라미터 형식에 맞게 변환
|
||||
// String year = date.format(yearFormatter);
|
||||
// String month = date.format(monthFormatter);
|
||||
// String day = date.format(dayFormatter);
|
||||
|
||||
// commuteVO.setSearchYear(year);
|
||||
// commuteVO.setSearchMonth(month);
|
||||
// commuteVO.setSearchDay(day);
|
||||
|
||||
// 서비스 메서드 호출
|
||||
Map<String, Object> resultMap = commuteService.transfer(commuteVO);
|
||||
|
||||
// 결과 출력 (디버깅용)
|
||||
// System.out.println("Date: " + date + ", Result: " + resultMap);
|
||||
// }
|
||||
|
||||
// return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK,"성공적으로 조회했습니다.",""));
|
||||
// 서비스 메서드 호출
|
||||
Map<String, Object> resultMap = commuteService.transfer(commuteVO);
|
||||
return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK,"성공적으로 조회했습니다.",resultMap));
|
||||
/*
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@GetMapping(value = "/api/commute/transferMonth")
|
||||
public ResponseEntity<RestResponse> transferMonth(@ModelAttribute("commuteVO") CommuteVO commuteVO, Model model) {
|
||||
// 서비스 메서드 호출
|
||||
|
||||
// 연도와 월 추출
|
||||
String year = commuteVO.getSearchYear();
|
||||
String month = commuteVO.getSearchMonth();
|
||||
|
||||
// 입력 데이터 로그 출력
|
||||
log.info(":: commuteVO.getSearchYear() :: [{}]", year);
|
||||
log.info(":: commuteVO.getSearchMonth() :: [{}]", month);
|
||||
log.info(":: commuteVO.getSearchDay() :: [{}]", commuteVO.getSearchDay());
|
||||
|
||||
// 결과 Map 초기화
|
||||
Map<String, Map<String, Object>> resultMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
// YearMonth 객체 생성 (해당 월의 정보를 관리)
|
||||
YearMonth yearMonth = YearMonth.of(Integer.parseInt(year), Integer.parseInt(month));
|
||||
int daysInMonth = yearMonth.lengthOfMonth(); // 해당 월의 총 일수 (예: 3월 → 31일)
|
||||
|
||||
// 날짜 포맷터 (키로 사용할 날짜 형식: "yyyy-MM-dd")
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
// 1일부터 마지막 날까지 순회
|
||||
for (int day = 1; day <= daysInMonth; day++) {
|
||||
// 날짜 설정
|
||||
LocalDate date = yearMonth.atDay(day);
|
||||
String formattedDate = date.format(formatter); // 예: "2025-03-01"
|
||||
|
||||
// commuteVO에 날짜 설정
|
||||
commuteVO.setSearchYear(String.valueOf(date.getYear()));
|
||||
commuteVO.setSearchMonth(String.format("%02d", date.getMonthValue())); // 2자리 형식 (예: "03")
|
||||
commuteVO.setSearchDay(String.format("%02d", date.getDayOfMonth())); // 2자리 형식 (예: "01")
|
||||
|
||||
// commuteService.transfer 호출
|
||||
try {
|
||||
Map<String, Object> dailyResult = commuteService.transfer(commuteVO);
|
||||
resultMap.put(formattedDate, dailyResult);
|
||||
log.info(":: Processed date :: [{}] - Result: [{}]", formattedDate, dailyResult);
|
||||
} catch (Exception e) {
|
||||
log.error(":: Error processing date :: [{}] - Error: [{}]", formattedDate, e.getMessage());
|
||||
resultMap.put(formattedDate, Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// 성공 응답 반환
|
||||
return ResponseEntity.ok()
|
||||
.body(new RestResponse(HttpStatus.OK, "성공적으로 조회했습니다.", resultMap));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Invalid year or month: year=[{}], month=[{}]", year, month);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(new RestResponse(HttpStatus.BAD_REQUEST, "연도 또는 월 형식이 잘못되었습니다.", null));
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing commute transfer: {}", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(new RestResponse(HttpStatus.INTERNAL_SERVER_ERROR, "조회 중 오류가 발생했습니다.", null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GetMapping(value = "/api/commute/transferYear")
|
||||
public ResponseEntity<RestResponse> transferYear(@ModelAttribute("commuteVO") CommuteVO commuteVO, Model model) {
|
||||
// 서비스 메서드 호출
|
||||
String year = commuteVO.getSearchYear();
|
||||
|
||||
// 입력 데이터 로그 출력
|
||||
log.info(":: commuteVO.getSearchYear() :: [{}]", year);
|
||||
log.info(":: commuteVO.getSearchMonth() :: [{}]", commuteVO.getSearchMonth());
|
||||
log.info(":: commuteVO.getSearchDay() :: [{}]", commuteVO.getSearchDay());
|
||||
|
||||
// 결과 Map 초기화
|
||||
Map<String, Map<String, Object>> resultMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 1월부터 12월까지 순회
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (int month = 1; month <= 12; month++) {
|
||||
// YearMonth 객체 생성 (해당 월의 정보를 관리)
|
||||
YearMonth yearMonth = YearMonth.of(Integer.parseInt(year), month);
|
||||
int daysInMonth = yearMonth.lengthOfMonth(); // 해당 월의 총 일수 (예: 3월 → 31일)
|
||||
|
||||
// 날짜 포맷터 (키로 사용할 날짜 형식: "yyyy-MM-dd")
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
// 1일부터 마지막 날까지 순회
|
||||
for (int day = 1; day <= daysInMonth; day++) {
|
||||
// 날짜 설정
|
||||
LocalDate date = yearMonth.atDay(day);
|
||||
String formattedDate = date.format(formatter); // 예: "2025-03-01"
|
||||
|
||||
// commuteVO에 날짜 설정
|
||||
commuteVO.setSearchYear(String.valueOf(date.getYear()));
|
||||
commuteVO.setSearchMonth(String.format("%02d", date.getMonthValue())); // 2자리 형식 (예: "03")
|
||||
commuteVO.setSearchDay(String.format("%02d", date.getDayOfMonth())); // 2자리 형식 (예: "01")
|
||||
|
||||
// commuteService.transfer 호출
|
||||
try {
|
||||
Map<String, Object> dailyResult = commuteService.transfer(commuteVO);
|
||||
resultMap.put(formattedDate, dailyResult);
|
||||
log.info(":: Processed date :: [{}] - Result: [{}]", formattedDate, dailyResult);
|
||||
} catch (Exception e) {
|
||||
log.error(":: Error processing date :: [{}] - Error: [{}]", formattedDate, e.getMessage());
|
||||
resultMap.put(formattedDate, Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// 월별 데이터 추가 (기존 day 대신 month 사용)
|
||||
map.put("month" + month, month);
|
||||
}
|
||||
|
||||
log.info("map :: [{}]", map.toString());
|
||||
// 성공 응답 반환
|
||||
return ResponseEntity.ok()
|
||||
.body(new RestResponse(HttpStatus.OK, "성공적으로 조회했습니다.", resultMap));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Invalid year: year=[{}]", year);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(new RestResponse(HttpStatus.BAD_REQUEST, "연도 형식이 잘못되었습니다.", null));
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing commute transfer: {}", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(new RestResponse(HttpStatus.INTERNAL_SERVER_ERROR, "조회 중 오류가 발생했습니다.", null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.itn.admin.gw.holiday.mapper;
|
||||
|
||||
import com.itn.admin.commute.mapper.domain.CommuteVO;
|
||||
import com.itn.admin.gw.holiday.mapper.domain.HolidayVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : com.itn.admin.gw.holiday.mapper
|
||||
* fileName : HolidayMapper
|
||||
* author : hylee
|
||||
* date : 2025-03-28
|
||||
* description :
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-03-28 hylee 최초 생성
|
||||
*/
|
||||
@Mapper
|
||||
public interface HolidayMapper {
|
||||
|
||||
List<HolidayVO> selectHolidaysByDateRange(String targetDate);
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.itn.admin.gw.holiday.mapper.domain;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* packageName : com.itn.admin.itn.commute.mapper.domain
|
||||
* fileName : MjonMsgVO
|
||||
* author : hylee
|
||||
* date : 2025-02-13
|
||||
* description : 출퇴근 log
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2023-05-09 hylee 최초 생성
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class HolidayVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
// 휴가 키 (Primary Key, Not Null)
|
||||
private String holiSeq;
|
||||
|
||||
// 사원 코드
|
||||
private String empCode;
|
||||
|
||||
// 사용자 ID
|
||||
private String userId;
|
||||
|
||||
// 이름
|
||||
private String name;
|
||||
|
||||
// 기안 사원 코드
|
||||
private String gianEmpCode;
|
||||
|
||||
// 기안자 이름
|
||||
private String gianName;
|
||||
|
||||
// 직급 ID
|
||||
private String posId;
|
||||
|
||||
// 직급 이름
|
||||
private String posName;
|
||||
|
||||
// 부서 ID
|
||||
private String deptId;
|
||||
|
||||
// 부서 이름
|
||||
private String deptName;
|
||||
|
||||
// 승인 상태
|
||||
private String apprStat;
|
||||
|
||||
// 승인 날짜
|
||||
private LocalDate apprDate;
|
||||
|
||||
// 시작 날짜 (yyyy-MM-dd 형식)
|
||||
private String startDt;
|
||||
|
||||
// 종료 날짜 (yyyy-MM-dd 형식)
|
||||
private String endDt;
|
||||
|
||||
// 시작 시간
|
||||
private Long startTm;
|
||||
|
||||
// 종료 시간
|
||||
private Long endTm;
|
||||
|
||||
// 휴가 코드
|
||||
private String holiCode;
|
||||
|
||||
// 휴가 내용
|
||||
private String holiCont;
|
||||
|
||||
// 목적지
|
||||
private String dest;
|
||||
|
||||
// 연락처
|
||||
private String phone;
|
||||
|
||||
// 구분 1
|
||||
private String divi1;
|
||||
|
||||
// 구분 2
|
||||
private String divi2;
|
||||
|
||||
// 구분 3
|
||||
private String divi3;
|
||||
|
||||
// 선택 연도
|
||||
private String selYear;
|
||||
|
||||
// 요청 횟수
|
||||
private Long reqCnt;
|
||||
|
||||
// 기타 횟수
|
||||
private Long etcCnt;
|
||||
|
||||
// 등록 날짜
|
||||
private LocalDate insDate;
|
||||
|
||||
// 휴가 상세 내용
|
||||
private String holiDetail;
|
||||
|
||||
}
|
||||
@ -22,4 +22,6 @@ public interface CodeDetailService {
|
||||
|
||||
// 코드 상세 삭제 메서드
|
||||
void deleteCodeDetail(String codeGroupId, String codeId);
|
||||
|
||||
String getCodeName(String codeGroupId, String codeId);
|
||||
}
|
||||
|
||||
@ -30,6 +30,14 @@ public class CodeDetailServiceImpl implements CodeDetailService {
|
||||
return codeDetailMapper.findById(codeGroupId, codeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCodeName(String codeGroupId, String codeId) {
|
||||
CodeDetailVO codeDetailVO = codeDetailMapper.findById(codeGroupId, codeId);
|
||||
log.info("codeDetailVO: {}", codeDetailVO);
|
||||
return codeDetailVO.getCodeName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RestResponse addCodeDetail(CodeDetailVO codeDetail) {
|
||||
|
||||
@ -72,4 +80,5 @@ public class CodeDetailServiceImpl implements CodeDetailService {
|
||||
public void deleteCodeDetail(String codeGroupId, String codeId){
|
||||
codeDetailMapper.delete(codeGroupId, codeId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.itn.admin.itn.commute.service;
|
||||
|
||||
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
|
||||
import com.itn.admin.itn.commute.mapper.domain.ItnCommuteBackVO;
|
||||
|
||||
import java.util.Map;
|
||||
@ -9,4 +10,6 @@ public interface ItnCommuteService {
|
||||
|
||||
|
||||
Map<String, Object> getList(ItnCommuteBackVO itnCommuteBackVO);
|
||||
|
||||
Map<String, Object> getCommuteList(ItnCommuteVO itnCommuteVO);
|
||||
}
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
package com.itn.admin.itn.commute.service.impl;
|
||||
|
||||
import com.itn.admin.cmn.util.DateUtils;
|
||||
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
|
||||
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;
|
||||
import com.itn.admin.itn.commute.mapper.domain.ItnCommuteBackVO;
|
||||
import com.itn.admin.itn.commute.service.ItnCommuteService;
|
||||
import com.itn.admin.itn.user.mapper.UserMapper;
|
||||
import com.itn.admin.itn.user.mapper.domain.UserVO;
|
||||
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.thymeleaf.util.StringUtils;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@ -24,6 +30,12 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
|
||||
@Autowired
|
||||
ItnCommuteMapper itnCommuteMapper;
|
||||
|
||||
@Autowired
|
||||
CodeDetailService codeDetailService;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
private static final int PAGE_SIZE = 5;
|
||||
|
||||
public Map<String, Object> getList(ItnCommuteBackVO itnCommuteBackVO) {
|
||||
@ -53,6 +65,30 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getCommuteList(ItnCommuteVO itnCommuteVO) {
|
||||
// controller에 return
|
||||
|
||||
if(StringUtils.isEmpty(itnCommuteVO.getWorkDt())){
|
||||
Map<String,String> map = DateUtils.getPreviousBusinessDay();
|
||||
itnCommuteVO.setWorkDt(DateUtils.getFormatToStandardDate(map.get("year") +"-"+map.get("month")+"-"+map.get("day")));
|
||||
}
|
||||
|
||||
log.info(":: rsnCode01List :: ");
|
||||
List<CodeDetailVO> codeList = codeDetailService.getDetailsByGroupId("COMMUTE");
|
||||
|
||||
List<ItnCommuteVO> itnCommuteList = itnCommuteMapper.findByWorkDtFromItnCommute(itnCommuteVO.getWorkDt());
|
||||
|
||||
|
||||
|
||||
|
||||
Map<String, Object> returnMap = new HashMap<String, Object>();
|
||||
returnMap.put("itnCommuteList", itnCommuteList);
|
||||
returnMap.put("codeList", codeList);
|
||||
return returnMap;
|
||||
}
|
||||
|
||||
|
||||
private String addSeconds(String startTimeStr) {
|
||||
|
||||
|
||||
@ -81,4 +117,6 @@ public class ItnCommuteServiceImpl implements ItnCommuteService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package com.itn.admin.itn.commute.web;
|
||||
|
||||
import com.itn.admin.commute.mapper.domain.ItnCommuteVO;
|
||||
import com.itn.admin.itn.commute.mapper.domain.ItnCommuteBackVO;
|
||||
import com.itn.admin.itn.commute.service.ItnCommuteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.Map;
|
||||
@ -13,15 +15,25 @@ import java.util.Map;
|
||||
@Controller
|
||||
public class ItnCommuteController {
|
||||
|
||||
private ItnCommuteService commuteService;
|
||||
private ItnCommuteService itnCommuteService;
|
||||
|
||||
@Autowired
|
||||
public void setCommuteService(ItnCommuteService commuteService) {
|
||||
this.commuteService = commuteService;
|
||||
this.itnCommuteService = commuteService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/itn/commute/list")
|
||||
public String list(
|
||||
public String list(@ModelAttribute ItnCommuteVO itnCommuteVO, Model model) {
|
||||
|
||||
|
||||
Map<String, Object> resultMap = itnCommuteService.getCommuteList(itnCommuteVO);
|
||||
//
|
||||
model.addAttribute("list", resultMap.get("itnCommuteList"));
|
||||
model.addAttribute("codeList", resultMap.get("codeList"));
|
||||
return "itn/commute/list";
|
||||
}
|
||||
@GetMapping(value = "/itn/commute/list_temp")
|
||||
public String list_temp(
|
||||
@RequestParam(value = "startDate", required = false) String startDate,
|
||||
@RequestParam(value = "endDate", required = false) String endDate,
|
||||
Model model) {
|
||||
@ -39,25 +51,15 @@ public class ItnCommuteController {
|
||||
.build();
|
||||
|
||||
|
||||
Map<String, Object> resultMap = commuteService.getList(itnCommuteBackVO);
|
||||
Map<String, Object> resultMap = itnCommuteService.getList(itnCommuteBackVO);
|
||||
|
||||
model.addAttribute("list", resultMap.get("resultList"));
|
||||
model.addAttribute("commuteVO", itnCommuteBackVO);
|
||||
model.addAttribute("startDate", startDate);
|
||||
model.addAttribute("endDate", endDate);
|
||||
|
||||
return "itn/commute/list";
|
||||
return "itn/commute/list_temp";
|
||||
}
|
||||
// @GetMapping(value = "/itn/commute/list")
|
||||
// public String list(@ModelAttribute("commuteVO") ItnCommuteVO itnCommuteVO, Model model) {
|
||||
//
|
||||
//
|
||||
// Map<String, Object> resultMap = commuteService.getList(itnCommuteVO);
|
||||
//
|
||||
// model.addAttribute("list", resultMap.get("resultList"));
|
||||
// model.addAttribute("commuteVO", itnCommuteVO);
|
||||
//
|
||||
// return "itn/commute/list";
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -21,17 +21,8 @@ public interface UserMapper {
|
||||
void save(UserVO userVO);
|
||||
|
||||
|
||||
default void saveUser(UserVO userVO) {
|
||||
// 역할을 문자열로 변환
|
||||
// String rolesStr = getRolesStr(userVO.getRoles());
|
||||
// userVO.setRolesStr(rolesStr);
|
||||
|
||||
// 사용자 저장
|
||||
save(userVO);
|
||||
}
|
||||
|
||||
|
||||
List<UserVO> findAll(UserVO userVO);
|
||||
List<UserVO> findAll();
|
||||
List<UserVO> findByBiostarIdIsNotNull();
|
||||
|
||||
void updateRole(UserVO user);
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ package com.itn.admin.itn.user.mapper.domain;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
@ -13,11 +14,15 @@ import java.util.Collections;
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class UserVO implements UserDetails {
|
||||
private String uniqId;
|
||||
private String userId;
|
||||
private String password;
|
||||
private String username;
|
||||
private String userName;
|
||||
private String userRank;
|
||||
private String gwId;
|
||||
private String biostarId;
|
||||
private Role role; // 단일 역할로 변경
|
||||
private String lastUpdtPnttm;
|
||||
|
||||
@ -34,7 +39,7 @@ public class UserVO implements UserDetails {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
return userName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,4 +61,6 @@ public class UserVO implements UserDetails {
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public class UserServiceImpl implements UserService {
|
||||
public Map<String, Object> getList(UserVO userVO) {
|
||||
|
||||
|
||||
List<UserVO> userList = userMapper.findAll(userVO);
|
||||
List<UserVO> userList = userMapper.findAll();
|
||||
// controller에 return
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ public class LoginController {
|
||||
UserVO user = new UserVO();
|
||||
user.setUniqId("ITN_"+UUID.randomUUID().toString().replace("-", "").substring(0, 10));
|
||||
user.setUserId(userDTO.getUserId());
|
||||
user.setUsername(userDTO.getUserName());
|
||||
user.setUserName(userDTO.getUserName());
|
||||
user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
|
||||
user.setRole(Role.ROLE_GUEST);
|
||||
|
||||
|
||||
@ -12,6 +12,11 @@ mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
server.servlet.session.timeout=120m
|
||||
|
||||
#sql \ucd9c\ub825 log \uc124\uc815
|
||||
logging.level.root=INFO
|
||||
# log4jdbc ??? ????
|
||||
logging.level.net.sf.log4jdbc=OFF
|
||||
# MyBatis SQL ?? ???
|
||||
logging.level.org.mybatis=info
|
||||
logging.level.jdbc.sqlonly=info
|
||||
logging.level.jdbc.sqltiming=info
|
||||
logging.level.jdbc.audit=info
|
||||
@ -25,65 +30,31 @@ spring.thymeleaf.cache=false
|
||||
|
||||
|
||||
# itn_admin
|
||||
#spring.main.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
||||
spring.main.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.main.datasource.jdbc-url=jdbc:log4jdbc:mysql://119.193.215.98:3306/itn_admin?serverTimezone=Asia/Seoul
|
||||
spring.main.datasource.username=itnAdmin
|
||||
spring.main.datasource.password=itntest123
|
||||
|
||||
#
|
||||
# ??? ?? ?? DB
|
||||
spring.commute.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
#spring.commute.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.200:3312/biostar2_ac?serverTimezone=Asia/Seoul
|
||||
spring.commute.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.31:3312/biostar2_ac?serverTimezone=Asia/Seoul
|
||||
spring.commute.datasource.username=root
|
||||
spring.commute.datasource.password=itntest123
|
||||
|
||||
# agent client 1
|
||||
spring.mjagent.client.one.userid=daltex
|
||||
spring.mjagent.client.one.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.client.one.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.125:3306/mjon_agent?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.client.one.datasource.username=mjonUr_agent
|
||||
spring.mjagent.client.one.datasource.password=mjagent123$
|
||||
|
||||
# agent client 2
|
||||
spring.mjagent.client.two.userid=006star
|
||||
spring.mjagent.client.two.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.client.two.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.60:3303/mjon_agent?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.client.two.datasource.username=mjonUr_agent
|
||||
spring.mjagent.client.two.datasource.password=mjagent123$
|
||||
|
||||
|
||||
# agent client 3
|
||||
spring.mjagent.client.three.userid=hylee250
|
||||
spring.mjagent.client.three.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.client.three.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.31:3307/mjonUr_agent?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.client.three.datasource.username=mjonAgentUr
|
||||
spring.mjagent.client.three.datasource.password=mjonAgentUr!@#$
|
||||
|
||||
# agent client 4
|
||||
spring.mjagent.client.four.userid=dlwnsgh
|
||||
spring.mjagent.client.four.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.client.four.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.31:3308/mjonUr_agent?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.client.four.datasource.username=mjonAgentUr
|
||||
spring.mjagent.client.four.datasource.password=mjonAgentUr!@#$
|
||||
|
||||
# agent client 5
|
||||
spring.mjagent.client.five.userid=dlwldn
|
||||
spring.mjagent.client.five.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.client.five.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.31:3309/mjonUr_agent?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.client.five.datasource.username=mjonAgentUr
|
||||
spring.mjagent.client.five.datasource.password=mjonAgentUr!@#$
|
||||
|
||||
# agent server
|
||||
spring.mjagent.server.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
spring.mjagent.server.datasource.jdbc-url=jdbc:log4jdbc:mysql://192.168.0.60:3303/mjon_agent_server?serverTimezone=Asia/Seoul
|
||||
spring.mjagent.server.datasource.username=mjonUr_agent
|
||||
spring.mjagent.server.datasource.password=mjagent123$
|
||||
spring.mjagent.server.datasource.hikari.minimum-idle=2
|
||||
spring.mjagent.server.datasource.hikari.maximum-pool-size=4
|
||||
# GW DB??
|
||||
spring.gw.datasource.driverClassName=oracle.jdbc.OracleDriver
|
||||
#spring.gw.datasource.url=jdbc:oracle:thin:@192.168.0.98:1521:gw
|
||||
spring.gw.datasource.jdbc-url=jdbc:oracle:thin:@192.168.0.98:1521:gw
|
||||
spring.gw.datasource.username=itn
|
||||
spring.gw.datasource.password=itntest123
|
||||
|
||||
|
||||
|
||||
logging.config=classpath:logback-spring.xml
|
||||
log.config.path=/data/tomcat/tomcat_api_9100_2023_0711/logs
|
||||
log.config.filename=mjonApi_log
|
||||
logging.level.root=INFO
|
||||
logging.config.path=/data/tomcat/tomcat_api_9100_2023_0711/logs
|
||||
logging.config.filename=mjonApi_log
|
||||
#logging.level.root=DEBUG
|
||||
|
||||
logging.level.net.sf.log4jdbc.sql.jdbcapi=DEBUG
|
||||
|
||||
@ -1,68 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
[Layout]
|
||||
%m : 로그내용이 출력
|
||||
%p : trace > debug > info > warn > error 등의 priority 출력
|
||||
%r : 어플리케이션이 시작되어 로깅이벤트가 발생하는 시점까지의 경과시간을 밀리세컨드로 출력
|
||||
%c : 예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
|
||||
%n : 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다
|
||||
%d : 로깅이벤트가 일어나 날짜 출력 ( 프로그램의 실행속도를 느리게 한다.)
|
||||
예) %d{HH:mm:ss} 또는 %d{dd MMMM yyyy HH:mm:ss}
|
||||
%C : 호출자의 클래스명 출력
|
||||
예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
|
||||
%M : 로깅이 발생한 method 이름을 나타냅니다.
|
||||
%F : 로깅이 발생한 프로그램 파일명을 나타냅니다.
|
||||
%l : 로깅이 발생한 caller의 정보를 나타냅니다
|
||||
%L : 로깅이 발생한 caller의 라인수를 나타냅니다
|
||||
%x : 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
|
||||
%X : 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.
|
||||
%% : % 표시를 출력하기 위해 사용한다.
|
||||
%t : 로그이벤트가 발생된 쓰레드의 이름을 출력합니다
|
||||
-->
|
||||
<configuration scan="true" scanPeriod="30 seconds">
|
||||
|
||||
<!--springProfile 태그를 사용하면 logback 설정파일에서 복수개의 프로파일을 설정할 수 있다.-->
|
||||
<!-- <springProfile name="dev">-->
|
||||
<!-- <property resource="logback-dev.properties"/>-->
|
||||
<!-- </springProfile>-->
|
||||
<!-- <springProfile name="prod">-->
|
||||
<!-- <property resource="logback-prod.properties"/>-->
|
||||
<!-- </springProfile>-->
|
||||
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
||||
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
|
||||
|
||||
<!-- <property name="LOG_PATTERN" value="%clr(%d{yyMMdd HH:mm:ss}){green} %clr([%-5level]){cyan} [%thread] %clr([%logger{1}:%line]){magenta} - %msg%n"/>-->
|
||||
|
||||
<property name="LOG_PATTERN"
|
||||
value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg%n%ex"/>
|
||||
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg%n%ex"/>
|
||||
|
||||
<property name="LOG_DIR" value="${log.config.path}"/>
|
||||
<property name="ERR_LOG_FILE_NAME" value="err_log"/><!-- err log file name -->
|
||||
<property name="ERR_LOG_FILE_NAME" value="err_log"/>
|
||||
|
||||
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- rollover daily -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_DIR}/logback-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<!-- or whenever the file size reaches 100MB -->
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>90</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- 에러의 경우 파일에 로그 처리 -->
|
||||
<appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>info</level>
|
||||
@ -71,40 +34,27 @@
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
<!-- Rolling 정책 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_DIR}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<!-- 파일당 최고 용량 kb, mb, gb -->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<!-- Loggers -->
|
||||
<!-- <logger name="org.apache.catalina" level="ERROR">
|
||||
</logger>
|
||||
|
||||
<logger name="org.apache.commons" level="ERROR">
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework" level="DEBUG" >
|
||||
</logger>
|
||||
|
||||
<logger name="egovframework.*" level="DEBUG">
|
||||
</logger>
|
||||
|
||||
<logger name="java.sql" level="DEBUG">
|
||||
</logger>
|
||||
<!-- log4jdbc 비활성화 -->
|
||||
<logger name="net.sf.log4jdbc" level="OFF" />
|
||||
|
||||
<!-- MyBatis SQL 로그 활성화 -->
|
||||
<logger name="org.mybatis.spring" level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="ROLLING" />
|
||||
</logger>
|
||||
|
||||
<logger name="egovframework.sqlmappers" level="TRACE">
|
||||
</logger> -->
|
||||
<!-- 매퍼 로그 상세 출력 -->
|
||||
<logger name="egovframework.sqlmappers" level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="ROLLING" />
|
||||
</logger>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="ROLLING"/>
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.itn.admin.agent.client.one.mapper.AgentCOneMapper">
|
||||
|
||||
<select id="findAll" resultType="agentCOneVO" parameterType="agentCOneVO">
|
||||
/* one findAll */
|
||||
SELECT
|
||||
MSG_TYPE
|
||||
, SEND_STATUS
|
||||
, REQUEST_DATE
|
||||
, RECV_PHONE
|
||||
, SEND_PHONE
|
||||
, MESSAGE
|
||||
from MUNJAON_MSG
|
||||
|
||||
</select>
|
||||
|
||||
<!-- 대량의 데이터를 삽입하는 쿼리 -->
|
||||
<insert id="insertAgents" parameterType="java.util.List">
|
||||
/* two insertAgents */
|
||||
INSERT INTO MUNJAON_MSG
|
||||
(
|
||||
MSG_TYPE
|
||||
, SEND_STATUS
|
||||
, REQUEST_DATE
|
||||
, RECV_PHONE
|
||||
, SEND_PHONE
|
||||
, SUBJECT
|
||||
, MESSAGE
|
||||
, fileName01
|
||||
, fileName02
|
||||
, fileName03
|
||||
)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
#{item.msgType}
|
||||
, #{item.sendStatus}
|
||||
, now()
|
||||
, #{item.recvPhone}
|
||||
, #{item.sendPhone}
|
||||
, #{item.subject}
|
||||
, #{item.message}
|
||||
, #{item.fileName01}
|
||||
, #{item.fileName02}
|
||||
, #{item.fileName03}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 대량 개수만큼 insert 됐는지 확인 -->
|
||||
<select id="countBySendStatusNotAndMsgType" resultType="Integer" parameterType="agentCOneVO">
|
||||
/* one countBySendStatusNotAndMsgType */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG
|
||||
WHERE SEND_STATUS != 1000
|
||||
<if test="msgType != null">
|
||||
and MSG_TYPE = #{msgType}
|
||||
</if>
|
||||
and MESSAGE LIKE CONCAT(#{message}, '%')
|
||||
</select>
|
||||
|
||||
<!-- 리포트할때 현재 데이터가 LOG 테이블에 이동됐는지 확인 -->
|
||||
<select id="countByLogMoveCntWhereMsgTypeAndMessage" resultType="Integer" parameterType="agentCTwoVO">
|
||||
/* one countByLogMoveCntWhereMsgTypeAndMessage */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG_LOG
|
||||
WHERE 1=1
|
||||
-- and SEND_STATUS = 1000
|
||||
and MSG_TYPE = #{msgType}
|
||||
and MESSAGE LIKE CONCAT(#{message}, '%')
|
||||
</select>
|
||||
|
||||
<!-- 리포트할때 전체 데이터가 LOG 테이블에 이동됐느지 확인 -->
|
||||
<select id="countByLogMoveCntWhereMsgType" resultType="Integer" parameterType="agentCTwoVO">
|
||||
/* one countByLogMoveCntWhereMsgType */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG_LOG
|
||||
WHERE MESSAGE LIKE CONCAT(#{message}, '%')
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.itn.admin.agent.client.two.mapper.AgentCTwoMapper">
|
||||
|
||||
<select id="findAll" resultType="agentCTwoVO" parameterType="agentCTwoVO">
|
||||
/* two findAll */
|
||||
SELECT
|
||||
MSG_TYPE
|
||||
, SEND_STATUS
|
||||
, REQUEST_DATE
|
||||
, RECV_PHONE
|
||||
, SEND_PHONE
|
||||
, MESSAGE
|
||||
from MUNJAON_MSG
|
||||
|
||||
</select>
|
||||
|
||||
<!-- 대량의 데이터를 삽입하는 쿼리 -->
|
||||
<insert id="insertAgents" parameterType="java.util.List">
|
||||
/* two insertAgents */
|
||||
INSERT INTO MUNJAON_MSG
|
||||
(
|
||||
MSG_TYPE
|
||||
, SEND_STATUS
|
||||
, REQUEST_DATE
|
||||
, RECV_PHONE
|
||||
, SEND_PHONE
|
||||
, SUBJECT
|
||||
, MESSAGE
|
||||
, fileName01
|
||||
, fileName02
|
||||
, fileName03
|
||||
)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
#{item.msgType}
|
||||
, #{item.sendStatus}
|
||||
, now()
|
||||
, #{item.recvPhone}
|
||||
, #{item.sendPhone}
|
||||
, #{item.subject}
|
||||
, #{item.message}
|
||||
, #{item.fileName01}
|
||||
, #{item.fileName02}
|
||||
, #{item.fileName03}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 대량 개수만큼 insert 됐는지 확인 -->
|
||||
<select id="countBySendStatusNotAndMsgType" resultType="Integer" parameterType="agentCTwoVO">
|
||||
/* two countBySendStatusNotAndMsgType */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG
|
||||
WHERE SEND_STATUS != 1000
|
||||
<if test="msgType != null">
|
||||
and MSG_TYPE = #{msgType}
|
||||
</if>
|
||||
and MESSAGE LIKE CONCAT(#{message}, '%')
|
||||
</select>
|
||||
|
||||
<!-- 리포트할때 현재 데이터가 LOG 테이블에 이동됐는지 확인 -->
|
||||
<select id="countByLogMoveCntWhereMsgTypeAndMessage" resultType="Integer" parameterType="agentCTwoVO">
|
||||
/* two countByLogMoveCntWhereMsgTypeAndMessage */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG_LOG
|
||||
WHERE 1=1
|
||||
-- and SEND_STATUS = 1000
|
||||
and MSG_TYPE = #{msgType}
|
||||
and MESSAGE LIKE CONCAT(#{message}, '%')
|
||||
</select>
|
||||
|
||||
<!-- 리포트할때 전체 데이터가 LOG 테이블에 이동됐느지 확인 -->
|
||||
<select id="findAllLogMoveCnt" resultType="Integer" parameterType="agentCTwoVO">
|
||||
/* two findAllLogMoveCnt */
|
||||
SELECT
|
||||
count(*) as cnt
|
||||
FROM
|
||||
MUNJAON_MSG_LOG
|
||||
WHERE SEND_STATUS = 1000
|
||||
</select>
|
||||
</mapper>
|
||||
@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.itn.admin.agent.server.mapper.AgentSMapper">
|
||||
|
||||
|
||||
<select id="findByCurStateAndUserIdAndSmsTxt" resultType="String" parameterType="agentSVO">
|
||||
|
||||
SELECT SMS_TXT
|
||||
FROM mj_msg_data
|
||||
WHERE SMS_TXT LIKE CONCAT(#{message}, '%')
|
||||
ORDER BY CAST(SUBSTRING(SMS_TXT, LENGTH(#{message}) + 2) AS UNSIGNED) DESC
|
||||
LIMIT 1
|
||||
|
||||
</select>
|
||||
|
||||
<select id="countByCurStateAndUserId" resultType="Integer" parameterType="agentSVO">
|
||||
SELECT
|
||||
count(*)
|
||||
FROM
|
||||
mj_msg_data
|
||||
WHERE
|
||||
CUR_STATE <![CDATA[ < ]]>3
|
||||
AND USER_ID = #{userId}
|
||||
</select>
|
||||
|
||||
<update id="updateReportWhereUserId" parameterType="agentSVO">
|
||||
UPDATE mj_msg_data SET
|
||||
CUR_STATE = 3,
|
||||
SENT_DATE = NOW(),
|
||||
RSLT_DATE = NOW(),
|
||||
RSLT_CODE = 1000,
|
||||
RSLT_NET = 'SKT'
|
||||
WHERE CUR_STATE <![CDATA[ < ]]>3
|
||||
AND USER_ID = #{userId}
|
||||
</update>
|
||||
|
||||
|
||||
<update id="updateReportWhereUserIdAndMassage" parameterType="agentSVO">
|
||||
UPDATE mj_msg_data SET
|
||||
CUR_STATE = 3,
|
||||
SENT_DATE = NOW(),
|
||||
RSLT_DATE = NOW(),
|
||||
RSLT_CODE = 1000,
|
||||
RSLT_NET = 'SKT'
|
||||
WHERE CUR_STATE <![CDATA[ < ]]>3
|
||||
AND USER_ID = #{userId}
|
||||
AND SMS_TXT LIKE CONCAT(#{message}, '%');
|
||||
</update>
|
||||
|
||||
|
||||
</mapper>
|
||||
@ -20,6 +20,22 @@
|
||||
ORDER BY SRVDT;
|
||||
|
||||
</select>
|
||||
<select id="findByUsridAndSrvdtBetween" resultType="com.itn.admin.commute.mapper.domain.CommuteVO" parameterType="com.itn.admin.commute.mapper.domain.CommuteVO">
|
||||
SELECT
|
||||
usrid,
|
||||
MIN(SRVDT) AS firstActivityTime,
|
||||
MAX(SRVDT) AS lastActivityTime
|
||||
FROM
|
||||
${tableNm}
|
||||
WHERE SRVDT BETWEEN #{startDate} AND #{endDate}
|
||||
-- AND usrid IS NOT NULL
|
||||
AND LENGTH(USRID)>0
|
||||
and usrid = #{biostarId}
|
||||
GROUP BY
|
||||
usrid
|
||||
ORDER BY SRVDT;
|
||||
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
44
src/main/resources/mapper/gw/holiday/HolidayMapper.xml
Normal file
44
src/main/resources/mapper/gw/holiday/HolidayMapper.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.itn.admin.gw.holiday.mapper.HolidayMapper">
|
||||
|
||||
<!-- 특정 날짜가 범위 내에 있는 휴가 목록 조회 -->
|
||||
<select id="selectHolidaysByDateRange" parameterType="string" resultType="holidayVO">
|
||||
SELECT
|
||||
HOLI_SEQ,
|
||||
EMP_CODE,
|
||||
USER_ID,
|
||||
NAME,
|
||||
GIAN_EMP_CODE,
|
||||
GIAN_NAME,
|
||||
POS_ID,
|
||||
POS_NAME,
|
||||
DEPT_ID,
|
||||
DEPT_NAME,
|
||||
APPR_STAT,
|
||||
APPR_DATE,
|
||||
START_DT,
|
||||
END_DT,
|
||||
START_TM,
|
||||
END_TM,
|
||||
HOLI_CODE,
|
||||
HOLI_CONT,
|
||||
DEST,
|
||||
PHONE,
|
||||
DIVI_1,
|
||||
DIVI_2,
|
||||
DIVI_3,
|
||||
SEL_YEAR,
|
||||
REQ_CNT,
|
||||
ETC_CNT,
|
||||
INS_DATE,
|
||||
HOLI_DETAIL
|
||||
FROM INTRAWARE.HOLIDAY_MAIN
|
||||
WHERE #{targetDate} BETWEEN START_DT AND END_DT
|
||||
AND appr_stat = '2' -- 결제 완료 2
|
||||
ORDER BY INS_DATE DESC
|
||||
</select>
|
||||
</mapper>
|
||||
@ -42,7 +42,7 @@
|
||||
select
|
||||
ic.commute_id
|
||||
,ic.commute_group_id
|
||||
,ic.name
|
||||
-- ,ic.name
|
||||
,ic.work_dt
|
||||
,ic.start_time
|
||||
,ic.start_rslt
|
||||
@ -51,27 +51,36 @@
|
||||
,ic.transfer_dt
|
||||
,ic.approver
|
||||
,ic.approver_dt
|
||||
,ic.pstn
|
||||
,ic.holi_code
|
||||
-- ,ic.pstn
|
||||
,us.user_name
|
||||
,us.user_rank
|
||||
from itn_commute ic
|
||||
left join itn_commute_group icg
|
||||
on ic.commute_group_id = icg.commute_group_id
|
||||
left join users us
|
||||
on ic.uniq_id = us.uniq_id
|
||||
where icg.work_dt =#{workDt}
|
||||
|
||||
</select>
|
||||
|
||||
<insert id="upsertCommuteList" parameterType="java.util.List">
|
||||
INSERT INTO itn_commute
|
||||
(commute_group_id, name, pstn, work_dt, start_time, start_rslt, end_time, end_rslt)
|
||||
(commute_group_id, uniq_id, work_dt, start_time, start_rslt, end_time, end_rslt, holi_code)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.commuteGroupId}, #{item.name}, #{item.pstn}, #{item.workDt},
|
||||
#{item.startTime}, #{item.startRslt}, #{item.endTime}, #{item.endRslt})
|
||||
(#{item.commuteGroupId}, #{item.uniqId}, #{item.workDt},
|
||||
#{item.startTime}, #{item.startRslt}, #{item.endTime}, #{item.endRslt}, #{item.holiCode})
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
uniq_id = VALUES(uniq_id),
|
||||
start_time = VALUES(start_time),
|
||||
start_rslt = VALUES(start_rslt),
|
||||
end_time = VALUES(end_time),
|
||||
end_rslt = VALUES(end_rslt)
|
||||
end_rslt = VALUES(end_rslt),
|
||||
holi_code = VALUES(holi_code)
|
||||
|
||||
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@ -8,15 +8,42 @@
|
||||
<select id="findAll" resultType="userVO">
|
||||
SELECT
|
||||
uniq_id
|
||||
, user_id
|
||||
, user_pw
|
||||
, user_name
|
||||
, LAST_UPDT_PNTTM
|
||||
, role
|
||||
, 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
|
||||
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
|
||||
where biostar_id is not null
|
||||
and gw_id is not null
|
||||
and active_yn = 'Y'
|
||||
</select>
|
||||
|
||||
<update id="updateRole" parameterType="userVO">
|
||||
UPDATE users SET role = #{role} WHERE uniq_id = #{uniqId}
|
||||
</update>
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
<typeAlias type="com.itn.admin.itn.code.mapper.domain.CodeDetailVO" alias="codeDetailVO"/>
|
||||
|
||||
<typeAlias type="com.itn.admin.itn.commute.mapper.domain.ItnCommuteBackVO" alias="itnCommuteBackVO"/>
|
||||
|
||||
<typeAlias type="com.itn.admin.gw.holiday.mapper.domain.HolidayVO" alias="holidayVO"/>
|
||||
</typeAliases>
|
||||
|
||||
<!-- <environments default="development">-->
|
||||
|
||||
@ -1,349 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- 관련 Namespace 선언 및 layout:decorate 추가 -->
|
||||
<html lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="layout">
|
||||
<head>
|
||||
|
||||
<!-- layout.html 에 들어간 head 부분을 제외하고 개별 파일에만 적용되는 head 부분 추가 -->
|
||||
<title>직원 출퇴근 관리</title>
|
||||
|
||||
<!-- 필요하다면 개별 파일에 사용될 css/js 선언 -->
|
||||
|
||||
<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>
|
||||
.attendance-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed; /* 고정된 너비를 위한 설정 */
|
||||
}
|
||||
|
||||
.attendance-table th, .attendance-table td {
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.attendance-table thead th {
|
||||
background-color: #f4f6f9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.attendance-table th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 순번과 이름 컬럼을 강조 */
|
||||
.attendance-table tbody td:nth-child(1), /* 순번 열 */
|
||||
.attendance-table tbody td:nth-child(2) { /* 이름 열 */
|
||||
font-weight: bold;
|
||||
background-color: #f1f1f1; /* 약간 연한 배경색 */
|
||||
}
|
||||
|
||||
/* 열 너비 조정 */
|
||||
.attendance-table th:nth-child(1),
|
||||
.attendance-table td:nth-child(1) {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.attendance-table th:nth-child(2),
|
||||
.attendance-table td:nth-child(2) {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
/* 이름과 퇴근시간(전일) 사이 경계선 굵게 설정 */
|
||||
.attendance-table th:nth-child(2),
|
||||
.attendance-table td:nth-child(2) {
|
||||
border-right: 2px solid #dee2e6; /* 이름 열의 오른쪽 테두리 굵게 */
|
||||
}
|
||||
/* 검색 버튼 스타일 */
|
||||
.search-btn {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 5px;
|
||||
width: 50%;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top_row {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
/* 버튼 및 날짜 필드 정렬 */
|
||||
.card-body .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-body .input-group, .card-body .search-btn {
|
||||
height: 55%;
|
||||
width: 222px;
|
||||
}
|
||||
|
||||
/* 테이블 및 컨테이너 스타일 */
|
||||
.attendance-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.attendance-table th, .attendance-table td {
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.attendance-table thead th {
|
||||
background-color: #f4f6f9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body layout:fragment="body">
|
||||
|
||||
<div class="wrapper">
|
||||
<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>
|
||||
|
||||
<!-- Content Wrapper. Contains page content -->
|
||||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0">출퇴근 관리</h1>
|
||||
</div><!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="#">Home</a></li>
|
||||
<li class="breadcrumb-item active">출퇴근 관리</li>
|
||||
</ol>
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</div>
|
||||
<!-- /.content-header -->
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- /.card -->
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">출퇴근 관리</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<form id="searchForm" method="GET" th:action="@{/commute/list_test}">
|
||||
<div class="row top_row">
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>검색 날짜</label>
|
||||
<div class="input-group">
|
||||
<input type="date" name="workDt" class="form-control" th:value="${itnCommuteVO.workDt}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 검색 버튼 -->
|
||||
<div class="col-sm-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary search-btn">검색</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="attendance-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>순번</th>
|
||||
<th>이름</th>
|
||||
<th>퇴근시간(전일)</th>
|
||||
<th>결과</th>
|
||||
<th>출근(당일)</th>
|
||||
<th>결과</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr th:each="row, stat : ${list}">
|
||||
<td th:text="${stat.count}"></td>
|
||||
<td th:text="${row.name}"></td>
|
||||
<!-- <td th:text="${row.pstn}"></td>-->
|
||||
<td th:text="${row.startTime}"></td>
|
||||
<!-- startRslt와 파일 업로드 버튼을 같은 <td>에 표시 -->
|
||||
<td>
|
||||
<span th:text="${row.startRslt}"></span>
|
||||
|
||||
<!--[th:if="${row.startRslt} == '지각'"] 버튼이 "지각"일 때만 주석도 함께 렌더링 -->
|
||||
<button type="button"
|
||||
th:if="${row.startRslt} == '지각' or ${row.startRslt} == '미체크'"
|
||||
class="btn btn-warning btn-sm"
|
||||
data-toggle="modal"
|
||||
data-target="#uploadModal"
|
||||
th:attr="data-commute-id=${row.commuteId}, data-name=${row.name}">
|
||||
<i class="fas fa-upload"></i> 파일 업로드
|
||||
</button>
|
||||
</td>
|
||||
<td th:text="${row.endTime}"></td>
|
||||
<td>
|
||||
<span th:text="${row.endRslt}"></span>
|
||||
<button type="button"
|
||||
th:if="${row.endRslt} == '조기퇴근' or ${row.endRslt} == '미체크'"
|
||||
class="btn btn-warning btn-sm"
|
||||
data-toggle="modal"
|
||||
data-target="#uploadModal"
|
||||
th:attr="data-commute-id=${row.commuteId}, data-name=${row.name}">
|
||||
<i class="fas fa-upload"></i> 파일 업로드
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 나머지 행들도 동일한 형식으로 추가 -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
<!-- /.container-fluid -->
|
||||
</section>
|
||||
<!-- /Main content -->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- /.content-wrapper -->
|
||||
<footer class="main-footer"
|
||||
th:insert="~{fragments/footer :: footerFragment}">
|
||||
</footer>
|
||||
|
||||
<!-- Control Sidebar -->
|
||||
<aside class="control-sidebar control-sidebar-dark">
|
||||
<!-- Control sidebar content goes here -->
|
||||
</aside>
|
||||
<!-- /.control-sidebar -->
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
// 모달이 열릴 때 commuteId를 설정하는 이벤트
|
||||
$('#uploadModal').on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget); // 파일 업로드 버튼
|
||||
var commuteId = button.data('commute-id'); // 버튼의 data-commute-id 값 읽기
|
||||
var name = button.data('name'); // 버튼의 data-name 값 읽기
|
||||
var modal = $(this);
|
||||
console.log('commuteId : ', commuteId);
|
||||
modal.find('#commuteIdInput').val(commuteId); // 숨겨진 필드에 commuteId 설정
|
||||
modal.find('#modalName').text(name); // 모달 제목의 #modalName 요소에 이름 표시
|
||||
});
|
||||
|
||||
function submitFile() {
|
||||
// 파일 업로드 폼을 제출하는 함수
|
||||
document.getElementById('fileUploadForm').submit();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 파일 업로드 모달 -->
|
||||
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="uploadModalLabel"> <span id="modalName"></span> - 수정 </h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="fileUploadForm" method="POST" enctype="multipart/form-data" th:action="@{/uploadFile}">
|
||||
|
||||
<!-- Select 박스 추가 -->
|
||||
<div class="form-group">
|
||||
<label for="fileType">파일 유형 선택</label>
|
||||
<select class="form-control" id="fileType" name="fileType" required>
|
||||
<!-- <option value="">-- 선택 --</option>-->
|
||||
<!-- <option value="type1">유형 1</option>-->
|
||||
<!-- <option value="type2">유형 2</option>-->
|
||||
<!-- <option value="type3">유형 3</option>-->
|
||||
|
||||
<option value="">선택해주세요.</option>
|
||||
<option th:each="code : ${codeList}"
|
||||
th:value="${code.codeId}"
|
||||
th:text="${code.codeName}">
|
||||
<!-- th:selected="${code.codeId} == ${spam.spamRsnCode01}"-->
|
||||
Code
|
||||
</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 파일 선택 -->
|
||||
<div class="form-group">
|
||||
<label for="fileInput">파일 선택</label>
|
||||
<input type="file" class="form-control-file" id="fileInput" name="file" 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="submitFile()">업로드(수정)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -60,7 +60,7 @@
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<form th:id="searchForm" th:name="searchForm" th:action="@{/commute/list}" >
|
||||
<form th:id="searchForm" th:name="searchForm" th:action="@{/commute/list_temp}" >
|
||||
<div class="row">
|
||||
<div class="col-sm-6" style="max-width: 10%;">
|
||||
<!-- select -->
|
||||
@ -116,9 +116,11 @@
|
||||
<td th:text="${row.usrid}"></td>
|
||||
<td th:text="${row.pstn}"></td>
|
||||
<td th:text="${row.firstActivityTime}"></td>
|
||||
<td th:text="${row.firstActivityTimeMemo}"></td>
|
||||
<td th:text="${@TCodeUtils.getCodeName('COMMUTE', row.firstActivityTimeMemo)}"></td>
|
||||
<!-- <td th:text="${row.firstActivityTimeMemo}"></td>-->
|
||||
|
||||
<td th:text="${row.lastActivityTime}"></td>
|
||||
<td th:text="${row.LastActivityTimeMemo}"></td>
|
||||
<td th:text="${@TCodeUtils.getCodeName('COMMUTE', row.LastActivityTimeMemo)}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
@ -50,7 +50,7 @@
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a th:href="@{/commute/list}" class="nav-link">
|
||||
<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>
|
||||
@ -59,7 +59,7 @@
|
||||
</ul>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a th:href="@{/itn/commute/list}" class="nav-link">
|
||||
<a th:href="@{/itn/commute/list_temp}" class="nav-link">
|
||||
<!-- <i class="far fa-circle nav-icon"></i>-->
|
||||
<i class="far fa-clock nav-icon"></i>
|
||||
<p>itn 출퇴근 관리</p>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="layout">
|
||||
<head>
|
||||
|
||||
<!-- layout.html 에 들어간 head 부분을 제외하고 개별 파일에만 적용되는 head 부분 추가 -->
|
||||
<title>직원 출퇴근 관리</title>
|
||||
|
||||
@ -13,20 +14,134 @@
|
||||
<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}">
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.attendance-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed; /* 고정된 너비를 위한 설정 */
|
||||
}
|
||||
|
||||
.attendance-table th, .attendance-table td {
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.attendance-table thead th {
|
||||
background-color: #f4f6f9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.attendance-table th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 순번과 이름 컬럼을 강조 */
|
||||
.attendance-table tbody td:nth-child(1), /* 순번 열 */
|
||||
.attendance-table tbody td:nth-child(2) { /* 이름 열 */
|
||||
font-weight: bold;
|
||||
background-color: #f1f1f1; /* 약간 연한 배경색 */
|
||||
}
|
||||
|
||||
/* 열 너비 조정 */
|
||||
.attendance-table th:nth-child(1),
|
||||
.attendance-table td:nth-child(1) {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.attendance-table th:nth-child(2),
|
||||
.attendance-table td:nth-child(2) {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
/* 이름과 퇴근시간(전일) 사이 경계선 굵게 설정 */
|
||||
.attendance-table th:nth-child(2),
|
||||
.attendance-table td:nth-child(2) {
|
||||
border-right: 2px solid #dee2e6; /* 이름 열의 오른쪽 테두리 굵게 */
|
||||
}
|
||||
/* 검색 버튼 스타일 */
|
||||
.search-btn {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 5px;
|
||||
width: 50%;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top_row {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
/* 버튼 및 날짜 필드 정렬 */
|
||||
.card-body .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-body .input-group, .card-body .search-btn {
|
||||
height: 55%;
|
||||
width: 222px;
|
||||
}
|
||||
|
||||
/* 테이블 및 컨테이너 스타일 */
|
||||
.attendance-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.attendance-table th, .attendance-table td {
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.attendance-table thead th {
|
||||
background-color: #f4f6f9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.attendance-table tbody tr:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body layout:fragment="body">
|
||||
|
||||
<div class="wrapper">
|
||||
<div th:replace="~{fragments/top_nav :: topFragment}"/>
|
||||
<div th:replace="~{fragments/top_nav :: topFragment}"></div>
|
||||
|
||||
<!-- Main Sidebar Container -->
|
||||
<aside class="main-sidebar sidebar-dark-primary elevation-4"
|
||||
@ -61,62 +176,79 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">목록</h3>
|
||||
<h3 class="card-title">출퇴근 관리</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<form id="searchForm" method="GET" th:action="@{/itn/commute/list}">
|
||||
<div class="row">
|
||||
<form id="searchForm" method="GET" th:action="@{/commute/list}">
|
||||
<div class="row top_row">
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>시작 날짜</label>
|
||||
<input type="date" name="startDate" class="form-control" th:value="${startDate}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>종료 날짜</label>
|
||||
<input type="date" name="endDate" class="form-control" th:value="${endDate}" required>
|
||||
<label>검색 날짜</label>
|
||||
<div class="input-group">
|
||||
<input type="date" name="workDt" class="form-control" th:value="${itnCommuteVO.workDt}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 검색 버튼 -->
|
||||
<div class="col-sm-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary btn-search btn-block" style="height: 70px;">검색</button>
|
||||
<button type="submit" class="btn btn-primary search-btn">검색</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table id="commuteTb" class="table table-bordered table-striped">
|
||||
<table class="attendance-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>출근</th>
|
||||
<!-- <th>직위</th>-->
|
||||
<th>비고</th>
|
||||
<th>퇴근</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>순번</th>
|
||||
<th>이름</th>
|
||||
<th>퇴근시간(전일)</th>
|
||||
<th>결과</th>
|
||||
<th>출근(당일)</th>
|
||||
<th>결과</th>
|
||||
<th>비고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="row, stat : ${list}">
|
||||
<td th:text="${row.commuteWorkDate}"></td>
|
||||
<td th:text="${row.commuteName}"></td>
|
||||
<td th:text="${row.commuteStartTime}"></td>
|
||||
<td th:text="${row.commuteCategory}"></td>
|
||||
<td th:text="${row.commuteEndTime}"></td>
|
||||
</tr>
|
||||
<tr th:each="row, stat : ${list}">
|
||||
<td th:text="${stat.count}"></td>
|
||||
<td th:text="${row.userName}"></td>
|
||||
<!-- <td th:text="${row.pstn}"></td>-->
|
||||
<td th:text="${row.startTime}"></td>
|
||||
<!-- startRslt와 파일 업로드 버튼을 같은 <td>에 표시 -->
|
||||
|
||||
<td>
|
||||
<span th:text="${@TCodeUtils.getCodeName('COMMUTE', row.startRslt)}"/>
|
||||
|
||||
<!--[th:if="${row.startRslt} == '지각'"] 버튼이 "지각"일 때만 주석도 함께 렌더링 -->
|
||||
<button type="button"
|
||||
th:if="${row.startRslt} == '60' or ${row.startRslt} == '70'"
|
||||
class="btn btn-warning btn-sm"
|
||||
data-toggle="modal"
|
||||
data-target="#uploadModal"
|
||||
th:attr="data-commute-id=${row.commuteId}, data-name=${row.userName}">
|
||||
<i class="fas fa-upload"></i> 파일 업로드
|
||||
</button>
|
||||
</td>
|
||||
<td th:text="${row.endTime}"/>
|
||||
<td>
|
||||
<span th:text="${@TCodeUtils.getCodeName('COMMUTE', row.endRslt)}"/>
|
||||
<button type="button"
|
||||
th:if="${row.endRslt} == '70'"
|
||||
class="btn btn-warning btn-sm"
|
||||
data-toggle="modal"
|
||||
data-target="#uploadModal"
|
||||
th:attr="data-commute-id=${row.commuteId}, data-name=${row.userName}">
|
||||
<i class="fas fa-upload"></i> 파일 업로드
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<span th:text="${@TCodeUtils.getCodeName('COMMUTE', row.holiCode)}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 나머지 행들도 동일한 형식으로 추가 -->
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>출근</th>
|
||||
<!-- <th>직위</th>-->
|
||||
<th>비고</th>
|
||||
<th>퇴근</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
@ -147,44 +279,75 @@
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
||||
<!-- DataTables & Plugins -->
|
||||
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
|
||||
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
|
||||
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
// 모달이 열릴 때 commuteId를 설정하는 이벤트
|
||||
$('#uploadModal').on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget); // 파일 업로드 버튼
|
||||
var commuteId = button.data('commute-id'); // 버튼의 data-commute-id 값 읽기
|
||||
var name = button.data('name'); // 버튼의 data-name 값 읽기
|
||||
var modal = $(this);
|
||||
console.log('commuteId : ', commuteId);
|
||||
modal.find('#commuteIdInput').val(commuteId); // 숨겨진 필드에 commuteId 설정
|
||||
modal.find('#modalName').text(name); // 모달 제목의 #modalName 요소에 이름 표시
|
||||
});
|
||||
|
||||
$("#commuteTb").DataTable({
|
||||
"responsive": true
|
||||
, "lengthChange": false
|
||||
, "autoWidth": false
|
||||
, "pageLength": 20000,
|
||||
"scrollY": "1600px", // 원하는 높이를 설정합니다.
|
||||
"scrollCollapse": true, // 내용이 적을 경우 테이블 높이를 축소합니다.
|
||||
"paging": false // 페이지네이션을 없애고 스크롤로 대체합니다.
|
||||
, "buttons": ["copy", { extend: 'csv', charset: 'UTF-8', bom: true }
|
||||
, "excel", "pdf", "print", "colvis"]
|
||||
}).buttons().container().appendTo('#commuteTb_wrapper .col-md-6:eq(0)');
|
||||
function submitFile() {
|
||||
// 파일 업로드 폼을 제출하는 함수
|
||||
document.getElementById('fileUploadForm').submit();
|
||||
}
|
||||
});
|
||||
|
||||
$( document ).ready( function() {
|
||||
$('#searchDay, #searchMonth, #searchYear').on('change', function (){
|
||||
$('#searchForm').submit();
|
||||
});
|
||||
} );
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 파일 업로드 모달 -->
|
||||
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="uploadModalLabel"> <span id="modalName"></span> - 수정 </h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="fileUploadForm" method="POST" enctype="multipart/form-data" th:action="@{/uploadFile}">
|
||||
|
||||
<!-- Select 박스 추가 -->
|
||||
<div class="form-group">
|
||||
<label for="fileType">파일 유형 선택</label>
|
||||
<select class="form-control" id="fileType" name="fileType" required>
|
||||
<!-- <option value="">-- 선택 --</option>-->
|
||||
<!-- <option value="type1">유형 1</option>-->
|
||||
<!-- <option value="type2">유형 2</option>-->
|
||||
<!-- <option value="type3">유형 3</option>-->
|
||||
|
||||
<option value="">선택해주세요.</option>
|
||||
<option th:each="code : ${codeList}"
|
||||
th:value="${code.codeId}"
|
||||
th:text="${code.codeName}">
|
||||
<!-- th:selected="${code.codeId} == ${spam.spamRsnCode01}"-->
|
||||
Code
|
||||
</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 파일 선택 -->
|
||||
<div class="form-group">
|
||||
<label for="fileInput">파일 선택</label>
|
||||
<input type="file" class="form-control-file" id="fileInput" name="file" 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="submitFile()">업로드(수정)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
190
src/main/resources/templates/itn/commute/list_temp.html
Normal file
190
src/main/resources/templates/itn/commute/list_temp.html
Normal file
@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- 관련 Namespace 선언 및 layout:decorate 추가 -->
|
||||
<html lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="layout">
|
||||
<head>
|
||||
<!-- layout.html 에 들어간 head 부분을 제외하고 개별 파일에만 적용되는 head 부분 추가 -->
|
||||
<title>직원 출퇴근 관리</title>
|
||||
|
||||
<!-- 필요하다면 개별 파일에 사용될 css/js 선언 -->
|
||||
|
||||
<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}">
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body layout:fragment="body">
|
||||
|
||||
<div class="wrapper">
|
||||
<div th:replace="~{fragments/top_nav :: topFragment}"/>
|
||||
|
||||
<!-- Main Sidebar Container -->
|
||||
<aside class="main-sidebar sidebar-dark-primary elevation-4"
|
||||
th:insert="~{fragments/mainsidebar :: sidebarFragment}">
|
||||
</aside>
|
||||
|
||||
<!-- Content Wrapper. Contains page content -->
|
||||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0">출퇴근 관리</h1>
|
||||
</div><!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="#">Home</a></li>
|
||||
<li class="breadcrumb-item active">출퇴근 관리</li>
|
||||
</ol>
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</div>
|
||||
<!-- /.content-header -->
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- /.card -->
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">목록</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<form id="searchForm" method="GET" th:action="@{/itn/commute/list}">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>시작 날짜</label>
|
||||
<input type="date" name="startDate" class="form-control" th:value="${startDate}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>종료 날짜</label>
|
||||
<input type="date" name="endDate" class="form-control" th:value="${endDate}" required>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 검색 버튼 -->
|
||||
<div class="col-sm-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary btn-search btn-block" style="height: 70px;">검색</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table id="commuteTb" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>출근</th>
|
||||
<!-- <th>직위</th>-->
|
||||
<th>비고</th>
|
||||
<th>퇴근</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="row, stat : ${list}">
|
||||
<td th:text="${row.commuteWorkDate}"></td>
|
||||
<td th:text="${row.commuteName}"></td>
|
||||
<td th:text="${row.commuteStartTime}"></td>
|
||||
<td th:text="${row.commuteCategory}"></td>
|
||||
<td th:text="${row.commuteEndTime}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>출근</th>
|
||||
<!-- <th>직위</th>-->
|
||||
<th>비고</th>
|
||||
<th>퇴근</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
<!-- /.container-fluid -->
|
||||
</section>
|
||||
<!-- /Main content -->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- /.content-wrapper -->
|
||||
<footer class="main-footer"
|
||||
th:insert="~{fragments/footer :: footerFragment}">
|
||||
</footer>
|
||||
|
||||
<!-- Control Sidebar -->
|
||||
<aside class="control-sidebar control-sidebar-dark">
|
||||
<!-- Control sidebar content goes here -->
|
||||
</aside>
|
||||
<!-- /.control-sidebar -->
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
||||
<!-- DataTables & Plugins -->
|
||||
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
|
||||
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
|
||||
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
|
||||
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
$("#commuteTb").DataTable({
|
||||
"responsive": true
|
||||
, "lengthChange": false
|
||||
, "autoWidth": false
|
||||
, "pageLength": 20000,
|
||||
"scrollY": "1600px", // 원하는 높이를 설정합니다.
|
||||
"scrollCollapse": true, // 내용이 적을 경우 테이블 높이를 축소합니다.
|
||||
"paging": false // 페이지네이션을 없애고 스크롤로 대체합니다.
|
||||
, "buttons": ["copy", { extend: 'csv', charset: 'UTF-8', bom: true }
|
||||
, "excel", "pdf", "print", "colvis"]
|
||||
}).buttons().container().appendTo('#commuteTb_wrapper .col-md-6:eq(0)');
|
||||
});
|
||||
|
||||
$( document ).ready( function() {
|
||||
$('#searchDay, #searchMonth, #searchYear').on('change', function (){
|
||||
$('#searchForm').submit();
|
||||
});
|
||||
} );
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user