Skip to content

Repository files navigation

社团活动报名管理系统

基于 Spring Boot 的社团活动报名管理后端项目,面向校园社团 / 工作室,支持普通用户查看、报名、取消报名活动,管理员发布、修改、删除活动并查看报名情况。

技术栈

类型 说明
语言 Java 21
框架 Spring Boot 4.1.0(Spring Framework 7.0)
构建工具 Maven(含 mvnw 包装器)
数据库 MySQL 8.0.46
持久层 Spring Data JPA(Hibernate 7.4)
安全框架 Spring Security
认证方式 JWT(jjwt 0.13.0)
密码加密 BCrypt
参数校验 Spring Bean Validation(jakarta.validation)
实体简化 Lombok
接口文档 Apifox(见下文)
版本管理 Git

Java 版本

JDK 21(见 pom.xmljava.version)。

数据库类型和版本

MySQL 8.0.x(本机验证版本 8.0.46),数据库名 activity_management

项目启动方式

  1. 准备 MySQL 8.x,创建数据库 activity_management(可执行 schema.sql 初始化,见下节)。
  2. 修改 src/main/resources/application.properties 中的数据库账号密码:
    spring.datasource.username=root
    spring.datasource.password=你的数据库密码
  3. 启动应用:
    ./mvnw spring-boot:run
  4. 应用监听 http://localhost:8080
  5. 应用启动时 DataInitializer 会自动创建管理员账号 root(密码为配置项 root.password),无需手工建管理员。

数据库初始化方式

  • 一键初始化脚本:schema.sql(项目根目录),可直接执行,使用 IF NOT EXISTS,可重复执行且不会影响已有数据。
  • 脚本包含 4 张表:usersactivitiessigninsoperation_logs
  • 本脚本不创建 root 管理员(由应用启动时 DataInitializer 创建,见上节)。
  • 应用侧配置 spring.jpa.hibernate.ddl-auto=update,即使不手工执行脚本,首次启动也会自动建表;两份方式完全兼容。

配置文件说明

src/main/resources/application.properties

配置项 说明
server.port 服务端口,默认 8080
spring.datasource.url MySQL 连接串,指向 activity_management
spring.datasource.username / password 数据库账号密码
spring.jpa.hibernate.ddl-auto=update 由 Hibernate 自动创建 / 更新表结构
spring.jpa.show-sql 打印 SQL 日志(便于调试)
jwt.secret JWT 签名密钥(生产环境应通过环境变量注入)
root.password 启动时自动创建的 root 管理员密码

Apifox 文档

  • 公开链接(占位,导入后替换)https://apifox.com/apidoc/shared/REPLACE_WITH_YOUR_SHARED_LINK
  • 生成方式:用 Apifox 新建项目 → 导入项目根目录的 apifox-openapi.yaml(OpenAPI 3.0 格式)→ 项目概览页"分享文档"生成公开链接 → 替换上方占位链接。
  • 导入文件与代码保持同步,文档包含全部接口、权限说明、成功 / 失败响应示例以及完整业务流程示例。

数据库设计说明

共 4 张表(物理列名由 Hibernate 命名策略自动转为下划线小写):

说明 软删除 / 状态字段 关键索引
users 用户(普通用户 + 管理员) is_unregistered PK;nameemail 唯一键
activities 活动 is_deleted PK;start_timeis_deleted
signins 报名记录 is_cancelled PK;user_idactivity_id
operation_logs 操作日志(扩展数据) - PK;user_id

设计要点:

  • 无物理外键,通过业务逻辑保证数据一致性(符合需求建议)。
  • 主要业务表支持软删除:活动 is_deleted、用户 is_unregistered、报名 is_cancelled
  • 活动冗余保存 current_participants 报名人数,报名 / 取消时同步增减,并用事务保证一致。
  • 活动报名截止、开始、结束时间均以 DATETIME(6) 存储;capacity = -1 表示不限人数。
  • 常用查询字段(报名按用户 / 活动、活动按开始时间)已建索引。

说明:分类功能未单独建表,采用"活动不区分分类"的替代设计(详见"特殊设计说明")。

