Compare commits
	
		
			11 Commits
		
	
	
		
			ca60cd46a5
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6bfc845693 | |||
| 6b50bfaeb4 | |||
| 50f3579f6a | |||
| 47cbb100d6 | |||
| 094044ed73 | |||
| 884967423f | |||
| 35b3cbfdc0 | |||
| 8b5648a3dc | |||
| bb92857db9 | |||
| ee7556f706 | |||
| ad54c0e1b5 | 
							
								
								
									
										5
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								pom.xml
									
									
									
									
									
								
							| @ -74,6 +74,11 @@ | ||||
|             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>com.alibaba.cloud</groupId> | ||||
|             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.cloud</groupId> | ||||
|             <artifactId>spring-cloud-starter-loadbalancer</artifactId> | ||||
|  | ||||
| @ -40,7 +40,7 @@ public class CustomRouteConfig { | ||||
|         RouteLocatorBuilder.Builder routesBuilder = builder.routes(); | ||||
|  | ||||
|         // 定义需要路由的服务列表 | ||||
|         String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay"}; | ||||
|         String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay", "sczx-notify"}; | ||||
|  | ||||
|         // 为每个服务添加路由规则(如果服务存在) | ||||
|         for (String serviceName : serviceNames) { | ||||
|  | ||||
| @ -59,7 +59,7 @@ public class DynamicRouteConfig implements RouteDefinitionLocator { | ||||
|             List<RouteDefinition> routeDefinitions = new ArrayList<>(); | ||||
|  | ||||
|             // 定义需要路由的服务列表 | ||||
|             String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay"}; | ||||
|             String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay", "sczx-notify"}; | ||||
|  | ||||
|             // 为每个服务添加路由规则(如果服务存在) | ||||
|             for (String serviceName : serviceNames) { | ||||
|  | ||||
| @ -42,7 +42,11 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered { | ||||
|             "/zc/car/carModel/pageStoreCarModel", | ||||
|             //同步服务都放行 | ||||
|             "/zc/sync/**", | ||||
|             //支付服务都放行 | ||||
|             "/zc/singlepay/**", | ||||
|             "/zc/notify/**", | ||||
|             "/zc/order/pubOrder/**", | ||||
|             "/zc/order/verify/**", | ||||
|  | ||||
|  | ||||
|             // Swagger 放行 | ||||
|  | ||||
| @ -20,6 +20,7 @@ import org.springframework.web.server.ServerWebExchangeDecorator; | ||||
| import reactor.core.publisher.Flux; | ||||
| import reactor.core.publisher.Mono; | ||||
|  | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.UUID; | ||||
|  | ||||
| @ -64,7 +65,8 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { | ||||
|     private static class CustomExchange extends ServerWebExchangeDecorator implements CustomExchangeInterface { | ||||
|  | ||||
|         private final StringBuilder requestBody = new StringBuilder(); | ||||
|         private String responseBody; | ||||
|         private final StringBuilder responseBodyBuilder = new StringBuilder(); | ||||
|  | ||||
|  | ||||
|  | ||||
|         public CustomExchange(ServerWebExchange delegate) { | ||||
| @ -88,25 +90,31 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         @Override | ||||
|         public ServerHttpResponse getResponse() { | ||||
|             return new ServerHttpResponseDecorator(super.getResponse()) { | ||||
|                 private final StringBuilder responseBodyBuilder = new StringBuilder(); | ||||
|             ServerHttpResponse originalResponse = super.getResponse(); | ||||
|             DataBufferFactory bufferFactory = originalResponse.bufferFactory(); | ||||
|  | ||||
|             return new ServerHttpResponseDecorator(originalResponse) { | ||||
|                 @Override | ||||
|                 public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { | ||||
|                     if (body instanceof Flux) { | ||||
|                         return super.writeWith(Flux.from(body) | ||||
|                                 .doOnNext(buffer -> { | ||||
|                                     DataBufferUtils.join(Flux.just(buffer)) | ||||
|                                             .map(dataBuffer -> { | ||||
|                                                 byte[] content = new byte[dataBuffer.readableByteCount()]; | ||||
|                                                 dataBuffer.read(content); | ||||
|                                                 responseBody = new String(content, StandardCharsets.UTF_8); | ||||
|                                                 return dataBuffer; | ||||
|                                             }) | ||||
|                                             .subscribe(); | ||||
|                                 })); | ||||
|                         Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body; | ||||
|                         return super.writeWith(flux.doOnNext(dataBuffer -> { | ||||
|                             // 复制数据以避免修改原始缓冲区 | ||||
|                             DataBuffer duplicate = dataBuffer.factory().allocateBuffer(dataBuffer.readableByteCount()); | ||||
|                             duplicate.write(dataBuffer); | ||||
|  | ||||
|                             // 读取数据内容用于日志记录 | ||||
|                             byte[] bytes = new byte[duplicate.readableByteCount()]; | ||||
|                             duplicate.read(bytes); | ||||
|                             String bodyContent = new String(bytes, StandardCharsets.UTF_8); | ||||
|                             responseBodyBuilder.append(bodyContent); | ||||
|  | ||||
|                             // 释放复制的缓冲区 | ||||
|                             DataBufferUtils.release(duplicate); | ||||
|                         })); | ||||
|                     } | ||||
|                     return super.writeWith(body); | ||||
|                 } | ||||
| @ -120,7 +128,7 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { | ||||
|  | ||||
|         @Override | ||||
|         public String getResponseBody() { | ||||
|             return responseBody; | ||||
|             return responseBodyBuilder.toString(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -23,6 +23,7 @@ import org.springframework.web.server.ServerWebExchange; | ||||
| import reactor.core.publisher.Flux; | ||||
| import reactor.core.publisher.Mono; | ||||
|  | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| @ -30,38 +31,46 @@ import java.util.List; | ||||
| //@Configuration | ||||
| public class ResponseLogGlobalFilter implements GlobalFilter, Ordered { | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { | ||||
|         try { | ||||
|             ServerHttpResponse originalResponse = exchange.getResponse(); | ||||
|             DataBufferFactory bufferFactory = originalResponse.bufferFactory(); | ||||
|             HttpStatus statusCode = originalResponse.getStatusCode(); | ||||
|  | ||||
|             // 排除特定路径,避免处理Swagger等静态资源 | ||||
|             String path = exchange.getRequest().getURI().getPath(); | ||||
|             if (path.contains("/v2/api-docs") || | ||||
|                     path.contains("/swagger") || | ||||
|                     path.contains("/webjars")) { | ||||
|                 return chain.filter(exchange); | ||||
|             } | ||||
|  | ||||
|             if (statusCode != HttpStatus.OK) { | ||||
|                 return chain.filter(exchange);//降级处理返回数据 | ||||
|             } | ||||
|  | ||||
|             ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { | ||||
|                 @Override | ||||
|                 public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { | ||||
|                     if (body instanceof Flux) { | ||||
|                         Flux<? extends DataBuffer> fluxBody = Flux.from(body); | ||||
|  | ||||
|                         return super.writeWith(fluxBody.buffer().map(dataBuffers -> { | ||||
|                         return super.writeWith(fluxBody.map(dataBuffer -> { | ||||
|                             // 复制数据以避免修改原始缓冲区 | ||||
|                             byte[] content = new byte[dataBuffer.readableByteCount()]; | ||||
|                             dataBuffer.read(content); | ||||
|                             DataBufferUtils.release(dataBuffer); | ||||
|  | ||||
|                             // 合并多个流集合,解决返回体分段传输 | ||||
|                             DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); | ||||
|                             DataBuffer buff = dataBufferFactory.join(dataBuffers); | ||||
|                             byte[] content = new byte[buff.readableByteCount()]; | ||||
|                             buff.read(content); | ||||
|                             DataBufferUtils.release(buff);//释放掉内存 | ||||
|  | ||||
|                             //排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。 | ||||
|                             // 排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。 | ||||
|                             MediaType contentType = originalResponse.getHeaders().getContentType(); | ||||
|                             if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { | ||||
|                             if (contentType == null || !MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { | ||||
|                                 return bufferFactory.wrap(content); | ||||
|                             } | ||||
|  | ||||
|                             // 构建返回日志 | ||||
|                             String joinData = new String(content); | ||||
|                             String joinData = new String(content, StandardCharsets.UTF_8); | ||||
|                             String result = modifyBody(joinData); | ||||
|                             List<Object> rspArgs = new ArrayList<>(); | ||||
|                             rspArgs.add(originalResponse.getStatusCode().value()); | ||||
| @ -69,8 +78,8 @@ public class ResponseLogGlobalFilter implements GlobalFilter, Ordered { | ||||
|                             rspArgs.add(result); | ||||
|                             log.info("<-- {} {}\n{}", rspArgs.toArray()); | ||||
|  | ||||
|                             getDelegate().getHeaders().setContentLength(result.getBytes().length); | ||||
|                             return bufferFactory.wrap(result.getBytes()); | ||||
|                             // 返回处理后的内容 | ||||
|                             return bufferFactory.wrap(result.getBytes(StandardCharsets.UTF_8)); | ||||
|                         })); | ||||
|                     } else { | ||||
|                         log.error("<-- {} 响应code异常", getStatusCode()); | ||||
| @ -88,14 +97,23 @@ public class ResponseLogGlobalFilter implements GlobalFilter, Ordered { | ||||
|  | ||||
|     @Override | ||||
|     public int getOrder() { | ||||
|         return Ordered.HIGHEST_PRECEDENCE; | ||||
|         // 设置较低优先级,确保在其他过滤器之后执行 | ||||
|         return Ordered.LOWEST_PRECEDENCE - 1; | ||||
|     } | ||||
|  | ||||
|     //返回统一的JSON日期数据 2024-02-23 11:00, null转空字符串 | ||||
|     private String modifyBody(String jsonStr){ | ||||
|         JSONObject json = JSON.parseObject(jsonStr, Feature.AllowISO8601DateFormat); | ||||
|         JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm"; | ||||
|         return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat); | ||||
|         if (jsonStr == null || jsonStr.isEmpty()) { | ||||
|             return jsonStr; | ||||
|         } | ||||
|         try { | ||||
|             JSONObject json = JSON.parseObject(jsonStr, Feature.AllowISO8601DateFormat); | ||||
|             JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm"; | ||||
|             return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat); | ||||
|         } catch (Exception e) { | ||||
|             // 如果不是有效的JSON,直接返回原始字符串 | ||||
|             return jsonStr; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -1,55 +1,6 @@ | ||||
| server: | ||||
|   port: 8089 | ||||
|   shutdown: graceful # 启用优雅停机 | ||||
|  | ||||
| spring: | ||||
|   application: | ||||
|     name: sczx-gateway | ||||
|   cloud: | ||||
| #    gateway: | ||||
| #      discovery: | ||||
| #        locator: | ||||
| #          enabled: true # 开启从注册中心动态获取路由 | ||||
| #          lower-case-service-id: true # 服务名转小写 | ||||
| #      routes: | ||||
| #        - id: sczx_user | ||||
| ##          uri: http://172.18.0.4:8081 | ||||
| #          uri: lb://sczx_user | ||||
| #          predicates: | ||||
| #            - Path=/zc/user/** | ||||
| #          filters: | ||||
| #            - StripPrefix=2 | ||||
| #        - id: sczx_store | ||||
| #          uri: lb://sczx_store | ||||
| #          predicates: | ||||
| #            - Path=/zc/store/** | ||||
| #          filters: | ||||
| #            - StripPrefix=2 | ||||
|     nacos: | ||||
|       discovery: | ||||
|         server-addr: 115.190.8.52:8848 # Nacos 地址 | ||||
|         group: DEFAULT_GROUP | ||||
|         watch-delay: 120000  # 60秒刷新一次 | ||||
|     loadbalancer: | ||||
|       ribbon: | ||||
|         enabled: false | ||||
|  | ||||
|  | ||||
| management: | ||||
|   endpoints: | ||||
|     web: | ||||
|       exposure: | ||||
|         include: "*" | ||||
|   endpoint: | ||||
|     health: | ||||
|       show-details: always | ||||
|  | ||||
| auth: | ||||
|   secret-key: his-is-a-very-long-and-secure-secret-key-for-jwt-signing-please-dont-use-short-keys | ||||
|   token-expiration: 86400000  # 24小时 | ||||
|  | ||||
| #logging: | ||||
| #  level: | ||||
| #    org.springframework.cloud.gateway: DEBUG | ||||
| #    org.springframework.cloud.loadbalancer: DEBUG | ||||
| #    reactor.core.publisher: DEBUG | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/main/resources/bootstrap.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/main/resources/bootstrap.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| spring: | ||||
|   application: | ||||
|     name: sczx-gateway # 应用名称,对应 Nacos 配置的 dataId | ||||
|   cloud: | ||||
|     nacos: | ||||
|       server-addr: 115.190.8.52:8848 # Nacos 服务器地址 | ||||
|       config: | ||||
|         group: DEFAULT_GROUP # 配置分组 | ||||
|         file-extension: yaml # 配置文件格式 | ||||
|         timeout: 5000 # 配置读取超时时间 | ||||
		Reference in New Issue
	
	Block a user