tanlie 1 mesiac pred
rodič
commit
d097cd8867
32 zmenil súbory, kde vykonal 1400 pridanie a 1 odobranie
  1. 2 0
      .gitignore
  2. 7 1
      wishing-platform/pom.xml
  3. 17 0
      wishing-platform/wishing-core/pom.xml
  4. 17 0
      wishing-platform/wishing-core/wishing-core-base/pom.xml
  5. 13 0
      wishing-platform/wishing-core/wishing-core-base/src/main/java/cn/qinys/App.java
  6. 28 0
      wishing-platform/wishing-entity/pom.xml
  7. 13 0
      wishing-platform/wishing-entity/src/main/java/cn/qinys/App.java
  8. 38 0
      wishing-platform/wishing-entity/src/test/java/cn/qinys/AppTest.java
  9. 83 0
      wishing-platform/wishing-gateway/pom.xml
  10. 19 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/GatewayApplication.java
  11. 49 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/GlobalExceptionConfiguration.java
  12. 64 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/RedisConfiguration.java
  13. 49 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/RequestRateLimitConfiguration.java
  14. 28 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/constants/GatewayConstants.java
  15. 30 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/dto/LoginDTO.java
  16. 56 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/dto/LoginUserInfoDTO.java
  17. 90 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/entity/GatewayLog.java
  18. 278 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/filter/AccessLogFilter.java
  19. 39 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/limiter/DiyRedisRateLimiter.java
  20. 75 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/limiter/DiyRequestRateLimiterGatewayFilterFactory.java
  21. 22 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/mapper/GatewayLogMapper.java
  22. 15 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/msgsender/IMessageService.java
  23. 105 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/msgsender/RabbitmqServiceImpl.java
  24. 37 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/response/UnifyResponse.java
  25. 43 0
      wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/utils/RedisUtils.java
  26. 35 0
      wishing-platform/wishing-gateway/src/main/resources/application-local.yml
  27. 24 0
      wishing-platform/wishing-gateway/src/main/resources/application-router.yml
  28. 7 0
      wishing-platform/wishing-gateway/src/main/resources/application.yml
  29. 38 0
      wishing-platform/wishing-gateway/src/test/java/cn/qinys/AppTest.java
  30. 28 0
      wishing-platform/wishing-service/pom.xml
  31. 13 0
      wishing-platform/wishing-service/src/main/java/cn/qinys/App.java
  32. 38 0
      wishing-platform/wishing-service/src/test/java/cn/qinys/AppTest.java

+ 2 - 0
.gitignore

@@ -6,3 +6,5 @@ dist/
 Thumbs.db
 .claude/
 wishing-tree-server/.idea/
+wishing-platform/.idea/
+wishing-platform/wishing-gateway/target/

+ 7 - 1
wishing-tree-server/pom.xml → wishing-platform/pom.xml

@@ -4,8 +4,14 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>cn.qinys</groupId>
-    <artifactId>wishingtree-platform</artifactId>
+    <artifactId>wishing-platform</artifactId>
     <packaging>pom</packaging>
+    <modules>
+        <module>wishing-core</module>
+        <module>wishing-entity</module>
+        <module>wishing-gateway</module>
+        <module>wishing-service</module>
+    </modules>
     <version>1.0.0-SNAPSHOT</version>
     <properties>
         <maven.compiler.source>21</maven.compiler.source>

+ 17 - 0
wishing-platform/wishing-core/pom.xml

@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.qinys</groupId>
+        <artifactId>wishing-platform</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>wishing-core</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>wishing-core-base</module>
+    </modules>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+</project>

+ 17 - 0
wishing-platform/wishing-core/wishing-core-base/pom.xml

@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.qinys</groupId>
+        <artifactId>wishing-core</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>wishing-core-base</artifactId>
+
+    <name>wishing-core-base</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+</project>

+ 13 - 0
wishing-platform/wishing-core/wishing-core-base/src/main/java/cn/qinys/App.java

@@ -0,0 +1,13 @@
+package cn.qinys;
+
+/**
+ * Hello world!
+ *
+ */
+public class App 
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
+    }
+}

