16 KiB
16 KiB
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 (奖励流水)
奖励流水是核心聚合根,记录每一笔奖励的详细信息和状态变化。
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 (奖励汇总)
用户维度的奖励汇总,提供快速查询能力。
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 │
└───────┬───────┘
│
▼
┌───────────────┐
│ 发布结算事件 │
└───────────────┘
外部服务集成
防腐层设计
通过接口定义与外部服务解耦:
// 推荐关系服务
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 | - |
领域事件
// 奖励创建事件
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'
}
定时任务
奖励过期检查
@Cron('0 * * * * *') // 每分钟执行
async handleExpiredRewards() {
await this.rewardApplicationService.expireOverdueRewards();
}
处理逻辑:
- 查找所有过期的待领取奖励
- 将奖励状态改为已过期
- 更新原用户的汇总数据
- 将金额转入总部社区账户
- 发布过期事件
数据库设计
核心表结构
-- 奖励流水表
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验证
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('JWT_SECRET'),
});
}
}
数据完整性
- 使用数据库事务保证数据一致性
- 领域模型不变式检查
- 状态机约束防止非法状态转换
性能优化
缓存策略
- Redis缓存用户奖励汇总
- 热点数据预加载
数据库优化
- 复合索引优化查询
- 分页查询避免全表扫描
- 批量写入减少IO
消息处理
- Kafka消费者组实现负载均衡
- 幂等性设计防止重复处理