Răsfoiți Sursa

2024-03-21 update

junggilpark 1 an în urmă
părinte
comite
e8206a2cca

+ 8 - 33
src/main/java/com/its/web/interceptor/ConnectHistory.java

@@ -1,6 +1,7 @@
 package com.its.web.interceptor;
 
 import com.its.web.service.common.CommonService;
+import com.its.web.util.CommonUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.dao.DataAccessException;
@@ -13,6 +14,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 
 @Slf4j
@@ -26,9 +29,12 @@ public class ConnectHistory implements HandlerInterceptor {
         String uri = request.getRequestURI();
         String[] ipAddressArr = new String[]{"127.0.0.1", "localhost", "192.168.20.46"};
         HttpSession session = request.getSession();
-        String hostIp = getClientIp(request);
+        String hostIp = CommonUtil.getHttpServletRemoteIP(request);
         if (session.getAttribute(hostIp) == null && !uri.contains("/phits") && !uri.contains("error")) {
-            log.info("Connect Ip Address : {}, UUID : {}", hostIp, session.getId());
+            LocalDateTime now = LocalDateTime.now();
+            String formatNow = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+            log.info("Connect Ip Address : {}, UUID : {}, Connect Time : {}", hostIp, session.getId(), formatNow);
             session.setAttribute(hostIp, session.getId());
             try {
                 service.insertConnHs();
@@ -64,35 +70,4 @@ public class ConnectHistory implements HandlerInterceptor {
         return true;
     }
 
-    public String getClientIp(HttpServletRequest request) {
-        if (request == null) {
-            return "";
-        }
-
-        String ipAddress = request.getHeader("X-FORWARDED-FOR");
-        // proxy 환경일 경우
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("Proxy-Client-IP");
-        }
-        // 웹로직 서버일 경우
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("WL-Proxy-Client-IP");
-        }
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("HTTP_CLIENT_IP");
-        }
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
-        }
-        // 기타
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getRemoteAddr() ;
-        }
-        //-Djava.net.preferIPv4Stack=true
-        if (ipAddress.equals("0:0:0:0:0:0:0:1"))   //==> ipv6 <== default
-        {
-            ipAddress = "127.0.0.1";   //==> localhost
-        }
-        return ipAddress;
-    }
 }

+ 2 - 33
src/main/java/com/its/web/security/WebLoginSuccessHandler.java

@@ -2,6 +2,7 @@ package com.its.web.security;
 
 import com.its.web.dto.admin.PrincipalDetail;
 import com.its.web.dto.admin.TbWwwMemberDto;
+import com.its.web.util.CommonUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.core.Authentication;
@@ -47,7 +48,7 @@ public class WebLoginSuccessHandler implements AuthenticationSuccessHandler {
         PrincipalDetail principal = (PrincipalDetail)authentication.getPrincipal();
         TbWwwMemberDto userInfr = principal.getUser();
 
-        String remoteIp = this.getHttpServletRemoteIP(request);
+        String remoteIp = CommonUtil.getHttpServletRemoteIP(request);
         log.info("Login History: {}, {}", currSysTime, remoteIp);
 
         HttpSession session = request.getSession(false); // 세션을 생성 하지 않음
@@ -82,36 +83,4 @@ public class WebLoginSuccessHandler implements AuthenticationSuccessHandler {
 
         session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
     }
-
-    public static String getHttpServletRemoteIP(HttpServletRequest request) {
-        if (request == null) {
-            return "";
-        }
-
-        String ipAddress = request.getHeader("X-FORWARDED-FOR");
-        // proxy 환경일 경우
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("Proxy-Client-IP");
-        }
-        // 웹로직 서버일 경우
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("WL-Proxy-Client-IP");
-        }
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("HTTP_CLIENT_IP");
-        }
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
-        }
-        // 기타
-        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getRemoteAddr() ;
-        }
-        //-Djava.net.preferIPv4Stack=true
-        if (ipAddress.equals("0:0:0:0:0:0:0:1"))   //==> ipv6 <== default
-        {
-            ipAddress = "127.0.0.1";   //==> localhost
-        }
-        return ipAddress;
-    }
 }

+ 34 - 0
src/main/java/com/its/web/util/CommonUtil.java

@@ -2,6 +2,8 @@ package com.its.web.util;
 
 import com.its.web.dto.message.ResultDto;
 
