Forráskód Böngészése

接收nginx日志入库,注释车辆php页面请求日志入库定时任务

weijianghai 5 hónapja
szülő
commit
1966f73857

+ 6 - 0
pom.xml

@@ -124,6 +124,12 @@
             <version>3.5.6</version>
             <scope>test</scope>
         </dependency>
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.2.0.Final</version>
+        </dependency>
     </dependencies>
 
     <dependencyManagement>

+ 10 - 0
src/main/java/com/nokia/finance/tasks/dao/common/NginxLogDao.java

@@ -0,0 +1,10 @@
+package com.nokia.finance.tasks.dao.common;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.nokia.finance.tasks.pojo.po.common.NginxLogPo;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface NginxLogDao extends BaseMapper<NginxLogPo> {
+
+}

+ 67 - 67
src/main/java/com/nokia/finance/tasks/dao/common/RequestLogDao.java

@@ -1,67 +1,67 @@
-package com.nokia.finance.tasks.dao.common;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.nokia.finance.tasks.pojo.po.common.CarPhpRequestLogPo;
-import com.nokia.finance.tasks.pojo.po.common.RequestLogPo;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
-import org.apache.ibatis.annotations.Update;
-
-import java.util.List;
-
-@Mapper
-public interface RequestLogDao extends BaseMapper<RequestLogPo> {
-    /**
-     * 批量插入
-     */
-    @Update("""
-<script>
-insert into common.request_log
-(
-request_time,
-login_id,
-page_url,
-api,
-request_parameters,
-headers,
-app_id,
-time_stamp,
-token,
-expire_time,
-expire_time_stamp
-)
-values
-<foreach collection="list" item="item" index="index" separator=",">
-(
-#{item.requestTime},
-#{item.loginId},
-#{item.pageUrl},
-#{item.api},
-#{item.requestParameters},
-#{item.headers},
-#{item.appId},
-#{item.timeStamp},
-#{item.token},
-#{item.expireTime},
-#{item.expireTimeStamp}
-)
-</foreach>
-</script>
-""")
-    int insertBatch(List<RequestLogPo> list);
-
-    @Select("""
-select
-    to_timestamp(create_time)::timestamp as request_time,
-    query_string
-from
-    car_theme.wz_adminlog
-where create_time >= #{startTime}
-and create_time < #{endTime}
-order by
-    create_time
-""")
-    List<CarPhpRequestLogPo> listCarPhpRequestLog(@Param("startTime") Long startTime,
-                                                  @Param("endTime") Long endTime);
-}
+//package com.nokia.finance.tasks.dao.common;
+//
+//import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+//import com.nokia.finance.tasks.pojo.po.common.CarPhpRequestLogPo;
+//import com.nokia.finance.tasks.pojo.po.common.RequestLogPo;
+//import org.apache.ibatis.annotations.Mapper;
+//import org.apache.ibatis.annotations.Param;
+//import org.apache.ibatis.annotations.Select;
+//import org.apache.ibatis.annotations.Update;
+//
+//import java.util.List;
+//
+//@Mapper
+//public interface RequestLogDao extends BaseMapper<RequestLogPo> {
+//    /**
+//     * 批量插入
+//     */
+//    @Update("""
+//<script>
+//insert into common.request_log
+//(
+//request_time,
+//login_id,
+//page_url,
+//api,
+//request_parameters,
+//headers,
+//app_id,
+//time_stamp,
+//token,
+//expire_time,
+//expire_time_stamp
+//)
+//values
+//<foreach collection="list" item="item" index="index" separator=",">
+//(
+//#{item.requestTime},
+//#{item.loginId},
+//#{item.pageUrl},
+//#{item.api},
+//#{item.requestParameters},
+//#{item.headers},
+//#{item.appId},
+//#{item.timeStamp},
+//#{item.token},
+//#{item.expireTime},
+//#{item.expireTimeStamp}
+//)
+//</foreach>
+//</script>
+//""")
+//    int insertBatch(List<RequestLogPo> list);
+//
+//    @Select("""
+//select
+//    to_timestamp(create_time)::timestamp as request_time,
+//    query_string
+//from
+//    car_theme.wz_adminlog
+//where create_time >= #{startTime}
+//and create_time < #{endTime}
+//order by
+//    create_time
+//""")
+//    List<CarPhpRequestLogPo> listCarPhpRequestLog(@Param("startTime") Long startTime,
+//                                                  @Param("endTime") Long endTime);
+//}

