feat : home -> 대쉬보드로 사용예정 수정중

pull/2/head
Kurt92 5 months ago
parent 8cf503d16c
commit 402f021bfa

@ -0,0 +1,27 @@
package go.kr.project.biz.minwon.init.controller;
import egovframework.constant.TilesConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MinwonInitController {
/**
* Q&A
* @return
*/
@GetMapping("/minwon/init/init.do")
@Operation(summary = "민원접수 초기자료", description = "민원접수 초기자료 목록 페이지를 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공")
})
public String minwonInitView() {
return "minwon/init" + TilesConstants.BASE;
}
}

@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@ -219,13 +220,21 @@ public class LoginController {
@ApiResponse(responseCode = "200", description = "로그아웃 성공")
})
@GetMapping("/logout.do")
public String logout(HttpServletRequest request, HttpServletResponse response) {
public String logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
try {
loginService.logout(request, response);
} catch (Exception e) {
log.error("로그아웃 처리 중 오류 발생", e);
}
return "redirect:" + loginProperties.getUrl();
String kind = (String) session.getAttribute("kind");
String redirectUrl = null;
if ("cp".equals(kind)) {
redirectUrl = "/login/cp/login.do";
} else if ("ep".equals(kind)) {
redirectUrl = "/login/ep/login.do";
}
return "redirect:" + redirectUrl;
}
/**

@ -86,7 +86,7 @@ springdoc:
# Login configuration
login:
url: /login/login.do # 로그인 페이지 URL (공통으로 사용)
url: /login/cp/login.do # 로그인 페이지 URL (공통으로 사용)
lock:
count: 5 # 비밀번호 잠김 최종 카운트 (0일 경우 잠김 로직 pass)
DefaultPassword: xitpassword # 초기 비밀번호, 초기화 비밀번호

@ -1,243 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<div class="modalz act" id="batchJobForm_detail">
<div class="modalz_body">
<div class="modalz_tit">
배치 잡 등록
<a class="pop-x-btn" id="btnCloseBatchJobForm"></a>
</div>
<div class="modalz_con">
<div class="box_column bottom_m">
<ul class="box_title">
<li class="tit">배치 잡 정보</li>
</ul>
<div class="containers">
<div class="forms_table_non">
<form id="batchJobForm">
<table>
<tbody>
<tr>
<th class="th">작업 클래스</th>
<td colspan="3">
<select class="input" id="jobClass" name="jobClass" validation-check="required">
<option value="">선택</option>
<c:forEach var="jobClass" items="${jobClasses}">
<option value="${jobClass.name}">${jobClass.simpleName}</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<th class="th">작업 이름</th>
<td colspan="3">
<input type="text" class="input" id="jobNm" name="jobNm" placeholder="작업 이름을 입력하세요" validation-check="required alphanumeric" maxlength="50">
</td>
</tr>
<tr>
<th class="th">작업 그룹</th>
<td colspan="3">
<input type="text" class="input" id="jobGroup" name="jobGroup" placeholder="작업 그룹을 입력하세요" validation-check="required alphanumeric" maxlength="50">
</td>
</tr>
<tr>
<th class="th">Cron 표현식</th>
<td colspan="3">
<input type="text" class="input" id="cronExpression" name="cronExpression" placeholder="Cron 표현식을 입력하세요 (예: 0 0/1 * * * ?)" validation-check="required" maxlength="100">
<div class="cron-examples">
<p>Cron 표현식 예시:</p>
<ul>
<li>매 분마다: 0 0/1 * * * ?</li>
<li>매 시간마다: 0 0 * * * ?</li>
<li>매일 자정: 0 0 0 * * ?</li>
<li>매주 월요일 오전 9시: 0 0 9 ? * MON</li>
<li>매월 1일 오전 10시: 0 0 10 1 * ?</li>
</ul>
</div>
</td>
</tr>
<tr>
<th class="th">설명</th>
<td colspan="3">
<textarea class="textzone" id="jobDc" name="jobDc" placeholder="배치 작업에 대한 설명을 입력하세요" maxlength="500"></textarea>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
<div class="modalz_foot">
<a href="#" class="newbtns bg1" id="btnSaveBatchJob">저장</a>
<a href="#" class="newbtns modalclose" id="btnCancelBatchJob">취소</a>
</div>
</div>
<div class="dim"></div>
</div>
<script>
$(document).ready(function() {
// 팝업 닫기 버튼 이벤트
$('#btnCloseBatchJobForm, #btnCancelBatchJob').click(function() {
// 모달 내용을 완전히 제거
$('#detail_pop').empty().hide();
return false;
});
// 배치 잡 저장 버튼 이벤트
$('#btnSaveBatchJob').click(function() {
saveBatchJob();
return false;
});
// 실시간 유효성 검사 이벤트 등록
$('#jobNm').on('blur', function() {
const jobNm = $(this).val();
if (jobNm && !validateJobName(jobNm)) {
errorElementCreate(this, '작업 이름은 영문자, 숫자, 하이픈, 언더스코어만 사용할 수 있습니다.', false);
} else {
errorElementCreate(this, '', true);
}
});
$('#jobGroup').on('blur', function() {
const jobGroup = $(this).val();
if (jobGroup && !validateJobGroup(jobGroup)) {
errorElementCreate(this, '작업 그룹은 영문자, 숫자, 하이픈, 언더스코어만 사용할 수 있습니다.', false);
} else {
errorElementCreate(this, '', true);
}
});
$('#cronExpression').on('blur', function() {
const cronExpression = $(this).val();
if (cronExpression && !validateCronExpression(cronExpression)) {
errorElementCreate(this, '유효하지 않은 Cron 표현식입니다. 올바른 형식으로 입력해주세요.', false);
} else {
errorElementCreate(this, '', true);
}
});
});
// Cron 표현식 유효성 검사 함수
function validateCronExpression(cronExpression) {
if (!cronExpression) {
return false;
}
return true;
// Cron 표현식 정규식 패턴
// 초 분 시 일 월 요일 [년도]
const cronPattern = /^(\*|([0-9]|[1-5][0-9])(\/([0-9]|[1-5][0-9]))?) (\*|([0-9]|[1-5][0-9])(\/([0-9]|[1-5][0-9]))?) (\*|([0-9]|1[0-9]|2[0-3])(\/([0-9]|1[0-9]|2[0-3]))?) (\*|\?|([1-9]|[12][0-9]|3[01])(\/([1-9]|[12][0-9]|3[01]))?) (\*|([1-9]|1[0-2])(\/([1-9]|1[0-2]))?) (\*|\?|[0-6](\/[0-6])?|[A-Z]{3}(\/[A-Z]{3})?) (\*|([0-9]{4})(\/([0-9]{4}))?)?$/;
return cronPattern.test(cronExpression);
}
// 작업 이름 유효성 검사 함수
function validateJobName(jobNm) {
if (!jobNm) {
return false;
}
// 영문자, 숫자, 하이픈, 언더스코어만 허용
const jobNmPattern = /^[a-zA-Z0-9\-_]+$/;
return jobNmPattern.test(jobNm);
}
// 작업 그룹 유효성 검사 함수
function validateJobGroup(jobGroup) {
if (!jobGroup) {
return false;
}
// 영문자, 숫자, 하이픈, 언더스코어만 허용
const jobGroupPattern = /^[a-zA-Z0-9\-_]+$/;
return jobGroupPattern.test(jobGroup);
}
// 배치 잡 저장 함수
function saveBatchJob() {
// 필수 입력 필드 검증
if(!validateFormByAttributes('batchJobForm')){
return;
}
// 추가 유효성 검사
const jobNm = $('#jobNm').val();
const jobGroup = $('#jobGroup').val();
const cronExpression = $('#cronExpression').val();
// 작업 이름 검증
if (!validateJobName(jobNm)) {
errorElementCreate(document.getElementById('jobNm'), '작업 이름은 영문자, 숫자, 하이픈, 언더스코어만 사용할 수 있습니다.', false);
return;
}
// 작업 그룹 검증
if (!validateJobGroup(jobGroup)) {
errorElementCreate(document.getElementById('jobGroup'), '작업 그룹은 영문자, 숫자, 하이픈, 언더스코어만 사용할 수 있습니다.', false);
return;
}
// Cron 표현식 검증
if (!validateCronExpression(cronExpression)) {
errorElementCreate(document.getElementById('cronExpression'), '유효하지 않은 Cron 표현식입니다. 올바른 형식으로 입력해주세요.', false);
return;
}
// 배치 잡 데이터 생성
var batchJobData = {
jobClass: $('#jobClass').val(),
jobNm: jobNm,
jobGroup: jobGroup,
cronExpression: cronExpression,
jobDc: $('#jobDc').val()
};
// AJAX 호출
$.ajax({
url: "<c:url value='/batch/register.ajax' />",
type: 'POST',
data: JSON.stringify(batchJobData),
contentType: 'application/json',
dataType: 'json',
success: function(response) {
if (response.result) {
alert(response.message || '배치 작업이 성공적으로 등록되었습니다.');
// 모달 내용을 완전히 제거
$('#detail_pop').empty().hide();
// 그리드 새로고침
if (typeof BatchJobList !== 'undefined' && BatchJobList.grid && BatchJobList.grid.instance) {
BatchJobList.grid.instance.readData(1);
}
} else {
alert(response.message || '배치 작업 등록에 실패했습니다.');
}
}
});
}
</script>
<style>
.cron-examples {
margin-top: 10px;
font-size: 12px;
color: #666;
}
.cron-examples p {
margin-bottom: 5px;
font-weight: bold;
}
.cron-examples ul {
margin: 0;
padding-left: 20px;
}
.cron-examples li {
margin-bottom: 3px;
}
</style>

@ -1,383 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="dateUtil" uri="http://egovframework.go.kr/functions/date-util" %>
<!-- Main body -->
<div class="main_body">
<section id="section8" class="main_bars">
<div class="bgs-main">
<section id="section5">
<div class="sub_title"></div>
<button type="button" id="backBtn" class="newbtn bg4 info_a1">목록으로</button>
</section>
</div>
</section>
<div class="contants_body">
<div class="gs_b_top">
<ul class="lef">
<li class="th">작업 이름</li>
<li>
<span id="jobNmDisplay">${jobNm}</span>
<input type="hidden" id="jobNm" name="jobNm" value="${jobNm}" />
</li>
<li class="th">작업 그룹</li>
<li>
<span id="jobGroupDisplay">${jobGroup}</span>
<input type="hidden" id="jobGroup" name="jobGroup" value="${jobGroup}" />
</li>
<li class="th">상태</li>
<li>
<select id="searchStatusCd" name="searchStatusCd" class="input">
<option value="">전체</option>
<c:forEach var="code" items="${statusCodeList}">
<option value="${code.cdId}">${code.cdNm}</option>
</c:forEach>
</select>
</li>
<li class="th">종료코드</li>
<li>
<select id="searchExitCd" name="searchExitCd" class="input">
<option value="">전체</option>
<c:forEach var="code" items="${exitCodeList}">
<option value="${code.cdId}">${code.cdNm}</option>
</c:forEach>
</select>
</li>
<li class="th">시작일</li>
<li>
<input type="text" id="searchStartDt" name="searchStartDt" class="input calender datepicker"value="${dateUtil:getCurrentDateAddDays('yyyy-MM-dd',-2)}"/>
~
<input type="text" id="searchEndDt" name="searchEndDt" class="input calender datepicker" value="${dateUtil:getCurrentDateTime('yyyy-MM-dd')}" />
</li>
<li>
<button type="button" id="search_btn" class="newbtnss bg1">검색</button>
</li>
</ul>
<ul class="rig2">
<li>
<select id="perPageSelect" class="input">
<option value="10">페이지당 10</option>
<option value="20">페이지당 20</option>
<option value="30">페이지당 30</option>
</select>
<span class="page_number"><span id="currentPage"></span><span class="bar">/</span><sapn id="totalPages"></sapn> Pages</span>
</li>
</ul>
</div>
<div class="gs_booking">
<div class="row">
<div class="col-sm-12">
<div class="box_column">
<div class="containers">
<div id="grid"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /Main body -->
<script type="text/javascript">
/**
* 배치 작업 실행 결과 관리 모듈
* 배치 작업의 실행 결과를 조회하고 관리하는 기능을 제공합니다.
*/
(function(window, $) {
'use strict';
var SEARCH_COND = {};
// 검색정보 셋팅
var setSearchCond = function() {
var jobGroup = $.trim(nvl($("#jobGroup").val(), ""));
var jobNm = $.trim(nvl($("#jobNm").val(), ""));
var searchStatusCd = $.trim(nvl($("#searchStatusCd").val(), ""));
var searchExitCd = $.trim(nvl($("#searchExitCd").val(), ""));
var searchStartDt = $.trim(nvl($("#searchStartDt").val(), ""));
var searchEndDt = $.trim(nvl($("#searchEndDt").val(), ""));
SEARCH_COND.jobGroup = jobGroup;
SEARCH_COND.jobNm = jobNm;
SEARCH_COND.searchStatusCd = searchStatusCd;
SEARCH_COND.searchExitCd = searchExitCd;
SEARCH_COND.searchStartDt = searchStartDt;
SEARCH_COND.searchEndDt = searchEndDt;
};
/**
* 배치 작업 실행 결과 관리 네임스페이스
*/
var BatchJobExecution = {
/**
* 그리드 관련 객체
*/
grid: {
/**
* 그리드 인스턴스
*/
instance: null,
/**
* 그리드 설정 초기화
* @returns {Object} 그리드 설정 객체
*/
initConfig: function() {
// 데이터 소스 설정
var dataSource = this.createDataSource();
// 현재 선택된 perPage 값 가져오기
var perPage = parseInt($('#perPageSelect').val() || 10, 10);
// 그리드 설정 객체 생성
var gridConfig = new XitTuiGridConfig();
// 기본 설정
gridConfig.setOptDataSource(dataSource); // 데이터소스 연결
gridConfig.setOptGridId('grid'); // 그리드를 출력할 Element ID
gridConfig.setOptGridHeight(390); // 그리드 높이(단위: px)
gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px)
gridConfig.setOptRowHeaderType('rowNum'); // 행 첫번째 셀 타입(rowNum: 순번, checkbox: 체크박스, '': 출력 안함)
// 페이징 옵션 설정
gridConfig.setOptPageOptions({
useClient: false, // 클라이언트 페이징 여부(false: 서버 페이징)
perPage: perPage // 페이지당 표시 건수
});
gridConfig.setOptUseClientSort(false); // 서버사이드 정렬 false
// 컬럼 정보 설정
gridConfig.setOptColumns([
{
header: '실행 ID',
name: 'executionId',
width: 250,
align: 'left'
},
{
header: '시작 시간',
name: 'startDttm',
width: 150,
align: 'center'
},
{
header: '종료 시간',
name: 'endDttm',
width: 150,
align: 'center'
},
{
header: '상태',
name: 'statusCd',
width: 100,
align: 'center',
hidden: true
},
{
header: '상태',
name: 'statusNm',
width: 100,
align: 'center'
},
{
header: '종료 코드',
name: 'exitCd',
width: 100,
align: 'center',
hidden: true
},
{
header: '종료 코드',
name: 'exitCdNm',
width: 100,
align: 'center'
},
{
header: '서버 정보',
name: 'serverInfo',
align: 'left'
},
{
header: '로그 보기',
name: 'viewLog',
width: 100,
align: 'center',
formatter: function(value) {
return '<button type="button" class="btn_view_log newbtn_grid bg1">로그 보기</button>';
}
}
]);
return gridConfig;
},
/**
* 데이터 소스 생성
* @returns {Object} 데이터 소스 객체
*/
createDataSource: function() {
return {
api: {
readData: {
url: '<c:url value="/batch/execution.ajax"/>',
method: 'POST',
contentType: 'application/x-www-form-urlencoded',
processData: true
}
},
initialRequest: true, // 초기 데이터 요청 여부
serializer: function(params) {
var defaultParams = $.param(params);
setSearchCond();
var searchParams = $.param(SEARCH_COND);
return defaultParams + '&' + searchParams;
}
};
},
/**
* 그리드 인스턴스 생성
*/
create: function() {
var gridConfig = this.initConfig();
var Grid = tui.Grid;
this.instance = gridConfig.instance(Grid);
// 그리드 테마 설정
Grid.applyTheme('striped');
// 그리드 이벤트 설정
this.gridBindEvents();
},
/**
* 그리드 이벤트 바인딩
*/
gridBindEvents: function() {
var self = this;
// 요청 성공 시 총 건수 표시
this.instance.on('successResponse', function(ev) {
var responseObj = JSON.parse(ev.xhr.response);
//$('.totCnt').text(responseObj.data.pagination.totalCount);
$("#currentPage").text(responseObj.data.pagination.page);
$("#totalPages").text(responseObj.data.pagination.totalPages);
});
// 로그 보기 버튼 클릭 이벤트
this.instance.on('click', function(ev) {
if (ev.columnName === 'viewLog') {
var rowData = self.instance.getRow(ev.rowKey);
if (rowData && rowData.executionId) {
window.location.href = '<c:url value="/batch/log.do"/>' + '?executionId=' + rowData.executionId;
}
}
});
// 행 더블클릭 이벤트 - 실행 결과 페이지로 이동
this.instance.on('dblclick', function(ev) {
if (ev.rowKey !== undefined && ev.columnName !== '_number' && ev.columnName !== 'actions') {
var rowData = self.instance.getRow(ev.rowKey);
if (rowData && rowData.executionId) {
window.location.href = '<c:url value="/batch/log.do"/>' + '?executionId=' + rowData.executionId;
}
}
});
//this.instance.on('onGridMounted', ({ instance }) => {
// instance.getData().forEach(({rowKey}) => {
// instance.setValue(rowKey, 'row_num', rowKey + 1);
// });
//});
}
},
/**
* 이벤트 핸들러 설정
*/
eventBindEvents: function() {
var self = this;
// 검색 버튼 클릭 이벤트
$('#search_btn').on('click', function() {
// 등록일 from~to 유효성 검사
var startDate = $("#searchStartDt").val();
var endDate = $("#searchEndDt").val();
// 시작일과 종료일 중 하나만 입력된 경우 체크
if ((startDate && !endDate) || (!startDate && endDate)) {
alert("등록일 검색 시 시작일과 종료일을 모두 입력해주세요.");
return;
}
// 시작일과 종료일이 모두 입력된 경우 유효성 검사
if (startDate && endDate) {
if (!isDate(startDate) || !isDate(endDate)) {
alert("유효한 날짜 형식이 아닙니다. (YYYY-MM-DD)");
return;
}
// 시작일이 종료일보다 늦은 경우 체크
var startDateObj = new Date(startDate);
var endDateObj = new Date(endDate);
if (startDateObj > endDateObj) {
alert("시작일은 종료일보다 이후일 수 없습니다.");
return;
}
}
// 페이지를 1로 리셋
$("#page").val(1);
// 그리드 데이터 리로드
self.grid.instance.readData(1);
});
// 검색어 입력 필드에서 엔터키 이벤트 처리
$('#searchKeyword').on('keypress', function(e) {
if (e.which === 13) { // 엔터키 코드는 13
e.preventDefault(); // 기본 이벤트 방지
$('#search_btn').trigger('click'); // 검색 버튼 클릭 이벤트 트리거
}
});
// perPage 변경 이벤트 추가
$('#perPageSelect').on('change', function() {
var perPage = parseInt($(this).val(), 10);
// Grid의 perPage 설정 변경 및 데이터 리로드
self.grid.instance.setPerPage(perPage);
});
// 목록으로 버튼 클릭 이벤트
$('#backBtn').on('click', function() {
window.location.href = '<c:url value="/batch/list.do"/>';
});
},
/**
* 모듈 초기화
*/
init: function() {
// 그리드 생성
this.grid.create();
// 이벤트 핸들러 설정
this.eventBindEvents();
}
};
// 페이지 로드 시 초기화
$(function() {
BatchJobExecution.init();
});
// 전역 네임스페이스에 모듈 노출
window.BatchJobExecution = BatchJobExecution;
})(window, jQuery);
</script>

