# Reward Service 架构概览 ## 服务概述 Reward Service 是 RWA Durian Queen 平台的核心收益分配服务,负责处理用户认种榴莲树后的收益计算、分配、领取和结算。 ### 技术栈 - **框架**: NestJS 11.x - **语言**: TypeScript 5.7 - **数据库**: PostgreSQL 15 (通过 Prisma 7.x ORM) - **缓存**: Redis 7.x - **消息队列**: Apache Kafka - **测试**: Jest 30.x + Supertest 7.x - **API文档**: Swagger/OpenAPI --- ## 领域驱动设计 (DDD) 架构 本服务采用领域驱动设计架构,分为以下四层: ``` ┌─────────────────────────────────────────────────────────────┐ │ API Layer │ │ Controllers, DTOs, Guards │ ├─────────────────────────────────────────────────────────────┤ │ Application Layer │ │ Application Services, Schedulers, Use Cases │ ├─────────────────────────────────────────────────────────────┤ │ Domain Layer │ │ Aggregates, Value Objects, Domain Events, Domain Services │ ├─────────────────────────────────────────────────────────────┤ │ Infrastructure Layer │ │ Repositories, External Clients, Kafka, Redis │ └─────────────────────────────────────────────────────────────┘ ``` ### 目录结构 ``` src/ ├── api/ # API层 │ ├── controllers/ # 控制器 │ │ ├── health.controller.ts │ │ ├── reward.controller.ts │ │ └── settlement.controller.ts │ └── dto/ # 数据传输对象 │ ├── request/ │ └── response/ │ ├── application/ # 应用层 │ ├── services/ │ │ └── reward-application.service.ts │ └── schedulers/ │ └── reward-expiration.scheduler.ts │ ├── domain/ # 领域层 (核心) │ ├── aggregates/ # 聚合根 │ │ ├── reward-ledger-entry/ │ │ └── reward-summary/ │ ├── value-objects/ # 值对象 │ │ ├── money.vo.ts │ │ ├── hashpower.vo.ts │ │ ├── reward-source.vo.ts │ │ ├── reward-status.enum.ts │ │ └── right-type.enum.ts │ ├── events/ # 领域事件 │ │ ├── reward-created.event.ts │ │ ├── reward-claimed.event.ts │ │ ├── reward-expired.event.ts │ │ └── reward-settled.event.ts │ ├── services/ # 领域服务 │ │ ├── reward-calculation.service.ts │ │ └── reward-expiration.service.ts │ └── repositories/ # 仓储接口 │ ├── infrastructure/ # 基础设施层 │ ├── persistence/ │ │ ├── prisma/ │ │ ├── repositories/ # 仓储实现 │ │ └── mappers/ # 持久化映射 │ ├── external/ # 外部服务客户端 │ │ ├── referral-service/ │ │ ├── authorization-service/ │ │ └── wallet-service/ │ ├── kafka/ # Kafka消息 │ └── redis/ # Redis缓存 │ ├── shared/ # 共享模块 │ ├── guards/ │ └── strategies/ │ └── config/ # 配置 ``` --- ## 核心领域模型 ### 聚合根 #### 1. RewardLedgerEntry (奖励流水) 奖励流水是核心聚合根,记录每一笔奖励的详细信息和状态变化。 ```typescript class RewardLedgerEntry { // 身份 id: bigint userId: bigint rewardSource: RewardSource // 金额 usdtAmount: Money hashpowerAmount: Hashpower // 状态 rewardStatus: RewardStatus // PENDING | SETTLEABLE | SETTLED | EXPIRED // 时间戳 createdAt: Date expireAt: Date | null // 待领取奖励24h后过期 claimedAt: Date | null settledAt: Date | null expiredAt: Date | null // 行为 claim(): void // 领取 (PENDING → SETTLEABLE) expire(): void // 过期 (PENDING → EXPIRED) settle(): void // 结算 (SETTLEABLE → SETTLED) } ``` **状态机**: ``` ┌──────────┐ │ PENDING │ └────┬─────┘ │ ┌────────────┼────────────┐ │ 用户认种 │ │ 24h超时 ▼ │ ▼ ┌───────────┐ │ ┌─────────┐ │ SETTLEABLE│ │ │ EXPIRED │ └─────┬─────┘ │ └─────────┘ │ │ │ 用户结算 │ ▼ │ ┌──────────┐ │ │ SETTLED │ │ └──────────┘ │ ``` #### 2. RewardSummary (奖励汇总) 用户维度的奖励汇总,提供快速查询能力。 ```typescript class RewardSummary { userId: bigint // 待领取 (24h倒计时) pendingUsdt: Money pendingHashpower: Hashpower pendingExpireAt: Date | null // 可结算 settleableUsdt: Money settleableHashpower: Hashpower // 累计已结算 settledTotalUsdt: Money settledTotalHashpower: Hashpower // 累计已过期 expiredTotalUsdt: Money expiredTotalHashpower: Hashpower } ``` ### 值对象 | 值对象 | 描述 | 不变式 | |--------|------|--------| | `Money` | 货币金额 | 金额 >= 0, 货币类型不可变 | | `Hashpower` | 算力 | 值 >= 0 | | `RewardSource` | 奖励来源 | 关联订单和用户不可变 | | `RewardStatus` | 奖励状态 | 枚举值 | | `RightType` | 权益类型 | 枚举值 | --- ## 收益类型 系统支持6种收益类型,每种有不同的计算规则: | 权益类型 | USDT金额 | 算力百分比 | 接收方 | |---------|---------|-----------|--------| | 分享权益 (SHARE_RIGHT) | 500 U/棵 | 0% | 直接推荐人 | | 省区域权益 (PROVINCE_AREA_RIGHT) | 15 U/棵 | 1% | 系统省公司 | | 省团队权益 (PROVINCE_TEAM_RIGHT) | 20 U/棵 | 0% | 授权省公司 | | 市区域权益 (CITY_AREA_RIGHT) | 35 U/棵 | 2% | 系统市公司 | | 市团队权益 (CITY_TEAM_RIGHT) | 40 U/棵 | 0% | 授权市公司 | | 社区权益 (COMMUNITY_RIGHT) | 80 U/棵 | 0% | 所属社区 | **总计**: 690 USDT + 3% 算力 / 棵 --- ## 核心业务流程 ### 1. 奖励分配流程 ``` 订单支付成功事件 │ ▼ ┌─────────────────┐ │ 计算6种权益奖励 │ │ RewardCalculation│ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 保存奖励流水 │ │ RewardLedgerEntry│ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 更新用户汇总 │ │ RewardSummary │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 发布领域事件 │ │ Kafka │ └─────────────────┘ ``` ### 2. 分享权益24小时机制 ``` 推荐人已认种? │ ├── 是 → 直接可结算 (SETTLEABLE) │ └── 否 → 待领取 (PENDING, 24h倒计时) │ ├── 24h内认种 → claim() → 可结算 │ └── 24h超时 → expire() → 进总部社区 ``` ### 3. 结算流程 ``` 用户发起结算请求 (选择BNB/OG/USDT/DST) │ ▼ ┌───────────────┐ │ 获取可结算奖励 │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ 调用钱包服务 │ │ 执行SWAP │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ 更新奖励状态 │ │ SETTLED │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ 发布结算事件 │ └───────────────┘ ``` --- ## 外部服务集成 ### 防腐层设计 通过接口定义与外部服务解耦: ```typescript // 推荐关系服务 interface IReferralServiceClient { getReferralChain(userId: bigint): Promise<{ ancestors: Array<{ userId: bigint; hasPlanted: boolean }>; }>; } // 授权体系服务 interface IAuthorizationServiceClient { findNearestAuthorizedProvince(userId: bigint, provinceCode: string): Promise; findNearestAuthorizedCity(userId: bigint, cityCode: string): Promise; findNearestCommunity(userId: bigint): Promise; } // 钱包服务 interface IWalletServiceClient { executeSwap(params: { userId: bigint; usdtAmount: number; targetCurrency: string; }): Promise; } ``` ### 服务依赖 ``` ┌──────────────────┐ ┌───────────────────┐ │ Planting Service │────▶│ │ │ (认种服务) │ │ │ └──────────────────┘ │ │ │ Reward Service │ ┌──────────────────┐ │ │ │ Referral Service │────▶│ │ │ (推荐服务) │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │Authorization Svc │────▶│ │ │ (授权服务) │ │ │ └──────────────────┘ └─────────┬─────────┘ │ ▼ ┌───────────────────┐ │ Wallet Service │ │ (钱包服务) │ └───────────────────┘ ``` --- ## 消息与事件 ### Kafka Topics | Topic | 描述 | 生产者 | 消费者 | |-------|------|--------|--------| | `planting.order.paid` | 认种订单支付成功 | Planting Service | Reward Service | | `planting.user.planted` | 用户完成认种 | Planting Service | Reward Service | | `reward.created` | 奖励创建 | Reward Service | - | | `reward.claimed` | 奖励领取 | Reward Service | - | | `reward.settled` | 奖励结算 | Reward Service | Wallet Service | | `reward.expired` | 奖励过期 | Reward Service | - | ### 领域事件 ```typescript // 奖励创建事件 interface RewardCreatedEvent { entryId: string; userId: string; sourceOrderId: string; rightType: RightType; usdtAmount: number; hashpowerAmount: number; rewardStatus: RewardStatus; expireAt: Date | null; } // 奖励领取事件 interface RewardClaimedEvent { entryId: string; userId: string; usdtAmount: number; hashpowerAmount: number; } // 奖励结算事件 interface RewardSettledEvent { entryId: string; userId: string; usdtAmount: number; settleCurrency: string; receivedAmount: number; } // 奖励过期事件 interface RewardExpiredEvent { entryId: string; userId: string; usdtAmount: number; transferredTo: string; // 'HEADQUARTERS_COMMUNITY' } ``` --- ## 定时任务 ### 奖励过期检查 ```typescript @Cron('0 * * * * *') // 每分钟执行 async handleExpiredRewards() { await this.rewardApplicationService.expireOverdueRewards(); } ``` 处理逻辑: 1. 查找所有过期的待领取奖励 2. 将奖励状态改为已过期 3. 更新原用户的汇总数据 4. 将金额转入总部社区账户 5. 发布过期事件 --- ## 数据库设计 ### 核心表结构 ```sql -- 奖励流水表 CREATE TABLE reward_ledger_entries ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, source_order_id BIGINT NOT NULL, source_user_id BIGINT NOT NULL, right_type VARCHAR(50) NOT NULL, usdt_amount DECIMAL(18,2) NOT NULL, hashpower_amount DECIMAL(18,8) NOT NULL, reward_status VARCHAR(20) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), expire_at TIMESTAMP, claimed_at TIMESTAMP, settled_at TIMESTAMP, expired_at TIMESTAMP, memo TEXT, INDEX idx_user_status (user_id, reward_status), INDEX idx_expire_at (expire_at) WHERE reward_status = 'PENDING' ); -- 用户奖励汇总表 CREATE TABLE reward_summaries ( user_id BIGINT PRIMARY KEY, pending_usdt DECIMAL(18,2) NOT NULL DEFAULT 0, pending_hashpower DECIMAL(18,8) NOT NULL DEFAULT 0, pending_expire_at TIMESTAMP, settleable_usdt DECIMAL(18,2) NOT NULL DEFAULT 0, settleable_hashpower DECIMAL(18,8) NOT NULL DEFAULT 0, settled_total_usdt DECIMAL(18,2) NOT NULL DEFAULT 0, settled_total_hashpower DECIMAL(18,8) NOT NULL DEFAULT 0, expired_total_usdt DECIMAL(18,2) NOT NULL DEFAULT 0, expired_total_hashpower DECIMAL(18,8) NOT NULL DEFAULT 0, updated_at TIMESTAMP NOT NULL DEFAULT NOW() ); ``` --- ## 安全设计 ### 认证与授权 - **JWT认证**: 所有API请求需要携带有效的JWT Token - **用户隔离**: 用户只能访问自己的奖励数据 - **Passport策略**: 使用`passport-jwt`进行Token验证 ```typescript @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('JWT_SECRET'), }); } } ``` ### 数据完整性 - 使用数据库事务保证数据一致性 - 领域模型不变式检查 - 状态机约束防止非法状态转换 --- ## 性能优化 ### 缓存策略 - Redis缓存用户奖励汇总 - 热点数据预加载 ### 数据库优化 - 复合索引优化查询 - 分页查询避免全表扫描 - 批量写入减少IO ### 消息处理 - Kafka消费者组实现负载均衡 - 幂等性设计防止重复处理