+ 28 - 0
wishing-platform/wishing-entity/pom.xml

@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.qinys</groupId>
+        <artifactId>wishing-platform</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>wishing-entity</artifactId>
+    <packaging>jar</packaging>
+
+    <name>wishing-entity</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 13 - 0
wishing-platform/wishing-entity/src/main/java/cn/qinys/App.java

@@ -0,0 +1,13 @@
+package cn.qinys;
+
+/**
+ * Hello world!
+ *
+ */
+public class App 
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
+    }
+}

+ 38 - 0
wishing-platform/wishing-entity/src/test/java/cn/qinys/AppTest.java

@@ -0,0 +1,38 @@
+package cn.qinys;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}

+ 83 - 0
wishing-platform/wishing-gateway/pom.xml

@@ -0,0 +1,83 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.qinys</groupId>
+        <artifactId>wishing-platform</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>wishing-gateway</artifactId>
+    <name>wishing-gateway</name>
+
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-loadbalancer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <!--数据库-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/sharding-jdbc-spring-boot-starter -->
+        <!-- Source: https://mvnrepository.com/artifact/com.alibaba/druid -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.2.28</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>wishing-gateway</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>3.5.14</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 19 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/GatewayApplication.java

@@ -0,0 +1,19 @@
+package cn.qinys.wishing;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * @author lie tan
+ * @description
+ * @date 2026-05-17 18:24
+ **/
+@SpringBootApplication
+@EnableAsync
+public class GatewayApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(GatewayApplication.class,args);
+    }
+
+}

+ 49 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/GlobalExceptionConfiguration.java

@@ -0,0 +1,49 @@
+package cn.qinys.wishing.gateway.config;
+
+
+import cn.qinys.wishing.gateway.response.UnifyResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.server.ResponseStatusException;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * 网关错误异常配置类
+ *
+ * @author v-lishiquan.gx@chinatelecom.cn
+ * @date 2020-09-03
+ */
+@Configuration
+@Slf4j
+public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler, Ordered {
+    Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
+        ServerHttpResponse response = exchange.getResponse();
+        if (response.isCommitted()) {
+            return Mono.error(ex);
+        }
+        // header set_json响应
+        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+        //是否响应状态异常
+        if (ex instanceof ResponseStatusException rse) {
+            response.setStatusCode(rse.getStatusCode());
+        }
+        logger.error("服务器异常 == {}", ex.getMessage());
+        return UnifyResponse.fail(response, 500, ex.getMessage());
+
+    }
+
+    @Override
+    public int getOrder() {
+        return -1;
+    }
+}

+ 64 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/RedisConfiguration.java

@@ -0,0 +1,64 @@
+package cn.qinys.wishing.gateway.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.net.UnknownHostException;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/1/9 9:07
+ */
+@EnableCaching
+@Configuration
+public class RedisConfiguration {
+
+
+    /**
+     * 缓存管理器
+     * @param factory
+     * @return
+     */
+    @Bean
+    @SuppressWarnings("all")
+    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
+        return RedisCacheManager.create(factory);
+    }
+
+    //重写template配置累
+    @Bean
+    @SuppressWarnings("all")
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) throws UnknownHostException {
+
+        RedisTemplate<String, Object> template = new RedisTemplate();
+        template.setConnectionFactory(factory);
+        //json序列化
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        //String 序列化
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        //key 采用string序列化
+        template.setKeySerializer(stringRedisSerializer);
+        template.setHashKeySerializer(stringRedisSerializer);  //hash key也采用string
+        //value 采用json
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+}

+ 49 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/config/RequestRateLimitConfiguration.java