@ -1,431 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 일정 상세 팝업을 위한 컨테이너 -->
<div id="detail_pop" style="display:none;"></div>
<!-- Main body -->
<div class="main_body">
<section id="section8" class="main_bars">
<div class="bgs-main">
<section id="section5">
<div class="sub_title"></div>
<button type="button" id="btnNewBatchJob" class="newbtn bg1">배치 잡 등록</button>
</section>
</div>
</section>
<div class="contants_body">
<div class="gs_b_top">
<ul class="lef">
<li class="th">상태</li>
<li>
<select id="searchStatus" name="searchStatus" class="input">
<option value="">전체</option>
<c:forEach var="status" items="${jobStatusCodeList}">
<option value="${status.cdId}">${status.cdNm}</option>
</c:forEach>
</select>
</li>
<li>
<button type="button" id="search_btn" class="newbtnss bg1">검색</button>
</li>
</ul>
</div>
<div class="gs_booking">
<div class="row">
<div class="col-sm-12">
<div class="box_column">
<div class="containers">
<div id="grid"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /Main body -->
<script type="text/javascript">
/**
* 배치 작업 목록 관리 모듈
* 배치 작업 목록을 조회하고 관리하는 기능을 제공합니다.
*/
(function(window, $) {
'use strict';
var SEARCH_COND = {};
// 검색정보 셋팅
var setSearchCond = function() {
var searchStatus = $.trim(nvl($("#searchStatus").val(), ""));
SEARCH_COND.searchStatus = searchStatus;
};
/**
* 배치 작업 목록 관리 네임스페이스
*/
var BatchJobList = {
/**
* 그리드 관련 객체
*/
grid: {
/**
* 그리드 인스턴스
*/
instance: null,
/**
* 그리드 설정 초기화
* @returns {Object} 그리드 설정 객체
*/
initConfig: function() {
// 데이터 소스 설정
var dataSource = this.createDataSource();
// 그리드 설정 객체 생성
var gridConfig = new XitTuiGridConfig();
// 기본 설정
gridConfig.setOptDataSource(dataSource); // 데이터소스 연결
gridConfig.setOptGridId('grid'); // 그리드를 출력할 Element ID
gridConfig.setOptGridHeight(390); // 그리드 높이(단위: px)
gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px)
gridConfig.setOptRowHeaderType('rowNum'); // 행 첫번째 셀 타입(rowNum: 순번, checkbox: 체크박스, '': 출력 안함)
gridConfig.setOptUseClientSort(true); // 서버사이드 정렬 false
// 컬럼 정보 설정
gridConfig.setOptColumns([
{
header: '작업 이름',
name: 'jobNm',
width: 150,
sortable: true,
align: 'left'
},
{
header: '작업 그룹',
name: 'jobGroup',
width: 150,
sortable: true,
align: 'left'
},
{
header: 'Cron 표현식',
name: 'cronExpression',
width: 150,
align: 'center'
},
{
header: '설명',
name: 'jobDc',
align: 'left'
},
{
header: '상태',
name: 'statusCd',
width: 80,
align: 'center'
},
{
header: '최근 48시간이내 실패',
name: 'recentlyFailed',
width: 120,
align: 'center',
formatter: function(value) {
if (value.value === true) {
return '<span class="badge bg-danger" style="background-color: #dc3545; color: white; padding: 5px 10px; border-radius: 4px;">실패</span>';
}
return '';
}
},
{
header: '최근 5일 평균 소요시간',
name: 'avgDuration',
width: 150,
align: 'center'
},
{
header: '관리',
name: 'actions',
width: 360,
align: 'center',
formatter: function(value) {
var row = value.row;
var html = '';
if (row.statusCd === 'ACTIVE') {
html += '<button type="button" class="btn_trigger newbtn_grid bg1" style="margin-right:5px;">즉시실행</button>';
html += '<button type="button" class="btn_pause newbtn_grid bg3" style="margin-right:5px;">일시중지</button>';
html += '<button type="button" class="btn_delete newbtn_grid bg2">완전중지</button>';
}
if (row.statusCd === 'PAUSED') {
html += '<button type="button" class="btn_trigger newbtn_grid bg1" style="margin-right:5px;">즉시실행</button>';
html += '<button type="button" class="btn_resume newbtn_grid bg1" style="margin-right:5px;">재개</button>';
html += '<button type="button" class="btn_delete newbtn_grid bg2">완전중지</button>';
}
if (row.statusCd === 'DELETED') {
html += '-';
}
return html;
}
}
]);
return gridConfig;
},
/**
* 데이터 소스 생성
* @returns {Object} 데이터 소스 객체
*/
createDataSource: function() {
var self = this;
return {
api: {
readData: {
url: '<c:url value="/batch/list.ajax"/>',
method: 'POST',
contentType: 'application/x-www-form-urlencoded',
processData: true
}
},
initialRequest: true, // 초기 데이터 요청 여부
serializer: function(params) {
// 기본 파라미터 (페이지 정보 등)
var defaultParams = $.param(params);
// 검색 폼 데이터
setSearchCond();
var searchParams = $.param(SEARCH_COND);
// 모든 파라미터 조합
return defaultParams + '&' + searchParams;
},
// 서버에서 이미 계산된 값을 사용하므로 추가 작업 필요 없음
onSuccess: function(data) {
// 서버에서 계산된 평균 소요시간 사용
}
};
},
/**
* 그리드 인스턴스 생성
*/
create: function() {
var gridConfig = this.initConfig();
var Grid = tui.Grid;
this.instance = gridConfig.instance(Grid);
// 그리드 테마 설정
Grid.applyTheme('striped');
// 그리드 이벤트 설정
this.gridBindEvents();
},
/**
* 그리드 이벤트 바인딩
*/
gridBindEvents: function() {
var self = this;
// 행 더블클릭 이벤트 - 실행 결과 페이지로 이동
this.instance.on('dblclick', function(ev) {
if (ev.rowKey !== undefined && ev.columnName !== '_number' && ev.columnName !== 'actions') {
var rowData = self.instance.getRow(ev.rowKey);
if (rowData && rowData.jobNm && rowData.jobGroup) {
window.location.href = '<c:url value="/batch/execution.do"/>' + '?jobNm=' + rowData.jobNm + '&jobGroup=' + rowData.jobGroup;
}
}
});
// 버튼 클릭 이벤트
this.instance.on('click', function(ev) {
if (ev.columnName === 'actions') {
var rowData = self.instance.getRow(ev.rowKey);
var $target = $(ev.nativeEvent.target);
// 즉시실행 버튼
if ($target.hasClass('btn_trigger')) {
self.triggerJob(rowData);
}
// 일시중지 버튼
else if ($target.hasClass('btn_pause')) {
self.pauseJob(rowData);
}
// 재개 버튼
else if ($target.hasClass('btn_resume')) {
self.resumeJob(rowData);
}
// 완전중지 버튼
else if ($target.hasClass('btn_delete')) {
self.deleteJob(rowData);
}
}
});
},
/**
* 배치 작업 즉시 실행
*/
triggerJob: function(rowData) {
if (confirm('배치 작업 [' + rowData.jobNm + ']을 즉시 실행하시겠습니까?')) {
$.ajax({
url: '<c:url value="/batch/trigger.ajax"/>',
type: 'POST',
data: {
jobId: rowData.jobId,
jobNm: rowData.jobNm,
jobGroup: rowData.jobGroup
},
success: function(response) {
if (response.result) {
alert('배치 작업이 성공적으로 실행되었습니다.');
// 실행 결과 페이지로 이동
window.location.href = '<c:url value="/batch/execution.do"/>' + '?jobNm=' + rowData.jobNm + '&jobGroup=' + rowData.jobGroup;
} else {
alert('배치 작업 실행에 실패했습니다: ' + response.message);
}
}
});
}
},
/**
* 배치 작업 일시 중지
*/
pauseJob: function(rowData) {
if (confirm('배치 작업 [' + rowData.jobNm + ']을 일시 중지하시겠습니까?')) {
$.ajax({
url: '<c:url value="/batch/pause.ajax"/>',
type: 'POST',
data: {
jobId: rowData.jobId,
jobNm: rowData.jobNm,
jobGroup: rowData.jobGroup
},
success: function(response) {
if (response.result) {
alert('배치 작업이 성공적으로 일시 중지되었습니다.');
// 그리드 데이터 갱신
BatchJobList.grid.instance.readData(1);
} else {
alert('배치 작업 일시 중지에 실패했습니다: ' + response.message);
}
}
});
}
},
/**
* 배치 작업 재개
*/
resumeJob: function(rowData) {
if (confirm('배치 작업 [' + rowData.jobNm + ']을 재개하시겠습니까?')) {
$.ajax({
url: '<c:url value="/batch/resume.ajax"/>',
type: 'POST',
data: {
jobId: rowData.jobId,
jobNm: rowData.jobNm,
jobGroup: rowData.jobGroup
},
success: function(response) {
if (response.result) {
alert('배치 작업이 성공적으로 재개되었습니다.');
// 그리드 데이터 갱신
BatchJobList.grid.instance.readData(1);
} else {
alert('배치 작업 재개에 실패했습니다: ' + response.message);
}
}
});
}
},
/**
* 배치 작업 완전 중지(삭제)
*/
deleteJob: function(rowData) {
if (confirm('배치 작업 [' + rowData.jobNm + ']을 완전히 중지하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) {
$.ajax({
url: '<c:url value="/batch/delete.ajax"/>',
type: 'POST',
data: {
jobId: rowData.jobId,
jobNm: rowData.jobNm,
jobGroup: rowData.jobGroup
},
success: function(response) {
if (response.result) {
alert('배치 작업이 성공적으로 중지되었습니다.');
// 그리드 데이터 갱신
BatchJobList.grid.instance.readData(1);
} else {
alert('배치 작업 중지에 실패했습니다: ' + response.message);
}
}
});
}
}
},
/**
* 이벤트 핸들러 설정
*/
eventBindEvents: function() {
var self = this;
// 검색 버튼 클릭 이벤트
$('#search_btn').on('click', function() {
// 페이지를 1로 리셋
$("#page").val(1);
// 그리드 데이터 리로드
self.grid.instance.readData(1);
});
// 배치 잡 등록 버튼 클릭 이벤트
$('#btnNewBatchJob').on('click', function() {
self.openBatchJobPopup();
});
},
/**
* 배치 잡 등록 팝업 열기
*/
openBatchJobPopup: function() {
// 기존 모달 내용을 완전히 제거
$("#detail_pop").empty();
$.ajax({
url: '<c:url value="/batch/batchJobForm_layerPop.ajax" />',
type: 'POST',
dataType: 'html',
success: function(data) {
$("#detail_pop").html(data);
$("#detail_pop").show();
}
});
},
/**
* 모듈 초기화
*/
init: function() {
// 그리드 생성
this.grid.create();
// 이벤트 핸들러 설정
this.eventBindEvents();
}
};
// 페이지 로드 시 초기화
$(function() {
BatchJobList.init();
});
// 전역 네임스페이스에 모듈 노출
window.BatchJobList = BatchJobList;
})(window, jQuery);
</script>

