# 🚀 语聊房火箭系统 - 完整需求文档 --- ## 📋 文档信息 | 项目 | 内容 | |-----|------| | **文档名称** | 语聊房火箭系统需求文档 | | **版本** | v1.0 | | **创建日期** | 2025-01-15 | | **所属项目** | Likei Services | | **所属模块** | rc-service-other | | **项目路径** | D:\workspace\likei-services\rc-service\rc-service-other | --- ## 1. 业务需求 ### 1.1 功能概述 语聊房火箭是一个房间级互动玩法,用户通过送礼积累火箭能量,达到阈值后自动发射并分发奖励。 ### 1.2 核心规则 #### 能量积累规则 - **基础规则**:1金币礼物 = 1火箭能量点 - **幸运加成**:幸运礼物额外增加4%能量 - **实时累积**:每次送礼立即增加能量 #### 火箭等级系统 | 等级 | 能量阈值 | 说明 | |-----|---------|------| | Lv1 | 150,000 | 一级火箭 | | Lv2 | 500,000 | 二级火箭 | | Lv3 | 1,000,000 | 三级火箭 | #### 发射机制 - 能量达到阈值自动触发发射 - 发射后进入"领取中"状态 - 领取有效期:1分钟 #### 奖励领取规则 - 房间内所有用户可领取 - 贡献者和普通用户奖励不同(配置待定) - 每人限领一次 - 过期后自动重置 #### 每日重置 - 每天00:00重置火箭状态 - 历史记录保存到MongoDB --- ## 2. 技术架构 ### 2.1 技术栈 | 组件 | 技术 | |-----|------| | **语言** | Java 17+ | | **框架** | Spring Boot | | **数据库** | MySQL + MongoDB | | **缓存** | Redis | | **架构模式** | DDD + CQRS | ### 2.2 数据库设计 #### MongoDB 集合 **room_rocket_status** - 火箭实时状态 ```javascript { "_id": ObjectId, "room_id": NumberLong, "date": "2025-01-15", "level": 1, // 1/2/3 "current_energy": NumberLong(85000), "max_energy": NumberLong(150000), "status": 1, // 1:CHARGING 2:LAUNCHED 3:CLAIMING "contributors": [ { "user_id": NumberLong, "user_name": "张三", "user_avatar": "https://...", "energy": NumberLong(50000), "contribution_rate": 58.82, "gift_count": 125, "first_contribute_time": ISODate, "last_contribute_time": ISODate } ], "launch_time": ISODate, "claim_expire_time": ISODate, "claim_count": 0, "created_at": ISODate, "updated_at": ISODate } // 索引 db.room_rocket_status.createIndex({"room_id": 1, "date": 1}, {unique: true}) db.room_rocket_status.createIndex({"status": 1}) ``` #### MySQL 表 **rocket_config** - 火箭配置表 ```sql CREATE TABLE `rocket_config` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `level` TINYINT UNSIGNED NOT NULL COMMENT '等级 1/2/3', `max_energy` BIGINT UNSIGNED NOT NULL COMMENT '最大能量值', `reward_config` JSON DEFAULT NULL COMMENT '奖励配置(预留)', `status` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_level` (`level`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `rocket_config` (`level`, `max_energy`) VALUES (1, 150000), (2, 500000), (3, 1000000); ``` **rocket_reward_claim_log** - 奖励领取记录表 ```sql CREATE TABLE `rocket_reward_claim_log` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `rocket_history_id` VARCHAR(32) NOT NULL COMMENT 'MongoDB _id', `room_id` BIGINT UNSIGNED NOT NULL, `user_id` BIGINT UNSIGNED NOT NULL, `is_contributor` TINYINT UNSIGNED NOT NULL COMMENT '是否贡献者', `reward_type` VARCHAR(32) NOT NULL COMMENT 'GOLD/DIAMOND/PROP', `reward_amount` BIGINT UNSIGNED NOT NULL, `claimed_at` DATETIME NOT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_rocket_user` (`rocket_history_id`, `user_id`), KEY `idx_room_id` (`room_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` ### 2.3 Redis 缓存设计 | Key | 类型 | TTL | 用途 | |-----|------|-----|------| | ROCKET:STATUS:{roomId} | String(JSON) | 24h | 状态缓存 | | ROCKET:LOCK:{roomId} | String | 5s | 分布式锁 | | ROCKET:CLAIMED:{historyId} | Set | 2min | 已领取用户 | | ROCKET:CONFIG:{level} | String(JSON) | 1h | 配置缓存 | --- ## 3. 核心流程 ### 3.1 能量积累流程 ``` 用户送礼 → GiftSendEvent ↓ RocketEnergyAddCmdExe ↓ 1. 获取分布式锁 (Redis) 2. 查询火箭状态 (MongoDB) 3. 计算能量值 (普通/幸运) 4. 更新能量和贡献者 5. 检查等级提升 6. 保存状态 7. 判断是否达到发射条件 ├─ 是 → 触发发射 └─ 否 → 推送能量更新 ``` ### 3.2 火箭发射流程 ``` 能量达到阈值 ↓ RocketLaunchCmdExe ↓ 1. 获取分布式锁 2. 校验状态和能量 3. 发射火箭 (status → LAUNCHED) 4. 设置领取过期时间 (+1分钟) 5. 进入领取状态 (status → CLAIMING) 6. 保存状态 7. 推送发射动画 (TODO: WebSocket) 8. 保存历史记录 (TODO) ``` ### 3.3 奖励领取流程 ``` 用户点击领取 ↓ RocketRewardClaimCmdExe ↓ 1. 获取分布式锁 2. 校验领取资格 ├─ 状态是否为CLAIMING ├─ 是否过期 └─ 是否重复领取 3. 判断是否贡献者 4. 计算奖励金额 (TODO: 配置) 5. 调用钱包服务发放 (TODO) 6. 记录领取日志 (MySQL) 7. 更新领取人数 8. 返回奖励详情 ``` ### 3.4 每日重置流程 ``` 定时任务 00:00 ↓ RocketDailyResetTask ↓ 1. 查询所有CHARGING状态火箭 2. 保存历史记录 (TODO) 3. 重置火箭状态 ├─ level = 1 ├─ current_energy = 0 ├─ contributors = [] └─ date = 今天 4. 清除Redis缓存 ``` --- ## 4. API 接口设计 ### 4.1 查询火箭状态 **接口**: `GET /api/rocket/status` **请求参数**: ```json { "roomId": 123456, "currentUserId": 789 // 可选 } ``` **响应数据**: ```json { "roomId": 123456, "date": "2025-01-15", "level": 2, "levelName": "二级火箭", "currentEnergy": 350000, "maxEnergy": 500000, "energyPercent": 70.00, "status": "CHARGING", "statusDesc": "充能中", "topContributors": [ { "userId": 789, "userName": "张三", "userAvatar": "https://...", "energy": 200000, "contributionRate": 57.14, "rank": 1, "giftCount": 500 } ], "totalContributors": 25, "claimCount": 0, "canClaim": false, "hasClaimed": false, "myContribution": 200000, "myContributionRate": 57.14 } ``` ### 4.2 领取奖励 **接口**: `POST /api/rocket/claim` **请求参数**: ```json { "roomId": 123456, "userId": 789, "rocketHistoryId": "507f1f77bcf86cd799439011" } ``` **响应数据**: ```json { "rewardType": "GOLD", "rewardAmount": 1000, "isContributor": true, "contributionEnergy": 200000, "contributionRate": 57.14, "message": "恭喜您获得贡献者奖励!" } ``` ### 4.3 手动触发发射 (测试用) **接口**: `POST /api/rocket/manual-launch` **请求参数**: ``` roomId=123456 ``` --- ## 5. 已实现文件清单 ### 5.1 Domain 层 ``` ✅ RocketLevel.java - 等级枚举 ✅ RocketStatusEnum.java - 状态枚举 ✅ RocketContributor.java - 贡献者值对象 ✅ RocketStatus.java - 火箭状态领域对象 ✅ RocketStatusGateway.java - Gateway接口 ``` ### 5.2 Infrastructure 层 ``` ✅ RocketKeys.java - Redis Key枚举 ✅ RocketStatusDocument.java - MongoDB文档 ✅ RocketStatusRepository.java - MongoDB Repository ✅ RocketStatusGatewayImpl.java - Gateway实现 ``` ### 5.3 Client 层 ``` ✅ RocketEnergyAddCmd.java - 能量增加命令 ✅ RocketRewardClaimCmd.java - 奖励领取命令 ✅ RocketStatusQueryCmd.java - 状态查询命令 ✅ RocketStatusCO.java - 状态客户端对象 ✅ RocketContributorCO.java - 贡献者客户端对象 ✅ RocketRewardCO.java - 奖励客户端对象 ✅ RocketService.java - 服务接口 ``` ### 5.4 Application 层 ``` ✅ RocketEnergyAddCmdExe.java - 能量增加执行器 ✅ RocketLaunchCmdExe.java - 火箭发射执行器 ✅ RocketStatusQryExe.java - 状态查询执行器 ✅ RocketRewardClaimCmdExe.java - 奖励领取执行器 ✅ RocketServiceImpl.java - 服务实现 ✅ RocketDailyResetTask.java - 每日重置任务 ✅ RocketClaimExpireTask.java - 过期检查任务 ``` ### 5.5 Adapter 层 ``` ✅ RocketRestController.java - REST控制器 ``` --- ## 6. 待实现功能清单 ### 6.1 高优先级 (P0) #### 1. 礼物事件监听 **文件**: `GiftSendRocketListener.java` **位置**: `other-application/src/.../app/listener/` **功能**: 监听送礼事件,自动触发能量增加 ```java @EventListener public void onGiftSend(GiftSendEvent event) { RocketEnergyAddCmd cmd = new RocketEnergyAddCmd(); cmd.setRoomId(event.getRoomId()); cmd.setUserId(event.getUserId()); cmd.setGiftGoldValue(event.getGiftGoldValue()); cmd.setIsLucky(event.getIsLuckyGift()); cmd.setBizNo("GIFT_" + event.getGiftOrderId()); rocketService.addEnergy(cmd); } ``` #### 2. MySQL 表创建 **执行SQL**: - `rocket_config` 配置表 - `rocket_reward_claim_log` 领取记录表 #### 3. MongoDB 索引创建 ```javascript db.room_rocket_status.createIndex( {"room_id": 1, "date": 1}, {unique: true, name: "uk_room_date"} ) db.room_rocket_status.createIndex( {"status": 1}, {name: "idx_status"} ) ``` #### 4. 奖励发放集成 **需要**: 调用 `wallet-inner-api` **实现**: 在 `RocketRewardClaimCmdExe` 中补充 ```java // 注入 Feign Client @Autowired private WalletGoldClient walletGoldClient; // 发放奖励 String bizNo = "ROCKET_" + rocketHistoryId + "_" + userId; WalletReceiptReqDTO req = new WalletReceiptReqDTO(); req.setUserId(userId); req.setAmount(rewardAmount); req.setBizType(WalletBizType.ROCKET_REWARD); req.setBizNo(bizNo); walletGoldClient.income(req); ``` #### 5. 领取防重实现 **实现**: MySQL唯一索引 + Redis检查 ```java // 查询MySQL boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser( rocketHistoryId, userId ); if (hasClaimed) { throw new RuntimeException("您已经领取过奖励了"); } ``` ### 6.2 中优先级 (P1) #### 6. 历史记录保存 **文件**: `RocketHistoryDocument.java` + `RocketHistoryRepository.java` **功能**: 发射后保存完整历史 #### 7. WebSocket 推送 **功能**: - 能量更新推送 - 发射动画推送 - 领取通知推送 #### 8. 奖励配置管理 **功能**: 后台配置各等级奖励规则 #### 9. 排行榜查询 **文件**: `RocketRankingQryExe.java` **功能**: 查询贡献排行榜 ### 6.3 低优先级 (P2) #### 10. 管理后台接口 **功能**: - 配置管理 - 数据统计 - 手动重置 #### 11. 数据统计报表 **功能**: 发射次数、领取人数等统计 #### 12. 监控告警 **功能**: 关键指标监控、异常告警 --- ## 7. 测试用例 ### 7.1 单元测试 ```java @Test public void testEnergyAdd_Normal() { // 正常送礼增加能量 } @Test public void testEnergyAdd_Lucky() { // 幸运礼物增加能量 } @Test public void testRocketLaunch_AutoTrigger() { // 能量满自动发射 } @Test public void testRewardClaim_Contributor() { // 贡献者领取奖励 } @Test public void testRewardClaim_Duplicate() { // 重复领取防重 } ``` ### 7.2 集成测试场景 1. **完整流程**: 送礼 → 发射 → 领取 2. **并发送礼**: 100人同时送礼 3. **分布式锁**: 高并发下的锁测试 4. **过期重置**: 领取过期自动重置 --- ## 8. 部署说明 ### 8.1 环境要求 - MongoDB 4.4+ - MySQL 8.0+ - Redis 6.0+ ### 8.2 配置示例 ```yaml # application.yml spring: data: mongodb: uri: mongodb://localhost:27017/likei datasource: url: jdbc:mysql://localhost:3306/likei username: root password: password rocket: enabled: true lucky-bonus-rate: 0.04 claim-expire-seconds: 60 ``` ### 8.3 启动顺序 1. 启动 MongoDB 2. 启动 MySQL 3. 启动 Redis 4. 执行数据库初始化脚本 5. 启动应用 --- ## 9. 注意事项 ### 9.1 幂等性 - 能量增加使用 `bizNo` 防重 - 奖励领取使用 MySQL 唯一索引防重 ### 9.2 并发控制 - 使用 Redis 分布式锁 - 锁粒度:房间级别 ### 9.3 数据一致性 - MongoDB 保存实时状态 - MySQL 保存领取记录 - 通过业务逻辑保证一致性 ### 9.4 性能优化 - 热点房间使用 Redis 缓存 - 批量查询贡献者 - 异步推送消息 --- d ## 10. 联系方式 **技术支持**: system **文档维护**: Leo **最后更新**: 2025-01-15 --- ## 附录:快速上手指南 ### A. 快速测试流程 1. **初始化数据库** ```sql -- 执行 MySQL 建表语句 -- 插入火箭配置数据 ``` 2. **创建 MongoDB 索引** ```javascript use likei db.room_rocket_status.createIndex({"room_id": 1, "date": 1}, {unique: true}) ``` 3. **启动应用** ```bash cd other-start mvn spring-boot:run ``` 4. **测试接口** ```bash # 查询状态 curl "http://localhost:8080/api/rocket/status?roomId=123456" # 手动发射 (测试用) curl -X POST "http://localhost:8080/api/rocket/manual-launch?roomId=123456" # 领取奖励 curl -X POST "http://localhost:8080/api/rocket/claim" \ -H "Content-Type: application/json" \ -d '{"roomId":123456,"userId":789,"rocketHistoryId":"xxx"}' ``` ### B. 常见问题 **Q: 如何手动触发能量增加?** A: 调用 `RocketService.addEnergy(cmd)` 或监听送礼事件 **Q: 如何查看当前火箭状态?** A: 访问 MongoDB `room_rocket_status` 集合或调用查询接口 **Q: 如何重置火箭?** A: 等待每日00:00自动重置,或手动调用重置方法 --- **祝开发顺利!🚀**