@@ -0,0 +1,49 @@
+package cn.qinys.wishing.gateway.config;
+
+
+import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.util.StringUtils;
+import reactor.core.publisher.Mono;
+
+import java.util.UUID;
+
+/**
+ * 网关限流配置类
+ *
+ * @author tanlie
+ * @date 2024-8-30
+ */
+@Configuration
+public class RequestRateLimitConfiguration {
+
+    /**
+     * ip地址限流
+     *
+     * @return 限流key
+     */
+    @Bean
+    @Primary
+    public KeyResolver remoteAddressKeyResolver() {
+        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
+    }
+
+    /**
+     * 请求路径限流
+     *
+     * @return 限流key
+     */
+    @Bean
+    public KeyResolver apiKeyResolver() {
+        return exchange -> {
+            String token = exchange.getRequest().getHeaders().getFirst("Authorization");
+            if (!StringUtils.hasLength(token)) {
+                token = UUID.randomUUID().toString().replaceAll("-", "");
+            }
+            String routeId = exchange.getRequest().getURI().getPath() + "-" + token;
+            return Mono.just(routeId);
+        };
+    }
+}

+ 28 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/constants/GatewayConstants.java

@@ -0,0 +1,28 @@
+package cn.qinys.wishing.gateway.constants;
+
+import lombok.Getter;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/6/17 12:02
+ */
+public class GatewayConstants {
+    @Getter
+    public enum RedisKeyEnum {
+        // 行销行政编码
+        MOBILE_BY_TOKEN("MOBILE_BY_TOKEN:", "根据token获取手机号", 10 * 3600),
+        GAODE_LIMIT_BY_TOKEN("GAODE_LIMIT_BY_TOKEN:", "每小时限制的逆地理解析次数", 3600);
+
+        RedisKeyEnum(String code, String msg, Integer expireTime) {
+            this.code = code;
+            this.msg = msg;
+            this.expireTime = expireTime;
+        }
+
+        private final String code;
+        private final String msg;
+        private final Integer expireTime;
+    }
+}

+ 30 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/dto/LoginDTO.java

@@ -0,0 +1,30 @@
+package cn.qinys.wishing.gateway.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/2/29 18:12
+ */
+@Data
+public class LoginDTO implements Serializable {
+    /**
+     * 登录token
+     */
+    private String token;
+    /**
+     * 手机号
+     */
+    private String mobile;
+
+    /**
+     * 用户信息
+     */
+    private LoginUserInfoDTO userInfo;
+
+
+}

+ 56 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/dto/LoginUserInfoDTO.java

@@ -0,0 +1,56 @@
+package cn.qinys.wishing.gateway.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/2/29 18:13
+ */
+@Data
+public class LoginUserInfoDTO implements Serializable {
+
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 性别
+     */
+    private String gender;
+    /**
+     * 手机号
+     */
+
+    private String mobile;
+    /**
+     * 姓名
+     */
+    private String name;
+    /**
+     * 缩略图
+     */
+    private String thumbAvatar;
+    /**
+     * uuid
+     */
+    private String uuid;
+    /**
+     * wxId
+     */
+    private String wxId;
+
+    private Integer loginRole;
+
+    private String loginRoleName;
+
+    /**
+     * 租户id
+     */
+    private String tenantId;
+
+
+}

+ 90 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/entity/GatewayLog.java

@@ -0,0 +1,90 @@
+package cn.qinys.wishing.gateway.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * (GatewayLog)实体类
+ *
+ * @author makejava
+ * @since 2024-04-26 15:10:32
+ */
+
+@Data
+@TableName("upms_gateway_log")
+public class GatewayLog implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+    /**
+     * 头部token
+     */
+    @TableField("token")
+    private String token;
+    /**
+     * 访问实例
+     */
+    @TableField("target_server")
+    private String targetServer;
+    /**
+     * 请求路径
+     */
+    @TableField("request_path")
+    private String requestPath;
+    /**
+     * 请求方法
+     */
+    @TableField("request_method")
+    private String requestMethod;
+    /**
+     * 协议
+     */
+    @TableField("http_schema")
+    private String httpSchema;
+    /**
+     * 请求体
+     */
+    @TableField("request_body")
+    private String requestBody;
+    /**
+     * 相应体
+     */
+    @TableField("response_data")
+    private String responseData;
+    /**
+     * 请求ip
+     */
+    @TableField("ip")
+    private String ip;
+    /**
+     * 请求时间
+     */
+    @TableField("request_time")
+    private Date requestTime;
+    /**
+     * 响应时间
+     */
+    @TableField("response_time")
+    private Date responseTime;
+    /**
+     * 执行时间按
+     */
+    @TableField("execute_time")
+    private Long executeTime;
+
+    /**
+     * 执行时间按
+     */
+    @TableField("user_mobile")
+    private String userMobile;
+
+    @TableField("month")
+    private Integer month;
+
+}

