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

16 KiB
Raw Blame History

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();
}

处理逻辑:

  1. 查找所有过期的待领取奖励
  2. 将奖励状态改为已过期
  3. 更新原用户的汇总数据
  4. 将金额转入总部社区账户
  5. 发布过期事件

数据库设计

核心表结构

-- 奖励流水表
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消费者组实现负载均衡
  • 幂等性设计防止重复处理