+import javax.servlet.http.HttpServletRequest;
+
 public class CommonUtil {
 
     public static ResultDto getResultDto(String success, String message) {
@@ -14,4 +16,36 @@ public class CommonUtil {
         result.setSuccess(success);
         return result;
     }
+
+    public static String getHttpServletRemoteIP(HttpServletRequest request) {
+        if (request == null) {
+            return "";
+        }
+
+        String ipAddress = request.getHeader("X-FORWARDED-FOR");
+        // proxy 환경일 경우
+        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+            ipAddress = request.getHeader("Proxy-Client-IP");
+        }
+        // 웹로직 서버일 경우
+        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+            ipAddress = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+            ipAddress = request.getHeader("HTTP_CLIENT_IP");
+        }
+        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
+        }
+        // 기타
+        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+            ipAddress = request.getRemoteAddr() ;
+        }
+        //-Djava.net.preferIPv4Stack=true
+        if (ipAddress.equals("0:0:0:0:0:0:0:1"))   //==> ipv6 <== default
+        {
+            ipAddress = "127.0.0.1";   //==> localhost
+        }
+        return ipAddress;
+    }
 }

+ 31 - 1
src/main/resources/static/css/main.css

@@ -230,10 +230,19 @@ body {
     align-items: flex-start;
 }
 .mainWrap .bottom > div:nth-child(2) > div:first-child{
-    width: 80%;
+    width: 100%;
     height: 30px;
+    margin-bottom: 10px;
     border-radius: 5%;
 }