+ 121 - 121
src/main/java/com/nokia/finance/tasks/jobs/gdc/car/CarPhpRequestLogJob.java

@@ -1,121 +1,121 @@
-package com.nokia.finance.tasks.jobs.gdc.car;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.TypeFactory;
-import com.nokia.finance.tasks.dao.common.RequestLogDao;
-import com.nokia.finance.tasks.pojo.po.common.CarPhpRequestLogPo;
-import com.nokia.finance.tasks.pojo.po.common.RequestLogPo;
-import com.nokia.finance.tasks.utils.AESUtil;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 车辆php页面请求日志入库定时任务
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class CarPhpRequestLogJob {
-    private final RequestLogDao requestLogDao;
-    private final ObjectMapper objectMapper;
-
-    /**
-     * 执行任务
-     */
-    @Scheduled(cron = "0 30 * * * ?")
-    public void runJob() {
-        log.info("执行车辆php页面请求日志入库定时任务");
-        try {
-            CompletableFuture.runAsync(() -> {
-//                LocalDateTime startLocalDateTime = LocalDateTime.of(2024, 3, 23, 0, 0);
-//                LocalDateTime endLocalDateTime = LocalDateTime.of(2024, 3, 25, 18, 0);
-                LocalDateTime now = LocalDateTime.now();
-                LocalDateTime startLocalDateTime = now.minusHours(1).withMinute(0).withSecond(0).withNano(0);
-                LocalDateTime endLocalDateTime = startLocalDateTime.plusHours(1);
-                Long startTime = startLocalDateTime.toEpochSecond(ZoneOffset.ofHours(8));
-                Long endTime = endLocalDateTime.toEpochSecond(ZoneOffset.ofHours(8));
-                List<CarPhpRequestLogPo> carPhpRequestLogPoList = requestLogDao.listCarPhpRequestLog(startTime, endTime);
-                List<RequestLogPo> list = new ArrayList<>();
-                for (CarPhpRequestLogPo carPhpRequestLogPo : carPhpRequestLogPoList) {
-                    try {
-                        LocalDateTime requestTime = carPhpRequestLogPo.getRequestTime();
-                        String queryString = carPhpRequestLogPo.getQueryString();
-                        if (requestTime == null || !StringUtils.hasText(queryString)) {
-                            continue;
-                        }
-                        UriComponents uriComponents = UriComponentsBuilder.fromUriString("?" + queryString).build();
-                        MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
-                        Map<String, String> singleValueMap = queryParams.toSingleValueMap();
-                        String data = singleValueMap.get("data");
-                        if (!StringUtils.hasText(data)) {
-                            continue;
-                        }
-                        String urlDecodeToken = URLDecoder.decode(data, StandardCharsets.UTF_8);
-                        String decryptToken = AESUtil.decrypt(urlDecodeToken);
-                        Map<String, String> tokenMap = objectMapper.readValue(decryptToken,
-                                TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String.class));
-                        if (!"FINANCE".equals(tokenMap.get("APP_ID"))
-                                || !StringUtils.hasText(tokenMap.get("LOGIN_ID"))
-                                || !StringUtils.hasText(tokenMap.get("TIME_STAMP"))
-                                || !StringUtils.hasText(tokenMap.get("EXPIRE_TIME"))
-                        ) {
-                            continue;
-                        }
-                        LocalDateTime timeStamp = LocalDateTime.parse(tokenMap.get("TIME_STAMP"),
-                                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
-                        int expireTime = Integer.parseInt(tokenMap.get("EXPIRE_TIME"));
-                        LocalDateTime expireTimeStamp = timeStamp.plusSeconds(expireTime);
-                        String loginId = tokenMap.get("LOGIN_ID");
-                        String pageUrl = tokenMap.get("REQUEST_URL");
-                        String api = StringUtils.hasText(singleValueMap.get("type")) ? pageUrl + "?type=" + singleValueMap.get("type") : pageUrl;
-                        Map<String, String> requestParameters = new HashMap<>();
-                        requestParameters.put("request_parameters", objectMapper.writeValueAsString(singleValueMap));
-                        String appId = tokenMap.get("APP_ID");
-                        RequestLogPo requestLogPo = new RequestLogPo();
-                        requestLogPo.setRequestTime(carPhpRequestLogPo.getRequestTime());
-                        requestLogPo.setLoginId(loginId);
-                        requestLogPo.setPageUrl(pageUrl);
-                        requestLogPo.setApi(api);
-                        requestLogPo.setRequestParameters(objectMapper.writeValueAsString(requestParameters));
-                        requestLogPo.setAppId(appId);
-                        requestLogPo.setToken(urlDecodeToken);
-                        requestLogPo.setTimeStamp(timeStamp);
-                        requestLogPo.setExpireTime(expireTime);
-                        requestLogPo.setExpireTimeStamp(expireTimeStamp);
-                        list.add(requestLogPo);
-                    } catch (Exception e) {
-                        log.error("请求日志解析失败: {} -> {}", carPhpRequestLogPo, e, e);
-                    }
-                }
-                if (CollectionUtils.isEmpty(list)) {
-                    return;
-                }
-                requestLogDao.insertBatch(list);
-            }).get(1, TimeUnit.MINUTES);
-        } catch (InterruptedException e) {
-            log.error("线程中断: {}", e, e);
-            Thread.currentThread().interrupt();
-        } catch (Exception e) {
-            log.error(e.toString(), e);
-        }
-    }
-}
+//package com.nokia.finance.tasks.jobs.gdc.car;
+//
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.fasterxml.jackson.databind.type.TypeFactory;
+//import com.nokia.finance.tasks.dao.common.RequestLogDao;
+//import com.nokia.finance.tasks.pojo.po.common.CarPhpRequestLogPo;
+//import com.nokia.finance.tasks.pojo.po.common.RequestLogPo;
+//import com.nokia.finance.tasks.utils.AESUtil;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Service;
+//import org.springframework.util.CollectionUtils;
+//import org.springframework.util.MultiValueMap;
+//import org.springframework.util.StringUtils;
+//import org.springframework.web.util.UriComponents;
+//import org.springframework.web.util.UriComponentsBuilder;
+//
+//import java.net.URLDecoder;
+//import java.nio.charset.StandardCharsets;
+//import java.time.LocalDateTime;
+//import java.time.ZoneOffset;
+//import java.time.format.DateTimeFormatter;
+//import java.util.ArrayList;
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.concurrent.CompletableFuture;
+//import java.util.concurrent.TimeUnit;
+//
+///**
+// * 车辆php页面请求日志入库定时任务
+// */
+//@Slf4j
+//@Service
+//@RequiredArgsConstructor
+//public class CarPhpRequestLogJob {
+//    private final RequestLogDao requestLogDao;
+//    private final ObjectMapper objectMapper;
+//
+//    /**
+//     * 执行任务
+//     */
+//    @Scheduled(cron = "0 30 * * * ?")
+//    public void runJob() {
+//        log.info("执行车辆php页面请求日志入库定时任务");
+//        try {
+//            CompletableFuture.runAsync(() -> {
+////                LocalDateTime startLocalDateTime = LocalDateTime.of(2024, 3, 23, 0, 0);
+////                LocalDateTime endLocalDateTime = LocalDateTime.of(2024, 3, 25, 18, 0);
+//                LocalDateTime now = LocalDateTime.now();
+//                LocalDateTime startLocalDateTime = now.minusHours(1).withMinute(0).withSecond(0).withNano(0);
+//                LocalDateTime endLocalDateTime = startLocalDateTime.plusHours(1);
+//                Long startTime = startLocalDateTime.toEpochSecond(ZoneOffset.ofHours(8));
+//                Long endTime = endLocalDateTime.toEpochSecond(ZoneOffset.ofHours(8));
+//                List<CarPhpRequestLogPo> carPhpRequestLogPoList = requestLogDao.listCarPhpRequestLog(startTime, endTime);
+//                List<RequestLogPo> list = new ArrayList<>();
+//                for (CarPhpRequestLogPo carPhpRequestLogPo : carPhpRequestLogPoList) {
+//                    try {
+//                        LocalDateTime requestTime = carPhpRequestLogPo.getRequestTime();
+//                        String queryString = carPhpRequestLogPo.getQueryString();
+//                        if (requestTime == null || !StringUtils.hasText(queryString)) {
+//                            continue;
+//                        }
+//                        UriComponents uriComponents = UriComponentsBuilder.fromUriString("?" + queryString).build();
+//                        MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
+//                        Map<String, String> singleValueMap = queryParams.toSingleValueMap();
+//                        String data = singleValueMap.get("data");
+//                        if (!StringUtils.hasText(data)) {
+//                            continue;
+//                        }
+//                        String urlDecodeToken = URLDecoder.decode(data, StandardCharsets.UTF_8);
+//                        String decryptToken = AESUtil.decrypt(urlDecodeToken);
+//                        Map<String, String> tokenMap = objectMapper.readValue(decryptToken,
+//                                TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String.class));
+//                        if (!"FINANCE".equals(tokenMap.get("APP_ID"))
+//                                || !StringUtils.hasText(tokenMap.get("LOGIN_ID"))
+//                                || !StringUtils.hasText(tokenMap.get("TIME_STAMP"))
+//                                || !StringUtils.hasText(tokenMap.get("EXPIRE_TIME"))
+//                        ) {
+//                            continue;
+//                        }
+//                        LocalDateTime timeStamp = LocalDateTime.parse(tokenMap.get("TIME_STAMP"),
+//                                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+//                        int expireTime = Integer.parseInt(tokenMap.get("EXPIRE_TIME"));
+//                        LocalDateTime expireTimeStamp = timeStamp.plusSeconds(expireTime);
+//                        String loginId = tokenMap.get("LOGIN_ID");
+//                        String pageUrl = tokenMap.get("REQUEST_URL");
+//                        String api = StringUtils.hasText(singleValueMap.get("type")) ? pageUrl + "?type=" + singleValueMap.get("type") : pageUrl;
+//                        Map<String, String> requestParameters = new HashMap<>();
+//                        requestParameters.put("request_parameters", objectMapper.writeValueAsString(singleValueMap));
+//                        String appId = tokenMap.get("APP_ID");
+//                        RequestLogPo requestLogPo = new RequestLogPo();
+//                        requestLogPo.setRequestTime(carPhpRequestLogPo.getRequestTime());
+//                        requestLogPo.setLoginId(loginId);
+//                        requestLogPo.setPageUrl(pageUrl);
+//                        requestLogPo.setApi(api);
+//                        requestLogPo.setRequestParameters(objectMapper.writeValueAsString(requestParameters));
+//                        requestLogPo.setAppId(appId);
+//                        requestLogPo.setToken(urlDecodeToken);
+//                        requestLogPo.setTimeStamp(timeStamp);
+//                        requestLogPo.setExpireTime(expireTime);
+//                        requestLogPo.setExpireTimeStamp(expireTimeStamp);
+//                        list.add(requestLogPo);
+//                    } catch (Exception e) {
+//                        log.error("请求日志解析失败: {} -> {}", carPhpRequestLogPo, e, e);
+//                    }
+//                }
+//                if (CollectionUtils.isEmpty(list)) {
+//                    return;
+//                }
+//                requestLogDao.insertBatch(list);
+//            }).get(1, TimeUnit.MINUTES);
+//        } catch (InterruptedException e) {
+//            log.error("线程中断: {}", e, e);
+//            Thread.currentThread().interrupt();
+//        } catch (Exception e) {
+//            log.error(e.toString(), e);
+//        }
+//    }
+//}

