# 🚀 语聊房火箭系统 - 完整需求文档

---

## 📋 文档信息

| 项目 | 内容 |
|-----|------|
| **文档名称** | 语聊房火箭系统需求文档 |
| **版本** | 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自动重置，或手动调用重置方法

---

**祝开发顺利！🚀**