+ 278 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/filter/AccessLogFilter.java

@@ -0,0 +1,278 @@
+package cn.qinys.wishing.gateway.filter;
+
+import cn.qinys.wishing.gateway.entity.GatewayLog;
+import cn.qinys.wishing.gateway.msgsender.IMessageService;
+import com.alibaba.nacos.common.utils.StringUtils;
+import org.reactivestreams.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.cloud.gateway.support.BodyInserterContext;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.HttpMessageReader;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerStrategies;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Create by Tanlie
+ * Date 2022-03-14 14:45
+ * Description
+ */
+@Component
+public class AccessLogFilter implements GlobalFilter, Ordered {
+    Logger logger = LoggerFactory.getLogger(this.getClass());
+    private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
+
+    @Autowired
+    IMessageService messageService;
+
+    /**
+     * 顺序必须是<-1,否则标准的NettyWriteResponseFilter将在您的过滤器得到一个被调用的机会之前发送响应
+     * 也就是说如果不小于 -1 ,将不会执行获取后端响应的逻辑
+     *
+     * @return
+     */
+    @Override
+    public int getOrder() {
+        return -100;
+    }
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        // 请求路径
+        String requestPath = request.getPath().pathWithinApplication().value();
+        Route route = this.getGatewayRoute(exchange);
+        String ipAddress = this.getIpAddress(request);
+        String token = request.getHeaders().getFirst("Authorization");
+        GatewayLog gatewayLog = new GatewayLog();
+        gatewayLog.setHttpSchema(request.getURI().getScheme());
+        gatewayLog.setRequestMethod(request.getMethod().name());
+        gatewayLog.setRequestPath(requestPath);
+        gatewayLog.setTargetServer(route.getId());
+        gatewayLog.setRequestTime(new Date());
+        gatewayLog.setIp(ipAddress);
+        gatewayLog.setToken(token);
+
+        MediaType mediaType = request.getHeaders().getContentType();
+        if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
+            return this.writeBodyLog(exchange, chain, gatewayLog);
+        } else {
+            //文件上传
+            return this.writeBasicLog(exchange, chain, gatewayLog);
+        }
+    }
+
+    private Route getGatewayRoute(ServerWebExchange exchange) {
+        return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
+    }
+
+    private Mono<Void> writeBasicLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {
+        StringBuilder builder = new StringBuilder();
+        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
+        for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+            builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ","));
+        }
+        accessLog.setRequestBody(builder.toString());
+        //获取响应体
+        ServerHttpResponseDecorator decoratedResponse = this.recordResponseLog(exchange, accessLog);
+        return chain.filter(exchange.mutate().response(decoratedResponse).build())
+                .then(Mono.fromRunnable(() -> {
+                    // 打印日志
+                    this.writeAccessLog(accessLog);
+                }));
+    }
+
+    /**
+     * 记录响应日志
+     * 通过 DataBufferFactory 解决响应体分段传输问题。
+     */
+    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, GatewayLog gatewayLog) {
+        ServerHttpResponse response = exchange.getResponse();
+        DataBufferFactory bufferFactory = response.bufferFactory();
+
+        return new ServerHttpResponseDecorator(response) {
+            @Override
+            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
+                if (body instanceof Flux) {
+                    Date responseTime = new Date();
+                    gatewayLog.setResponseTime(responseTime);
+                    // 计算执行时间
+                    long executeTime = (responseTime.getTime() - gatewayLog.getRequestTime().getTime());
+                    gatewayLog.setExecuteTime(executeTime);
+                    // 获取响应类型,如果是 json 就打印
+                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
+
+                    if (StringUtils.isNotBlank(originalResponseContentType)
+                            && originalResponseContentType.contains("application/json")) {
+                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
+                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
+                            // 合并多个流集合,解决返回体分段传输
+                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
+                            DataBuffer join = dataBufferFactory.join(dataBuffers);
+                            byte[] content = new byte[join.readableByteCount()];
+                            join.read(content);
+                            // 释放掉内存
+                            DataBufferUtils.release(join);
+                            String responseResult = new String(content, StandardCharsets.UTF_8);
+                            gatewayLog.setResponseData(responseResult);
+                            return bufferFactory.wrap(content);
+                        }));
+                    }
+                }
+                // if body is not a flux. never got there.
+                return super.writeWith(body);
+            }
+        };
+    }
+
+    /**
+     * 解决 request body 只能读取一次问题,
+     * 参考: org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory
+     *
+     * @param exchange
+     * @param chain
+     * @param gatewayLog
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    private Mono<Void> writeBodyLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog gatewayLog) {
+        ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
+
+        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
+                .flatMap(body -> {
+                    gatewayLog.setRequestBody(body);
+                    return Mono.just(body);
+                });
+
+        // 通过 BodyInserter 插入 body(支持修改body), 避免 request body 只能获取一次
+        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
+        HttpHeaders headers = new HttpHeaders();
+        headers.putAll(exchange.getRequest().getHeaders());
+        // the new content type will be computed by bodyInserter
+        // and then set in the request decorator
+        headers.remove(HttpHeaders.CONTENT_LENGTH);
+
+        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
+        return bodyInserter.insert(outputMessage, new BodyInserterContext())
+                .then(Mono.defer(() -> {
+                    // 重新封装请求
+                    ServerHttpRequest decoratedRequest = this.requestDecorate(exchange, headers, outputMessage);
+                    // 记录响应日志
+                    ServerHttpResponseDecorator decoratedResponse = this.recordResponseLog(exchange, gatewayLog);
+                    // 记录普通的
+                    return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())
+                            .then(Mono.fromRunnable(() -> {
+                                // 打印日志
+                                this.writeAccessLog(gatewayLog);
+                            }));
+                }));
+    }
+
+
+    /**
+     * 请求装饰器,重新计算 headers
+     *
+     * @param exchange
+     * @param headers
+     * @param outputMessage
+     * @return
+     */
+    private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers,
+                                                       CachedBodyOutputMessage outputMessage) {
+        return new ServerHttpRequestDecorator(exchange.getRequest()) {
+            @Override
+            public HttpHeaders getHeaders() {
+                long contentLength = headers.getContentLength();
+                HttpHeaders httpHeaders = new HttpHeaders();
+                httpHeaders.putAll(super.getHeaders());
+                if (contentLength > 0) {
+                    httpHeaders.setContentLength(contentLength);
+                } else {
+                    // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
+                    // httpbin.org
+                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+                }
+                return httpHeaders;
+            }
+
+            @Override
+            public Flux<DataBuffer> getBody() {
+                return outputMessage.getBody();
+            }
+        };
+    }
+
+    /**
+     * 打印日志
+     *
+     * @param gatewayLog 网关日志
+     *                   推入消息队列
+     * @author javadaily
+     * @date 2021/3/24 14:53
+     */
+    private void writeAccessLog(GatewayLog gatewayLog) {
+        messageService.send(gatewayLog);
+    }
+
+
+    private String getIpAddress(ServerHttpRequest request) {
+        HttpHeaders headers = request.getHeaders();
+        String ip = headers.getFirst("x-forwarded-for");
+        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
+            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
+            if (ip.contains(",")) {
+                ip = ip.split(",")[0];
+            }
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = headers.getFirst("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = headers.getFirst("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = headers.getFirst("HTTP_CLIENT_IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = headers.getFirst("X-Real-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
+        }
+        return ip;
+    }
+
+
+}

