Files
schoolNews/schoolNewsServ/achievement
2025-10-30 16:40:56 +08:00
..
2025-10-24 18:28:49 +08:00
2025-10-30 16:40:56 +08:00
2025-10-24 18:28:49 +08:00
2025-10-24 18:28:49 +08:00

成就系统模块

📋 模块概述

成就系统是一个独立的、可扩展的成就管理模块,采用事件驱动 + 策略模式设计,支持多种成就类型的自动检测和触发。

核心特性

  • 事件驱动架构 - 通过Spring事件机制自动监听业务事件并触发成就检测
  • 策略模式 - 可扩展的成就检测器,支持多种成就类型
  • 进度追踪 - 实时追踪用户朝向每个成就的进度
  • 异步处理 - 成就检测异步执行,不阻塞主业务流程
  • 完整统计 - 用户成就统计、排行榜等功能

🏗️ 系统架构

achievement/
├── checker/                    # 成就检测器(策略模式)
│   ├── AchievementChecker.java              # 检测器接口
│   ├── AbstractAchievementChecker.java      # 抽象基类
│   └── impl/                   # 具体检测器实现
│       ├── LearningTimeChecker.java         # 学习时长检测器
│       ├── CourseCompleteChecker.java       # 课程完成检测器
│       ├── ResourceViewChecker.java         # 资源浏览检测器
│       ├── ResourceCollectChecker.java      # 资源收藏检测器
│       ├── TaskCompleteChecker.java         # 任务完成检测器
│       └── ContinuousLoginChecker.java      # 连续登录检测器
├── controller/                 # 控制器层
│   └── AchievementController.java
├── service/                    # 服务层
│   └── impl/
│       └── ACHAchievementServiceImpl.java
├── mapper/                     # 数据访问层
│   ├── AchievementMapper.java
│   ├── UserAchievementMapper.java
│   └── UserAchievementProgressMapper.java
├── listener/                   # 事件监听器
│   └── AchievementEventListener.java
└── resources/
    └── mapper/                 # MyBatis XML映射文件
        ├── AchievementMapper.xml
        ├── UserAchievementMapper.xml
        └── UserAchievementProgressMapper.xml

📊 数据库设计

1. tb_achievement - 成就定义表

字段 类型 说明
id VARCHAR(32) 主键
achievement_id VARCHAR(32) 成就唯一标识
name VARCHAR(100) 成就名称
description VARCHAR(500) 成就描述
icon VARCHAR(255) 成就图标URL
type INT 成就类型1-勋章 2-等级)
level INT 成就等级
condition_type INT 触发条件类型
condition_value INT 条件值
points INT 获得积分
order_num INT 排序号
deleted TINYINT 是否删除

2. tb_user_achievement - 用户成就表

字段 类型 说明
id VARCHAR(32) 主键
user_id VARCHAR(32) 用户ID
achievement_id VARCHAR(32) 成就ID
obtain_time DATETIME 获得时间

3. tb_user_achievement_progress - 用户成就进度表

字段 类型 说明
id VARCHAR(32) 主键
user_id VARCHAR(32) 用户ID
achievement_id VARCHAR(32) 成就ID
current_value INT 当前进度值
target_value INT 目标值
progress_percentage INT 进度百分比
completed TINYINT 是否已完成
last_update_time DATETIME 最后更新时间

🎯 成就条件类型

类型编码 条件类型 说明
1 LEARNING_TIME 学习时长(分钟)
2 RESOURCE_VIEW_COUNT 浏览资源数量
3 COURSE_COMPLETE_COUNT 完成课程数量
4 CONTINUOUS_LOGIN_DAYS 连续登录天数
5 RESOURCE_COLLECT_COUNT 收藏资源数量
6 TASK_COMPLETE_COUNT 完成任务数量
7 POINTS_EARNED 获得积分数量
8 COMMENT_COUNT 发表评论数量
9 CHAPTER_COMPLETE_COUNT 完成章节数量
10 TOTAL_LOGIN_DAYS 累计登录天数

🚀 使用方法

1. 创建成就定义

// 创建一个"学习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事件发布器

@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);
    }
}

方式二:直接调用成就服务

@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. 查询用户成就

// 获取当前用户的所有成就
ResultDomain<TbUserAchievement> result = achievementService.getMyAchievements(null);

// 获取指定用户的成就
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, null);

// 获取用户的某类型成就
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, 1); // 类型1:勋章

4. 查询成就进度

// 获取当前用户的所有成就进度
ResultDomain<TbUserAchievementProgress> result = 
    achievementService.getMyAchievementProgress(null);

// 获取特定成就的进度
ResultDomain<TbUserAchievementProgress> result = 
    achievementService.getMyAchievementProgress(achievementID);

5. 获取统计和排行榜

// 获取用户成就统计
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. 添加新的条件类型枚举

// 在 AchievementConditionType.java 中添加
SHARE_COUNT(11, "分享次数");

2. 添加新的事件类型

// 在 AchievementEventType.java 中添加
RESOURCE_SHARED("resource_shared", "分享资源");

3. 创建新的检测器

@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. 在业务代码中触发事件

// 用户分享资源时
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. 事务处理:成就授予操作已添加事务控制,确保数据一致性

🎉 使用示例

完整示例:资源浏览触发成就

@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是否正确设置

📚 相关文档


作者: yslg
版本: 1.0.0
最后更新: 2025-10-24