@ -1,217 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- Main body -->
<div class="main_body">
<section id="section8" class="main_bars">
<div class="bgs-main">
<section id="section5">
<div class="sub_title"></div>
<button type="button" id="backBtn" class="newbtn bg4 info_a1">이전으로</button>
</section>
</div>
</section>
<div class="contants_body">
<div class="gs_b_top">
<ul class="lef">
<li class="th">실행 ID</li>
<li>
<span id="executionIdDisplay">${executionId}</span>
<input type="hidden" id="executionId" value="${executionId}">
</li>
<li>
<select name="searchLogLevel" id="searchLogLevel" class="input">
<option value="">모든 레벨</option>
<option value="INFO">INFO</option>
<option value="WARN">WARN</option>
<option value="ERROR">ERROR</option>
</select>
<button type="button" id="search_btn" class="newbtnss bg1" style="display: none;">검색</button>
</li>
</ul>
</div>
<div class="gs_booking">
<div class="row">
<div class="col-sm-12">
<div class="box_column">
<div class="containers">
<div id="grid"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /Main body -->
<script type="text/javascript">
/**
* 배치 작업 로그 조회 모듈
* 배치 작업의 실행 로그를 조회하는 기능을 제공합니다.
*/
(function(window, $) {
'use strict';
var SEARCH_COND = {};
// 검색정보 셋팅
var setSearchCond = function() {
var executionId = $.trim(nvl($("#executionId").val(), ""));
var searchLogLevel = $.trim(nvl($("#searchLogLevel").val(), ""));
SEARCH_COND.executionId = executionId;
SEARCH_COND.searchLogLevel = searchLogLevel;
};
/**
* 배치 작업 로그 조회 네임스페이스
*/
var BatchJobLog = {
/**
* 그리드 관련 객체
*/
grid: {
/**
* 그리드 인스턴스
*/
instance: null,
/**
* 그리드 설정 초기화
* @returns {Object} 그리드 설정 객체
*/
initConfig: function() {
// 데이터 소스 설정
var dataSource = this.createDataSource();
// 그리드 설정 객체 생성
var gridConfig = new XitTuiGridConfig();
// 기본 설정
gridConfig.setOptDataSource(dataSource); // 데이터소스 연결
gridConfig.setOptGridId('grid'); // 그리드를 출력할 Element ID
gridConfig.setOptGridHeight(390); // 그리드 높이(단위: px)
gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px)
gridConfig.setOptRowHeaderType('rowNum'); // 행 첫번째 셀 타입(rowNum: 순번, checkbox: 체크박스, '': 출력 안함)
// 컬럼 정보 설정
gridConfig.setOptColumns([
{
header: '로그 시간',
name: 'logDttm',
width: 150,
align: 'center'
},
{
header: '로그 레벨',
name: 'logLevel',
width: 80,
align: 'center',
formatter: function(value) {
var level = value.value;
var className = '';
if (level === 'ERROR') {
className = 'badge danger';
} else if (level === 'WARN') {
className = 'badge warning';
} else {
className = 'badge success';
}
return '<span class="' + className + '">' + level + '</span>';
}
},
{
header: '로그 메시지',
name: 'logMessage',
align: 'left'
}
]);
return gridConfig;
},
/**
* 데이터 소스 생성
* @returns {Object} 데이터 소스 객체
*/
createDataSource: function() {
return {
api: {
readData: {
url: '<c:url value="/batch/log.ajax"/>',
method: 'POST',
contentType: 'application/x-www-form-urlencoded',
processData: true
}
},
initialRequest: true, // 초기 데이터 요청 여부
serializer: function(params) {
// 기본 파라미터 (페이지 정보 등)
var defaultParams = $.param(params);
// 검색 조건 추가
setSearchCond();
var searchParams = $.param(SEARCH_COND);
// 모든 파라미터 조합
return defaultParams + '&' + searchParams;
}
};
},
/**
* 그리드 인스턴스 생성
*/
create: function() {
var gridConfig = this.initConfig();
var Grid = tui.Grid;
this.instance = gridConfig.instance(Grid);
// 그리드 테마 설정
Grid.applyTheme('striped');
}
},
/**
* 이벤트 핸들러 설정
*/
eventBindEvents: function() {
var self = this;
// 이전으로 버튼 클릭 이벤트
$('#backBtn').on('click', function() {
window.history.back();
});
// 필터 버튼 클릭 이벤트
$('#search_btn').on('click', function() {
self.grid.instance.readData(1);
});
// 로그 레벨 선택 변경 이벤트
$('#searchLogLevel').on('change', function() {
$('#search_btn').trigger('click');
});
},
/**
* 모듈 초기화
*/
init: function() {
// 그리드 생성
this.grid.create();
// 이벤트 핸들러 설정
this.eventBindEvents();
}
};
// 페이지 로드 시 초기화
$(function() {
BatchJobLog.init();
});
// 전역 네임스페이스에 모듈 노출
window.BatchJobLog = BatchJobLog;
})(window, jQuery);
</script>