主要接口设计说明

  • 统一响应格式 Result
    { "code": 200, "message": "Success", "data": {} }
    成功 code = 200;失败 code != 200message 说明原因。
  • 分页响应 PageResponse{ "records": [], "total": 0, "page": 0, "size": 15 }page 从 0 开始,分页大小固定为 15。
  • 接口路径遵循 RESTful 语义,/api 为统一前缀;GET /api/activity(列表)、GET /api/activity/{id}(详情)公开访问,其余按角色鉴权。
  • 主要接口一览:
方法 路径 权限 说明
POST /api/auth/register 公开 用户注册
POST /api/auth/login 公开 用户登录,返回 JWT
GET /api/activity 公开 活动列表(分页,description 截断前 40 字符)
GET /api/activity/{id} 公开 活动详情
POST /api/activity/{id}/signin 登录用户 报名活动
DELETE /api/activity/{id}/signin 登录用户 取消自己的报名
GET /api/mysignins 登录用户 我的报名记录(分页)
GET /api/activity/{id}/signins 管理员 某活动的报名名单(分页)
POST /api/activity/create 管理员 创建活动
PUT /api/activity/{id}/update 管理员 修改活动
DELETE /api/activity/delete/{id} 管理员 删除活动(软删除)
GET /api/user/{id}/ 管理员或本人 查看用户信息(不含密码)
GET /api/user/{id}/unregister 管理员或本人 注销用户
GET /api/admin/logs 管理员 查看操作日志(分页)