+ 39 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/limiter/DiyRedisRateLimiter.java

@@ -0,0 +1,39 @@
+package cn.qinys.wishing.gateway.limiter;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.gateway.event.FilterArgsEvent;
+import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
+import org.springframework.cloud.gateway.support.ConfigurationService;
+import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/8/30 11:51
+ */
+@Slf4j
+@Component
+public class DiyRedisRateLimiter extends RedisRateLimiter {
+    public DiyRedisRateLimiter(ReactiveStringRedisTemplate redisTemplate, RedisScript<List<Long>> script, ConfigurationService configurationService) {
+        super(redisTemplate, script, configurationService);
+    }
+
+    private final String KEY_RESOLVER_KEY = "key-resolver";
+
+    @Override
+    public void onApplicationEvent(FilterArgsEvent event) {
+        Map<String, Object> args = event.getArgs();
+        if (args.containsKey(KEY_RESOLVER_KEY)) {
+            String routeId = event.getRouteId() + args.get(KEY_RESOLVER_KEY).hashCode();
+            log.debug("MultiRedisRateLimiter::routeId={}", routeId);
+            super.onApplicationEvent(new FilterArgsEvent(event.getSource(), routeId, args));
+        }
+        super.onApplicationEvent(event);
+    }
+}

+ 75 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/limiter/DiyRequestRateLimiterGatewayFilterFactory.java