@ -0,0 +1,16 @@
<%--
Created by IntelliJ IDEA.
User: kurt
Date: 2025. 7. 30.
Time: 오후 2:44
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
init test
</body>
</html>

@ -356,23 +356,23 @@ $(document).ready(function() {
function loadNotifications() {
console.log('[알림] loadNotifications 시작:', new Date().toISOString());
$.ajax({
url: "<c:url value='/common/header/notifications.ajax'/>",
type: "POST",
dataType: "json",
cache: false,
timeout: 10000, // 10초 타임아웃
success: function(response) {
if (response.result) {
handleNotificationsResponse(response.data);
} else {
console.error("알림 정보를 가져오는 중 오류가 발생했습니다:", response.message);
}
},
error: function(xhr, status, error) {
console.error("알림 정보를 가져오는 중 오류가 발생했습니다:", error);
}
});
<%--$.ajax({--%>
<%-- url: "<c:url value='/common/header/notifications.ajax'/>",--%>
<%-- type: "POST",--%>
<%-- dataType: "json",--%>
<%-- cache: false,--%>
<%-- timeout: 10000, // 10초 타임아웃--%>
<%-- success: function(response) {--%>
<%-- if (response.result) {--%>
<%-- handleNotificationsResponse(response.data);--%>
<%-- } else {--%>
<%-- console.error("알림 정보를 가져오는 중 오류가 발생했습니다:", response.message);--%>
<%-- }--%>
<%-- },--%>
<%-- error: function(xhr, status, error) {--%>
<%-- console.error("알림 정보를 가져오는 중 오류가 발생했습니다:", error);--%>
<%-- }--%>
<%--});--%>
}
/**
@ -604,22 +604,22 @@ $(document).ready(function() {
}
// AJAX 요청으로 스케줄 데이터 가져오기
$.ajax({
url: "<c:url value='/common/header/schedule/list.ajax'/>",
type: "POST",
data: searchParams,
dataType: "json",
cache: false,
timeout: 10000, // 10초 타임아웃
success: function(response) {
console.log('[스케줄] AJAX 응답 수신:', new Date().toISOString());
handleScheduleResponse(response, now, $ticker);
},
error: function(xhr, status, error) {
console.error("스케줄 데이터를 가져오는 중 오류가 발생했습니다:", error);
$ticker.empty().append('<div class="schedule-item">일정을 불러올 수 없습니다.</div>');
}
});
<%--$.ajax({--%>
<%-- url: "<c:url value='/common/header/schedule/list.ajax'/>",--%>
<%-- type: "POST",--%>
<%-- data: searchParams,--%>
<%-- dataType: "json",--%>
<%-- cache: false,--%>
<%-- timeout: 10000, // 10초 타임아웃--%>
<%-- success: function(response) {--%>
<%-- console.log('[스케줄] AJAX 응답 수신:', new Date().toISOString());--%>
<%-- handleScheduleResponse(response, now, $ticker);--%>
<%-- },--%>
<%-- error: function(xhr, status, error) {--%>
<%-- console.error("스케줄 데이터를 가져오는 중 오류가 발생했습니다:", error);--%>
<%-- $ticker.empty().append('<div class="schedule-item">일정을 불러올 수 없습니다.</div>');--%>
<%-- }--%>
<%--});--%>
}
/**

@ -4,244 +4,16 @@
<section id="section8" class="main_bars">
<div class="bgs-main">
<section id="section5">
<div class="sub_title"> - 디자인 샘플 페이지</div>
<button class="newbtn bg1 class_detail" type="button" onclick="alert('detail sample');">상세보기</button>
<button class="newbtn bg4 iconz" type="button" onclick="alert('excel download');">
<span class="mdi mdi-microsoft-excel"></span>
</button>
<div class="sub_title"> - 대시보드</div>
</section>
</div>
</section>
<div class="contants_body">
<div class="gs_b_top">
<ul>
<li class="th">단속일자</li>
<li><input type="text" class="input calender" id="datepicker" placeholder="시작일"></li>
<li>~</li>
<li><input type="text" class="input calender" id="datepicker2" placeholder="종료일"></li>
<li class="th">상세검색</li>
<li>
<select class="input">
<option>납부유형</option>
</select>
</li>
<li>
<select class="input">
<option>구분</option>
</select>
</li>
<li>
<select class="input">
<option>납부유형</option>
</select>
</li>
<li><input type="text" class="input" placeholder="단속자명"></li>
<li>
<button class="newbtnss bg1">검색</button>
</li>
</ul>
</div>
<div class="box_column margin_top_20">
<div class="containers">
<div class="tabels cols2">
<table id="example" class="display">
<thead>
<tr>
<th><input type="checkbox"></th>
<th>No.</th>
<th>단속일시</th>
<th>납부일시</th>
<th>납부확인</th>
<th>단속확인</th>
<th>회원코드</th>
<th>단속자명</th>
<th>휴대번호</th>
<th>구분</th>
<th>위반명</th>
<th>납부기간</th>
<th>회차</th>
<th>납부유형</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>1</td>
<td>2021-10-20 10:10</td>
<td>2021-10-20 10:10</td>
<td><label class="yesno no">N</label></td>
<td><label class="yesno yes">Y</label></td>
<td>00000000</td>
<td>홍길동</td>
<td>010-0000-0000</td>
<td>주정차</td>
<td>주정차 위반</td>
<td>2021-10-20 ~ 2021-12-20</td>
<td>4/12</td>
<td>가상계좌결제</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

Loading…
Cancel
Save