serv-成就模块初始
This commit is contained in:
396
schoolNewsServ/achievement/README.md
Normal file
396
schoolNewsServ/achievement/README.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# 成就系统模块
|
||||
|
||||
## 📋 模块概述
|
||||
|
||||
成就系统是一个独立的、可扩展的成就管理模块,采用**事件驱动 + 策略模式**设计,支持多种成就类型的自动检测和触发。
|
||||
|
||||
### 核心特性
|
||||
|
||||
- ✅ **事件驱动架构** - 通过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
|
||||
|
||||
331
schoolNewsServ/achievement/docs/MIGRATION.md
Normal file
331
schoolNewsServ/achievement/docs/MIGRATION.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# 成就模块迁移指南
|
||||
|
||||
## 📝 迁移概述
|
||||
|
||||
成就相关功能已从 `usercenter` 模块迁移到独立的 `achievement` 模块。
|
||||
|
||||
### 迁移日期
|
||||
2025-10-24
|
||||
|
||||
### 迁移原因
|
||||
- ✅ **模块职责分离** - usercenter负责用户中心,achievement专注于成就系统
|
||||
- ✅ **更好的可维护性** - 独立模块易于管理和扩展
|
||||
- ✅ **清晰的依赖关系** - 减少模块间的耦合
|
||||
- ✅ **可扩展性** - 便于添加新的成就类型和功能
|
||||
|
||||
## 🔄 包路径变更
|
||||
|
||||
### API接口变更
|
||||
|
||||
**旧路径(已删除):**
|
||||
```java
|
||||
org.xyzh.api.usercenter.achievement.UserAchievementService
|
||||
```
|
||||
|
||||
**新路径:**
|
||||
```java
|
||||
org.xyzh.api.achievement.AchievementService
|
||||
```
|
||||
|
||||
### 服务实现变更
|
||||
|
||||
**旧路径(已删除):**
|
||||
```java
|
||||
org.xyzh.usercenter.service.UCUserAchievementService
|
||||
org.xyzh.usercenter.service.impl.UCUserAchievementServiceImpl
|
||||
```
|
||||
|
||||
**新路径:**
|
||||
```java
|
||||
org.xyzh.api.achievement.AchievementService
|
||||
org.xyzh.achievement.service.impl.ACHAchievementServiceImpl
|
||||
```
|
||||
|
||||
### Mapper变更
|
||||
|
||||
**旧路径(已删除):**
|
||||
```java
|
||||
org.xyzh.usercenter.mapper.AchievementMapper
|
||||
org.xyzh.usercenter.mapper.UserAchievementMapper
|
||||
```
|
||||
|
||||
**新路径:**
|
||||
```java
|
||||
org.xyzh.achievement.mapper.AchievementMapper
|
||||
org.xyzh.achievement.mapper.UserAchievementMapper
|
||||
org.xyzh.achievement.mapper.UserAchievementProgressMapper // 新增
|
||||
```
|
||||
|
||||
### Controller变更
|
||||
|
||||
**旧路径(已删除):**
|
||||
```java
|
||||
org.xyzh.usercenter.controller.UserAchievementController
|
||||
```
|
||||
|
||||
**新路径:**
|
||||
```java
|
||||
org.xyzh.achievement.controller.AchievementController
|
||||
```
|
||||
|
||||
## 📦 依赖变更
|
||||
|
||||
### 如果你的模块之前依赖了成就功能
|
||||
|
||||
#### 旧的依赖配置(需要删除):
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId> <!-- 包含成就接口 -->
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 新的依赖配置(需要添加):
|
||||
```xml
|
||||
<!-- API接口依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 如果需要使用实现类(通常不需要,使用Dubbo远程调用) -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 🔧 代码迁移步骤
|
||||
|
||||
### 1. 更新导入语句
|
||||
|
||||
**旧代码:**
|
||||
```java
|
||||
import org.xyzh.api.usercenter.achievement.UserAchievementService;
|
||||
```
|
||||
|
||||
**新代码:**
|
||||
```java
|
||||
import org.xyzh.api.achievement.AchievementService;
|
||||
```
|
||||
|
||||
### 2. 更新服务注入
|
||||
|
||||
**旧代码:**
|
||||
```java
|
||||
@Autowired
|
||||
private UserAchievementService userAchievementService;
|
||||
```
|
||||
|
||||
**新代码:**
|
||||
```java
|
||||
@Autowired
|
||||
private AchievementService achievementService;
|
||||
```
|
||||
|
||||
### 3. 更新方法调用
|
||||
|
||||
大部分方法签名保持不变,只需更新服务名称:
|
||||
|
||||
**旧代码:**
|
||||
```java
|
||||
ResultDomain<TbUserAchievement> result = userAchievementService.getUserAchievements(userID, type);
|
||||
```
|
||||
|
||||
**新代码:**
|
||||
```java
|
||||
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, type);
|
||||
```
|
||||
|
||||
### 4. 新增功能调用
|
||||
|
||||
新的成就系统增加了事件驱动机制:
|
||||
|
||||
```java
|
||||
// 发布成就事件
|
||||
AchievementEvent event = AchievementEvent.builder(userID, AchievementEventType.COURSE_COMPLETED)
|
||||
.value(1)
|
||||
.build();
|
||||
|
||||
// 方式1:使用Spring事件发布(推荐)
|
||||
eventPublisher.publishEvent(event);
|
||||
|
||||
// 方式2:直接调用服务
|
||||
achievementService.processAchievementEvent(event);
|
||||
```
|
||||
|
||||
## 🗄️ 数据库变更
|
||||
|
||||
### 新增表
|
||||
|
||||
```sql
|
||||
-- 用户成就进度表(新增)
|
||||
CREATE TABLE tb_user_achievement_progress (
|
||||
id VARCHAR(32) NOT NULL,
|
||||
user_id VARCHAR(32) NOT NULL,
|
||||
achievement_id VARCHAR(32) NOT NULL,
|
||||
current_value INT DEFAULT 0,
|
||||
target_value INT DEFAULT 0,
|
||||
progress_percentage INT DEFAULT 0,
|
||||
completed TINYINT(1) DEFAULT 0,
|
||||
last_update_time DATETIME,
|
||||
create_time DATETIME,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_user_achievement_progress (user_id, achievement_id)
|
||||
);
|
||||
```
|
||||
|
||||
### 已有表
|
||||
|
||||
`tb_achievement` 和 `tb_user_achievement` 表结构保持不变,无需迁移数据。
|
||||
|
||||
## 🌐 REST API变更
|
||||
|
||||
### 接口路径变更
|
||||
|
||||
**旧路径:**
|
||||
```
|
||||
/usercenter/achievement/*
|
||||
```
|
||||
|
||||
**新路径:**
|
||||
```
|
||||
/achievement/*
|
||||
```
|
||||
|
||||
### 具体接口映射
|
||||
|
||||
| 功能 | 旧接口 | 新接口 | 变化 |
|
||||
|-----|-------|-------|-----|
|
||||
| 获取所有成就 | GET /usercenter/achievement/list | GET /achievement/list | 路径变更 |
|
||||
| 获取用户成就 | GET /usercenter/achievement/user/{userID} | GET /achievement/user/{userID} | 路径变更 |
|
||||
| 获取我的成就 | - | GET /achievement/my | **新增** |
|
||||
| 授予成就 | POST /usercenter/achievement/grant | POST /achievement/grant | 路径变更 |
|
||||
| 检查条件 | GET /usercenter/achievement/condition/{userID}/{achievementID} | GET /achievement/condition/check/{userID}/{achievementID} | 路径变更 |
|
||||
| 获取进度 | - | GET /achievement/progress/{userID} | **新增** |
|
||||
| 处理事件 | - | POST /achievement/event/process | **新增** |
|
||||
| 获取统计 | - | GET /achievement/statistics/{userID} | **新增** |
|
||||
| 排行榜 | - | GET /achievement/ranking | **新增** |
|
||||
|
||||
## ✨ 新增功能
|
||||
|
||||
### 1. 事件驱动机制
|
||||
```java
|
||||
// 自动监听业务事件并触发成就检测
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
AchievementEvent event = AchievementEvent.builder(userID, eventType)
|
||||
.value(value)
|
||||
.build();
|
||||
eventPublisher.publishEvent(event);
|
||||
```
|
||||
|
||||
### 2. 成就进度追踪
|
||||
```java
|
||||
// 查询用户的成就进度
|
||||
ResultDomain<TbUserAchievementProgress> result =
|
||||
achievementService.getMyAchievementProgress(achievementID);
|
||||
```
|
||||
|
||||
### 3. 策略模式检测器
|
||||
- 可扩展的成就检测器
|
||||
- 支持多种成就类型
|
||||
- 易于添加新的成就类型
|
||||
|
||||
### 4. 统计和排行榜
|
||||
```java
|
||||
// 获取用户统计
|
||||
ResultDomain<Map<String, Object>> stats =
|
||||
achievementService.getUserAchievementStatistics(userID);
|
||||
|
||||
// 获取排行榜
|
||||
ResultDomain<Map<String, Object>> ranking =
|
||||
achievementService.getAchievementRanking(10);
|
||||
```
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
### 1. 破坏性变更
|
||||
|
||||
#### ❌ 已删除的类(不再可用)
|
||||
- `org.xyzh.api.usercenter.achievement.UserAchievementService`
|
||||
- `org.xyzh.usercenter.service.UCUserAchievementService`
|
||||
- `org.xyzh.usercenter.mapper.AchievementMapper`
|
||||
- `org.xyzh.usercenter.controller.UserAchievementController`
|
||||
|
||||
#### ✅ 对应的新类
|
||||
- `org.xyzh.api.achievement.AchievementService`
|
||||
- `org.xyzh.achievement.service.impl.ACHAchievementServiceImpl`
|
||||
- `org.xyzh.achievement.mapper.AchievementMapper`
|
||||
- `org.xyzh.achievement.controller.AchievementController`
|
||||
|
||||
### 2. 接口变化
|
||||
|
||||
新的 `AchievementService` 接口方法更多、更完善:
|
||||
- ✅ 所有旧方法都有对应的新方法
|
||||
- ✅ 新增了事件处理、进度查询、统计等功能
|
||||
- ✅ 方法签名大部分保持兼容
|
||||
|
||||
### 3. 前端调用
|
||||
|
||||
如果你的前端代码调用了成就相关API,需要:
|
||||
1. 更新API路径:`/usercenter/achievement/*` → `/achievement/*`
|
||||
2. 检查返回数据格式(大部分保持兼容)
|
||||
3. 利用新增的API(进度、统计、排行榜等)
|
||||
|
||||
## 📋 迁移检查清单
|
||||
|
||||
迁移时请检查以下项目:
|
||||
|
||||
- [ ] 更新 pom.xml 依赖
|
||||
- [ ] 更新 import 语句
|
||||
- [ ] 更新服务注入
|
||||
- [ ] 更新方法调用
|
||||
- [ ] 测试成就创建功能
|
||||
- [ ] 测试成就授予功能
|
||||
- [ ] 测试成就查询功能
|
||||
- [ ] 测试事件触发功能(如有)
|
||||
- [ ] 更新前端API路径(如有)
|
||||
- [ ] 更新相关文档
|
||||
- [ ] 执行集成测试
|
||||
|
||||
## 🆘 常见问题
|
||||
|
||||
### Q1: 编译报错找不到 UserAchievementService
|
||||
**A:** 更新 import 语句为:
|
||||
```java
|
||||
import org.xyzh.api.achievement.AchievementService;
|
||||
```
|
||||
|
||||
### Q2: 旧的成就数据会丢失吗?
|
||||
**A:** 不会。数据库表结构保持不变,所有数据完整保留。
|
||||
|
||||
### Q3: 需要修改数据库吗?
|
||||
**A:** 需要执行新表创建SQL(`tb_user_achievement_progress`),见 `.bin/mysql/sql/createTableAchievement.sql`。
|
||||
|
||||
### Q4: 如何使用新的事件驱动功能?
|
||||
**A:** 参考 `README.md` 中的"使用方法"章节。
|
||||
|
||||
### Q5: admin模块已经自动配置好了吗?
|
||||
**A:** 是的,admin模块的pom.xml已自动添加achievement依赖。
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [成就系统使用文档](../README.md)
|
||||
- [数据库建表SQL](../../.bin/mysql/sql/createTableAchievement.sql)
|
||||
|
||||
## 👥 联系支持
|
||||
|
||||
如果在迁移过程中遇到问题,请联系:
|
||||
- 开发者:yslg
|
||||
- 创建Issue或PR
|
||||
|
||||
---
|
||||
|
||||
**迁移完成日期**: 2025-10-24
|
||||
**版本**: 1.0.0
|
||||
|
||||
101
schoolNewsServ/achievement/pom.xml
Normal file
101
schoolNewsServ/achievement/pom.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>achievement</name>
|
||||
<description>成就模块</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 禁用 Spring Boot 插件,因为 achievement 是 admin 的一部分 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.xyzh.achievement.checker;
|
||||
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
import org.xyzh.common.dto.usercenter.TbAchievement;
|
||||
|
||||
/**
|
||||
* @description 成就检测器抽象基类
|
||||
* @filename AbstractAchievementChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
public abstract class AbstractAchievementChecker implements AchievementChecker {
|
||||
|
||||
/**
|
||||
* 支持的条件类型
|
||||
*/
|
||||
protected abstract AchievementConditionType getSupportedConditionType();
|
||||
|
||||
/**
|
||||
* 支持的事件类型(可多个)
|
||||
*/
|
||||
protected abstract AchievementEventType[] getSupportedEventTypes();
|
||||
|
||||
@Override
|
||||
public boolean supports(TbAchievement achievement) {
|
||||
if (achievement == null || achievement.getConditionType() == null) {
|
||||
return false;
|
||||
}
|
||||
return achievement.getConditionType().equals(getSupportedConditionType().getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(AchievementEventType eventType) {
|
||||
if (eventType == null) {
|
||||
return false;
|
||||
}
|
||||
for (AchievementEventType supportedType : getSupportedEventTypes()) {
|
||||
if (supportedType == eventType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(AchievementEvent event, TbAchievement achievement, Integer currentProgress) {
|
||||
if (event == null || achievement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer newProgress = calculateNewProgress(event, currentProgress);
|
||||
Integer targetValue = achievement.getConditionValue();
|
||||
|
||||
return newProgress != null && targetValue != null && newProgress >= targetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer calculateNewProgress(AchievementEvent event, Integer currentProgress) {
|
||||
if (currentProgress == null) {
|
||||
currentProgress = 0;
|
||||
}
|
||||
|
||||
Integer eventValue = event.getEventValue();
|
||||
if (eventValue == null) {
|
||||
eventValue = 1; // 默认增加1
|
||||
}
|
||||
|
||||
return currentProgress + eventValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.xyzh.achievement.checker;
|
||||
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
import org.xyzh.common.dto.usercenter.TbAchievement;
|
||||
|
||||
/**
|
||||
* @description 成就检测器接口 - 策略模式
|
||||
* @filename AchievementChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
public interface AchievementChecker {
|
||||
|
||||
/**
|
||||
* @description 判断该检测器是否支持该成就类型
|
||||
* @param achievement 成就
|
||||
* @return boolean 是否支持
|
||||
*/
|
||||
boolean supports(TbAchievement achievement);
|
||||
|
||||
/**
|
||||
* @description 判断该检测器是否支持该事件类型
|
||||
* @param eventType 事件类型
|
||||
* @return boolean 是否支持
|
||||
*/
|
||||
boolean supportsEventType(AchievementEventType eventType);
|
||||
|
||||
/**
|
||||
* @description 检查用户是否满足成就条件
|
||||
* @param event 成就事件
|
||||
* @param achievement 成就定义
|
||||
* @param currentProgress 当前进度值
|
||||
* @return boolean 是否满足条件
|
||||
*/
|
||||
boolean check(AchievementEvent event, TbAchievement achievement, Integer currentProgress);
|
||||
|
||||
/**
|
||||
* @description 计算新的进度值
|
||||
* @param event 成就事件
|
||||
* @param currentProgress 当前进度值
|
||||
* @return Integer 新的进度值
|
||||
*/
|
||||
Integer calculateNewProgress(AchievementEvent event, Integer currentProgress);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
|
||||
/**
|
||||
* @description 连续登录天数成就检测器
|
||||
* @filename ContinuousLoginChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class ContinuousLoginChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.CONTINUOUS_LOGIN_DAYS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.CONTINUOUS_LOGIN,
|
||||
AchievementEventType.USER_LOGIN
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer calculateNewProgress(AchievementEvent event, Integer currentProgress) {
|
||||
// 对于连续登录,直接使用事件值(连续天数)
|
||||
return event.getEventValue() != null ? event.getEventValue() : currentProgress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
|
||||
/**
|
||||
* @description 完成课程数量成就检测器
|
||||
* @filename CourseCompleteChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class CourseCompleteChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.COURSE_COMPLETE_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.COURSE_COMPLETED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
|
||||
/**
|
||||
* @description 学习时长成就检测器
|
||||
* @filename LearningTimeChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class LearningTimeChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.LEARNING_TIME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.LEARNING_TIME_UPDATED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
|
||||
/**
|
||||
* @description 收藏资源数量成就检测器
|
||||
* @filename ResourceCollectChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class ResourceCollectChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.RESOURCE_COLLECT_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.RESOURCE_COLLECTED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
|
||||
/**
|
||||
* @description 浏览资源数量成就检测器
|
||||
* @filename ResourceViewChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class ResourceViewChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.RESOURCE_VIEW_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.RESOURCE_VIEWED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.xyzh.achievement.checker.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.achievement.checker.AbstractAchievementChecker;
|
||||
import org.xyzh.common.core.enums.AchievementConditionType;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
|
||||
/**
|
||||
* @description 完成任务数量成就检测器
|
||||
* @filename TaskCompleteChecker.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class TaskCompleteChecker extends AbstractAchievementChecker {
|
||||
|
||||
@Override
|
||||
protected AchievementConditionType getSupportedConditionType() {
|
||||
return AchievementConditionType.TASK_COMPLETE_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AchievementEventType[] getSupportedEventTypes() {
|
||||
return new AchievementEventType[]{
|
||||
AchievementEventType.TASK_COMPLETED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package org.xyzh.achievement.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.xyzh.api.achievement.AchievementService;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.dto.usercenter.TbAchievement;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievement;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievementProgress;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description 成就控制器
|
||||
* @filename AchievementController.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/achievement")
|
||||
public class AchievementController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AchievementController.class);
|
||||
|
||||
@Autowired
|
||||
private AchievementService achievementService;
|
||||
|
||||
// ==================== 成就定义管理(管理员)====================
|
||||
|
||||
/**
|
||||
* 创建成就
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public ResultDomain<TbAchievement> createAchievement(@RequestBody TbAchievement achievement) {
|
||||
return achievementService.createAchievement(achievement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成就
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
public ResultDomain<TbAchievement> updateAchievement(@RequestBody TbAchievement achievement) {
|
||||
return achievementService.updateAchievement(achievement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成就
|
||||
*/
|
||||
@DeleteMapping("/delete/{achievementID}")
|
||||
public ResultDomain<Void> deleteAchievement(@PathVariable String achievementID) {
|
||||
return achievementService.deleteAchievement(achievementID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有成就列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public ResultDomain<TbAchievement> getAllAchievements(
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer level) {
|
||||
return achievementService.getAllAchievements(type, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询成就
|
||||
*/
|
||||
@PostMapping("/page")
|
||||
public ResultDomain<TbAchievement> getAchievementPage(
|
||||
@RequestBody(required = false) TbAchievement filter,
|
||||
PageParam pageParam) {
|
||||
return achievementService.getAchievementPage(filter, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成就详情
|
||||
*/
|
||||
@GetMapping("/detail/{achievementID}")
|
||||
public ResultDomain<TbAchievement> getAchievementDetail(@PathVariable String achievementID) {
|
||||
return achievementService.getAchievementDetail(achievementID);
|
||||
}
|
||||
|
||||
// ==================== 用户成就查询 ====================
|
||||
|
||||
/**
|
||||
* 获取用户已获得的成就
|
||||
*/
|
||||
@GetMapping("/user/{userID}")
|
||||
public ResultDomain<TbUserAchievement> getUserAchievements(
|
||||
@PathVariable String userID,
|
||||
@RequestParam(required = false) Integer type) {
|
||||
return achievementService.getUserAchievements(userID, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的成就列表
|
||||
*/
|
||||
@GetMapping("/my")
|
||||
public ResultDomain<TbUserAchievement> getMyAchievements(
|
||||
@RequestParam(required = false) Integer type) {
|
||||
return achievementService.getMyAchievements(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已获得成就
|
||||
*/
|
||||
@GetMapping("/check/{userID}/{achievementID}")
|
||||
public ResultDomain<Boolean> hasAchievement(
|
||||
@PathVariable String userID,
|
||||
@PathVariable String achievementID) {
|
||||
return achievementService.hasAchievement(userID, achievementID);
|
||||
}
|
||||
|
||||
// ==================== 成就授予(管理员)====================
|
||||
|
||||
/**
|
||||
* 手动授予用户成就
|
||||
*/
|
||||
@PostMapping("/grant")
|
||||
public ResultDomain<TbUserAchievement> grantAchievement(
|
||||
@RequestParam String userID,
|
||||
@RequestParam String achievementID) {
|
||||
return achievementService.grantAchievement(userID, achievementID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销用户成就
|
||||
*/
|
||||
@DeleteMapping("/revoke")
|
||||
public ResultDomain<Void> revokeAchievement(
|
||||
@RequestParam String userID,
|
||||
@RequestParam String achievementID) {
|
||||
return achievementService.revokeAchievement(userID, achievementID);
|
||||
}
|
||||
|
||||
// ==================== 成就进度查询 ====================
|
||||
|
||||
/**
|
||||
* 获取用户成就进度
|
||||
*/
|
||||
@GetMapping("/progress/{userID}")
|
||||
public ResultDomain<TbUserAchievementProgress> getUserAchievementProgress(
|
||||
@PathVariable String userID,
|
||||
@RequestParam(required = false) String achievementID) {
|
||||
return achievementService.getUserAchievementProgress(userID, achievementID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的成就进度
|
||||
*/
|
||||
@GetMapping("/progress/my")
|
||||
public ResultDomain<TbUserAchievementProgress> getMyAchievementProgress(
|
||||
@RequestParam(required = false) String achievementID) {
|
||||
return achievementService.getMyAchievementProgress(achievementID);
|
||||
}
|
||||
|
||||
// ==================== 成就检测 ====================
|
||||
|
||||
/**
|
||||
* 处理成就事件(内部接口,由其他服务调用)
|
||||
*/
|
||||
@PostMapping("/event/process")
|
||||
public ResultDomain<List<TbUserAchievement>> processAchievementEvent(@RequestBody AchievementEvent event) {
|
||||
return achievementService.processAchievementEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否满足成就条件
|
||||
*/
|
||||
@GetMapping("/condition/check/{userID}/{achievementID}")
|
||||
public ResultDomain<Boolean> checkAchievementCondition(
|
||||
@PathVariable String userID,
|
||||
@PathVariable String achievementID) {
|
||||
return achievementService.checkAchievementCondition(userID, achievementID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量检查用户可获得的成就
|
||||
*/
|
||||
@GetMapping("/available/{userID}")
|
||||
public ResultDomain<List<TbAchievement>> checkAvailableAchievements(@PathVariable String userID) {
|
||||
return achievementService.checkAvailableAchievements(userID);
|
||||
}
|
||||
|
||||
// ==================== 成就统计 ====================
|
||||
|
||||
/**
|
||||
* 获取用户成就统计
|
||||
*/
|
||||
@GetMapping("/statistics/{userID}")
|
||||
public ResultDomain<Map<String, Object>> getUserAchievementStatistics(@PathVariable String userID) {
|
||||
return achievementService.getUserAchievementStatistics(userID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成就排行榜
|
||||
*/
|
||||
@GetMapping("/ranking")
|
||||
public ResultDomain<Map<String, Object>> getAchievementRanking(
|
||||
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
||||
return achievementService.getAchievementRanking(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近获得成就的用户
|
||||
*/
|
||||
@GetMapping("/recent/{achievementID}")
|
||||
public ResultDomain<List<TbUserAchievement>> getRecentAchievers(
|
||||
@PathVariable String achievementID,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
||||
return achievementService.getRecentAchievers(achievementID, limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.xyzh.achievement.listener;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.api.achievement.AchievementService;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description 成就事件监听器 - 监听业务事件并自动触发成就检测
|
||||
* @filename AchievementEventListener.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Component
|
||||
public class AchievementEventListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AchievementEventListener.class);
|
||||
|
||||
@Autowired
|
||||
private AchievementService achievementService;
|
||||
|
||||
/**
|
||||
* 监听成就事件
|
||||
* 使用@Async异步处理,避免阻塞主业务流程
|
||||
*/
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleAchievementEvent(AchievementEvent event) {
|
||||
try {
|
||||
logger.debug("接收到成就事件: {}", event);
|
||||
|
||||
// 处理成就事件
|
||||
ResultDomain<List<TbUserAchievement>> result = achievementService.processAchievementEvent(event);
|
||||
|
||||
if (result.isSuccess() && result.getData() != null && !result.getData().isEmpty()) {
|
||||
logger.info("用户 {} 通过事件 {} 获得 {} 个新成就",
|
||||
event.getUserID(),
|
||||
event.getEventType(),
|
||||
result.getData().size());
|
||||
|
||||
// 这里可以添加通知逻辑,如发送消息给用户
|
||||
notifyUserAboutNewAchievements(event.getUserID(), result.getData());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("处理成就事件异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知用户获得新成就(示例方法,需要根据实际业务实现)
|
||||
*/
|
||||
private void notifyUserAboutNewAchievements(String userID, List<TbUserAchievement> achievements) {
|
||||
// TODO: 实现通知逻辑
|
||||
// 1. 发送系统消息
|
||||
// 2. 发送推送通知
|
||||
// 3. 记录通知日志
|
||||
logger.info("通知用户 {} 获得成就,数量: {}", userID, achievements.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package org.xyzh.achievement.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.dto.usercenter.TbAchievement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description 成就数据访问层
|
||||
* @filename AchievementMapper.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Mapper
|
||||
public interface AchievementMapper extends BaseMapper<TbAchievement> {
|
||||
|
||||
/**
|
||||
* @description 查询成就列表
|
||||
* @param filter 过滤条件
|
||||
* @return List<TbAchievement> 成就列表
|
||||
*/
|
||||
List<TbAchievement> selectAchievements(@Param("filter") TbAchievement filter);
|
||||
|
||||
/**
|
||||
* @description 根据成就ID查询成就信息
|
||||
* @param achievementId 成就ID
|
||||
* @return TbAchievement 成就信息
|
||||
*/
|
||||
TbAchievement selectByAchievementId(@Param("achievementId") String achievementId);
|
||||
|
||||
/**
|
||||
* @description 根据类型查询成就列表
|
||||
* @param type 成就类型
|
||||
* @return List<TbAchievement> 成就列表
|
||||
*/
|
||||
List<TbAchievement> selectByType(@Param("type") Integer type);
|
||||
|
||||
/**
|
||||
* @description 根据类型和等级查询成就列表
|
||||
* @param type 成就类型
|
||||
* @param level 成就等级
|
||||
* @return List<TbAchievement> 成就列表
|
||||
*/
|
||||
List<TbAchievement> selectByTypeAndLevel(@Param("type") Integer type, @Param("level") Integer level);
|
||||
|
||||
/**
|
||||
* @description 根据条件类型查询成就列表
|
||||
* @param conditionType 条件类型
|
||||
* @return List<TbAchievement> 成就列表
|
||||
*/
|
||||
List<TbAchievement> selectByConditionType(@Param("conditionType") Integer conditionType);
|
||||
|
||||
/**
|
||||
* @description 查询成就排行榜
|
||||
* @param limit 限制数量
|
||||
* @return List<TbAchievement> 成就排行榜
|
||||
*/
|
||||
List<TbAchievement> selectAchievementRanking(@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* @description 检查成就名称是否存在
|
||||
* @param name 成就名称
|
||||
* @param excludeId 排除的成就ID(用于更新时排除自身)
|
||||
* @return int 存在的数量
|
||||
*/
|
||||
int countByName(@Param("name") String name, @Param("excludeId") String excludeId);
|
||||
|
||||
/**
|
||||
* @description 插入成就
|
||||
* @param achievement 成就
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int insertAchievement(TbAchievement achievement);
|
||||
|
||||
/**
|
||||
* @description 更新成就
|
||||
* @param achievement 成就
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int updateAchievement(TbAchievement achievement);
|
||||
|
||||
/**
|
||||
* @description 删除成就
|
||||
* @param achievement 成就
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int deleteAchievement(TbAchievement achievement);
|
||||
|
||||
/**
|
||||
* @description 批量插入成就
|
||||
* @param achievementList 成就列表
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int batchInsertAchievements(@Param("achievementList") List<TbAchievement> achievementList);
|
||||
|
||||
/**
|
||||
* @description 批量删除成就
|
||||
* @param ids 成就ID列表
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int batchDeleteAchievements(@Param("ids") List<String> ids);
|
||||
|
||||
/**
|
||||
* @description 分页查询成就
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return List<TbAchievement> 成就列表
|
||||
*/
|
||||
List<TbAchievement> selectAchievementsPage(@Param("filter") TbAchievement filter, @Param("pageParam") PageParam pageParam);
|
||||
|
||||
/**
|
||||
* @description 统计成就总数
|
||||
* @param filter 过滤条件
|
||||
* @return long 总数
|
||||
*/
|
||||
long countAchievements(@Param("filter") TbAchievement filter);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package org.xyzh.achievement.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievement;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description 用户成就数据访问层
|
||||
* @filename UserAchievementMapper.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserAchievementMapper extends BaseMapper<TbUserAchievement> {
|
||||
|
||||
/**
|
||||
* @description 查询用户成就列表
|
||||
* @param filter 过滤条件
|
||||
* @return List<TbUserAchievement> 用户成就列表
|
||||
*/
|
||||
List<TbUserAchievement> selectUserAchievements(@Param("filter") TbUserAchievement filter);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID查询成就记录
|
||||
* @param userId 用户ID
|
||||
* @return List<TbUserAchievement> 成就记录列表
|
||||
*/
|
||||
List<TbUserAchievement> selectByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID和成就ID查询成就记录
|
||||
* @param userId 用户ID
|
||||
* @param achievementId 成就ID
|
||||
* @return TbUserAchievement 成就记录
|
||||
*/
|
||||
TbUserAchievement selectByUserIdAndAchievementId(@Param("userId") String userId, @Param("achievementId") String achievementId);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID和成就类型查询成就记录
|
||||
* @param userId 用户ID
|
||||
* @param type 成就类型
|
||||
* @return List<TbUserAchievement> 成就记录列表
|
||||
*/
|
||||
List<TbUserAchievement> selectByUserIdAndType(@Param("userId") String userId, @Param("type") Integer type);
|
||||
|
||||
/**
|
||||
* @description 查询用户成就统计
|
||||
* @param userId 用户ID
|
||||
* @return Map<String, Object> 统计信息
|
||||
*/
|
||||
Map<String, Object> selectAchievementStatistics(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* @description 查询成就的最近获得者
|
||||
* @param achievementId 成就ID
|
||||
* @param limit 查询数量
|
||||
* @return List<TbUserAchievement> 用户成就列表
|
||||
*/
|
||||
List<TbUserAchievement> selectRecentAchievers(@Param("achievementId") String achievementId, @Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* @description 查询成就排行榜(按用户获得成就数量)
|
||||
* @param limit 排行榜条数
|
||||
* @return List<Map<String, Object>> 排行榜数据
|
||||
*/
|
||||
List<Map<String, Object>> selectUserAchievementRanking(@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* @description 插入用户成就
|
||||
* @param userAchievement 用户成就
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int insertUserAchievement(TbUserAchievement userAchievement);
|
||||
|
||||
/**
|
||||
* @description 更新用户成就
|
||||
* @param userAchievement 用户成就
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int updateUserAchievement(TbUserAchievement userAchievement);
|
||||
|
||||
/**
|
||||
* @description 删除用户成就
|
||||
* @param id 用户成就ID
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int deleteUserAchievement(@Param("id") String id);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID和成就ID删除
|
||||
* @param userId 用户ID
|
||||
* @param achievementId 成就ID
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int deleteByUserIdAndAchievementId(@Param("userId") String userId, @Param("achievementId") String achievementId);
|
||||
|
||||
/**
|
||||
* @description 批量插入用户成就
|
||||
* @param userAchievementList 用户成就列表
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int batchInsertUserAchievements(@Param("userAchievementList") List<TbUserAchievement> userAchievementList);
|
||||
|
||||
/**
|
||||
* @description 分页查询用户成就
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return List<TbUserAchievement> 用户成就列表
|
||||
*/
|
||||
List<TbUserAchievement> selectUserAchievementsPage(@Param("filter") TbUserAchievement filter, @Param("pageParam") PageParam pageParam);
|
||||
|
||||
/**
|
||||
* @description 统计用户成就总数
|
||||
* @param filter 过滤条件
|
||||
* @return long 总数
|
||||
*/
|
||||
long countUserAchievements(@Param("filter") TbUserAchievement filter);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.xyzh.achievement.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievementProgress;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description 用户成就进度数据访问层
|
||||
* @filename UserAchievementProgressMapper.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserAchievementProgressMapper extends BaseMapper<TbUserAchievementProgress> {
|
||||
|
||||
/**
|
||||
* @description 根据用户ID和成就ID查询进度
|
||||
* @param userId 用户ID
|
||||
* @param achievementId 成就ID
|
||||
* @return TbUserAchievementProgress 成就进度
|
||||
*/
|
||||
TbUserAchievementProgress selectByUserIdAndAchievementId(@Param("userId") String userId, @Param("achievementId") String achievementId);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID查询所有进度
|
||||
* @param userId 用户ID
|
||||
* @return List<TbUserAchievementProgress> 成就进度列表
|
||||
*/
|
||||
List<TbUserAchievementProgress> selectByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID和条件类型查询进度
|
||||
* @param userId 用户ID
|
||||
* @param conditionType 条件类型
|
||||
* @return List<TbUserAchievementProgress> 成就进度列表
|
||||
*/
|
||||
List<TbUserAchievementProgress> selectByUserIdAndConditionType(@Param("userId") String userId, @Param("conditionType") Integer conditionType);
|
||||
|
||||
/**
|
||||
* @description 查询未完成的进度
|
||||
* @param userId 用户ID
|
||||
* @return List<TbUserAchievementProgress> 成就进度列表
|
||||
*/
|
||||
List<TbUserAchievementProgress> selectIncompletedByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* @description 插入进度记录
|
||||
* @param progress 成就进度
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int insertProgress(TbUserAchievementProgress progress);
|
||||
|
||||
/**
|
||||
* @description 更新进度记录
|
||||
* @param progress 成就进度
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int updateProgress(TbUserAchievementProgress progress);
|
||||
|
||||
/**
|
||||
* @description 增加进度值
|
||||
* @param userId 用户ID
|
||||
* @param achievementId 成就ID
|
||||
* @param incrementValue 增量值
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int incrementProgress(@Param("userId") String userId, @Param("achievementId") String achievementId, @Param("incrementValue") Integer incrementValue);
|
||||
|
||||
/**
|
||||
* @description 批量插入进度记录
|
||||
* @param progressList 成就进度列表
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int batchInsertProgress(@Param("progressList") List<TbUserAchievementProgress> progressList);
|
||||
|
||||
/**
|
||||
* @description 删除进度记录
|
||||
* @param userId 用户ID
|
||||
* @param achievementId 成就ID
|
||||
* @return int 影响行数
|
||||
*/
|
||||
int deleteProgress(@Param("userId") String userId, @Param("achievementId") String achievementId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,754 @@
|
||||
package org.xyzh.achievement.service.impl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.xyzh.achievement.checker.AchievementChecker;
|
||||
import org.xyzh.achievement.mapper.AchievementMapper;
|
||||
import org.xyzh.achievement.mapper.UserAchievementMapper;
|
||||
import org.xyzh.achievement.mapper.UserAchievementProgressMapper;
|
||||
import org.xyzh.api.achievement.AchievementService;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.enums.AchievementEventType;
|
||||
import org.xyzh.common.core.event.AchievementEvent;
|
||||
import org.xyzh.common.core.page.PageDomain;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.dto.user.TbSysUser;
|
||||
import org.xyzh.common.dto.usercenter.TbAchievement;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievement;
|
||||
import org.xyzh.common.dto.usercenter.TbUserAchievementProgress;
|
||||
import org.xyzh.common.utils.IDUtils;
|
||||
import org.xyzh.system.utils.LoginUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @description 成就服务实现类
|
||||
* @filename ACHAchievementServiceImpl.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-24
|
||||
*/
|
||||
@Service
|
||||
public class ACHAchievementServiceImpl implements AchievementService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ACHAchievementServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private AchievementMapper achievementMapper;
|
||||
|
||||
@Autowired
|
||||
private UserAchievementMapper userAchievementMapper;
|
||||
|
||||
@Autowired
|
||||
private UserAchievementProgressMapper progressMapper;
|
||||
|
||||
@Autowired
|
||||
private List<AchievementChecker> checkers;
|
||||
|
||||
// ==================== 成就定义管理 ====================
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbAchievement> createAchievement(TbAchievement achievement) {
|
||||
ResultDomain<TbAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
// 参数验证
|
||||
if (achievement == null || !StringUtils.hasText(achievement.getName())) {
|
||||
resultDomain.fail("成就名称不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 检查名称是否已存在
|
||||
int count = achievementMapper.countByName(achievement.getName(), null);
|
||||
if (count > 0) {
|
||||
resultDomain.fail("成就名称已存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (achievement.getID() == null) {
|
||||
achievement.setID(IDUtils.generateID());
|
||||
}
|
||||
if (achievement.getAchievementID() == null) {
|
||||
achievement.setAchievementID(IDUtils.generateID());
|
||||
}
|
||||
achievement.setCreateTime(new Date());
|
||||
achievement.setDeleted(false);
|
||||
|
||||
// 插入数据库
|
||||
int result = achievementMapper.insertAchievement(achievement);
|
||||
if (result > 0) {
|
||||
resultDomain.success("创建成就成功", achievement);
|
||||
} else {
|
||||
resultDomain.fail("创建成就失败");
|
||||
}
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("创建成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("创建成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbAchievement> updateAchievement(TbAchievement achievement) {
|
||||
ResultDomain<TbAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
// 参数验证
|
||||
if (achievement == null || !StringUtils.hasText(achievement.getID())) {
|
||||
resultDomain.fail("成就ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 检查成就是否存在
|
||||
TbAchievement existing = achievementMapper.selectById(achievement.getID());
|
||||
if (existing == null) {
|
||||
resultDomain.fail("成就不存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 检查名称是否重复
|
||||
if (StringUtils.hasText(achievement.getName())) {
|
||||
int count = achievementMapper.countByName(achievement.getName(), achievement.getID());
|
||||
if (count > 0) {
|
||||
resultDomain.fail("成就名称已存在");
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
achievement.setUpdateTime(new Date());
|
||||
int result = achievementMapper.updateAchievement(achievement);
|
||||
if (result > 0) {
|
||||
resultDomain.success("更新成就成功", achievement);
|
||||
} else {
|
||||
resultDomain.fail("更新成就失败");
|
||||
}
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("更新成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("更新成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<Void> deleteAchievement(String achievementID) {
|
||||
ResultDomain<Void> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("成就ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
TbAchievement achievement = achievementMapper.selectByAchievementId(achievementID);
|
||||
if (achievement == null) {
|
||||
resultDomain.fail("成就不存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
achievement.setDeleted(true);
|
||||
achievement.setDeleteTime(new Date());
|
||||
int result = achievementMapper.updateAchievement(achievement);
|
||||
|
||||
if (result > 0) {
|
||||
resultDomain.success("删除成就成功", (Void) null);
|
||||
} else {
|
||||
resultDomain.fail("删除成就失败");
|
||||
}
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("删除成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("删除成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbAchievement> getAllAchievements(Integer type, Integer level) {
|
||||
ResultDomain<TbAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
List<TbAchievement> list;
|
||||
if (type != null && level != null) {
|
||||
list = achievementMapper.selectByTypeAndLevel(type, level);
|
||||
} else if (type != null) {
|
||||
list = achievementMapper.selectByType(type);
|
||||
} else {
|
||||
TbAchievement filter = new TbAchievement();
|
||||
filter.setDeleted(false);
|
||||
list = achievementMapper.selectAchievements(filter);
|
||||
}
|
||||
|
||||
resultDomain.success("获取成就列表成功", list);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取成就列表异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取成就列表失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbAchievement> getAchievementPage(TbAchievement filter, PageParam pageParam) {
|
||||
ResultDomain<TbAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (filter == null) {
|
||||
filter = new TbAchievement();
|
||||
filter.setDeleted(false);
|
||||
}
|
||||
|
||||
List<TbAchievement> list = achievementMapper.selectAchievementsPage(filter, pageParam);
|
||||
long total = achievementMapper.countAchievements(filter);
|
||||
|
||||
pageParam.setTotalElements(total);
|
||||
pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
|
||||
|
||||
resultDomain.success("获取成就分页成功", new PageDomain<>(pageParam, list));
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取成就分页异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取成就分页失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbAchievement> getAchievementDetail(String achievementID) {
|
||||
ResultDomain<TbAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("成就ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
TbAchievement achievement = achievementMapper.selectByAchievementId(achievementID);
|
||||
if (achievement == null || achievement.getDeleted()) {
|
||||
resultDomain.fail("成就不存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
resultDomain.success("获取成就详情成功", achievement);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取成就详情异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取成就详情失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 用户成就管理 ====================
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbUserAchievement> getUserAchievements(String userID, Integer type) {
|
||||
ResultDomain<TbUserAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID)) {
|
||||
resultDomain.fail("用户ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
List<TbUserAchievement> list;
|
||||
if (type != null) {
|
||||
list = userAchievementMapper.selectByUserIdAndType(userID, type);
|
||||
} else {
|
||||
list = userAchievementMapper.selectByUserId(userID);
|
||||
}
|
||||
|
||||
resultDomain.success("获取用户成就成功", list);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取用户成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取用户成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbUserAchievement> getMyAchievements(Integer type) {
|
||||
TbSysUser user = LoginUtil.getCurrentUser();
|
||||
if (user == null) {
|
||||
ResultDomain<TbUserAchievement> resultDomain = new ResultDomain<>();
|
||||
resultDomain.fail("请先登录");
|
||||
return resultDomain;
|
||||
}
|
||||
return getUserAchievements(user.getID(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Boolean> hasAchievement(String userID, String achievementID) {
|
||||
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("参数不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
TbUserAchievement userAchievement = userAchievementMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
resultDomain.success("检查成功", userAchievement != null);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("检查用户成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("检查用户成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbUserAchievement> grantAchievement(String userID, String achievementID) {
|
||||
ResultDomain<TbUserAchievement> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("参数不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 检查成就是否存在
|
||||
TbAchievement achievement = achievementMapper.selectByAchievementId(achievementID);
|
||||
if (achievement == null || achievement.getDeleted()) {
|
||||
resultDomain.fail("成就不存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 检查用户是否已获得
|
||||
TbUserAchievement existing = userAchievementMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
if (existing != null) {
|
||||
resultDomain.fail("用户已获得该成就");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 创建用户成就记录
|
||||
TbUserAchievement userAchievement = new TbUserAchievement();
|
||||
userAchievement.setID(IDUtils.generateID());
|
||||
userAchievement.setUserID(userID);
|
||||
userAchievement.setAchievementID(achievementID);
|
||||
userAchievement.setObtainTime(new Date());
|
||||
|
||||
int result = userAchievementMapper.insertUserAchievement(userAchievement);
|
||||
if (result > 0) {
|
||||
// 更新进度为完成
|
||||
updateProgressToCompleted(userID, achievementID);
|
||||
|
||||
logger.info("用户 {} 获得成就: {}", userID, achievement.getName());
|
||||
resultDomain.success("授予成就成功", userAchievement);
|
||||
} else {
|
||||
resultDomain.fail("授予成就失败");
|
||||
}
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("授予成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("授予成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<Void> revokeAchievement(String userID, String achievementID) {
|
||||
ResultDomain<Void> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("参数不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
int result = userAchievementMapper.deleteByUserIdAndAchievementId(userID, achievementID);
|
||||
if (result > 0) {
|
||||
// 重置进度
|
||||
progressMapper.deleteProgress(userID, achievementID);
|
||||
resultDomain.success("撤销成就成功", (Void) null);
|
||||
} else {
|
||||
resultDomain.fail("撤销成就失败");
|
||||
}
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("撤销成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("撤销成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 成就进度管理 ====================
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbUserAchievementProgress> getUserAchievementProgress(String userID, String achievementID) {
|
||||
ResultDomain<TbUserAchievementProgress> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID)) {
|
||||
resultDomain.fail("用户ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
List<TbUserAchievementProgress> list;
|
||||
if (StringUtils.hasText(achievementID)) {
|
||||
TbUserAchievementProgress progress = progressMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
list = progress != null ? Collections.singletonList(progress) : new ArrayList<>();
|
||||
} else {
|
||||
list = progressMapper.selectByUserId(userID);
|
||||
}
|
||||
|
||||
resultDomain.success("获取成就进度成功", list);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取成就进度异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取成就进度失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbUserAchievementProgress> getMyAchievementProgress(String achievementID) {
|
||||
TbSysUser user = LoginUtil.getCurrentUser();
|
||||
if (user == null) {
|
||||
ResultDomain<TbUserAchievementProgress> resultDomain = new ResultDomain<>();
|
||||
resultDomain.fail("请先登录");
|
||||
return resultDomain;
|
||||
}
|
||||
return getUserAchievementProgress(user.getID(), achievementID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbUserAchievementProgress> updateProgress(String userID, String achievementID, Integer incrementValue) {
|
||||
ResultDomain<TbUserAchievementProgress> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("参数不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 获取或创建进度记录
|
||||
TbUserAchievementProgress progress = getOrCreateProgress(userID, achievementID);
|
||||
if (progress == null) {
|
||||
resultDomain.fail("获取进度失败");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
progress.setCurrentValue(progress.getCurrentValue() + incrementValue);
|
||||
progress.setLastUpdateTime(new Date());
|
||||
progressMapper.updateProgress(progress);
|
||||
|
||||
resultDomain.success("更新进度成功", progress);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("更新进度异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("更新进度失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 成就检测与触发 ====================
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<List<TbUserAchievement>> processAchievementEvent(AchievementEvent event) {
|
||||
ResultDomain<List<TbUserAchievement>> resultDomain = new ResultDomain<>();
|
||||
List<TbUserAchievement> newAchievements = new ArrayList<>();
|
||||
|
||||
try {
|
||||
if (event == null || !StringUtils.hasText(event.getUserID()) || event.getEventType() == null) {
|
||||
resultDomain.fail("事件参数不完整");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
logger.debug("处理成就事件: {}", event);
|
||||
|
||||
// 获取该事件类型相关的所有成就
|
||||
List<TbAchievement> achievements = getAchievementsByEventType(event.getEventType());
|
||||
if (achievements.isEmpty()) {
|
||||
resultDomain.success("无相关成就", newAchievements);
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 遍历每个成就进行检测
|
||||
for (TbAchievement achievement : achievements) {
|
||||
// 检查用户是否已获得
|
||||
if (userAchievementMapper.selectByUserIdAndAchievementId(event.getUserID(), achievement.getAchievementID()) != null) {
|
||||
continue; // 已获得,跳过
|
||||
}
|
||||
|
||||
// 获取合适的检测器
|
||||
AchievementChecker checker = getCheckerForAchievement(achievement);
|
||||
if (checker == null || !checker.supportsEventType(event.getEventType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取当前进度
|
||||
TbUserAchievementProgress progress = getOrCreateProgress(event.getUserID(), achievement.getAchievementID());
|
||||
Integer currentProgress = progress.getCurrentValue();
|
||||
|
||||
// 计算新进度
|
||||
Integer newProgress = checker.calculateNewProgress(event, currentProgress);
|
||||
|
||||
// 更新进度
|
||||
progress.setCurrentValue(newProgress);
|
||||
progress.setTargetValue(achievement.getConditionValue());
|
||||
progress.setLastUpdateTime(new Date());
|
||||
progressMapper.updateProgress(progress);
|
||||
|
||||
// 检查是否达成
|
||||
if (checker.check(event, achievement, currentProgress)) {
|
||||
TbUserAchievement userAchievement = grantAchievementInternal(event.getUserID(), achievement.getAchievementID());
|
||||
if (userAchievement != null) {
|
||||
newAchievements.add(userAchievement);
|
||||
logger.info("用户 {} 通过事件 {} 获得成就: {}", event.getUserID(), event.getEventType(), achievement.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultDomain.success("处理成就事件成功", newAchievements);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("处理成就事件异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("处理成就事件失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Boolean> checkAchievementCondition(String userID, String achievementID) {
|
||||
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("参数不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 获取成就定义
|
||||
TbAchievement achievement = achievementMapper.selectByAchievementId(achievementID);
|
||||
if (achievement == null) {
|
||||
resultDomain.fail("成就不存在");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 获取进度
|
||||
TbUserAchievementProgress progress = progressMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
Integer currentValue = progress != null ? progress.getCurrentValue() : 0;
|
||||
Integer targetValue = achievement.getConditionValue();
|
||||
|
||||
boolean satisfied = currentValue >= targetValue;
|
||||
resultDomain.success("检查完成", satisfied);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("检查成就条件异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("检查成就条件失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<List<TbAchievement>> checkAvailableAchievements(String userID) {
|
||||
ResultDomain<List<TbAchievement>> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID)) {
|
||||
resultDomain.fail("用户ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
// 获取所有成就
|
||||
TbAchievement filter = new TbAchievement();
|
||||
filter.setDeleted(false);
|
||||
List<TbAchievement> allAchievements = achievementMapper.selectAchievements(filter);
|
||||
|
||||
// 获取用户进度
|
||||
List<TbUserAchievementProgress> progressList = progressMapper.selectIncompletedByUserId(userID);
|
||||
Map<String, Integer> progressMap = progressList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
TbUserAchievementProgress::getAchievementID,
|
||||
TbUserAchievementProgress::getCurrentValue
|
||||
));
|
||||
|
||||
// 筛选可获得的成就
|
||||
List<TbAchievement> availableAchievements = allAchievements.stream()
|
||||
.filter(achievement -> {
|
||||
Integer currentValue = progressMap.getOrDefault(achievement.getAchievementID(), 0);
|
||||
return currentValue >= achievement.getConditionValue();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
resultDomain.success("检查可获得成就成功", availableAchievements);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("检查可获得成就异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("检查可获得成就失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 成就统计 ====================
|
||||
|
||||
@Override
|
||||
public ResultDomain<Map<String, Object>> getUserAchievementStatistics(String userID) {
|
||||
ResultDomain<Map<String, Object>> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(userID)) {
|
||||
resultDomain.fail("用户ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
Map<String, Object> statistics = userAchievementMapper.selectAchievementStatistics(userID);
|
||||
if (statistics == null) {
|
||||
statistics = new HashMap<>();
|
||||
statistics.put("totalAchievements", 0);
|
||||
statistics.put("totalPoints", 0);
|
||||
}
|
||||
|
||||
resultDomain.success("获取统计信息成功", statistics);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取统计信息异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取统计信息失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Map<String, Object>> getAchievementRanking(Integer limit) {
|
||||
ResultDomain<Map<String, Object>> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (limit == null || limit <= 0) {
|
||||
limit = 10; // 默认10条
|
||||
}
|
||||
|
||||
List<Map<String, Object>> ranking = userAchievementMapper.selectUserAchievementRanking(limit);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("ranking", ranking);
|
||||
result.put("limit", limit);
|
||||
|
||||
resultDomain.success("获取排行榜成功", result);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取排行榜异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取排行榜失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<List<TbUserAchievement>> getRecentAchievers(String achievementID, Integer limit) {
|
||||
ResultDomain<List<TbUserAchievement>> resultDomain = new ResultDomain<>();
|
||||
try {
|
||||
if (!StringUtils.hasText(achievementID)) {
|
||||
resultDomain.fail("成就ID不能为空");
|
||||
return resultDomain;
|
||||
}
|
||||
|
||||
if (limit == null || limit <= 0) {
|
||||
limit = 10;
|
||||
}
|
||||
|
||||
List<TbUserAchievement> achievers = userAchievementMapper.selectRecentAchievers(achievementID, limit);
|
||||
resultDomain.success("获取最近获得者成功", achievers);
|
||||
return resultDomain;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取最近获得者异常: {}", e.getMessage(), e);
|
||||
resultDomain.fail("获取最近获得者失败: " + e.getMessage());
|
||||
return resultDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 私有辅助方法 ====================
|
||||
|
||||
/**
|
||||
* 获取或创建进度记录
|
||||
*/
|
||||
private TbUserAchievementProgress getOrCreateProgress(String userID, String achievementID) {
|
||||
TbUserAchievementProgress progress = progressMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
if (progress == null) {
|
||||
// 创建新的进度记录
|
||||
TbAchievement achievement = achievementMapper.selectByAchievementId(achievementID);
|
||||
if (achievement == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
progress = new TbUserAchievementProgress();
|
||||
progress.setID(IDUtils.generateID());
|
||||
progress.setUserID(userID);
|
||||
progress.setAchievementID(achievementID);
|
||||
progress.setCurrentValue(0);
|
||||
progress.setTargetValue(achievement.getConditionValue());
|
||||
progress.setCompleted(false);
|
||||
progress.setLastUpdateTime(new Date());
|
||||
progress.setCreateTime(new Date());
|
||||
|
||||
progressMapper.insertProgress(progress);
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新进度为已完成
|
||||
*/
|
||||
private void updateProgressToCompleted(String userID, String achievementID) {
|
||||
TbUserAchievementProgress progress = progressMapper.selectByUserIdAndAchievementId(userID, achievementID);
|
||||
if (progress != null) {
|
||||
progress.setCompleted(true);
|
||||
progress.setCurrentValue(progress.getTargetValue());
|
||||
progress.setLastUpdateTime(new Date());
|
||||
progressMapper.updateProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部授予成就方法(不检查是否已获得)
|
||||
*/
|
||||
private TbUserAchievement grantAchievementInternal(String userID, String achievementID) {
|
||||
try {
|
||||
TbUserAchievement userAchievement = new TbUserAchievement();
|
||||
userAchievement.setID(IDUtils.generateID());
|
||||
userAchievement.setUserID(userID);
|
||||
userAchievement.setAchievementID(achievementID);
|
||||
userAchievement.setObtainTime(new Date());
|
||||
|
||||
int result = userAchievementMapper.insertUserAchievement(userAchievement);
|
||||
if (result > 0) {
|
||||
updateProgressToCompleted(userID, achievementID);
|
||||
return userAchievement;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("内部授予成就失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据事件类型获取相关成就
|
||||
*/
|
||||
private List<TbAchievement> getAchievementsByEventType(AchievementEventType eventType) {
|
||||
// 获取所有成就
|
||||
TbAchievement filter = new TbAchievement();
|
||||
filter.setDeleted(false);
|
||||
List<TbAchievement> allAchievements = achievementMapper.selectAchievements(filter);
|
||||
|
||||
// 筛选支持该事件类型的成就
|
||||
return allAchievements.stream()
|
||||
.filter(achievement -> {
|
||||
AchievementChecker checker = getCheckerForAchievement(achievement);
|
||||
return checker != null && checker.supportsEventType(eventType);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成就对应的检测器
|
||||
*/
|
||||
private AchievementChecker getCheckerForAchievement(TbAchievement achievement) {
|
||||
for (AchievementChecker checker : checkers) {
|
||||
if (checker.supports(achievement)) {
|
||||
return checker;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.xyzh.achievement.mapper.AchievementMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.usercenter.TbAchievement">
|
||||
<id column="id" property="ID" />
|
||||
<result column="achievement_id" property="achievementID" />
|
||||
<result column="name" property="name" />
|
||||
<result column="description" property="description" />
|
||||
<result column="icon" property="icon" />
|
||||
<result column="type" property="type" />
|
||||
<result column="level" property="level" />
|
||||
<result column="condition_type" property="conditionType" />
|
||||
<result column="condition_value" property="conditionValue" />
|
||||
<result column="points" property="points" />
|
||||
<result column="order_num" property="orderNum" />
|
||||
<result column="creator" property="creator" />
|
||||
<result column="updater" property="updater" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="delete_time" property="deleteTime" />
|
||||
<result column="deleted" property="deleted" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 字段列表 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, achievement_id, name, description, icon, type, level, condition_type,
|
||||
condition_value, points, order_num, creator, updater, create_time, update_time,
|
||||
delete_time, deleted
|
||||
</sql>
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<sql id="Base_Where_Clause">
|
||||
<where>
|
||||
<if test="filter != null">
|
||||
<if test="filter.ID != null and filter.ID != ''">
|
||||
AND id = #{filter.ID}
|
||||
</if>
|
||||
<if test="filter.achievementID != null and filter.achievementID != ''">
|
||||
AND achievement_id = #{filter.achievementID}
|
||||
</if>
|
||||
<if test="filter.name != null and filter.name != ''">
|
||||
AND name LIKE CONCAT('%', #{filter.name}, '%')
|
||||
</if>
|
||||
<if test="filter.type != null">
|
||||
AND type = #{filter.type}
|
||||
</if>
|
||||
<if test="filter.level != null">
|
||||
AND level = #{filter.level}
|
||||
</if>
|
||||
<if test="filter.conditionType != null">
|
||||
AND condition_type = #{filter.conditionType}
|
||||
</if>
|
||||
<if test="filter.points != null">
|
||||
AND points = #{filter.points}
|
||||
</if>
|
||||
<if test="filter.creator != null and filter.creator != ''">
|
||||
AND creator = #{filter.creator}
|
||||
</if>
|
||||
<if test="filter.deleted != null">
|
||||
AND deleted = #{filter.deleted}
|
||||
</if>
|
||||
</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<!-- 查询成就列表 -->
|
||||
<select id="selectAchievements" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
<include refid="Base_Where_Clause" />
|
||||
ORDER BY order_num ASC, create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据成就ID查询成就信息 -->
|
||||
<select id="selectByAchievementId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
WHERE achievement_id = #{achievementId} AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 根据类型查询成就列表 -->
|
||||
<select id="selectByType" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
WHERE type = #{type} AND deleted = 0
|
||||
ORDER BY order_num ASC, create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据类型和等级查询成就列表 -->
|
||||
<select id="selectByTypeAndLevel" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
WHERE type = #{type} AND level = #{level} AND deleted = 0
|
||||
ORDER BY order_num ASC, create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据条件类型查询成就列表 -->
|
||||
<select id="selectByConditionType" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
WHERE condition_type = #{conditionType} AND deleted = 0
|
||||
ORDER BY order_num ASC, create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询成就排行榜 -->
|
||||
<select id="selectAchievementRanking" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
WHERE deleted = 0
|
||||
ORDER BY points DESC, level DESC, order_num ASC
|
||||
<if test="limit != null and limit > 0">
|
||||
LIMIT #{limit}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 检查成就名称是否存在 -->
|
||||
<select id="countByName" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM tb_achievement
|
||||
WHERE name = #{name} AND deleted = 0
|
||||
<if test="excludeId != null and excludeId != ''">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 插入成就 -->
|
||||
<insert id="insertAchievement" parameterType="org.xyzh.common.dto.usercenter.TbAchievement">
|
||||
INSERT INTO tb_achievement (
|
||||
id, achievement_id, name, description, icon, type, level, condition_type,
|
||||
condition_value, points, order_num, creator, updater, create_time, update_time,
|
||||
delete_time, deleted
|
||||
) VALUES (
|
||||
#{ID}, #{achievementID}, #{name}, #{description}, #{icon}, #{type}, #{level},
|
||||
#{conditionType}, #{conditionValue}, #{points}, #{orderNum}, #{creator}, #{updater},
|
||||
#{createTime}, #{updateTime}, #{deleteTime}, #{deleted}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 更新成就 -->
|
||||
<update id="updateAchievement" parameterType="org.xyzh.common.dto.usercenter.TbAchievement">
|
||||
UPDATE tb_achievement
|
||||
<set>
|
||||
<if test="achievementID != null and achievementID != ''">
|
||||
achievement_id = #{achievementID},
|
||||
</if>
|
||||
<if test="name != null and name != ''">
|
||||
name = #{name},
|
||||
</if>
|
||||
<if test="description != null and description != ''">
|
||||
description = #{description},
|
||||
</if>
|
||||
<if test="icon != null and icon != ''">
|
||||
icon = #{icon},
|
||||
</if>
|
||||
<if test="type != null">
|
||||
type = #{type},
|
||||
</if>
|
||||
<if test="level != null">
|
||||
level = #{level},
|
||||
</if>
|
||||
<if test="conditionType != null">
|
||||
condition_type = #{conditionType},
|
||||
</if>
|
||||
<if test="conditionValue != null">
|
||||
condition_value = #{conditionValue},
|
||||
</if>
|
||||
<if test="points != null">
|
||||
points = #{points},
|
||||
</if>
|
||||
<if test="orderNum != null">
|
||||
order_num = #{orderNum},
|
||||
</if>
|
||||
<if test="updater != null and updater != ''">
|
||||
updater = #{updater},
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
update_time = #{updateTime},
|
||||
</if>
|
||||
<if test="deleteTime != null">
|
||||
delete_time = #{deleteTime},
|
||||
</if>
|
||||
<if test="deleted != null">
|
||||
deleted = #{deleted},
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{ID}
|
||||
</update>
|
||||
|
||||
<!-- 删除成就 -->
|
||||
<delete id="deleteAchievement" parameterType="org.xyzh.common.dto.usercenter.TbAchievement">
|
||||
DELETE FROM tb_achievement
|
||||
WHERE id = #{ID}
|
||||
</delete>
|
||||
|
||||
<!-- 批量插入成就 -->
|
||||
<insert id="batchInsertAchievements" parameterType="java.util.List">
|
||||
INSERT INTO tb_achievement (
|
||||
id, achievement_id, name, description, icon, type, level, condition_type,
|
||||
condition_value, points, order_num, creator, updater, create_time, update_time,
|
||||
delete_time, deleted
|
||||
) VALUES
|
||||
<foreach collection="achievementList" item="item" separator=",">
|
||||
(
|
||||
#{item.ID}, #{item.achievementID}, #{item.name}, #{item.description}, #{item.icon},
|
||||
#{item.type}, #{item.level}, #{item.conditionType}, #{item.conditionValue},
|
||||
#{item.points}, #{item.orderNum}, #{item.creator}, #{item.updater},
|
||||
#{item.createTime}, #{item.updateTime}, #{item.deleteTime}, #{item.deleted}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 批量删除成就 -->
|
||||
<delete id="batchDeleteAchievements">
|
||||
DELETE FROM tb_achievement
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<!-- 分页查询成就 -->
|
||||
<select id="selectAchievementsPage" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_achievement
|
||||
<include refid="Base_Where_Clause" />
|
||||
ORDER BY order_num ASC, create_time DESC
|
||||
LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset}
|
||||
</select>
|
||||
|
||||
<!-- 统计成就总数 -->
|
||||
<select id="countAchievements" resultType="long">
|
||||
SELECT COUNT(1)
|
||||
FROM tb_achievement
|
||||
<include refid="Base_Where_Clause" />
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.xyzh.achievement.mapper.UserAchievementMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.usercenter.TbUserAchievement">
|
||||
<id column="id" property="ID" />
|
||||
<result column="user_id" property="userID" />
|
||||
<result column="achievement_id" property="achievementID" />
|
||||
<result column="obtain_time" property="obtainTime" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 字段列表 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, user_id, achievement_id, obtain_time
|
||||
</sql>
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<sql id="Base_Where_Clause">
|
||||
<where>
|
||||
<if test="filter != null">
|
||||
<if test="filter.ID != null and filter.ID != ''">
|
||||
AND ua.id = #{filter.ID}
|
||||
</if>
|
||||
<if test="filter.userID != null and filter.userID != ''">
|
||||
AND ua.user_id = #{filter.userID}
|
||||
</if>
|
||||
<if test="filter.achievementID != null and filter.achievementID != ''">
|
||||
AND ua.achievement_id = #{filter.achievementID}
|
||||
</if>
|
||||
</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<!-- 查询用户成就列表 -->
|
||||
<select id="selectUserAchievements" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
ua.id, ua.user_id, ua.achievement_id, ua.obtain_time
|
||||
FROM tb_user_achievement ua
|
||||
<include refid="Base_Where_Clause" />
|
||||
ORDER BY ua.obtain_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID查询成就记录 -->
|
||||
<select id="selectByUserId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement
|
||||
WHERE user_id = #{userId}
|
||||
ORDER BY obtain_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID和成就ID查询成就记录 -->
|
||||
<select id="selectByUserIdAndAchievementId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement
|
||||
WHERE user_id = #{userId} AND achievement_id = #{achievementId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID和成就类型查询成就记录 -->
|
||||
<select id="selectByUserIdAndType" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
ua.id, ua.user_id, ua.achievement_id, ua.obtain_time
|
||||
FROM tb_user_achievement ua
|
||||
INNER JOIN tb_achievement a ON ua.achievement_id = a.achievement_id
|
||||
WHERE ua.user_id = #{userId} AND a.type = #{type} AND a.deleted = 0
|
||||
ORDER BY ua.obtain_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询用户成就统计 -->
|
||||
<select id="selectAchievementStatistics" resultType="map">
|
||||
SELECT
|
||||
COUNT(*) AS totalAchievements,
|
||||
IFNULL(SUM(a.points), 0) AS totalPoints,
|
||||
COUNT(DISTINCT a.type) AS achievementTypes,
|
||||
MAX(ua.obtain_time) AS latestObtainTime
|
||||
FROM tb_user_achievement ua
|
||||
INNER JOIN tb_achievement a ON ua.achievement_id = a.achievement_id
|
||||
WHERE ua.user_id = #{userId} AND a.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 查询成就的最近获得者 -->
|
||||
<select id="selectRecentAchievers" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement
|
||||
WHERE achievement_id = #{achievementId}
|
||||
ORDER BY obtain_time DESC
|
||||
<if test="limit != null and limit > 0">
|
||||
LIMIT #{limit}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 查询成就排行榜(按用户获得成就数量) -->
|
||||
<select id="selectUserAchievementRanking" resultType="map">
|
||||
SELECT
|
||||
ua.user_id AS userID,
|
||||
u.username,
|
||||
u.nickname,
|
||||
COUNT(*) AS achievementCount,
|
||||
IFNULL(SUM(a.points), 0) AS totalPoints,
|
||||
MAX(ua.obtain_time) AS latestObtainTime
|
||||
FROM tb_user_achievement ua
|
||||
INNER JOIN tb_achievement a ON ua.achievement_id = a.achievement_id
|
||||
INNER JOIN tb_sys_user u ON ua.user_id = u.id
|
||||
WHERE a.deleted = 0
|
||||
GROUP BY ua.user_id, u.username, u.nickname
|
||||
ORDER BY achievementCount DESC, totalPoints DESC, latestObtainTime DESC
|
||||
<if test="limit != null and limit > 0">
|
||||
LIMIT #{limit}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 插入用户成就 -->
|
||||
<insert id="insertUserAchievement" parameterType="org.xyzh.common.dto.usercenter.TbUserAchievement">
|
||||
INSERT INTO tb_user_achievement (
|
||||
id, user_id, achievement_id, obtain_time
|
||||
) VALUES (
|
||||
#{ID}, #{userID}, #{achievementID}, #{obtainTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 更新用户成就 -->
|
||||
<update id="updateUserAchievement" parameterType="org.xyzh.common.dto.usercenter.TbUserAchievement">
|
||||
UPDATE tb_user_achievement
|
||||
<set>
|
||||
<if test="userID != null and userID != ''">
|
||||
user_id = #{userID},
|
||||
</if>
|
||||
<if test="achievementID != null and achievementID != ''">
|
||||
achievement_id = #{achievementID},
|
||||
</if>
|
||||
<if test="obtainTime != null">
|
||||
obtain_time = #{obtainTime},
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{ID}
|
||||
</update>
|
||||
|
||||
<!-- 删除用户成就 -->
|
||||
<delete id="deleteUserAchievement">
|
||||
DELETE FROM tb_user_achievement
|
||||
WHERE id = #{id}
|
||||
</delete>
|
||||
|
||||
<!-- 根据用户ID和成就ID删除 -->
|
||||
<delete id="deleteByUserIdAndAchievementId">
|
||||
DELETE FROM tb_user_achievement
|
||||
WHERE user_id = #{userId} AND achievement_id = #{achievementId}
|
||||
</delete>
|
||||
|
||||
<!-- 批量插入用户成就 -->
|
||||
<insert id="batchInsertUserAchievements" parameterType="java.util.List">
|
||||
INSERT INTO tb_user_achievement (
|
||||
id, user_id, achievement_id, obtain_time
|
||||
) VALUES
|
||||
<foreach collection="userAchievementList" item="item" separator=",">
|
||||
(
|
||||
#{item.ID}, #{item.userID}, #{item.achievementID}, #{item.obtainTime}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 分页查询用户成就 -->
|
||||
<select id="selectUserAchievementsPage" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
ua.id, ua.user_id, ua.achievement_id, ua.obtain_time
|
||||
FROM tb_user_achievement ua
|
||||
<include refid="Base_Where_Clause" />
|
||||
ORDER BY ua.obtain_time DESC
|
||||
LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset}
|
||||
</select>
|
||||
|
||||
<!-- 统计用户成就总数 -->
|
||||
<select id="countUserAchievements" resultType="long">
|
||||
SELECT COUNT(1)
|
||||
FROM tb_user_achievement ua
|
||||
<include refid="Base_Where_Clause" />
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.xyzh.achievement.mapper.UserAchievementProgressMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.usercenter.TbUserAchievementProgress">
|
||||
<id column="id" property="ID" />
|
||||
<result column="user_id" property="userID" />
|
||||
<result column="achievement_id" property="achievementID" />
|
||||
<result column="current_value" property="currentValue" />
|
||||
<result column="target_value" property="targetValue" />
|
||||
<result column="progress_percentage" property="progressPercentage" />
|
||||
<result column="completed" property="completed" />
|
||||
<result column="last_update_time" property="lastUpdateTime" />
|
||||
<result column="create_time" property="createTime" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 字段列表 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, user_id, achievement_id, current_value, target_value, progress_percentage,
|
||||
completed, last_update_time, create_time
|
||||
</sql>
|
||||
|
||||
<!-- 根据用户ID和成就ID查询进度 -->
|
||||
<select id="selectByUserIdAndAchievementId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement_progress
|
||||
WHERE user_id = #{userId} AND achievement_id = #{achievementId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID查询所有进度 -->
|
||||
<select id="selectByUserId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement_progress
|
||||
WHERE user_id = #{userId}
|
||||
ORDER BY last_update_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID和条件类型查询进度 -->
|
||||
<select id="selectByUserIdAndConditionType" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
p.id, p.user_id, p.achievement_id, p.current_value, p.target_value,
|
||||
p.progress_percentage, p.completed, p.last_update_time, p.create_time
|
||||
FROM tb_user_achievement_progress p
|
||||
INNER JOIN tb_achievement a ON p.achievement_id = a.achievement_id
|
||||
WHERE p.user_id = #{userId} AND a.condition_type = #{conditionType} AND a.deleted = 0
|
||||
ORDER BY p.last_update_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询未完成的进度 -->
|
||||
<select id="selectIncompletedByUserId" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List" />
|
||||
FROM tb_user_achievement_progress
|
||||
WHERE user_id = #{userId} AND completed = 0
|
||||
ORDER BY last_update_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 插入进度记录 -->
|
||||
<insert id="insertProgress" parameterType="org.xyzh.common.dto.usercenter.TbUserAchievementProgress">
|
||||
INSERT INTO tb_user_achievement_progress (
|
||||
id, user_id, achievement_id, current_value, target_value,
|
||||
progress_percentage, completed, last_update_time, create_time
|
||||
) VALUES (
|
||||
#{ID}, #{userID}, #{achievementID}, #{currentValue}, #{targetValue},
|
||||
#{progressPercentage}, #{completed}, #{lastUpdateTime}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 更新进度记录 -->
|
||||
<update id="updateProgress" parameterType="org.xyzh.common.dto.usercenter.TbUserAchievementProgress">
|
||||
UPDATE tb_user_achievement_progress
|
||||
<set>
|
||||
<if test="currentValue != null">
|
||||
current_value = #{currentValue},
|
||||
</if>
|
||||
<if test="targetValue != null">
|
||||
target_value = #{targetValue},
|
||||
</if>
|
||||
<if test="progressPercentage != null">
|
||||
progress_percentage = #{progressPercentage},
|
||||
</if>
|
||||
<if test="completed != null">
|
||||
completed = #{completed},
|
||||
</if>
|
||||
<if test="lastUpdateTime != null">
|
||||
last_update_time = #{lastUpdateTime},
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{ID}
|
||||
</update>
|
||||
|
||||
<!-- 增加进度值 -->
|
||||
<update id="incrementProgress">
|
||||
UPDATE tb_user_achievement_progress
|
||||
SET
|
||||
current_value = current_value + #{incrementValue},
|
||||
progress_percentage = LEAST(100, ROUND(((current_value + #{incrementValue}) * 100.0 / target_value), 0)),
|
||||
completed = IF((current_value + #{incrementValue}) >= target_value, 1, 0),
|
||||
last_update_time = NOW()
|
||||
WHERE user_id = #{userId} AND achievement_id = #{achievementId}
|
||||
</update>
|
||||
|
||||
<!-- 批量插入进度记录 -->
|
||||
<insert id="batchInsertProgress" parameterType="java.util.List">
|
||||
INSERT INTO tb_user_achievement_progress (
|
||||
id, user_id, achievement_id, current_value, target_value,
|
||||
progress_percentage, completed, last_update_time, create_time
|
||||
) VALUES
|
||||
<foreach collection="progressList" item="item" separator=",">
|
||||
(
|
||||
#{item.ID}, #{item.userID}, #{item.achievementID}, #{item.currentValue},
|
||||
#{item.targetValue}, #{item.progressPercentage}, #{item.completed},
|
||||
#{item.lastUpdateTime}, #{item.createTime}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 删除进度记录 -->
|
||||
<delete id="deleteProgress">
|
||||
DELETE FROM tb_user_achievement_progress
|
||||
WHERE user_id = #{userId} AND achievement_id = #{achievementId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user