目录

一、swagger
在oj-common下创建,oj-common-swagger⼦module。
依赖导入:
xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
注解介绍:
| 注解 | 用途 | 用于 | 常用属性 |
|---|---|---|---|
| @Tag | ⽤于给接⼝分组,⽤途类似于为接⼝⽂档添加标签 | ⽅法、类、接⼝ | name:分组的名称 |
| @Operation | ⽤于描述接⼝的操作 | 方法 | summary:操作的摘要信息。 description:操作的详细描述。 |
| @Parameters | ⽤于指定@Parameter注解对象数组,描述操作的输⼊参数 | ⽅法 | |
| @Parameter | ⽤于描述输⼊参数 | ⽅法 | name:参数的名称。in:参数的位置,可以是path、query、header、cookie中的⼀种。description:参数的描述 |
| @ApiResponse | ⽤于描述操作的响应结果 | ⽅法 | responseCode:响应的状态码。description:响应的描述。 |
| @Schema | ⽤于描述数据模型的属性 | ⽅法、类、接⼝ | description:响应的描述。 |
配置:
java
package com.yj.common.swagger;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("在线判题系统")
.description(" 在线判题系统 接⼝⽂档")
.version("v1") );
}
}
配置自动加载:
oj-common/oj-common-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中加上SwaggerConfig 的路径:

二、全局异常处理
在oj-common下,创建oj-common-security⼦⼯程
引入依赖:
xml
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>oj-common-core</artifactId>
<version>${oj-common-core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
创建GlobalExceptionHandler类
- @RestControllerAdvice:当抛出异常时,@RestControllerAdvice 标注的类将被⾃动调⽤,并根据异常类型和处理程序的注解来决定如何处理该异常。这使得开发者可以在整个应⽤程序范围内统⼀处理异常。
- @ExceptionHandler:@ExceptionHandler⼀般与@RestControllerAdvice配合使⽤,使⽤其来捕
获和处理不同类型的异常。
java
package com.yj.common.security;
import com.yj.common.core.enums.ResultCodeEnum;
import com.yj.common.core.pojo.Result;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<?>
handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不⽀持'{}'请求", requestURI, e.getMethod());
return Result.fail(ResultCodeEnum.ERROR);
}
/**
*
拦截运⾏时异常
*/
@ExceptionHandler(RuntimeException.class)
public Result<?> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发⽣异常.", requestURI, e);
return Result.fail(ResultCodeEnum.ERROR);
}
/**
*系统异常
*/
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}', 发⽣异常.", requestURI, e);
return Result.fail(ResultCodeEnum.ERROR);
}
}
创建src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports⽂件
java
com.yj.common.security.GlobalExceptionHandler
三、日志框架
⽇志框架使用:slf4j+logback
滚动策略:
- TimeBasedRollingPolicy:最常⽤的滚动策略,它根据时间来制定滚动策略。
- SizeBasedTriggeringPolicy:基于⽇志⽂件⼤⼩的滚动策略。当⽇志⽂件达到指定的⼤⼩时,它会被滚动(即创建⼀个新的⽇志⽂件)。
- FixedWindowRollingPolicy:固定窗⼝滚动策略。它根据⼀个固定的窗⼝⼤⼩(即可以保留的⽇志⽂件数量)来滚动⽇志⽂件。
在oj-modules/oj-system/src/main/resources/logback.xml下添加:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- ⽇志存放路径 -->
<property name="log.path" value="logs/oj-system" />
<!-- ⽇志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!--系统⽇志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!--循环政策:基于时间创建⽇志⽂件 -->
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--⽇志⽂件名格式-->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- ⽇志最⼤的历史 10天 -->
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录)-->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建⽇志⽂件-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- ⽇志⽂件名格式-->
<fileNamePattern>${log.path}/error.%d{yyyy-MM
dd}.log</fileNamePattern>
<!-- ⽇志最⼤的历史10天 -->
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别-->
<level>ERROR</level>
<!--匹配时的操作:接收(记录)-->
<onMatch>ACCEPT</onMatch>
<!--不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- ⽇志级别-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="console" />
<appender-ref ref="file_error" />
</root>
</configuration>
其他模块的日志配置,只需要将日志输出的存放路径修改为对应的模块名即可。
java
<!-- ⽇志存放路径 -->
<property name="log.path" value="logs/oj-system" />
四、Nacos
docker 拉取nacos镜像:
java
docker pull nacos/nacos-server:v2.2.3
配置MySQL数据库:
https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql
sql
/******************************************/
/*
库名称 oj_nacos_local
*/
/******************************************/
create database oj_nacos_local;
/******************************************/
/*
表名称 config_info
*/
/******************************************/
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) DEFAULT NULL COMMENT 'group_id',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description',
`c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage',
`effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述',
`type` varchar(64) DEFAULT NULL COMMENT '配置的类型',
`c_schema` text COMMENT '配置的模式',
`encrypted_data_key` text NOT NULL COMMENT '密钥',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/*
表名称 config_info_aggr
*/
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum`
(`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/*
表名称 config_info_beta
*/
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` text NOT NULL COMMENT '密钥',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant`
(`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
COMMENT='config_info_beta';
/******************************************/
/*
表名称 config_info_tag
*/
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag`
(`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
COMMENT='config_info_tag';
/******************************************/
/*
表名称 config_tags_relation
*/
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
COMMENT='config_tag_relation';
/******************************************/
/*
表名称 group_capacity
*/
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '
单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/*
表名称 his_config_info
*/
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(20) unsigned NOT NULL COMMENT 'id',
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`op_type` char(10) DEFAULT NULL COMMENT 'operation type',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` text NOT NULL COMMENT '密钥',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/*
表名称 tenant_capacity
*/
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username',
`password` varchar(500) NOT NULL COMMENT 'password',
`enabled` boolean NOT NULL COMMENT 'enabled'
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL COMMENT 'username',
`role` varchar(50) NOT NULL COMMENT 'role',
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL COMMENT 'role',
`resource` varchar(128) NOT NULL COMMENT 'resource',
`action` varchar(8) NOT NULL COMMENT 'action',
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos',
'$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
启动容器:
java
docker run -d -p 8848:8848 -p 9848:9848 --name oj-nacos -e MODE=standalone -e JVM_XMS=256m -e JVM_XMX=256m -e SPRING_DATASOURCE_PLATFORM=mysql -e MYSQL_SERVICE_HOST=${mysql_ip} -e MYSQL_SERVICE_PORT=${mysql_port} -e MYSQL_SERVICE_DB_NAME=${nacos_db_name} -e MYSQL_SERVICE_USER=${mysql_user} -e MYSQL_SERVICE_PASSWORD=${mysql_password} nacos/nacos-server:v2.2.3
docker run -d -p 8848:8848 -p 9848:9848 --name oj-nacos -e MODE=standalone -e JVM_XMS=256m -e JVM_XMX=256m -e SPRING_DATASOURCE_PLATFORM=mysql -e MYSQL_SERVICE_HOST=172.17.0.2 -e MYSQL_SERVICE_PORT=3306 -e MYSQL_SERVICE_DB_NAME=oj-nacos-dev -e MYSQL_SERVICE_USER=ojtest -e MYSQL_SERVICE_PASSWORD=123456 nacos/nacos-server:v2.2.3
命名空间:

新增配置:

yml
server:
port: 9201
spring:
datasource:
url: jdbc:mysql://localhost:3306/oj_dev?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
username: ojtest
password: 123456
hikari:
minimum-idle: 5 #最⼩空闲连接数
maximum-pool-size: 20 # 最⼤连接数
idle-timeout: 30000 # 空闲连接存活时间(毫秒)
connection-timeout: 30000 # 连接超时时间(毫秒)
修改bootstrap.yml:
yml
spring:
application:
name: oj-system
profiles:
active: local
cloud:
nacos:
discovery:
namespace: e5d83703-5dcb-4f94-b252-ce7b37d960ce
server-addr: http://localhost:8848
config:
namespace: e5d83703-5dcb-4f94-b252-ce7b37d960ce
server-addr: http://localhost:8848
file-extension: yaml
在oj-modules的pom文件中引入依赖:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
五、API网关
在yi-oj创建子模块oj-gateway:
引入依赖:
xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>yj-oj</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>oj-gateway</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- SpringCloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- SpringCloud Loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
</project>
修改yaml文件oj-gateway/src/main/resources/bootstrap.yml:
yml
spring:
application:
name: oj-gateway
profiles:
active: local
cloud:
nacos:
discovery:
namespace: e5d83703-5dcb-4f94-b252-ce7b37d960ce
server-addr: http://localhost:8848
config:
namespace: e5d83703-5dcb-4f94-b252-ce7b37d960ce
server-addr: http://localhost:8848
file-extension: yaml
启动类:
java
package com.yj.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OjGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(OjGatewayApplication.class,args);
}
}
nacos配置:
- id:⾃定义路由ID,保持唯⼀
url⽬标服务地址,⽀持普通URI及lb://应⽤注册服务名称 .lb表⽰负载均衡,使⽤lb://⽅式表⽰从注册中⼼获取服务地址.- predicates: 路由条件,根据匹配结果决定是否执⾏该请求路由,上述代码中,我们把符合Path规则的⼀切请求,都代理到uri参数指定的地址.
- filters:于定义在请求转发到⽬标地址之前或之后执⾏的过滤器。
- StripPrefix:这是⼀个StripPrefix过滤器,它的作⽤是从请求的路径中去除⼀部分前缀。 StripPrefix=1
表⽰去除1个路径段。例如,如果原始请求的路径是/system/test那么经过这个过滤器处理后,转发到⽬标服务的路径就变成了 /test
yaml
server:
port: 19090
spring:
cloud:
gateway:
routes:
#管理模块
- id: oj-system
uri: lb://oj-system
predicates:
- Path=/system/**
filters:
- StripPrefix=1

六、Redis
安装redis
java
docker pull redis
启动redis
java
docker run --name oj-redis -d -p 6379:6379 redis --requirepass "123456"
在 oj-common 模块下创建 oj-common-redis 子模块
引入依赖:
xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>oj-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>oj-common-redis</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- SpringBoot Boot Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Alibaba Fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.43</version>
</dependency>
</dependencies>
</project>
提供redis配置:
java
package com.yj.common.redis.config;
import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
public class JsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
}
java
package com.yj.common.redis.config;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
JsonRedisSerializer serializer = new JsonRedisSerializer(Object.class);
//使⽤StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采⽤StringRedisSerializer的序列化⽅式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
封装service:
java
package com.yj.common.redis.service;
import com.alibaba.fastjson2.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component
public class RedisService {
@Autowired
public RedisTemplate redisTemplate;
//**************************** 操作key ***************************
/**
* 判断 key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key) {
return redisTemplate.delete(key);
}
//**************************** 操作String类型 ***************************
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key, Class<T> clazz) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
T t = operation.get(key);
if (t instanceof String) {
return t;
}
return JSON.parseObject(String.valueOf(t), clazz);
}
//****************** 操作list结构 *******************
/**
* 获取list中存储数据数量
*
* @param key
* @return
*/
public Long getListSize(final String key) {
return redisTemplate.opsForList().size(key);
}
/**
* 获取list中指定范围数据
*
* @param key
* @param start
* @param end
* @param clazz
* @param <T>
* @return
*/
public <T> List<T> getCacheListByRange(final String key, long start, long end, Class<T> clazz) {
List range = redisTemplate.opsForList().range(key, start, end);
if (CollectionUtils.isEmpty(range)) {
return null;
}
return JSON.parseArray(JSON.toJSONString(range), clazz);
}
/**
* 底层使用list结构存储数据(尾插 批量插入)
*/
public <T> Long rightPushAll(final String key, Collection<T> list) {
return redisTemplate.opsForList().rightPushAll(key, list);
}
/**
* 底层使用list结构存储数据(头插)
*/
public <T> Long leftPushForList(final String key, T value) {
return redisTemplate.opsForList().leftPush(key, value);
}
/**
* 底层使用list结构,删除指定数据
*/
public <T> Long removeForList(final String key, T value) {
return redisTemplate.opsForList().remove(key, 1L, value);
}
//**************************** 操作hash类型 ***************************
/**
* 获取缓存Map Value
*/
public <T> T getCacheMapValue(final String key, final String hKey, Class<T> clazz) {
Object cacheMapValue = redisTemplate.opsForHash().get(key, hKey);
if (cacheMapValue != null) {
return JSON.parseObject(String.valueOf(cacheMapValue), clazz);
}
return null;
}
/**
* 获取多个hash中的数据
*
* @param key Redis键
* @param hKeys hash键集合
* @param clazz 待转换对象类型
* @return hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<String> hKeys, Class<T> clazz) {
List<Object> list = redisTemplate.opsForHash().multiGet(key, hKeys);
List<T> result = new ArrayList<>();
for (Object item : list) {
result.add(JSON.parseObject(JSON.toJSONString(item), clazz));
}
return result;
}
/**
* 往hash中存入数据
*
* @param key Redis键
* @param hKey hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <K, T> void setCacheMap(final String key, final Map<K, T> dataMap) {
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
public Long deleteCacheMapValue(final String key, final String hKey) {
return redisTemplate.opsForHash().delete(key, hKey);
}
}
oj-common/oj-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:
java
com.yj.common.redis.service.RedisService
com.yj.common.redis.config.RedisConfig
修改nacos中的oj-system-local.yaml配置
yml
server:
port: 9201
spring:
datasource:
url: jdbc:mysql://localhost:3306/oj_dev?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
username: ojtest
password: 123456
hikari:
minimum-idle: 5 #最⼩空闲连接数
maximum-pool-size: 20 # 最⼤连接数
idle-timeout: 30000 # 空闲连接存活时间(毫秒)
connection-timeout: 30000 # 连接超时时间(毫秒)
data:
redis:
host: localhost
password: 123456