@@ -0,0 +1,75 @@
+package cn.qinys.wishing.gateway.limiter;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
+import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/8/30 11:45
+ */
+@Slf4j
+@Component
+public class DiyRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
+
+    private final RateLimiter defaultRateLimiter;
+    private final KeyResolver defaultKeyResolver;
+
+
+    public DiyRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
+        super(defaultRateLimiter, defaultKeyResolver);
+        this.defaultRateLimiter = defaultRateLimiter;
+        this.defaultKeyResolver = defaultKeyResolver;
+    }
+
+    @Override
+    public GatewayFilter apply(Config config) {
+        KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
+        RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
+        return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
+            String routeId = config.getRouteId() + config.getKeyResolver().hashCode();
+            return limiter.isAllowed(routeId, key).flatMap(response -> {
+                for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
+                    exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
+                }
+                if (response.isAllowed()) {
+                    return chain.filter(exchange);
+                }
+                String token = exchange.getRequest().getHeaders().getFirst("Authorization");
+                log.error("已限流: routeId ={}, token={}", routeId, token);
+                ServerHttpResponse httpResponse = exchange.getResponse();
+                //修改code为500
+                httpResponse.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
+                if (!httpResponse.getHeaders().containsKey("Content-Type")) {
+                    httpResponse.getHeaders().add("Content-Type", "application/json");
+                }
+                //此处无法触发全局异常处理,手动返回
+                DataBuffer buffer = httpResponse.bufferFactory().wrap(("{\n"
+                        + "  \"code\": \"1414\","
+                        + "  \"msg\": \"服务器限流\","
+                        + "  \"data\": \"Server throttling\""
+                        + "}").getBytes(StandardCharsets.UTF_8));
+                return httpResponse.writeWith(Mono.just(buffer));
+            });
+        });
+    }
+
+
+    private <T> T getOrDefault(T configValue, T defaultValue) {
+        return (configValue != null) ? configValue : defaultValue;
+    }
+
+
+}

+ 22 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/mapper/GatewayLogMapper.java

@@ -0,0 +1,22 @@
+package cn.qinys.wishing.gateway.mapper;
+
+import cn.qinys.wishing.gateway.entity.GatewayLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+
+/**
+ * (GatewayLog)表数据库访问层
+ *
+ * @author makejava
+ * @since 2024-04-26 15:10:38
+ */
+ @Repository
+ @Mapper
+public interface GatewayLogMapper extends BaseMapper<GatewayLog>{
+
+
+}
+

+ 15 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/msgsender/IMessageService.java

@@ -0,0 +1,15 @@
+package cn.qinys.wishing.gateway.msgsender;
+
+
+import cn.qinys.wishing.gateway.entity.GatewayLog;
+
+/**
+ * Create by Tanlie
+ * Date 2022-03-17 9:06
+ * Description
+ */
+public interface IMessageService {
+
+
+    void send(GatewayLog log);
+}

+ 105 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/msgsender/RabbitmqServiceImpl.java