+ 138 - 0
src/main/java/com/nokia/finance/tasks/pojo/po/common/NginxLogPo.java

@@ -0,0 +1,138 @@
+package com.nokia.finance.tasks.pojo.po.common;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("common.nginx_log")
+public class NginxLogPo {
+
+    /**
+     * 访问时间
+     */
+    private LocalDateTime accessTime;
+
+    /**
+     * 响应状态码
+     */
+    private Integer status;
+
+    /**
+     * 请求处理的总时间(秒,精确到毫秒)
+     */
+    private BigDecimal requestTime;
+
+    /**
+     * 当前处理的URI(不带参数)
+     */
+    private String uri;
+
+    /**
+     * 账号,从token解密
+     */
+    private String loginId;
+
+    /**
+     * 页面url,从token解密
+     */
+    private String pageUrl;
+
+    /**
+     * 应用id,从token解密
+     */
+    private String appId;
+
+    /**
+     * 访问令牌
+     */
+    private String token;
+
+    /**
+     * 时间戳,从token解密
+     */
+    private LocalDateTime timeStamp;
+
+    /**
+     * 过期秒数,从token解密
+     */
+    private Integer expireTime;
+
+    /**
+     * token过期时间
+     */
+    private LocalDateTime expireTimeStamp;
+
+    /**
+     * 请求中的查询参数
+     */
+    private String queryString;
+
+    /**
+     * 请求体内容
+     */
+    private String requestBody;
+
+    /**
+     * 请求头
+     */
+    private String requestHeaders;
+
+    /**
+     * 响应头
+     */
+    private String responseHeaders;
+
+    /**
+     * 响应体内容
+     */
+    private String responseBody;
+
+
+    /**
+     * 请求方法
+     */
+    private String requestMethod;
+
+    /**
+     * 请求协议
+     */
+    private String scheme;
+
+    /**
+     * 请求协议版本
+     */
+    private String serverProtocol;
+
+    /**
+     * 请求的总长度(请求行+头+体)
+     */
+    private Integer requestLength;
+
+    /**
+     * 发送给客户端的响应体字节数(不包含头)
+     */
+    private Integer bodyBytesSent;
+
+    /**
+     * 发送给客户端的总字节数(包括头和体)
+     */
+    private Integer bytesSent;
+
+    /**
+     * 传递客户端真实 IP
+     */
+    private String httpXRealIp;
+
+    /**
+     * 请求路径中的所有代理 IP 和客户端 IP
+     */
+    private String httpXForwardedFor;
+
+    /**
+     * 客户端IP地址
+     */
+    private String remoteAddr;
+}

