分布式在线判题系统(分布式OJ系统)服务端

yangyi 1cfac5969b # fix: OpenAPI错误响应显式声明Response schema,避免回落到成功返回类型 2 miesięcy temu
gradle 23cbe4ab8d # project init 7 miesięcy temu
sql 43a70d0fe8 feat:数据表增加逻辑删除字段和相关的注释 2 miesięcy temu
src 1cfac5969b # fix: OpenAPI错误响应显式声明Response schema,避免回落到成功返回类型 2 miesięcy temu
.gitattributes 23cbe4ab8d # project init 7 miesięcy temu
.gitignore 23cbe4ab8d # project init 7 miesięcy temu
AGENTS.md 196392ba5f # feat: 状态常量添加注释并在AGENTS.md补充常量使用约定 2 miesięcy temu
README.md 2fd32c9f62 # sql:各表updated_at增加自动刷新触发器并补充文档 2 miesięcy temu
build.gradle.kts 5ed5d189c0 # feat: 配置分页插件、开放公开浏览接口并暴露当前用户ID 2 miesięcy temu
gradlew 23cbe4ab8d # project init 7 miesięcy temu
gradlew.bat 23cbe4ab8d # project init 7 miesięcy temu
settings.gradle.kts 23cbe4ab8d # project init 7 miesięcy temu

README.md

serve_template

在线评测系统(OJ)后端服务模板,基于 Spring Boot 构建,提供统一的认证鉴权、用户管理与通用响应封装,可直接作为后续功能(题目、评测、比赛等)的开发底座。

技术栈

分类 选型 版本
语言 Java 17
框架 Spring Boot 3.5.13
构建工具 Gradle (Wrapper) 8.14.4
安全 Spring Security + JWT -
ORM MyBatis-Plus 3.5.15
数据库 PostgreSQL + Druid 连接池 -
接口文档 springdoc-openapi (Swagger UI) 2.8.16

已实现功能

  • 用户认证:注册、登录,密码 BCrypt 加密,登录后签发 JWT(有效期 24 小时)
  • 接口鉴权:基于角色的方法级鉴权(user / coach / admin),未认证、无权限统一 JSON 返回
  • 用户管理:分页查询、详情查询、新增、编辑、批量删除,以及状态 / 头像 / 密码更新
  • 统一响应:Response<T> 通用响应体 + 全局异常处理(参数校验、业务异常)
  • 请求日志:基于 CommonsRequestLoggingFilter 记录请求入参
  • 接口文档:Swagger UI 在线调试

项目结构

serve_template
├── sql/postgersql.sql            # 数据库初始化脚本(表结构、索引与触发器)
├── src/main/java/space/anyi/serve
│   ├── ServeApplication.java     # 启动类
│   ├── config                    # SecurityConfig / WebConfig(CORS/日志) / OpenApiConfig
│   ├── controller                # AuthController(登录注册) / UserController(用户管理)
│   ├── entity                    # Response(响应体) / PageVo(分页) / auth / user(DTO、VO、实体)
│   ├── filter                    # JwtAuthenticationFilter(JWT 解析过滤器)
│   ├── handler                   # GlobalExceptionHandler + security(JWT 工具、异常入口)
│   ├── mapper                    # MyBatis-Plus Mapper
│   └── service                   # UserService 接口 + impl 实现
└── src/main/resources
    ├── application.yaml          # 应用配置
    └── mapper/UserMapper.xml     # Mapper XML

快速开始

1. 环境要求

  • JDK 17+
  • PostgreSQL(本地或远程均可)

2. 初始化数据库

psql -U <postgres用户> -f sql/postgersql.sql

脚本会创建 oj 数据库、dev schema,以及用户、题目、评测用例、提交记录、分类、比赛等表(含注释与索引)。

3. 配置环境变量

application.yaml 中数据库连接通过环境变量注入:

环境变量 说明 默认值
databaseHost 数据库地址 -
databaseUsername 数据库用户名 -
databasePassword 数据库密码 -

4. 启动

./gradlew bootRun

服务默认运行在 http://localhost:8080。

接口文档

启动后访问 Swagger UI:

http://localhost:8080/swagger-ui/index.html

auth/login、auth/register 及 Swagger 相关路径无需认证,其余接口需在请求头携带 Authorization: Bearer <token>。

数据库设计

表均位于 dev schema 下:

  • user:用户(账号、密码、用户名、角色、头像、启用状态、逻辑删除标记)
  • problem:题目(标题、描述、时间/内存限制、提交与通过次数、公开状态、难度)
  • problem_case:题目样例(展示给用户)
  • problem_test:评测用例(后台判题,可隐藏)
  • submission:提交记录(代码、语言、判题状态、耗时/内存、得分、用例统计)
  • category / problem_category_rel:题目类别及多对多关联
  • contest / contest_problem / contest_registration:比赛、比赛题目、比赛报名

各表之间使用逻辑外键(仅记录 ID),便于水平扩展。

时间戳自动维护

  • 创建时间:所有表的 created_at 由数据库列默认值 DEFAULT NOW() 在插入时自动填充。
  • 更新时间:所有表的 updated_at 由 BEFORE UPDATE 触发器自动刷新。触发器函数 set_updated_at() 将每行更新前的 updated_at 置为当前时间,各表对应触发器如下(均在 sql/postgersql.sql 中定义):
表 触发器
user trg_user_updated_at
problem trg_problem_updated_at
problem_case trg_problem_case_updated_at
problem_test trg_problem_test_updated_at
submission trg_submission_updated_at
category trg_category_updated_at
problem_category_rel trg_problem_category_rel_updated_at
contest trg_contest_updated_at
contest_problem trg_contest_problem_updated_at
contest_registration trg_contest_registration_updated_at
-- 触发器函数(定义一次,所有表共用)
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 每个表单独建触发器,例如:
CREATE TRIGGER trg_user_updated_at
BEFORE UPDATE ON dev.user
FOR EACH ROW EXECUTE FUNCTION set_updated_at();

应用层(MyBatis-Plus 实体)无需维护 created_at / updated_at,插入与更新均由数据库自动处理。

E-R 图

erDiagram
    user ||--o{ problem : "创建(created_by)"
    user ||--o{ contest : "创建(created_by)"
    user ||--o{ submission : "提交(user_id)"
    user ||--o{ contest_registration : "报名(user_id)"
    problem ||--o{ problem_case : "样例(problem_id)"
    problem ||--o{ problem_test : "评测用例(problem_id)"
    problem ||--o{ submission : "提交记录(problem_id)"
    problem ||--o{ problem_category_rel : "关联(problem_id)"
    category ||--o{ problem_category_rel : "关联(category_id)"
    contest ||--o{ submission : "包含(contest_id)"
    contest ||--o{ contest_problem : "题目(contest_id)"
    problem ||--o{ contest_problem : "被纳入(problem_id)"
    contest ||--o{ contest_registration : "报名(contest_id)"

主要依赖

依赖 用途
spring-boot-starter-web Web MVC
spring-boot-starter-security 认证与鉴权
spring-boot-starter-validation 参数校验
mybatis-plus-spring-boot3-starter ORM
druid-spring-boot-3-starter 数据库连接池
postgresql PostgreSQL 驱动
jjwt JWT 生成与解析
springdoc-openapi-starter-webmvc-ui Swagger 接口文档
spring-boot-starter-actuator 健康检查与监控(端点全量暴露)