1. 项目背景与目标

本系统是 《软件开发架构平台》 课程的综合实践大作业。 旨在整合课程中所学的 Spring、SpringMVC、MyBatis 等核心技术,构建一个功能完备、可应对高并发场景的 B/S 架构校园活动管理平台。


针对传统实验作业中存在的痛点进行了深度重构:

解决无并发控制导致超卖
解决文件重启丢失问题
解决交互体验差的问题

2. 课程知识点应用 (Knowledge Map)

本项目严格遵循课程大纲要求,将理论知识转化为实际代码实现:

Spring & SpringMVC
  • IoC & Bean管理
    使用 @Autowired, @Service, @Controller 实现分层解耦。
  • Spring 事务管理
    在报名/取消报名逻辑中使用 @Transactional 保证数据原子性。
  • 文件上传与下载
    实现了用户头像与活动封面的 MultipartFile 上传与虚拟路径映射。
MyBatis & Database
  • 动态 SQL
    利用 <if>, <choose> 标签实现多条件搜索与动态排序。
  • 关联查询
    使用 <association> 标签在查询报名记录时关联查询 User 和 Activity 信息。
  • 连接池与优化
    集成 Druid/HikariCP 连接池,并引入 Redis 缓存减轻数据库压力。

3. 技术栈与存储架构

MySQL 8.0
核心业务数据持久化
(用户/活动/订单)
Redis Cache
热点活动列表/详情缓存
(TTL: 10min)
Local FileSystem
非结构化数据存储
(/uploads/images)

4. 核心源码结构解析

系统严格遵循 MVC 分层架构,各层职责清晰:

职责: 系统的“前台”。接收 HTTP 请求,解析参数,调用 Service,并将数据放入 Model 返回给 Thymeleaf 页面。

  • listActivities(): 处理首页请求,接收 keywordsort 参数。
  • searchSuggest(): AJAX 接口,返回 JSON 格式的搜索候选词,供前端 JS 调用。
  • uploadAvatar(): 处理 MultipartFile,调用工具方法保存文件到磁盘。

职责: 系统的“大脑”。处理业务逻辑、事务控制 (@Transactional) 和缓存管理。

  • joinActivity(): 核心方法。包含 查重 -> 查活动状态 -> 乐观锁扣减库存 -> 写入报名表 完整流程。
  • cancelRegistration(): 实现了“活动开始前1天不可取消”的业务规则校验。

职责: 系统的“仓库管理员”。通过 XML 编写复杂 SQL。

<select id="selectActivities" ...> SELECT * FROM activities <where> <if test="keyword != null">...</if> </where> <choose> <when test="sort == 'time_desc'">ORDER BY start_time DESC</when> ... </choose> </select>

  • WebConfig: 配置 addResourceHandlers,将 URL /uploads/** 映射到本地物理路径 D:/.../uploads/,解决图片回显问题。
  • RedisConfig: 解决 Redis 默认序列化器无法处理 Java8 LocalDateTime 的问题。

5. 核心难点:并发与超卖

问题重现:为什么 Java 逻辑锁不住?

假设活动仅剩 1个名额,A和B同时请求。在多线程环境下,两者可能同时读取到 current=99,判断 99 < 100 通过,然后都执行 update(100),导致实际报名人数变成 101 人。

// ❌ 错误代码:非原子操作 int current = activity.getCurrent(); // A读99, B读99 if (current < max) { activity.setCurrent(current + 1); // A写100, B写100 (覆盖) repo.save(activity); }
解决方案:MySQL 乐观锁 (Optimistic Locking)

放弃 Java 层面的锁,利用数据库 Update 语句的原子性。我们将判断逻辑下沉到 SQL 中,数据库会自动对修改行加排他锁。

// ✅ 正确代码:利用 SQL 返回值判断 UPDATE activities SET current_participants = current_participants + 1 WHERE id = #{id} AND current_participants < max_participants; -- 核心判断

原理: 若 A 执行成功,数据变为 100;B 再执行时条件 100 < 100 不满足,SQL 返回“受影响行数 0”。Service 层捕获此结果并抛出“名额已满”异常。

6. 数据库模型

表名 说明 关键设计
users 用户表 role (0学生/1管理员), avatar (持久化路径)
activities 活动表 current_participants (原子计数),
max_participants (并发边界)
activity_registrations 报名表 UNIQUE KEY (user_id, activity_id)
物理级防止重复报名
Designed & Developed by Huang Yuan
Documentation generated for Course Project Exp8.