Showing preview only (672K chars total). Download the full file or copy to clipboard to get everything.
Repository: Decade-qiu/D-OnlineJudge
Branch: main
Commit: 519a23071aab
Files: 225
Total size: 557.8 KB
Directory structure:
gitextract_vd6v_lct/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── ci-cd.yml
├── .gitignore
├── DOJ-BE/
│ ├── .gitignore
│ ├── common/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── common/
│ │ │ │ ├── annotation/
│ │ │ │ │ └── AdminRequired.java
│ │ │ │ ├── client/
│ │ │ │ │ ├── ProblemClient.java
│ │ │ │ │ ├── SubmissionClient.java
│ │ │ │ │ └── UserClient.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── custom/
│ │ │ │ │ │ ├── DefaultFeignConfig.java
│ │ │ │ │ │ ├── JwtTool.java
│ │ │ │ │ │ ├── MVCConfig.java
│ │ │ │ │ │ ├── MybatisConfig.java
│ │ │ │ │ │ └── SecurityConfig.java
│ │ │ │ │ ├── global/
│ │ │ │ │ │ └── CommonExceptionConfig.java
│ │ │ │ │ ├── properties/
│ │ │ │ │ │ ├── AppNameProperties.java
│ │ │ │ │ │ ├── JwtProperties.java
│ │ │ │ │ │ └── ResourceProperties.java
│ │ │ │ │ └── thread/
│ │ │ │ │ └── ThreadPoolConfig.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── PageDTO.java
│ │ │ │ │ ├── PageQueryDTO.java
│ │ │ │ │ ├── R.java
│ │ │ │ │ ├── json/
│ │ │ │ │ │ └── StringListDeserializer.java
│ │ │ │ │ ├── po/
│ │ │ │ │ │ ├── Problem.java
│ │ │ │ │ │ ├── Submission.java
│ │ │ │ │ │ └── User.java
│ │ │ │ │ └── vo/
│ │ │ │ │ ├── ExecuteMessage.java
│ │ │ │ │ ├── InfoVO.java
│ │ │ │ │ ├── StatsVO.java
│ │ │ │ │ └── SubmissionStatsVO.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── BadRequestException.java
│ │ │ │ │ ├── BizIllegalException.java
│ │ │ │ │ ├── CommonException.java
│ │ │ │ │ ├── DbException.java
│ │ │ │ │ ├── ForbiddenException.java
│ │ │ │ │ └── UnauthorizedException.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── AdminCheckInterceptor.java
│ │ │ │ │ └── IdentityInterceptor.java
│ │ │ │ └── utils/
│ │ │ │ ├── LocalResource.java
│ │ │ │ └── UserContext.java
│ │ │ └── resources/
│ │ │ ├── META-INF/
│ │ │ │ ├── spring-configuration-metadata.json
│ │ │ │ └── spring.factories
│ │ │ ├── application-common.yaml
│ │ │ └── doj.jks
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── common/
│ │ └── domain/
│ │ └── po/
│ │ └── ProblemJsonTest.java
│ ├── gateway-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── gateway/
│ │ │ ├── GatewayApplication.java
│ │ │ ├── config/
│ │ │ │ └── properties/
│ │ │ │ └── AuthProperties.java
│ │ │ └── filters/
│ │ │ ├── AuthGlobalFilter.java
│ │ │ └── CrossFilter.java
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── bootstrap.yaml
│ ├── pom.xml
│ ├── problem-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── problem/
│ │ │ │ ├── ProblemApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── CacheConfig.java
│ │ │ │ │ └── ProblemIndexInitializer.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── ProblemController.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ └── ProblemDocument.java
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── ProblemPageQueryDTO.java
│ │ │ │ │ │ └── ProblemTagView.java
│ │ │ │ │ └── po/
│ │ │ │ │ ├── Problem.java
│ │ │ │ │ ├── ProblemTag.java
│ │ │ │ │ └── Tag.java
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── ProblemMapper.java
│ │ │ │ │ ├── ProblemTagMapper.java
│ │ │ │ │ └── TagMapper.java
│ │ │ │ ├── mq/
│ │ │ │ │ ├── MqConfig.java
│ │ │ │ │ └── StatsUpdateListener.java
│ │ │ │ ├── repository/
│ │ │ │ │ └── ProblemRepository.java
│ │ │ │ └── service/
│ │ │ │ ├── IProblemService.java
│ │ │ │ └── impl/
│ │ │ │ └── ProblemServiceImpl.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ ├── es/
│ │ │ │ └── problem-settings.json
│ │ │ ├── mapper/
│ │ │ │ └── ProblemMapper.xml
│ │ │ └── sql/
│ │ │ └── problem_rebuild.sql
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── problem/
│ │ └── TestProblem.java
│ ├── sandbox-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── sandbox/
│ │ │ │ ├── SandboxApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ └── MqConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── SandboxController.java
│ │ │ │ ├── domain/
│ │ │ │ │ └── vo/
│ │ │ │ │ ├── ExecuteMessage.java
│ │ │ │ │ └── JudgingTask.java
│ │ │ │ ├── enums/
│ │ │ │ │ └── LanguageEnum.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ISandboxService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ └── SandboxService.java
│ │ │ │ └── worker/
│ │ │ │ └── JudgingWorker.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ └── test/
│ │ │ ├── 20240906.py
│ │ │ ├── main.cpp
│ │ │ └── main.java
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── sandbox/
│ │ └── SandboxTest.java
│ ├── submission-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── submission/
│ │ │ │ ├── SubmissionApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── MqConfig.java
│ │ │ │ │ └── WebSocketConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── SubmissionController.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── SubmissionPageQueryDTO.java
│ │ │ │ │ └── po/
│ │ │ │ │ └── Submission.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── SubmissionMapper.java
│ │ │ │ ├── mq/
│ │ │ │ │ └── ResultListener.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ISubmissionService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ └── ISubmissionServiceImpl.java
│ │ │ │ └── websocket/
│ │ │ │ └── SubmissionWSServer.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── submission/
│ │ │ └── mapper/
│ │ │ └── SubmissionMapper.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── submission/
│ │ └── SubmissionTest.java
│ └── user-service/
│ ├── Dockerfile
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── user/
│ │ │ ├── UserApplication.java
│ │ │ ├── controller/
│ │ │ │ ├── AnnouncementController.java
│ │ │ │ └── UserController.java
│ │ │ ├── domain/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── LoginDTO.java
│ │ │ │ │ ├── RegisterDTO.java
│ │ │ │ │ └── UpdPwdDTO.java
│ │ │ │ ├── po/
│ │ │ │ │ ├── Announcement.java
│ │ │ │ │ └── User.java
│ │ │ │ └── vo/
│ │ │ │ ├── InfoVO.java
│ │ │ │ ├── LoginVO.java
│ │ │ │ └── RankVO.java
│ │ │ ├── mapper/
│ │ │ │ ├── AnnouncementMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── mq/
│ │ │ │ ├── MqConfig.java
│ │ │ │ └── StatsUpdateListener.java
│ │ │ ├── service/
│ │ │ │ ├── IAnnouncementService.java
│ │ │ │ ├── IUserService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AnnouncementServiceImpl.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ └── utils/
│ │ │ └── AESTool.java
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── bootstrap.yaml
│ └── test/
│ └── java/
│ └── com/
│ └── decade/
│ └── doj/
│ └── user/
│ └── UserTest.java
├── DOJ-FE/
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── index.html
│ ├── nginx.conf
│ ├── package.json
│ ├── run-docker.sh
│ ├── scripts/
│ │ └── preinstall.js
│ ├── src/
│ │ ├── App.vue
│ │ ├── api/
│ │ │ ├── announcement/
│ │ │ │ └── index.ts
│ │ │ ├── base/
│ │ │ │ └── index.ts
│ │ │ ├── problem/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ ├── stats/
│ │ │ │ └── index.ts
│ │ │ ├── submission/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ ├── submit/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ └── user/
│ │ │ ├── index.ts
│ │ │ └── type.ts
│ │ ├── components/
│ │ │ ├── CodeEditor/
│ │ │ │ ├── codeEditor.vue
│ │ │ │ ├── index.vue
│ │ │ │ ├── lang-code/
│ │ │ │ │ ├── cpp/
│ │ │ │ │ │ ├── cpp.cpp
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── java/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── java.java
│ │ │ │ │ └── python/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── python.py
│ │ │ │ ├── languages.ts
│ │ │ │ ├── problemEditor.vue
│ │ │ │ ├── showEditor.vue
│ │ │ │ ├── themes.ts
│ │ │ │ └── toolbar.vue
│ │ │ ├── SvgIcon/
│ │ │ │ └── index.vue
│ │ │ └── index.ts
│ │ ├── main.ts
│ │ ├── router/
│ │ │ └── index.ts
│ │ ├── stores/
│ │ │ └── userStore/
│ │ │ └── index.ts
│ │ ├── styles/
│ │ │ ├── index.scss
│ │ │ ├── theme.scss
│ │ │ └── variable.scss
│ │ ├── utils/
│ │ │ ├── markdown.ts
│ │ │ ├── request.ts
│ │ │ └── websocket.ts
│ │ ├── views/
│ │ │ ├── Admin/
│ │ │ │ ├── Problem/
│ │ │ │ │ ├── components/
│ │ │ │ │ │ └── ProblemDialog.vue
│ │ │ │ │ └── index.vue
│ │ │ │ └── index.vue
│ │ │ ├── Home/
│ │ │ │ └── index.vue
│ │ │ ├── Layout/
│ │ │ │ ├── components/
│ │ │ │ │ ├── LayoutFixed.vue
│ │ │ │ │ └── LayoutFooter.vue
│ │ │ │ └── index.vue
│ │ │ ├── OnlineEditor/
│ │ │ │ └── index.vue
│ │ │ ├── Problem/
│ │ │ │ ├── detail/
│ │ │ │ │ └── index.vue
│ │ │ │ └── index.vue
│ │ │ ├── Rankings/
│ │ │ │ └── index.vue
│ │ │ ├── Status/
│ │ │ │ └── index.vue
│ │ │ └── User/
│ │ │ ├── home/
│ │ │ │ └── index.vue
│ │ │ ├── info/
│ │ │ │ └── index.vue
│ │ │ ├── login/
│ │ │ │ └── index.vue
│ │ │ └── register/
│ │ │ └── index.vue
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── LICENSE
├── README.md
├── docker-compose-monitoring.yml
├── docker-compose-service.yml
├── docs/
│ ├── 0.build.md
│ ├── 0.re.md
│ ├── 0.technology.md
│ ├── 1.docker部署.md
│ ├── 2.Common项目配置.md
│ ├── 3.Gateway项目配置.md
│ ├── 4.User-service项目配置.md
│ ├── 5.Problem-service项目配置.md
│ ├── 6.Submission-service项目配置.md
│ ├── 7.Sandbox-service项目配置.md
│ └── SQL/
│ ├── doj_problem.sql
│ ├── doj_submission.sql
│ ├── doj_user.sql
│ └── nacos.sql
└── prometheus/
├── grafana-dashboard.json
├── prometheus.yml
└── promtail-config.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.vue linguist-vendored
================================================
FILE: .github/workflows/ci-cd.yml
================================================
name: CI/CD for DOJ Backend
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
service: ['user-service', 'problem-service', 'submission-service', 'sandbox-service', 'gateway-service']
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
run: |
SERVICE_NAME=${{ matrix.service }}
IMAGE_NAME=${{ secrets.DOCKER_USERNAME }}/doj-${SERVICE_NAME}
# Build the Docker image using the project root as context
docker build -t ${IMAGE_NAME}:latest -f DOJ-BE/${SERVICE_NAME}/Dockerfile .
# Push the Docker image
docker push ${IMAGE_NAME}:latest
# deploy:
# needs: build-and-push
# runs-on: ubuntu-latest
# steps:
# - name: Deploy to server
# uses: appleboy/ssh-action@master
# with:
# host: ${{ secrets.SERVER_HOST }}
# username: ${{ secrets.SERVER_USERNAME }}
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# script: |
# cd /path/to/your/project/D-OnlineJudge
# git pull
# docker-compose pull
# docker-compose up -d --force-recreate
================================================
FILE: .gitignore
================================================
.idea/
*.iml
.DS_Store
**/.DS_Store
.vscode/
/target/
/DOJ-BE/target/
/DOJ-BE/.idea/
/DOJ-FE/node_modules/
/DOJ-FE/dist/
# Ignore local data and static files
redis/
nacos/
mysql/
static/
elasticsearch/
GEMINI.md
skywalking-agent/
.secrets
================================================
FILE: DOJ-BE/.gitignore
================================================
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
./logs/**/*
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
================================================
FILE: DOJ-BE/common/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>common</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>
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<!--nacos配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/annotation/AdminRequired.java
================================================
package com.decade.doj.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,用于标记需要管理员权限的接口
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminRequired {
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/ProblemClient.java
================================================
package com.decade.doj.common.client;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.po.Problem;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("problem-service")
public interface ProblemClient {
@GetMapping("/problem/{id}")
R<Problem> getProblemById(@PathVariable Long id);
@GetMapping("/problem/count")
R<Long> getCount();
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/SubmissionClient.java
================================================
package com.decade.doj.common.client;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.po.Submission;
import com.decade.doj.common.domain.vo.SubmissionStatsVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient("submission-service")
public interface SubmissionClient {
@PostMapping("/submission/submit")
R<Long> submit(@RequestBody Submission submission);
@GetMapping("/submission/stats")
R<SubmissionStatsVO> getStats();
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/UserClient.java
================================================
package com.decade.doj.common.client;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.vo.InfoVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import javax.validation.constraints.NotNull;
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/{id}")
R<InfoVO> getUser(@PathVariable("id") @NotNull Long id);
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/DefaultFeignConfig.java
================================================
package com.decade.doj.common.config.custom;
import com.decade.doj.common.config.properties.JwtProperties;
import feign.Logger;
import feign.RequestInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import com.decade.doj.common.utils.UserContext;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
@RequiredArgsConstructor
public class DefaultFeignConfig {
private final JwtProperties jwtProperties;
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return template -> {
// 获取登录用户
Long userId = UserContext.getCurrentUser();
if(userId == null) {
// 如果为空则直接跳过
return;
}
// 如果不为空则放入请求头中,传递给下游微服务
template.header(jwtProperties.getSecretKey(), userId.toString());
};
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/JwtTool.java
================================================
package com.decade.doj.common.config.custom;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.decade.doj.common.exception.UnauthorizedException;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import java.security.KeyPair;
import java.time.Duration;
import java.util.Date;
@Component
@Import(SecurityConfig.class)
public class JwtTool {
private final JWTSigner jwtSigner;
public JwtTool(KeyPair keyPair) {
this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
}
public String createToken(Long userId, Duration ttl) {
return JWT.create()
.setPayload("user", userId)
.setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
.setSigner(jwtSigner)
.sign();
}
public Long parseToken(String token) {
// 1.校验token是否为空
if (token == null) {
throw new UnauthorizedException("未登录");
}
// 2.校验并解析jwt
JWT jwt;
try {
jwt = JWT.of(token).setSigner(jwtSigner);
} catch (Exception e) {
throw new UnauthorizedException("无效的token", e);
}
// 2.校验jwt是否有效
if (!jwt.verify()) {
// 验证失败
throw new UnauthorizedException("无效的token");
}
// 3.校验是否过期
try {
JWTValidator.of(jwt).validateDate();
} catch (ValidateException e) {
throw new UnauthorizedException("token已经过期");
}
// 4.数据格式校验
Object userPayload = jwt.getPayload("user");
if (userPayload == null) {
// 数据为空
throw new UnauthorizedException("无效的token");
}
// 5.数据解析
try {
return Long.valueOf(userPayload.toString());
} catch (RuntimeException e) {
// 数据格式有误
throw new UnauthorizedException("无效的token");
}
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MVCConfig.java
================================================
package com.decade.doj.common.config.custom;
import com.decade.doj.common.config.properties.ResourceProperties;
import com.decade.doj.common.interceptor.AdminCheckInterceptor;
import com.decade.doj.common.interceptor.IdentityInterceptor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
@EnableConfigurationProperties(ResourceProperties.class)
@Slf4j
@RequiredArgsConstructor
public class MVCConfig implements WebMvcConfigurer {
private final ResourceProperties resourceProperties;
private final IdentityInterceptor identityInterceptor;
private final AdminCheckInterceptor adminCheckInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String request = resourceProperties.getRequest() + "**";
String location = "file:" + resourceProperties.getLocation();
log.info("request: {}, location: {}", request, location);
registry.addResourceHandler(request).addResourceLocations(location);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 身份识别拦截器,必须先执行
registry.addInterceptor(identityInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html/**", "/actuator/**");
// 管理员权限拦截器,后执行
registry.addInterceptor(adminCheckInterceptor)
.addPathPatterns("/**");
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MybatisConfig.java
================================================
package com.decade.doj.common.config.custom;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(BaseMapper.class)
public class MybatisConfig {
@Bean
@ConditionalOnMissingBean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/SecurityConfig.java
================================================
package com.decade.doj.common.config.custom;
import com.decade.doj.common.config.properties.JwtProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.PrivateKey;
import java.security.PublicKey;
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class SecurityConfig {
@Bean
public KeyPair keyPair(JwtProperties properties) throws Exception {
// 加载密钥库
KeyStore keyStore = KeyStore.getInstance("JKS");
try (InputStream inputStream = properties.getLocation().getInputStream()) {
keyStore.load(inputStream, properties.getPassword().toCharArray());
}
// 读取私钥和证书
PrivateKey privateKey = (PrivateKey) keyStore.getKey(properties.getAlias(), properties.getPassword().toCharArray());
Certificate certificate = keyStore.getCertificate(properties.getAlias());
PublicKey publicKey = certificate.getPublicKey();
return new KeyPair(publicKey, privateKey);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/global/CommonExceptionConfig.java
================================================
package com.decade.doj.common.config.global;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.exception.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.NestedServletException;
import java.net.BindException;
import java.util.stream.Collectors;
@RestControllerAdvice
@Slf4j
public class CommonExceptionConfig {
@ExceptionHandler(DbException.class)
public Object handleDbException(DbException e) {
log.error("mysql数据库操作异常 -> ", e);
return processResponse(e);
}
@ExceptionHandler(CommonException.class)
public Object handleBadRequestException(CommonException e) {
log.error("自定义异常 -> {} , 异常原因:{} ",e.getClass().getName(), e.getMessage());
log.debug("", e);
return processResponse(e);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getAllErrors()
.stream().map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("|"));
log.error("请求参数校验异常 -> {}", msg);
log.debug("", e);
return processResponse(new BadRequestException(msg));
}
@ExceptionHandler(BindException.class)
public Object handleBindException(BindException e) {
log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
log.debug("", e);
return processResponse(new BadRequestException("请求参数格式错误"));
}
@ExceptionHandler(Exception.class)
public Object handleRuntimeException(Exception e) {
log.error("其他异常 uri : {} {}", e.getMessage(), e.getStackTrace());
return processResponse(new CommonException(e.getMessage(), 500));
}
@ExceptionHandler(UnauthorizedException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public R<Void> handleUnauthorizedException(UnauthorizedException e) {
log.error("<UNK> uri : {} {}", e.getMessage(), e.getStackTrace());
return R.error(401, e.getMessage());
}
@ExceptionHandler(ForbiddenException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public R<Void> handleForbiddenException(ForbiddenException e) {
log.error("403 uri : {} {}", e.getMessage(), e.getStackTrace());
return R.error(403, e.getMessage());
}
private ResponseEntity<R<Void>> processResponse(CommonException e){
return ResponseEntity.status(e.getCode()).body(R.error(e));
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/AppNameProperties.java
================================================
package com.decade.doj.common.config.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class AppNameProperties {
@Value("${spring.application.name}")
private String name;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/JwtProperties.java
================================================
package com.decade.doj.common.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import java.time.Duration;
@Data
@ConfigurationProperties(prefix = "doj.jwt")
public class JwtProperties {
private Resource location;
private String password;
private String alias;
private Duration tokenTTL = Duration.ofMinutes(10);
private Duration refreshTokenTTL = Duration.ofDays(7);
private String authorization;
private String secretKey;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/ResourceProperties.java
================================================
package com.decade.doj.common.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import java.time.Duration;
@Data
@ConfigurationProperties(prefix = "doj.resource")
public class ResourceProperties {
private String request;
private String location;
private String codePath;
private String problemCodePath;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/thread/ThreadPoolConfig.java
================================================
package com.decade.doj.common.config.thread;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置类
* <p>
* 支持通过 application.yml 配置参数:
* <pre>
* doj:
* thread-pool:
* run-code-core-size: 10
* run-code-max-size: 20
* run-code-queue-capacity: 50
* judging-core-size: 4
* judging-max-size: 8
* judging-queue-capacity: 100
* </pre>
*/
@Configuration
@EnableAsync
@ConfigurationProperties(prefix = "doj.thread-pool")
@Data
@Slf4j
public class ThreadPoolConfig {
// RunCodeThreadPool 配置
private int runCodeCoreSize = 10;
private int runCodeMaxSize = 20;
private int runCodeQueueCapacity = 50;
// JudgingThreadPool 配置
private int judgingCoreSize = 4;
private int judgingMaxSize = 8;
private int judgingQueueCapacity = 100;
/**
* 代码运行线程池
* 用于执行 /code 和 /problem 端点的代码运行任务
*/
@Bean("RunCodeThreadPool")
public ThreadPoolTaskExecutor runCodeThreadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(runCodeCoreSize);
executor.setMaxPoolSize(runCodeMaxSize);
executor.setQueueCapacity(runCodeQueueCapacity);
executor.setThreadNamePrefix("RunCodeThread-");
// 拒绝策略:调用者线程执行,防止任务丢失
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 优雅关闭:等待任务完成
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
log.info("RunCodeThreadPool initialized: coreSize={}, maxSize={}, queueCapacity={}",
runCodeCoreSize, runCodeMaxSize, runCodeQueueCapacity);
return executor;
}
/**
* 判题任务线程池
* 用于执行从 Redis 队列消费的判题任务
*/
@Bean("JudgingThreadPool")
public ThreadPoolTaskExecutor judgingThreadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(judgingCoreSize);
executor.setMaxPoolSize(judgingMaxSize);
executor.setQueueCapacity(judgingQueueCapacity);
executor.setThreadNamePrefix("JudgingThread-");
// 拒绝策略:调用者线程执行,确保任务不丢失
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 优雅关闭:判题任务可能较长,给予更多等待时间
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(120);
executor.initialize();
log.info("JudgingThreadPool initialized: coreSize={}, maxSize={}, queueCapacity={}",
judgingCoreSize, judgingMaxSize, judgingQueueCapacity);
return executor;
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageDTO.java
================================================
package com.decade.doj.common.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDTO<T> {
protected Long total;
protected Long pages;
protected List<T> list;
public static <T> PageDTO<T> empty(Long total, Long pages) {
return new PageDTO<>(total, pages, Collections.emptyList());
}
public static <T> PageDTO<T> fullPage(Long total, Long pages, List<T> list) {
return new PageDTO<>(total, pages, list);
}
public static <T> PageDTO<T> empty(Page<?> page) {
return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());
}
public static <T> PageDTO<T> of(Page<T> page) {
if(page == null){
return new PageDTO<>();
}
if (CollUtil.isEmpty(page.getRecords())) {
return empty(page);
}
return new PageDTO<>(page.getTotal(), page.getPages(), page.getRecords());
}
public static <T,R> PageDTO<T> of(Page<R> page, Function<R, T> mapper) {
if(page == null){
return new PageDTO<>();
}
if (CollUtil.isEmpty(page.getRecords())) {
return empty(page);
}
return new PageDTO<>(page.getTotal(), page.getPages(),
page.getRecords().stream().map(mapper).collect(Collectors.toList()));
}
public static <T> PageDTO<T> of(Page<?> page, List<T> list) {
return new PageDTO<>(page.getTotal(), page.getPages(), list);
}
public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz) {
return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtil.copyToList(page.getRecords(), clazz));
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageQueryDTO.java
================================================
package com.decade.doj.common.domain;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
@Data
@Schema(description = "分页查询条件")
@Accessors(chain = true)
public class PageQueryDTO {
public static final Integer DEFAULT_PAGE_SIZE = 20;
public static final Integer DEFAULT_PAGE_NUM = 1;
@Schema(description = "页码")
@Min(value = 1, message = "页码不能小于1")
private Integer pageNo = DEFAULT_PAGE_NUM;
@Schema(description = "每页数量")
@Min(value = 1, message = "每页查询数量不能小于1")
private Integer pageSize = DEFAULT_PAGE_SIZE;
@Schema(description = "是否升序")
private Boolean isAsc = true;
@Schema(description = "排序字段")
private String sortBy;
public int from() {
return (pageNo - 1) * pageSize;
}
public <T> Page<T> toMpPage(OrderItem... orderItems) {
Page<T> page = new Page<>(pageNo, pageSize);
// 是否手动指定排序方式
if (orderItems != null && orderItems.length > 0) {
for (OrderItem orderItem : orderItems) {
page.addOrder(orderItem);
}
return page;
}
// 前端是否有排序字段
if (StrUtil.isNotEmpty(sortBy)) {
OrderItem orderItem = new OrderItem();
orderItem.setAsc(isAsc);
orderItem.setColumn(sortBy);
page.addOrder(orderItem);
}
return page;
}
public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
if (StringUtils.isBlank(sortBy)) {
sortBy = defaultSortBy;
this.isAsc = isAsc;
}
Page<T> page = new Page<>(pageNo, pageSize);
OrderItem orderItem = new OrderItem();
orderItem.setAsc(this.isAsc);
orderItem.setColumn(sortBy);
page.addOrder(orderItem);
return page;
}
public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
return toMpPage("create_time", false);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/R.java
================================================
package com.decade.doj.common.domain;
import com.decade.doj.common.exception.CommonException;
import lombok.Data;
@Data
public class R<T> {
private int code;
private String msg;
private T data;
public static R<Void> ok() {
return ok(null);
}
public static <T> R<T> ok(T data) {
return new R<>(200, "OK", data);
}
public static <T> R<T> error(String msg) {
return new R<>(500, msg, null);
}
public static <T> R<T> error(int code, String msg) {
return new R<>(code, msg, null);
}
public static <T> R<T> error(CommonException e) {
return new R<>(e.getCode(), e.getMessage(), null);
}
public R() {
}
public R(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public boolean success(){
return code == 200;
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/json/StringListDeserializer.java
================================================
package com.decade.doj.common.domain.json;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class StringListDeserializer extends JsonDeserializer<List<String>> {
@Override
public List<String> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonToken token = parser.currentToken();
if (token == JsonToken.START_ARRAY) {
List<String> values = new ArrayList<>();
while (parser.nextToken() != JsonToken.END_ARRAY) {
values.add(parser.getValueAsString());
}
return values;
}
if (token == JsonToken.VALUE_STRING) {
String value = parser.getValueAsString();
if (value == null || value.isBlank()) {
return List.of();
}
return List.of(value);
}
return context.readValue(parser, context.getTypeFactory().constructCollectionType(List.class, String.class));
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Problem.java
================================================
package com.decade.doj.common.domain.po;
import com.decade.doj.common.domain.json.StringListDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author
* @since 2024-09-17
*/
@Data
@Accessors(chain = true)
@Schema(description="Problem对象")
public class Problem {
private Long id;
private String name;
private String description;
private String inputStyle;
private String outputStyle;
@JsonDeserialize(using = StringListDeserializer.class)
private List<String> inputSample;
@JsonDeserialize(using = StringListDeserializer.class)
private List<String> outputSample;
private String hint;
private String difficulty;
private Integer timeLimit;
private Integer memoryLimit;
private Integer totalPass;
private Integer totalAttempt;
private List<String> tags;
private String testData;
private String testAns;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Submission.java
================================================
package com.decade.doj.common.domain.po;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
public class Submission {
private Long id;
private Long userId;
private String userName;
private Long problemId;
private String problemName;
private String language;
private String code;
private Integer exitValue;
private String status;
private String message;
private Double time;
private Long memory;
private Date submitTime;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/User.java
================================================
package com.decade.doj.common.domain.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author
* @since 2024-08-26
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
private String avatar = "";
private String email;
private String password;
private Integer score = 0;
private Integer ranks = 0;
private String school = "";
private Boolean gender = true;
private Integer easySolve = 0;
private Integer middleSolve = 0;
private Integer hardSolve = 0;
private Boolean role = true;
private String url = "";
private String sign = "这个人很懒,什么都没留下";
private Long fans = 0L;
private Long subscribe = 0L;
private Boolean ban = false;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/ExecuteMessage.java
================================================
package com.decade.doj.common.domain.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Data
@Accessors(chain = true)
public class ExecuteMessage {
private Integer exitValue;
private String status;
private String message;
private double time;
private Long memory;
private static final Map<Integer, String> exitStatusMap = new HashMap<>();
private static final Set<Integer> InfoStatus = new HashSet<>();
static {
exitStatusMap.put(0, "Finished");
exitStatusMap.put(1, "Runtime Error");
exitStatusMap.put(2, "Compile Error");
exitStatusMap.put(124, "Time Limit Exceeded");
exitStatusMap.put(137, "Memory Limit Exceeded");
exitStatusMap.put(10, "Accepted");
exitStatusMap.put(11, "Wrong Answer");
InfoStatus.add(0);
InfoStatus.add(1);
InfoStatus.add(2);
InfoStatus.add(10);
InfoStatus.add(11);
}
public static String getStatus(Integer exitValue) {
if (exitValue == null) {
return "Unknown Error";
}
return exitStatusMap.getOrDefault(exitValue, "Unknown exitValue");
}
public static boolean show(Integer exitValue) {
return InfoStatus.contains(exitValue);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/InfoVO.java
================================================
package com.decade.doj.common.domain.vo;
import com.decade.doj.common.domain.po.User;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class InfoVO extends User {
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/StatsVO.java
================================================
package com.decade.doj.common.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 首页统计数据 VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StatsVO {
/**
* 累计提交数
*/
private Long totalSubmissions;
/**
* 今日提交数
*/
private Long todaySubmissions;
/**
* 题目总数
*/
private Long totalProblems;
/**
* 活跃用户数(有提交记录的用户)
*/
private Long activeUsers;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/SubmissionStatsVO.java
================================================
package com.decade.doj.common.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 提交统计 VO
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SubmissionStatsVO {
/**
* 累计提交数
*/
private Long totalSubmissions;
/**
* 今日提交数
*/
private Long todaySubmissions;
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BadRequestException.java
================================================
package com.decade.doj.common.exception;
public class BadRequestException extends CommonException{
public BadRequestException(String message) {
super(message, 400);
}
public BadRequestException(String message, Throwable cause) {
super(message, cause, 400);
}
public BadRequestException(Throwable cause) {
super(cause, 400);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BizIllegalException.java
================================================
package com.decade.doj.common.exception;
public class BizIllegalException extends CommonException{
public BizIllegalException(String message) {
super(message, 500);
}
public BizIllegalException(String message, Throwable cause) {
super(message, cause, 500);
}
public BizIllegalException(Throwable cause) {
super(cause, 500);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/CommonException.java
================================================
package com.decade.doj.common.exception;
import lombok.Getter;
@Getter
public class CommonException extends RuntimeException{
private int code;
public CommonException(String message, int code) {
super(message);
this.code = code;
}
public CommonException(String message, Throwable cause, int code) {
super(message, cause);
this.code = code;
}
public CommonException(Throwable cause, int code) {
super(cause);
this.code = code;
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/DbException.java
================================================
package com.decade.doj.common.exception;
public class DbException extends CommonException{
public DbException(String message) {
super(message, 500);
}
public DbException(String message, Throwable cause) {
super(message, cause, 500);
}
public DbException(Throwable cause) {
super(cause, 500);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/ForbiddenException.java
================================================
package com.decade.doj.common.exception;
public class ForbiddenException extends CommonException{
public ForbiddenException(String message) {
super(message, 403);
}
public ForbiddenException(String message, Throwable cause) {
super(message, cause, 403);
}
public ForbiddenException(Throwable cause) {
super(cause, 403);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/UnauthorizedException.java
================================================
package com.decade.doj.common.exception;
public class UnauthorizedException extends CommonException{
public UnauthorizedException(String message) {
super(message, 401);
}
public UnauthorizedException(String message, Throwable cause) {
super(message, cause, 401);
}
public UnauthorizedException(Throwable cause) {
super(cause, 401);
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/AdminCheckInterceptor.java
================================================
package com.decade.doj.common.interceptor;
import com.decade.doj.common.annotation.AdminRequired;
import com.decade.doj.common.client.UserClient;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.vo.InfoVO;
import com.decade.doj.common.exception.ForbiddenException;
import com.decade.doj.common.exception.UnauthorizedException;
import com.decade.doj.common.utils.UserContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class AdminCheckInterceptor implements HandlerInterceptor, ApplicationContextAware {
private ApplicationContext applicationContext;
private UserClient userClient;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true;
}
AdminRequired adminRequired = handlerMethod.getMethodAnnotation(AdminRequired.class);
if (adminRequired == null) {
return true;
}
Long userId = UserContext.getCurrentUser();
if (userId == null) {
throw new UnauthorizedException("用户未登录");
}
// Lazily get the UserClient bean from the context
if (userClient == null) {
this.userClient = applicationContext.getBean(UserClient.class);
}
R<InfoVO> userResult = userClient.getUser(userId);
if (!userResult.success() || userResult.getData() == null) {
throw new ForbiddenException("无法获取用户信息");
}
InfoVO userInfo = userResult.getData();
if (userInfo.getRole()) {
throw new ForbiddenException("无权访问,需要管理员权限");
}
return true;
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/IdentityInterceptor.java
================================================
package com.decade.doj.common.interceptor;
import cn.hutool.core.util.StrUtil;
import com.decade.doj.common.config.properties.JwtProperties;
import com.decade.doj.common.exception.UnauthorizedException;
import com.decade.doj.common.utils.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
@EnableConfigurationProperties(JwtProperties.class)
@RequiredArgsConstructor
public class IdentityInterceptor implements HandlerInterceptor {
private final JwtProperties jwtProperties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("path: {}", request.getRequestURI());
String userId = request.getHeader(jwtProperties.getSecretKey());
if (userId == null) {
// throw new UnauthorizedException("请访问网关服务!");
userId = String.valueOf(3);
}
log.debug("当前用户ID: {}", userId);
if (StrUtil.isNotBlank(userId)) {
UserContext.setCurrentUser(Long.parseLong(userId));
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserContext.clear();
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/utils/LocalResource.java
================================================
package com.decade.doj.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import java.io.File;
public class LocalResource {
public static String getLocalFilePath(String resourcePath) {
try {
ClassPathResource resource = new ClassPathResource(resourcePath);
File file = resource.getFile(); // 获取文件对象
return file.getAbsolutePath().replace("\\", "/"); // 返回文件的绝对路径
} catch (Exception e) {
System.out.println("获取文件失败"+e);
return null;
}
}
}
================================================
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/utils/UserContext.java
================================================
package com.decade.doj.common.utils;
public class UserContext {
private static final ThreadLocal<Long> userHolder = new ThreadLocal<>();
public static Long getCurrentUser() {
return userHolder.get();
}
public static void setCurrentUser(Long id) {
userHolder.set(id);
}
public static void clear() {
userHolder.remove();
}
}
================================================
FILE: DOJ-BE/common/src/main/resources/META-INF/spring-configuration-metadata.json
================================================
{
"groups": [
{
"name": "doj.db"
},
{
"name": "doj.nacos"
},
{
"name": "doj.swagger"
}
],
"properties": [
{
"name": "doj.db.host",
"type": "java.lang.String",
"description": "rabbitmq的地址",
"defaultValue": "192.168.150.101"
}
],
"hints": []
}
================================================
FILE: DOJ-BE/common/src/main/resources/META-INF/spring.factories
================================================
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.decade.doj.common.config.global.CommonExceptionConfig
================================================
FILE: DOJ-BE/common/src/main/resources/application-common.yaml
================================================
doj:
port:
gateway-service: ${DOJ_PORT_GATEWAY:8080}
user-service: ${DOJ_PORT_USER:8081}
sandbox-service: ${DOJ_PORT_SANDBOX:8082}
problem-service: ${DOJ_PORT_PROBLEM:8083}
submission-service: ${DOJ_PORT_SUBMISSION:8084}
resource:
location: ${DOJ_RESOURCE_LOCATION:/Users/qzj/Desktop/Development/D-OnlineJudge/static/files/}
request: ${DOJ_RESOURCE_REQUEST:/static/}
code-path: ${DOJ_CODE_PATH:/Users/qzj/Desktop/Development/D-OnlineJudge/static/codes/}
problem-code-path: ${DOJ_PROBLEM_CODE_PATH:/Users/qzj/Desktop/Development/D-OnlineJudge/static/problem-codes/}
================================================
FILE: DOJ-BE/common/src/test/java/com/decade/doj/common/domain/po/ProblemJsonTest.java
================================================
package com.decade.doj.common.domain.po;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ProblemJsonTest {
@Test
void shouldDeserializeSampleArrays() throws Exception {
String json = "{\"id\":1,\"name\":\"A+B\",\"inputSample\":[\"1 2\"],\"outputSample\":[\"3\"]}";
ObjectMapper mapper = new ObjectMapper();
Problem problem = mapper.readValue(json, Problem.class);
assertEquals(List.of("1 2"), problem.getInputSample());
assertEquals(List.of("3"), problem.getOutputSample());
}
}
================================================
FILE: DOJ-BE/gateway-service/Dockerfile
================================================
# Stage 1: Build the application
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
# Copy all pom.xml files first to leverage Docker cache
COPY DOJ-BE/pom.xml .
COPY DOJ-BE/common/pom.xml ./common/
COPY DOJ-BE/gateway-service/pom.xml ./gateway-service/
COPY DOJ-BE/problem-service/pom.xml ./problem-service/
COPY DOJ-BE/sandbox-service/pom.xml ./sandbox-service/
COPY DOJ-BE/submission-service/pom.xml ./submission-service/
COPY DOJ-BE/user-service/pom.xml ./user-service/
# Download all dependencies
RUN mvn dependency:go-offline
# Copy all source code
COPY DOJ-BE/common/src ./common/src
COPY DOJ-BE/gateway-service/src ./gateway-service/src
# Build the specific service
RUN mvn -f ./pom.xml -pl gateway-service -am clean package -DskipTests
RUN ls -al ./gateway-service/target
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre
WORKDIR /app
# Copy the executable jar from the build stage
COPY --from=build /app/gateway-service/target/gateway-service.jar ./app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "./app.jar"]
================================================
FILE: DOJ-BE/gateway-service/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>gateway-service</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>
<!--common-->
<dependency>
<groupId>com.decade</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel持久化到nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/GatewayApplication.java
================================================
package com.decade.doj.gateway;
import com.decade.doj.common.config.custom.JwtTool;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import({JwtTool.class})
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
================================================
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/config/properties/AuthProperties.java
================================================
package com.decade.doj.gateway.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Data
@ConfigurationProperties(prefix = "doj.auth")
public class AuthProperties {
private List<String> includePaths;
private List<String> excludePaths;
}
================================================
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/AuthGlobalFilter.java
================================================
package com.decade.doj.gateway.filters;
import cn.hutool.core.text.AntPathMatcher;
import com.decade.doj.common.config.properties.JwtProperties;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.exception.UnauthorizedException;
import com.decade.doj.common.config.custom.JwtTool;
import com.decade.doj.gateway.config.properties.AuthProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Component
@RequiredArgsConstructor
@EnableConfigurationProperties({AuthProperties.class, JwtProperties.class})
@Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final JwtTool jwtTool;
private final AuthProperties authProperties;
private final JwtProperties jwtProperties;
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(jwtProperties.getSecretKey());
String path = exchange.getRequest().getURI().getPath();
if (isExcludedPath(path)) {
ServerWebExchange build = exchange.mutate()
.request(builder -> builder.header(jwtProperties.getSecretKey(), String.valueOf(0)))
.build();
return chain.filter(build);
}
log.info("path:{}", path);
log.info("headers:{}", exchange.getRequest().getHeaders().entrySet());
String token = exchange.getRequest().getHeaders().getFirst(jwtProperties.getAuthorization());
Long userId;
try {
userId = jwtTool.parseToken(token);
} catch (UnauthorizedException e) {
return handleUnauthorizedResponse(exchange);
}
// 校验成功,将userId放入请求头
ServerWebExchange build = exchange.mutate()
.request(builder -> builder.header(jwtProperties.getSecretKey(), String.valueOf(userId)))
.build();
return chain.filter(build);
}
public Mono<Void> handleUnauthorizedResponse(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
R<String> response = R.error(401, "登陆过期,请重新登陆!");
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
try {
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = objectMapper.writeValueAsBytes(response);
DataBuffer dataBuffer = bufferFactory.wrap(bytes);
return exchange.getResponse().writeWith(Mono.just(dataBuffer));
} catch (Exception e) {
return exchange.getResponse().setComplete();
}
}
private boolean isExcludedPath(String path) {
for (String ignorePath : authProperties.getExcludePaths()) {
if (antPathMatcher.match(ignorePath, path)) {
return true;
}
}
return false;
}
@Override
public int getOrder() {
return 0;
}
}
================================================
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/CrossFilter.java
================================================
package com.decade.doj.gateway.filters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration
public class CrossFilter {
/**
* 添加跨域过滤器
* @return
*/
@Bean
public CorsWebFilter corsWebFilter(){
//基于url跨域,选择reactive包下的
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 跨域配置信息
CorsConfiguration configuration = new CorsConfiguration();
// 允许跨域的头
configuration.addAllowedHeader("*");
// 允许跨域的请求方式
configuration.addAllowedMethod("*");
// 允许跨域的请求来源
configuration.addAllowedOrigin("http://localhost:5174");
configuration.addAllowedOrigin("http://localhost:5173");
configuration.addAllowedOrigin("http://localhost:8088");
// 预检请求的有效期,单位为秒
configuration.setMaxAge(3600L);
// 是否允许携带cookie跨域
configuration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**", configuration);
return new CorsWebFilter(source);
}
}
================================================
FILE: DOJ-BE/gateway-service/src/main/resources/application.yaml
================================================
server:
port: ${doj.port.gateway-service}
management:
endpoints:
web:
exposure:
include: health,prometheus
spring:
cloud:
sentinel:
transport:
dashboard: ${SENTINEL_DASHBOARD_ADDR:localhost:8858}
datasource:
ds1:
nacos:
server-addr: ${spring.cloud.nacos.server-addr}
data-id: gateway-sentinel-rules
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
degrade:
nacos:
server-addr: ${spring.cloud.nacos.server-addr}
data-id: gateway-sentinel-degrade-rules
group-id: DEFAULT_GROUP
data-type: json
rule-type: degrade
doj:
auth:
excludePaths:
- /
- /static/**
- /user/login
- /user/register
- /user/refresh
================================================
FILE: DOJ-BE/gateway-service/src/main/resources/bootstrap.yaml
================================================
spring:
application:
name: gateway-service
profiles:
active: dev, common
cloud:
nacos:
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
config:
file-extension: yaml
shared-configs:
- dataId: shared-jwt.yaml
gateway:
routes:
- id: user
uri: lb://user-service
predicates:
- Path=/user/**,/static/**
- id: problem
uri: lb://problem-service
predicates:
- Path=/problem/**
- id: sandbox
uri: lb://sandbox-service
predicates:
- Path=/sandbox/**
- id: submission
uri: lb://submission-service
predicates:
- Path=/submission/**
================================================
FILE: DOJ-BE/pom.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>
<groupId>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>user-service</module>
<module>gateway-service</module>
<module>common</module>
<module>sandbox-service</module>
<module>problem-service</module>
<module>submission-service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
<org.projectlombok.version>1.18.32</org.projectlombok.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
<mysql.version>8.0.23</mysql.version>
<knife4j.version>4.5.0</knife4j.version>
<hutool.version>5.8.21</hutool.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.github.xiaoymin</groupId>-->
<!-- <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>-->
<!-- <version>4.1.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.58</version>
</dependency>
<!-- actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- micrometer-prometheus -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source> <!-- depending on your project -->
<target>17</target> <!-- depending on your project -->
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
================================================
FILE: DOJ-BE/problem-service/Dockerfile
================================================
# Stage 1: Build the application
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
# Copy all pom.xml files first to leverage Docker cache
COPY DOJ-BE/pom.xml .
COPY DOJ-BE/common/pom.xml ./common/
COPY DOJ-BE/gateway-service/pom.xml ./gateway-service/
COPY DOJ-BE/problem-service/pom.xml ./problem-service/
COPY DOJ-BE/sandbox-service/pom.xml ./sandbox-service/
COPY DOJ-BE/submission-service/pom.xml ./submission-service/
COPY DOJ-BE/user-service/pom.xml ./user-service/
# Download all dependencies
RUN mvn dependency:go-offline
# Copy all source code
COPY DOJ-BE/common/src ./common/src
COPY DOJ-BE/problem-service/src ./problem-service/src
# Build the specific service
RUN mvn -f ./pom.xml -pl problem-service -am clean package -DskipTests
RUN ls -al ./problem-service/target
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre
WORKDIR /app
# Copy the executable jar from the build stage
COPY --from=build /app/problem-service/target/problem-service.jar ./app.jar
EXPOSE 8083
ENTRYPOINT ["java", "-jar", "./app.jar"]
================================================
FILE: DOJ-BE/problem-service/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>problem-service</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>
<!--common-->
<dependency>
<groupId>com.decade</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/ProblemApplication.java
================================================
package com.decade.doj.problem;
import com.decade.doj.common.config.custom.DefaultFeignConfig;
import com.decade.doj.common.config.custom.JwtTool;
import com.decade.doj.common.config.custom.MVCConfig;
import com.decade.doj.common.config.custom.MybatisConfig;
import com.decade.doj.common.interceptor.AdminCheckInterceptor;
import com.decade.doj.common.interceptor.IdentityInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@MapperScan("com.decade.doj.problem.mapper")
@EnableCaching
@EnableFeignClients(basePackages = "com.decade.doj.common.client", defaultConfiguration = DefaultFeignConfig.class)
@Import({MVCConfig.class, MybatisConfig.class, IdentityInterceptor.class, AdminCheckInterceptor.class})
public class ProblemApplication {
public static void main(String[] args) {
SpringApplication.run(ProblemApplication.class, args);
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/CacheConfig.java
================================================
package com.decade.doj.problem.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.time.Duration;
@Configuration
public class CacheConfig {
// L1 本地缓存 (Caffeine)
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100) // 初始大小
.maximumSize(500) // 最大数量
.expireAfterWrite(Duration.ofMinutes(5))); // 5分钟后过期
return cacheManager;
}
// L2 分布式缓存 (Redis)
@Primary // 将 Redis 缓存作为默认和主要的缓存管理器
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // L2 缓存时间更长
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/ProblemIndexInitializer.java
================================================
package com.decade.doj.problem.config;
import com.decade.doj.problem.domain.document.ProblemDocument;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Slf4j
@Component
@RequiredArgsConstructor
public class ProblemIndexInitializer {
private final ElasticsearchRestTemplate elasticsearchTemplate;
@PostConstruct
public void initIndex() {
IndexOperations indexOps = elasticsearchTemplate.indexOps(ProblemDocument.class);
if (!indexOps.exists()) {
indexOps.create();
indexOps.putMapping(indexOps.createMapping());
log.info("Created Elasticsearch index: {}", indexOps.getIndexCoordinates().getIndexName());
}
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/controller/ProblemController.java
================================================
package com.decade.doj.problem.controller;
import com.decade.doj.common.annotation.AdminRequired;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.R;
import com.decade.doj.problem.domain.dto.ProblemPageQueryDTO;
import com.decade.doj.problem.domain.po.Problem;
import com.decade.doj.problem.service.IProblemService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author
* @since 2024-09-17
*/
@RestController
@RequestMapping("/problem")
@Tag(name = "题目相关接口")
@Slf4j
@RequiredArgsConstructor
public class ProblemController {
private final IProblemService problemService;
@PostMapping("/admin/sync-es")
@AdminRequired
@Operation(summary = "同步题目数据到elasticsearch")
public R<Integer> syncProblemToEs() {
Integer res = problemService.syncAllToElasticsearch();
return R.ok(res);
}
@PostMapping("/admin/reindex")
@AdminRequired
@Operation(summary = "重建索引并同步题目")
public R<Integer> reindexProblems() {
Integer res = problemService.reindexAll();
return R.ok(res);
}
@PostMapping("/admin/reset")
@AdminRequired
@Operation(summary = "清空 problem 数据与 ES 索引")
public R<Void> resetProblems() {
problemService.resetProblems();
return R.ok();
}
@PostMapping
@AdminRequired
@Operation(summary = "新增题目")
public R<Void> createProblem(@RequestBody Problem problem) {
problemService.save(problem);
return R.ok();
}
@PutMapping
@AdminRequired
@Operation(summary = "修改题目")
public R<Void> updateProblem(@RequestBody Problem problem) {
problemService.updateById(problem);
return R.ok();
}
@DeleteMapping("/{id}")
@AdminRequired
@Operation(summary = "删除题目")
public R<Void> deleteProblem(@PathVariable Long id) {
problemService.removeById(id);
return R.ok();
}
@GetMapping("/list")
@Operation(summary = "获取题目列表")
public R<List<Problem>> list() {
return R.ok(problemService.list());
}
@GetMapping("/count")
@Operation(summary = "获取题目总数")
public R<Long> getCount() {
return R.ok(problemService.count());
}
@GetMapping("/page")
@Operation(summary = "分页获取题目列表")
public R<PageDTO<Problem>> page(ProblemPageQueryDTO problemPageQueryDTO) {
PageDTO<Problem> res = problemService.pageQuery(problemPageQueryDTO);
return R.ok(res);
}
@GetMapping("/{id}")
@Operation(summary = "获取题目详情")
public R<Problem> getProblemById(@PathVariable Long id) {
return R.ok(problemService.getById(id));
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/document/ProblemDocument.java
================================================
package com.decade.doj.problem.domain.document;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "doj_problem")
@Setting(settingPath = "es/problem-settings.json")
public class ProblemDocument {
@Id
private Long id;
// 题目名称,全文检索(大小写不敏感)
@Field(type = FieldType.Text, analyzer = "ik_max_lowercase", searchAnalyzer = "ik_smart_lowercase")
private String name;
// 题目描述,全文检索
@Field(type = FieldType.Text, analyzer = "ik_max_lowercase", searchAnalyzer = "ik_smart_lowercase")
private String description;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemPageQueryDTO.java
================================================
package com.decade.doj.problem.domain.dto;
import com.decade.doj.common.domain.PageQueryDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class ProblemPageQueryDTO extends PageQueryDTO {
@Schema(description = "题目名称")
private String name;
@Schema(description = "题目描述")
private String description;
@Schema(description = "难度")
private String difficulty;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "状态: 已解决/尝试中/未开始")
private String status;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemTagView.java
================================================
package com.decade.doj.problem.domain.dto;
import lombok.Data;
@Data
public class ProblemTagView {
private Long problemId;
private String tagName;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Problem.java
================================================
package com.decade.doj.problem.domain.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import java.io.Serializable;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author
* @since 2024-09-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName(value = "problem", autoResultMap = true)
@Schema(description="Problem对象")
public class Problem implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("name")
private String name;
@TableField("description")
private String description;
@TableField("input_style")
private String inputStyle;
@TableField("output_style")
private String outputStyle;
@TableField(value = "input_sample", typeHandler = JacksonTypeHandler.class)
private java.util.List<String> inputSample;
@TableField(value = "output_sample", typeHandler = JacksonTypeHandler.class)
private java.util.List<String> outputSample;
@TableField("hint")
private String hint;
@TableField("difficulty")
private String difficulty;
@TableField("time_limit")
private Integer timeLimit;
@TableField("memory_limit")
private Integer memoryLimit;
@TableField("total_pass")
private Integer totalPass;
@TableField("total_attempt")
private Integer totalAttempt;
@TableField(exist = false)
private java.util.List<String> tags;
@TableField(exist = false)
private String status;
@TableField("test_data")
private String testData;
@TableField("test_ans")
private String testAns;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/ProblemTag.java
================================================
package com.decade.doj.problem.domain.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("problem_tag")
@Schema(description = "题目与标签关联对象")
public class ProblemTag implements Serializable {
private static final long serialVersionUID = 1L;
@TableField("problem_id")
private Long problemId;
@TableField("tag_id")
private Long tagId;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Tag.java
================================================
package com.decade.doj.problem.domain.po;
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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@TableName("tag")
@Schema(description = "标签对象")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("name")
private String name;
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemMapper.java
================================================
package com.decade.doj.problem.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.decade.doj.problem.domain.po.Problem;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author
* @since 2024-09-17
*/
public interface ProblemMapper extends BaseMapper<Problem> {
IPage<Problem> selectPageWithFilters(Page<Problem> page,
@Param("ids") List<Long> ids,
@Param("difficulty") String difficulty,
@Param("tagNames") List<String> tagNames,
@Param("status") String status,
@Param("userId") Long userId);
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemTagMapper.java
================================================
package com.decade.doj.problem.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.decade.doj.problem.domain.dto.ProblemTagView;
import com.decade.doj.problem.domain.po.ProblemTag;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ProblemTagMapper extends BaseMapper<ProblemTag> {
@Select({
"<script>",
"SELECT pt.problem_id AS problemId, t.name AS tagName",
"FROM problem_tag pt",
"JOIN tag t ON pt.tag_id = t.id",
"WHERE pt.problem_id IN",
"<foreach collection='problemIds' item='id' open='(' separator=',' close=')'>",
"#{id}",
"</foreach>",
"</script>"
})
List<ProblemTagView> selectTagsByProblemIds(@Param("problemIds") List<Long> problemIds);
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/TagMapper.java
================================================
package com.decade.doj.problem.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.decade.doj.problem.domain.po.Tag;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TagMapper extends BaseMapper<Tag> {
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/MqConfig.java
================================================
package com.decade.doj.problem.mq;
import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
// --- 用于题目统计更新的队列 ---
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("doj.topic");
}
@Bean
public Queue problemStatsUpdateQueue() {
return new Queue("problem.stats.update.queue");
}
@Bean
public Binding statsBinding() {
return BindingBuilder.bind(problemStatsUpdateQueue()).to(topicExchange()).with("submission.created");
}
// --- 用于多级缓存同步的广播队列 ---
@Bean
public FanoutExchange cacheUpdateExchange() {
return new FanoutExchange("cache.update.exchange");
}
@Bean
public Queue problemCacheUpdateQueue() {
// 匿名队列:每个消费者实例都有自己的、临时的、独占的队列
return new AnonymousQueue();
}
@Bean
public Binding cacheBinding() {
return BindingBuilder.bind(problemCacheUpdateQueue()).to(cacheUpdateExchange());
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/StatsUpdateListener.java
================================================
package com.decade.doj.problem.mq;
import com.decade.doj.problem.service.IProblemService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor
public class StatsUpdateListener {
private final IProblemService problemService;
@RabbitListener(queues = "problem.stats.update.queue")
public void listenStatsUpdateQueue(Map<String, Object> message) {
if (message == null) {
return;
}
Object problemIdObj = message.get("problemId");
Long problemId = null;
if (problemIdObj instanceof Integer) {
problemId = ((Integer) problemIdObj).longValue();
} else if (problemIdObj instanceof Long) {
problemId = (Long) problemIdObj;
}
Boolean isAccepted = (Boolean) message.get("isAccepted");
if (problemId == null || isAccepted == null) {
log.error("接收到无效的消息:{}", message);
return;
}
log.info("接收到题目提交消息,开始更新题目统计数据。problemId: {}, isAccepted: {}", problemId, isAccepted);
problemService.updateProblemStats(problemId, isAccepted);
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/repository/ProblemRepository.java
================================================
package com.decade.doj.problem.repository;
import com.decade.doj.problem.domain.document.ProblemDocument;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ProblemRepository extends ElasticsearchRepository<ProblemDocument, Long> {
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/IProblemService.java
================================================
package com.decade.doj.problem.service;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.PageQueryDTO;
import com.decade.doj.problem.domain.dto.ProblemPageQueryDTO;
import com.decade.doj.problem.domain.po.Problem;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author
* @since 2024-09-17
*/
public interface IProblemService extends IService<Problem> {
PageDTO<Problem> pageQuery(ProblemPageQueryDTO problemPageQueryDTO);
void updateProblemStats(Long problemId, boolean isAccepted);
int syncAllToElasticsearch();
int reindexAll();
void resetProblems();
}
================================================
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/impl/ProblemServiceImpl.java
================================================
package com.decade.doj.problem.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.problem.domain.document.ProblemDocument;
import com.decade.doj.common.utils.UserContext;
import com.decade.doj.problem.domain.dto.ProblemPageQueryDTO;
import com.decade.doj.problem.domain.dto.ProblemTagView;
import com.decade.doj.problem.domain.po.Problem;
import com.decade.doj.problem.domain.po.ProblemTag;
import com.decade.doj.problem.domain.po.Tag;
import com.decade.doj.problem.mapper.ProblemMapper;
import com.decade.doj.problem.mapper.ProblemTagMapper;
import com.decade.doj.problem.mapper.TagMapper;
import com.decade.doj.problem.repository.ProblemRepository;
import com.decade.doj.problem.service.IProblemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@Slf4j
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> implements IProblemService {
private static final int MAX_ES_RESULTS = 10000;
private final CacheManager redisCacheManager;
private final CacheManager caffeineCacheManager;
private final RabbitTemplate rabbitTemplate;
private final ProblemRepository problemRepository;
private final ElasticsearchRestTemplate elasticsearchTemplate;
private final TagMapper tagMapper;
private final ProblemTagMapper problemTagMapper;
public ProblemServiceImpl(@Qualifier("redisCacheManager") CacheManager redisCacheManager,
@Qualifier("caffeineCacheManager") CacheManager caffeineCacheManager,
RabbitTemplate rabbitTemplate,
ProblemRepository problemRepository,
ElasticsearchRestTemplate elasticsearchTemplate,
TagMapper tagMapper,
ProblemTagMapper problemTagMapper) {
this.redisCacheManager = redisCacheManager;
this.caffeineCacheManager = caffeineCacheManager;
this.rabbitTemplate = rabbitTemplate;
this.problemRepository = problemRepository;
this.elasticsearchTemplate = elasticsearchTemplate;
this.tagMapper = tagMapper;
this.problemTagMapper = problemTagMapper;
}
@Override
public Problem getById(Serializable id) {
// 1. 查 L1 本地缓存
Cache caffeineCache = caffeineCacheManager.getCache("problem");
if (caffeineCache != null && caffeineCache.get(id) != null) {
log.info("L1 缓存命中, id: {}", id);
return caffeineCache.get(id, Problem.class);
}
// 2. 查 L2 Redis 缓存
Cache redisCache = redisCacheManager.getCache("problem");
if (redisCache != null && redisCache.get(id) != null) {
log.info("L2 缓存命中, id: {}", id);
Problem problem = redisCache.get(id, Problem.class);
// 将数据写回 L1
if (caffeineCache != null && problem != null) {
caffeineCache.put(id, problem);
}
return problem;
}
// 3. 查 L3 数据库
log.info("缓存未命中,从数据库查询, id: {}", id);
Problem problem = super.getById(id);
if (problem != null) {
attachTags(List.of(problem));
}
// 4. 依次写回 L2 和 L1
if (problem != null) {
if (redisCache != null) {
redisCache.put(id, problem);
}
if (caffeineCache != null) {
caffeineCache.put(id, problem);
}
}
return problem;
}
@Override
public boolean save(Problem entity) {
// 1. 保存到数据库
boolean success = super.save(entity);
if (success) {
// 2. 处理标签
syncTags(entity.getId(), entity.getTags());
// 3. 同步到 Elasticsearch
syncToElasticsearch(entity);
}
return success;
}
@Override
public boolean updateById(Problem entity) {
// 1. 更新数据库
boolean success = super.updateById(entity);
if (success) {
// 2. 同步标签
syncTags(entity.getId(), entity.getTags());
// 3. 清理缓存
clearCache(entity.getId());
// 4. 同步到 Elasticsearch
syncToElasticsearch(entity);
}
return success;
}
@Override
public boolean removeById(Serializable id) {
// 1. 删除数据库
boolean success = super.removeById(id);
if (success) {
// 2. 删除标签关联
problemTagMapper.delete(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ProblemTag>()
.eq(ProblemTag::getProblemId, id));
// 3. 清理缓存
clearCache(id);
// 4. 从 Elasticsearch 删除
deleteFromElasticsearch((Long) id);
}
return success;
}
/**
* 同步标签到数据库
*/
private void syncTags(Long problemId, List<String> tags) {
// 1. 先删除原有关联
problemTagMapper.delete(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ProblemTag>()
.eq(ProblemTag::getProblemId, problemId));
if (tags == null || tags.isEmpty()) {
return;
}
Set<String> normalizedTags = new LinkedHashSet<>();
for (String tagName : tags) {
if (!StringUtils.hasText(tagName)) {
continue;
}
normalizedTags.add(tagName.trim());
}
// 2. 处理每个标签
for (String tagName : normalizedTags) {
// 查找标签,不存在则创建
Tag tag = tagMapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Tag>()
.eq(Tag::getName, tagName));
if (tag == null) {
tag = new Tag().setName(tagName);
tagMapper.insert(tag);
}
// 创建关联
problemTagMapper.insert(new ProblemTag(problemId, tag.getId()));
}
}
/**
* 同步数据到 Elasticsearch
*/
private void syncToElasticsearch(Problem problem) {
try {
ProblemDocument document = new ProblemDocument();
BeanUtils.copyProperties(problem, document);
problemRepository.save(document);
log.info("同步到 ES 成功, id: {}", problem.getId());
} catch (Exception e) {
log.error("同步到 ES 失败, id: {}, error: {}", problem.getId(), e.getMessage(), e);
}
}
/**
* 从 Elasticsearch 删除
*/
private void deleteFromElasticsearch(Long id) {
try {
problemRepository.deleteById(id);
log.info("从 ES 删除成功, id: {}", id);
} catch (Exception e) {
log.error("从 ES 删除失败, id: {}, error: {}", id, e.getMessage(), e);
}
}
private void clearCache(Serializable id) {
// 1. 删除 L2 Redis 缓存
Cache redisCache = redisCacheManager.getCache("problem");
if (redisCache != null) {
redisCache.evict(id);
log.info("L2 缓存已清除, id: {}", id);
}
// 2. 发送 MQ 消息,通知其他实例删除 L1 缓存
rabbitTemplate.convertAndSend("cache.update.exchange", "", id.toString());
log.info("发送缓存更新广播, id: {}", id);
}
// 监听 MQ 消息,删除本地 L1 缓存
@RabbitListener(queues = "#{@problemCacheUpdateQueue.name}")
public void onCacheUpdate(String id) {
Cache caffeineCache = caffeineCacheManager.getCache("problem");
if (caffeineCache != null) {
caffeineCache.evict(Long.valueOf(id));
log.info("收到广播,L1 缓存已清除, id: {}", id);
}
}
@Override
public PageDTO<Problem> pageQuery(ProblemPageQueryDTO problemPageQueryDTO) {
log.info("分页查询题目列表: {}", problemPageQueryDTO);
List<Long> ids = null;
if (needElasticsearchSearch(problemPageQueryDTO)) {
ids = searchIdsByElasticsearch(problemPageQueryDTO);
if (ids.isEmpty()) {
long esCount = safeEsCount();
long dbCount = super.count();
if (shouldResyncEs(esCount, dbCount)) {
syncAllToElasticsearch();
ids = searchIdsByElasticsearch(problemPageQueryDTO);
}
}
if (ids.isEmpty()) {
return PageDTO.empty(0L, 0L);
}
}
List<String> tagNames = normalizeTags(problemPageQueryDTO.getTags());
Page<Problem> page = (Page<Problem>) baseMapper.selectPageWithFilters(
problemPageQueryDTO.toMpPage("id", true),
ids,
problemPageQueryDTO.getDifficulty(),
tagNames,
problemPageQueryDTO.getStatus(),
UserContext.getCurrentUser()
);
List<Problem> records = page.getRecords();
attachTags(records);
return PageDTO.fullPage(page.getTotal(), page.getPages(), records);
}
/**
* 判断是否需要使用 ES 搜索
* 当有文本搜索需求时使用 ES,简单的精确查询使用 MySQL
*/
private boolean needElasticsearchSearch(ProblemPageQueryDTO dto) {
return StringUtils.hasText(dto.getName()) || StringUtils.hasText(dto.getDescription());
}
/**
* 使用 Elasticsearch 搜索
*/
private List<Long> searchIdsByElasticsearch(ProblemPageQueryDTO dto) {
log.info("使用 ES 搜索并结合 MySQL 过滤");
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolean hasQuery = false;
if (StringUtils.hasText(dto.getName())) {
boolQuery.should(QueryBuilders.matchQuery("name", dto.getName()).fuzziness(Fuzziness.AUTO));
boolQuery.should(QueryBuilders.matchQuery("description", dto.getName()).fuzziness(Fuzziness.AUTO));
hasQuery = true;
}
if (StringUtils.hasText(dto.getDescription())) {
boolQuery.should(QueryBuilders.matchQuery("description", dto.getDescription()).fuzziness(Fuzziness.AUTO));
hasQuery = true;
}
if (!hasQuery) {
return List.of();
}
boolQuery.minimumShouldMatch(1);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(boolQuery)
.withPageable(PageRequest.of(0, MAX_ES_RESULTS))
.build();
SearchHits<ProblemDocument> searchHits = elasticsearchTemplate.search(query, ProblemDocument.class);
return searchHits.getSearchHits().stream()
.map(hit -> hit.getContent().getId())
.collect(Collectors.toList());
}
static List<String> normalizeTags(List<String> tags) {
if (tags == null || tags.isEmpty()) {
return List.of();
}
Set<String> normalized = new LinkedHashSet<>();
for (String tag : tags) {
if (!StringUtils.hasText(tag)) {
continue;
}
normalized.add(tag.trim());
}
return new ArrayList<>(normalized);
}
static boolean shouldResyncEs(long esCount, long dbCount) {
return esCount == 0 && dbCount > 0;
}
private long safeEsCount() {
try {
return problemRepository.count();
} catch (Exception e) {
log.warn("查询 ES count 失败,尝试重新创建索引", e);
recreateIndex();
return 0L;
}
}
private void attachTags(List<Problem> problems) {
if (problems == null || problems.isEmpty()) {
return;
}
List<Long> problemIds = problems.stream()
.map(Problem::getId)
.filter(id -> id != null)
.collect(Collectors.toList());
if (problemIds.isEmpty()) {
return;
}
List<ProblemTagView> tagViews = problemTagMapper.selectTagsByProblemIds(problemIds);
Map<Long, List<String>> tagMap = new HashMap<>();
for (ProblemTagView view : tagViews) {
tagMap.computeIfAbsent(view.getProblemId(), key -> new ArrayList<>()).add(view.getTagName());
}
for (Problem problem : problems) {
problem.setTags(tagMap.getOrDefault(problem.getId(), List.of()));
}
}
@Override
public void updateProblemStats(Long problemId, boolean isAccepted) {
lambdaUpdate()
.eq(Problem::getId, problemId)
.setSql("total_attempt = total_attempt + 1")
.setSql(isAccepted, "total_pass = total_pass + 1")
.update();
// 更新后同步到 ES
Problem problem = super.getById(problemId);
if (problem != null) {
syncToElasticsearch(problem);
}
}
/**
* 全量同步数据到 ES (用于初始化或数据修复)
*/
@Override
public int syncAllToElasticsearch() {
log.info("开始全量同步数据到 ES");
List<Problem> allProblems = super.list();
List<ProblemDocument> documents = allProblems.stream()
.map(problem -> new ProblemDocument(problem.getId(), problem.getName(), problem.getDescription()))
.collect(Collectors.toList());
log.info("全量同步完成,共同步 {} 条数据", documents.size());
problemRepository.saveAll(documents);
return documents.size();
}
@Override
public int reindexAll() {
recreateIndex();
return syncAllToElasticsearch();
}
@Override
public void resetProblems() {
log.info("开始清空 problem 模块数据...");
problemTagMapper.delete(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>());
tagMapper.delete(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>());
super.remove(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>());
recreateIndex();
log.info("problem 数据与 ES 索引已清空");
}
private void recreateIndex() {
IndexOperations indexOps = elasticsearchTemplate.indexOps(ProblemDocument.class);
if (indexOps.exists()) {
indexOps.delete();
}
indexOps.create();
indexOps.putMapping(indexOps.createMapping());
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/resources/application.yaml
================================================
server:
port: ${doj.port.problem-service}
management:
endpoints:
web:
exposure:
include: health,prometheus
metrics:
export:
prometheus:
enabled: true
doj:
db:
host: ${DOJ_DB_HOST:127.0.0.1}
name: doj_problem
user: ${DOJ_DB_USER:root}
pwd: ${DOJ_DB_PWD:123}
mq:
host: ${DOJ_MQ_HOST:127.0.0.1}
redis:
host: ${DOJ_REDIS_HOST:127.0.0.1}
swagger:
title: 题目接口文档
scan: com.decade.doj.problem.controller
spring:
elasticsearch:
uris: ${ELASTICSEARCH_URIS:http://127.0.0.1:9200}
================================================
FILE: DOJ-BE/problem-service/src/main/resources/bootstrap.yaml
================================================
spring:
application:
name: problem-service
profiles:
active: dev, common
cloud:
nacos:
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
config:
file-extension: yaml
shared-configs:
- dataId: shared-jdbc.yaml
- dataId: shared-swagger.yaml
- dataId: shared-jwt.yaml
- dataId: shared-rabbitmq.yaml
# shared-jdbc.yaml
#spring:
# datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://${doj.db.host:192.168.74.131}:3306/${doj.db.name}
# username: ${doj.db.user:root}
# password: ${doj.db.pwd:123}
# hikari:
# auto-commit: true
# maximum-pool-size: 10
# minimum-idle: 10
# connection-test-query: select 1
# connection-timeout: 20000
#mybatis-plus:
# global-config:
# db-config:
# update-strategy: not_null
# id-type: auto
# shared-swagger.yaml
#logging:
# level:
# com.decade: debug
# pattern:
# dateformat: HH:mm:ss:SSS
#knife4j:
# enable: true
# openapi:
# title: ${doj.swagger.title}
# description: "Duck Online Judge API"
# email: "decade-qzj@foxmail.com"
# version: v1.0.0
# group:
# default:
# group-name: default
# api-rule: package
# api-rule-resources:
# - ${doj.swagger.scan}
# shared-jwt.yaml
#doj:
# jwt:
# location: classpath:doj.jks
# alias: decade
# password: doj123
# tokenTTL: 30m
# authorization: "authorization"
# secret-key: "uid"
================================================
FILE: DOJ-BE/problem-service/src/main/resources/es/problem-settings.json
================================================
{
"analysis": {
"analyzer": {
"ik_max_lowercase": {
"tokenizer": "ik_max_word",
"filter": ["lowercase"]
},
"ik_smart_lowercase": {
"tokenizer": "ik_smart",
"filter": ["lowercase"]
}
},
"normalizer": {
"lowercase_normalizer": {
"type": "custom",
"filter": ["lowercase"]
}
}
}
}
================================================
FILE: DOJ-BE/problem-service/src/main/resources/mapper/ProblemMapper.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.decade.doj.problem.mapper.ProblemMapper">
<resultMap id="ProblemResultMap" type="com.decade.doj.problem.domain.po.Problem">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="input_style" property="inputStyle" />
<result column="output_style" property="outputStyle" />
<result column="hint" property="hint" />
<result column="input_sample" property="inputSample" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
<result column="output_sample" property="outputSample" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
<result column="difficulty" property="difficulty" />
<result column="time_limit" property="timeLimit" />
<result column="memory_limit" property="memoryLimit" />
<result column="total_pass" property="totalPass" />
<result column="total_attempt" property="totalAttempt" />
<result column="test_data" property="testData" />
<result column="test_ans" property="testAns" />
<result column="status" property="status" />
</resultMap>
<select id="selectPageWithFilters" resultMap="ProblemResultMap">
SELECT
p.*,
CASE
WHEN EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
AND s.status = 'Accepted'
) THEN '已解决'
WHEN EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
) THEN '尝试中'
ELSE '未开始'
END AS status
FROM problem p
WHERE 1 = 1
<if test="ids != null and ids.size > 0">
AND p.id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
<if test="difficulty != null and difficulty != ''">
AND p.difficulty = #{difficulty}
</if>
<if test="tagNames != null and tagNames.size > 0">
AND EXISTS (
SELECT 1
FROM problem_tag pt
JOIN tag t ON pt.tag_id = t.id
WHERE pt.problem_id = p.id
AND t.name IN
<foreach collection="tagNames" item="tag" open="(" separator="," close=")">
#{tag}
</foreach>
)
</if>
<if test="status != null and status != ''">
<choose>
<when test="status == '已解决'">
AND EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
AND s.status = 'Accepted'
)
</when>
<when test="status == '尝试中'">
AND EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
)
AND NOT EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
AND s.status = 'Accepted'
)
</when>
<when test="status == '未开始'">
AND NOT EXISTS (
SELECT 1 FROM doj_submission.submission s
WHERE s.problem_id = p.id
AND s.user_id = #{userId}
)
</when>
<otherwise>
AND 1 = 1
</otherwise>
</choose>
</if>
ORDER BY p.id ASC
</select>
</mapper>
================================================
FILE: DOJ-BE/problem-service/src/main/resources/sql/problem_rebuild.sql
================================================
SET NAMES utf8mb4;
DROP TABLE IF EXISTS problem_tag;
DROP TABLE IF EXISTS tag;
DROP TABLE IF EXISTS problem;
CREATE TABLE problem (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
description MEDIUMTEXT NOT NULL,
input_style TEXT NOT NULL,
output_style TEXT NOT NULL,
input_sample JSON NOT NULL,
output_sample JSON NOT NULL,
difficulty VARCHAR(10) NOT NULL,
time_limit INT NOT NULL,
memory_limit INT NOT NULL,
hint TEXT,
total_pass INT NOT NULL DEFAULT 0,
total_attempt INT NOT NULL DEFAULT 0,
test_data LONGTEXT NOT NULL,
test_ans LONGTEXT NOT NULL,
INDEX idx_problem_difficulty (difficulty)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE tag (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE problem_tag (
problem_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL,
PRIMARY KEY (problem_id, tag_id),
INDEX idx_tag_id (tag_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO tag (id, name) VALUES
(1, '入门'),
(2, '模拟'),
(3, '动态规划'),
(4, '数组'),
(5, '栈'),
(6, '字符串'),
(7, '矩阵'),
(8, '二分'),
(9, '有序'),
(10, '排序'),
(11, '区间'),
(12, '网格'),
(13, '图论'),
(14, '拓扑排序'),
(15, '最小生成树'),
(16, '最短路径'),
(17, '数学'),
(18, '基础'),
(19, '双指针'),
(20, '分治'),
(21, '归并排序'),
(22, '前缀和'),
(23, 'DFS'),
(24, 'BFS'),
(25, '数论');
INSERT INTO problem (
id, name, description, input_style, output_style, input_sample, output_sample,
difficulty, time_limit, memory_limit, hint, total_pass, total_attempt, test_data, test_ans
) VALUES
(1, 'A+B Problem',
'给定两个整数 A 和 B,输出它们的和。\n\n**任务**\n- 读取两个整数并计算 A+B。\n- 结果可能为负数。',
'- 一行包含两个整数 A 和 B,用空格分隔。',
'- 输出一个整数,即 A+B。',
'["1 2"]',
'["3"]',
'简单', 1000, 256,
'- 范围:-1e9 <= A,B <= 1e9\n- 使用 64 位整数存储结果',
0, 0, '1 2', '3'),
(2, '最大子数组和',
'给定长度为 n 的整数数组,找到和最大的连续子数组,输出该最大和。\n\n**说明**\n- 子数组必须连续。\n- 至少选择一个元素。',
'- 第一行一个整数 n。\n- 第二行 n 个整数,表示数组元素。',
'- 输出最大子数组和。',
'["9\\n-2 1 -3 4 -1 2 1 -5 4"]',
'["6"]',
'中等', 1000, 256,
'- 1 <= n <= 200000\n- 元素范围 -1e9 到 1e9\n- 可使用 Kadane 线性算法',
0, 0, '9\n-2 1 -3 4 -1 2 1 -5 4', '6'),
(3, '有效括号',
'给定仅由 ()[]{} 组成的字符串,判断括号是否成对且顺序正确。\n\n**有效条件**\n- 左括号必须由同类型右括号闭合。\n- 括号必须按正确顺序闭合。',
'- 一行字符串 s。',
'- 若有效输出 true,否则输出 false。',
'["([])"]',
'["true"]',
'简单', 1000, 256,
'- 1 <= |s| <= 100000\n- 使用栈进行匹配',
0, 0, '([])', 'true'),
(4, '矩阵转置',
'给定一个 m x n 矩阵,输出其转置矩阵(n x m)。\n\n转置后,第 i 行第 j 列元素变为原矩阵的第 j 行第 i 列。',
'- 第一行两个整数 m n。\n- 接下来 m 行,每行 n 个整数。',
'- 输出 n 行,每行 m 个整数,为转置后的矩阵。',
'["2 3\\n1 2 3\\n4 5 6"]',
'["1 4\\n2 5\\n3 6"]',
'简单', 1000, 256,
'- 1 <= m,n <= 500\n- 注意空格与换行格式',
0, 0, '2 3\n1 2 3\n4 5 6', '1 4\n2 5\n3 6'),
(5, '二分查找',
'在一个升序数组中查找目标值 target。\n\n若找到,输出其 0-based 索引;否则输出 -1。',
'- 第一行两个整数 n 和 target。\n- 第二行 n 个整数,按升序排列。',
'- 输出目标值的索引或 -1。',
'["5 7\\n1 3 5 7 9"]',
'["3"]',
'简单', 1000, 256,
'- 1 <= n <= 1000000\n- 数组已升序\n- 使用二分查找 O(log n)',
0, 0, '5 7\n1 3 5 7 9', '3'),
(6, '合并区间',
'给定 n 个闭区间 [l, r],将所有重叠或相邻的区间合并,并按 l 升序输出。',
'- 第一行一个整数 n。\n- 接下来 n 行,每行两个整数 l r。',
'- 输出合并后的区间,每行一个 l r。',
'["4\\n1 3\\n2 6\\n8 10\\n15 18"]',
'["1 6\\n8 10\\n15 18"]',
'中等', 1000, 256,
'- 1 <= n <= 200000\n- 先按 l 排序\n- 若前一个区间右端点 >= 下一个区间左端点,则合并',
0, 0, '4\n1 3\n2 6\n8 10\n15 18', '1 6\n8 10\n15 18'),
(7, '最小路径和',
'给定一个 m x n 的非负整数网格,从左上角到右下角,只能向右或向下移动,求路径上的最小和。',
'- 第一行 m n。\n- 接下来 m 行,每行 n 个非负整数。',
'- 输出最小路径和。',
'["3 3\\n1 3 1\\n1 5 1\\n4 2 1"]',
'["7"]',
'中等', 1000, 256,
'- 1 <= m,n <= 200\n- 0 <= grid[i][j] <= 100\n- 使用动态规划',
0, 0, '3 3\n1 3 1\n1 5 1\n4 2 1', '7'),
(8, '编辑距离',
'给定两个字符串 s 和 t,你可以对 s 执行插入、删除、替换操作,使其变为 t。\n\n求最少操作数。',
'- 第一行字符串 s。\n- 第二行字符串 t。',
'- 输出最小编辑距离。',
'["kitten\\nsitting"]',
'["3"]',
'困难', 1000, 256,
'- 1 <= |s|, |t| <= 2000\n- dp[i][j] 表示前 i 和前 j 的最小操作数',
0, 0, 'kitten\nsitting', '3'),
(9, '拓扑排序',
'给定一个有向无环图 (DAG),输出**字典序最小**的拓扑序列。\n\n如果多个节点入度为 0,优先选择编号更小的节点。',
'- 第一行 n m,表示节点数和边数(节点编号从 1 到 n)。\n- 接下来 m 行,每行一条有向边 u v。',
'- 输出 n 个整数,为字典序最小的拓扑序。',
'["4 3\\n1 2\\n2 3\\n3 4"]',
'["1 2 3 4"]',
'中等', 1000, 256,
'- 1 <= n <= 200000, 1 <= m <= 300000\n- 使用小根堆维护入度为 0 的节点',
0, 0, '4 3\n1 2\n2 3\n3 4', '1 2 3 4'),
(10, '最小生成树',
'给定一个连通无向带权图,求其最小生成树的权值之和。',
'- 第一行 n m。\n- 接下来 m 行,每行 u v w,表示边及权值。',
'- 输出最小生成树权值之和。',
'["4 5\\n1 2 1\\n1 3 4\\n2 3 2\\n2 4 5\\n3 4 3"]',
'["6"]',
'中等', 1000, 256,
'- 1 <= n <= 200000, 1 <= m <= 300000\n- 使用 Kruskal 或 Prim',
0, 0, '4 5\n1 2 1\n1 3 4\n2 3 2\n2 4 5\n3 4 3', '6'),
(11, '最短路径',
'给定一个正权图,求从 s 到 t 的最短路径长度。\n\n若不可达,输出 -1。',
'- 第一行 n m s t。\n- 接下来 m 行,每行 u v w,表示从 u 到 v 的权值为 w 的边。',
'- 输出最短距离,若不可达输出 -1。',
'["5 6 1 5\\n1 2 2\\n1 3 4\\n2 3 1\\n2 4 7\\n3 5 3\\n4 5 1"]',
'["6"]',
'中等', 1000, 256,
'- 1 <= n <= 200000, 1 <= m <= 300000\n- 权值均为正\n- 使用 Dijkstra',
0, 0, '5 6 1 5\n1 2 2\n1 3 4\n2 3 1\n2 4 7\n3 5 3\n4 5 1', '6'),
(12, '最大公约数',
'给定两个非负整数 a 和 b,输出它们的最大公约数。',
'- 一行两个整数 a 和 b。',
'- 输出 gcd(a,b)。',
'["48 18"]',
'["6"]',
'简单', 1000, 256,
'- 1 <= a,b <= 1e18\n- 使用欧几里得算法',
0, 0, '48 18', '6'),
(13, '判断回文',
'判断给定字符串是否为回文串。\n\n回文串从左到右和从右到左完全一致。',
'- 一行字符串 s(仅包含字母或数字,无空格)。',
'- 若为回文输出 true,否则输出 false。',
'["level"]',
'["true"]',
'简单', 1000, 256,
'- 1 <= |s| <= 1000000\n- 双指针从两端向中间',
0, 0, 'level', 'true'),
(14, '数组旋转',
'给定数组,将其向右旋转 k 步。\n\n例如 [1,2,3,4,5] 向右旋转 2 步得到 [4,5,1,2,3]。',
'- 第一行 n k。\n- 第二行 n 个整数。',
'- 输出旋转后的数组。',
'["5 2\\n1 2 3 4 5"]',
'["4 5 1 2 3"]',
'简单', 1000, 256,
'- 1 <= n <= 200000\n- k 可能很大,先取 k % n',
0, 0, '5 2\n1 2 3 4 5', '4 5 1 2 3'),
(15, '逆序对计数',
'给定一个数组,统计其中逆序对的数量。\n\n若 i < j 且 a[i] > a[j],则 (i,j) 构成一对逆序对。',
'- 第一行 n。\n- 第二行 n 个整数。',
'- 输出逆序对数量。',
'["5\\n2 3 8 6 1"]',
'["5"]',
'困难', 1000, 256,
'- 1 <= n <= 200000\n- 使用归并排序或树状数组',
0, 0, '5\n2 3 8 6 1', '5'),
(16, '数位和',
'给定一个非负整数 N,计算其十进制表示中所有数字之和。',
'- 一行一个整数 N。',
'- 输出数字之和。',
'["12345"]',
'["15"]',
'简单', 1000, 256,
'- 0 <= N <= 1e18\n- 逐位取模累加',
0, 0, '12345', '15'),
(17, '区间和查询',
'给定长度为 n 的数组,回答 q 次区间求和查询。\n\n每个查询给出 l r(1-based),输出 a[l..r] 的和。',
'- 第一行 n q。\n- 第二行 n 个整数。\n- 接下来 q 行,每行 l r。',
'- 对每个查询输出一行区间和。',
'["5 3\\n1 2 3 4 5\\n1 3\\n2 5\\n4 4"]',
'["6\\n14\\n4"]',
'简单', 1000, 256,
'- 1 <= n,q <= 200000\n- 使用前缀和',
0, 0, '5 3\n1 2 3 4 5\n1 3\n2 5\n4 4', '6\n14\n4'),
(18, '岛屿数量',
'给定由 0 和 1 组成的网格,1 表示陆地,0 表示水域。\n\n相邻(上下左右)陆地连成一个岛屿,求岛屿数量。',
'- 第一行 m n。\n- 接下来 m 行,每行 n 个数字(0 或 1)。',
'- 输出岛屿数量。',
'["3 4\\n1 1 0 0\\n0 1 0 1\\n1 0 0 1"]',
'["3"]',
'中等', 1000, 256,
'- 1 <= m,n <= 500\n- 使用 DFS/BFS 标记访问',
0, 0, '3 4\n1 1 0 0\n0 1 0 1\n1 0 0 1', '3'),
(19, '最长公共子序列',
'给定两个字符串 s 和 t,求它们的最长公共子序列长度。\n\n子序列不要求连续,但相对顺序必须一致。',
'- 第一行字符串 s。\n- 第二行字符串 t。',
'- 输出最长公共子序列长度。',
'["abcde\\nace"]',
'["3"]',
'中等', 1000, 256,
'- 1 <= |s|, |t| <= 2000\n- 使用二维 DP',
0, 0, 'abcde\nace', '3'),
(20, '阶乘末尾零',
'给定整数 N,计算 N! 末尾连续 0 的个数。\n\nN! 中 0 的数量取决于因子 2 和 5 的数量,且 2 的数量充足。',
'- 一行一个整数 N。',
'- 输出末尾 0 的个数。',
'["25"]',
'["6"]',
'中等', 1000, 256,
'- 0 <= N <= 1e18\n- 统计 N/5 + N/25 + N/125 + ...',
0, 0, '25', '6');
INSERT INTO problem_tag (problem_id, tag_id) VALUES
(1, 1), (1, 2),
(2, 3), (2, 4),
(3, 5), (3, 6),
(4, 7), (4, 2),
(5, 8), (5, 4), (5, 9),
(6, 10), (6, 11),
(7, 3), (7, 12),
(8, 3), (8, 6),
(9, 13), (9, 14),
(10, 13), (10, 15),
(11, 13), (11, 16),
(12, 17), (12, 18),
(13, 6), (13, 19),
(14, 4), (14, 2),
(15, 20), (15, 21),
(16, 17), (16, 18),
(17, 22), (17, 4),
(18, 23), (18, 24), (18, 13),
(19, 3), (19, 6),
(20, 17), (20, 25);
================================================
FILE: DOJ-BE/problem-service/src/test/java/com/decade/doj/problem/TestProblem.java
================================================
package com.decade.doj.problem;
import com.decade.doj.problem.domain.po.Problem;
import com.decade.doj.problem.service.IProblemService;
import com.decade.doj.problem.service.impl.ProblemServiceImpl;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class TestProblem {
@Autowired
private IProblemService problemService;
@Test
public void test() {
Problem problem = problemService.getById(1);
System.out.println(problem);
System.out.println("fin");
}
}
================================================
FILE: DOJ-BE/sandbox-service/Dockerfile
================================================
# Stage 1: Build the application
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
# Copy all pom.xml files first to leverage Docker cache
COPY DOJ-BE/pom.xml .
COPY DOJ-BE/common/pom.xml ./common/
COPY DOJ-BE/gateway-service/pom.xml ./gateway-service/
COPY DOJ-BE/problem-service/pom.xml ./problem-service/
COPY DOJ-BE/sandbox-service/pom.xml ./sandbox-service/
COPY DOJ-BE/submission-service/pom.xml ./submission-service/
COPY DOJ-BE/user-service/pom.xml ./user-service/
# Download all dependencies
RUN mvn dependency:go-offline
# Copy all source code
COPY DOJ-BE/common/src ./common/src
COPY DOJ-BE/sandbox-service/src ./sandbox-service/src
# Build the specific service
RUN mvn -f ./pom.xml -pl sandbox-service -am clean package -DskipTests
RUN ls -al ./sandbox-service/target
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre
# Install Docker
USER root
RUN apt-get update && apt-get install -y docker.io
WORKDIR /app
# Copy the executable jar from the build stage
COPY --from=build /app/sandbox-service/target/sandbox-service.jar ./app.jar
EXPOSE 8082
ENTRYPOINT ["java", "-jar", "./app.jar"]
================================================
FILE: DOJ-BE/sandbox-service/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>sandbox-service</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>
<!-- <dependency>-->
<!-- <groupId>com.github.docker-java</groupId>-->
<!-- <artifactId>docker-java</artifactId>-->
<!-- <version>3.3.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.github.docker-java</groupId>-->
<!-- <artifactId>docker-java-transport-httpclient5</artifactId>-->
<!-- <version>3.3.0</version>-->
<!-- </dependency>-->
<!--common-->
<dependency>
<groupId>com.decade</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/SandboxApplication.java
================================================
package com.decade.doj.sandbox;
import com.decade.doj.common.config.custom.DefaultFeignConfig;
import com.decade.doj.common.config.custom.MVCConfig;
import com.decade.doj.common.config.custom.MybatisConfig;
import com.decade.doj.common.config.thread.ThreadPoolConfig;
import com.decade.doj.common.interceptor.AdminCheckInterceptor;
import com.decade.doj.common.interceptor.IdentityInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.decade.doj.common.client", defaultConfiguration = DefaultFeignConfig.class)
@Import({ThreadPoolConfig.class, MVCConfig.class, MybatisConfig.class, IdentityInterceptor.class, AdminCheckInterceptor.class})
public class SandboxApplication {
public static void main(String[] args) {
SpringApplication.run(SandboxApplication.class, args);
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/config/MqConfig.java
================================================
package com.decade.doj.sandbox.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/controller/SandboxController.java
================================================
package com.decade.doj.sandbox.controller;
import com.alibaba.fastjson.JSON;
import com.decade.doj.common.client.ProblemClient;
import com.decade.doj.common.client.SubmissionClient;
import com.decade.doj.common.config.properties.ResourceProperties;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.po.Problem;
import com.decade.doj.common.domain.po.Submission;
import com.decade.doj.common.utils.UserContext;
import com.decade.doj.sandbox.domain.vo.ExecuteMessage;
import com.decade.doj.sandbox.domain.vo.JudgingTask;
import com.decade.doj.sandbox.enums.LanguageEnum;
import com.decade.doj.sandbox.service.ISandboxService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@RestController
@RequestMapping("/sandbox")
@Tag(name = "沙箱相关接口")
@Slf4j
@RequiredArgsConstructor
@EnableConfigurationProperties(ResourceProperties.class)
public class SandboxController {
private final ResourceProperties resourceProperties;
private final ISandboxService sandboxService;
private final SubmissionClient submissionClient;
private final ProblemClient problemClient;
private final StringRedisTemplate redisTemplate;
@PostMapping("/code")
@Operation(summary = "运行代码文件(异步执行)")
public CompletableFuture<R<ExecuteMessage>> runCode(@RequestParam("file") MultipartFile file, @RequestParam("language") @NotBlank String lang) {
if (file.isEmpty()) {
return CompletableFuture.completedFuture(R.error("上传的文件不能为空!"));
}
if (LanguageEnum.isInValidLanguage(lang)) {
return CompletableFuture.completedFuture(R.error("不支持的编程语言: " + lang));
}
try {
String path = saveFile(file, resourceProperties.getCodePath(), null)[0];
return sandboxService
.runCodeInSandbox(path, file.getOriginalFilename(), lang)
.thenApply(R::ok);
} catch (IOException e) {
log.error("保存代码文件失败", e);
return CompletableFuture.completedFuture(R.error("文件保存失败: " + e.getMessage()));
}
}
@PostMapping("/problem")
@Operation(summary = "运行题目代码(异步执行)")
public CompletableFuture<R<ExecuteMessage>> runProblem(@RequestParam("file") MultipartFile file, @RequestParam("input") MultipartFile input, @RequestParam("language") @NotBlank String lang, @RequestParam("pid") Long pid) {
if (file.isEmpty()) {
return CompletableFuture.completedFuture(R.error("上传的文件不能为空!"));
}
if (LanguageEnum.isInValidLanguage(lang)) {
return CompletableFuture.completedFuture(R.error("不支持的编程语言: " + lang));
}
try {
String[] Paths = saveFile(file, resourceProperties.getCodePath(), null);
String codePath = Paths[0];
saveFile(input, resourceProperties.getCodePath(), Paths[1]);
return sandboxService
.runCodeInSandboxWI(codePath, input.getOriginalFilename(), file.getOriginalFilename(), lang)
.thenApply(R::ok);
} catch (IOException e) {
log.error("保存代码/输入文件失败", e);
return CompletableFuture.completedFuture(R.error("文件保存失败: " + e.getMessage()));
}
}
@PostMapping("/validate")
@Operation(summary = "验证题目代码(异步提交)")
public R<Long> runProblemValidate(@RequestParam("file") MultipartFile file, @RequestParam("language") @NotBlank String lang, @RequestParam("pid") Long pid) throws IOException {
if (file.isEmpty()) {
return R.error(400, "上传的文件不能为空!");
}
if (LanguageEnum.isInValidLanguage(lang)) {
return R.error(400, "不支持的编程语言: " + lang);
}
String[] Paths = saveFile(file, resourceProperties.getCodePath(), null);
String codePath = Paths[0];
String[] data = saveText2File(pid, resourceProperties.getCodePath(), Paths[1]);
// 1. 调用 submission-service 创建一个 PENDING 状态的提交记录
Submission submissionDTO = new Submission();
submissionDTO.setProblemId(pid);
submissionDTO.setUserId(UserContext.getCurrentUser());
submissionDTO.setLanguage(lang);
submissionDTO.setCode(Paths[2]);
R<Long> response = submissionClient.submit(submissionDTO);
if (!response.success()) {
return R.error(500, "创建提交记录失败: " + response.getMsg());
}
Long submissionId = response.getData();
// 2. 创建判题任务
JudgingTask task = new JudgingTask()
.setSubmissionId(submissionId)
.setLocalPath(codePath)
.setInput(data[0])
.setOutput(data[1])
.setFilename(file.getOriginalFilename())
.setLang(lang)
.setProblemId(pid)
.setUid(UserContext.getCurrentUser());
// 3. 将任务推入 Redis 队列
redisTemplate.opsForList().leftPush("judging:queue", JSON.toJSONString(task));
log.info("新的[验证]任务已推入队列, submissionId: {}", submissionId);
// 4. 立即返回 submissionId
return R.ok(submissionId);
}
private String[] saveText2File(Long pid, String basePath, String folderName) throws IOException {
if (folderName == null) {
folderName = UUID.randomUUID().toString();
}
String subFolderPathStr = basePath + folderName + FileSystems.getDefault().getSeparator();
Path subFolderPath = Paths.get(subFolderPathStr);
Files.createDirectories(subFolderPath);
String inputFileName = pid + "_p_input.txt";
// 从其他微服务读取input和output数据
Problem problem = problemClient.getProblemById(pid).getData();
String inputdata = problem.getTestData();
String outputdata = problem.getTestAns();
Path inputFilePath = subFolderPath.resolve(inputFileName);
Files.writeString(inputFilePath, inputdata);
return new String[]{inputFileName, outputdata};
}
private String[] saveFile(MultipartFile file, String basePath, String folderName) throws IOException {
if (folderName == null) {
folderName = UUID.randomUUID().toString();
}
String subFolderPathStr = basePath + folderName + FileSystems.getDefault().getSeparator();
Path subFolderPath = Paths.get(subFolderPathStr);
Files.createDirectories(subFolderPath);
String origFilename = file.getOriginalFilename();
if (origFilename == null || origFilename.isBlank()) {
throw new IOException("上传文件原始文件名为空,无法保存");
}
Path destinationFilePath = subFolderPath.resolve(origFilename);
byte[] bytes = file.getBytes();
String content = new String(bytes, StandardCharsets.UTF_8);
Files.write(destinationFilePath, bytes);
// file.transferTo(destinationFilePath.toFile());
return new String[]{destinationFilePath.toUri().getPath(), folderName, content};
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/ExecuteMessage.java
================================================
package com.decade.doj.sandbox.domain.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Data
@Accessors(chain = true)
public class ExecuteMessage {
private Integer exitValue;
private String status;
private String message;
private double time;
private Long memory;
private static final Map<Integer, String> exitStatusMap = new HashMap<>();
private static final Set<Integer> InfoStatus = new HashSet<>();
static {
exitStatusMap.put(0, "Finished");
exitStatusMap.put(1, "Runtime Error");
exitStatusMap.put(2, "Compile Error");
exitStatusMap.put(124, "Time Limit Exceeded");
exitStatusMap.put(137, "Memory Limit Exceeded");
exitStatusMap.put(10, "Accepted");
exitStatusMap.put(11, "Wrong Answer");
InfoStatus.add(0);
InfoStatus.add(1);
InfoStatus.add(2);
InfoStatus.add(10);
InfoStatus.add(11);
}
public static String getStatus(Integer exitValue) {
if (exitValue == null) {
return "Unknown Error";
}
return exitStatusMap.getOrDefault(exitValue, "Unknown exitValue");
}
public static boolean show(Integer exitValue) {
return InfoStatus.contains(exitValue);
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/JudgingTask.java
================================================
package com.decade.doj.sandbox.domain.vo;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class JudgingTask {
Long submissionId;
String localPath;
String input;
String output;
String filename;
String lang;
Long problemId;
String code;
Long uid;
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/enums/LanguageEnum.java
================================================
package com.decade.doj.sandbox.enums;
import lombok.Getter;
import lombok.Setter;
import java.util.*;
@Getter
public enum LanguageEnum {
PYTHON("python3 %s.py", "python", "128m", 5, "code-runner-python"),
JAVA("sh -c 'javac %s.java && java -Xmx1024m %s'", "java", "128m", 2, "code-runner-java"),
CPP("sh -c 'g++ -std=c++17 %s.cpp -o %s.out && ./%s.out'", "cpp", "128m", 1, "code-runner-cpp");
private final String runCmd;
private final String language;
private final String imageName;
@Setter
private String memoryLimit;
@Setter
private int timeLimit;
// 语言名称(小写)到枚举实例的映射
private static final Map<String, LanguageEnum> NAME_MAP;
static {
Map<String, LanguageEnum> map = new HashMap<>();
for (LanguageEnum le : LanguageEnum.values()) {
map.put(le.language.toLowerCase(Locale.ROOT), le);
}
NAME_MAP = Collections.unmodifiableMap(map);
}
LanguageEnum(String runCmd,
String language,
String memoryLimit,
int timeLimit,
String imageName) {
this.runCmd = runCmd;
this.language = language;
this.memoryLimit = memoryLimit;
this.timeLimit = timeLimit;
this.imageName = imageName;
}
/**
* 检查给定语言字符串是否不在支持列表中。
*
* @param lang 待校验的语言,比如 "java"、"python"、"cpp"
* @return 如果 lang 不在 NAME_MAP 中,返回 true;否则返回 false
*/
public static boolean isInValidLanguage(String lang) {
if (lang == null) {
return true;
}
return !NAME_MAP.containsKey(lang.trim().toLowerCase(Locale.ROOT));
}
/**
* 根据语言字符串获取对应的枚举实例,大小写不敏感。
*
* @param lang 语言名称,比如 "java"、"python"、"cpp"
* @return 对应的 LanguageEnum 实例;若找不到则返回 null
*/
public static LanguageEnum getLanguageEnum(String lang) {
if (lang == null) {
return null;
}
return NAME_MAP.get(lang.trim().toLowerCase(Locale.ROOT));
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/ISandboxService.java
================================================
package com.decade.doj.sandbox.service;
import com.decade.doj.sandbox.domain.vo.ExecuteMessage;
import com.decade.doj.sandbox.domain.vo.JudgingTask;
import java.util.concurrent.CompletableFuture;
public interface ISandboxService {
CompletableFuture<ExecuteMessage> runCodeInSandbox(String localPath, String filename, String lang);
CompletableFuture<ExecuteMessage> runCodeInSandboxWI(String localPath, String inputname, String filename, String lang);
void execute(JudgingTask task);
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/impl/SandboxService.java
================================================
package com.decade.doj.sandbox.service.impl;
import cn.hutool.core.date.DateTime;
import com.decade.doj.common.client.ProblemClient;
import com.decade.doj.common.client.SubmissionClient;
import com.decade.doj.common.client.UserClient;
import com.decade.doj.common.domain.po.Problem;
import com.decade.doj.common.domain.po.Submission;
import com.decade.doj.common.domain.po.User;
import com.decade.doj.common.utils.UserContext;
import com.decade.doj.sandbox.domain.vo.ExecuteMessage;
import com.decade.doj.sandbox.domain.vo.JudgingTask;
import com.decade.doj.sandbox.enums.LanguageEnum;
import com.decade.doj.sandbox.service.ISandboxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
@Slf4j
public class SandboxService implements ISandboxService {
private final RabbitTemplate rabbitTemplate;
private final ThreadPoolTaskExecutor runCodeExecutor;
public SandboxService(
RabbitTemplate rabbitTemplate,
@Qualifier("RunCodeThreadPool") ThreadPoolTaskExecutor runCodeExecutor
) {
this.rabbitTemplate = rabbitTemplate;
this.runCodeExecutor = runCodeExecutor;
}
/**
* 通过占位符数量构造最终执行命令,例如:
* rawCmd = "python3 %s.py",baseName = "Main" -> "python3 Main.py"
* rawCmd = "g++ %s.cpp -o %s.out && ./%s.out" -> 3 个占位符,将 baseName 填充三次
*
* @param rawCmd 带有若干 "%s" 占位符的 Shell 命令模板
* @param baseName 去掉后缀后的文件名
* @return 填充好占位符的最终命令字符串
*/
public static String buildRunCommand(String rawCmd, String baseName) {
// 计算 "%s" 出现的次数
int placeholderCount = rawCmd.split("%s", -1).length - 1;
return switch (placeholderCount) {
case 1 -> String.format(rawCmd, baseName);
case 2 -> String.format(rawCmd, baseName, baseName);
case 3 -> String.format(rawCmd, baseName, baseName, baseName);
default -> throw new IllegalArgumentException("Unsupported placeholder count in runCmd: " + placeholderCount);
};
}
// 正则编译为静态常量,避免每次重复编译
private static final Pattern MEM_PATTERN = Pattern.compile(
"Maximum resident set size \\(kbytes\\): (\\d+)"
);
private static final Pattern TIME_PATTERN = Pattern.compile(
"Elapsed \\(wall clock\\) time \\(h:mm:ss or m:ss\\): \\d+:(\\d+\\.\\d+)"
);
// 构造 /usr/bin/time 与 timeout 的命令模板
private static final String TIMEOUT_TEMPLATE = "/usr/bin/time -v timeout %ds %s";
// 挂载目录
private static final String MOUNT_PATH = "/app";
@Value("${DOJ_CODE_PATH:/app/static/codes/}")
private String containerCodePath;
@Value("${HOST_CODE_PATH:}")
private String hostCodePath;
private String convertToHostPath(String containerPath) {
if (hostCodePath == null || hostCodePath.isEmpty()) {
log.warn("HOST_CODE_PATH 未配置,使用容器路径: {}", containerPath);
return containerPath;
}
String normalizedContainerPath = containerCodePath.replaceAll("/$", "");
String normalizedHostPath = hostCodePath.replaceAll("/$", "");
if (containerPath.startsWith(normalizedContainerPath)) {
String converted = containerPath.replace(normalizedContainerPath, normalizedHostPath);
log.debug("路径转换: {} -> {}", containerPath, converted);
return converted;
}
return containerPath;
}
@Override
public CompletableFuture<ExecuteMessage> runCodeInSandbox(String filePath, String filename, String lang) {
return CompletableFuture.supplyAsync(
() -> _runCodeInSandbox(filePath, filename, lang),
runCodeExecutor
);
}
private ExecuteMessage _runCodeInSandbox(String filePath, String filename, String lang) {
LanguageEnum languageEnum = LanguageEnum.getLanguageEnum(lang);
// 镜像名称
String imageName = languageEnum.getImageName();
// 运行时挂载目录(宿主与容器)
// String fileDir = new File(filePath).getParent();
String containerFileDir = new File(filePath).getParent();
String hostFileDir = convertToHostPath(containerFileDir);
String mountPath = MOUNT_PATH;
int dotIndex = filename.lastIndexOf(".");
String baseName = (dotIndex >= 0)
? filename.substring(0, dotIndex)
: filename;
try {
// 构造真实要执行的命令
String runCmd = buildRunCommand(languageEnum.getRunCmd(), baseName);
String execCmd = String.format(
TIMEOUT_TEMPLATE,
languageEnum.getTimeLimit(),
runCmd
);
// 准备 Docker 运行命令列表
List<String> command = Arrays.asList(
"docker", "run", "--rm",
"-v", hostFileDir + ":" + mountPath,
"--memory", languageEnum.getMemoryLimit(),
imageName,
execCmd
);
ProcessBuilder builder = new ProcessBuilder(command);
// 合并标准输出与标准错误
builder.redirectErrorStream(true);
long startTimeMillis = System.currentTimeMillis();
Process process = builder.start();
// 读取进程输出
StringBuilder fullOutputBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
fullOutputBuilder.append(line).append("\n");
}
}
int exitCode = process.waitFor();
long endTimeMillis = System.currentTimeMillis();
String fullOutput = fullOutputBuilder.toString().trim();
// 匹配内存、耗时信息
Matcher memMatcher = MEM_PATTERN.matcher(fullOutput);
Matcher timeMatcher = TIME_PATTERN.matcher(fullOutput);
String memoryUsage = memMatcher.find() ? memMatcher.group(1) : "-1";
String timeUsed = timeMatcher.find()
? timeMatcher.group(1)
: String.valueOf((endTimeMillis - startTimeMillis) / 1000.0);
int statIndex = fullOutput.indexOf("Command being timed:");
String trimmedOutput = (statIndex != -1)
? fullOutput.substring(0, statIndex).trim()
: fullOutput;
log.info("Exit code: {}", exitCode);
log.info("Raw output:\n{}", fullOutput);
return new ExecuteMessage()
.setExitValue(exitCode)
.setStatus(ExecuteMessage.getStatus(exitCode))
.setMessage(ExecuteMessage.show(exitCode) ? trimmedOutput : "")
.setTime(Double.parseDouble(timeUsed))
.setMemory(Long.parseLong(memoryUsage));
} catch (Exception e) {
log.error("运行沙箱代码出错", e);
return new ExecuteMessage()
.setExitValue(1)
.setStatus("Runtime Error")
.setMessage("执行代码时发生错误: " + e.getMessage());
}
}
@Override
public CompletableFuture<ExecuteMessage> runCodeInSandboxWI(String localPath, String inputname, String filename, String lang) {
return CompletableFuture.supplyAsync(
() -> _runCodeInSandboxWI(localPath, inputname, filename, lang, null),
runCodeExecutor
);
}
@Override
public void execute(JudgingTask task) {
ExecuteMessage result = _runCodeInSandboxWI(task.getLocalPath(), task.getInput(), task.getFilename(), task.getLang(), task.getOutput());
log.info("User: {}, Problem: {}, 判题结果: {}", task.getUid(), task.getProblemId(), result);
Map<String, Object> resultMessage = Map.of(
"submissionId", task.getSubmissionId(),
"executeMessage", result
);
rabbitTemplate.convertAndSend("doj.topic", "judging.result", resultMessage);
log.info("判题结果已发布到MQ, submissionId: {}", task.getSubmissionId());
}
private ExecuteMessage _runCodeInSandboxWI(String filePath, String inputname, String filename, String lang, String answer) {
LanguageEnum languageEnum = LanguageEnum.getLanguageEnum(lang);
// 镜像名称
String imageName = languageEnum.getImageName();
// 运行时挂载目录(宿主与容器)
// String fileDir = new File(filePath).getParent();
String containerFileDir = new File(filePath).getParent();
String hostFileDir = convertToHostPath(containerFileDir);
String mountPath = MOUNT_PATH;
int dotIndex = filename.lastIndexOf(".");
String baseName = (dotIndex >= 0)
? filename.substring(0, dotIndex)
: filename;
try {
// 构造真实要执行的命令
String runCmd = buildRunCommand(languageEnum.getRunCmd(), baseName);
runCmd += " < " + inputname; // 添加输入重定向
String execCmd = String.format(
TIMEOUT_TEMPLATE,
languageEnum.getTimeLimit(),
runCmd
);
// 准备 Docker 运行命令列表
List<String> command = Arrays.asList(
"docker", "run", "--rm",
"-v", hostFileDir + ":" + mountPath,
"--memory", languageEnum.getMemoryLimit(),
imageName,
execCmd
);
ProcessBuilder builder = new ProcessBuilder(command);
// 合并标准输出与标准错误
builder.redirectErrorStream(true);
long startTimeMillis = System.currentTimeMillis();
Process process = builder.start();
// 读取进程输出
StringBuilder fullOutputBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
fullOutputBuilder.append(line).append("\n");
}
}
int exitCode = process.waitFor();
long endTimeMillis = System.currentTimeMillis();
String fullOutput = fullOutputBuilder.toString().trim();
// 匹配内存、耗时信息
Matcher memMatcher = MEM_PATTERN.matcher(fullOutput);
Matcher timeMatcher = TIME_PATTERN.matcher(fullOutput);
String memoryUsage = memMatcher.find() ? memMatcher.group(1) : "-1";
String timeUsed = timeMatcher.find()
? timeMatcher.group(1)
: String.valueOf((endTimeMillis - startTimeMillis) / 1000.0);
int statIndex = fullOutput.indexOf("Command being timed:");
String trimmedOutput = (statIndex != -1)
? fullOutput.substring(0, statIndex).trim()
: fullOutput;
log.info("Exit code: {}", exitCode);
log.info("Raw output:\n{}", fullOutput);
String status = ExecuteMessage.getStatus(exitCode);
if (answer != null && ExecuteMessage.getStatus(exitCode).equals("Finished")) {
// 比较输出与答案
boolean isCorrect = trimmedOutput.equals(answer.trim());
if (isCorrect) {
status = "Accepted";
exitCode = 10;
} else {
status = "Wrong Answer";
exitCode = 11;
trimmedOutput += "\n```\nExpected: " + answer.trim();
}
}
return new ExecuteMessage()
.setExitValue(exitCode)
.setStatus(status)
.setMessage(ExecuteMessage.show(exitCode) ? trimmedOutput : "")
.setTime(Double.parseDouble(timeUsed))
.setMemory(Long.parseLong(memoryUsage));
} catch (Exception e) {
log.error("运行沙箱代码出错", e);
return new ExecuteMessage()
.setExitValue(1)
.setStatus("Runtime Error")
.setMessage("执行代码时发生错误: " + e.getMessage());
}
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/worker/JudgingWorker.java
================================================
package com.decade.doj.sandbox.worker;
import com.alibaba.fastjson.JSON;
import com.decade.doj.sandbox.domain.vo.JudgingTask;
import com.decade.doj.sandbox.service.ISandboxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 判题任务消费者
* <p>
* 使用单一调度线程从 Redis 队列轮询任务,并将任务分发到 JudgingThreadPool 执行。
* 实现 DisposableBean 接口支持优雅关闭。
*/
@Slf4j
@Component
public class JudgingWorker implements ApplicationRunner, DisposableBean {
private final StringRedisTemplate redisTemplate;
private final ISandboxService sandboxService;
private final ThreadPoolTaskExecutor judgingExecutor;
private static final String JUDGING_QUEUE_KEY = "judging:queue";
private static final int POLL_TIMEOUT_SECONDS = 5;
private final AtomicBoolean running = new AtomicBoolean(true);
private Thread schedulerThread;
public JudgingWorker(
StringRedisTemplate redisTemplate,
ISandboxService sandboxService,
@Qualifier("JudgingThreadPool") ThreadPoolTaskExecutor judgingExecutor
) {
this.redisTemplate = redisTemplate;
this.sandboxService = sandboxService;
this.judgingExecutor = judgingExecutor;
}
@Override
public void run(ApplicationArguments args) {
schedulerThread = new Thread(this::pollAndDispatch, "JudgingScheduler");
schedulerThread.start();
log.info("判题调度器已启动,任务将分发到 JudgingThreadPool 执行");
}
/**
* 轮询 Redis 队列并分发任务到线程池
*/
private void pollAndDispatch() {
while (running.get()) {
try {
String taskJson = redisTemplate.opsForList()
.rightPop(JUDGING_QUEUE_KEY, POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (taskJson != null) {
JudgingTask task = JSON.parseObject(taskJson, JudgingTask.class);
log.info("收到判题任务: submissionId={}, problemId={}",
task.getSubmissionId(), task.getProblemId());
// 分发到线程池异步执行
judgingExecutor.execute(() -> executeTask(task));
}
} catch (Exception e) {
if (running.get()) {
log.error("轮询判题队列出现异常", e);
// 短暂休眠避免错误风暴
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
log.info("判题调度器已停止轮询");
}
/**
* 执行单个判题任务
*/
private void executeTask(JudgingTask task) {
try {
log.debug("开始执行判题任务: submissionId={}", task.getSubmissionId());
sandboxService.execute(task);
log.debug("判题任务执行完成: submissionId={}", task.getSubmissionId());
} catch (Exception e) {
log.error("执行判题任务异常: submissionId={}, problemId={}",
task.getSubmissionId(), task.getProblemId(), e);
}
}
@Override
public void destroy() {
log.info("正在关闭判题调度器...");
running.set(false);
if (schedulerThread != null) {
schedulerThread.interrupt();
try {
// 等待调度线程终止
schedulerThread.join(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
log.info("判题调度器已关闭,线程池将由 Spring 容器管理关闭");
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/resources/application.yaml
================================================
server:
port: ${doj.port.sandbox-service}
management:
endpoints:
web:
exposure:
include: health,prometheus
doj:
mq:
host: ${DOJ_MQ_HOST:127.0.0.1}
redis:
host: ${DOJ_REDIS_HOST:127.0.0.1}
swagger:
title: 沙箱接口文档
scan: com.decade.doj.sandbox.controller
================================================
FILE: DOJ-BE/sandbox-service/src/main/resources/bootstrap.yaml
================================================
spring:
application:
name: sandbox-service
profiles:
active: dev, common
cloud:
nacos:
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
config:
file-extension: yaml
shared-configs:
- dataId: shared-jdbc.yaml
- dataId: shared-swagger.yaml
- dataId: shared-jwt.yaml
- dataId: shared-rabbitmq.yaml
================================================
FILE: DOJ-BE/sandbox-service/src/main/resources/test/20240906.py
================================================
ans = []
res = 0
for i in range(10000000):
ans.append(i)
res += i
print(f"{res} test success nopt failed!")
================================================
FILE: DOJ-BE/sandbox-service/src/main/resources/test/main.cpp
================================================
#include <iostream>
#include <vector>
using namespace std;
int main() {
long long ans = 0;
vector<int> v;
for (int i = 0; i < 10000000; i++) {
ans += i;
v.push_back(i);
}
cout << ans << " test success!" << endl;
return 0;
}
================================================
FILE: DOJ-BE/sandbox-service/src/main/resources/test/main.java
================================================
import java.util.List;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
long ans = 0;
List<Integer> res = new ArrayList<>();
for (int i = 0;i < 10000000;i++){
ans += i;
res.add(i);
}
System.out.println(ans+" test success!");
}
}
================================================
FILE: DOJ-BE/sandbox-service/src/test/java/com/decade/doj/sandbox/SandboxTest.java
================================================
package com.decade.doj.sandbox;
import com.decade.doj.sandbox.domain.vo.ExecuteMessage;
import com.decade.doj.sandbox.service.ISandboxService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class SandboxTest {
@Autowired
private ISandboxService sandboxService;
@Test
public void testRunCode() {
String filePath = "/Users/qzj/Desktop/Development/D-OnlineJudge/static/docker-sandbox/python/Main.py"; // 替换为实际的文件路径
String filename = "Main.py"; // 替换为实际的文件名
String lang = "python";
// try {
// ExecuteMessage res = sandboxService.runCodeInSandbox(filePath, filename, lang);
// System.out.println(res);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
================================================
FILE: DOJ-BE/submission-service/Dockerfile
================================================
# Stage 1: Build the application
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
# Copy all pom.xml files first to leverage Docker cache
COPY DOJ-BE/pom.xml .
COPY DOJ-BE/common/pom.xml ./common/
COPY DOJ-BE/gateway-service/pom.xml ./gateway-service/
COPY DOJ-BE/problem-service/pom.xml ./problem-service/
COPY DOJ-BE/sandbox-service/pom.xml ./sandbox-service/
COPY DOJ-BE/submission-service/pom.xml ./submission-service/
COPY DOJ-BE/user-service/pom.xml ./user-service/
# Download all dependencies
RUN mvn dependency:go-offline
# Copy all source code
COPY DOJ-BE/common/src ./common/src
COPY DOJ-BE/submission-service/src ./submission-service/src
# Build the specific service
RUN mvn -f ./pom.xml -pl submission-service -am clean package -DskipTests
RUN ls -al ./submission-service/target
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre
WORKDIR /app
# Copy the executable jar from the build stage
COPY --from=build /app/submission-service/target/submission-service.jar ./app.jar
EXPOSE 8084
ENTRYPOINT ["java", "-jar", "./app.jar"]
================================================
FILE: DOJ-BE/submission-service/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.decade.doj</groupId>
<artifactId>submission-service</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>
<!--common-->
<dependency>
<groupId>com.decade</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/SubmissionApplication.java
================================================
package com.decade.doj.submission;
import com.decade.doj.common.config.custom.DefaultFeignConfig;
import com.decade.doj.common.config.custom.JwtTool;
import com.decade.doj.common.config.custom.MVCConfig;
import com.decade.doj.common.config.custom.MybatisConfig;
import com.decade.doj.common.interceptor.AdminCheckInterceptor;
import com.decade.doj.common.interceptor.IdentityInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@MapperScan("com.decade.doj.submission.mapper")
@EnableFeignClients(basePackages = "com.decade.doj.common.client", defaultConfiguration = DefaultFeignConfig.class)
@Import({JwtTool.class, MVCConfig.class, MybatisConfig.class, IdentityInterceptor.class, AdminCheckInterceptor.class})
public class SubmissionApplication {
public static void main(String[] args) {
SpringApplication.run(SubmissionApplication.class, args);
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/MqConfig.java
================================================
package com.decade.doj.submission.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("doj.topic");
}
@Bean
public Queue judgingResultQueue() {
return new Queue("judging.result.queue");
}
@Bean
public Binding binding() {
return BindingBuilder.bind(judgingResultQueue()).to(topicExchange()).with("judging.result");
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/WebSocketConfig.java
================================================
package com.decade.doj.submission.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
// 让 @ServerEndpoint 生效
return new ServerEndpointExporter();
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/controller/SubmissionController.java
================================================
package com.decade.doj.submission.controller;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.vo.ExecuteMessage;
import com.decade.doj.common.domain.vo.SubmissionStatsVO;
import com.decade.doj.common.utils.UserContext;
import com.decade.doj.submission.domain.dto.SubmissionPageQueryDTO;
import com.decade.doj.submission.domain.po.Submission;
import com.decade.doj.submission.service.ISubmissionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/submission")
@Tag(name = "提交相关接口")
@Slf4j
@RequiredArgsConstructor
public class SubmissionController {
private final ISubmissionService submissionService;
@PostMapping("/submit")
@Operation(summary = "提交记录")
public R<Long> submit(@RequestBody Submission submission) {
submissionService.save(submission);
return R.ok(submission.getId());
}
@GetMapping("/page")
@Operation(summary = "分页获取提交列表")
public R<PageDTO<Submission>> page(SubmissionPageQueryDTO problemPageQueryDTO) {
PageDTO<Submission> res = submissionService.pageQuery(problemPageQueryDTO);
return R.ok(res);
}
@GetMapping("/stats")
@Operation(summary = "获取提交统计")
public R<SubmissionStatsVO> getStats() {
return R.ok(submissionService.getStats());
}
@GetMapping("/match/{id}")
@Operation(summary = "获取当前用户指定问题的提交详情")
public R<Integer> getById(@PathVariable String id) {
List<Submission> submissions = submissionService.lambdaQuery()
.eq(Submission::getProblemId, id)
.list();
int f = 0;
for (Submission submission : submissions) {
if (submission.getUserId() != null && submission.getUserId().equals(UserContext.getCurrentUser())) {
f = 1;
if (ExecuteMessage.getStatus(submission.getExitValue()).equals("Accepted")) {
return R.ok(1); // 已经提交过且通过
}
}
}
if (f == 1) {
return R.ok(2); // 已经提交过但未通过
}
return R.ok(0);
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/dto/SubmissionPageQueryDTO.java
================================================
package com.decade.doj.submission.domain.dto;
import com.decade.doj.common.domain.PageQueryDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class SubmissionPageQueryDTO extends PageQueryDTO {
@Schema(description = "提交ID")
private Long submissionId;
@Schema(description = "用户ID")
private String userId;
@Schema(description = "题目ID")
private String problemId;
@Schema(description = "语言")
private String language;
@Schema(description = "判题状态")
private String status;
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/po/Submission.java
================================================
package com.decade.doj.submission.domain.po;
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 java.util.Date;
import lombok.Data;
/**
* 代码提交记录表
* @TableName submission
*/
@TableName(value ="submission")
@Data
public class Submission {
/**
* 提交记录主键
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户ID(来源于 doj_user.user)
*/
private Long userId;
/**
* 题目ID(来源于 doj_problem.problem)
*/
private Long problemId;
private String userName;
private String problemName;
/**
* 编程语言
*/
private String language;
/**
* 提交的代码文本内容
*/
private String code;
/**
* 程序退出码
*/
private Integer exitValue;
/**
* 判题状态
*/
private String status;
/**
* 判题详细信息
*/
private String message;
/**
* 运行时间(单位:秒)
*/
private Double time;
/**
* 内存使用(单位:KB)
*/
private Long memory;
/**
* 提交时间
*/
private Date submitTime;
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mapper/SubmissionMapper.java
================================================
package com.decade.doj.submission.mapper;
import com.decade.doj.submission.domain.po.Submission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author qzj
* @description 针对表【submission(代码提交记录表)】的数据库操作Mapper
* @createDate 2025-06-05 13:28:52
* @Entity com.decade.doj.submission.domain.po.Submission
*/
public interface SubmissionMapper extends BaseMapper<Submission> {
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mq/ResultListener.java
================================================
package com.decade.doj.submission.mq;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.decade.doj.common.client.ProblemClient;
import com.decade.doj.common.client.UserClient;
import com.decade.doj.common.domain.po.Problem;
import com.decade.doj.common.domain.po.User;
import com.decade.doj.common.utils.UserContext;
import com.decade.doj.submission.domain.po.Submission;
import com.decade.doj.submission.service.ISubmissionService;
import com.decade.doj.submission.websocket.SubmissionWSServer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor
public class ResultListener {
private final ISubmissionService submissionService;
private final ProblemClient problemClient;
private final UserClient userClient;
private final RabbitTemplate rabbitTemplate;
@RabbitListener(queues = "judging.result.queue")
public void onMessage(Map<String, Object> message) {
log.info("从 RabbitMQ 收到判题结果消息: {}", message);
try {
Long submissionId = Long.valueOf(message.get("submissionId").toString());
Map<String, Object> executeMessage = (Map<String, Object>) message.get("executeMessage");
if (executeMessage == null) {
log.error("消息格式错误,缺少 submissionId 或 executeMessage");
return;
}
// 1. 更新数据库
Submission submission = submissionService.getById(submissionId);
UserContext.setCurrentUser(submission.getUserId());
User user = userClient.getUser(submission.getUserId()).getData();
if (user == null) {
log.error("用户 {} 不存在,无法更新提交记录", submission.getUserId());
return;
}
submission.setUserName(user.getUsername());
Problem problem = problemClient.getProblemById(submission.getProblemId()).getData();
if (problem == null) {
log.error("题目 {} 不存在,无法更新提交记录", submission.getProblemId());
return;
}
submission.setProblemName(problem.getName());
submission.setId(submissionId);
submission.setStatus((String) executeMessage.get("status"));
submission.setExitValue((Integer) executeMessage.get("exitValue"));
submission.setMessage((String) executeMessage.get("message"));
submission.setTime((Double) executeMessage.get("time"));
submission.setMemory(((Number) executeMessage.get("memory")).longValue());
submissionService.updateById(submission);
log.info("提交记录 {} 已更新", submissionId);
// 2. 通过 WebSocket 推送给前端
SubmissionWSServer.sendMessage(submissionId, JSON.toJSONString(submission));
// 3. 发送 RabbitMQ 事件,用于更新用户和题目统计
Map<String, Object> submissionMessage = Map.of(
"problemId", submission.getProblemId(),
"isAccepted", "Accepted".equals(submission.getStatus())
);
rabbitTemplate.convertAndSend("doj.topic", "submission.created", submissionMessage);
if ("Accepted".equals(submission.getStatus())) {
long acCount = submissionService.lambdaQuery()
.eq(Submission::getUserId, submission.getUserId())
.eq(Submission::getProblemId, submission.getProblemId())
.eq(Submission::getStatus, "Accepted")
.count();
if (acCount == 1) {
Map<String, Long> solvedMessage = Map.of(
"userId", submission.getUserId(),
"problemId", submission.getProblemId()
);
rabbitTemplate.convertAndSend("doj.topic", "problem.solved", solvedMessage);
log.info("用户 {} 首次 AC 题目 {},已发送 problem.solved 事件", submission.getUserId(), submission.getProblemId());
}
}
} catch (Exception e) {
log.error("处理判题结果消息时发生异常", e);
}
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/ISubmissionService.java
================================================
package com.decade.doj.submission.service;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.vo.SubmissionStatsVO;
import com.decade.doj.submission.domain.dto.SubmissionPageQueryDTO;
import com.decade.doj.submission.domain.po.Submission;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author qzj
* @description 针对表【submission(代码提交记录表)】的数据库操作Service
* @createDate 2025-06-05 13:28:52
*/
public interface ISubmissionService extends IService<Submission> {
PageDTO<Submission> pageQuery(SubmissionPageQueryDTO submissionPageQueryDTO);
SubmissionStatsVO getStats();
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/impl/ISubmissionServiceImpl.java
================================================
package com.decade.doj.submission.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.vo.SubmissionStatsVO;
import com.decade.doj.submission.domain.dto.SubmissionPageQueryDTO;
import com.decade.doj.submission.domain.po.Submission;
import com.decade.doj.submission.service.ISubmissionService;
import com.decade.doj.submission.mapper.SubmissionMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author qzj
* @description 针对表【submission(代码提交记录表)】的数据库操作Service实现
* @createDate 2025-06-05 13:28:52
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class ISubmissionServiceImpl extends ServiceImpl<SubmissionMapper, Submission>
implements ISubmissionService {
public PageDTO<Submission> pageQuery(SubmissionPageQueryDTO submissionPageQueryDTO) {
// 如果 submissionId 存在,则执行精确查询
if (submissionPageQueryDTO.getSubmissionId() != null) {
Submission submission = this.getById(submissionPageQueryDTO.getSubmissionId());
if (submission == null) {
return PageDTO.empty(0L, 0L);
}
return PageDTO.fullPage(1L, 1L, List.of(submission));
}
log.info("分页查询提交列表: {}", submissionPageQueryDTO);
log.info("userId={}, problemId={}, status={}, language={}",
submissionPageQueryDTO.getUserId(),
submissionPageQueryDTO.getProblemId(),
submissionPageQueryDTO.getStatus(),
submissionPageQueryDTO.getLanguage());
String user = submissionPageQueryDTO.getUserId();
String problem = submissionPageQueryDTO.getProblemId();
Page<Submission> submissionList = lambdaQuery()
.like(user != null && !user.isBlank(), Submission::getUserName, submissionPageQueryDTO.getUserId())
.like(problem != null && !problem.isBlank(), Submission::getProblemName, submissionPageQueryDTO.getProblemId())
.eq(submissionPageQueryDTO.getStatus() != null, Submission::getStatus, submissionPageQueryDTO.getStatus())
.eq(submissionPageQueryDTO.getLanguage() != null, Submission::getLanguage, submissionPageQueryDTO.getLanguage())
.page(submissionPageQueryDTO.toMpPage("submit_time", false));
return PageDTO.fullPage(submissionList.getTotal(), submissionList.getPages(), submissionList.getRecords());
}
@Override
public SubmissionStatsVO getStats() {
// 获取总提交数
long totalSubmissions = this.count();
// 获取今日提交数
LocalDate today = LocalDate.now();
Date startOfDay = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
long todaySubmissions = this.lambdaQuery()
.ge(Submission::getSubmitTime, startOfDay)
.count();
return new SubmissionStatsVO(totalSubmissions, todaySubmissions);
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/websocket/SubmissionWSServer.java
================================================
package com.decade.doj.submission.websocket;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
@ServerEndpoint("/ws/submission")
public class SubmissionWSServer {
private static final Map<Long, Session> ONLINE_SESSIONS = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session) {
log.info("WebSocket 连接已建立: {}", session.getId());
}
@OnMessage
public void onMessage(String message, Session session) {
try {
Map<String, Object> data = JSON.parseObject(message);
Long submissionId = Long.valueOf(data.get("submissionId").toString());
ONLINE_SESSIONS.put(submissionId, session);
log.info("submissionId: {} 已订阅 WebSocket 通知", submissionId);
} catch (Exception e) {
log.error("处理 WebSocket 消息时出错: {}", message, e);
}
}
@OnClose
public void onClose(Session session) {
// 清理无效的 session
ONLINE_SESSIONS.values().removeIf(s -> !s.isOpen());
log.info("WebSocket 连接已关闭: {}", session.getId());
}
public static void sendMessage(Long submissionId, String message) {
Session session = ONLINE_SESSIONS.get(submissionId);
if (session != null && session.isOpen()) {
try {
session.getBasicRemote().sendText(message);
log.info("成功向 submissionId: {} 推送消息", submissionId);
// 推送成功后可以移除,避免重复推送
ONLINE_SESSIONS.remove(submissionId);
} catch (IOException e) {
log.error("向 submissionId: {} 推送消息失败", submissionId, e);
}
}
}
}
================================================
FILE: DOJ-BE/submission-service/src/main/resources/application.yaml
================================================
server:
port: ${doj.port.submission-service}
management:
endpoints:
web:
exposure:
include: health,prometheus
doj:
db:
host: ${DOJ_DB_HOST:127.0.0.1}
name: doj_submission
user: ${DOJ_DB_USER:root}
pwd: ${DOJ_DB_PWD:123}
mq:
host: ${DOJ_MQ_HOST:127.0.0.1}
swagger:
title: 沙箱接口文档
scan: com.decade.doj.submission.controller
================================================
FILE: DOJ-BE/submission-service/src/main/resources/bootstrap.yaml
================================================
spring:
application:
name: submission-service
profiles:
active: dev, common
cloud:
nacos:
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
config:
file-extension: yaml
shared-configs:
- dataId: shared-jdbc.yaml
- dataId: shared-swagger.yaml
- dataId: shared-jwt.yaml
- dataId: shared-rabbitmq.yaml
================================================
FILE: DOJ-BE/submission-service/src/main/resources/com/decade/doj/submission/mapper/SubmissionMapper.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.decade.doj.submission.mapper.SubmissionMapper">
<resultMap id="BaseResultMap" type="com.decade.doj.submission.domain.po.Submission">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="problemId" column="problem_id" />
<result property="language" column="language" />
<result property="code" column="code" />
<result property="exitValue" column="exit_value" />
<result property="status" column="status" />
<result property="message" column="message" />
<result property="time" column="time" />
<result property="memory" column="memory" />
<result property="submitTime" column="submit_time" />
</resultMap>
<sql id="Base_Column_List">
id,user_id,problem_id,language,code,exit_value,
status,message,time,memory,submit_time
</sql>
</mapper>
================================================
FILE: DOJ-BE/submission-service/src/test/java/com/decade/doj/submission/SubmissionTest.java
================================================
package com.decade.doj.submission;
import com.decade.doj.submission.domain.po.Submission;
import com.decade.doj.submission.service.ISubmissionService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
public class SubmissionTest {
@Autowired
private ISubmissionService submissionService;
@Test
public void testSubmissionService() {
Submission submission = new Submission();
submission.setUserId(3L);
submission.setProblemId(1L);
submission.setLanguage("Java");
submission.setCode("public class Solution { public int add(int a, int b) { return a + b; } }");
submission.setStatus("Wrong");
submission.setMessage("Compilation Error");
submission.setMemory(12L);
submission.setTime(100.0);
submission.setSubmitTime(new Date());
boolean result = submissionService.save(submission);
assert result : "Submission should be saved successfully";
}
}
================================================
FILE: DOJ-BE/user-service/Dockerfile
================================================
# Stage 1: Build the application
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
# Copy all pom.xml files first to leverage Docker cache
COPY DOJ-BE/pom.xml .
COPY DOJ-BE/common/pom.xml ./common/
COPY DOJ-BE/gateway-service/pom.xml ./gateway-service/
COPY DOJ-BE/problem-service/pom.xml ./problem-service/
COPY DOJ-BE/sandbox-service/pom.xml ./sandbox-service/
COPY DOJ-BE/submission-service/pom.xml ./submission-service/
COPY DOJ-BE/user-service/pom.xml ./user-service/
# Download all dependencies
RUN mvn dependency:go-offline
# Copy all source code
COPY DOJ-BE/common/src ./common/src
COPY DOJ-BE/user-service/src ./user-service/src
# Build the specific service
RUN mvn -f ./pom.xml -pl user-service -am clean package -DskipTests
RUN ls -al ./user-service/target
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre
WORKDIR /app
# Copy the executable jar from the build stage
COPY --from=build /app/user-service/target/user-service.jar ./app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "./app.jar"]
================================================
FILE: DOJ-BE/user-service/pom.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>com.decade</groupId>
<artifactId>DOJ-BE</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>user-service</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>
<!--common-->
<dependency>
<groupId>com.decade</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- <!–redis–>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!-- </dependency>-->
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.mybatis</groupId>-->
<!-- <artifactId>mybatis-spring</artifactId>-->
<!-- <version>3.0.2</version>-->
<!-- </dependency>-->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/UserApplication.java
================================================
package com.decade.doj.user;
import com.decade.doj.common.config.custom.DefaultFeignConfig;
import com.decade.doj.common.config.custom.JwtTool;
import com.decade.doj.common.config.custom.MVCConfig;
import com.decade.doj.common.config.custom.MybatisConfig;
import com.decade.doj.common.config.properties.AppNameProperties;
import com.decade.doj.common.interceptor.AdminCheckInterceptor;
import com.decade.doj.common.interceptor.IdentityInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@MapperScan("com.decade.doj.user.mapper")
@EnableFeignClients(basePackages = "com.decade.doj.common.client", defaultConfiguration = DefaultFeignConfig.class)
@Import({JwtTool.class, MVCConfig.class, MybatisConfig.class, IdentityInterceptor.class, AppNameProperties.class, AdminCheckInterceptor.class})
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
================================================
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/AnnouncementController.java
================================================
package com.decade.doj.user.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.decade.doj.common.annotation.AdminRequired;
import com.decade.doj.common.domain.R;
import com.decade.doj.user.domain.po.Announcement;
import com.decade.doj.user.service.IAnnouncementService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
@RestController
@RequestMapping("/user/announcement")
@Tag(name = "公告接口")
@RequiredArgsConstructor
public class AnnouncementController {
private final IAnnouncementService announcementService;
@GetMapping("/list")
@Operation(summary = "获取公告列表")
public R<List<Announcement>> list() {
return R.ok(announcementService.list(new LambdaQueryWrapper<Announcement>()
.eq(Announcement::getDeleted, false)
.orderByDesc(Announcement::getCreateTime)));
}
// Admin endpoints (simplified, assuming standard auth/admin check or internal use if gateway handles it)
// For now, I'll allow creation to support the requirement
@PostMapping
@AdminRequired
@Operation(summary = "新增公告")
public R<Void> create(@RequestBody Announcement announcement) {
announcement.setCreateTime(LocalDateTime.now());
announcement.setUpdateTime(LocalDateTime.now());
announcementService.save(announcement);
return R.ok();
}
}
================================================
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/UserController.java
================================================
package com.decade.doj.user.controller;
import cn.hutool.core.bean.BeanUtil;
import com.decade.doj.common.config.properties.ResourceProperties;
import com.decade.doj.common.domain.PageQueryDTO;
import com.decade.doj.common.domain.PageDTO;
import com.decade.doj.common.domain.R;
import com.decade.doj.common.domain.vo.StatsVO;
import com.decade.doj.user.domain.dto.LoginDTO;
import com.decade.doj.user.domain.dto.RegisterDTO;
import com.decade.doj.user.domain.dto.UpdPwdDTO;
import com.decade.doj.user.domain.po.User;
import com.decade.doj.user.domain.vo.InfoVO;
import com.decade.doj.user.domain.vo.LoginVO;
import com.decade.doj.user.domain.vo.RankVO;
import com.decade.doj.user.service.IUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
/**
* <p>
* 前端控制器
* </p>
*
* @author
* @since 2024-08-26
*/
@RestController
@RequestMapping("/user")
@Tag(name = "用户相关接口")
@Slf4j
@RequiredA
gitextract_vd6v_lct/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── ci-cd.yml
├── .gitignore
├── DOJ-BE/
│ ├── .gitignore
│ ├── common/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── common/
│ │ │ │ ├── annotation/
│ │ │ │ │ └── AdminRequired.java
│ │ │ │ ├── client/
│ │ │ │ │ ├── ProblemClient.java
│ │ │ │ │ ├── SubmissionClient.java
│ │ │ │ │ └── UserClient.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── custom/
│ │ │ │ │ │ ├── DefaultFeignConfig.java
│ │ │ │ │ │ ├── JwtTool.java
│ │ │ │ │ │ ├── MVCConfig.java
│ │ │ │ │ │ ├── MybatisConfig.java
│ │ │ │ │ │ └── SecurityConfig.java
│ │ │ │ │ ├── global/
│ │ │ │ │ │ └── CommonExceptionConfig.java
│ │ │ │ │ ├── properties/
│ │ │ │ │ │ ├── AppNameProperties.java
│ │ │ │ │ │ ├── JwtProperties.java
│ │ │ │ │ │ └── ResourceProperties.java
│ │ │ │ │ └── thread/
│ │ │ │ │ └── ThreadPoolConfig.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── PageDTO.java
│ │ │ │ │ ├── PageQueryDTO.java
│ │ │ │ │ ├── R.java
│ │ │ │ │ ├── json/
│ │ │ │ │ │ └── StringListDeserializer.java
│ │ │ │ │ ├── po/
│ │ │ │ │ │ ├── Problem.java
│ │ │ │ │ │ ├── Submission.java
│ │ │ │ │ │ └── User.java
│ │ │ │ │ └── vo/
│ │ │ │ │ ├── ExecuteMessage.java
│ │ │ │ │ ├── InfoVO.java
│ │ │ │ │ ├── StatsVO.java
│ │ │ │ │ └── SubmissionStatsVO.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── BadRequestException.java
│ │ │ │ │ ├── BizIllegalException.java
│ │ │ │ │ ├── CommonException.java
│ │ │ │ │ ├── DbException.java
│ │ │ │ │ ├── ForbiddenException.java
│ │ │ │ │ └── UnauthorizedException.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── AdminCheckInterceptor.java
│ │ │ │ │ └── IdentityInterceptor.java
│ │ │ │ └── utils/
│ │ │ │ ├── LocalResource.java
│ │ │ │ └── UserContext.java
│ │ │ └── resources/
│ │ │ ├── META-INF/
│ │ │ │ ├── spring-configuration-metadata.json
│ │ │ │ └── spring.factories
│ │ │ ├── application-common.yaml
│ │ │ └── doj.jks
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── common/
│ │ └── domain/
│ │ └── po/
│ │ └── ProblemJsonTest.java
│ ├── gateway-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── gateway/
│ │ │ ├── GatewayApplication.java
│ │ │ ├── config/
│ │ │ │ └── properties/
│ │ │ │ └── AuthProperties.java
│ │ │ └── filters/
│ │ │ ├── AuthGlobalFilter.java
│ │ │ └── CrossFilter.java
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── bootstrap.yaml
│ ├── pom.xml
│ ├── problem-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── problem/
│ │ │ │ ├── ProblemApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── CacheConfig.java
│ │ │ │ │ └── ProblemIndexInitializer.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── ProblemController.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ └── ProblemDocument.java
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── ProblemPageQueryDTO.java
│ │ │ │ │ │ └── ProblemTagView.java
│ │ │ │ │ └── po/
│ │ │ │ │ ├── Problem.java
│ │ │ │ │ ├── ProblemTag.java
│ │ │ │ │ └── Tag.java
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── ProblemMapper.java
│ │ │ │ │ ├── ProblemTagMapper.java
│ │ │ │ │ └── TagMapper.java
│ │ │ │ ├── mq/
│ │ │ │ │ ├── MqConfig.java
│ │ │ │ │ └── StatsUpdateListener.java
│ │ │ │ ├── repository/
│ │ │ │ │ └── ProblemRepository.java
│ │ │ │ └── service/
│ │ │ │ ├── IProblemService.java
│ │ │ │ └── impl/
│ │ │ │ └── ProblemServiceImpl.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ ├── es/
│ │ │ │ └── problem-settings.json
│ │ │ ├── mapper/
│ │ │ │ └── ProblemMapper.xml
│ │ │ └── sql/
│ │ │ └── problem_rebuild.sql
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── problem/
│ │ └── TestProblem.java
│ ├── sandbox-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── sandbox/
│ │ │ │ ├── SandboxApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ └── MqConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── SandboxController.java
│ │ │ │ ├── domain/
│ │ │ │ │ └── vo/
│ │ │ │ │ ├── ExecuteMessage.java
│ │ │ │ │ └── JudgingTask.java
│ │ │ │ ├── enums/
│ │ │ │ │ └── LanguageEnum.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ISandboxService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ └── SandboxService.java
│ │ │ │ └── worker/
│ │ │ │ └── JudgingWorker.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ └── test/
│ │ │ ├── 20240906.py
│ │ │ ├── main.cpp
│ │ │ └── main.java
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── sandbox/
│ │ └── SandboxTest.java
│ ├── submission-service/
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── decade/
│ │ │ │ └── doj/
│ │ │ │ └── submission/
│ │ │ │ ├── SubmissionApplication.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── MqConfig.java
│ │ │ │ │ └── WebSocketConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── SubmissionController.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── SubmissionPageQueryDTO.java
│ │ │ │ │ └── po/
│ │ │ │ │ └── Submission.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── SubmissionMapper.java
│ │ │ │ ├── mq/
│ │ │ │ │ └── ResultListener.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ISubmissionService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ └── ISubmissionServiceImpl.java
│ │ │ │ └── websocket/
│ │ │ │ └── SubmissionWSServer.java
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ ├── bootstrap.yaml
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── submission/
│ │ │ └── mapper/
│ │ │ └── SubmissionMapper.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── decade/
│ │ └── doj/
│ │ └── submission/
│ │ └── SubmissionTest.java
│ └── user-service/
│ ├── Dockerfile
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── decade/
│ │ │ └── doj/
│ │ │ └── user/
│ │ │ ├── UserApplication.java
│ │ │ ├── controller/
│ │ │ │ ├── AnnouncementController.java
│ │ │ │ └── UserController.java
│ │ │ ├── domain/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── LoginDTO.java
│ │ │ │ │ ├── RegisterDTO.java
│ │ │ │ │ └── UpdPwdDTO.java
│ │ │ │ ├── po/
│ │ │ │ │ ├── Announcement.java
│ │ │ │ │ └── User.java
│ │ │ │ └── vo/
│ │ │ │ ├── InfoVO.java
│ │ │ │ ├── LoginVO.java
│ │ │ │ └── RankVO.java
│ │ │ ├── mapper/
│ │ │ │ ├── AnnouncementMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── mq/
│ │ │ │ ├── MqConfig.java
│ │ │ │ └── StatsUpdateListener.java
│ │ │ ├── service/
│ │ │ │ ├── IAnnouncementService.java
│ │ │ │ ├── IUserService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AnnouncementServiceImpl.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ └── utils/
│ │ │ └── AESTool.java
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── bootstrap.yaml
│ └── test/
│ └── java/
│ └── com/
│ └── decade/
│ └── doj/
│ └── user/
│ └── UserTest.java
├── DOJ-FE/
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── index.html
│ ├── nginx.conf
│ ├── package.json
│ ├── run-docker.sh
│ ├── scripts/
│ │ └── preinstall.js
│ ├── src/
│ │ ├── App.vue
│ │ ├── api/
│ │ │ ├── announcement/
│ │ │ │ └── index.ts
│ │ │ ├── base/
│ │ │ │ └── index.ts
│ │ │ ├── problem/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ ├── stats/
│ │ │ │ └── index.ts
│ │ │ ├── submission/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ ├── submit/
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ └── user/
│ │ │ ├── index.ts
│ │ │ └── type.ts
│ │ ├── components/
│ │ │ ├── CodeEditor/
│ │ │ │ ├── codeEditor.vue
│ │ │ │ ├── index.vue
│ │ │ │ ├── lang-code/
│ │ │ │ │ ├── cpp/
│ │ │ │ │ │ ├── cpp.cpp
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── java/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── java.java
│ │ │ │ │ └── python/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── python.py
│ │ │ │ ├── languages.ts
│ │ │ │ ├── problemEditor.vue
│ │ │ │ ├── showEditor.vue
│ │ │ │ ├── themes.ts
│ │ │ │ └── toolbar.vue
│ │ │ ├── SvgIcon/
│ │ │ │ └── index.vue
│ │ │ └── index.ts
│ │ ├── main.ts
│ │ ├── router/
│ │ │ └── index.ts
│ │ ├── stores/
│ │ │ └── userStore/
│ │ │ └── index.ts
│ │ ├── styles/
│ │ │ ├── index.scss
│ │ │ ├── theme.scss
│ │ │ └── variable.scss
│ │ ├── utils/
│ │ │ ├── markdown.ts
│ │ │ ├── request.ts
│ │ │ └── websocket.ts
│ │ ├── views/
│ │ │ ├── Admin/
│ │ │ │ ├── Problem/
│ │ │ │ │ ├── components/
│ │ │ │ │ │ └── ProblemDialog.vue
│ │ │ │ │ └── index.vue
│ │ │ │ └── index.vue
│ │ │ ├── Home/
│ │ │ │ └── index.vue
│ │ │ ├── Layout/
│ │ │ │ ├── components/
│ │ │ │ │ ├── LayoutFixed.vue
│ │ │ │ │ └── LayoutFooter.vue
│ │ │ │ └── index.vue
│ │ │ ├── OnlineEditor/
│ │ │ │ └── index.vue
│ │ │ ├── Problem/
│ │ │ │ ├── detail/
│ │ │ │ │ └── index.vue
│ │ │ │ └── index.vue
│ │ │ ├── Rankings/
│ │ │ │ └── index.vue
│ │ │ ├── Status/
│ │ │ │ └── index.vue
│ │ │ └── User/
│ │ │ ├── home/
│ │ │ │ └── index.vue
│ │ │ ├── info/
│ │ │ │ └── index.vue
│ │ │ ├── login/
│ │ │ │ └── index.vue
│ │ │ └── register/
│ │ │ └── index.vue
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── LICENSE
├── README.md
├── docker-compose-monitoring.yml
├── docker-compose-service.yml
├── docs/
│ ├── 0.build.md
│ ├── 0.re.md
│ ├── 0.technology.md
│ ├── 1.docker部署.md
│ ├── 2.Common项目配置.md
│ ├── 3.Gateway项目配置.md
│ ├── 4.User-service项目配置.md
│ ├── 5.Problem-service项目配置.md
│ ├── 6.Submission-service项目配置.md
│ ├── 7.Sandbox-service项目配置.md
│ └── SQL/
│ ├── doj_problem.sql
│ ├── doj_submission.sql
│ ├── doj_user.sql
│ └── nacos.sql
└── prometheus/
├── grafana-dashboard.json
├── prometheus.yml
└── promtail-config.yml
SYMBOL INDEX (393 symbols across 125 files)
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/ProblemClient.java
type ProblemClient (line 9) | @FeignClient("problem-service")
method getProblemById (line 12) | @GetMapping("/problem/{id}")
method getCount (line 15) | @GetMapping("/problem/count")
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/SubmissionClient.java
type SubmissionClient (line 11) | @FeignClient("submission-service")
method submit (line 14) | @PostMapping("/submission/submit")
method getStats (line 17) | @GetMapping("/submission/stats")
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/client/UserClient.java
type UserClient (line 11) | @FeignClient("user-service")
method getUser (line 14) | @GetMapping("/user/{id}")
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/DefaultFeignConfig.java
class DefaultFeignConfig (line 12) | @Configuration
method feignLogLevel (line 19) | @Bean
method userInfoRequestInterceptor (line 24) | @Bean
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/JwtTool.java
class JwtTool (line 16) | @Component
method JwtTool (line 22) | public JwtTool(KeyPair keyPair) {
method createToken (line 26) | public String createToken(Long userId, Duration ttl) {
method parseToken (line 34) | public Long parseToken(String token) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MVCConfig.java
class MVCConfig (line 16) | @Configuration
method addResourceHandlers (line 27) | @Override
method addInterceptors (line 35) | @Override
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MybatisConfig.java
class MybatisConfig (line 12) | @Configuration
method mybatisPlusInterceptor (line 15) | @Bean
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/SecurityConfig.java
class SecurityConfig (line 15) | @Configuration
method keyPair (line 19) | @Bean
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/global/CommonExceptionConfig.java
class CommonExceptionConfig (line 18) | @RestControllerAdvice
method handleDbException (line 22) | @ExceptionHandler(DbException.class)
method handleBadRequestException (line 28) | @ExceptionHandler(CommonException.class)
method handleMethodArgumentNotValidException (line 35) | @ExceptionHandler(MethodArgumentNotValidException.class)
method handleBindException (line 44) | @ExceptionHandler(BindException.class)
method handleRuntimeException (line 51) | @ExceptionHandler(Exception.class)
method handleUnauthorizedException (line 57) | @ExceptionHandler(UnauthorizedException.class)
method handleForbiddenException (line 64) | @ExceptionHandler(ForbiddenException.class)
method processResponse (line 71) | private ResponseEntity<R<Void>> processResponse(CommonException e){
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/AppNameProperties.java
class AppNameProperties (line 7) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/JwtProperties.java
class JwtProperties (line 9) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/ResourceProperties.java
class ResourceProperties (line 9) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/config/thread/ThreadPoolConfig.java
class ThreadPoolConfig (line 28) | @Configuration
method runCodeThreadPoolExecutor (line 49) | @Bean("RunCodeThreadPool")
method judgingThreadPoolExecutor (line 71) | @Bean("JudgingThreadPool")
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageDTO.java
class PageDTO (line 16) | @Data
method empty (line 24) | public static <T> PageDTO<T> empty(Long total, Long pages) {
method fullPage (line 28) | public static <T> PageDTO<T> fullPage(Long total, Long pages, List<T> ...
method empty (line 32) | public static <T> PageDTO<T> empty(Page<?> page) {
method of (line 36) | public static <T> PageDTO<T> of(Page<T> page) {
method of (line 45) | public static <T,R> PageDTO<T> of(Page<R> page, Function<R, T> mapper) {
method of (line 55) | public static <T> PageDTO<T> of(Page<?> page, List<T> list) {
method of (line 59) | public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageQueryDTO.java
class PageQueryDTO (line 13) | @Data
method from (line 31) | public int from() {
method toMpPage (line 35) | public <T> Page<T> toMpPage(OrderItem... orderItems) {
method toMpPage (line 54) | public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
method toMpPageDefaultSortByCreateTimeDesc (line 67) | public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/R.java
class R (line 7) | @Data
method ok (line 13) | public static R<Void> ok() {
method ok (line 17) | public static <T> R<T> ok(T data) {
method error (line 21) | public static <T> R<T> error(String msg) {
method error (line 25) | public static <T> R<T> error(int code, String msg) {
method error (line 29) | public static <T> R<T> error(CommonException e) {
method R (line 33) | public R() {
method R (line 36) | public R(int code, String msg, T data) {
method success (line 42) | public boolean success(){
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/json/StringListDeserializer.java
class StringListDeserializer (line 12) | public class StringListDeserializer extends JsonDeserializer<List<String...
method deserialize (line 14) | @Override
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Problem.java
class Problem (line 20) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Submission.java
class Submission (line 8) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/User.java
class User (line 18) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/ExecuteMessage.java
class ExecuteMessage (line 11) | @Data
method getStatus (line 40) | public static String getStatus(Integer exitValue) {
method show (line 47) | public static boolean show(Integer exitValue) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/InfoVO.java
class InfoVO (line 8) | @EqualsAndHashCode(callSuper = true)
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/StatsVO.java
class StatsVO (line 11) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/SubmissionStatsVO.java
class SubmissionStatsVO (line 10) | @Data
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BadRequestException.java
class BadRequestException (line 3) | public class BadRequestException extends CommonException{
method BadRequestException (line 5) | public BadRequestException(String message) {
method BadRequestException (line 9) | public BadRequestException(String message, Throwable cause) {
method BadRequestException (line 13) | public BadRequestException(Throwable cause) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BizIllegalException.java
class BizIllegalException (line 3) | public class BizIllegalException extends CommonException{
method BizIllegalException (line 5) | public BizIllegalException(String message) {
method BizIllegalException (line 9) | public BizIllegalException(String message, Throwable cause) {
method BizIllegalException (line 13) | public BizIllegalException(Throwable cause) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/CommonException.java
class CommonException (line 5) | @Getter
method CommonException (line 9) | public CommonException(String message, int code) {
method CommonException (line 14) | public CommonException(String message, Throwable cause, int code) {
method CommonException (line 19) | public CommonException(Throwable cause, int code) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/DbException.java
class DbException (line 3) | public class DbException extends CommonException{
method DbException (line 5) | public DbException(String message) {
method DbException (line 9) | public DbException(String message, Throwable cause) {
method DbException (line 13) | public DbException(Throwable cause) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/ForbiddenException.java
class ForbiddenException (line 3) | public class ForbiddenException extends CommonException{
method ForbiddenException (line 5) | public ForbiddenException(String message) {
method ForbiddenException (line 9) | public ForbiddenException(String message, Throwable cause) {
method ForbiddenException (line 13) | public ForbiddenException(Throwable cause) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/exception/UnauthorizedException.java
class UnauthorizedException (line 3) | public class UnauthorizedException extends CommonException{
method UnauthorizedException (line 5) | public UnauthorizedException(String message) {
method UnauthorizedException (line 9) | public UnauthorizedException(String message, Throwable cause) {
method UnauthorizedException (line 13) | public UnauthorizedException(Throwable cause) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/AdminCheckInterceptor.java
class AdminCheckInterceptor (line 19) | @Component
method setApplicationContext (line 26) | @Override
method preHandle (line 31) | @Override
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/IdentityInterceptor.java
class IdentityInterceptor (line 16) | @Slf4j
method preHandle (line 24) | @Override
method afterCompletion (line 39) | @Override
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/utils/LocalResource.java
class LocalResource (line 8) | public class LocalResource {
method getLocalFilePath (line 10) | public static String getLocalFilePath(String resourcePath) {
FILE: DOJ-BE/common/src/main/java/com/decade/doj/common/utils/UserContext.java
class UserContext (line 3) | public class UserContext {
method getCurrentUser (line 7) | public static Long getCurrentUser() {
method setCurrentUser (line 11) | public static void setCurrentUser(Long id) {
method clear (line 15) | public static void clear() {
FILE: DOJ-BE/common/src/test/java/com/decade/doj/common/domain/po/ProblemJsonTest.java
class ProblemJsonTest (line 10) | public class ProblemJsonTest {
method shouldDeserializeSampleArrays (line 12) | @Test
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/GatewayApplication.java
class GatewayApplication (line 8) | @SpringBootApplication
method main (line 11) | public static void main(String[] args) {
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/config/properties/AuthProperties.java
class AuthProperties (line 9) | @Data
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/AuthGlobalFilter.java
class AuthGlobalFilter (line 27) | @Component
method filter (line 39) | @Override
method handleUnauthorizedResponse (line 65) | public Mono<Void> handleUnauthorizedResponse(ServerWebExchange exchang...
method isExcludedPath (line 83) | private boolean isExcludedPath(String path) {
method getOrder (line 92) | @Override
FILE: DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/CrossFilter.java
class CrossFilter (line 9) | @Configuration
method corsWebFilter (line 15) | @Bean
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/ProblemApplication.java
class ProblemApplication (line 16) | @SpringBootApplication
method main (line 22) | public static void main(String[] args) {
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/CacheConfig.java
class CacheConfig (line 17) | @Configuration
method caffeineCacheManager (line 21) | @Bean
method redisCacheManager (line 32) | @Primary // 将 Redis 缓存作为默认和主要的缓存管理器
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/ProblemIndexInitializer.java
class ProblemIndexInitializer (line 12) | @Slf4j
method initIndex (line 19) | @PostConstruct
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/controller/ProblemController.java
class ProblemController (line 26) | @RestController
method syncProblemToEs (line 35) | @PostMapping("/admin/sync-es")
method reindexProblems (line 43) | @PostMapping("/admin/reindex")
method resetProblems (line 51) | @PostMapping("/admin/reset")
method createProblem (line 59) | @PostMapping
method updateProblem (line 67) | @PutMapping
method deleteProblem (line 75) | @DeleteMapping("/{id}")
method list (line 83) | @GetMapping("/list")
method getCount (line 89) | @GetMapping("/count")
method page (line 95) | @GetMapping("/page")
method getProblemById (line 102) | @GetMapping("/{id}")
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/document/ProblemDocument.java
class ProblemDocument (line 12) | @Data
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemPageQueryDTO.java
class ProblemPageQueryDTO (line 10) | @EqualsAndHashCode(callSuper = true)
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemTagView.java
class ProblemTagView (line 5) | @Data
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Problem.java
class Problem (line 24) | @Data
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/ProblemTag.java
class ProblemTag (line 12) | @Data
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Tag.java
class Tag (line 13) | @Data
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemMapper.java
type ProblemMapper (line 19) | public interface ProblemMapper extends BaseMapper<Problem> {
method selectPageWithFilters (line 21) | IPage<Problem> selectPageWithFilters(Page<Problem> page,
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemTagMapper.java
type ProblemTagMapper (line 12) | @Mapper
method selectTagsByProblemIds (line 15) | @Select({
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/TagMapper.java
type TagMapper (line 7) | @Mapper
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/MqConfig.java
class MqConfig (line 9) | @Configuration
method topicExchange (line 13) | @Bean
method problemStatsUpdateQueue (line 18) | @Bean
method statsBinding (line 23) | @Bean
method cacheUpdateExchange (line 29) | @Bean
method problemCacheUpdateQueue (line 34) | @Bean
method cacheBinding (line 40) | @Bean
method jsonMessageConverter (line 45) | @Bean
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/StatsUpdateListener.java
class StatsUpdateListener (line 11) | @Slf4j
method listenStatsUpdateQueue (line 18) | @RabbitListener(queues = "problem.stats.update.queue")
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/repository/ProblemRepository.java
type ProblemRepository (line 6) | public interface ProblemRepository extends ElasticsearchRepository<Probl...
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/IProblemService.java
type IProblemService (line 17) | public interface IProblemService extends IService<Problem> {
method pageQuery (line 19) | PageDTO<Problem> pageQuery(ProblemPageQueryDTO problemPageQueryDTO);
method updateProblemStats (line 21) | void updateProblemStats(Long problemId, boolean isAccepted);
method syncAllToElasticsearch (line 23) | int syncAllToElasticsearch();
method reindexAll (line 25) | int reindexAll();
method resetProblems (line 27) | void resetProblems();
FILE: DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/impl/ProblemServiceImpl.java
class ProblemServiceImpl (line 46) | @Service
method ProblemServiceImpl (line 60) | public ProblemServiceImpl(@Qualifier("redisCacheManager") CacheManager...
method getById (line 76) | @Override
method save (line 116) | @Override
method updateById (line 129) | @Override
method removeById (line 144) | @Override
method syncTags (line 163) | private void syncTags(Long problemId, List<String> tags) {
method syncToElasticsearch (line 197) | private void syncToElasticsearch(Problem problem) {
method deleteFromElasticsearch (line 211) | private void deleteFromElasticsearch(Long id) {
method clearCache (line 220) | private void clearCache(Serializable id) {
method onCacheUpdate (line 233) | @RabbitListener(queues = "#{@problemCacheUpdateQueue.name}")
method pageQuery (line 242) | @Override
method needElasticsearchSearch (line 281) | private boolean needElasticsearchSearch(ProblemPageQueryDTO dto) {
method searchIdsByElasticsearch (line 288) | private List<Long> searchIdsByElasticsearch(ProblemPageQueryDTO dto) {
method normalizeTags (line 318) | static List<String> normalizeTags(List<String> tags) {
method shouldResyncEs (line 332) | static boolean shouldResyncEs(long esCount, long dbCount) {
method safeEsCount (line 336) | private long safeEsCount() {
method attachTags (line 346) | private void attachTags(List<Problem> problems) {
method updateProblemStats (line 370) | @Override
method syncAllToElasticsearch (line 388) | @Override
method reindexAll (line 401) | @Override
method resetProblems (line 407) | @Override
method recreateIndex (line 417) | private void recreateIndex() {
FILE: DOJ-BE/problem-service/src/main/resources/sql/problem_rebuild.sql
type problem (line 7) | CREATE TABLE problem (
type tag (line 26) | CREATE TABLE tag (
type problem_tag (line 31) | CREATE TABLE problem_tag (
FILE: DOJ-BE/problem-service/src/test/java/com/decade/doj/problem/TestProblem.java
class TestProblem (line 11) | @SpringBootTest
method test (line 17) | @Test
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/SandboxApplication.java
class SandboxApplication (line 14) | @SpringBootApplication
method main (line 18) | public static void main(String[] args) {
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/config/MqConfig.java
class MqConfig (line 12) | @Configuration
method jsonMessageConverter (line 14) | @Bean
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/controller/SandboxController.java
class SandboxController (line 34) | @RestController
method runCode (line 49) | @PostMapping("/code")
method runProblem (line 71) | @PostMapping("/problem")
method runProblemValidate (line 96) | @PostMapping("/validate")
method saveText2File (line 142) | private String[] saveText2File(Long pid, String basePath, String folde...
method saveFile (line 164) | private String[] saveFile(MultipartFile file, String basePath, String ...
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/ExecuteMessage.java
class ExecuteMessage (line 11) | @Data
method getStatus (line 40) | public static String getStatus(Integer exitValue) {
method show (line 47) | public static boolean show(Integer exitValue) {
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/JudgingTask.java
class JudgingTask (line 6) | @Data
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/enums/LanguageEnum.java
type LanguageEnum (line 8) | @Getter
method LanguageEnum (line 36) | LanguageEnum(String runCmd,
method isInValidLanguage (line 54) | public static boolean isInValidLanguage(String lang) {
method getLanguageEnum (line 67) | public static LanguageEnum getLanguageEnum(String lang) {
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/ISandboxService.java
type ISandboxService (line 8) | public interface ISandboxService {
method runCodeInSandbox (line 10) | CompletableFuture<ExecuteMessage> runCodeInSandbox(String localPath, S...
method runCodeInSandboxWI (line 12) | CompletableFuture<ExecuteMessage> runCodeInSandboxWI(String localPath,...
method execute (line 14) | void execute(JudgingTask task);
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/impl/SandboxService.java
class SandboxService (line 32) | @Service
method SandboxService (line 39) | public SandboxService(
method buildRunCommand (line 56) | public static String buildRunCommand(String rawCmd, String baseName) {
method convertToHostPath (line 86) | private String convertToHostPath(String containerPath) {
method runCodeInSandbox (line 104) | @Override
method _runCodeInSandbox (line 112) | private ExecuteMessage _runCodeInSandbox(String filePath, String filen...
method runCodeInSandboxWI (line 201) | @Override
method execute (line 209) | @Override
method _runCodeInSandboxWI (line 222) | private ExecuteMessage _runCodeInSandboxWI(String filePath, String inp...
FILE: DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/worker/JudgingWorker.java
class JudgingWorker (line 24) | @Slf4j
method JudgingWorker (line 38) | public JudgingWorker(
method run (line 48) | @Override
method pollAndDispatch (line 58) | private void pollAndDispatch() {
method executeTask (line 91) | private void executeTask(JudgingTask task) {
method destroy (line 102) | @Override
FILE: DOJ-BE/sandbox-service/src/main/resources/test/main.cpp
function main (line 6) | int main() {
FILE: DOJ-BE/sandbox-service/src/main/resources/test/main.java
class Main (line 4) | public class Main {
method main (line 5) | public static void main(String[] args) {
FILE: DOJ-BE/sandbox-service/src/test/java/com/decade/doj/sandbox/SandboxTest.java
class SandboxTest (line 9) | @SpringBootTest
method testRunCode (line 15) | @Test
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/SubmissionApplication.java
class SubmissionApplication (line 15) | @SpringBootApplication
method main (line 20) | public static void main(String[] args) {
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/MqConfig.java
class MqConfig (line 12) | @Configuration
method topicExchange (line 15) | @Bean
method judgingResultQueue (line 20) | @Bean
method binding (line 25) | @Bean
method jsonMessageConverter (line 30) | @Bean
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/WebSocketConfig.java
class WebSocketConfig (line 7) | @Configuration
method serverEndpointExporter (line 9) | @Bean
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/controller/SubmissionController.java
class SubmissionController (line 19) | @RestController
method submit (line 28) | @PostMapping("/submit")
method page (line 35) | @GetMapping("/page")
method getStats (line 42) | @GetMapping("/stats")
method getById (line 48) | @GetMapping("/match/{id}")
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/dto/SubmissionPageQueryDTO.java
class SubmissionPageQueryDTO (line 9) | @EqualsAndHashCode(callSuper = true)
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/po/Submission.java
class Submission (line 14) | @TableName(value ="submission")
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mapper/SubmissionMapper.java
type SubmissionMapper (line 12) | public interface SubmissionMapper extends BaseMapper<Submission> {
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mq/ResultListener.java
class ResultListener (line 21) | @Slf4j
method onMessage (line 31) | @RabbitListener(queues = "judging.result.queue")
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/ISubmissionService.java
type ISubmissionService (line 14) | public interface ISubmissionService extends IService<Submission> {
method pageQuery (line 15) | PageDTO<Submission> pageQuery(SubmissionPageQueryDTO submissionPageQue...
method getStats (line 17) | SubmissionStatsVO getStats();
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/impl/ISubmissionServiceImpl.java
class ISubmissionServiceImpl (line 27) | @Service
method pageQuery (line 33) | public PageDTO<Submission> pageQuery(SubmissionPageQueryDTO submission...
method getStats (line 63) | @Override
FILE: DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/websocket/SubmissionWSServer.java
class SubmissionWSServer (line 16) | @Slf4j
method onOpen (line 23) | @OnOpen
method onMessage (line 28) | @OnMessage
method onClose (line 40) | @OnClose
method sendMessage (line 47) | public static void sendMessage(Long submissionId, String message) {
FILE: DOJ-BE/submission-service/src/test/java/com/decade/doj/submission/SubmissionTest.java
class SubmissionTest (line 11) | @SpringBootTest
method testSubmissionService (line 17) | @Test
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/UserApplication.java
class UserApplication (line 16) | @SpringBootApplication
method main (line 21) | public static void main(String[] args) {
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/AnnouncementController.java
class AnnouncementController (line 16) | @RestController
method list (line 24) | @GetMapping("/list")
method create (line 35) | @PostMapping
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/UserController.java
class UserController (line 39) | @RestController
method updatePwd (line 50) | @PutMapping("/pwd")
method uploadAvatar (line 56) | @PostMapping("/avatar")
method register (line 74) | @PostMapping("/register")
method login (line 80) | @PostMapping("/login")
method refreshToken (line 86) | @PostMapping("/refresh")
method getRankings (line 92) | @GetMapping("/rankings")
method getStats (line 98) | @GetMapping("/stats")
method getUser (line 104) | @GetMapping("/{id}")
method updateUser (line 115) | @PutMapping()
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/LoginDTO.java
class LoginDTO (line 9) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/RegisterDTO.java
class RegisterDTO (line 10) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/UpdPwdDTO.java
class UpdPwdDTO (line 9) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/po/Announcement.java
class Announcement (line 21) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/po/User.java
class User (line 19) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/InfoVO.java
class InfoVO (line 8) | @EqualsAndHashCode(callSuper = true)
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/LoginVO.java
class LoginVO (line 6) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/RankVO.java
class RankVO (line 5) | @Data
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/mapper/AnnouncementMapper.java
type AnnouncementMapper (line 14) | public interface AnnouncementMapper extends BaseMapper<Announcement> {
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/mapper/UserMapper.java
type UserMapper (line 15) | public interface UserMapper extends BaseMapper<User> {
method chooseById (line 17) | @Select("select id, username, sign, easy_solve from user where id = #{...
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/mq/MqConfig.java
class MqConfig (line 12) | @Configuration
method topicExchange (line 15) | @Bean
method statsUpdateQueue (line 20) | @Bean
method binding (line 25) | @Bean
method jsonMessageConverter (line 30) | @Bean
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/mq/StatsUpdateListener.java
class StatsUpdateListener (line 12) | @Slf4j
method listenStatsUpdateQueue (line 19) | @RabbitListener(queues = "stats.update.queue")
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/IAnnouncementService.java
type IAnnouncementService (line 14) | public interface IAnnouncementService extends IService<Announcement> {
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/IUserService.java
type IUserService (line 23) | public interface IUserService extends IService<User> {
method login (line 25) | R<LoginVO> login(LoginDTO loginDTO);
method refreshToken (line 27) | R<String> refreshToken(String refreshToken);
method register (line 29) | R register(RegisterDTO registerDTO);
method updateUser (line 31) | R updateUser(User user);
method updatePwd (line 33) | R updatePwd(UpdPwdDTO updPwdDTO);
method getRankings (line 35) | R<PageDTO<RankVO>> getRankings(PageQueryDTO pageQueryDTO);
method handleProblemSolved (line 37) | void handleProblemSolved(Long userId, Long problemId);
method getStats (line 39) | StatsVO getStats();
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/impl/AnnouncementServiceImpl.java
class AnnouncementServiceImpl (line 17) | @Service
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/impl/UserServiceImpl.java
class UserServiceImpl (line 48) | @Service
method initRankings (line 61) | @PostConstruct
method getRedisKeyPrefix (line 75) | private String getRedisKeyPrefix() {
method getRankingKey (line 79) | private String getRankingKey() {
method login (line 83) | @Override
method refreshToken (line 116) | @Override
method getRankings (line 130) | @Override
method handleProblemSolved (line 176) | @Override
method register (line 207) | @Override
method updatePwd (line 228) | @Override
method updateUser (line 244) | @Override
method getStats (line 265) | @Override
FILE: DOJ-BE/user-service/src/main/java/com/decade/doj/user/utils/AESTool.java
class AESTool (line 21) | @Component
method init (line 31) | @PostConstruct
method fnv1aHash (line 48) | public int fnv1aHash(String str) {
method _encode (line 59) | private String _encode(String data) {
method match (line 73) | public boolean match(String raw, String encoded) {
method encode (line 78) | public String encode(String data, int index) {
method decode (line 91) | public String decode(String data) {
FILE: DOJ-BE/user-service/src/test/java/com/decade/doj/user/UserTest.java
class UserTest (line 13) | @SpringBootTest
method testAes (line 25) | @Test
method testSaveUser (line 32) | @Test
FILE: DOJ-FE/src/api/announcement/index.ts
type Announcement (line 6) | interface Announcement {
type AnnouncementListResponse (line 14) | type AnnouncementListResponse = BaseResponseData & {
type API (line 18) | enum API {
FILE: DOJ-FE/src/api/base/index.ts
type BaseResponse (line 2) | type BaseResponse = {
type BaseResponseData (line 7) | type BaseResponseData = BaseResponse & {
type BasePageQueryForm (line 11) | type BasePageQueryForm = {
FILE: DOJ-FE/src/api/problem/index.ts
type API (line 7) | enum API {
FILE: DOJ-FE/src/api/problem/type.ts
type ProblemPageQueryForm (line 3) | type ProblemPageQueryForm = BasePageQueryForm & {
type ProblemType (line 11) | type ProblemType = {
type ProblemsPageResponseData (line 31) | type ProblemsPageResponseData = BaseResponse & {
type ProblemsResponseData (line 39) | type ProblemsResponseData = BaseResponse & {
type ProblemsDetailResponseData (line 43) | type ProblemsDetailResponseData = BaseResponse & {
FILE: DOJ-FE/src/api/stats/index.ts
type Stats (line 6) | interface Stats {
type StatsResponse (line 13) | type StatsResponse = BaseResponseData & {
type API (line 17) | enum API {
FILE: DOJ-FE/src/api/submission/index.ts
type API (line 7) | enum API {
FILE: DOJ-FE/src/api/submission/type.ts
type Submission (line 3) | type Submission = {
type SubmissionPageQueryForm (line 17) | type SubmissionPageQueryForm = BasePageQueryForm & {
type SubmissionsPageResponseData (line 24) | type SubmissionsPageResponseData = BaseResponse & {
type SubmissionUserMatch (line 32) | type SubmissionUserMatch = BaseResponse & {
FILE: DOJ-FE/src/api/submit/index.ts
type API (line 7) | enum API {
FILE: DOJ-FE/src/api/submit/type.ts
type submitForm (line 3) | type submitForm = {
type problemSubmitForm (line 8) | type problemSubmitForm = {
type executeMessage (line 15) | type executeMessage = {
type executeResponseData (line 23) | type executeResponseData = BaseResponse & {
type sidResponseData (line 27) | type sidResponseData = BaseResponse & {
FILE: DOJ-FE/src/api/user/index.ts
type API (line 6) | enum API {
FILE: DOJ-FE/src/api/user/type.ts
type loginForm (line 4) | type loginForm = {
type dataType (line 10) | type dataType = {
type loginResponseData (line 18) | type loginResponseData = BaseResponse & {
type registerForm (line 23) | type registerForm = {
type registerResponseData (line 30) | type registerResponseData = BaseResponse & {
type uploadAvatarResponseData (line 35) | type uploadAvatarResponseData = BaseResponse & {
type userType (line 40) | type userType = {
type userInfoResponseData (line 61) | type userInfoResponseData = BaseResponse & {
type updPwdForm (line 66) | type updPwdForm = {
FILE: DOJ-FE/src/components/CodeEditor/lang-code/cpp/cpp.cpp
function main (line 7) | int main() {
FILE: DOJ-FE/src/components/CodeEditor/lang-code/java/java.java
class Main (line 3) | public class Main {
method main (line 4) | public static void main(String[] args) {
FILE: DOJ-FE/src/components/index.ts
type GlobalComponents (line 5) | type GlobalComponents = {
method install (line 11) | install(app: App){
FILE: DOJ-FE/src/router/index.ts
method scrollBehavior (line 67) | scrollBehavior() {
FILE: DOJ-FE/src/stores/userStore/index.ts
type UserInfo (line 9) | interface UserInfo {
FILE: DOJ-FE/src/vite-env.d.ts
type ImportMetaEnv (line 12) | interface ImportMetaEnv{
type GlobalComponents (line 22) | interface GlobalComponents {
FILE: docs/SQL/doj_problem.sql
type `problem` (line 24) | CREATE TABLE `problem` (
type `problem_tag` (line 48) | CREATE TABLE `problem_tag` (
type `tag` (line 59) | CREATE TABLE `tag` (
FILE: docs/SQL/doj_submission.sql
type `submission` (line 24) | CREATE TABLE `submission` (
FILE: docs/SQL/doj_user.sql
type `announcement` (line 24) | CREATE TABLE `announcement` (
type `user` (line 39) | CREATE TABLE `user` (
FILE: docs/SQL/nacos.sql
type `config_info` (line 24) | CREATE TABLE `config_info` (
type `config_info_gray` (line 50) | CREATE TABLE `config_info_gray` (
type `config_tags_relation` (line 75) | CREATE TABLE `config_tags_relation` (
type `group_capacity` (line 92) | CREATE TABLE `group_capacity` (
type `his_config_info` (line 111) | CREATE TABLE `his_config_info` (
type `permissions` (line 139) | CREATE TABLE `permissions` (
type `roles` (line 150) | CREATE TABLE `roles` (
type `tenant_capacity` (line 160) | CREATE TABLE `tenant_capacity` (
type `tenant_info` (line 179) | CREATE TABLE `tenant_info` (
type `users` (line 197) | CREATE TABLE `users` (
Condensed preview — 225 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (658K chars).
[
{
"path": ".gitattributes",
"chars": 24,
"preview": "*.vue linguist-vendored\n"
},
{
"path": ".github/workflows/ci-cd.yml",
"chars": 1578,
"preview": "name: CI/CD for DOJ Backend\n\non:\n push:\n branches:\n - main\n\njobs:\n build-and-push:\n runs-on: ubuntu-latest\n"
},
{
"path": ".gitignore",
"chars": 241,
"preview": ".idea/\n*.iml\n.DS_Store\n**/.DS_Store\n.vscode/\n/target/\n/DOJ-BE/target/\n/DOJ-BE/.idea/\n/DOJ-FE/node_modules/\n/DOJ-FE/dist/"
},
{
"path": "DOJ-BE/.gitignore",
"chars": 503,
"preview": "target/\n!.mvn/wrapper/maven-wrapper.jar\n!**/src/main/**/target/\n!**/src/test/**/target/\n\n./logs/**/*\n\n### IntelliJ IDEA "
},
{
"path": "DOJ-BE/common/pom.xml",
"chars": 2562,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/annotation/AdminRequired.java",
"chars": 337,
"preview": "package com.decade.doj.common.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retentio"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/client/ProblemClient.java",
"chars": 518,
"preview": "package com.decade.doj.common.client;\n\nimport com.decade.doj.common.domain.R;\nimport com.decade.doj.common.domain.po.Pro"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/client/SubmissionClient.java",
"chars": 668,
"preview": "package com.decade.doj.common.client;\n\nimport com.decade.doj.common.domain.R;\nimport com.decade.doj.common.domain.po.Sub"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/client/UserClient.java",
"chars": 502,
"preview": "package com.decade.doj.common.client;\n\nimport com.decade.doj.common.domain.R;\nimport com.decade.doj.common.domain.vo.Inf"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/DefaultFeignConfig.java",
"chars": 1124,
"preview": "package com.decade.doj.common.config.custom;\n\nimport com.decade.doj.common.config.properties.JwtProperties;\nimport feign"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/JwtTool.java",
"chars": 2114,
"preview": "package com.decade.doj.common.config.custom;\n\nimport cn.hutool.core.exceptions.ValidateException;\nimport cn.hutool.jwt.J"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MVCConfig.java",
"chars": 2054,
"preview": "package com.decade.doj.common.config.custom;\n\nimport com.decade.doj.common.config.properties.ResourceProperties;\nimport "
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/MybatisConfig.java",
"chars": 1110,
"preview": "package com.decade.doj.common.config.custom;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.myb"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/custom/SecurityConfig.java",
"chars": 1270,
"preview": "package com.decade.doj.common.config.custom;\n\nimport com.decade.doj.common.config.properties.JwtProperties;\nimport org.s"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/global/CommonExceptionConfig.java",
"chars": 2914,
"preview": "package com.decade.doj.common.config.global;\n\nimport com.decade.doj.common.domain.R;\nimport com.decade.doj.common.except"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/AppNameProperties.java",
"chars": 298,
"preview": "package com.decade.doj.common.config.properties;\n\nimport lombok.Data;\nimport org.springframework.beans.factory.annotatio"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/JwtProperties.java",
"chars": 568,
"preview": "package com.decade.doj.common.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/properties/ResourceProperties.java",
"chars": 436,
"preview": "package com.decade.doj.common.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/config/thread/ThreadPoolConfig.java",
"chars": 3043,
"preview": "package com.decade.doj.common.config.thread;\n\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springfra"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageDTO.java",
"chars": 2049,
"preview": "package com.decade.doj.common.domain;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.collection.CollUtil;\ni"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/PageQueryDTO.java",
"chars": 2215,
"preview": "package com.decade.doj.common.domain;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.baomidou.mybatisplus.core.metadata"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/R.java",
"chars": 900,
"preview": "package com.decade.doj.common.domain;\n\nimport com.decade.doj.common.exception.CommonException;\nimport lombok.Data;\n\n\n@Da"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/json/StringListDeserializer.java",
"chars": 1226,
"preview": "package com.decade.doj.common.domain.json;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.c"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Problem.java",
"chars": 1143,
"preview": "package com.decade.doj.common.domain.po;\n\nimport com.decade.doj.common.domain.json.StringListDeserializer;\nimport com.fa"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/Submission.java",
"chars": 537,
"preview": "package com.decade.doj.common.domain.po;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.util.Da"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/po/User.java",
"chars": 1102,
"preview": "package com.decade.doj.common.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatis"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/ExecuteMessage.java",
"chars": 1364,
"preview": "package com.decade.doj.common.domain.vo;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.util.Ha"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/InfoVO.java",
"chars": 285,
"preview": "package com.decade.doj.common.domain.vo;\n\nimport com.decade.doj.common.domain.po.User;\nimport lombok.Data;\nimport lombok"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/StatsVO.java",
"chars": 513,
"preview": "package com.decade.doj.common.domain.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\ni"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/domain/vo/SubmissionStatsVO.java",
"chars": 358,
"preview": "package com.decade.doj.common.domain.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsCons"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BadRequestException.java",
"chars": 380,
"preview": "package com.decade.doj.common.exception;\n\npublic class BadRequestException extends CommonException{\n\n public BadReque"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/BizIllegalException.java",
"chars": 380,
"preview": "package com.decade.doj.common.exception;\n\npublic class BizIllegalException extends CommonException{\n\n public BizIlleg"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/CommonException.java",
"chars": 511,
"preview": "package com.decade.doj.common.exception;\n\nimport lombok.Getter;\n\n@Getter\npublic class CommonException extends RuntimeExc"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/DbException.java",
"chars": 348,
"preview": "package com.decade.doj.common.exception;\n\npublic class DbException extends CommonException{\n\n public DbException(Stri"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/ForbiddenException.java",
"chars": 376,
"preview": "package com.decade.doj.common.exception;\n\npublic class ForbiddenException extends CommonException{\n\n public Forbidden"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/exception/UnauthorizedException.java",
"chars": 388,
"preview": "package com.decade.doj.common.exception;\n\npublic class UnauthorizedException extends CommonException{\n\n public Unauth"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/AdminCheckInterceptor.java",
"chars": 2224,
"preview": "package com.decade.doj.common.interceptor;\n\nimport com.decade.doj.common.annotation.AdminRequired;\nimport com.decade.doj"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/interceptor/IdentityInterceptor.java",
"chars": 1662,
"preview": "package com.decade.doj.common.interceptor;\nimport cn.hutool.core.util.StrUtil;\nimport com.decade.doj.common.config.prope"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/utils/LocalResource.java",
"chars": 588,
"preview": "package com.decade.doj.common.utils;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.ClassPathReso"
},
{
"path": "DOJ-BE/common/src/main/java/com/decade/doj/common/utils/UserContext.java",
"chars": 381,
"preview": "package com.decade.doj.common.utils;\n\npublic class UserContext {\n\n private static final ThreadLocal<Long> userHolder "
},
{
"path": "DOJ-BE/common/src/main/resources/META-INF/spring-configuration-metadata.json",
"chars": 325,
"preview": "{\n \"groups\": [\n {\n \"name\": \"doj.db\"\n },\n {\n \"name\": \"doj.nacos\"\n },\n {\n \"name\": \"doj.swag"
},
{
"path": "DOJ-BE/common/src/main/resources/META-INF/spring.factories",
"chars": 124,
"preview": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n com.decade.doj.common.config.global.CommonExceptionCo"
},
{
"path": "DOJ-BE/common/src/main/resources/application-common.yaml",
"chars": 646,
"preview": "doj:\n port:\n gateway-service: ${DOJ_PORT_GATEWAY:8080}\n user-service: ${DOJ_PORT_USER:8081}\n san"
},
{
"path": "DOJ-BE/common/src/test/java/com/decade/doj/common/domain/po/ProblemJsonTest.java",
"chars": 673,
"preview": "package com.decade.doj.common.domain.po;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.a"
},
{
"path": "DOJ-BE/gateway-service/Dockerfile",
"chars": 1042,
"preview": "# Stage 1: Build the application\nFROM maven:3.8.4-openjdk-17 AS build\n\nWORKDIR /app\n\n# Copy all pom.xml files first to l"
},
{
"path": "DOJ-BE/gateway-service/pom.xml",
"chars": 2374,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/GatewayApplication.java",
"chars": 458,
"preview": "package com.decade.doj.gateway;\n\nimport com.decade.doj.common.config.custom.JwtTool;\nimport org.springframework.boot.Spr"
},
{
"path": "DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/config/properties/AuthProperties.java",
"chars": 383,
"preview": "package com.decade.doj.gateway.config.properties;\n\nimport lombok.Data;\nimport org.springframework.boot.context.propertie"
},
{
"path": "DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/AuthGlobalFilter.java",
"chars": 3769,
"preview": "package com.decade.doj.gateway.filters;\n\nimport cn.hutool.core.text.AntPathMatcher;\nimport com.decade.doj.common.config."
},
{
"path": "DOJ-BE/gateway-service/src/main/java/com/decade/doj/gateway/filters/CrossFilter.java",
"chars": 1316,
"preview": "package com.decade.doj.gateway.filters;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework."
},
{
"path": "DOJ-BE/gateway-service/src/main/resources/application.yaml",
"chars": 906,
"preview": "server:\n port: ${doj.port.gateway-service}\n\nmanagement:\n endpoints:\n web:\n exposure:\n "
},
{
"path": "DOJ-BE/gateway-service/src/main/resources/bootstrap.yaml",
"chars": 980,
"preview": "spring:\n application:\n name: gateway-service\n profiles:\n active: dev, common\n cloud:\n naco"
},
{
"path": "DOJ-BE/pom.xml",
"chars": 5489,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/problem-service/Dockerfile",
"chars": 1042,
"preview": "# Stage 1: Build the application\nFROM maven:3.8.4-openjdk-17 AS build\n\nWORKDIR /app\n\n# Copy all pom.xml files first to l"
},
{
"path": "DOJ-BE/problem-service/pom.xml",
"chars": 2533,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/ProblemApplication.java",
"chars": 1185,
"preview": "package com.decade.doj.problem;\n\nimport com.decade.doj.common.config.custom.DefaultFeignConfig;\nimport com.decade.doj.co"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/CacheConfig.java",
"chars": 1810,
"preview": "package com.decade.doj.problem.config;\n\nimport com.github.benmanes.caffeine.cache.Caffeine;\nimport org.springframework.c"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/config/ProblemIndexInitializer.java",
"chars": 948,
"preview": "package com.decade.doj.problem.config;\n\nimport com.decade.doj.problem.domain.document.ProblemDocument;\nimport lombok.Req"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/controller/ProblemController.java",
"chars": 2853,
"preview": "package com.decade.doj.problem.controller;\n\n\nimport com.decade.doj.common.annotation.AdminRequired;\nimport com.decade.do"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/document/ProblemDocument.java",
"chars": 958,
"preview": "package com.decade.doj.problem.domain.document;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoA"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemPageQueryDTO.java",
"chars": 714,
"preview": "package com.decade.doj.problem.domain.dto;\n\nimport com.decade.doj.common.domain.PageQueryDTO;\nimport io.swagger.v3.oas.a"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/dto/ProblemTagView.java",
"chars": 160,
"preview": "package com.decade.doj.problem.domain.dto;\n\nimport lombok.Data;\n\n@Data\npublic class ProblemTagView {\n\n private Long p"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Problem.java",
"chars": 2031,
"preview": "package com.decade.doj.problem.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.my"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/ProblemTag.java",
"chars": 643,
"preview": "package com.decade.doj.problem.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.my"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/domain/po/Tag.java",
"chars": 693,
"preview": "package com.decade.doj.problem.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybati"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemMapper.java",
"chars": 913,
"preview": "package com.decade.doj.problem.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.myba"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/ProblemTagMapper.java",
"chars": 930,
"preview": "package com.decade.doj.problem.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.decade.doj.pr"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mapper/TagMapper.java",
"chars": 250,
"preview": "package com.decade.doj.problem.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.decade.doj.pr"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/MqConfig.java",
"chars": 1350,
"preview": "package com.decade.doj.problem.mq;\n\nimport org.springframework.amqp.core.*;\nimport org.springframework.amqp.support.conv"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/mq/StatsUpdateListener.java",
"chars": 1302,
"preview": "package com.decade.doj.problem.mq;\n\nimport com.decade.doj.problem.service.IProblemService;\nimport lombok.RequiredArgsCon"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/repository/ProblemRepository.java",
"chars": 284,
"preview": "package com.decade.doj.problem.repository;\n\nimport com.decade.doj.problem.domain.document.ProblemDocument;\nimport org.sp"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/IProblemService.java",
"chars": 663,
"preview": "package com.decade.doj.problem.service;\n\nimport com.decade.doj.common.domain.PageDTO;\nimport com.decade.doj.common.domai"
},
{
"path": "DOJ-BE/problem-service/src/main/java/com/decade/doj/problem/service/impl/ProblemServiceImpl.java",
"chars": 15284,
"preview": "package com.decade.doj.problem.service.impl;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport "
},
{
"path": "DOJ-BE/problem-service/src/main/resources/application.yaml",
"chars": 641,
"preview": "server:\n port: ${doj.port.problem-service}\n\nmanagement:\n endpoints:\n web:\n exposure:\n "
},
{
"path": "DOJ-BE/problem-service/src/main/resources/bootstrap.yaml",
"chars": 1748,
"preview": "spring:\n application:\n name: problem-service\n profiles:\n active: dev, common\n cloud:\n naco"
},
{
"path": "DOJ-BE/problem-service/src/main/resources/es/problem-settings.json",
"chars": 381,
"preview": "{\n \"analysis\": {\n \"analyzer\": {\n \"ik_max_lowercase\": {\n \"tokenizer\": \"ik_max_word\",\n \"filter\": [\""
},
{
"path": "DOJ-BE/problem-service/src/main/resources/mapper/ProblemMapper.xml",
"chars": 4334,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/"
},
{
"path": "DOJ-BE/problem-service/src/main/resources/sql/problem_rebuild.sql",
"chars": 7377,
"preview": "SET NAMES utf8mb4;\n\nDROP TABLE IF EXISTS problem_tag;\nDROP TABLE IF EXISTS tag;\nDROP TABLE IF EXISTS problem;\n\nCREATE TA"
},
{
"path": "DOJ-BE/problem-service/src/test/java/com/decade/doj/problem/TestProblem.java",
"chars": 672,
"preview": "package com.decade.doj.problem;\n\nimport com.decade.doj.problem.domain.po.Problem;\nimport com.decade.doj.problem.service."
},
{
"path": "DOJ-BE/sandbox-service/Dockerfile",
"chars": 1122,
"preview": "# Stage 1: Build the application\nFROM maven:3.8.4-openjdk-17 AS build\n\nWORKDIR /app\n\n# Copy all pom.xml files first to l"
},
{
"path": "DOJ-BE/sandbox-service/pom.xml",
"chars": 2226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/SandboxApplication.java",
"chars": 1050,
"preview": "package com.decade.doj.sandbox;\n\nimport com.decade.doj.common.config.custom.DefaultFeignConfig;\nimport com.decade.doj.co"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/config/MqConfig.java",
"chars": 656,
"preview": "package com.decade.doj.sandbox.config;\n\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.co"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/controller/SandboxController.java",
"chars": 7538,
"preview": "package com.decade.doj.sandbox.controller;\n\nimport com.alibaba.fastjson.JSON;\nimport com.decade.doj.common.client.Proble"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/ExecuteMessage.java",
"chars": 1365,
"preview": "package com.decade.doj.sandbox.domain.vo;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.util.H"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/domain/vo/JudgingTask.java",
"chars": 333,
"preview": "package com.decade.doj.sandbox.domain.vo;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n@Data\n@Accessors(c"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/enums/LanguageEnum.java",
"chars": 2014,
"preview": "package com.decade.doj.sandbox.enums;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.*;\n\n@Getter\npublic "
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/ISandboxService.java",
"chars": 503,
"preview": "package com.decade.doj.sandbox.service;\n\nimport com.decade.doj.sandbox.domain.vo.ExecuteMessage;\nimport com.decade.doj.s"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/service/impl/SandboxService.java",
"chars": 12784,
"preview": "package com.decade.doj.sandbox.service.impl;\n\nimport cn.hutool.core.date.DateTime;\nimport com.decade.doj.common.client.P"
},
{
"path": "DOJ-BE/sandbox-service/src/main/java/com/decade/doj/sandbox/worker/JudgingWorker.java",
"chars": 3996,
"preview": "package com.decade.doj.sandbox.worker;\n\nimport com.alibaba.fastjson.JSON;\nimport com.decade.doj.sandbox.domain.vo.Judgin"
},
{
"path": "DOJ-BE/sandbox-service/src/main/resources/application.yaml",
"chars": 320,
"preview": "server:\n port: ${doj.port.sandbox-service}\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: health"
},
{
"path": "DOJ-BE/sandbox-service/src/main/resources/bootstrap.yaml",
"chars": 466,
"preview": "spring:\n application:\n name: sandbox-service\n profiles:\n active: dev, common\n cloud:\n naco"
},
{
"path": "DOJ-BE/sandbox-service/src/main/resources/test/20240906.py",
"chars": 117,
"preview": "\nans = []\nres = 0\nfor i in range(10000000):\n ans.append(i)\n res += i\n\nprint(f\"{res} test success nopt failed!\")"
},
{
"path": "DOJ-BE/sandbox-service/src/main/resources/test/main.cpp",
"chars": 265,
"preview": "#include <iostream>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n long long ans = 0;\n vector<int> v;\n "
},
{
"path": "DOJ-BE/sandbox-service/src/main/resources/test/main.java",
"chars": 341,
"preview": "import java.util.List;\nimport java.util.ArrayList;\n\npublic class Main {\n public static void main(String[] args) {\n "
},
{
"path": "DOJ-BE/sandbox-service/src/test/java/com/decade/doj/sandbox/SandboxTest.java",
"chars": 903,
"preview": "package com.decade.doj.sandbox;\n\nimport com.decade.doj.sandbox.domain.vo.ExecuteMessage;\nimport com.decade.doj.sandbox.s"
},
{
"path": "DOJ-BE/submission-service/Dockerfile",
"chars": 1060,
"preview": "# Stage 1: Build the application\nFROM maven:3.8.4-openjdk-17 AS build\n\nWORKDIR /app\n\n# Copy all pom.xml files first to l"
},
{
"path": "DOJ-BE/submission-service/pom.xml",
"chars": 2360,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/SubmissionApplication.java",
"chars": 1138,
"preview": "package com.decade.doj.submission;\n\nimport com.decade.doj.common.config.custom.DefaultFeignConfig;\nimport com.decade.doj"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/MqConfig.java",
"chars": 1024,
"preview": "package com.decade.doj.submission.config;\n\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/config/WebSocketConfig.java",
"chars": 436,
"preview": "package com.decade.doj.submission.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframewor"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/controller/SubmissionController.java",
"chars": 2333,
"preview": "package com.decade.doj.submission.controller;\n\nimport com.decade.doj.common.domain.PageDTO;\nimport com.decade.doj.common"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/dto/SubmissionPageQueryDTO.java",
"chars": 686,
"preview": "package com.decade.doj.submission.domain.dto;\n\nimport com.decade.doj.common.domain.PageQueryDTO;\nimport io.swagger.v3.oa"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/domain/po/Submission.java",
"chars": 1185,
"preview": "package com.decade.doj.submission.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.myb"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mapper/SubmissionMapper.java",
"chars": 411,
"preview": "package com.decade.doj.submission.mapper;\r\n\r\nimport com.decade.doj.submission.domain.po.Submission;\r\nimport com.baomidou"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/mq/ResultListener.java",
"chars": 4293,
"preview": "package com.decade.doj.submission.mq;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport "
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/ISubmissionService.java",
"chars": 643,
"preview": "package com.decade.doj.submission.service;\r\n\r\nimport com.decade.doj.common.domain.PageDTO;\r\nimport com.decade.doj.common"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/service/impl/ISubmissionServiceImpl.java",
"chars": 3357,
"preview": "package com.decade.doj.submission.service.impl;\r\n\r\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\r\ni"
},
{
"path": "DOJ-BE/submission-service/src/main/java/com/decade/doj/submission/websocket/SubmissionWSServer.java",
"chars": 1982,
"preview": "package com.decade.doj.submission.websocket;\n\nimport com.alibaba.fastjson.JSON;\nimport lombok.extern.slf4j.Slf4j;\nimport"
},
{
"path": "DOJ-BE/submission-service/src/main/resources/application.yaml",
"chars": 414,
"preview": "server:\n port: ${doj.port.submission-service}\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: hea"
},
{
"path": "DOJ-BE/submission-service/src/main/resources/bootstrap.yaml",
"chars": 469,
"preview": "spring:\n application:\n name: submission-service\n profiles:\n active: dev, common\n cloud:\n n"
},
{
"path": "DOJ-BE/submission-service/src/main/resources/com/decade/doj/submission/mapper/SubmissionMapper.xml",
"chars": 1159,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE mapper\r\n PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n \""
},
{
"path": "DOJ-BE/submission-service/src/test/java/com/decade/doj/submission/SubmissionTest.java",
"chars": 1110,
"preview": "package com.decade.doj.submission;\n\nimport com.decade.doj.submission.domain.po.Submission;\nimport com.decade.doj.submiss"
},
{
"path": "DOJ-BE/user-service/Dockerfile",
"chars": 1024,
"preview": "# Stage 1: Build the application\nFROM maven:3.8.4-openjdk-17 AS build\n\nWORKDIR /app\n\n# Copy all pom.xml files first to l"
},
{
"path": "DOJ-BE/user-service/pom.xml",
"chars": 2587,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/UserApplication.java",
"chars": 1205,
"preview": "package com.decade.doj.user;\n\nimport com.decade.doj.common.config.custom.DefaultFeignConfig;\nimport com.decade.doj.commo"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/AnnouncementController.java",
"chars": 1572,
"preview": "package com.decade.doj.user.controller;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimpor"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/controller/UserController.java",
"chars": 3670,
"preview": "package com.decade.doj.user.controller;\n\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.decade.doj.common.config.prope"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/LoginDTO.java",
"chars": 514,
"preview": "package com.decade.doj.user.domain.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport l"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/RegisterDTO.java",
"chars": 726,
"preview": "package com.decade.doj.user.domain.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport l"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/dto/UpdPwdDTO.java",
"chars": 526,
"preview": "package com.decade.doj.user.domain.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport l"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/po/Announcement.java",
"chars": 942,
"preview": "package com.decade.doj.user.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatispl"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/po/User.java",
"chars": 1101,
"preview": "package com.decade.doj.user.domain.po;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybati"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/InfoVO.java",
"chars": 281,
"preview": "package com.decade.doj.user.domain.vo;\n\nimport com.decade.doj.user.domain.po.User;\nimport lombok.Data;\nimport lombok.Equ"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/LoginVO.java",
"chars": 275,
"preview": "package com.decade.doj.user.domain.vo;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n@Data\n@Accessors(chai"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/domain/vo/RankVO.java",
"chars": 354,
"preview": "package com.decade.doj.user.domain.vo;\n\nimport lombok.Data;\n\n@Data\npublic class RankVO {\n private Long rank;\n priv"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/mapper/AnnouncementMapper.java",
"chars": 306,
"preview": "package com.decade.doj.user.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.decade.doj.user."
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/mapper/UserMapper.java",
"chars": 427,
"preview": "package com.decade.doj.user.mapper;\n\nimport com.decade.doj.user.domain.po.User;\nimport com.baomidou.mybatisplus.core.map"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/mq/MqConfig.java",
"chars": 1008,
"preview": "package com.decade.doj.user.mq;\n\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.Bind"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/mq/StatsUpdateListener.java",
"chars": 1045,
"preview": "package com.decade.doj.user.mq;\n\nimport com.decade.doj.common.utils.UserContext;\nimport com.decade.doj.user.service.IUse"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/IAnnouncementService.java",
"chars": 305,
"preview": "package com.decade.doj.user.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.decade.doj."
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/IUserService.java",
"chars": 1039,
"preview": "package com.decade.doj.user.service;\n\nimport com.decade.doj.common.domain.PageDTO;\nimport com.decade.doj.common.domain.P"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/impl/AnnouncementServiceImpl.java",
"chars": 541,
"preview": "package com.decade.doj.user.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport co"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/service/impl/UserServiceImpl.java",
"chars": 10797,
"preview": "package com.decade.doj.user.service.impl;\n\nimport com.decade.doj.common.client.ProblemClient;\nimport com.decade.doj.comm"
},
{
"path": "DOJ-BE/user-service/src/main/java/com/decade/doj/user/utils/AESTool.java",
"chars": 3590,
"preview": "package com.decade.doj.user.utils;\n\nimport com.decade.doj.common.exception.UnauthorizedException;\nimport org.springframe"
},
{
"path": "DOJ-BE/user-service/src/main/resources/application.yaml",
"chars": 451,
"preview": "server:\n port: ${doj.port.user-service}\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: health,pr"
},
{
"path": "DOJ-BE/user-service/src/main/resources/bootstrap.yaml",
"chars": 1198,
"preview": "spring:\n application:\n name: user-service\n profiles:\n active: dev, common\n cloud:\n nacos:\n"
},
{
"path": "DOJ-BE/user-service/src/test/java/com/decade/doj/user/UserTest.java",
"chars": 945,
"preview": "package com.decade.doj.user;\n\nimport com.decade.doj.user.domain.po.User;\nimport com.decade.doj.user.mapper.UserMapper;\ni"
},
{
"path": "DOJ-FE/.eslintignore",
"chars": 17,
"preview": "dist\nnode_modules"
},
{
"path": "DOJ-FE/.eslintrc.cjs",
"chars": 2025,
"preview": "module.exports = {\n env: {\n browser: true,\n es2021: true,\n node: true,\n jest: true,\n }"
},
{
"path": "DOJ-FE/.gitignore",
"chars": 253,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
},
{
"path": "DOJ-FE/Dockerfile",
"chars": 112,
"preview": "FROM nginx:alpine\n\nCOPY dist/ /usr/share/nginx/html/\n\nCOPY nginx.conf /etc/nginx/conf.d/default.conf\n\nEXPOSE 80\n"
},
{
"path": "DOJ-FE/index.html",
"chars": 644,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/vite.svg\">\n <meta nam"
},
{
"path": "DOJ-FE/nginx.conf",
"chars": 736,
"preview": "upstream gateway_backend {\n server gateway-service:8080;\n}\n\nserver {\n resolver 127.0.0.11 valid=5s; \n\n listen "
},
{
"path": "DOJ-FE/package.json",
"chars": 1840,
"preview": "{\n \"name\": \"duck_online_judge\",\n \"private\": true,\n \"version\": \"1.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"lint\":"
},
{
"path": "DOJ-FE/run-docker.sh",
"chars": 644,
"preview": "#!/bin/bash\n\n# 脚本执行时,如果任何命令失败,则立即退出\nset -e\n\npnpm build\n\n# 定义镜像和容器的名称\nIMAGE_NAME=\"doj-fe\"\nCONTAINER_NAME=\"doj-fe-containe"
},
{
"path": "DOJ-FE/scripts/preinstall.js",
"chars": 257,
"preview": "console.log(\"Check install tools!\");\nif (!/pnpm/.test(process.env.npm_execpath || \"\")) {\n console.warn(\n `\\u001b[33m"
},
{
"path": "DOJ-FE/src/App.vue",
"chars": 293,
"preview": "<script setup lang=\"ts\">\nimport { onMounted } from 'vue';\nimport { useWebSocket } from '@/utils/websocket';\n\nonMounted(("
},
{
"path": "DOJ-FE/src/api/announcement/index.ts",
"chars": 578,
"preview": "import request from '@/utils/request';\nimport type { BaseResponseData } from '@/api/base';\nimport { AxiosResponse } from"
},
{
"path": "DOJ-FE/src/api/base/index.ts",
"chars": 271,
"preview": "// types.ts\nexport type BaseResponse = {\n code: number;\n message: string;\n};\n\nexport type BaseResponseData = BaseR"
},
{
"path": "DOJ-FE/src/api/problem/index.ts",
"chars": 1841,
"preview": "// 统一管理项目接口\nimport request from '@/utils/request';\nimport type { ProblemsResponseData, ProblemPageQueryForm, ProblemsPag"
},
{
"path": "DOJ-FE/src/api/problem/type.ts",
"chars": 975,
"preview": "import { BaseResponse, BasePageQueryForm } from '@/api/base';\n\nexport type ProblemPageQueryForm = BasePageQueryForm & {\n"
},
{
"path": "DOJ-FE/src/api/stats/index.ts",
"chars": 490,
"preview": "import request from '@/utils/request';\nimport type { BaseResponseData } from '@/api/base';\nimport { AxiosResponse } from"
},
{
"path": "DOJ-FE/src/api/submission/index.ts",
"chars": 1023,
"preview": "// 统一管理项目接口\nimport request from '@/utils/request';\nimport type { Submission, SubmissionPageQueryForm, SubmissionsPageRes"
},
{
"path": "DOJ-FE/src/api/submission/type.ts",
"chars": 935,
"preview": "import { BaseResponse, BasePageQueryForm } from '@/api/base';\n\nexport type Submission = {\n id: number; // "
},
{
"path": "DOJ-FE/src/api/submit/index.ts",
"chars": 844,
"preview": "// 统一管理项目接口\nimport request from '@/utils/request';\nimport type { submitForm, executeResponseData, problemSubmitForm, sid"
},
{
"path": "DOJ-FE/src/api/submit/type.ts",
"chars": 507,
"preview": "import { BaseResponse } from '@/api/base'\n\nexport type submitForm = {\n file: File,\n language: string,\n};\n\nexport t"
},
{
"path": "DOJ-FE/src/api/user/index.ts",
"chars": 1214,
"preview": "// 统一管理项目接口\nimport request from '@/utils/request';\nimport type { loginForm, loginResponseData, registerForm, userInfoRes"
},
{
"path": "DOJ-FE/src/api/user/type.ts",
"chars": 1266,
"preview": "import { BaseResponse } from '@/api/base'\n\n// 登陆接口请求数据类型\nexport type loginForm = {\n username: string,\n password: s"
},
{
"path": "DOJ-FE/src/components/CodeEditor/codeEditor.vue",
"chars": 12454,
"preview": "<template>\n <div class=\"editor\">\n <div class=\"main\">\n <codemirror v-model=\"code\" :style=\"{\n "
},
{
"path": "DOJ-FE/src/components/CodeEditor/index.vue",
"chars": 3638,
"preview": "<template>\n <div :class=\"['online-coding', { 'full-page': isPageFullscreen }]\">\n <toolbar v-if=\"config.editorT"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/cpp/cpp.cpp",
"chars": 175,
"preview": "// template\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n int a, b, n;\n cin >> a >> b"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/cpp/index.ts",
"chars": 120,
"preview": "import { cpp } from '@codemirror/lang-cpp'\nimport code from './cpp.cpp?raw'\n\nexport default {\n language: cpp,\n code\n}\n"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/java/index.ts",
"chars": 125,
"preview": "import { java } from '@codemirror/lang-java'\nimport code from './java.java?raw'\n\nexport default {\n language: java,\n co"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/java/java.java",
"chars": 319,
"preview": "// template\n\npublic class Main {\n public static void main(String[] args) {\n int mbSize = 256 * 1024 * 1024; //"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/python/index.ts",
"chars": 131,
"preview": "import { python } from '@codemirror/lang-python'\nimport code from './python.py?raw'\n\nexport default {\n language: python"
},
{
"path": "DOJ-FE/src/components/CodeEditor/lang-code/python/python.py",
"chars": 100,
"preview": "# template\n\na = []\n\nfor i in range(10000000):\n a.append(i)\n\nprint(sum(a))\n\nprint(\"Hello, World!\")"
},
{
"path": "DOJ-FE/src/components/CodeEditor/languages.ts",
"chars": 323,
"preview": "const importers = import.meta.glob<string>('./lang-code/*/index.ts')\nconst languages: { [key in string]: () => any } = {"
},
{
"path": "DOJ-FE/src/components/CodeEditor/problemEditor.vue",
"chars": 15453,
"preview": "<template>\n <div class=\"editor\">\n <div class=\"main\">\n <codemirror v-model=\"code\" :style=\"{\n "
},
{
"path": "DOJ-FE/src/components/CodeEditor/showEditor.vue",
"chars": 9963,
"preview": "<template>\n <div class=\"editor\">\n <div class=\"main\">\n <codemirror v-model=\"code\" :style=\"{\n "
},
{
"path": "DOJ-FE/src/components/CodeEditor/themes.ts",
"chars": 375,
"preview": "export { oneDark } from '@codemirror/theme-one-dark'\nexport { materialDark } from 'cm6-theme-material-dark'\nexport { nor"
},
{
"path": "DOJ-FE/src/components/CodeEditor/toolbar.vue",
"chars": 3647,
"preview": "<template>\n <div class=\"toolbar\">\n <div class=\"item\">\n <label for=\"language\">language:</label>\n "
},
{
"path": "DOJ-FE/src/components/SvgIcon/index.vue",
"chars": 734,
"preview": "<template>\n <!-- svg:图标外层容器节点,内部需要与use标签结合使用 -->\n <svg :style=\"{ width, height }\">\n <!-- xlink:href执行用哪一个图标"
},
{
"path": "DOJ-FE/src/components/index.ts",
"chars": 444,
"preview": "import { App, Component } from 'vue';\nimport svgIcon from './SvgIcon/index.vue';\nimport CodeEditor from './CodeEditor/in"
},
{
"path": "DOJ-FE/src/main.ts",
"chars": 764,
"preview": "import { createApp } from \"vue\";\nimport App from \"@/App.vue\";\n// elm组件\nimport ElementPlus from 'element-plus';\nimport 'e"
},
{
"path": "DOJ-FE/src/router/index.ts",
"chars": 2564,
"preview": "// createRouter:创建router实例对象\n// createWebHistory:创建history模式的路由\n\nimport { createRouter, createWebHistory } from 'vue-rou"
},
{
"path": "DOJ-FE/src/stores/userStore/index.ts",
"chars": 1099,
"preview": "// 管理用户数据相关\n\nimport { defineStore } from 'pinia';\nimport { ref } from 'vue';\nimport { reqLogin } from '@/api/user';\nimpo"
},
{
"path": "DOJ-FE/src/styles/index.scss",
"chars": 9756,
"preview": "// ================================\n// D-OnlineJudge Design System\n// Component Styles & Utilities\n// =================="
},
{
"path": "DOJ-FE/src/styles/theme.scss",
"chars": 2142,
"preview": "\n\n// ========== CSS Variables (Theme Support) ==========\n:root {\n // Colors\n --primary-start: #{$primary-start};\n --p"
},
{
"path": "DOJ-FE/src/styles/variable.scss",
"chars": 4674,
"preview": "@use 'sass:math';\n\n// ================================\n// D-OnlineJudge Design System\n// Modern Dark Theme Variables\n// "
},
{
"path": "DOJ-FE/src/utils/markdown.ts",
"chars": 1653,
"preview": "import { marked } from 'marked';\nimport katex from 'katex';\n\n// Configure marked options if needed\nmarked.setOptions({\n "
},
{
"path": "DOJ-FE/src/utils/request.ts",
"chars": 3018,
"preview": "// 二次封装axios:使用请求与响应拦截器\nimport axios, { type InternalAxiosRequestConfig } from \"axios\";\nimport { ElMessage } from \"eleme"
},
{
"path": "DOJ-FE/src/utils/websocket.ts",
"chars": 3576,
"preview": "import { ElNotification } from 'element-plus';\nimport router from '@/router';\nimport { h } from 'vue';\n\nlet socket: WebS"
},
{
"path": "DOJ-FE/src/views/Admin/Problem/components/ProblemDialog.vue",
"chars": 6836,
"preview": "<template>\n <el-dialog \n :title=\"isEdit ? '编辑题目' : '新增题目'\" \n v-model=\"visible\" \n width=\"800px\" \n"
},
{
"path": "DOJ-FE/src/views/Admin/Problem/index.vue",
"chars": 4685,
"preview": "<template>\n <div class=\"problem-manager\">\n <div class=\"actions-bar\">\n <el-button type=\"primary\" @cl"
},
{
"path": "DOJ-FE/src/views/Admin/index.vue",
"chars": 1356,
"preview": "<template>\n <div class=\"admin-page\">\n <div class=\"admin-header\">\n <h1>后台管理</h1>\n <div cl"
},
{
"path": "DOJ-FE/src/views/Home/index.vue",
"chars": 19874,
"preview": "<script lang=\"ts\" setup>\nimport { ref, onMounted } from 'vue';\nimport { useRouter } from 'vue-router';\n\nconst router = u"
},
{
"path": "DOJ-FE/src/views/Layout/components/LayoutFixed.vue",
"chars": 17413,
"preview": "<script lang=\"ts\" setup>\nimport { useUserStore } from '@/stores/userStore'\nimport { ElDropdown, ElDropdownMenu, ElDropdo"
},
{
"path": "DOJ-FE/src/views/Layout/components/LayoutFooter.vue",
"chars": 4817,
"preview": "<script lang=\"ts\" setup>\nconst currentYear = new Date().getFullYear();\n</script>\n\n<template>\n <footer class=\"footer\">"
},
{
"path": "DOJ-FE/src/views/Layout/index.vue",
"chars": 2595,
"preview": "<script lang=\"ts\" setup>\nimport LayoutFooter from '@/views/Layout/components/LayoutFooter.vue'\nimport LayoutFixed from '"
},
{
"path": "DOJ-FE/src/views/OnlineEditor/index.vue",
"chars": 1729,
"preview": "<template>\n <div class=\"online-editor-page\">\n <!-- Page Header -->\n <div class=\"page-header\">\n "
},
{
"path": "DOJ-FE/src/views/Problem/detail/index.vue",
"chars": 14506,
"preview": "<template>\n <div class=\"problem-detail-page\">\n <section class=\"hero card-glass\">\n <div class=\"hero-"
},
{
"path": "DOJ-FE/src/views/Problem/index.vue",
"chars": 13092,
"preview": "<template>\n <div class=\"problem-page\">\n <!-- Page Header -->\n <div class=\"page-header\">\n <h1"
},
{
"path": "DOJ-FE/src/views/Rankings/index.vue",
"chars": 9619,
"preview": "<template>\n <div class=\"rankings-page\">\n <!-- Page Header -->\n <div class=\"page-header\">\n <h"
},
{
"path": "DOJ-FE/src/views/Status/index.vue",
"chars": 13087,
"preview": "<template>\n <div class=\"status-page\">\n <!-- Page Header -->\n <div class=\"page-header\">\n <h1 "
},
{
"path": "DOJ-FE/src/views/User/home/index.vue",
"chars": 21165,
"preview": "<template>\n <div class=\"profile-page\">\n <!-- Profile Header -->\n <div class=\"profile-header card-glass\""
},
{
"path": "DOJ-FE/src/views/User/info/index.vue",
"chars": 14995,
"preview": "<template>\n <div class=\"settings-page\">\n <div class=\"settings-container card-glass\">\n <!-- Sidebar "
},
{
"path": "DOJ-FE/src/views/User/login/index.vue",
"chars": 10349,
"preview": "<template>\n <div class=\"login-page\">\n <div class=\"login-container\">\n <!-- Left Side - Branding -->\n"
},
{
"path": "DOJ-FE/src/views/User/register/index.vue",
"chars": 9722,
"preview": "<template>\n <div class=\"register-page\">\n <div class=\"register-container\">\n <!-- Left Side - Brandin"
},
{
"path": "DOJ-FE/src/vite-env.d.ts",
"chars": 511,
"preview": "/// <reference types=\"vite/client\" />\n\n// 识别.vue文件\ndeclare module \"*.vue\" {\n import { DefineComponent } from \"vue\";\n "
}
]
// ... and 25 more files (download for full content)
About this extraction
This page contains the full source code of the Decade-qiu/D-OnlineJudge GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 225 files (557.8 KB), approximately 158.4k tokens, and a symbol index with 393 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.