+ 206 - 0
src/main/java/com/nokia/finance/tasks/service/common/NginxLogService.java

@@ -0,0 +1,206 @@
+package com.nokia.finance.tasks.service.common;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.nokia.finance.tasks.dao.common.NginxLogDao;
+import com.nokia.finance.tasks.pojo.po.common.NginxLogPo;
+import com.nokia.finance.tasks.utils.AESUtil;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.SmartLifecycle;
+import org.springframework.stereotype.Service;
+
+import java.net.InetSocketAddress;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+
+/**
+ * 接收nginx日志并记录到数据库
+ */
+@Slf4j
+@Data
+@Service
+@ConfigurationProperties("nginx-log")
+@RequiredArgsConstructor
+public class NginxLogService implements SmartLifecycle {
+    // 定义服务监听的端口号
+    private Integer port;
+    // 定义接收消息的最大长度
+    private Integer maxLength;
+    // 标志变量,用于标识服务是否正在运行
+    private volatile boolean running = false;
+    // Netty 的主事件循环组,用于接收客户端连接
+    private EventLoopGroup parentGroup;
+    // Netty 的子事件循环组,用于处理客户端连接后的数据读写
+    private EventLoopGroup childGroup;
+    // 保存绑定到指定端口的 ChannelFuture 对象
+    private ChannelFuture channelFuture;
+    // 注入的 Mapper 接口,用于操作数据库
+    private final NginxLogDao nginxLogDao;
+
+    /**
+     * 启动方法,初始化并启动 TCP 服务
+     */
+    @Override
+    public void start() {
+        // 初始化主事件循环组和子事件循环组
+        parentGroup = new NioEventLoopGroup();
+        childGroup = new NioEventLoopGroup();
+        try {
+            // 使用 ServerBootstrap 配置并启动 Netty 服务端
+            ServerBootstrap serverBootstrap = new ServerBootstrap()
+                    .group(parentGroup, childGroup) // 设置主从线程组
+                    .channel(NioServerSocketChannel.class) // 指定使用 NIO 通道
+                    .childHandler(new ChannelInitializer<SocketChannel>() { // 配置子通道的处理器
+                        @Override
+                        protected void initChannel(SocketChannel socketChannel) {
+                            // 添加 IP 过滤器,限制只允许本地连接
+                            socketChannel.pipeline().addLast(new IpFilterHandler());
+                            // 添加基于行的帧解码器,限制单行最大长度
+                            socketChannel.pipeline().addLast(new LineBasedFrameDecoder(maxLength));
+                            // 添加字符串解码器,将接收到的字节流转换为字符串
+                            socketChannel.pipeline().addLast(new StringDecoder());
+                            // 添加自定义的业务处理器,处理解析后的日志数据
+                            socketChannel.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
+                                @Override
+                                protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
+                                    // 创建 ObjectMapper 对象,用于 JSON 数据解析
+                                    ObjectMapper mapper = new ObjectMapper();
+                                    // 配置 ObjectMapper 忽略未知字段
+                                    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                                    // 设置属性命名策略为蛇形命名法
+                                    mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
+                                    // 创建 JavaTimeModule 模块,支持 LocalDateTime 的序列化和反序列化
+                                    JavaTimeModule module = new JavaTimeModule();
+                                    module.addSerializer(LocalDateTime.class,
+                                            new LocalDateTimeSerializer(DateTimeFormatter.ISO_DATE_TIME));
+                                    module.addDeserializer(LocalDateTime.class,
+                                            new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
+                                    mapper.registerModule(module);
+                                    // 将接收到的 JSON 字符串解析为 NginxLogPo 对象
+                                    NginxLogPo po = mapper.readValue(s, NginxLogPo.class);
+                                    // 获取日志中的 Token 字段
+                                    String token = po.getToken();
+                                    // 对 Token 进行 URL 解码
+                                    String urlDecodeToken = URLDecoder.decode(token, StandardCharsets.UTF_8);
+                                    // 对解码后的 Token 进行 AES 解密
+                                    String decryptToken = AESUtil.decrypt(urlDecodeToken);
+                                    // 将解密后的 Token 转换为 Map
+                                    Map<String, String> map = mapper.readValue(decryptToken,
+                                            TypeFactory.defaultInstance().constructMapType(Map.class, String.class,
+                                                    String.class));
+                                    // 从 Map 中解析时间戳,并格式化为 LocalDateTime
+                                    LocalDateTime timeStamp = LocalDateTime.parse(map.get("TIME_STAMP"),
+                                            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                                    // 获取过期时间(秒)
+                                    int expireTime = Integer.parseInt(map.get("EXPIRE_TIME"));
+                                    // 计算过期时间戳
+                                    LocalDateTime expireTimeStamp = timeStamp.plusSeconds(expireTime);
+                                    // 设置 NginxLogPo 对象的其他字段
+                                    po.setLoginId(map.get("LOGIN_ID"));
+                                    po.setPageUrl(map.get("REQUEST_URL"));
+                                    po.setAppId(map.get("APP_ID"));
+                                    po.setToken(urlDecodeToken);
+                                    po.setTimeStamp(timeStamp);
+                                    po.setExpireTime(expireTime);
+                                    po.setExpireTimeStamp(expireTimeStamp);
+                                    // 将日志对象插入数据库
+                                    nginxLogDao.insert(po);
+                                }
+                            });
+                        }
+                    })
+                    .childOption(ChannelOption.SO_KEEPALIVE, true); // 设置保持连接选项
+            // 绑定端口并同步等待绑定完成
+            channelFuture = serverBootstrap.bind(port).sync();
+            // 打印服务启动成功的日志
+            log.info("TCP Server started on port: {}", port);
+        } catch (InterruptedException e) {
+            // 如果发生中断异常,设置线程中断状态并记录错误日志
+            Thread.currentThread().interrupt();
+            log.error(e.toString(), e);
+            // 停止服务
+            stop();
+        }
+    }
+
+    /**
+     * 停止方法,优雅地关闭服务
+     */
+    @Override
+    public void stop() {
+        // 设置运行标志为 false
+        running = false;
+        // 优雅地关闭主事件循环组
+        if (parentGroup != null) {
+            parentGroup.shutdownGracefully();
+        }
+        // 优雅地关闭子事件循环组
+        if (childGroup != null) {
+            childGroup.shutdownGracefully();
+        }
+        // 关闭绑定的 ChannelFuture
+        if (channelFuture != null) {
+            try {
+                channelFuture.channel().closeFuture().sync();
+            } catch (InterruptedException e) {
+                // 如果发生中断异常,设置线程中断状态并记录错误日志
+                Thread.currentThread().interrupt();
+                log.error(e.toString(), e);
+            }
+        }
+    }
+
+    /**
+     * 判断服务是否正在运行
+     *
+     * @return 如果服务正在运行则返回 true,否则返回 false
+     */
+    @Override
+    public boolean isRunning() {
+        return running;
+    }
+
+    /**
+     * 内部类:IP 过滤处理器,限制只允许本地连接
+     */
+    private static class IpFilterHandler extends ChannelInboundHandlerAdapter {
+        @Override
+        public void channelActive(ChannelHandlerContext ctx) throws Exception {
+            // 获取远程客户端的地址
+            InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
+            String hostAddress = remoteAddress.getAddress().getHostAddress();
+            // 如果远程地址不是本地地址,则拒绝连接
+            if (!"127.0.0.1".equals(hostAddress)) {
+                log.warn("Rejected connection from non-local IP: {}", hostAddress);
+                ctx.close();
+                return;
+            }
+            // 调用父类方法,继续处理连接
+            super.channelActive(ctx);
+        }
+    }
+}

+ 4 - 1
src/main/resources/application-dev.yml

@@ -10,7 +10,7 @@ spring:
     username: postgres
     password: Test!234
 #    url: jdbc:postgresql://127.0.0.1:5432/financialdb
-    url: jdbc:postgresql://192.168.65.128:5432/financialdb
+    url: jdbc:postgresql://192.168.211.128:5432/financialdb
 #    url: jdbc:postgresql://172.16.107.5:5432/financialdb
 #    username: finance
 #    password: Finance@unicom23
@@ -208,3 +208,6 @@ house-report:
   document-ftl: document.ftl
   # document xml路径
   document-xml: templates/house/report/docx/word/document.xml
+nginx-log:
+  port: 39876
+  max-length: 1048576

+ 4 - 0
src/main/resources/application-prod.yml

@@ -4,6 +4,7 @@ logging:
   level:
     com:
       nokia: debug
+    com.nokia.finance.tasks.dao.common.NginxLogDao: warn
 spring:
   datasource:
     driver-class-name: org.postgresql.Driver
@@ -199,3 +200,6 @@ house-report:
   document-ftl: document.ftl
   # document xml路径
   document-xml: templates/house/report/docx/word/document.xml
+nginx-log:
+  port: 39876
+  max-length: 1048576