@@ -0,0 +1,105 @@
+package cn.qinys.wishing.gateway.msgsender;
+
+
+import cn.qinys.wishing.gateway.constants.GatewayConstants;
+import cn.qinys.wishing.gateway.dto.LoginDTO;
+import cn.qinys.wishing.gateway.entity.GatewayLog;
+import cn.qinys.wishing.gateway.mapper.GatewayLogMapper;
+import cn.qinys.wishing.gateway.utils.RedisUtils;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.util.DateUtils;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Date 2022-03-17 9:06
+ * Description
+ *
+ * @author Tanlie
+ */
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class RabbitmqServiceImpl implements IMessageService {
+    private final GatewayLogMapper logMapper;
+    private final RedisUtils redisUtils;
+
+    @Async
+    @Override
+    public void send(GatewayLog gatewayLog) {
+        try {
+            if (gatewayLog.getRequestPath().contains(".jpg")
+                    || gatewayLog.getRequestPath().contains(".png")
+                    || gatewayLog.getRequestPath().contains(".jpeg")) {
+                return;
+            }
+            gatewayLog.setMonth(LocalDate.now().getMonthValue());
+            gatewayLog.setUserMobile(this.getMobileByToken(gatewayLog.getToken(), gatewayLog.getMonth()));
+            logMapper.insert(gatewayLog);
+        } catch (Exception e) {
+            log.error("收集网关日志出错 == {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 获取用户
+     *
+     * @param token
+     * @return
+     */
+    private String getMobileByToken(String token, Integer month) {
+        if (!StringUtils.hasText(token)) {
+            return "";
+        }
+        token = token.replaceFirst("Bearer ", "");
+        if (!StringUtils.hasLength(token)) {
+            return "";
+        }
+        String key = GatewayConstants.RedisKeyEnum.MOBILE_BY_TOKEN.getCode() + token;
+        if (redisUtils.exists(key)) {
+            return (String) redisUtils.get(key);
+        }
+        String mobile = "";
+        try {
+            List<String> loginPath = new ArrayList<>();
+            loginPath.add("/dgapi/mobile/map/user/login");
+            loginPath.add("/dgapi/v3/mobile/map/user/login");
+            loginPath.add("/dgapi/upms/login/admin-token");
+            QueryWrapper<GatewayLog> wrapper = new QueryWrapper<>();
+            wrapper.select("id", "response_data");
+            wrapper.eq("month", month);
+            wrapper.in("request_path", loginPath);
+            wrapper.eq("DATE_FORMAT(request_time,'%Y-%m-%d')", DateUtils.format(new Date(), "yyyy-MM-dd"));
+            wrapper.orderByDesc("id");
+            List<GatewayLog> logList = logMapper.selectList(wrapper);
+            for (GatewayLog gatewayLog : logList) {
+                JSONObject json = JSON.parseObject(gatewayLog.getResponseData());
+                if (json.getIntValue("code", 0) == 200) {
+                    LoginDTO dto = JSON.parseObject(json.getString("data"), LoginDTO.class);
+                    if (token.equalsIgnoreCase(dto.getToken())) {
+                        if (dto.getMobile() != null) {
+                            mobile = dto.getMobile();
+                        } else {
+                            mobile = dto.getUserInfo().getMobile();
+                        }
+                        break;
+                    }
+                }
+            }
+        } catch (Exception ignored) {
+        }
+        redisUtils.set(key, mobile, GatewayConstants.RedisKeyEnum.MOBILE_BY_TOKEN.getExpireTime());
+        return mobile;
+    }
+}

+ 37 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/response/UnifyResponse.java

@@ -0,0 +1,37 @@
+package cn.qinys.wishing.gateway.response;
+
+import com.alibaba.fastjson.JSON;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author tanlie
+ * Date 2021/12/24 15:58
+ * Description
+ */
+
+@Component
+public class UnifyResponse {
+
+    public static Mono<Void> fail(ServerHttpResponse response, Integer code, String msg) {
+        if (StringUtils.hasLength(msg) && msg.contains("404 NOT_FOUND")) {
+            code = 404;
+        }
+        Map<String, Object> map = new HashMap<>(3);
+        map.put("code", code);
+        map.put("msg", msg);
+        map.put("data", "");
+        String jsonStr = JSON.toJSONString(map);
+        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+        DataBuffer buffer = response.bufferFactory().wrap(jsonStr.getBytes());
+        return response.writeWith(Mono.just(buffer));
+    }
+
+
+}

+ 43 - 0
wishing-platform/wishing-gateway/src/main/java/cn/qinys/wishing/gateway/utils/RedisUtils.java

@@ -0,0 +1,43 @@
+package cn.qinys.wishing.gateway.utils;
+
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Description:
+ *
+ * @author tanlie
+ * Date: 2024/1/9 9:08
+ */
+@Component
+public class RedisUtils {
+
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 分布式锁过期时间
+     */
+    private final long EXPIRE_TIME = 5 * 10000;
+
+    public Object get(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    public void set(String key, Object obj) {
+        redisTemplate.opsForValue().set(key, obj);
+    }
+
+    public void set(String key, Object obj, long expire) {
+        redisTemplate.opsForValue().set(key, obj, expire, TimeUnit.SECONDS);
+    }
+
+    public Boolean exists(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+}

+ 35 - 0
wishing-platform/wishing-gateway/src/main/resources/application-local.yml

@@ -0,0 +1,35 @@
+spring:
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 192.168.1.7:8848
+        namespace: a62810f4-be60-4973-8bd0-8199d19526ef
+        username: nacos
+        password: h8zG&mehJ#.s7V
+        group: wishing
+
+  datasource:
+    username: root
+    password: "@@qinys12346.."
+    url: jdbc:mysql://192.168.1.7:3306/gateway?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    type: com.alibaba.druid.pool.DruidDataSource
+
+
+  data:
+    redis:
+      host: 192.168.1.7
+      port: 6379
+      password: "XinchanR@2022###"
+      database: 0
+
+mybatis-plus:
+  configuration:
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    map-underscore-to-camel-case: true
+  global-config:
+    db-config:
+      logic-delete-field: deleted
+      logic-delete-value: 1
+      logic-not-delete-value: 0
+

+ 24 - 0
wishing-platform/wishing-gateway/src/main/resources/application-router.yml

@@ -0,0 +1,24 @@
+spring:
+  cloud:
+    gateway:
+      server:
+        webflux:
+          routes:
+            # 移动端管理
+            - id: wishing-mobile
+              uri: lb://wishing-mobile
+              predicates:
+                - Path=/api/mobile/**
+              filters:
+                - StripPrefix=2
+                - name: DiyRequestRateLimiter # ip限流
+                  args:
+                    redis-rate-limiter.burstCapacity: 200 #令牌桶的容积
+                    redis-rate-limiter.replenishRate: 100 # 流速 每秒
+                    key-resolver: "#{@remoteAddressKeyResolver}"
+                - name: DiyRequestRateLimiter #同一个用户,同一个接口每秒只能访问5次
+                  args:
+                    redis-rate-limiter.burstCapacity: 20 #令牌桶的容积
+                    redis-rate-limiter.replenishRate: 10 # 流速 每秒
+                    key-resolver: "#{@apiKeyResolver}"
+

+ 7 - 0
wishing-platform/wishing-gateway/src/main/resources/application.yml

@@ -0,0 +1,7 @@
+server:
+  port: 8999
+spring:
+  application:
+    name: gateway
+  profiles:
+    active: router, local

+ 38 - 0
wishing-platform/wishing-gateway/src/test/java/cn/qinys/AppTest.java

@@ -0,0 +1,38 @@
+package cn.qinys;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}

+ 28 - 0
wishing-platform/wishing-service/pom.xml

@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.qinys</groupId>
+        <artifactId>wishing-platform</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>wishing-service</artifactId>
+    <packaging>jar</packaging>
+
+    <name>wishing-service</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 13 - 0
wishing-platform/wishing-service/src/main/java/cn/qinys/App.java

@@ -0,0 +1,13 @@
+package cn.qinys;
+
+/**
+ * Hello world!
+ *
+ */
+public class App 
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
+    }
+}

+ 38 - 0
wishing-platform/wishing-service/src/test/java/cn/qinys/AppTest.java

@@ -0,0 +1,38 @@
+package cn.qinys;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}