+
+.mainWrap .bottom .video-box {
+    width: 100%;
+    height: calc(100% - 40px);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
 .mainWrap .bottom video {
     width: 85%;
     height: calc(100% - 40px);
@@ -369,8 +378,29 @@ body {
     -webkit-box-pack: justify;
     justify-content: space-between;
     margin-bottom: 1.25rem;
+    /*padding-bottom: 10px;*/
+    /*position: relative;*/
 }
 
+/*.mainWrap .notice > .content > div::after{*/
+/*    background: none repeat scroll 0 0 transparent;*/
+/*    bottom: 0;*/
+/*    content: "";*/
+/*    display: block;*/
+/*    height: 1px;*/
+/*    left: 50%;*/
+/*    position: absolute;*/
+/*    background: black;*/
+/*    transition: width 0.3s ease 0s, left 0.3s ease 0s;*/
+/*    width: 0;*/
+/*}*/
+
+/*.mainWrap .notice > .content > div:hover:after {*/
+/*    width: 100%;*/
+/*    left: 0;*/
+/*}*/
+
+
 .mainWrap .notice > .content > div > div {
     font-size: 14px;
     overflow: hidden;

+ 11 - 0
src/main/resources/static/css/statistics.css

@@ -379,4 +379,15 @@
     .statisticWrap .search-bar > div:nth-child(1) > div:nth-child(2) select.month {
         min-width: 65px;
     }
+    .search-empty {
+        display: none !important;
+    }
+
+    .search-content .table-box table td:first-child, .search-content .table-box table th:first-child {
+        min-width: 150px;
+        max-width: 150px;
+    }
+    .table-box table th, .table-box table td {
+        height: 35px;
+    }
 }

+ 6 - 1
src/main/resources/static/css/traffic.css

@@ -506,6 +506,7 @@ ul, li {
     width: calc(100% - 30px);
     height: 100%;
     cursor: default;
+    font-weight: bold;
 }
 
 .incident-info-window .title > div:nth-child(2),
@@ -558,12 +559,16 @@ ul, li {
     height: 25px;
     display: flex;
     align-items: center;
-    justify-content: center;
+    justify-content: space-between;
     margin-top: 5px;
 }
 
 .cctv-info-window .content > div > div:nth-child(1) {
     font-size: 14px;
+    user-select: none;
+}
+.cctv-info-window .content > div > div:nth-child(1) .timer {
+    font-weight: bold;
 }
 .cctv-info-window .content > div > div:nth-child(2) {
     margin-left: 5px;

+ 69 - 28
src/main/resources/static/js/traffic/traffic.js

@@ -758,6 +758,12 @@ class MarkerObj {
             clearTimeout(this.timer);
             this.timer = null;
         }
+
+        if (this.limitTimer) {
+            clearInterval(this.limitTimer);
+            this.limitTimer = null;
+        }
+
         if (this.imageOnOff.includes(this.type)) {// 클릭 이미지가 있는 경우
             this.setImage('1');
         }
@@ -799,24 +805,61 @@ class MarkerObj {
         _MarkerHandle.selectedMarker = null;
     }
 
-    videoEvent() {
-        let video = createVideoJs(this.ID, this.URL);
+    showLimitTime() {
+        const timer = $('.timer');
+        if (!this.video) {
+            timer.text('-');
+            return;
+        }
+
+        timer.text(30);
+
+        if (this.limitTimer) {
+            clearInterval(this.limitTimer)
+            this.limitTimer = null;
+        }
+        let cnt = 30;
+        this.limitTimer = setInterval(()=>{
+            if(cnt === 0) {
+                clearInterval(this.limitTimer);
+                this.limitTimer = null;
+                return;
+            }
+            $('.timer').text(--cnt);
+        }, 1000)
+    }
+
+    playTimer() {
+        if (this.timer) {
+            clearTimeout(this.timer);
+            this.timer = null;
+        }
 
         this.timer = setTimeout(()=>{
-            if (video) {
-                video.pause();
+            if (this.video) {
+                this.video.pause();
             }
         }, CCTV_DISPLAY_TIME);
+    }
+
+    videoEvent() {
+        this.video = createVideoJs(this.ID, this.URL, this);
+
+        this.playTimer();
+
+        this.showLimitTime();
 
         $('.continue-play').on('click', ()=>{
-            if (this.timer) {
-                setTimeout(this.timer);
+            this.playTimer();
+            this.showLimitTime();
+
+            if (this.video) {
+                this.video.play();
             }
-            if (video) {
-                video.play();
-                this.timer = setTimeout(()=>{
-                    video.pause();
-                }, CCTV_DISPLAY_TIME);
+            else {
+                $('#video-error').css('display', 'none');
+                $('.cctv-info-window .content > div:nth-child(1)').append($('<video id="video-'+this.ID+'" class="video-js" style="width: 100%; height: 100%;"></video>'))
+                this.videoEvent();
             }
         });
     }
@@ -990,16 +1033,17 @@ class TbCCtvObj extends MarkerObj{
                             <div onclick="infoWindowEvent('${type}', '${ID}', 'close')">X</div>
                         </div>
                         <div class="content">
-                            <div>
+                            <div style="display: flex; align-items: center; justify-content: center;">
                                 <video id="video-${ID}" class="video-js" style="width: 100%; height: 100%;"></video>
+                                <img id="video-error" style="display: none;" src="/images/icon/error.png" alt="스트리밍 오류 이미지">
                             </div>
                             <div>
-                                <div>※ CCTV영상은 30초간 제공됩니다.</div>
+                                <div>남은시간 : <span class="timer">30</span> 초</div>
                                 <div class="continue-play">계속재생</div>
                             </div>
                         </div>
                     </div>`;
-
+//<!--                                <div>※ CCTV영상은 30초간 제공됩니다.</div>-->
         return iwContent;
     }
 }
@@ -1224,7 +1268,7 @@ class IntersectionCameraObj extends MarkerObj{
                                 <video id="video-${ID}" class="video-js" playsinline style="width: 100%; height: 100%;"></video>
                             </div>
                             <div>
-                                <div>※ CCTV영상은 30초간 제공됩니다.</div>
+                                <div>남은시간 : <span class="timer">30</span> 초</div>
                                 <div class="continue-play">계속재생</div>
                             </div>
                         </div>
@@ -1439,7 +1483,7 @@ function getKakaoPosition(yCoordinate, xCoordinate) {
 /**
  * videoJs 객체 생성
  */
-function createVideoJs(id, url) {
+function createVideoJs(id, url, obj) {
     let video = videojs("video-" + id, {
         sources: [
             {
@@ -1455,18 +1499,15 @@ function createVideoJs(id, url) {
     });
 
     //에러발생 시 대체 이미지 생성
-    video.on('error', ()=>{
-        if (video.error().code === 4) {
-            video.pause();
-            video.dispose();
-            const $errorBox = $('.content > div:nth-child(1)');
-            $errorBox.append($('<img src="/images/icon/error.png" alt="스트리밍 오류 이미지">'));
-            $errorBox.css({
-                display : 'flex',
-                alignItems : 'center',
-                justifyContent : 'center',
-            });
-            video = null;
+    video.on('error', function(){
+        if (this.error().code === 4) {
+            this.pause();
+            this.dispose();
+            $('#video-error').css('display', 'block');
+            clearInterval(obj.limitTimer);
+            clearTimeout(obj.timer);
+            $('.timer').text('-');
+            obj.video = null;
         }
     });
 

BIN
src/main/resources/static/video/gumi.mp4


+ 2 - 17
src/main/resources/templates/admin/cctv.html

@@ -111,22 +111,6 @@
                 }
             }, false);
         })
-        // if (confirm(frontStr + message)) {
-        //     getDataAsync(uri, "POST", param, null, (jsonData)=>{
-        //         if (jsonData) {
-        //             alert(jsonData.message);
-        //             if (jsonData.success === 'S') {
-        //                 $(this).prop('checked', !$(this).is(':checked'));
-        //                 if (isCctv) {
-        //                     getCctvData(cctvId);
-        //                 }
-        //                 else {
-        //                     getIxrData(cctvId, ixrId);
-        //                 }
-        //             }
-        //         }
-        //     }, false);
-        // }
     })
 
     let video  =  videojs("video", {
@@ -172,7 +156,7 @@
             video.pause();
             video.dispose();
             video = null;
-            $('.right .content').html('<video id="video" class="video-js vjs-big-play-centered" style="width: 100%; height: 100%;"></video>');
+            $('.right .content').html('<video id="video" class="video-js vjs-big-play-centered" style="width: 100%; height: 100%; object-fit: fill;"></video>');
         }
     }
 
@@ -252,6 +236,7 @@
         $list.html(strMap.get(show));
         $title.text($show.text() + ' 목록');
     }
+
     function cctvClick(cctvNo) {
         const obj = cctvMap.get(Number(cctvNo));
         selectToggle(cctvNo)

+ 5 - 19
src/main/resources/templates/main/main.html

@@ -7,6 +7,8 @@
         <meta name="description" content="포항시 교통정보센터입니다"/>
         <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
         <title>포항시 교통정보센터</title>
+        <script th:src="@{/js/videojs/video-7.10.2.js}"></script>
+        <link rel="stylesheet" th:href="@{/css/video-7.10.2.css}">
         <th:block th:include="include/head.html"></th:block>
         <link rel="stylesheet" th:href="@{/css/main.css}">
     </head>
@@ -35,20 +37,6 @@
                             </div>
                         </div>
                     </div>
-<!--                    <div class="bottom">-->
-<!--                        <div class="first-box">-->
-<!--                            <h1>POHANG</h1>-->
-<!--                            <div>포항시교통정보센터 홍보 영상입니다.</div>-->
-<!--                        </div>-->
-<!--                        <div>-->
-<!--                            <div>-->
-<!--                                <video id="video" muted playsinline controls>-->
-<!--                                    <source src="/video/video.mp4" preload="metadata" type="video/mp4">-->
-<!--                                    이 문장은 여러분의 브라우저가 video 태그를 지원하지 않을 때 화면에 표시됩니다!-->
-<!--                                </video>-->
-<!--                            </div>-->
-<!--                        </div>-->
-<!--                    </div>-->
                     <div class="mid">
                         <div class="notice">
                             <div>
@@ -88,10 +76,9 @@
                         </div>
                         <div>
                             <div><img src="/images/icon/video.png" alt="동영상 아이콘">홍보 동영상</div>
-                            <video id="video" muted playsinline controls>
-                                <source src="/video/video.mp4" preload="metadata" type="video/mp4">
-                                이 문장은 여러분의 브라우저가 video 태그를 지원하지 않을 때 화면에 표시됩니다.
-                            </video>
+                            <div class="video-box">
+                                <video style="object-fit: fill; width: 100%; height: 100%;" controls preload="metadata" th:src="@{/video/gumi.mp4}" type="video/mp4"></video>
+                            </div>
                         </div>
                     </div>
 <!--                    <div class="bottom">-->
@@ -119,7 +106,6 @@
 <script th:inline="javascript">
     //    let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
 
-
     const popup = [[${popup}]];
     if (popup && popup.length > 0) {
         let top = $('header').innerHeight();

+ 1 - 1
src/main/resources/templates/statistics/congest-stat.html

@@ -28,7 +28,7 @@
                         <option th:if="${list != null && list.size() > 0}" th:each="item : ${list}" th:value="${item.getCode()}" th:text="${item.getName()}"></option>
                     </select>
                 </div>
-                <div></div>
+                <div class="search-empty"></div>
                 <img title="정체 통계 엑셀 파일 다운로드" alt="엑셀 다운로드" class="download" onclick="excelDown()" src="/images/icon/excel.png" width="30" height="30">
             </div>
             <div class="button congest">