diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..76bd8cd
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..3f19c11
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1768306117632
+
+
+ 1768306117632
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server/.idea/appmap.xml b/server/.idea/appmap.xml
new file mode 100644
index 0000000..a61fde7
--- /dev/null
+++ b/server/.idea/appmap.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server/.vscode/settings.json b/server/.vscode/settings.json
new file mode 100644
index 0000000..9250139
--- /dev/null
+++ b/server/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable"
+}
\ No newline at end of file
diff --git a/server/docs/业务流程图.md b/server/docs/业务流程图.md
new file mode 100644
index 0000000..979c225
--- /dev/null
+++ b/server/docs/业务流程图.md
@@ -0,0 +1,1119 @@
+# 校园活动系统 - 业务流程图
+
+## 系统架构概览
+
+本系统采用典型的分层架构,包含以下层次:
+
+- **表现层(Controller层)**:处理HTTP请求和响应
+- **业务逻辑层(Service层)**:实现核心业务逻辑
+- **数据访问层(Mapper层)**:与数据库交互
+- **实体层(Entity层)**:数据模型定义
+- **安全层(Security层)**:JWT认证和授权
+
+---
+
+## 1. 用户认证流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as AuthController
认证控制器
+ participant Service as AuthServiceImpl
认证服务
+ participant Security as SecurityConfig
安全配置
+ participant JwtProvider as JwtTokenProvider
JWT令牌提供者
+ participant Mapper as UserMapper
用户数据访问
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 用户注册流程
+ Client->>Controller: POST /api/v1/auth/register
RegisterRequest
+ Controller->>Service: register(request)
+ Service->>Mapper: 检查用户名是否存在
selectCount(username)
+ Mapper->>DB: SELECT COUNT(*) FROM user
WHERE username = ?
+ DB-->>Mapper: 返回数量
+ Mapper-->>Service: 返回数量
+ alt 用户名已存在
+ Service-->>Controller: 抛出异常
USER_ALREADY_EXISTS
+ Controller-->>Client: 400 错误
+ else 用户名不存在
+ Service->>Mapper: 检查学号是否存在
selectCount(studentId)
+ Mapper->>DB: SELECT COUNT(*) FROM user
WHERE student_id = ?
+ DB-->>Mapper: 返回数量
+ Mapper-->>Service: 返回数量
+ alt 学号已存在
+ Service-->>Controller: 抛出异常
STUDENT_ID_ALREADY_EXISTS
+ Controller-->>Client: 400 错误
+ else 学号不存在
+ Service->>Service: 创建用户对象
passwordEncoder.encode()
+ Service->>Mapper: insert(user)
+ Mapper->>DB: INSERT INTO user(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回结果
+ Service-->>Controller: 注册成功
+ Controller-->>Client: 200 成功
+ end
+ end
+
+ Note over Client,DB: 2. 用户登录流程
+ Client->>Controller: POST /api/v1/auth/login
LoginRequest
+ Controller->>Security: authenticationManager.authenticate()
+ Security->>Mapper: 加载用户信息
loadUserByUsername()
+ Mapper->>DB: SELECT * FROM user
WHERE username = ?
+ DB-->>Mapper: 返回用户信息
+ Mapper-->>Security: 返回UserDetails
+ Security->>Security: 验证密码
passwordEncoder.matches()
+ alt 密码错误
+ Security-->>Controller: 抛出异常
+ Controller-->>Client: 401 未授权
+ else 密码正确
+ Security-->>Controller: 认证成功
+ Controller->>Service: login(request)
+ Service->>Mapper: 查询用户
selectById(username)
+ Mapper->>DB: SELECT * FROM user
WHERE username = ?
+ DB-->>Mapper: 返回用户信息
+ Mapper-->>Service: 返回User对象
+ Service->>JwtProvider: generateToken(userId, username, role)
+ JwtProvider-->>Service: 返回accessToken
+ Service->>JwtProvider: generateRefreshToken(userId)
+ JwtProvider-->>Service: 返回refreshToken
+ Service-->>Controller: 返回LoginResponse
+ Controller-->>Client: 200 成功
(包含accessToken和refreshToken)
+ end
+
+ Note over Client,DB: 3. Token刷新流程
+ Client->>Controller: POST /api/v1/auth/refresh
RefreshTokenRequest
+ Controller->>Service: refreshToken(request)
+ Service->>JwtProvider: validateToken(refreshToken)
+ JwtProvider-->>Service: 返回验证结果
+ alt Token无效
+ Service-->>Controller: 抛出异常
UNAUTHORIZED
+ Controller-->>Client: 401 未授权
+ else Token有效
+ Service->>JwtProvider: getUsernameFromToken(refreshToken)
+ JwtProvider-->>Service: 返回userId
+ Service->>Mapper: selectById(userId)
+ Mapper->>DB: SELECT * FROM user
WHERE id = ?
+ DB-->>Mapper: 返回用户信息
+ Mapper-->>Service: 返回User对象
+ Service->>JwtProvider: generateToken(userId, username, role)
+ JwtProvider-->>Service: 返回新accessToken
+ Service-->>Controller: 返回RefreshTokenResponse
+ Controller-->>Client: 200 成功
+ end
+
+ Note over Client,DB: 4. 获取当前用户信息
+ Client->>Controller: GET /api/v1/auth/me
Header: Authorization: Bearer token
+ Controller->>Security: JwtAuthenticationFilter
验证token
+ Security->>JwtProvider: validateToken(accessToken)
+ JwtProvider-->>Security: 验证通过
+ Security->>Security: 设置SecurityContext
+ Controller->>Service: getCurrentUser()
+ Service->>Mapper: selectById(userId)
+ Mapper->>DB: SELECT * FROM user
WHERE id = ?
+ DB-->>Mapper: 返回用户信息
+ Mapper-->>Service: 返回User对象
+ Service-->>Controller: 返回用户信息
+ Controller-->>Client: 200 成功
+```
+
+---
+
+## 2. 活动管理流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as ActivityController
活动控制器
+ participant Service as ActivityServiceImpl
活动服务
+ participant Mapper as ActivityMapper
活动数据访问
+ participant RegMapper as RegistrationMapper
报名数据访问
+ participant UserMapper as UserMapper
用户数据访问
+ participant Auth as AuthService
认证服务
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 获取活动列表
+ Client->>Controller: GET /api/v1/activities
(current, size, status, keyword, category, startDate, endDate)
+ Controller->>Service: pageActivities(page, params)
+ Service->>Mapper: selectActivityPage(page, params)
+ Mapper->>DB: SELECT * FROM activity
WHERE conditions
LIMIT offset, size
+ DB-->>Mapper: 返回活动列表
+ Mapper-->>Service: 返回IPage
+ Service->>Service: 转换为ActivityVO列表
convertToVO()
+ Service-->>Controller: 返回IPage
+ Controller-->>Client: 200 成功
+
+ Note over Client,DB: 2. 获取活动详情
+ Client->>Controller: GET /api/v1/activities/{id}
+ Controller->>Service: getActivityById(id)
+ Service->>Mapper: selectById(id)
+ Mapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>Mapper: 返回活动信息
+ Mapper-->>Service: 返回Activity对象
+ Service->>UserMapper: selectById(adminId)
+ UserMapper->>DB: SELECT * FROM user
WHERE id = ?
+ DB-->>UserMapper: 返回管理员信息
+ UserMapper-->>Service: 返回User对象
+ alt 用户已登录
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>RegMapper: 检查是否已报名
selectOne(userId, activityId)
+ RegMapper->>DB: SELECT * FROM registration
WHERE user_id = ? AND activity_id = ?
+ DB-->>RegMapper: 返回报名记录
+ RegMapper-->>Service: 返回Registration对象
+ end
+ Service->>Service: 转换为ActivityVO
convertToVO()
+ Service-->>Controller: 返回ActivityVO
+ Controller-->>Client: 200 成功
+
+ Note over Client,DB: 3. 创建活动(管理员)
+ Client->>Controller: POST /api/v1/activities
ActivityCreateRequest
Header: Authorization: Bearer token
+ Controller->>Service: createActivity(request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ alt 用户不是管理员
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 用户是管理员
+ Service->>Service: 验证时间参数
+ alt 开始时间晚于结束时间
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 时间参数正确
+ Service->>Service: checkConflict(startTime, endTime)
+ Service->>Mapper: selectConflictActivities(startTime, endTime)
+ Mapper->>DB: SELECT * FROM activity
WHERE time_conflict
+ DB-->>Mapper: 返回冲突活动列表
+ Mapper-->>Service: 返回冲突活动
+ alt 存在时间冲突
+ Service-->>Controller: 抛出异常
ACTIVITY_TIME_CONFLICT
+ Controller-->>Client: 409 冲突
+ else 无冲突
+ Service->>Service: 创建Activity对象
+ Service->>Mapper: insert(activity)
+ Mapper->>DB: INSERT INTO activity(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回activityId
+ Service-->>Controller: 返回activityId
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+
+ Note over Client,DB: 4. 更新活动(管理员)
+ Client->>Controller: PUT /api/v1/activities/{id}
ActivityUpdateRequest
Header: Authorization: Bearer token
+ Controller->>Service: updateActivity(id, request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ alt 用户不是管理员
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 用户是管理员
+ Service->>Mapper: selectById(id)
+ Mapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>Mapper: 返回活动信息
+ Mapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ alt 不是活动创建者
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是活动创建者
+ Service->>Service: 验证时间参数
+ Service->>Service: checkConflict(startTime, endTime, excludeActivityId)
+ Service->>Mapper: selectConflictActivities(startTime, endTime, excludeActivityId)
+ Mapper->>DB: SELECT * FROM activity
WHERE time_conflict AND id != ?
+ DB-->>Mapper: 返回冲突活动列表
+ Mapper-->>Service: 返回冲突活动
+ alt 存在时间冲突
+ Service-->>Controller: 抛出异常
ACTIVITY_TIME_CONFLICT
+ Controller-->>Client: 409 冲突
+ else 无冲突
+ Service->>Service: 更新Activity对象
+ Service->>Mapper: updateById(activity)
+ Mapper->>DB: UPDATE activity SET ...
WHERE id = ?
+ DB-->>Mapper: 返回更新结果
+ Mapper-->>Service: 返回成功
+ Service-->>Controller: 更新成功
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+ end
+
+ Note over Client,DB: 5. 删除活动(管理员)
+ Client->>Controller: DELETE /api/v1/activities/{id}
Header: Authorization: Bearer token
+ Controller->>Service: deleteActivity(id)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ alt 用户不是管理员
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 用户是管理员
+ Service->>Mapper: selectById(id)
+ Mapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>Mapper: 返回活动信息
+ Mapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ alt 不是活动创建者
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是活动创建者
+ Service->>Mapper: deleteById(id)
+ Mapper->>DB: UPDATE activity SET deleted = 1
WHERE id = ?
+ DB-->>Mapper: 返回删除结果
+ Mapper-->>Service: 返回成功
+ Service-->>Controller: 删除成功
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+
+ Note over Client,DB: 6. 检测时间冲突(管理员)
+ Client->>Controller: POST /api/v1/activities/check-conflict
CheckConflictRequest
Header: Authorization: Bearer token
+ Controller->>Service: checkConflict(request)
+ Service->>Service: 验证时间参数
+ Service->>Mapper: selectConflictActivities(startTime, endTime, excludeActivityId)
+ Mapper->>DB: SELECT * FROM activity
WHERE time_conflict AND id != ?
+ DB-->>Mapper: 返回冲突活动列表
+ Mapper-->>Service: 返回冲突活动
+ Service->>Service: 构建ConflictCheckVO
+ Service-->>Controller: 返回ConflictCheckVO
+ Controller-->>Client: 200 成功
+```
+
+---
+
+## 3. 活动报名流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as RegistrationController
报名控制器
+ participant Service as RegistrationServiceImpl
报名服务
+ participant Mapper as RegistrationMapper
报名数据访问
+ participant ActMapper as ActivityMapper
活动数据访问
+ participant Auth as AuthService
认证服务
+ participant PdfUtil as PdfUtil
PDF工具类
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 报名活动
+ Client->>Controller: POST /api/v1/registrations
RegistrationRequest
Header: Authorization: Bearer token
+ Controller->>Service: register(request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ alt 活动不在报名中
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 活动在报名中
+ alt 报名已截止
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 报名未截止
+ alt 报名人数已满
+ Service-->>Controller: 抛出异常
ACTIVITY_FULL
+ Controller-->>Client: 409 冲突
+ else 报名未满
+ Service->>Mapper: 检查是否已报名
selectOne(userId, activityId)
+ Mapper->>DB: SELECT * FROM registration
WHERE user_id = ? AND activity_id = ?
AND status IN (1, 2)
+ DB-->>Mapper: 返回报名记录
+ Mapper-->>Service: 返回Registration对象
+ alt 已报名该活动
+ Service-->>Controller: 抛出异常
ALREADY_REGISTERED
+ Controller-->>Client: 409 冲突
+ else 未报名该活动
+ Service->>Mapper: 查询用户所有报名
selectList(userId, status=1)
+ Mapper->>DB: SELECT * FROM registration
WHERE user_id = ? AND status = 1
+ DB-->>Mapper: 返回报名列表
+ Mapper-->>Service: 返回List
+ loop 遍历报名列表
+ Service->>ActMapper: selectById(reg.activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ Service->>Service: isTimeConflict(activity1, activity2)
+ alt 存在时间冲突
+ Service-->>Controller: 抛出异常
ACTIVITY_TIME_CONFLICT
+ Controller-->>Client: 409 冲突
+ end
+ end
+ Service->>Service: 创建Registration对象
generateTicketCode()
+ Service->>Mapper: insert(registration)
+ Mapper->>DB: INSERT INTO registration(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回registrationId
+ Service->>ActMapper: 更新活动报名人数
updateById(activity)
+ ActMapper->>DB: UPDATE activity SET
current_participants = current_participants + 1
WHERE id = ?
+ DB-->>ActMapper: 返回更新结果
+ ActMapper-->>Service: 返回成功
+ Service->>Service: convertToVO()
+ Service-->>Controller: 返回RegistrationVO
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+ end
+ end
+
+ Note over Client,DB: 2. 取消报名
+ Client->>Controller: DELETE /api/v1/registrations/{id}
Header: Authorization: Bearer token
+ Controller->>Service: cancelRegistration(id)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>Mapper: selectById(id)
+ Mapper->>DB: SELECT * FROM registration
WHERE id = ?
+ DB-->>Mapper: 返回报名记录
+ Mapper-->>Service: 返回Registration对象
+ alt 报名记录不存在
+ Service-->>Controller: 抛出异常
REGISTRATION_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 报名记录存在
+ alt 不是自己的报名
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是自己的报名
+ alt 报名状态不是已报名
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 报名状态是已报名
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动已开始
+ Service-->>Controller: 抛出异常
ACTIVITY_ALREADY_STARTED
+ Controller-->>Client: 409 冲突
+ else 活动未开始
+ Service->>Mapper: 更新报名状态
updateById(registration)
+ Mapper->>DB: UPDATE registration SET
status = 0, canceled_at = NOW()
WHERE id = ?
+ DB-->>Mapper: 返回更新结果
+ Mapper-->>Service: 返回成功
+ Service->>ActMapper: 减少活动报名人数
updateById(activity)
+ ActMapper->>DB: UPDATE activity SET
current_participants = current_participants - 1
WHERE id = ?
+ DB-->>ActMapper: 返回更新结果
+ ActMapper-->>Service: 返回成功
+ Service-->>Controller: 取消成功
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+ end
+
+ Note over Client,DB: 3. 下载电子票PDF
+ Client->>Controller: GET /api/v1/registrations/{id}/ticket
Header: Authorization: Bearer token
+ Controller->>Service: generateTicketPdf(id)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>Mapper: selectById(id)
+ Mapper->>DB: SELECT * FROM registration
WHERE id = ?
+ DB-->>Mapper: 返回报名记录
+ Mapper-->>Service: 返回Registration对象
+ alt 报名记录不存在
+ Service-->>Controller: 抛出异常
REGISTRATION_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 报名记录存在
+ alt 不是自己的报名
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是自己的报名
+ alt 报名状态不是已报名
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 报名状态是已报名
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ Service->>PdfUtil: generateTicketPdf(user, activity, registration)
+ PdfUtil-->>Service: 返回PDF字节数组
+ Service-->>Controller: 返回PDF字节数组
+ Controller-->Client: 200 成功
Content-Type: application/pdf
+ end
+ end
+ end
+```
+
+---
+
+## 4. 签到管理流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as CheckInController
签到控制器
+ participant Service as CheckInServiceImpl
签到服务
+ participant Mapper as CheckInMapper
签到数据访问
+ participant RegMapper as RegistrationMapper
报名数据访问
+ participant ActMapper as ActivityMapper
活动数据访问
+ participant Auth as AuthService
认证服务
+ participant QrUtil as QrCodeUtil
二维码工具类
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 生成签到二维码(管理员)
+ Client->>Controller: POST /api/v1/checkin/qrcode/{activityId}
Header: Authorization: Bearer token
+ Controller->>Service: generateQrCode(activityId)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ alt 用户不是管理员
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 用户是管理员
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ alt 不是活动创建者
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是活动创建者
+ Service->>QrUtil: generateQrCodeContent(activityId)
+ QrUtil-->>Service: 返回二维码内容
+ Service->>Service: 构建QrCodeVO
+ Service-->>Controller: 返回QrCodeVO
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+
+ Note over Client,DB: 2. 学生扫码签到
+ Client->>Controller: POST /api/v1/checkin/scan
ScanCheckInRequest
Header: Authorization: Bearer token
+ Controller->>Service: scanCheckIn(request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>QrUtil: parseActivityIdFromQrCode(qrCodeContent)
+ QrUtil-->>Service: 返回activityId
+ alt 二维码无效
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 二维码有效
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ Service->>Service: 检查签到时间
startTime - 1小时 <= now <= endTime
+ alt 签到时间已过期
+ Service-->>Controller: 抛出异常
CHECKIN_TIME_EXPIRED
+ Controller-->>Client: 400 错误
+ else 签到时间有效
+ Service->>RegMapper: 查询报名记录
selectOne(userId, activityId, status=1)
+ RegMapper->>DB: SELECT * FROM registration
WHERE user_id = ? AND activity_id = ?
AND status = 1
+ DB-->>RegMapper: 返回报名记录
+ RegMapper-->>Service: 返回Registration对象
+ alt 未报名该活动
+ Service-->>Controller: 抛出异常
NOT_REGISTERED
+ Controller-->>Client: 400 错误
+ else 已报名该活动
+ Service->>Mapper: 检查是否已签到
selectOne(registrationId)
+ Mapper->>DB: SELECT * FROM check_in
WHERE registration_id = ?
+ DB-->>Mapper: 返回签到记录
+ Mapper-->>Service: 返回CheckIn对象
+ alt 已签到
+ Service-->>Controller: 抛出异常
ALREADY_CHECKED_IN
+ Controller-->>Client: 409 冲突
+ else 未签到
+ Service->>Service: performCheckIn(registration, user, activityId, method=0)
+ Service->>Mapper: insert(checkIn)
+ Mapper->>DB: INSERT INTO check_in(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回checkInId
+ Service->>RegMapper: 更新报名状态为已签到
updateById(registration)
+ RegMapper->>DB: UPDATE registration SET status = 2
WHERE id = ?
+ DB-->>RegMapper: 返回更新结果
+ RegMapper-->>Service: 返回成功
+ Service->>Service: convertToVO()
+ Service-->>Controller: 返回CheckInVO
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+ end
+ end
+
+ Note over Client,DB: 3. 管理员扫学生票签到
+ Client->>Controller: POST /api/v1/checkin/ticket
TicketCheckInRequest
Header: Authorization: Bearer token
+ Controller->>Service: ticketCheckIn(request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ alt 用户不是管理员
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 用户是管理员
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ alt 不是活动创建者
+ Service-->>Controller: 抛出异常
FORBIDDEN
+ Controller-->>Client: 403 禁止访问
+ else 是活动创建者
+ Service->>Service: 检查签到时间
+ alt 签到时间已过期
+ Service-->>Controller: 抛出异常
CHECKIN_TIME_EXPIRED
+ Controller-->>Client: 400 错误
+ else 签到时间有效
+ Service->>RegMapper: selectByTicketCode(ticketCode)
+ RegMapper->>DB: SELECT * FROM registration
WHERE ticket_code = ?
+ DB-->>RegMapper: 返回报名记录
+ RegMapper-->>Service: 返回Registration对象
+ alt 电子票无效
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 电子票有效
+ alt 电子票与活动不匹配
+ Service-->>Controller: 抛出异常
BAD_REQUEST
+ Controller-->>Client: 400 错误
+ else 电子票与活动匹配
+ Service->>Mapper: 检查是否已签到
selectOne(registrationId)
+ Mapper->>DB: SELECT * FROM check_in
WHERE registration_id = ?
+ DB-->>Mapper: 返回签到记录
+ Mapper-->>Service: 返回CheckIn对象
+ alt 已签到
+ Service-->>Controller: 抛出异常
ALREADY_CHECKED_IN
+ Controller-->>Client: 409 冲突
+ else 未签到
+ Service->>Auth: getUserById(userId)
+ Auth-->>Service: 返回学生信息
+ Service->>Service: performCheckIn(registration, student, activityId, method=1)
+ Service->>Mapper: insert(checkIn)
+ Mapper->>DB: INSERT INTO check_in(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回checkInId
+ Service->>RegMapper: 更新报名状态
updateById(registration)
+ RegMapper->>DB: UPDATE registration SET status = 2
WHERE id = ?
+ DB-->>RegMapper: 返回更新结果
+ RegMapper-->>Service: 返回成功
+ Service->>Service: convertToVO()
+ Service-->>Controller: 返回CheckInVO
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+```
+
+---
+
+## 5. 评价管理流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as ReviewController
评价控制器
+ participant Service as ReviewServiceImpl
评价服务
+ participant Mapper as ReviewMapper
评价数据访问
+ participant ActMapper as ActivityMapper
活动数据访问
+ participant CheckInMapper as CheckInMapper
签到数据访问
+ participant Auth as AuthService
认证服务
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 提交评价
+ Client->>Controller: POST /api/v1/reviews
ReviewRequest
Header: Authorization: Bearer token
+ Controller->>Service: createReview(request)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ Service->>Mapper: 检查是否已评价
selectOne(userId, activityId)
+ Mapper->>DB: SELECT * FROM review
WHERE user_id = ? AND activity_id = ?
+ DB-->>Mapper: 返回评价记录
+ Mapper-->>Service: 返回Review对象
+ alt 已评价该活动
+ Service-->>Controller: 抛出异常
REVIEW_ALREADY_EXISTS
+ Controller-->>Client: 409 冲突
+ else 未评价该活动
+ Service->>CheckInMapper: 检查是否已签到
selectOne(userId, activityId)
+ CheckInMapper->>DB: SELECT * FROM check_in
WHERE user_id = ? AND activity_id = ?
+ DB-->>CheckInMapper: 返回签到记录
+ CheckInMapper-->>Service: 返回CheckIn对象
+ alt 未参加该活动
+ Service-->>Controller: 抛出异常
NOT_PARTICIPATED
+ Controller-->>Client: 400 错误
+ else 已参加该活动
+ Service->>Service: 创建Review对象
+ Service->>Mapper: insert(review)
+ Mapper->>DB: INSERT INTO review(...)
+ DB-->>Mapper: 返回插入ID
+ Mapper-->>Service: 返回reviewId
+ Service->>Service: convertToVO()
+ Service-->>Controller: 返回ReviewVO
+ Controller-->>Client: 200 成功
+ end
+ end
+ end
+
+ Note over Client,DB: 2. 获取活动评价列表
+ Client->>Controller: GET /api/v1/reviews/activity/{activityId}
(current, size)
+ Controller->>Service: getActivityReviews(page, activityId)
+ Service->>Mapper: selectActivityReviews(page, activityId)
+ Mapper->>DB: SELECT * FROM review
WHERE activity_id = ?
LIMIT offset, size
+ DB-->>Mapper: 返回评价列表
+ Mapper-->>Service: 返回IPage
+ Service-->>Controller: 返回IPage
+ Controller-->>Client: 200 成功
+
+ Note over Client,DB: 3. 获取我的评价列表
+ Client->>Controller: GET /api/v1/reviews/my
(current, size)
Header: Authorization: Bearer token
+ Controller->>Service: getMyReviews(page)
+ Service->>Auth: getCurrentUser()
+ Auth-->>Service: 返回当前用户
+ Service->>Mapper: selectMyReviews(page, userId)
+ Mapper->>DB: SELECT * FROM review
WHERE user_id = ?
LIMIT offset, size
+ DB-->>Mapper: 返回评价列表
+ Mapper-->>Service: 返回IPage
+ Service-->>Controller: 返回IPage
+ Controller-->>Client: 200 成功
+```
+
+---
+
+## 6. 统计数据流程
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Client as 客户端
+ participant Controller as StatisticsController
统计控制器
+ participant Service as StatisticsServiceImpl
统计服务
+ participant RegMapper as RegistrationMapper
报名数据访问
+ participant CheckInMapper as CheckInMapper
签到数据访问
+ participant ReviewMapper as ReviewMapper
评价数据访问
+ participant ActMapper as ActivityMapper
活动数据访问
+ participant CsvUtil as CsvUtil
CSV工具类
+ participant DB as MySQL数据库
+
+ Note over Client,DB: 1. 获取活动统计数据(管理员)
+ Client->>Controller: GET /api/v1/statistics/activity/{activityId}
Header: Authorization: Bearer token
+ Controller->>Service: getActivityStatistics(activityId)
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ Service->>RegMapper: 统计报名人数
selectCount(activityId, status IN (1,2))
+ RegMapper->>DB: SELECT COUNT(*) FROM registration
WHERE activity_id = ?
AND status IN (1, 2)
+ DB-->>RegMapper: 返回报名人数
+ RegMapper-->>Service: 返回registeredCount
+
+ Service->>CheckInMapper: 查询所有签到记录
selectList(activityId)
+ CheckInMapper->>DB: SELECT * FROM check_in
WHERE activity_id = ?
+ DB-->>CheckInMapper: 返回签到列表
+ CheckInMapper-->>Service: 返回List
+ Service->>Service: 计算签到人数
+
+ Service->>ReviewMapper: 统计评价数量
selectCount(activityId)
+ ReviewMapper->>DB: SELECT COUNT(*) FROM review
WHERE activity_id = ?
+ DB-->>ReviewMapper: 返回评价数量
+ ReviewMapper-->>Service: 返回reviewCount
+
+ Service->>ReviewMapper: 查询所有评价
selectList(activityId)
+ ReviewMapper->>DB: SELECT * FROM review
WHERE activity_id = ?
+ DB-->>ReviewMapper: 返回评价列表
+ ReviewMapper-->>Service: 返回List
+ Service->>Service: 计算平均评分
+ Service->>Service: 计算评分分布
+ Service->>Service: 计算签到率
+ Service->>Service: 构建ActivityStatisticsVO
+ Service-->>Controller: 返回ActivityStatisticsVO
+ Controller-->>Client: 200 成功
+ end
+
+ Note over Client,DB: 2. 导出活动数据(管理员)
+ Client->>Controller: GET /api/v1/statistics/activity/{activityId}/export
Header: Authorization: Bearer token
+ Controller->>Service: exportActivityData(activityId, format)
+ Service->>ActMapper: selectById(activityId)
+ ActMapper->>DB: SELECT * FROM activity
WHERE id = ?
+ DB-->>ActMapper: 返回活动信息
+ ActMapper-->>Service: 返回Activity对象
+ alt 活动不存在或已删除
+ Service-->>Controller: 抛出异常
ACTIVITY_NOT_FOUND
+ Controller-->>Client: 404 未找到
+ else 活动存在
+ Service->>RegMapper: 查询活动报名列表
selectList(activityId)
+ RegMapper->>DB: SELECT * FROM registration
WHERE activity_id = ?
+ DB-->>RegMapper: 返回报名列表
+ RegMapper-->>Service: 返回List
+
+ Service->>CheckInMapper: 查询活动签到列表
selectList(activityId)
+ CheckInMapper->>DB: SELECT * FROM check_in
WHERE activity_id = ?
+ DB-->>CheckInMapper: 返回签到列表
+ CheckInMapper-->>Service: 返回List
+
+ Service->>ReviewMapper: 查询活动评价列表
selectList(activityId)
+ ReviewMapper->>DB: SELECT * FROM review
WHERE activity_id = ?
+ DB-->>ReviewMapper: 返回评价列表
+ ReviewMapper-->>Service: 返回List
+
+ Service->>CsvUtil: exportActivityData(activity, registrations, checkIns, reviews)
+ CsvUtil-->>Service: 返回CSV字节数组
+ Service-->>Controller: 返回CSV字节数组
+ Controller->>Client: 200 成功
Content-Type: text/csv
+ end
+
+ Note over Client,DB: 3. 获取总体统计(管理员)
+ Client->>Controller: GET /api/v1/statistics/overview
Header: Authorization: Bearer token
+ Controller->>Service: getOverviewStatistics()
+
+ Service->>ActMapper: 统计活动总数
selectCount(deleted=0)
+ ActMapper->>DB: SELECT COUNT(*) FROM activity
WHERE deleted = 0
+ DB-->>ActMapper: 返回活动总数
+ ActMapper-->>Service: 返回totalActivities
+
+ Service->>RegMapper: 统计报名总数
selectCount(status=1)
+ RegMapper->>DB: SELECT COUNT(*) FROM registration
WHERE status = 1
+ DB-->>RegMapper: 返回报名总数
+ RegMapper-->>Service: 返回totalRegistrations
+
+ Service->>CheckInMapper: 统计签到总数
selectCount()
+ CheckInMapper->>DB: SELECT COUNT(*) FROM check_in
+ DB-->>CheckInMapper: 返回签到总数
+ CheckInMapper-->>Service: 返回totalCheckIns
+
+ Service->>ReviewMapper: 统计评价总数
selectCount()
+ ReviewMapper->>DB: SELECT COUNT(*) FROM review
+ DB-->>ReviewMapper: 返回评价总数
+ ReviewMapper-->>Service: 返回totalReviews
+
+ Service->>ReviewMapper: 查询所有评价
selectList()
+ ReviewMapper->>DB: SELECT * FROM review
+ DB-->>ReviewMapper: 返回评价列表
+ ReviewMapper-->>Service: 返回List
+ Service->>Service: 计算平均评分
+
+ Service->>Service: calculateMonthlyStats()
+ loop 最近6个月
+ Service->>ActMapper: 统计月活动数
selectCount(createdAt BETWEEN monthStart AND monthEnd)
+ ActMapper->>DB: SELECT COUNT(*) FROM activity
WHERE created_at BETWEEN ? AND ?
+ DB-->>ActMapper: 返回月活动数
+ ActMapper-->>Service: 返回activityCount
+
+ Service->>RegMapper: 统计月报名数
selectCount(createdAt BETWEEN monthStart AND monthEnd)
+ RegMapper->>DB: SELECT COUNT(*) FROM registration
WHERE created_at BETWEEN ? AND ?
+ DB-->>RegMapper: 返回月报名数
+ RegMapper-->>Service: 返回registrationCount
+ end
+ Service->>Service: 构建OverviewStatisticsVO
+ Service-->>Controller: 返回OverviewStatisticsVO
+ Controller-->>Client: 200 成功
+```
+
+---
+
+## 系统分层架构图
+
+```mermaid
+graph TB
+ subgraph "表现层 (Controller Layer)"
+ A1[AuthController
认证控制器]
+ A2[ActivityController
活动控制器]
+ A3[RegistrationController
报名控制器]
+ A4[CheckInController
签到控制器]
+ A5[ReviewController
评价控制器]
+ A6[StatisticsController
统计控制器]
+ end
+
+ subgraph "业务逻辑层 (Service Layer)"
+ B1[AuthServiceImpl
认证服务]
+ B2[ActivityServiceImpl
活动服务]
+ B3[RegistrationServiceImpl
报名服务]
+ B4[CheckInServiceImpl
签到服务]
+ B5[ReviewServiceImpl
评价服务]
+ B6[StatisticsServiceImpl
统计服务]
+ end
+
+ subgraph "数据访问层 (Mapper Layer)"
+ C1[UserMapper
用户数据访问]
+ C2[ActivityMapper
活动数据访问]
+ C3[RegistrationMapper
报名数据访问]
+ C4[CheckInMapper
签到数据访问]
+ C5[ReviewMapper
评价数据访问]
+ end
+
+ subgraph "实体层 (Entity Layer)"
+ D1[User
用户实体]
+ D2[Activity
活动实体]
+ D3[Registration
报名实体]
+ D4[CheckIn
签到实体]
+ D5[Review
评价实体]
+ end
+
+ subgraph "工具层 (Util Layer)"
+ E1[JwtTokenProvider
JWT令牌提供者]
+ E2[PdfUtil
PDF工具类]
+ E3[QrCodeUtil
二维码工具类]
+ E4[CsvUtil
CSV工具类]
+ end
+
+ subgraph "安全层 (Security Layer)"
+ F1[SecurityConfig
安全配置]
+ F2[JwtAuthenticationFilter
JWT认证过滤器]
+ F3[UserDetailsServiceImpl
用户详情服务]
+ end
+
+ subgraph "数据库层 (Database Layer)"
+ G1[(MySQL数据库)]
+ end
+
+ A1 --> B1
+ A2 --> B2
+ A3 --> B3
+ A4 --> B4
+ A5 --> B5
+ A6 --> B6
+
+ B1 --> C1
+ B1 --> E1
+ B2 --> C2
+ B2 --> C1
+ B2 --> C3
+ B3 --> C3
+ B3 --> C2
+ B3 --> E2
+ B4 --> C4
+ B4 --> C3
+ B4 --> C2
+ B4 --> E3
+ B5 --> C5
+ B5 --> C2
+ B5 --> C4
+ B6 --> C3
+ B6 --> C4
+ B6 --> C5
+ B6 --> C2
+ B6 --> E4
+
+ C1 --> D1
+ C2 --> D2
+ C3 --> D3
+ C4 --> D4
+ C5 --> D5
+
+ C1 --> G1
+ C2 --> G1
+ C3 --> G1
+ C4 --> G1
+ C5 --> G1
+
+ F1 --> F2
+ F1 --> F3
+ F2 --> E1
+ F3 --> C1
+
+ style A1 fill:#e1f5ff
+ style A2 fill:#e1f5ff
+ style A3 fill:#e1f5ff
+ style A4 fill:#e1f5ff
+ style A5 fill:#e1f5ff
+ style A6 fill:#e1f5ff
+ style B1 fill:#fff4e1
+ style B2 fill:#fff4e1
+ style B3 fill:#fff4e1
+ style B4 fill:#fff4e1
+ style B5 fill:#fff4e1
+ style B6 fill:#fff4e1
+ style C1 fill:#e8f5e9
+ style C2 fill:#e8f5e9
+ style C3 fill:#e8f5e9
+ style C4 fill:#e8f5e9
+ style C5 fill:#e8f5e9
+ style D1 fill:#f3e5f5
+ style D2 fill:#f3e5f5
+ style D3 fill:#f3e5f5
+ style D4 fill:#f3e5f5
+ style D5 fill:#f3e5f5
+ style E1 fill:#fce4ec
+ style E2 fill:#fce4ec
+ style E3 fill:#fce4ec
+ style E4 fill:#fce4ec
+ style F1 fill:#fff9c4
+ style F2 fill:#fff9c4
+ style F3 fill:#fff9c4
+ style G1 fill:#cfd8dc
+```
+
+---
+
+## 核心业务流程总览
+
+```mermaid
+graph LR
+ subgraph "用户认证"
+ A[注册] --> B[登录]
+ B --> C[获取Token]
+ end
+
+ subgraph "活动管理"
+ D[创建活动] --> E[发布活动]
+ E --> F[时间冲突检测]
+ F --> G[活动列表]
+ G --> H[活动详情]
+ H --> I[更新活动]
+ I --> J[删除活动]
+ end
+
+ subgraph "报名流程"
+ K[浏览活动] --> L[报名活动]
+ L --> M[生成电子票]
+ M --> N[下载PDF]
+ N --> O[取消报名]
+ end
+
+ subgraph "签到流程"
+ P[生成二维码] --> Q[学生扫码]
+ Q --> R[管理员扫码]
+ R --> S[签到成功]
+ end
+
+ subgraph "评价流程"
+ T[参加活动] --> U[签到完成]
+ U --> V[提交评价]
+ V --> W[查看评价]
+ end
+
+ subgraph "统计分析"
+ X[活动统计] --> Y[数据导出]
+ Y --> Z[总体统计]
+ end
+
+ C --> K
+ C --> D
+ C --> P
+ C --> V
+ C --> X
+
+ style A fill:#4CAF50,color:#fff
+ style B fill:#4CAF50,color:#fff
+ style C fill:#4CAF50,color:#fff
+ style D fill:#2196F3,color:#fff
+ style E fill:#2196F3,color:#fff
+ style F fill:#2196F3,color:#fff
+ style G fill:#2196F3,color:#fff
+ style H fill:#2196F3,color:#fff
+ style I fill:#2196F3,color:#fff
+ style J fill:#2196F3,color:#fff
+ style K fill:#FF9800,color:#fff
+ style L fill:#FF9800,color:#fff
+ style M fill:#FF9800,color:#fff
+ style N fill:#FF9800,color:#fff
+ style O fill:#FF9800,color:#fff
+ style P fill:#9C27B0,color:#fff
+ style Q fill:#9C27B0,color:#fff
+ style R fill:#9C27B0,color:#fff
+ style S fill:#9C27B0,color:#fff
+ style T fill:#E91E63,color:#fff
+ style U fill:#E91E63,color:#fff
+ style V fill:#E91E63,color:#fff
+ style W fill:#E91E63,color:#fff
+ style X fill:#607D8B,color:#fff
+ style Y fill:#607D8B,color:#fff
+ style Z fill:#607D8B,color:#fff
+```
+
+---
+
+## 代码文件索引
+
+### Controller层
+- `AuthController.java` - 认证控制器
+- `ActivityController.java` - 活动控制器
+- `RegistrationController.java` - 报名控制器
+- `CheckInController.java` - 签到控制器
+- `ReviewController.java` - 评价控制器
+- `StatisticsController.java` - 统计控制器
+
+### Service层
+- `AuthServiceImpl.java` - 认证服务实现
+- `ActivityServiceImpl.java` - 活动服务实现
+- `RegistrationServiceImpl.java` - 报名服务实现
+- `CheckInServiceImpl.java` - 签到服务实现
+- `ReviewServiceImpl.java` - 评价服务实现
+- `StatisticsServiceImpl.java` - 统计服务实现
+
+### Mapper层
+- `UserMapper.java` - 用户数据访问
+- `ActivityMapper.java` - 活动数据访问
+- `RegistrationMapper.java` - 报名数据访问
+- `CheckInMapper.java` - 签到数据访问
+- `ReviewMapper.java` - 评价数据访问
+
+### Entity层
+- `User.java` - 用户实体
+- `Activity.java` - 活动实体
+- `Registration.java` - 报名实体
+- `CheckIn.java` - 签到实体
+- `Review.java` - 评价实体
+
+### Security层
+- `SecurityConfig.java` - 安全配置
+- `JwtAuthenticationFilter.java` - JWT认证过滤器
+- `JwtTokenProvider.java` - JWT令牌提供者
+- `UserDetailsServiceImpl.java` - 用户详情服务
+
+### Util层
+- `PdfUtil.java` - PDF工具类
+- `QrCodeUtil.java` - 二维码工具类
+- `CsvUtil.java` - CSV工具类
+
+---
+
+## 总结
+
+本业务流程图详细展示了校园活动系统的核心业务流程,包括:
+
+1. **用户认证流程**:注册、登录、Token刷新、获取用户信息
+2. **活动管理流程**:创建、查询、更新、删除活动,时间冲突检测
+3. **活动报名流程**:报名、取消报名、生成电子票、下载PDF
+4. **签到管理流程**:生成二维码、学生扫码签到、管理员扫码签到
+5. **评价管理流程**:提交评价、查看活动评价、查看我的评价
+6. **统计数据流程**:活动统计、数据导出、总体统计
+
+每个流程都详细标注了涉及的代码文件、方法名和数据库操作,清晰地展示了从Controller层到Service层、Mapper层,最后到数据库的完整调用链路。
+
+系统采用标准的分层架构,各层职责清晰:
+- **Controller层**:处理HTTP请求和响应
+- **Service层**:实现核心业务逻辑
+- **Mapper层**:与数据库交互
+- **Entity层**:数据模型定义
+- **Security层**:JWT认证和授权
+- **Util层**:提供工具类支持
+
+这种架构设计保证了代码的可维护性、可扩展性和可测试性。
\ No newline at end of file
diff --git a/server/docs/前端开发规范.md b/server/docs/前端开发规范.md
deleted file mode 100644
index c300384..0000000
--- a/server/docs/前端开发规范.md
+++ /dev/null
@@ -1,1343 +0,0 @@
-# 校园活动组织与报名系统 - 前端开发规范
-
-## 1. 命名规范
-
-### 1.1 文件命名
-
-| 类型 | 规范 | 示例 |
-|------|------|------|
-| Vue 组件 | PascalCase | `ActivityCard.vue`, `NavBar.vue` |
-| TypeScript 文件 | camelCase | `useAuth.ts`, `request.ts` |
-| 样式文件 | kebab-case | `global.scss`, `variables.scss` |
-| 常量文件 | camelCase | `constant.ts` |
-| 类型定义文件 | kebab-case.d.ts | `user.d.ts`, `activity.d.ts` |
-
-### 1.2 变量命名
-
-```typescript
-// 常量:全大写下划线分隔
-const API_BASE_URL = '/api/v1'
-const MAX_FILE_SIZE = 1024 * 1024 * 5
-
-// 变量:小驼峰
-const userName = 'zhangsan'
-const activityList = []
-
-// 布尔值:is/has/can 前缀
-const isLoading = false
-const hasPermission = true
-const canEdit = false
-
-// 私有属性:下划线前缀
-const _privateData = {}
-
-// 组件 props:小驼峰
-const props = defineProps<{
- activityId: number
- showDetail: boolean
-}>()
-
-// 事件名:on 前缀
-const emit = defineEmits<{
- onSubmit: [data: FormData]
- onCancel: []
-}>()
-```
-
-### 1.3 函数命名
-
-```typescript
-// 获取数据:get 前缀
-function getActivityList() {}
-function getUserInfo() {}
-
-// 设置数据:set 前缀
-function setToken(token: string) {}
-
-// 处理事件:handle 前缀
-function handleSubmit() {}
-function handleClick() {}
-
-// 判断逻辑:is/has/can 前缀
-function isAdmin() {}
-function hasRegistered() {}
-function canCheckIn() {}
-
-// 格式化:format 前缀
-function formatDate(date: Date) {}
-function formatPrice(price: number) {}
-
-// 转换:to 前缀
-function toArray(data: any) {}
-function toString(value: any) {}
-
-// 异步操作:async/await
-async function fetchActivities() {}
-async function submitRegistration() {}
-```
-
-### 1.4 CSS 类命名 (BEM 规范)
-
-```scss
-// Block__Element--Modifier
-
-// 活动卡片
-.activity-card {
- // Element
- &__header {}
- &__title {}
- &__content {}
- &__footer {}
- &__image {}
-
- // Modifier
- &--active {}
- &--disabled {}
- &--loading {}
-}
-
-// 示例
-
-```
-
-### 1.5 路由命名
-
-```typescript
-// 路由 name:PascalCase
-{
- path: '/activities',
- name: 'ActivityList',
- component: () => import('@/views/activity/List.vue')
-}
-
-// 路由 path:kebab-case
-{
- path: '/my/registrations',
- name: 'MyRegistrations',
- component: () => import('@/views/registration/MyList.vue')
-}
-```
-
----
-
-## 2. 代码风格规范
-
-### 2.1 Vue 组件结构
-
-```vue
-
-
-
-
-
-
-
-```
-
-### 2.2 TypeScript 规范
-
-```typescript
-// 1. 明确类型声明,避免使用 any
-// Bad
-const data: any = {}
-
-// Good
-interface ActivityData {
- id: number
- title: string
-}
-const data: ActivityData = { id: 1, title: '活动' }
-
-// 2. 使用 interface 定义对象类型
-interface User {
- id: number
- username: string
- name: string
- role: 0 | 1
-}
-
-// 3. 使用 type 定义联合类型
-type ActivityStatus = 0 | 1 | 2 | 3
-type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
-
-// 4. 使用枚举定义常量集合
-enum ActivityStatusEnum {
- PENDING = 0, // 未开始
- OPEN = 1, // 报名中
- ONGOING = 2, // 进行中
- ENDED = 3 // 已结束
-}
-
-// 5. 泛型使用
-interface ApiResponse {
- code: number
- message: string
- data: T
- timestamp: number
-}
-
-// 6. 可选属性使用 ?
-interface CreateActivityParams {
- title: string
- description?: string
- coverImage?: string
-}
-
-// 7. 只读属性使用 readonly
-interface Config {
- readonly apiUrl: string
- readonly timeout: number
-}
-```
-
-### 2.3 API 请求规范
-
-```typescript
-// src/api/activity.ts
-import request from '@/utils/request'
-import type { Activity, ActivityListParams, CreateActivityParams } from '@/types/activity'
-import type { ApiResponse, PageResult } from '@/types/api'
-
-/**
- * 获取活动列表
- * @param params 查询参数
- */
-export function getActivityList(params: ActivityListParams) {
- return request.get>>('/activities', { params })
-}
-
-/**
- * 获取活动详情
- * @param id 活动ID
- */
-export function getActivityDetail(id: number) {
- return request.get>(`/activities/${id}`)
-}
-
-/**
- * 创建活动(管理员)
- * @param data 活动数据
- */
-export function createActivity(data: CreateActivityParams) {
- return request.post>('/activities', data)
-}
-
-/**
- * 更新活动(管理员)
- * @param id 活动ID
- * @param data 更新数据
- */
-export function updateActivity(id: number, data: Partial) {
- return request.put>(`/activities/${id}`, data)
-}
-
-/**
- * 删除活动(管理员)
- * @param id 活动ID
- */
-export function deleteActivity(id: number) {
- return request.delete>(`/activities/${id}`)
-}
-```
-
-### 2.4 Store 规范
-
-```typescript
-// src/stores/user.ts
-import { defineStore } from 'pinia'
-import { ref, computed } from 'vue'
-import type { UserInfo } from '@/types/user'
-import { login, getUserInfo, refreshToken } from '@/api/auth'
-
-export const useUserStore = defineStore('user', () => {
- // State
- const token = ref(localStorage.getItem('accessToken'))
- const refreshTokenValue = ref(localStorage.getItem('refreshToken'))
- const userInfo = ref(null)
-
- // Getters
- const isLoggedIn = computed(() => !!token.value)
- const isAdmin = computed(() => userInfo.value?.role === 1)
- const userName = computed(() => userInfo.value?.name || '')
-
- // Actions
- async function loginAction(username: string, password: string) {
- try {
- const res = await login({ username, password })
- token.value = res.data.accessToken
- refreshTokenValue.value = res.data.refreshToken
- userInfo.value = res.data.userInfo
-
- localStorage.setItem('accessToken', res.data.accessToken)
- localStorage.setItem('refreshToken', res.data.refreshToken)
-
- return true
- } catch (error) {
- return false
- }
- }
-
- function logout() {
- token.value = null
- refreshTokenValue.value = null
- userInfo.value = null
-
- localStorage.removeItem('accessToken')
- localStorage.removeItem('refreshToken')
- }
-
- async function fetchUserInfo() {
- try {
- const res = await getUserInfo()
- userInfo.value = res.data
- } catch (error) {
- console.error('获取用户信息失败', error)
- }
- }
-
- return {
- // State
- token,
- userInfo,
- // Getters
- isLoggedIn,
- isAdmin,
- userName,
- // Actions
- loginAction,
- logout,
- fetchUserInfo
- }
-})
-```
-
-### 2.5 Composable 规范
-
-```typescript
-// src/composables/usePagination.ts
-import { ref, computed } from 'vue'
-
-interface PaginationOptions {
- defaultPage?: number
- defaultSize?: number
-}
-
-export function usePagination(options: PaginationOptions = {}) {
- const { defaultPage = 1, defaultSize = 10 } = options
-
- const currentPage = ref(defaultPage)
- const pageSize = ref(defaultSize)
- const total = ref(0)
-
- const totalPages = computed(() => Math.ceil(total.value / pageSize.value))
- const hasMore = computed(() => currentPage.value < totalPages.value)
-
- function setTotal(value: number) {
- total.value = value
- }
-
- function nextPage() {
- if (hasMore.value) {
- currentPage.value++
- }
- }
-
- function prevPage() {
- if (currentPage.value > 1) {
- currentPage.value--
- }
- }
-
- function reset() {
- currentPage.value = defaultPage
- total.value = 0
- }
-
- return {
- currentPage,
- pageSize,
- total,
- totalPages,
- hasMore,
- setTotal,
- nextPage,
- prevPage,
- reset
- }
-}
-```
-
----
-
-## 3. 组件开发规范
-
-### 3.1 组件分类
-
-| 类型 | 位置 | 说明 |
-|------|------|------|
-| 基础组件 | components/common/ | 通用 UI 组件,如 NavBar、TabBar |
-| 业务组件 | components/[module]/ | 特定业务模块组件 |
-| 页面组件 | views/[module]/ | 路由对应的页面组件 |
-| 布局组件 | layouts/ | 页面布局组件 |
-
-### 3.2 组件设计原则
-
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### 3.3 组件 Props 规范
-
-```typescript
-// 1. 使用 TypeScript 定义 Props
-interface Props {
- // 必填属性
- activityId: number
- // 可选属性,带默认值
- showImage?: boolean
- // 复杂类型
- activity: Activity
-}
-
-const props = withDefaults(defineProps(), {
- showImage: true
-})
-
-// 2. Props 校验(可选,TS 已提供类型检查)
-const props = defineProps({
- activityId: {
- type: Number,
- required: true
- },
- status: {
- type: Number,
- default: 0,
- validator: (value: number) => [0, 1, 2, 3].includes(value)
- }
-})
-```
-
-### 3.4 组件样式规范
-
-```vue
-
-```
-
----
-
-## 4. 类型定义规范
-
-### 4.1 API 响应类型
-
-```typescript
-// src/types/api.d.ts
-
-// 统一响应格式
-interface ApiResponse {
- code: number
- message: string
- data: T
- timestamp: number
-}
-
-// 分页响应
-interface PageResult {
- records: T[]
- total: number
- pages: number
- current: number
- size: number
-}
-
-// 分页请求参数
-interface PageParams {
- page?: number
- size?: number
-}
-```
-
-### 4.2 业务类型定义
-
-```typescript
-// src/types/user.d.ts
-interface UserInfo {
- id: number
- username: string
- name: string
- studentId: string | null
- email: string | null
- phone: string | null
- avatar: string | null
- role: 0 | 1 // 0-学生,1-管理员
-}
-
-interface LoginRequest {
- username: string
- password: string
-}
-
-interface LoginResponse {
- accessToken: string
- refreshToken: string
- expiresIn: number
- tokenType: string
- userInfo: UserInfo
-}
-
-// src/types/activity.d.ts
-interface Activity {
- id: number
- title: string
- description: string
- coverImage: string | null
- startTime: string
- endTime: string
- registrationDeadline: string | null
- location: string
- maxParticipants: number
- currentParticipants: number
- status: ActivityStatus
- category: string | null
- adminId: number
- adminName?: string
- averageRating?: number
- reviewCount?: number
- isRegistered?: boolean
- createdAt: string
-}
-
-type ActivityStatus = 0 | 1 | 2 | 3
-
-interface ActivityListParams extends PageParams {
- status?: ActivityStatus
- keyword?: string
- category?: string
- startDate?: string
- endDate?: string
-}
-
-interface CreateActivityParams {
- title: string
- description?: string
- coverImage?: string
- startTime: string
- endTime: string
- registrationDeadline?: string
- location: string
- maxParticipants: number
- category?: string
-}
-
-// src/types/registration.d.ts
-interface Registration {
- id: number
- activityId: number
- activityTitle: string
- activityStartTime: string
- activityLocation: string
- ticketCode: string
- ticketPdfUrl: string
- status: RegistrationStatus
- createdAt: string
-}
-
-type RegistrationStatus = 0 | 1 | 2 // 0-已取消,1-已报名,2-已签到
-
-// src/types/review.d.ts
-interface Review {
- id: number
- userId: number
- userName: string
- userAvatar: string | null
- activityId: number
- rating: 1 | 2 | 3 | 4 | 5
- content: string
- createdAt: string
-}
-
-interface CreateReviewParams {
- activityId: number
- rating: number
- content: string
-}
-```
-
----
-
-## 5. 错误处理规范
-
-### 5.1 API 错误处理
-
-```typescript
-// src/utils/request.ts
-import axios from 'axios'
-import { showToast, showDialog } from 'vant'
-import router from '@/router'
-import { useUserStore } from '@/stores/user'
-
-const instance = axios.create({
- baseURL: import.meta.env.VITE_API_BASE_URL,
- timeout: 10000
-})
-
-// 响应拦截器
-instance.interceptors.response.use(
- response => {
- const { code, message, data } = response.data
-
- // 业务成功
- if (code === 200) {
- return response.data
- }
-
- // 业务错误
- showToast(message || '操作失败')
- return Promise.reject(new Error(message))
- },
- error => {
- const { response } = error
-
- if (response) {
- const { status, data } = response
-
- switch (status) {
- case 400:
- showToast(data.message || '请求参数错误')
- break
- case 401:
- // Token 过期,清除登录状态,跳转登录页
- const userStore = useUserStore()
- userStore.logout()
- router.push('/login')
- showToast('登录已过期,请重新登录')
- break
- case 403:
- showToast('无权限访问')
- break
- case 404:
- showToast('请求的资源不存在')
- break
- case 409:
- // 业务冲突,显示具体原因
- showToast(data.message || '操作冲突')
- break
- case 500:
- showToast('服务器异常,请稍后重试')
- break
- default:
- showToast('网络异常')
- }
- } else {
- showToast('网络连接失败')
- }
-
- return Promise.reject(error)
- }
-)
-```
-
-### 5.2 组件错误处理
-
-```vue
-
-```
-
-### 5.3 表单校验
-
-```vue
-
-
-
-
-
-
-
-
- 提交
-
-
-
-
-
-
-```
-
----
-
-## 6. 注释规范
-
-### 6.1 文件头注释
-
-```typescript
-/**
- * @file 活动相关 API 接口
- * @author Your Name
- * @description 包含活动的增删改查、日历视图、冲突检测等接口
- */
-```
-
-### 6.2 函数注释
-
-```typescript
-/**
- * 格式化日期时间
- * @param date - 日期字符串或 Date 对象
- * @param format - 格式化模板,默认 'YYYY-MM-DD HH:mm'
- * @returns 格式化后的日期字符串
- * @example
- * formatDateTime('2025-06-01T09:00:00') // '2025-06-01 09:00'
- * formatDateTime(new Date(), 'YYYY年MM月DD日') // '2025年06月01日'
- */
-export function formatDateTime(
- date: string | Date,
- format: string = 'YYYY-MM-DD HH:mm'
-): string {
- return dayjs(date).format(format)
-}
-```
-
-### 6.3 组件注释
-
-```vue
-
-```
-
-### 6.4 复杂逻辑注释
-
-```typescript
-async function handleRegister() {
- // 1. 检查是否已登录
- if (!userStore.isLoggedIn) {
- router.push('/login')
- return
- }
-
- // 2. 检查报名状态
- // - 已报名:提示已报名
- // - 已满员:提示名额已满
- // - 已截止:提示报名已截止
- if (activity.value.isRegistered) {
- showToast('您已报名该活动')
- return
- }
-
- // 3. 发起报名请求
- // ...
-}
-```
-
----
-
-## 7. Git 提交规范
-
-### 7.1 提交信息格式
-
-```
-():
-
-
-
-