Spring Security + JWT 鉴权流程

  1. 注册:密码经 BCryptPasswordEncoder 加密后入库,默认角色为普通用户 USER
  2. 登录AuthService.login 通过 AuthenticationManager 认证,成功后用 JwtUtil 生成 JWT(签名密钥取自 jwt.secret,有效期 1 天)。
  3. Token 校验JwtAuthenticationFilter 在每个请求中解析 Authorization: Bearer <token>,校验签名与过期时间,并从数据库加载用户构造 Authentication 写入 SecurityContext
  4. 权限控制
    • 路径级:SecurityConfig 中公开接口(/api/auth/**、活动浏览)permitAll,其余要求认证。
    • 方法级:@EnableMethodSecurity + @PreAuthorize,如管理员接口 hasRole('ADMIN')、登录接口 isAuthenticated()
  5. 统一异常:Token 缺失 / 无效、权限不足时由安全配置或全局异常处理器返回统一 Result

持久层实现说明

本项目使用 Spring Data JPA(而非需求文档技术栈表中所列的 MyBatis / MyBatis-Plus),原因如下:

  • JPA 的实体映射、仓库抽象与 Spring Boot 集成度更高,能显著减少样板代码;
  • 复杂查询同样具备清晰的表达方式:本项目中的"我的报名记录""活动报名名单"均使用自定义 @Query 联表投影查询(SigninRepository),配合显式 countQuery 实现分页;
  • 派生查询(findByUserIdAndActivityIdAndIsCancelledFalse 等)用于多条件查询;软删除通过查询条件显式过滤(如 a.isDeleted = falses.isCancelled = false)。

使用 JPA 提供的能力覆盖:单表 CRUD、多条件查询、分页查询、联表查询 / 数据组装、软删除过滤。项目不依赖框架默认 CRUD 堆功能,报名 / 取消 / 删除等复杂业务逻辑集中在 Service 层。

权限控制说明

  • 角色由 users.is_admin 区分,CustomUserDetails.getAuthorities() 映射为 ROLE_ADMIN / ROLE_USER
  • 普通用户无法访问任何管理员接口(方法级 hasRole('ADMIN') 拦截,返回 PERMISSION_DENIED)。
  • 未登录用户只能访问注册、登录与活动浏览等公开接口。
  • 报名、取消报名、我的报名记录等要求登录;身份一律取自 JWT(@AuthenticationPrincipal),杜绝伪造他人身份。

核心业务规则说明

报名活动signinActivity):活动存在且未删除 → 未重复报名 → 名额未满(capacity != -1 时)→ 未过报名截止时间 → 报名人数 +1 并写入报名记录,全程事务。

取消报名signoutActivity):活动存在且未删除 → 活动未开始 → 只能取消自己的报名(按 JWT 用户 ID 查询)→ 软取消(is_cancelled = true)并人数 -1,全程事务。

修改活动updateActivity):活动存在且未删除 → 活动未开始 → 重新校验时间合法性 → 新容量不得小于当前报名人数。

删除活动deleteActivity):软删除(is_deleted = true),并级联软取消该活动所有有效报名、释放名额。

注销用户unregisterUser):软删除(is_unregistered = true),并级联软取消该用户所有有效报名、释放相应活动名额。

操作日志设计

采用 AOP 注解式操作日志(行业常见的审计日志做法),业务代码无需侵入式调用:

  • 定义 @LogOperation(action, targetType) 注解,标注在需要审计的 Service 方法上(注册、登录、创建 / 修改 / 删除活动、报名 / 取消报名、注销用户)。
  • OperationLogAspect 通过 @Around 环绕通知自动记录:操作人(取自 SecurityContext 的 JWT 身份)、操作类型、目标类型与目标 ID、操作结果(成功 / 失败及失败原因)、来源 IP、操作时间。
  • 切面以最高优先级(@Order(HIGHEST_PRECEDENCE))包裹业务方法,日志在业务事务提交 / 回滚之后独立落库——即使业务失败也能记录,且日志写入异常不影响业务。
  • 目标 ID 提取规则:优先取返回值实体的 id,其次取最后一个 Integer 参数(如活动 id),再取实体参数。
  • 日志写入 operation_logs 表,管理员通过 GET /api/admin/logs 分页查看。

特殊设计说明

  • 管理员是否可以报名活动:允许。管理员同时承担普通用户行为(报名 / 取消),便于演示与数据验证;权限控制上两者互不影响。
  • 活动开始后是否允许取消报名:不允许。活动开始后名额已锁定,取消操作会被拒绝(ACTIVITY_STARTED)。
  • 活动开始后是否允许修改:不允许。避免已启动活动被改动造成混乱。
  • 删除已有报名的活动时如何处理:软删除活动并级联软取消其全部有效报名记录,报名记录保留在表中(is_cancelled = true),活动不再对外展示。选择此方案可完整保留历史数据。
  • 分类功能:未实现分类模块。采用"活动不区分分类"的替代设计——活动列表支持按开始时间排序与分页浏览,不设置分类字段;如需扩展,可新增 categories 表并通过 activity_id ↔ category_id 关联,代码结构已按此预留分层。

Git 提交说明

  • 使用 Git 管理,提交信息采用 类型: 说明 风格(如 Feature:Bugfix:),按功能模块拆分提交,不一次性提交整个项目。
  • 已忽略 target/、IDE 配置等无关文件(见 .gitignore)。

使用 AI 辅助的情况说明

开发过程中使用 AI 辅助完成以下工作,核心设计(数据库、接口、权限、业务规则)均由本人理解并实现:

  • 查询语法与框架用法(Spring Security、JWT、JPA 等);
  • 排查报错(如数据库残留列导致注册失败、Token 校验失败等);
  • 生成少量模板代码后自行修改,并理解每一处含义;
  • 辅助整理本文档与接口文档。

遇到的问题与解决方案

  1. 数据库残留 last_signin_time 列导致注册报错:旧版本实体字段变更后,表内残留了无默认值的 NOT NULL 列,插入用户时报 Field 'last_signin_time' doesn't have a default value。通过 ALTER TABLE users DROP COLUMN last_signin_time 清理残留列解决。
  2. JWT 校验失败(Token 无效)application.propertiesjwt.secret 值带引号,Properties 加载后密钥含引号导致签名不匹配,统一后正常。
  3. 取消报名错误语义:原先未报名也提示"已取消",改为专用错误码 REGISTRATION_NOT_FOUND,语义更准确。
  4. 时区偏移:JDBC 连接串使用 serverTimezone=UTC,在非 UTC 环境下时间展示可能偏移,建议按部署环境调整时区配置。
  5. 持久层选型差异:需求技术栈表要求 MyBatis / MyBatis-Plus,本项目基于开发效率与查询表达力选择 Spring Data JPA,相关能力与理由见"持久层实现说明"。

About

twt test project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages