rwadurian/backend/services/reward-service/docs/ARCHITECTURE.md

512 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<bigint | null>;
findNearestAuthorizedCity(userId: bigint, cityCode: string): Promise<bigint | null>;
findNearestCommunity(userId: bigint): Promise<bigint | null>;
}
// 钱包服务
interface IWalletServiceClient {
executeSwap(params: {
userId: bigint;
usdtAmount: number;
targetCurrency: string;
}): Promise<SwapResult>;
}
```
### 服务依赖
```
┌──────────────────┐ ┌───────────────────┐
│ 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消费者组实现负载均衡
- 幂等性设计防止重复处理