diff --git a/ROCKET_REQUIREMENT_DOC.md b/ROCKET_REQUIREMENT_DOC.md new file mode 100644 index 00000000..fe0ad3f9 --- /dev/null +++ b/ROCKET_REQUIREMENT_DOC.md @@ -0,0 +1,622 @@ +# 🚀 语聊房火箭系统 - 完整需求文档 + +--- + +## 📋 文档信息 + +| 项目 | 内容 | +|-----|------| +| **文档名称** | 语聊房火箭系统需求文档 | +| **版本** | 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自动重置,或手动调用重置方法 + +--- + +**祝开发顺利!🚀** diff --git a/rc-service/rc-service-other/docs/sql/rocket_init.sql b/rc-service/rc-service-other/docs/sql/rocket_init.sql new file mode 100644 index 00000000..138c1d26 --- /dev/null +++ b/rc-service/rc-service-other/docs/sql/rocket_init.sql @@ -0,0 +1,48 @@ +-- ============================================ +-- 火箭系统数据库初始化脚本 +-- ============================================ + +-- 1. 创建火箭配置表 +CREATE TABLE IF NOT EXISTS `rocket_config` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `level` TINYINT UNSIGNED NOT NULL COMMENT '火箭等级 1/2/3', + `max_energy` BIGINT UNSIGNED NOT NULL COMMENT '最大能量值', + `reward_config` JSON DEFAULT NULL COMMENT '奖励配置(JSON,预留)', + `contributor_reward_rate` DECIMAL(5,2) DEFAULT NULL COMMENT '贡献者奖励比例%', + `normal_reward_config` JSON DEFAULT NULL COMMENT '普通用户奖励配置(JSON,预留)', + `status` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态 1:启用 0:禁用', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_level` (`level`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='火箭配置表'; + +-- 2. 插入初始配置数据 +INSERT INTO `rocket_config` (`level`, `max_energy`, `status`) VALUES +(1, 150000, 1), +(2, 500000, 1), +(3, 1000000, 1) +ON DUPLICATE KEY UPDATE `max_energy` = VALUES(`max_energy`); + +-- 3. 创建奖励领取记录表 +CREATE TABLE IF NOT EXISTS `rocket_reward_claim_log` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `rocket_history_id` VARCHAR(32) NOT NULL COMMENT '火箭历史记录ID(MongoDB _id)', + `room_id` BIGINT UNSIGNED NOT NULL COMMENT '房间ID', + `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID', + `rocket_level` TINYINT UNSIGNED NOT NULL COMMENT '火箭等级', + `is_contributor` TINYINT UNSIGNED NOT NULL COMMENT '是否贡献者 1:是 0:否', + `contribution_energy` BIGINT UNSIGNED DEFAULT 0 COMMENT '贡献能量值', + `contribution_rate` DECIMAL(8,4) DEFAULT 0.0000 COMMENT '贡献率%', + `reward_type` VARCHAR(32) NOT NULL COMMENT '奖励类型 GOLD/DIAMOND/PROP', + `reward_item_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '奖励物品ID', + `reward_amount` BIGINT UNSIGNED NOT NULL COMMENT '奖励数量', + `wallet_biz_no` VARCHAR(64) DEFAULT NULL COMMENT '钱包业务单号', + `claimed_at` DATETIME NOT NULL COMMENT '领取时间', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + 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`), + KEY `idx_claimed_at` (`claimed_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='火箭奖励领取记录表'; diff --git a/rc-service/rc-service-other/docs/sql/rocket_mongo_init.js b/rc-service/rc-service-other/docs/sql/rocket_mongo_init.js new file mode 100644 index 00000000..069021ee --- /dev/null +++ b/rc-service/rc-service-other/docs/sql/rocket_mongo_init.js @@ -0,0 +1,82 @@ +// ============================================ +// 火箭系统MongoDB索引初始化脚本 +// 执行方式: mongosh < rocket_mongo_init.js +// ============================================ + +// 切换到likei数据库 (根据实际数据库名修改) +use likei + +print("开始创建火箭系统MongoDB索引..."); + +// 1. room_rocket_status 集合索引 +print("1. 创建 room_rocket_status 索引..."); + +// 唯一索引:房间ID+日期 +db.room_rocket_status.createIndex( + { "room_id": 1, "date": 1 }, + { unique: true, name: "uk_room_date", background: true } +); +print(" ✓ 创建唯一索引 uk_room_date"); + +// 普通索引:状态 +db.room_rocket_status.createIndex( + { "status": 1 }, + { name: "idx_status", background: true } +); +print(" ✓ 创建索引 idx_status"); + +// 普通索引:状态+过期时间 (用于定时任务清理) +db.room_rocket_status.createIndex( + { "status": 1, "claim_expire_time": 1 }, + { name: "idx_status_expire", background: true } +); +print(" ✓ 创建索引 idx_status_expire"); + +// TTL索引:30天自动过期 +db.room_rocket_status.createIndex( + { "updated_at": 1 }, + { expireAfterSeconds: 2592000, name: "ttl_30days", background: true } +); +print(" ✓ 创建TTL索引 ttl_30days (30天过期)"); + +// 2. rocket_launch_history 集合索引 +print("2. 创建 rocket_launch_history 索引..."); + +// 普通索引:房间ID+发射时间 (倒序) +db.rocket_launch_history.createIndex( + { "room_id": 1, "launch_time": -1 }, + { name: "idx_room_launch_time", background: true } +); +print(" ✓ 创建索引 idx_room_launch_time"); + +// 普通索引:日期 +db.rocket_launch_history.createIndex( + { "date": 1 }, + { name: "idx_date", background: true } +); +print(" ✓ 创建索引 idx_date"); + +// 普通索引:等级 +db.rocket_launch_history.createIndex( + { "level": 1 }, + { name: "idx_level", background: true } +); +print(" ✓ 创建索引 idx_level"); + +// 普通索引:发射时间 (用于时间范围查询) +db.rocket_launch_history.createIndex( + { "launch_time": -1 }, + { name: "idx_launch_time", background: true } +); +print(" ✓ 创建索引 idx_launch_time"); + +print("\n索引创建完成!\n"); + +// 验证索引 +print("验证 room_rocket_status 索引:"); +printjson(db.room_rocket_status.getIndexes()); + +print("\n验证 rocket_launch_history 索引:"); +printjson(db.rocket_launch_history.getIndexes()); + +print("\n火箭系统MongoDB初始化完成!"); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketLaunchCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketLaunchCmdExe.java index 6106469d..12c2b7bf 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketLaunchCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketLaunchCmdExe.java @@ -1,7 +1,12 @@ package com.red.circle.other.app.command.rocket; +import com.red.circle.other.app.convertor.RocketConvertor; +import com.red.circle.other.app.dto.clientobject.RocketStatusCO; +import com.red.circle.other.app.manager.RocketImPushManager; import com.red.circle.other.app.util.DistributedLockUtil; +import com.red.circle.other.domain.gateway.RocketHistoryGateway; import com.red.circle.other.domain.gateway.RocketStatusGateway; +import com.red.circle.other.domain.rocket.RocketHistory; import com.red.circle.other.domain.rocket.RocketStatus; import com.red.circle.other.domain.rocket.RocketStatusEnum; import com.red.circle.other.infra.database.cache.key.RocketKeys; @@ -23,7 +28,10 @@ import java.time.LocalDate; public class RocketLaunchCmdExe { private final RocketStatusGateway rocketStatusGateway; + private final RocketHistoryGateway rocketHistoryGateway; private final DistributedLockUtil distributedLockUtil; + private final RocketImPushManager rocketImPushManager; + private final RocketConvertor rocketConvertor; /** * 执行火箭发射 @@ -74,14 +82,28 @@ public class RocketLaunchCmdExe { // 6. 保存状态 rocketStatusGateway.save(rocketStatus); + // 7. 保存历史记录 + try { + RocketHistory history = RocketHistory.fromRocketStatus(rocketStatus); + String historyId = rocketHistoryGateway.save(history); + log.info("保存火箭历史记录成功: historyId={}, roomId={}", historyId, roomId); + } catch (Exception e) { + log.error("保存火箭历史记录失败: roomId={}", roomId, e); + } + + // 8. 推送发射动画消息 + try { + RocketStatusCO statusCO = rocketConvertor.toStatusCO(rocketStatus); + rocketImPushManager.pushRocketLaunch(roomId, statusCO); + } catch (Exception e) { + log.error("推送火箭发射消息失败: roomId={}", roomId, e); + } + log.info("火箭发射成功, roomId={}, level={}, finalEnergy={}, contributors={}, launchTime={}", roomId, rocketStatus.getLevel().getLevel(), rocketStatus.getCurrentEnergy(), rocketStatus.getContributors() != null ? rocketStatus.getContributors().size() : 0, rocketStatus.getLaunchTime()); - - // TODO: 7. 推送发射动画 (WebSocket) - // TODO: 8. 保存历史记录 } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java index d714379f..014af86e 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java @@ -2,9 +2,13 @@ package com.red.circle.other.app.command.rocket; import com.red.circle.other.app.dto.clientobject.RocketRewardCO; import com.red.circle.other.app.dto.cmd.RocketRewardClaimCmd; +import com.red.circle.other.app.manager.RocketImPushManager; import com.red.circle.other.app.util.DistributedLockUtil; +import com.red.circle.other.domain.gateway.RocketHistoryGateway; +import com.red.circle.other.domain.gateway.RocketRewardLogGateway; import com.red.circle.other.domain.gateway.RocketStatusGateway; import com.red.circle.other.domain.rocket.RocketContributor; +import com.red.circle.other.domain.rocket.RocketRewardLog; import com.red.circle.other.domain.rocket.RocketStatus; import com.red.circle.other.infra.database.cache.key.RocketKeys; import lombok.RequiredArgsConstructor; @@ -12,6 +16,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.time.LocalDate; +import java.time.LocalDateTime; /** * 火箭奖励领取命令执行器 @@ -25,8 +30,13 @@ import java.time.LocalDate; public class RocketRewardClaimCmdExe { private final RocketStatusGateway rocketStatusGateway; + private final RocketRewardLogGateway rocketRewardLogGateway; + private final RocketHistoryGateway rocketHistoryGateway; private final DistributedLockUtil distributedLockUtil; - // TODO: 后续需要注入 WalletGoldClient 调用钱包服务 + private final RocketImPushManager rocketImPushManager; + + // TODO: 注入钱包服务Feign Client + // private final WalletGoldClient walletGoldClient; /** * 执行奖励领取 @@ -60,46 +70,67 @@ public class RocketRewardClaimCmdExe { throw new RuntimeException("当前状态不可领取奖励"); } - // 3. 检查是否已领取 (TODO: 需要查询MySQL防重) - // boolean hasClaimed = checkHasClaimed(cmd.getRoomId(), cmd.getUserId(), rocketStatus.getId()); - // if (hasClaimed) { - // throw new RuntimeException("您已经领取过奖励了"); - // } + // 3. 检查是否已领取 (MySQL防重) + boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser( + cmd.getRocketHistoryId(), cmd.getUserId()); + if (hasClaimed) { + throw new RuntimeException("您已经领取过奖励了"); + } // 4. 判断是否为贡献者 RocketContributor contributor = findContributor(rocketStatus, cmd.getUserId()); boolean isContributor = contributor != null; - // 5. 计算奖励 (TODO: 奖励配置后续补充) + // 5. 计算奖励 (TODO: 后续根据配置计算) + Long rewardAmount = calculateReward(isContributor, contributor, rocketStatus); + RocketRewardCO rewardCO = new RocketRewardCO(); rewardCO.setIsContributor(isContributor); + rewardCO.setRewardType("GOLD"); + rewardCO.setRewardAmount(rewardAmount); if (isContributor) { - // 贡献者奖励 rewardCO.setContributionEnergy(contributor.getEnergy()); rewardCO.setContributionRate(contributor.getContributionRate()); - rewardCO.setRewardType("GOLD"); - rewardCO.setRewardAmount(1000L); // TODO: 根据配置计算 rewardCO.setMessage("恭喜您获得贡献者奖励!"); } else { - // 普通用户奖励 - rewardCO.setRewardType("GOLD"); - rewardCO.setRewardAmount(100L); // TODO: 根据配置计算 rewardCO.setMessage("恭喜您获得参与奖励!"); } - // 6. 发放奖励 (TODO: 调用钱包服务) - // String bizNo = "ROCKET_" + rocketStatus.getId() + "_" + cmd.getUserId(); - // walletGoldClient.income(...); + // 6. 发放奖励 (调用钱包服务) + String walletBizNo = RocketRewardLog.buildBizNo(cmd.getRocketHistoryId(), cmd.getUserId()); + try { + sendRewardToWallet(cmd.getUserId(), rewardAmount, walletBizNo); + } catch (Exception e) { + log.error("调用钱包服务发放奖励失败: userId={}, amount={}", cmd.getUserId(), rewardAmount, e); + throw new RuntimeException("奖励发放失败,请稍后重试"); + } - // 7. 记录领取日志 (TODO: 保存到MySQL) + // 7. 记录领取日志 (MySQL) + saveRewardLog(cmd, rocketStatus, isContributor, contributor, rewardAmount, walletBizNo); // 8. 更新领取人数 rocketStatus.incrementClaimCount(); rocketStatusGateway.save(rocketStatus); + // 9. 更新历史记录的领取统计 + try { + rocketHistoryGateway.updateClaimStats(cmd.getRocketHistoryId(), 1, rewardAmount); + } catch (Exception e) { + log.error("更新历史记录领取统计失败: historyId={}", cmd.getRocketHistoryId(), e); + } + + // 10. 推送领取成功消息 + try { + // TODO: 获取用户名称 + String userName = "用户" + cmd.getUserId(); + rocketImPushManager.pushRewardClaimed(cmd.getRoomId(), cmd.getUserId(), userName, rewardAmount); + } catch (Exception e) { + log.error("推送奖励领取消息失败: roomId={}, userId={}", cmd.getRoomId(), cmd.getUserId(), e); + } + log.info("火箭奖励领取成功, roomId={}, userId={}, isContributor={}, rewardAmount={}", - cmd.getRoomId(), cmd.getUserId(), isContributor, rewardCO.getRewardAmount()); + cmd.getRoomId(), cmd.getUserId(), isContributor, rewardAmount); return rewardCO; } @@ -116,4 +147,73 @@ public class RocketRewardClaimCmdExe { .findFirst() .orElse(null); } + + /** + * 计算奖励金额 + * TODO: 后续根据配置表计算 + */ + private Long calculateReward(boolean isContributor, RocketContributor contributor, RocketStatus rocketStatus) { + if (isContributor) { + // 贡献者奖励 = 基础奖励 + 按贡献率分配的额外奖励 + // 目前固定返回1000,后续根据配置计算 + return 1000L; + } else { + // 普通用户固定奖励 + return 100L; + } + } + + /** + * 调用钱包服务发放奖励 + * TODO: 集成wallet-inner-api + */ + private void sendRewardToWallet(Long userId, Long amount, String bizNo) { + // 示例代码,需要引入实际的Feign Client + /* + WalletReceiptReqDTO req = new WalletReceiptReqDTO(); + req.setUserId(userId); + req.setAmount(PennyAmount.of(amount)); // 金币 + req.setBizType(WalletBizType.ROCKET_REWARD); + req.setBizNo(bizNo); + req.setRemark("火箭奖励"); + + ResultResponse response = walletGoldClient.income(req); + if (!response.isSuccess()) { + throw new RuntimeException("钱包服务调用失败: " + response.getMessage()); + } + */ + + log.info("TODO: 调用钱包服务发放奖励, userId={}, amount={}, bizNo={}", userId, amount, bizNo); + } + + /** + * 保存领取记录 + */ + private void saveRewardLog(RocketRewardClaimCmd cmd, RocketStatus rocketStatus, + boolean isContributor, RocketContributor contributor, + Long rewardAmount, String walletBizNo) { + RocketRewardLog log = new RocketRewardLog(); + log.setRocketHistoryId(cmd.getRocketHistoryId()); + log.setRoomId(cmd.getRoomId()); + log.setUserId(cmd.getUserId()); + log.setRocketLevel(rocketStatus.getLevel().getLevel()); + log.setIsContributor(isContributor ? 1 : 0); + + if (isContributor && contributor != null) { + log.setContributionEnergy(contributor.getEnergy()); + log.setContributionRate(contributor.getContributionRate()); + } else { + log.setContributionEnergy(0L); + log.setContributionRate(null); + } + + log.setRewardType("GOLD"); + log.setRewardItemId(null); + log.setRewardAmount(rewardAmount); + log.setWalletBizNo(walletBizNo); + log.setClaimedAt(LocalDateTime.now()); + log.setCreatedAt(LocalDateTime.now()); + + rocketRewardLogGateway.save(log); + } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/GiftSendRocketListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/GiftSendRocketListener.java new file mode 100644 index 00000000..6b6e56e9 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/GiftSendRocketListener.java @@ -0,0 +1,72 @@ +package com.red.circle.other.app.listener; + +import com.red.circle.other.app.dto.cmd.RocketEnergyAddCmd; +import com.red.circle.other.app.service.RocketService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * 礼物送出事件监听器 - 触发火箭能量增加 + * + * @author system + * @date 2025-01-15 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class GiftSendRocketListener { + + private final RocketService rocketService; + + /** + * 监听礼物送出事件 + * 注意:需要项目中实际的GiftSendEvent类,这里使用占位符 + */ + @Async("rocketExecutor") + @EventListener + public void onGiftSend(Object event) { + // TODO: 替换为实际的GiftSendEvent类型 + // 示例代码,需要根据实际事件结构调整 + + /* + try { + // 1. 从事件中提取数据 + Long roomId = event.getRoomId(); + Long userId = event.getUserId(); + Long giftGoldValue = event.getGiftGoldValue(); + Boolean isLucky = event.getIsLuckyGift(); + String giftOrderId = event.getGiftOrderId(); + + // 2. 构建命令 + RocketEnergyAddCmd cmd = new RocketEnergyAddCmd(); + cmd.setRoomId(roomId); + cmd.setUserId(userId); + cmd.setGiftGoldValue(giftGoldValue); + cmd.setIsLucky(isLucky != null && isLucky); + cmd.setBizNo("GIFT_" + giftOrderId); + + // 3. 执行能量增加 + rocketService.addEnergy(cmd); + + log.info("礼物事件触发火箭能量增加成功: roomId={}, userId={}, energy={}", + roomId, userId, giftGoldValue); + + } catch (Exception e) { + log.error("礼物事件触发火箭能量增加失败", e); + } + */ + + log.warn("GiftSendRocketListener: 需要接入实际的礼物送出事件"); + } + + /** + * 说明: + * 1. 需要在项目中找到实际的礼物送出事件类(可能在live-service或other-service中) + * 2. 替换方法参数中的 Object 为实际的事件类型 + * 3. 根据实际事件结构提取 roomId、userId、giftGoldValue 等字段 + * 4. 确保事件中包含 isLuckyGift 字段或通过其他方式判断是否幸运礼物 + */ +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketImPushManager.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketImPushManager.java new file mode 100644 index 00000000..3e70af6b --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketImPushManager.java @@ -0,0 +1,178 @@ +package com.red.circle.other.app.manager; + +import com.alibaba.fastjson.JSON; +import com.red.circle.other.app.dto.clientobject.RocketStatusCO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 火箭IM消息推送管理器 + * + * @author system + * @date 2025-01-15 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class RocketImPushManager { + + // TODO: 注入实际的IM推送服务 + // private final ImPushService imPushService; + + /** + * 推送能量更新消息 + */ + public void pushEnergyUpdate(Long roomId, RocketStatusCO status) { + try { + // 构建消息 + ImMessage message = buildEnergyUpdateMessage(status); + + // TODO: 调用实际的IM推送服务 + // imPushService.pushToRoom(roomId, message); + + log.info("推送火箭能量更新消息成功: roomId={}, level={}, energy={}/{}", + roomId, status.getLevel(), status.getCurrentEnergy(), status.getMaxEnergy()); + + } catch (Exception e) { + log.error("推送火箭能量更新消息失败: roomId={}", roomId, e); + } + } + + /** + * 推送火箭发射消息 + */ + public void pushRocketLaunch(Long roomId, RocketStatusCO status) { + try { + // 构建消息 + ImMessage message = buildLaunchMessage(status); + + // TODO: 调用实际的IM推送服务 + // imPushService.pushToRoom(roomId, message); + + log.info("推送火箭发射消息成功: roomId={}, level={}", roomId, status.getLevel()); + + } catch (Exception e) { + log.error("推送火箭发射消息失败: roomId={}", roomId, e); + } + } + + /** + * 推送奖励领取成功消息 + */ + public void pushRewardClaimed(Long roomId, Long userId, String userName, Long rewardAmount) { + try { + // 构建消息 + ImMessage message = buildRewardClaimedMessage(userId, userName, rewardAmount); + + // TODO: 调用实际的IM推送服务 + // imPushService.pushToRoom(roomId, message); + + log.info("推送奖励领取消息成功: roomId={}, userId={}, amount={}", roomId, userId, rewardAmount); + + } catch (Exception e) { + log.error("推送奖励领取消息失败: roomId={}, userId={}", roomId, userId, e); + } + } + + /** + * 推送火箭重置消息 + */ + public void pushRocketReset(Long roomId) { + try { + // 构建消息 + ImMessage message = buildResetMessage(); + + // TODO: 调用实际的IM推送服务 + // imPushService.pushToRoom(roomId, message); + + log.info("推送火箭重置消息成功: roomId={}", roomId); + + } catch (Exception e) { + log.error("推送火箭重置消息失败: roomId={}", roomId, e); + } + } + + /** + * 构建能量更新消息 + */ + private ImMessage buildEnergyUpdateMessage(RocketStatusCO status) { + ImMessage message = new ImMessage(); + message.setType("ROCKET_ENERGY_UPDATE"); + message.setData(JSON.toJSONString(status)); + return message; + } + + /** + * 构建发射消息 + */ + private ImMessage buildLaunchMessage(RocketStatusCO status) { + ImMessage message = new ImMessage(); + message.setType("ROCKET_LAUNCH"); + message.setData(JSON.toJSONString(status)); + return message; + } + + /** + * 构建奖励领取消息 + */ + private ImMessage buildRewardClaimedMessage(Long userId, String userName, Long rewardAmount) { + RewardClaimedData data = new RewardClaimedData(); + data.setUserId(userId); + data.setUserName(userName); + data.setRewardAmount(rewardAmount); + + ImMessage message = new ImMessage(); + message.setType("ROCKET_REWARD_CLAIMED"); + message.setData(JSON.toJSONString(data)); + return message; + } + + /** + * 构建重置消息 + */ + private ImMessage buildResetMessage() { + ImMessage message = new ImMessage(); + message.setType("ROCKET_RESET"); + message.setData("{}"); + return message; + } + + /** + * IM消息对象(示例) + * TODO: 替换为实际的IM消息类 + */ + private static class ImMessage { + private String type; + private String data; + + public void setType(String type) { + this.type = type; + } + + public void setData(String data) { + this.data = data; + } + } + + /** + * 奖励领取数据 + */ + private static class RewardClaimedData { + private Long userId; + private String userName; + private Long rewardAmount; + + public void setUserId(Long userId) { + this.userId = userId; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setRewardAmount(Long rewardAmount) { + this.rewardAmount = rewardAmount; + } + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketConfigGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketConfigGateway.java new file mode 100644 index 00000000..3d10838b --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketConfigGateway.java @@ -0,0 +1,39 @@ +package com.red.circle.other.domain.gateway; + +import com.red.circle.other.domain.rocket.RocketConfig; + +import java.util.List; + +/** + * 火箭配置网关 + * + * @author system + * @date 2025-01-15 + */ +public interface RocketConfigGateway { + + /** + * 根据等级查询配置 + */ + RocketConfig findByLevel(Integer level); + + /** + * 查询所有配置 + */ + List findAll(); + + /** + * 查询启用的配置 + */ + List findAllEnabled(); + + /** + * 保存配置 + */ + void save(RocketConfig config); + + /** + * 更新配置 + */ + void update(RocketConfig config); +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java new file mode 100644 index 00000000..53af87b8 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java @@ -0,0 +1,45 @@ +package com.red.circle.other.domain.gateway; + +import com.red.circle.other.domain.rocket.RocketHistory; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 火箭历史网关 + * + * @author system + * @date 2025-01-15 + */ +public interface RocketHistoryGateway { + + /** + * 保存历史记录 + */ + String save(RocketHistory history); + + /** + * 根据ID查询 + */ + RocketHistory findById(String id); + + /** + * 根据房间ID查询历史 + */ + List findByRoomId(Long roomId); + + /** + * 根据房间ID和日期查询 + */ + List findByRoomIdAndDate(Long roomId, String date); + + /** + * 根据房间ID和时间范围查询 + */ + List findByRoomIdAndTimeRange(Long roomId, LocalDateTime startTime, LocalDateTime endTime); + + /** + * 更新领取统计 + */ + void updateClaimStats(String id, int claimedCount, long rewardSent); +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketRewardLogGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketRewardLogGateway.java new file mode 100644 index 00000000..708756b2 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketRewardLogGateway.java @@ -0,0 +1,39 @@ +package com.red.circle.other.domain.gateway; + +import com.red.circle.other.domain.rocket.RocketRewardLog; + +import java.util.List; + +/** + * 火箭奖励领取记录网关 + * + * @author system + * @date 2025-01-15 + */ +public interface RocketRewardLogGateway { + + /** + * 保存领取记录 + */ + void save(RocketRewardLog log); + + /** + * 检查是否已领取 + */ + boolean existsByRocketAndUser(String rocketHistoryId, Long userId); + + /** + * 根据火箭历史ID查询所有领取记录 + */ + List findByRocketHistoryId(String rocketHistoryId); + + /** + * 根据房间ID和用户ID查询 + */ + List findByRoomIdAndUserId(Long roomId, Long userId); + + /** + * 统计某次发射的领取人数 + */ + int countByRocketHistoryId(String rocketHistoryId); +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketConfig.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketConfig.java new file mode 100644 index 00000000..0b1d2bff --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketConfig.java @@ -0,0 +1,83 @@ +package com.red.circle.other.domain.rocket; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 火箭配置领域对象 + * + * @author system + * @date 2025-01-15 + */ +@Data +public class RocketConfig { + + /** + * 主键ID + */ + private Long id; + + /** + * 火箭等级 + */ + private Integer level; + + /** + * 最大能量值 + */ + private Long maxEnergy; + + /** + * 奖励配置(JSON预留) + */ + private String rewardConfig; + + /** + * 贡献者奖励比例% + */ + private BigDecimal contributorRewardRate; + + /** + * 普通用户奖励配置(JSON预留) + */ + private String normalRewardConfig; + + /** + * 状态 1:启用 0:禁用 + */ + private Integer status; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; + + /** + * 更新时间 + */ + private LocalDateTime updatedAt; + + /** + * 是否启用 + */ + public boolean isEnabled() { + return Integer.valueOf(1).equals(this.status); + } + + /** + * 获取等级名称 + */ + public String getLevelName() { + if (level == null) { + return ""; + } + return switch (level) { + case 1 -> "一级火箭"; + case 2 -> "二级火箭"; + case 3 -> "三级火箭"; + default -> "未知等级"; + }; + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java new file mode 100644 index 00000000..e6f6bb54 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java @@ -0,0 +1,122 @@ +package com.red.circle.other.domain.rocket; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * 火箭历史领域对象 + * + * @author system + * @date 2025-01-15 + */ +@Data +public class RocketHistory { + + private String id; + private Long roomId; + private String date; + private Integer level; + private Long finalEnergy; + private Long overEnergy; + private List contributors; + private Integer totalContributors; + private RewardPool rewardPool; + private ClaimStats claimStats; + private LocalDateTime launchTime; + private LocalDateTime claimStartTime; + private LocalDateTime claimEndTime; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + @Data + public static class HistoryContributor { + private Long userId; + private String userName; + private String userAvatar; + private Long energy; + private BigDecimal contributionRate; + private Integer rank; + } + + @Data + public static class RewardPool { + private Long totalGold; + private Long contributorGold; + private Long normalGold; + + public static RewardPool empty() { + RewardPool pool = new RewardPool(); + pool.setTotalGold(0L); + pool.setContributorGold(0L); + pool.setNormalGold(0L); + return pool; + } + } + + @Data + public static class ClaimStats { + private Integer totalClaimed; + private Integer contributorClaimed; + private Integer normalClaimed; + private Long totalRewardSent; + + public static ClaimStats init() { + ClaimStats stats = new ClaimStats(); + stats.setTotalClaimed(0); + stats.setContributorClaimed(0); + stats.setNormalClaimed(0); + stats.setTotalRewardSent(0L); + return stats; + } + } + + /** + * 从RocketStatus创建历史记录 + */ + public static RocketHistory fromRocketStatus(RocketStatus status) { + RocketHistory history = new RocketHistory(); + history.setRoomId(status.getRoomId()); + history.setDate(status.getDate()); + history.setLevel(status.getLevel().getLevel()); + history.setFinalEnergy(status.getCurrentEnergy()); + + // 计算超出能量 + Long overEnergy = status.getCurrentEnergy() - status.getLevel().getMaxEnergy(); + history.setOverEnergy(Math.max(overEnergy, 0L)); + + // 转换贡献者 + List historyContributors = new ArrayList<>(); + for (int i = 0; i < Math.min(status.getContributors().size(), 100); i++) { + RocketContributor contributor = status.getContributors().get(i); + HistoryContributor hc = new HistoryContributor(); + hc.setUserId(contributor.getUserId()); + hc.setUserName(contributor.getUserName()); + hc.setUserAvatar(contributor.getUserAvatar()); + hc.setEnergy(contributor.getEnergy()); + hc.setContributionRate(contributor.getContributionRate()); + hc.setRank(i + 1); + historyContributors.add(hc); + } + history.setContributors(historyContributors); + history.setTotalContributors(status.getContributors().size()); + + // 设置奖励池 (预留) + history.setRewardPool(RewardPool.empty()); + + // 初始化领取统计 + history.setClaimStats(ClaimStats.init()); + + // 设置时间 + history.setLaunchTime(status.getLaunchTime()); + history.setClaimStartTime(status.getLaunchTime()); + history.setClaimEndTime(status.getClaimExpireTime()); + history.setCreatedAt(LocalDateTime.now()); + history.setUpdatedAt(LocalDateTime.now()); + + return history; + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketRewardLog.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketRewardLog.java new file mode 100644 index 00000000..e6c2d5bb --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketRewardLog.java @@ -0,0 +1,100 @@ +package com.red.circle.other.domain.rocket; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 火箭奖励领取记录领域对象 + * + * @author system + * @date 2025-01-15 + */ +@Data +public class RocketRewardLog { + + /** + * 主键ID + */ + private Long id; + + /** + * 火箭历史记录ID (MongoDB _id) + */ + private String rocketHistoryId; + + /** + * 房间ID + */ + private Long roomId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 火箭等级 + */ + private Integer rocketLevel; + + /** + * 是否贡献者 1:是 0:否 + */ + private Integer isContributor; + + /** + * 贡献能量值 + */ + private Long contributionEnergy; + + /** + * 贡献率% + */ + private BigDecimal contributionRate; + + /** + * 奖励类型 GOLD/DIAMOND/PROP + */ + private String rewardType; + + /** + * 奖励物品ID + */ + private Long rewardItemId; + + /** + * 奖励数量 + */ + private Long rewardAmount; + + /** + * 钱包业务单号 + */ + private String walletBizNo; + + /** + * 领取时间 + */ + private LocalDateTime claimedAt; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; + + /** + * 判断是否为贡献者 + */ + public boolean isContributorUser() { + return Integer.valueOf(1).equals(this.isContributor); + } + + /** + * 构建业务单号 + */ + public static String buildBizNo(String rocketHistoryId, Long userId) { + return "ROCKET_" + rocketHistoryId + "_" + userId; + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java new file mode 100644 index 00000000..30adb13c --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java @@ -0,0 +1,31 @@ +package com.red.circle.other.infra.convertor; + +import com.red.circle.other.domain.rocket.RocketConfig; +import com.red.circle.other.infra.database.rds.entity.RocketConfigEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * 火箭配置转换器 + * + * @author system + * @date 2025-01-15 + */ +@Mapper(componentModel = "spring") +public interface RocketConfigConvertor { + + RocketConfigConvertor INSTANCE = Mappers.getMapper(RocketConfigConvertor.class); + + /** + * Entity转Domain + */ + RocketConfig toRocketConfig(RocketConfigEntity entity); + + /** + * Domain转Entity + */ + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "updatedAt", ignore = true) + RocketConfigEntity toEntity(RocketConfig config); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketHistoryConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketHistoryConvertor.java new file mode 100644 index 00000000..839336d8 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketHistoryConvertor.java @@ -0,0 +1,28 @@ +package com.red.circle.other.infra.convertor; + +import com.red.circle.other.domain.rocket.RocketHistory; +import com.red.circle.other.infra.database.mongo.document.RocketHistoryDocument; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 火箭历史转换器 + * + * @author system + * @date 2025-01-15 + */ +@Mapper(componentModel = "spring") +public interface RocketHistoryConvertor { + + RocketHistoryConvertor INSTANCE = Mappers.getMapper(RocketHistoryConvertor.class); + + /** + * Document转Domain + */ + RocketHistory toDomain(RocketHistoryDocument document); + + /** + * Domain转Document + */ + RocketHistoryDocument toDocument(RocketHistory history); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketRewardLogConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketRewardLogConvertor.java new file mode 100644 index 00000000..937be84e --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketRewardLogConvertor.java @@ -0,0 +1,30 @@ +package com.red.circle.other.infra.convertor; + +import com.red.circle.other.domain.rocket.RocketRewardLog; +import com.red.circle.other.infra.database.rds.entity.RocketRewardClaimLogEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * 火箭奖励领取记录转换器 + * + * @author system + * @date 2025-01-15 + */ +@Mapper(componentModel = "spring") +public interface RocketRewardLogConvertor { + + RocketRewardLogConvertor INSTANCE = Mappers.getMapper(RocketRewardLogConvertor.class); + + /** + * Entity转Domain + */ + RocketRewardLog toDomain(RocketRewardClaimLogEntity entity); + + /** + * Domain转Entity + */ + @Mapping(target = "createdAt", ignore = true) + RocketRewardClaimLogEntity toEntity(RocketRewardLog log); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java new file mode 100644 index 00000000..110a780f --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java @@ -0,0 +1,113 @@ +package com.red.circle.other.infra.database.mongo.document; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 火箭发射历史文档 + * + * @author system + * @date 2025-01-15 + */ +@Data +@Document(collection = "rocket_launch_history") +public class RocketHistoryDocument { + + @Id + private String id; + + @Field("room_id") + private Long roomId; + + @Field("date") + private String date; + + @Field("level") + private Integer level; + + @Field("final_energy") + private Long finalEnergy; + + @Field("over_energy") + private Long overEnergy; + + @Field("contributors") + private List contributors; + + @Field("total_contributors") + private Integer totalContributors; + + @Field("reward_pool") + private RewardPool rewardPool; + + @Field("claim_stats") + private ClaimStats claimStats; + + @Field("launch_time") + private LocalDateTime launchTime; + + @Field("claim_start_time") + private LocalDateTime claimStartTime; + + @Field("claim_end_time") + private LocalDateTime claimEndTime; + + @Field("created_at") + private LocalDateTime createdAt; + + @Field("updated_at") + private LocalDateTime updatedAt; + + @Data + public static class HistoryContributor { + @Field("user_id") + private Long userId; + + @Field("user_name") + private String userName; + + @Field("user_avatar") + private String userAvatar; + + @Field("energy") + private Long energy; + + @Field("contribution_rate") + private Double contributionRate; + + @Field("rank") + private Integer rank; + } + + @Data + public static class RewardPool { + @Field("total_gold") + private Long totalGold; + + @Field("contributor_gold") + private Long contributorGold; + + @Field("normal_gold") + private Long normalGold; + } + + @Data + public static class ClaimStats { + @Field("total_claimed") + private Integer totalClaimed; + + @Field("contributor_claimed") + private Integer contributorClaimed; + + @Field("normal_claimed") + private Integer normalClaimed; + + @Field("total_reward_sent") + private Long totalRewardSent; + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java new file mode 100644 index 00000000..98e658ce --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java @@ -0,0 +1,42 @@ +package com.red.circle.other.infra.database.mongo.repository; + +import com.red.circle.other.infra.database.mongo.document.RocketHistoryDocument; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 火箭历史Repository + * + * @author system + * @date 2025-01-15 + */ +@Repository +public interface RocketHistoryRepository extends MongoRepository { + + /** + * 根据房间ID查询历史 + */ + List findByRoomIdOrderByLaunchTimeDesc(Long roomId); + + /** + * 根据房间ID和日期查询 + */ + List findByRoomIdAndDateOrderByLaunchTimeDesc(Long roomId, String date); + + /** + * 根据房间ID和时间范围查询 + */ + List findByRoomIdAndLaunchTimeBetweenOrderByLaunchTimeDesc( + Long roomId, + LocalDateTime startTime, + LocalDateTime endTime + ); + + /** + * 根据等级查询 + */ + List findByLevelOrderByLaunchTimeDesc(Integer level); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketConfigMapper.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketConfigMapper.java new file mode 100644 index 00000000..6078e388 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketConfigMapper.java @@ -0,0 +1,47 @@ +package com.red.circle.other.infra.database.rds.dao; + +import com.red.circle.other.infra.database.rds.entity.RocketConfigEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 火箭配置Mapper + * + * @author system + * @date 2025-01-15 + */ +@Mapper +public interface RocketConfigMapper { + + /** + * 根据等级查询 + */ + RocketConfigEntity selectByLevel(@Param("level") Integer level); + + /** + * 查询所有 + */ + List selectAll(); + + /** + * 查询启用的 + */ + List selectAllEnabled(); + + /** + * 插入 + */ + int insert(RocketConfigEntity entity); + + /** + * 更新 + */ + int update(RocketConfigEntity entity); + + /** + * 根据ID查询 + */ + RocketConfigEntity selectById(@Param("id") Long id); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketRewardClaimLogMapper.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketRewardClaimLogMapper.java new file mode 100644 index 00000000..d4658f36 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/RocketRewardClaimLogMapper.java @@ -0,0 +1,44 @@ +package com.red.circle.other.infra.database.rds.dao; + +import com.red.circle.other.infra.database.rds.entity.RocketRewardClaimLogEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 火箭奖励领取记录Mapper + * + * @author system + * @date 2025-01-15 + */ +@Mapper +public interface RocketRewardClaimLogMapper { + + /** + * 插入 + */ + int insert(RocketRewardClaimLogEntity entity); + + /** + * 检查是否已领取 + */ + int countByRocketAndUser(@Param("rocketHistoryId") String rocketHistoryId, + @Param("userId") Long userId); + + /** + * 根据火箭历史ID查询 + */ + List selectByRocketHistoryId(@Param("rocketHistoryId") String rocketHistoryId); + + /** + * 根据房间ID和用户ID查询 + */ + List selectByRoomIdAndUserId(@Param("roomId") Long roomId, + @Param("userId") Long userId); + + /** + * 统计某次发射的领取人数 + */ + int countByRocketHistoryId(@Param("rocketHistoryId") String rocketHistoryId); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketConfigEntity.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketConfigEntity.java new file mode 100644 index 00000000..060caff3 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketConfigEntity.java @@ -0,0 +1,63 @@ +package com.red.circle.other.infra.database.rds.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 火箭配置实体 + * + * @author system + * @date 2025-01-15 + */ +@Data +@TableName("rocket_config") +public class RocketConfigEntity { + + /** + * 主键ID + */ + private Long id; + + /** + * 火箭等级 1/2/3 + */ + private Integer level; + + /** + * 最大能量值 + */ + private Long maxEnergy; + + /** + * 奖励配置(JSON) + */ + private String rewardConfig; + + /** + * 贡献者奖励比例% + */ + private BigDecimal contributorRewardRate; + + /** + * 普通用户奖励配置(JSON) + */ + private String normalRewardConfig; + + /** + * 状态 1:启用 0:禁用 + */ + private Integer status; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; + + /** + * 更新时间 + */ + private LocalDateTime updatedAt; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketRewardClaimLogEntity.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketRewardClaimLogEntity.java new file mode 100644 index 00000000..5bdea824 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/RocketRewardClaimLogEntity.java @@ -0,0 +1,88 @@ +package com.red.circle.other.infra.database.rds.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 火箭奖励领取记录实体 + * + * @author system + * @date 2025-01-15 + */ +@Data +@TableName("rocket_reward_claim_log") +public class RocketRewardClaimLogEntity { + + /** + * 主键ID + */ + private Long id; + + /** + * 火箭历史记录ID + */ + private String rocketHistoryId; + + /** + * 房间ID + */ + private Long roomId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 火箭等级 + */ + private Integer rocketLevel; + + /** + * 是否贡献者 + */ + private Integer isContributor; + + /** + * 贡献能量值 + */ + private Long contributionEnergy; + + /** + * 贡献率% + */ + private BigDecimal contributionRate; + + /** + * 奖励类型 + */ + private String rewardType; + + /** + * 奖励物品ID + */ + private Long rewardItemId; + + /** + * 奖励数量 + */ + private Long rewardAmount; + + /** + * 钱包业务单号 + */ + private String walletBizNo; + + /** + * 领取时间 + */ + private LocalDateTime claimedAt; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketConfigGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketConfigGatewayImpl.java new file mode 100644 index 00000000..831923be --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketConfigGatewayImpl.java @@ -0,0 +1,63 @@ +package com.red.circle.other.infra.gateway; + +import com.red.circle.other.domain.gateway.RocketConfigGateway; +import com.red.circle.other.domain.rocket.RocketConfig; +import com.red.circle.other.infra.convertor.RocketConfigConvertor; +import com.red.circle.other.infra.database.rds.dao.RocketConfigMapper; +import com.red.circle.other.infra.database.rds.entity.RocketConfigEntity; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 火箭配置网关实现 + * + * @author system + * @date 2025-01-15 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class RocketConfigGatewayImpl implements RocketConfigGateway { + + private final RocketConfigMapper rocketConfigMapper; + private final RocketConfigConvertor rocketConfigConvertor; + + @Override + public RocketConfig findByLevel(Integer level) { + RocketConfigEntity entity = rocketConfigMapper.selectByLevel(level); + return rocketConfigConvertor.toRocketConfig(entity); + } + + @Override + public List findAll() { + List entities = rocketConfigMapper.selectAll(); + return entities.stream() + .map(rocketConfigConvertor::toRocketConfig) + .collect(Collectors.toList()); + } + + @Override + public List findAllEnabled() { + List entities = rocketConfigMapper.selectAllEnabled(); + return entities.stream() + .map(rocketConfigConvertor::toRocketConfig) + .collect(Collectors.toList()); + } + + @Override + public void save(RocketConfig config) { + RocketConfigEntity entity = rocketConfigConvertor.toEntity(config); + rocketConfigMapper.insert(entity); + config.setId(entity.getId()); + } + + @Override + public void update(RocketConfig config) { + RocketConfigEntity entity = rocketConfigConvertor.toEntity(config); + rocketConfigMapper.update(entity); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java new file mode 100644 index 00000000..c355e4a1 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java @@ -0,0 +1,88 @@ +package com.red.circle.other.infra.gateway; + +import com.red.circle.other.domain.gateway.RocketHistoryGateway; +import com.red.circle.other.domain.rocket.RocketHistory; +import com.red.circle.other.infra.convertor.RocketHistoryConvertor; +import com.red.circle.other.infra.database.mongo.document.RocketHistoryDocument; +import com.red.circle.other.infra.database.mongo.repository.RocketHistoryRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 火箭历史网关实现 + * + * @author system + * @date 2025-01-15 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class RocketHistoryGatewayImpl implements RocketHistoryGateway { + + private final RocketHistoryRepository rocketHistoryRepository; + private final RocketHistoryConvertor rocketHistoryConvertor; + private final MongoTemplate mongoTemplate; + + @Override + public String save(RocketHistory history) { + RocketHistoryDocument document = rocketHistoryConvertor.toDocument(history); + document.setCreatedAt(LocalDateTime.now()); + document.setUpdatedAt(LocalDateTime.now()); + RocketHistoryDocument saved = rocketHistoryRepository.save(document); + return saved.getId(); + } + + @Override + public RocketHistory findById(String id) { + return rocketHistoryRepository.findById(id) + .map(rocketHistoryConvertor::toDomain) + .orElse(null); + } + + @Override + public List findByRoomId(Long roomId) { + List documents = rocketHistoryRepository.findByRoomIdOrderByLaunchTimeDesc(roomId); + return documents.stream() + .map(rocketHistoryConvertor::toDomain) + .collect(Collectors.toList()); + } + + @Override + public List findByRoomIdAndDate(Long roomId, String date) { + List documents = + rocketHistoryRepository.findByRoomIdAndDateOrderByLaunchTimeDesc(roomId, date); + return documents.stream() + .map(rocketHistoryConvertor::toDomain) + .collect(Collectors.toList()); + } + + @Override + public List findByRoomIdAndTimeRange(Long roomId, LocalDateTime startTime, LocalDateTime endTime) { + List documents = + rocketHistoryRepository.findByRoomIdAndLaunchTimeBetweenOrderByLaunchTimeDesc( + roomId, startTime, endTime); + return documents.stream() + .map(rocketHistoryConvertor::toDomain) + .collect(Collectors.toList()); + } + + @Override + public void updateClaimStats(String id, int claimedCount, long rewardSent) { + Query query = new Query(Criteria.where("_id").is(id)); + Update update = new Update() + .inc("claim_stats.total_claimed", 1) + .inc("claim_stats.total_reward_sent", rewardSent) + .set("updated_at", LocalDateTime.now()); + + mongoTemplate.updateFirst(query, update, RocketHistoryDocument.class); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketRewardLogGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketRewardLogGatewayImpl.java new file mode 100644 index 00000000..7d2fed0e --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketRewardLogGatewayImpl.java @@ -0,0 +1,64 @@ +package com.red.circle.other.infra.gateway; + +import com.red.circle.other.domain.gateway.RocketRewardLogGateway; +import com.red.circle.other.domain.rocket.RocketRewardLog; +import com.red.circle.other.infra.convertor.RocketRewardLogConvertor; +import com.red.circle.other.infra.database.rds.dao.RocketRewardClaimLogMapper; +import com.red.circle.other.infra.database.rds.entity.RocketRewardClaimLogEntity; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 火箭奖励领取记录网关实现 + * + * @author system + * @date 2025-01-15 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class RocketRewardLogGatewayImpl implements RocketRewardLogGateway { + + private final RocketRewardClaimLogMapper rocketRewardClaimLogMapper; + private final RocketRewardLogConvertor rocketRewardLogConvertor; + + @Override + public void save(RocketRewardLog log) { + RocketRewardClaimLogEntity entity = rocketRewardLogConvertor.toEntity(log); + rocketRewardClaimLogMapper.insert(entity); + log.setId(entity.getId()); + } + + @Override + public boolean existsByRocketAndUser(String rocketHistoryId, Long userId) { + int count = rocketRewardClaimLogMapper.countByRocketAndUser(rocketHistoryId, userId); + return count > 0; + } + + @Override + public List findByRocketHistoryId(String rocketHistoryId) { + List entities = + rocketRewardClaimLogMapper.selectByRocketHistoryId(rocketHistoryId); + return entities.stream() + .map(rocketRewardLogConvertor::toDomain) + .collect(Collectors.toList()); + } + + @Override + public List findByRoomIdAndUserId(Long roomId, Long userId) { + List entities = + rocketRewardClaimLogMapper.selectByRoomIdAndUserId(roomId, userId); + return entities.stream() + .map(rocketRewardLogConvertor::toDomain) + .collect(Collectors.toList()); + } + + @Override + public int countByRocketHistoryId(String rocketHistoryId) { + return rocketRewardClaimLogMapper.countByRocketHistoryId(rocketHistoryId); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigMapper.xml b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigMapper.xml new file mode 100644 index 00000000..b74e7ec5 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + id, level, max_energy, reward_config, contributor_reward_rate, + normal_reward_config, status, created_at, updated_at + + + + + + + + + + + + INSERT INTO rocket_config ( + level, max_energy, reward_config, contributor_reward_rate, + normal_reward_config, status, created_at, updated_at + ) VALUES ( + #{level}, #{maxEnergy}, #{rewardConfig}, #{contributorRewardRate}, + #{normalRewardConfig}, #{status}, #{createdAt}, #{updatedAt} + ) + + + + UPDATE rocket_config + + max_energy = #{maxEnergy}, + reward_config = #{rewardConfig}, + contributor_reward_rate = #{contributorRewardRate}, + normal_reward_config = #{normalRewardConfig}, + status = #{status}, + updated_at = NOW() + + WHERE id = #{id} + + + diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketRewardClaimLogMapper.xml b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketRewardClaimLogMapper.xml new file mode 100644 index 00000000..93c4db5d --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketRewardClaimLogMapper.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + id, rocket_history_id, room_id, user_id, rocket_level, is_contributor, + contribution_energy, contribution_rate, reward_type, reward_item_id, + reward_amount, wallet_biz_no, claimed_at, created_at + + + + INSERT INTO rocket_reward_claim_log ( + rocket_history_id, room_id, user_id, rocket_level, is_contributor, + contribution_energy, contribution_rate, reward_type, reward_item_id, + reward_amount, wallet_biz_no, claimed_at, created_at + ) VALUES ( + #{rocketHistoryId}, #{roomId}, #{userId}, #{rocketLevel}, #{isContributor}, + #{contributionEnergy}, #{contributionRate}, #{rewardType}, #{rewardItemId}, + #{rewardAmount}, #{walletBizNo}, #{claimedAt}, #{createdAt} + ) + + + + + + + + + + +