Files
schoolNews/schoolNewsServ/achievement/README.md

397 lines
12 KiB
Markdown
Raw Normal View History

2025-10-24 18:28:49 +08:00
# 成就系统模块
## 📋 模块概述
成就系统是一个独立的、可扩展的成就管理模块,采用**事件驱动 + 策略模式**设计,支持多种成就类型的自动检测和触发。
### 核心特性
-**事件驱动架构** - 通过Spring事件机制自动监听业务事件并触发成就检测
-**策略模式** - 可扩展的成就检测器,支持多种成就类型
-**进度追踪** - 实时追踪用户朝向每个成就的进度
-**异步处理** - 成就检测异步执行,不阻塞主业务流程
-**完整统计** - 用户成就统计、排行榜等功能
## 🏗️ 系统架构
```
achievement/
├── checker/ # 成就检测器(策略模式)
│ ├── AchievementChecker.java # 检测器接口
│ ├── AbstractAchievementChecker.java # 抽象基类
│ └── impl/ # 具体检测器实现
│ ├── LearningTimeChecker.java # 学习时长检测器
│ ├── CourseCompleteChecker.java # 课程完成检测器
│ ├── ResourceViewChecker.java # 资源浏览检测器
│ ├── ResourceCollectChecker.java # 资源收藏检测器
│ ├── TaskCompleteChecker.java # 任务完成检测器
│ └── ContinuousLoginChecker.java # 连续登录检测器
├── controller/ # 控制器层
│ └── AchievementController.java
├── service/ # 服务层
│ └── impl/
│ └── ACHAchievementServiceImpl.java
├── mapper/ # 数据访问层
│ ├── AchievementMapper.java
│ ├── UserAchievementMapper.java
│ └── UserAchievementProgressMapper.java
├── listener/ # 事件监听器
│ └── AchievementEventListener.java
└── resources/
└── mapper/ # MyBatis XML映射文件
├── AchievementMapper.xml
├── UserAchievementMapper.xml
└── UserAchievementProgressMapper.xml
```
## 📊 数据库设计
### 1. tb_achievement - 成就定义表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | VARCHAR(32) | 主键 |
| achievement_id | VARCHAR(32) | 成就唯一标识 |
| name | VARCHAR(100) | 成就名称 |
| description | VARCHAR(500) | 成就描述 |
| icon | VARCHAR(255) | 成就图标URL |
| type | INT | 成就类型1-勋章 2-等级) |
| level | INT | 成就等级 |
| condition_type | INT | 触发条件类型 |
| condition_value | INT | 条件值 |
| points | INT | 获得积分 |
| order_num | INT | 排序号 |
| deleted | TINYINT | 是否删除 |
### 2. tb_user_achievement - 用户成就表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | VARCHAR(32) | 主键 |
| user_id | VARCHAR(32) | 用户ID |
| achievement_id | VARCHAR(32) | 成就ID |
| obtain_time | DATETIME | 获得时间 |
### 3. tb_user_achievement_progress - 用户成就进度表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | VARCHAR(32) | 主键 |
| user_id | VARCHAR(32) | 用户ID |
| achievement_id | VARCHAR(32) | 成就ID |
| current_value | INT | 当前进度值 |
| target_value | INT | 目标值 |
| progress_percentage | INT | 进度百分比 |
| completed | TINYINT | 是否已完成 |
| last_update_time | DATETIME | 最后更新时间 |
## 🎯 成就条件类型
| 类型编码 | 条件类型 | 说明 |
|---------|---------|------|
| 1 | LEARNING_TIME | 学习时长(分钟) |
| 2 | RESOURCE_VIEW_COUNT | 浏览资源数量 |
| 3 | COURSE_COMPLETE_COUNT | 完成课程数量 |
| 4 | CONTINUOUS_LOGIN_DAYS | 连续登录天数 |
| 5 | RESOURCE_COLLECT_COUNT | 收藏资源数量 |
| 6 | TASK_COMPLETE_COUNT | 完成任务数量 |
| 7 | POINTS_EARNED | 获得积分数量 |
| 8 | COMMENT_COUNT | 发表评论数量 |
| 9 | CHAPTER_COMPLETE_COUNT | 完成章节数量 |
| 10 | TOTAL_LOGIN_DAYS | 累计登录天数 |
## 🚀 使用方法
### 1. 创建成就定义
```java
// 创建一个"学习100小时"的成就
TbAchievement achievement = new TbAchievement();
achievement.setName("学习达人");
achievement.setDescription("累计学习时长达到100小时");
achievement.setType(1); // 勋章类型
achievement.setLevel(3); // 金牌等级
achievement.setConditionType(1); // 学习时长条件
achievement.setConditionValue(6000); // 6000分钟 = 100小时
achievement.setPoints(100); // 奖励100积分
ResultDomain<TbAchievement> result = achievementService.createAchievement(achievement);
```
### 2. 触发成就事件(在业务代码中)
#### 方式一使用Spring事件发布器
```java
@Service
public class LearningRecordService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void updateLearningTime(String userID, Integer minutes) {
// 业务逻辑:更新学习记录
// ...
// 发布成就事件
AchievementEvent event = AchievementEvent.builder(userID, AchievementEventType.LEARNING_TIME_UPDATED)
.value(minutes) // 本次学习时长
.extra("courseID", "course123")
.build();
eventPublisher.publishEvent(event);
}
}
```
#### 方式二:直接调用成就服务
```java
@Service
public class CourseService {
@Autowired
private AchievementService achievementService;
public void completeCourse(String userID, String courseID) {
// 业务逻辑:完成课程
// ...
// 触发成就检测
AchievementEvent event = new AchievementEvent(
userID,
AchievementEventType.COURSE_COMPLETED,
1 // 完成1门课程
);
ResultDomain<List<TbUserAchievement>> result =
achievementService.processAchievementEvent(event);
if (result.isSuccess() && !result.getData().isEmpty()) {
// 用户获得了新成就
logger.info("用户获得{}个新成就", result.getData().size());
}
}
}
```
### 3. 查询用户成就
```java
// 获取当前用户的所有成就
ResultDomain<TbUserAchievement> result = achievementService.getMyAchievements(null);
// 获取指定用户的成就
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, null);
// 获取用户的某类型成就
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, 1); // 类型1:勋章
```
### 4. 查询成就进度
```java
// 获取当前用户的所有成就进度
ResultDomain<TbUserAchievementProgress> result =
achievementService.getMyAchievementProgress(null);
// 获取特定成就的进度
ResultDomain<TbUserAchievementProgress> result =
achievementService.getMyAchievementProgress(achievementID);
```
### 5. 获取统计和排行榜
```java
// 获取用户成就统计
ResultDomain<Map<String, Object>> stats =
achievementService.getUserAchievementStatistics(userID);
/*
返回数据示例:
{
"totalAchievements": 15, // 总成就数
"totalPoints": 500, // 总积分
"achievementTypes": 3, // 成就类型数
"latestObtainTime": "2025-10-24 10:30:00" // 最新获得时间
}
*/
// 获取成就排行榜前10名
ResultDomain<Map<String, Object>> ranking =
achievementService.getAchievementRanking(10);
```
## 🔧 扩展新的成就类型
### 1. 添加新的条件类型枚举
```java
// 在 AchievementConditionType.java 中添加
SHARE_COUNT(11, "分享次数");
```
### 2. 添加新的事件类型
```java
// 在 AchievementEventType.java 中添加
RESOURCE_SHARED("resource_shared", "分享资源");
```
### 3. 创建新的检测器
```java
@Component
public class ShareChecker extends AbstractAchievementChecker {
@Override
protected AchievementConditionType getSupportedConditionType() {
return AchievementConditionType.SHARE_COUNT;
}
@Override
protected AchievementEventType[] getSupportedEventTypes() {
return new AchievementEventType[]{
AchievementEventType.RESOURCE_SHARED
};
}
// 如有特殊逻辑,可重写 calculateNewProgress 和 check 方法
}
```
### 4. 在业务代码中触发事件
```java
// 用户分享资源时
AchievementEvent event = AchievementEvent.builder(userID, AchievementEventType.RESOURCE_SHARED)
.value(1)
.extra("resourceID", resourceID)
.build();
eventPublisher.publishEvent(event);
```
## 📝 API接口列表
### 成就管理(管理员)
- `POST /achievement/create` - 创建成就
- `PUT /achievement/update` - 更新成就
- `DELETE /achievement/delete/{achievementID}` - 删除成就
- `GET /achievement/list` - 获取成就列表
- `POST /achievement/page` - 分页查询成就
- `GET /achievement/detail/{achievementID}` - 获取成就详情
### 用户成就查询
- `GET /achievement/user/{userID}` - 获取用户成就
- `GET /achievement/my` - 获取当前用户成就
- `GET /achievement/check/{userID}/{achievementID}` - 检查是否已获得
### 成就授予(管理员)
- `POST /achievement/grant` - 手动授予成就
- `DELETE /achievement/revoke` - 撤销成就
### 成就进度
- `GET /achievement/progress/{userID}` - 获取用户进度
- `GET /achievement/progress/my` - 获取当前用户进度
### 成就检测
- `POST /achievement/event/process` - 处理成就事件
- `GET /achievement/condition/check/{userID}/{achievementID}` - 检查条件
- `GET /achievement/available/{userID}` - 获取可获得的成就
### 统计与排行
- `GET /achievement/statistics/{userID}` - 获取用户统计
- `GET /achievement/ranking` - 获取排行榜
- `GET /achievement/recent/{achievementID}` - 获取最近获得者
## ⚠️ 注意事项
1. **事件发布建议使用异步**:成就检测已经是异步的,但建议业务代码中发布事件也使用异步方式,避免阻塞主流程
2. **进度计算**
- 累积型成就(如学习时长):每次事件累加到进度上
- 状态型成就(如连续登录):每次事件直接设置进度值
3. **性能优化**
- 成就检测器会自动筛选相关成就,不会检测所有成就
- 已获得的成就会自动跳过,不会重复检测
4. **事务处理**:成就授予操作已添加事务控制,确保数据一致性
## 🎉 使用示例
### 完整示例:资源浏览触发成就
```java
@Service
public class ResourceService {
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 用户浏览资源
*/
public void viewResource(String userID, String resourceID) {
// 1. 业务逻辑:记录浏览记录
saveBrowseRecord(userID, resourceID);
// 2. 发布成就事件
AchievementEvent event = AchievementEvent.builder(
userID,
AchievementEventType.RESOURCE_VIEWED
)
.value(1) // 浏览了1个资源
.extra("resourceID", resourceID)
.extra("timestamp", System.currentTimeMillis())
.build();
// 3. 发布事件,异步处理
eventPublisher.publishEvent(event);
// 业务流程继续,不阻塞
}
}
```
成就系统会自动:
1. 接收事件
2. 查找相关成就(浏览资源类型)
3. 更新用户进度
4. 判断是否达成
5. 自动授予成就
6. 可选:发送通知给用户
## 🔍 故障排查
### 成就未触发
1. 检查事件是否正确发布
2. 检查成就定义的条件类型是否正确
3. 查看日志中是否有异常信息
4. 确认成就状态为启用deleted=0
### 进度不更新
1. 检查数据库表是否存在
2. 确认Mapper扫描路径正确
3. 检查事件值eventValue是否正确设置
## 📚 相关文档
- [数据库建表SQL](../.bin/mysql/sql/createTableAchievement.sql)
- [迁移指南](./docs/MIGRATION.md)
---
**作者**: yslg
**版本**: 1.0.0
**最后更新**: 2025-10-24