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 + + + + + + \ 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 提交信息格式 - -``` -(): - - - -