오류 수정

dev
박성영 5 months ago
parent a50c92f5ae
commit 16853564f3

@ -13,6 +13,7 @@ import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
@ -27,17 +28,32 @@ import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* MyBatis Query Interceptor
* MyBatis
*
* This interceptor logs detailed information about SQL queries executed by MyBatis,
* including the original SQL, actual SQL with parameters, and parameter values.
* MyBatis SQL .
* SQL, SQL( ), .
* local dev .
*
* :
* - SQL
* -
* -
* - include SQL
* - (String, Number, Boolean, Date, LocalDateTime )
*/
@Component
@Profile({"local", "dev"}) // local과 dev 프로파일에서만 활성화
@ -48,105 +64,205 @@ import java.util.stream.Collectors;
@Slf4j
public class MyBatisQueryInterceptor implements Interceptor {
/**
* .
*
* @param target
* @return
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 실제 실행될 쿼리 추출 (MyBatis 내부에서 처리된 결과)
String actualSql = getActualSql(boundSql, parameter);
// 파라미터 정보 추출
Map<String, Object> paramMap = extractDetailedParameters(parameter, parameterMappings);
logDetailedQueryInfo(ms.getId(), sql, actualSql, paramMap);
// 원래 쿼리 실행
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
// 쿼리 실행 시간 로깅
log.debug("Query execution time: {} ms", (endTime - startTime));
public Object plugin(Object target) {
return org.apache.ibatis.plugin.Plugin.wrap(target, this);
}
/**
* .
*
* @param properties
*/
@Override
public void setProperties(java.util.Properties properties) {
// 필요한 경우 속성 설정
}
return result;
/**
* MyBatis .
* include SQL .
*
* @param invocation MyBatis
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 로깅 레벨 확인으로 불필요한 처리 방지
boolean isDebugEnabled = log.isDebugEnabled();
boolean isInfoEnabled = log.isInfoEnabled();
if (!isInfoEnabled && !isDebugEnabled) {
// 로깅이 비활성화된 경우 바로 원래 쿼리 실행
return invocation.proceed();
}
try {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
// 쿼리 정보 추출
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 실제 실행될 쿼리 추출 (MyBatis 내부에서 처리된 결과)
String actualSql = getActualSql(boundSql, parameter);
// 파라미터 정보 추출
Map<String, Object> paramMap = extractDetailedParameters(parameter, parameterMappings);
// 쿼리 정보 로깅
logDetailedQueryInfo(ms.getId(), sql, actualSql, paramMap);
// 원래 쿼리 실행 및 실행 시간 측정
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
// 쿼리 실행 시간 로깅
if (isDebugEnabled) {
log.debug("쿼리 실행 시간: {} ms", (endTime - startTime));
}
return result;
} catch (Exception e) {
// 인터셉터 내부 오류는 로깅하고 원래 쿼리 실행 (인터셉터 오류로 쿼리가 실패하지 않도록)
log.warn("쿼리 인터셉터 처리 중 오류 발생: {}", e.getMessage());
if (isDebugEnabled) {
log.debug("인터셉터 오류 상세 정보:", e);
}
return invocation.proceed();
}
}
/**
* SQL .
* SQL '?' .
* include SQL .
*/
private String getActualSql(BoundSql boundSql, Object parameter) {
String sql = boundSql.getSql();
if (parameter == null) {
return sql;
}
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings.isEmpty()) {
if (parameterMappings == null || parameterMappings.isEmpty()) {
return sql;
}
// SQL 복사본 생성
String actualSql = sql;
try {
// 파라미터 값 추출
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
Object value = null;
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameter instanceof Map) {
value = ((Map<?, ?>) parameter).get(propertyName);
} else if (parameter instanceof String) {
// String 타입 파라미터 처리
value = parameter;
} else if (parameter instanceof Number) {
// Number 타입 파라미터 처리 (Integer, Long, Double 등)
value = parameter;
} else if (parameter instanceof Boolean) {
// Boolean 타입 파라미터 처리
value = parameter;
// SQL에서 실제 파라미터 바인딩 위치 확인
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(sql);
StringBuffer result = new StringBuffer();
int paramIndex = 0;
while (matcher.find() && paramIndex < parameterMappings.size()) {
ParameterMapping parameterMapping = parameterMappings.get(paramIndex);
Object value = null;
try {
// include SQL 내부의 파라미터 처리 개선
if (parameter instanceof MapperMethod.ParamMap) {
MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) parameter;
String propertyName = parameterMapping.getProperty();
// 직접 키로 접근
if (paramMap.containsKey(propertyName)) {
value = paramMap.get(propertyName);
}
// param1, param2 등으로 접근
else if (propertyName.matches("param\\d+")) {
value = paramMap.get(propertyName);
}
// 중첩 객체 내부 필드 접근
else if (propertyName.contains(".")) {
value = getNestedParameterValue(paramMap, propertyName);
}
// VO 객체 내부 필드 접근
else {
for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
if (entry.getValue() != null) {
Object nestedValue = getParameterValue(entry.getValue(), propertyName);
if (nestedValue != null) {
value = nestedValue;
break;
}
}
}
}
} else {
value = getParameterValue(parameter, propertyName);
value = getParameterValue(parameter, parameterMapping.getProperty());
}
String replacement = formatParameterValue(value);
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
} catch (Exception e) {
log.debug("파라미터 값 추출 실패: {}, 기본값 사용", parameterMapping.getProperty());
matcher.appendReplacement(result, "?");
}
paramIndex++;
}
matcher.appendTail(result);
return result.toString();
}
String valueStr = value != null ? value.toString() : "null";
// SQL 인젝션 방지를 위한 문자열 이스케이프 처리
valueStr = valueStr.replace("'", "''");
// 다양한 데이터 타입에 대한 처리
if (value instanceof String) {
// 문자열 타입은 따옴표로 감싸기
actualSql = actualSql.replaceFirst("\\?", "'" + valueStr + "'");
} else if (value instanceof Number) {
// 숫자 타입 (Integer, Long, Double 등)은 그대로 사용
actualSql = actualSql.replaceFirst("\\?", valueStr);
} else if (value instanceof Boolean) {
// Boolean 타입은 그대로 사용
actualSql = actualSql.replaceFirst("\\?", valueStr);
} else if (value instanceof java.util.Date) {
// 표준 타임스탬프 포맷 사용
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
actualSql = actualSql.replaceFirst("\\?", "'" + sdf.format(value) + "'");
} else if (value instanceof java.time.LocalDateTime) {
// Java 8 LocalDateTime 처리
actualSql = actualSql.replaceFirst("\\?", "'" + value.toString().replace('T', ' ') + "'");
} else {
// 기타 타입은 null이 아닌 경우 따옴표로 감싸기
actualSql = actualSql.replaceFirst("\\?", value != null ? "'" + valueStr + "'" : valueStr);
}
/**
* .
*/
private Object getNestedParameterValue(MapperMethod.ParamMap<?> paramMap, String propertyPath) {
String[] parts = propertyPath.split("\\.");
Object current = null;
// 첫 번째 키로 객체 찾기
for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
if (entry.getKey().equals(parts[0]) ||
(entry.getValue() != null && entry.getValue().getClass().getSimpleName().toLowerCase().contains(parts[0].toLowerCase()))) {
current = entry.getValue();
break;
}
} catch (Exception e) {
log.warn("Failed to get actual SQL with parameters", e);
return sql;
}
// 중첩 필드 접근
for (int i = 1; i < parts.length && current != null; i++) {
current = getParameterValue(current, parts[i]);
}
return current;
}
return actualSql;
/**
* SQL .
*/
private String formatParameterValue(Object value) {
if (value == null) {
return "NULL";
}
if (value instanceof String) {
return "'" + value.toString().replace("'", "''") + "'";
} else if (value instanceof Number || value instanceof Boolean) {
return value.toString();
} else if (value instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return "'" + sdf.format((Date) value) + "'";
} else if (value instanceof java.time.LocalDateTime) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return "'" + ((java.time.LocalDateTime) value).format(formatter) + "'";
} else if (value instanceof java.time.LocalDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return "'" + ((java.time.LocalDate) value).format(formatter) + "'";
} else {
return "'" + value.toString().replace("'", "''") + "'";
}
}
private Object getParameterValue(Object parameter, String propertyName) {
@ -170,113 +286,308 @@ private Object getParameterValue(Object parameter, String propertyName) {
}
}
/**
* .
* include SQL .
*/
private Map<String, Object> extractDetailedParameters(Object parameter, List<ParameterMapping> parameterMappings) {
Map<String, Object> paramMap = new HashMap<>();
if (parameter == null) {
Map<String, Object> paramMap = new LinkedHashMap<>();
if (parameter == null || parameterMappings == null || parameterMappings.isEmpty()) {
return paramMap;
}
if (parameter instanceof Map) {
paramMap.putAll((Map<String, Object>) parameter);
} else {
// 객체의 필드 정보도 추출
paramMap.put("param", parameter);
extractObjectFields(parameter, paramMap);
// 실제 사용되는 파라미터만 추출
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
Object value = null;
try {
if (parameter instanceof MapperMethod.ParamMap) {
MapperMethod.ParamMap<?> mapperParamMap = (MapperMethod.ParamMap<?>) parameter;
// 1. 직접 키로 접근
if (mapperParamMap.containsKey(propertyName)) {
value = mapperParamMap.get(propertyName);
}
// 2. 중첩 객체 내부 필드 접근 (include SQL에서 자주 발생)
else if (propertyName.contains(".")) {
value = getNestedParameterValue(mapperParamMap, propertyName);
}
// 3. VO 객체 내부 검색
else {
for (Map.Entry<String, ?> entry : mapperParamMap.entrySet()) {
if (entry.getValue() != null) {
Object nestedValue = getParameterValue(entry.getValue(), propertyName);
if (nestedValue != null) {
value = nestedValue;
break;
}
}
}
}
// 4. 전체 맵 내용도 포함 (디버깅용)
paramMap.putAll(mapperParamMap);
} else {
value = getParameterValue(parameter, propertyName);
extractObjectFields(parameter, paramMap);
}
if (value != null) {
paramMap.put(propertyName, value);
}
} catch (Exception e) {
log.debug("파라미터 추출 실패: {} - {}", propertyName, e.getMessage());
}
}
return paramMap;
}
/**
* .
* include SQL .
*
* @param obj
* @param paramMap
*/
private void extractObjectFields(Object obj, Map<String, Object> paramMap) {
if (obj == null) {
return;
}
// 기본 타입이나 래퍼 타입은 처리하지 않음
if (obj instanceof String || obj instanceof Number || obj instanceof Boolean ||
obj instanceof java.util.Date || obj instanceof java.time.temporal.Temporal) {
return;
}
try {
Class<?> currentClass = obj.getClass();
while (currentClass != null) {
// 시스템 클래스는 처리하지 않음
if (currentClass.getName().startsWith("java.") ||
currentClass.getName().startsWith("javax.") ||
currentClass.getName().startsWith("sun.")) {
return;
}
// 현재 클래스부터 상위 클래스까지 순회하면서 필드 추출
while (currentClass != null && !currentClass.getName().startsWith("java.")) {
Field[] fields = currentClass.getDeclaredFields();
for (Field field : fields) {
try {
// String 타입의 객체는 건너뛰기
if (obj instanceof String) {
// static 또는 transient 필드는 건너뛰기
if (java.lang.reflect.Modifier.isStatic(field.getModifiers()) ||
java.lang.reflect.Modifier.isTransient(field.getModifiers())) {
continue;
}
// 시스템 클래스의 필드는 건너뛰기
if (field.getDeclaringClass().getName().startsWith("java.")) {
continue;
}
field.setAccessible(true);
Object value = field.get(obj);
paramMap.put(field.getName(), value);
// 값이 null이 아닌 경우만 맵에 추가
if (value != null) {
String fieldName = field.getName();
paramMap.put(fieldName, value);
// MANAGER_CD 특별 처리 (include SQL 내부에서도 적용)
if (fieldName.equalsIgnoreCase("currentUserId")) {
paramMap.put("MANAGER_CD", value);
paramMap.put("manager_cd", value);
}
// 중첩된 객체의 필드도 추출 (깊이 1까지만)
if (!(value instanceof Map) &&
!(value instanceof Collection) &&
!(value instanceof String) &&
!(value instanceof Number) &&
!(value instanceof Boolean) &&
!value.getClass().getName().startsWith("java.")) {
// 중첩된 객체의 필드를 추출하여 필드명.중첩필드명 형태로 저장
Class<?> nestedClass = value.getClass();
Field[] nestedFields = nestedClass.getDeclaredFields();
for (Field nestedField : nestedFields) {
try {
if (java.lang.reflect.Modifier.isStatic(nestedField.getModifiers()) ||
java.lang.reflect.Modifier.isTransient(nestedField.getModifiers())) {
continue;
}
nestedField.setAccessible(true);
Object nestedValue = nestedField.get(value);
if (nestedValue != null) {
String nestedFieldName = nestedField.getName();
paramMap.put(nestedFieldName, nestedValue);
}
} catch (IllegalAccessException | SecurityException e) {
// 중첩 필드 접근 실패는 무시하고 계속 진행
}
}
}
}
} catch (IllegalAccessException | SecurityException e) {
// 개별 필드 접근 실패는 무시하고 계속 진행
log.debug("필드 접근 실패: {} ({})", field.getName(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("필드 접근 실패: {} ({})", field.getName(), e.getMessage());
}
}
}
currentClass = currentClass.getSuperclass();
}
} catch (Exception e) {
log.warn("객체 필드 추출 실패", e);
log.warn("객체 필드 추출 중 오류 발생: {}", e.getMessage());
if (log.isDebugEnabled()) {
log.debug("상세 오류 정보:", e);
}
}
}
/**
* .
*
* @param mapperMethod
* @param originalSql SQL
* @param actualSql SQL ( )
* @param paramMap
*/
private void logDetailedQueryInfo(String mapperMethod, String originalSql, String actualSql, Map<String, Object> paramMap) {
// 로깅 레벨 확인으로 불필요한 문자열 연산 방지
if (!log.isInfoEnabled()) {
return;
}
StringBuilder logMessage = new StringBuilder();
logMessage.append("\n");
logMessage.append("┌─────────────── MyBatis Query Details ───────────────\n");
logMessage.append("│ Mapper Method: ").append(mapperMethod).append("\n");
logMessage.append("│ Parameters: ").append(paramMap).append("\n");
// 원본 SQL 포맷팅, prd, 운영
//logMessage.append("│ Original SQL:\n");
//formatSqlInLog(logMessage, originalSql);
// 실제 실행 SQL 포맷팅, local, dev 로컬 개발
logMessage.append("│ Actual SQL:\n");
logMessage.append("┌─────────────── MyBatis 쿼리 상세 정보 ───────────────\n");
logMessage.append("│ 매퍼 메서드: ").append(mapperMethod).append("\n");
// 파라미터 정보 로깅 (너무 길면 축약)
String paramString = paramMap.toString();
if (paramString.length() > 1000) {
paramString = paramString.substring(0, 997) + "...";
}
logMessage.append("│ 파라미터: ").append(paramString).append("\n");
// 실제 실행 SQL 포맷팅 (local, dev 환경에서만)
logMessage.append("│ 실행 SQL:\n");
formatSqlInLog(logMessage, actualSql);
logMessage.append("└──────────────────────────────────────────────────────");
log.info(logMessage.toString());
}
/**
* SQL .
*
* @param logMessage
* @param sql SQL
*/
private void formatSqlInLog(StringBuilder logMessage, String sql) {
// SQL 키워드 하이라이트 및 들여쓰기
String formattedSql = formatSql(sql);
String[] lines = formattedSql.split("\n");
try {
// SQL 키워드 하이라이트 및 들여쓰기
String formattedSql = formatSql(sql);
String[] lines = formattedSql.split("\n");
for (String line : lines) {
//logMessage.append("│ ").append(line).append("\n");
logMessage.append(line).append("\n");
for (String line : lines) {
logMessage.append("│ ").append(line).append("\n");
}
} catch (Exception e) {
// 포맷팅 실패 시 원본 SQL 출력
logMessage.append("│ ").append(sql).append("\n");
log.debug("SQL 포맷팅 실패: {}", e.getMessage());
}
}
/**
* SQL .
*
* @param sql SQL
* @return SQL
*/
private String formatSql(String sql) {
if (sql == null || sql.trim().isEmpty()) {
return "";
}
try {
// SQL 파서를 사용한 포맷팅
Statement statement = CCJSqlParserUtil.parse(sql);
return formatStatement(statement, 0);
} catch (JSQLParserException e) {
log.debug("SQL 파싱 실패. 기본 포맷팅으로 대체합니다.", e);
log.info("SQL 파싱 실패. 기본 포맷팅으로 대체합니다.");
// 파싱 실패 시 기본 포맷팅 사용
return sql.replaceAll("\\s+", " ").trim();
if (log.isDebugEnabled()) {
log.debug("SQL 파싱 실패. 기본 포맷팅으로 대체합니다: {}", e.getMessage());
}
// 기본 포맷팅: 키워드 대문자화 및 줄바꿈 추가
String formattedSql = sql.replaceAll("\\s+", " ").trim();
// 주요 SQL 키워드 앞에 줄바꿈 추가
formattedSql = formattedSql
.replaceAll("(?i)\\s+SELECT\\s+", "\nSELECT ")
.replaceAll("(?i)\\s+FROM\\s+", "\nFROM ")
.replaceAll("(?i)\\s+WHERE\\s+", "\nWHERE ")
.replaceAll("(?i)\\s+AND\\s+", "\n AND ")
.replaceAll("(?i)\\s+OR\\s+", "\n OR ")
.replaceAll("(?i)\\s+GROUP BY\\s+", "\nGROUP BY ")
.replaceAll("(?i)\\s+HAVING\\s+", "\nHAVING ")
.replaceAll("(?i)\\s+ORDER BY\\s+", "\nORDER BY ")
.replaceAll("(?i)\\s+LIMIT\\s+", "\nLIMIT ")
.replaceAll("(?i)\\s+OFFSET\\s+", "\nOFFSET ")
.replaceAll("(?i)\\s+UNION\\s+", "\nUNION\n")
.replaceAll("(?i)\\s+INSERT\\s+INTO\\s+", "\nINSERT INTO ")
.replaceAll("(?i)\\s+VALUES\\s+", "\nVALUES ")
.replaceAll("(?i)\\s+UPDATE\\s+", "\nUPDATE ")
.replaceAll("(?i)\\s+SET\\s+", "\nSET ")
.replaceAll("(?i)\\s+DELETE\\s+FROM\\s+", "\nDELETE FROM ")
.replaceAll("(?i)\\s+JOIN\\s+", "\nJOIN ")
.replaceAll("(?i)\\s+LEFT\\s+JOIN\\s+", "\nLEFT JOIN ")
.replaceAll("(?i)\\s+RIGHT\\s+JOIN\\s+", "\nRIGHT JOIN ")
.replaceAll("(?i)\\s+INNER\\s+JOIN\\s+", "\nINNER JOIN ")
.replaceAll("(?i)\\s+OUTER\\s+JOIN\\s+", "\nOUTER JOIN ");
return formattedSql;
}
}
/**
* SQL .
*
* @param statement SQL
* @param indent
* @return SQL
*/
private String formatStatement(Statement statement, int indent) {
if (statement == null) {
return "";
}
StringBuilder result = new StringBuilder();
String indentation = String.join("", java.util.Collections.nCopies(indent, " "));
if (statement instanceof Select) {
formatSelect((Select) statement, result, indent);
} else if (statement instanceof Insert) {
formatInsert((Insert) statement, result, indent);
} else if (statement instanceof Update) {
formatUpdate((Update) statement, result, indent);
} else if (statement instanceof Delete) {
formatDelete((Delete) statement, result, indent);
try {
if (statement instanceof Select) {
formatSelect((Select) statement, result, indent);
} else if (statement instanceof Insert) {
formatInsert((Insert) statement, result, indent);
} else if (statement instanceof Update) {
formatUpdate((Update) statement, result, indent);
} else if (statement instanceof Delete) {
formatDelete((Delete) statement, result, indent);
} else {
// 지원하지 않는 문장 유형
result.append(statement.toString());
}
} catch (Exception e) {
// 포맷팅 중 오류 발생 시 원본 문장 반환
if (log.isDebugEnabled()) {
log.debug("SQL 문장 포맷팅 실패: {}", e.getMessage());
}
result.append(statement.toString());
}
return result.toString();

@ -68,22 +68,6 @@ login:
DefaultPassword: ibmspassword # 초기 비밀번호, 초기화 비밀번호
allowMultipleLogin: true # 동시 접속 가능 여부 (true: 가능, false: 불가능)
# Notice board configuration
notice:
writer: 관리자
# DB(tb_notification_target_setting)에 알림받는 대상 정보가 없을 경우, yml 파일에 설정된 USER_ID 기준으로 등록
notification:
receivers:
- ADMIN # 시스템 관리자
# 알림 관련 URL 설정 (기본값은 NotificationProperties에 정의됨)
urls:
calendar-sample: /template/calendarSample/calendarSample.do
qna-manage-view: /bbs/manage/qna/%s/view.do?postId=%s
qna-user-view: /bbs/user/qna/%s/view.do?postId=%s
batch-job-execution: /batch/log.do?executionId=%s
# Query executor configuration
query-executor:
transaction:

@ -4,133 +4,6 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="go.kr.project.ma30.mapper.Ma30Mapper">
<!-- 단속자료 조회 -->
<select id="findListCount" parameterType="go.kr.project.ma30.model.Ma30FindRlistSearchVO" resultType="int">
SELECT COUNT(A.ACTION_YEAR) CNT
FROM RGL_RET_M A
INNER JOIN JUKBAL_METHOD_C B ON (A.JB_METHOD_CD=B.JB_METHOD_CD)
INNER JOIN BDONG_C C ON (A.ORG_CD=C.ORG_CD AND A.BDONG_CD=C.BDONG_CD)
LEFT OUTER JOIN UNLAW_ACT_D_DAEPYO D ON (A.ORG_CD=D.ORG_CD AND A.AREA_TYPE=D.AREA_TYPE AND A.MNG_YY=D.MNG_YY AND A.MNG_NO=D.MNG_NO)
LEFT OUTER JOIN UNLAW_POS_D_DAEPYO E ON (D.ORG_CD=E.ORG_CD AND D.AREA_TYPE=E.AREA_TYPE AND D.MNG_YY=E.MNG_YY AND D.MNG_NO=E.MNG_NO AND D.ACT_NO=E.ACT_NO)
WHERE A.DEL_YN = 0
AND A.ORG_CD = #{currentUserOrgCd} /* TODO : 일산서구코드인데 필요한가? */
<if test='schMyworkGubun == "Y"'>
<include refid="findListMywork"/>
</if>
<if test='schMyworkGubun == "N"'>
<include refid="findListDefault"/>
</if>
<if test='schDataChk == "B"'>
AND A.REPETITION_LEVY = "신규"
</if>
<if test='schDataChk == "C"'>
AND A.REPETITION_LEVY = "재부과"
</if>
</select>
<!-- 단속자료 조회 -->
<select id="findList" parameterType="go.kr.project.ma30.model.Ma30FindRlistSearchVO" resultType="go.kr.project.ma30.model.Ma30FindListVO">
SELECT A.ORG_CD as orgCd
, A.AREA_TYPE as areaType
, A.MNG_YY as mngYy
, A.MNG_NO as mngNo
, A.RECEIPT_YM as receiptYm
, A.RECEIPT_NO as receiptNo
, A.JB_METHOD_CD as jbMethodCd
, B.JB_METHOD_NAME as jbMethodName
, A.BDONG_CD as bdongCd
, C.BDONG_NAME as bdongName
, A.JOSA_DT as josaDt
, A.JOSA_ENDDT as josaEnddt
, CASE WHEN LENGTH(A.JOSA_ENDDT) = 8 THEN CONCAT(GET_MASK_DATE(A.JOSA_DT, '-'), ' ~ ', GET_MASK_DATE(A.JOSA_ENDDT, '-'))
ELSE GET_MASK_DATE(A.JOSA_DT, '-')
END AS josaDate
, A.FST_WARNNING_DT as fstWarnningDt
, A.FST_WARNNING_ENDDT as fstWarnningEnddt
, A.SND_WARNNING_DT as sndWarnningDt
, A.SND_WARNNING_ENDDT as sndWarnningEnddt
, A.GOBAL_DT as gobalDt
, A.GOBAL_ENDDT as gobalEnddt
, A.PRE_BUGWA_DT as preBugwaDt
, A.PRE_BUGWA_ENDDT as preBugwaEnddt
, A.NAPBU_CHOKGU_DT as napbuChokguDt <!-- 2021.06.09 현수 추가 (납부촉구 기능 추가건) -->
, A.NAPBU_CHOKGU_ENDDT as napbuChokguEnddt <!-- 2021.06.09 현수 추가 (납부촉구 기능 추가건) -->
, A.BUGWA_DT as bugwaDt
, A.BUGWA_ENDDT as bugwaEnddt
, A.RECOVER_DT as recoverDt
, A.JOCHI_YN as jochiYn
, A.JOCHI_DT as jochiDt
, A.REMARK as remark
, A.REPETITION_LEVY as repetitionLevy
, A.JIBHANG_DT as jibhangDt
, A.BUGWA_TARGET as bugwaTarget
, A.ACTION_YEAR as actionYear
, D.ACT_NO as actNo
, D.ACT_CNT as actCnt
, D.USE_OBJ_NAME as useObjName
, D.UNLAW_SHORT_NAME as unlawShortName
, D.STT_NAME as sttName
, D.UNLAW_SIZE as unlawSize
, D.ACT_START_DT as actStartDt
, D.LAW_NAME as lawName
, D.WIBAN_JOHANG as wibanJohang
, D.BUGWA_LAW_CD as bugwaLawCd
, E.JIMOK_NAME as jimokName
, D.H_RES_DIV as hResDiv
, D.H_RES_NO as hResNo
, D.H_RES_NAME as hResName
, E.O_RES_DIV as oResDiv
, E.O_RES_NO as oResNo
, E.O_RES_NAME as oResName
<!-- 도로명 주소에 따른 변경(위치정보가 아닌 소유자 정보가 들어가므로
쿼리 검증 필요)
, CASE WHEN E.ADDR_BON = '0' THEN ''
WHEN E.ADDR_BON = '' THEN ''
ELSE CASE WHEN E.ADDR_BU = '0' THEN RTRIM(E.ADDR_BON)
WHEN E.ADDR_BU = '' THEN RTRIM(E.ADDR_BON)
ELSE CONCAT(RTRIM(E.ADDR_BON), '-', RTRIM(E.ADDR_BU))
END
END AS POS_BUNJI -->
, CASE WHEN E.BULD_MNNM = '0' THEN ''
WHEN E.BULD_MNNM = '' THEN ''
ELSE CASE WHEN E.BULD_SLNO = '0' THEN RTRIM(E.BULD_MNNM)
WHEN E.BULD_SLNO = '' THEN RTRIM(E.BULD_MNNM)
ELSE CONCAT(RTRIM(E.BULD_MNNM), '-', RTRIM(E.BULD_SLNO))
END
END AS posBunji
, A.REGSTR_MNG_NO as regstrMngNo <!-- [2022.01.10] 임현수 : 담당자 요청에 의하여 관리번호 별도로 추가됨 -->
<!-- ,datediff(date_format(GREATEST(IFNULL(A.FST_WARNNING_ENDDT,'0'), IFNULL(A.SND_WARNNING_ENDDT,'0'), IFNULL(A.GOBAL_ENDDT,'0'),
IFNULL(A.PRE_BUGWA_ENDDT,'0'),IFNULL(A.BUGWA_ENDDT,'0'),IFNULL(A.NAPBU_CHOKGU_ENDDT,'0')), '%Y%m%d'),date_format(now(), '%Y%m%d')) AS REMAING_EXTENT -->
,CASE
WHEN A.RECOVER_DT IS NULL THEN datediff(date_format(GREATEST(IFNULL(A.FST_WARNNING_ENDDT,'0'), IFNULL(A.SND_WARNNING_ENDDT,'0'), IFNULL(A.GOBAL_ENDDT,'0'),
IFNULL(A.PRE_BUGWA_ENDDT,'0'),IFNULL(A.BUGWA_ENDDT,'0'),IFNULL(A.NAPBU_CHOKGU_ENDDT,'0')), '%Y%m%d'),date_format(now(), '%Y%m%d'))
ELSE ''
END AS remaingExtent
,E.ADDR_ETC AS addrEtc
FROM RGL_RET_M A
INNER JOIN JUKBAL_METHOD_C B ON (A.JB_METHOD_CD=B.JB_METHOD_CD)
INNER JOIN BDONG_C C ON (A.ORG_CD=C.ORG_CD AND A.BDONG_CD=C.BDONG_CD)
LEFT OUTER JOIN UNLAW_ACT_D_DAEPYO D ON (A.ORG_CD=D.ORG_CD AND A.AREA_TYPE=D.AREA_TYPE AND A.MNG_YY=D.MNG_YY AND A.MNG_NO=D.MNG_NO)
LEFT OUTER JOIN UNLAW_POS_D_DAEPYO E ON (D.ORG_CD=E.ORG_CD AND D.AREA_TYPE=E.AREA_TYPE AND D.MNG_YY=E.MNG_YY AND D.MNG_NO=E.MNG_NO AND D.ACT_NO=E.ACT_NO)
WHERE A.DEL_YN = 0
AND A.ORG_CD = #{currentUserOrgCd} /* TODO : 일산서구코드인데 필요한가? */
<if test='schMyworkGubun == "Y"'>
<include refid="findListMywork"/>
</if>
<if test='schMyworkGubun == "N"'>
<include refid="findListDefault"/>
</if>
<if test='schDataChk == "B"'>
AND A.REPETITION_LEVY = "신규"
</if>
<if test='schDataChk == "C"'>
AND A.REPETITION_LEVY = "재부과"
</if>
<if test='pagingYn != null and pagingYn == "Y"'>
LIMIT #{startIndex}, #{perPage}
</if>
</select>
<sql id="findListMywork">
<if test="schUserDongMap == true"> <!-- 사용자 관리동만 검색 -->
<!-- [2021.10.18] 임현수 수정 : 조건절이 잘못 되어 있어 수정함
@ -377,4 +250,131 @@
</if>
</sql>
<!-- 단속자료 조회 -->
<select id="findListCount" parameterType="Ma30FindRlistSearchVO" resultType="int">
SELECT COUNT(A.ACTION_YEAR) CNT
FROM RGL_RET_M A
INNER JOIN JUKBAL_METHOD_C B ON (A.JB_METHOD_CD=B.JB_METHOD_CD)
INNER JOIN BDONG_C C ON (A.ORG_CD=C.ORG_CD AND A.BDONG_CD=C.BDONG_CD)
LEFT OUTER JOIN UNLAW_ACT_D_DAEPYO D ON (A.ORG_CD=D.ORG_CD AND A.AREA_TYPE=D.AREA_TYPE AND A.MNG_YY=D.MNG_YY AND A.MNG_NO=D.MNG_NO)
LEFT OUTER JOIN UNLAW_POS_D_DAEPYO E ON (D.ORG_CD=E.ORG_CD AND D.AREA_TYPE=E.AREA_TYPE AND D.MNG_YY=E.MNG_YY AND D.MNG_NO=E.MNG_NO AND D.ACT_NO=E.ACT_NO)
WHERE A.DEL_YN = 0
AND A.ORG_CD = #{currentUserOrgCd} /* TODO : 일산서구코드인데 필요한가? */
<if test='schMyworkGubun == "Y"'>
<include refid="findListMywork"/>
</if>
<if test='schMyworkGubun == "N"'>
<include refid="findListDefault"/>
</if>
<if test='schDataChk == "B"'>
AND A.REPETITION_LEVY = "신규"
</if>
<if test='schDataChk == "C"'>
AND A.REPETITION_LEVY = "재부과"
</if>
</select>
<!-- 단속자료 조회 -->
<select id="findList" parameterType="Ma30FindRlistSearchVO" resultType="Ma30FindListVO">
SELECT A.ORG_CD as orgCd
, A.AREA_TYPE as areaType
, A.MNG_YY as mngYy
, A.MNG_NO as mngNo
, A.RECEIPT_YM as receiptYm
, A.RECEIPT_NO as receiptNo
, A.JB_METHOD_CD as jbMethodCd
, B.JB_METHOD_NAME as jbMethodName
, A.BDONG_CD as bdongCd
, C.BDONG_NAME as bdongName
, A.JOSA_DT as josaDt
, A.JOSA_ENDDT as josaEnddt
, CASE WHEN LENGTH(A.JOSA_ENDDT) = 8 THEN CONCAT(GET_MASK_DATE(A.JOSA_DT, '-'), ' ~ ', GET_MASK_DATE(A.JOSA_ENDDT, '-'))
ELSE GET_MASK_DATE(A.JOSA_DT, '-')
END AS josaDate
, A.FST_WARNNING_DT as fstWarnningDt
, A.FST_WARNNING_ENDDT as fstWarnningEnddt
, A.SND_WARNNING_DT as sndWarnningDt
, A.SND_WARNNING_ENDDT as sndWarnningEnddt
, A.GOBAL_DT as gobalDt
, A.GOBAL_ENDDT as gobalEnddt
, A.PRE_BUGWA_DT as preBugwaDt
, A.PRE_BUGWA_ENDDT as preBugwaEnddt
, A.NAPBU_CHOKGU_DT as napbuChokguDt <!-- 2021.06.09 현수 추가 (납부촉구 기능 추가건) -->
, A.NAPBU_CHOKGU_ENDDT as napbuChokguEnddt <!-- 2021.06.09 현수 추가 (납부촉구 기능 추가건) -->
, A.BUGWA_DT as bugwaDt
, A.BUGWA_ENDDT as bugwaEnddt
, A.RECOVER_DT as recoverDt
, A.JOCHI_YN as jochiYn
, A.JOCHI_DT as jochiDt
, A.REMARK as remark
, A.REPETITION_LEVY as repetitionLevy
, A.JIBHANG_DT as jibhangDt
, A.BUGWA_TARGET as bugwaTarget
, A.ACTION_YEAR as actionYear
, D.ACT_NO as actNo
, D.ACT_CNT as actCnt
, D.USE_OBJ_NAME as useObjName
, D.UNLAW_SHORT_NAME as unlawShortName
, D.STT_NAME as sttName
, D.UNLAW_SIZE as unlawSize
, D.ACT_START_DT as actStartDt
, D.LAW_NAME as lawName
, D.WIBAN_JOHANG as wibanJohang
, D.BUGWA_LAW_CD as bugwaLawCd
, E.JIMOK_NAME as jimokName
, D.H_RES_DIV as hResDiv
, D.H_RES_NO as hResNo
, D.H_RES_NAME as hResName
, E.O_RES_DIV as oResDiv
, E.O_RES_NO as oResNo
, E.O_RES_NAME as oResName
<!-- 도로명 주소에 따른 변경(위치정보가 아닌 소유자 정보가 들어가므로
쿼리 검증 필요)
, CASE WHEN E.ADDR_BON = '0' THEN ''
WHEN E.ADDR_BON = '' THEN ''
ELSE CASE WHEN E.ADDR_BU = '0' THEN RTRIM(E.ADDR_BON)
WHEN E.ADDR_BU = '' THEN RTRIM(E.ADDR_BON)
ELSE CONCAT(RTRIM(E.ADDR_BON), '-', RTRIM(E.ADDR_BU))
END
END AS POS_BUNJI -->
, CASE WHEN E.BULD_MNNM = '0' THEN ''
WHEN E.BULD_MNNM = '' THEN ''
ELSE CASE WHEN E.BULD_SLNO = '0' THEN RTRIM(E.BULD_MNNM)
WHEN E.BULD_SLNO = '' THEN RTRIM(E.BULD_MNNM)
ELSE CONCAT(RTRIM(E.BULD_MNNM), '-', RTRIM(E.BULD_SLNO))
END
END AS posBunji
, A.REGSTR_MNG_NO as regstrMngNo <!-- [2022.01.10] 임현수 : 담당자 요청에 의하여 관리번호 별도로 추가됨 -->
<!-- ,datediff(date_format(GREATEST(IFNULL(A.FST_WARNNING_ENDDT,'0'), IFNULL(A.SND_WARNNING_ENDDT,'0'), IFNULL(A.GOBAL_ENDDT,'0'),
IFNULL(A.PRE_BUGWA_ENDDT,'0'),IFNULL(A.BUGWA_ENDDT,'0'),IFNULL(A.NAPBU_CHOKGU_ENDDT,'0')), '%Y%m%d'),date_format(now(), '%Y%m%d')) AS REMAING_EXTENT -->
,CASE
WHEN A.RECOVER_DT IS NULL THEN datediff(date_format(GREATEST(IFNULL(A.FST_WARNNING_ENDDT,'0'), IFNULL(A.SND_WARNNING_ENDDT,'0'), IFNULL(A.GOBAL_ENDDT,'0'),
IFNULL(A.PRE_BUGWA_ENDDT,'0'),IFNULL(A.BUGWA_ENDDT,'0'),IFNULL(A.NAPBU_CHOKGU_ENDDT,'0')), '%Y%m%d'),date_format(now(), '%Y%m%d'))
ELSE ''
END AS remaingExtent
,E.ADDR_ETC AS addrEtc
FROM RGL_RET_M A
INNER JOIN JUKBAL_METHOD_C B ON (A.JB_METHOD_CD=B.JB_METHOD_CD)
INNER JOIN BDONG_C C ON (A.ORG_CD=C.ORG_CD AND A.BDONG_CD=C.BDONG_CD)
LEFT OUTER JOIN UNLAW_ACT_D_DAEPYO D ON (A.ORG_CD=D.ORG_CD AND A.AREA_TYPE=D.AREA_TYPE AND A.MNG_YY=D.MNG_YY AND A.MNG_NO=D.MNG_NO)
LEFT OUTER JOIN UNLAW_POS_D_DAEPYO E ON (D.ORG_CD=E.ORG_CD AND D.AREA_TYPE=E.AREA_TYPE AND D.MNG_YY=E.MNG_YY AND D.MNG_NO=E.MNG_NO AND D.ACT_NO=E.ACT_NO)
WHERE A.DEL_YN = 0
AND A.ORG_CD = #{currentUserOrgCd} /* TODO : 일산서구코드인데 필요한가? */
<if test='schMyworkGubun == "Y"'>
<include refid="findListMywork"/>
</if>
<if test='schMyworkGubun == "N"'>
<include refid="findListDefault"/>
</if>
<if test='schDataChk == "B"'>
AND A.REPETITION_LEVY = "신규"
</if>
<if test='schDataChk == "C"'>
AND A.REPETITION_LEVY = "재부과"
</if>
<if test='pagingYn != null and pagingYn == "Y"'>
LIMIT #{startIndex}, #{perPage}
</if>
</select>
</mapper>

@ -15,14 +15,16 @@
</div>
</section>
<div class="contants_body">
<div class="gs_b_top">
<div class="gs_b_top" style="border-bottom: 0;">
<ul class="lef">
<li class="th">
<input id="schDateChk" name="schDateChk" type="checkbox" style="width: 18px; height: 18px; padding-bottom: 15px;" value="1">
일자검색 :
<label>
<input id="schDateChk" name="schDateChk" type="checkbox" style="width: 18px; height: 18px; padding-bottom: 15px;" value="1">
일자검색 :
</label>
</li>
<li>
<select id="schDayGubun" name="schDayGubun" title="일자구분" class="input">
<select id="schDayGubun" name="schDayGubun" title="일자구분" class="input" disabled="disabled">
<option value="JOSA_ENDDT">적발일자</option>
<option value="FST_WARNNING_ENDDT">사전통지</option>
<option value="SND_WARNNING_ENDDT">시정명령</option>
@ -34,8 +36,8 @@
<option value="YUYEENDDATE">유예기간</option>
<option value="A.MNG_YY">연도</option>
</select>
<input type="text" id="schBeginGobalDt" name="schBeginGobalDt" class="input calender datepicker" value=""/> ~
<input type="text" id="schEndGobalDt" name="schEndGobalDt" class="input calender datepicker" value="" />
<input type="text" id="schBeginDt" name="schBeginDt" class="input calender datepicker" value="${dateUtil:getCurrentDateAddDays('yyyy-MM-dd',-30)}" disabled="disabled"/> ~
<input type="text" id="schEndDt" name="schEndDt" class="input calender datepicker" value="${dateUtil:getCurrentDateTime('yyyy-MM-dd')}" disabled="disabled" />
</li>
<li class="th">법정동 :</li>
<li><input type="text" id="schBdongName" name="schBdongName" maxlength="10" class="input"/></li>
@ -56,6 +58,21 @@
<li><button type="button" id="search_btn" class="newbtnss bg1">검색</button></li>
</ul>
</div>
<div class="gs_b_top">
<ul class="lef">
<li class="th">
<label>
<input id="schOnlyMyDataChk" name="schOnlyMyDataChk" type="checkbox" style="width: 18px; height: 18px; padding-bottom: 15px;" value="1">
내가 등록한 자료만 조회
</label>
</li>
<li class="th">
<label style="padding-left: 15px;"><input type="radio" id="schAllDataChk" name="schDataChk" value="A"/> 전체</label>
<label style="padding-left: 15px;"><input type="radio" id="schNewDataChk" name="schDataChk" value="B"/> 신규</label>
<label style="padding-left: 15px;"><input type="radio" id="schJaeBugwaDataChk" name="schDataChk" value="C"/> 재부과</label>
</li>
</ul>
</div>
<div class="gs_booking">
<div class="row">
<div class="col-sm-12">
@ -95,17 +112,42 @@
// 검색정보 셋팅
var setSearchCond = function() {
var searchUserAcnt = $.trim(nvl($("#searchUserAcnt").val(), ""));
var searchSuccessYn = $.trim(nvl($("#searchSuccessYn").val(), ""));
var searchStartDt = $.trim(nvl($("#searchStartDt").val(), ""));
var searchEndDt = $.trim(nvl($("#searchEndDt").val(), ""));
var searchDeviceInfo = $.trim(nvl($("#searchDeviceInfo").val(), ""));
SEARCH_COND.searchUserAcnt = searchUserAcnt;
SEARCH_COND.searchSuccessYn = searchSuccessYn;
SEARCH_COND.searchStartDt = searchStartDt;
SEARCH_COND.searchEndDt = searchEndDt;
SEARCH_COND.searchDeviceInfo = searchDeviceInfo;
var schDateChk = !!$("#schDateChk").is(':checked');
var schDayGubun = $.trim(nvl($("#schDayGubun").val(), ""));
var schBeginDt = $.trim(nvl($("#schBeginDt").val(), ""));
var schEndDt = $.trim(nvl($("#schEndDt").val(), ""));
var schBdongName = $.trim(nvl($("#schBdongName").val(), ""));
var schBuldMnnm = $.trim(nvl($("#schBuldMnnm").val(), ""));
var schBuldSlno = $.trim(nvl($("#schBuldSlno").val(), ""));
var schOResName = $.trim(nvl($("#schOResName").val(), ""));
var schHResName = $.trim(nvl($("#schHResName").val(), ""));
var schRegstrMngNo = $.trim(nvl($("#schRegstrMngNo").val(), ""));
var schOnlyMyDataChk = !!$("#schOnlyMyDataChk").is(':checked');
var schDataChk = $.trim(nvl($("input[name=schDataChk]:checked").val(), ""));
SEARCH_COND.schDateChk = schDateChk;
SEARCH_COND.schDayGubun = schDayGubun;
SEARCH_COND.schBeginDt = schBeginDt;
SEARCH_COND.schEndDt = schEndDt;
SEARCH_COND.schBdongName = schBdongName;
SEARCH_COND.schBuldMnnm = schBuldMnnm;
SEARCH_COND.schBuldSlno = schBuldSlno;
SEARCH_COND.schOResName = schOResName;
SEARCH_COND.schHResName = schHResName;
SEARCH_COND.schRegstrMngNo = schRegstrMngNo;
SEARCH_COND.schOnlyMyDataChk = schOnlyMyDataChk;
SEARCH_COND.schDataChk = schDataChk;
SEARCH_COND.isMywork = false; //이거 왜쓰는거임?
SEARCH_COND.schMyworkGubun = "N"; //이거 왜쓰는거임?
};
/**
@ -278,7 +320,16 @@
eventBindEvents: function() {
var self = this;
$("#schDateChk").on("change", function() {
if( $(this).is(":checked") ){
$("#schDayGubun").removeAttr("disabled");
$("#schBeginDt").removeAttr("disabled");
$("#schEndDt").removeAttr("disabled");
}else{
$("#schDayGubun").attr("disabled", "disabled");
$("#schBeginDt").attr("disabled", "disabled");
$("#schEndDt").attr("disabled", "disabled");}
})
// 검색 버튼 클릭 이벤트
$('#search_btn').on('click', function() {

Loading…
Cancel
Save