rwadurian/backend/services/docs/contribution-wallet-archite...

15 KiB
Raw Blame History

2.0 贡献值计算与钱包存储方案

一、系统架构总览

┌─────────────────────────────────────────────────────────────────────────────┐
│                           1.0 系统 (数据源)                                   │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐                     │
│  │ auth-service │   │ planting-svc │   │ referral-svc │                     │
│  │  (用户注册)   │   │  (认种订单)   │   │  (推荐关系)   │                     │
│  └──────┬───────┘   └──────┬───────┘   └──────┬───────┘                     │
└─────────┼──────────────────┼──────────────────┼─────────────────────────────┘
          │                  │                  │
          │ Kafka CDC        │ Kafka CDC        │ Kafka CDC
          ▼                  ▼                  ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           2.0 系统                                           │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │                    contribution-service                                 │ │
│  │  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐        │ │
│  │  │  CDC Consumer   │  │ 贡献值计算引擎   │  │  Outbox 发布器  │        │ │
│  │  │  (同步数据)      │→ │ (分配逻辑)       │→ │  (Kafka)        │        │ │
│  │  └─────────────────┘  └─────────────────┘  └────────┬────────┘        │ │
│  └──────────────────────────────────────────────────────┼─────────────────┘ │
│                                                         │                    │
│                         Kafka: contribution.distribution.completed           │
│                                                         │                    │
│                                                         ▼                    │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │                   mining-wallet-service                                 │ │
│  │  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐        │ │
│  │  │ Kafka Consumer  │  │  钱包账户管理    │  │  分类账记录      │        │ │
│  │  │ (接收分配结果)   │→ │  (存储贡献值)    │→ │  (交易明细)      │        │ │
│  │  └─────────────────┘  └─────────────────┘  └─────────────────┘        │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │                   mining-admin-service                                  │ │
│  │  订阅 contribution-service 和 mining-wallet-service 的事件进行数据同步    │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

二、贡献值分配规则

2.1 分配比例

类型 比例 说明
个人贡献 70% 认种人自己获得
运营账户 12% 系统运营账户(永不过期)
省级公司 1% 认种所在省份公司
市级公司 2% 认种所在城市公司
团队层级 7.5% 上线1-15级每级0.5%
团队奖励 7.5% 直接上线的3档奖励每档2.5%

2.2 解锁条件

层级解锁每级0.5%共15级

档位 层级 解锁条件
第1档 L1-L5 直推≥1人认种
第2档 L6-L10 直推≥3人认种
第3档 L11-L15 直推≥5人认种

奖励解锁每档2.5%共3档

档位 解锁条件
第1档 自己认种
第2档 直推≥2人认种
第3档 直推≥4人认种

2.3 有效期规则

  • 用户贡献值2年有效期从认种次日开始计算
  • 运营账户:永不过期
  • 未分配贡献值:归总部账户(永不过期)

三、Kafka 事件设计

3.1 用户注册事件

Topic: auth.user.registered

interface UserRegisteredEvent {
  eventType: 'UserRegistered';
  eventId: string;
  timestamp: string;
  payload: {
    accountSequence: string;
    phone: string;
    referrerAccountSequence: string | null;
    registeredAt: string;
    source: 'LEGACY_MIGRATION' | 'NEW_REGISTRATION';
  };
}

消费者

  • contribution-service - 创建 ContributionAccount
  • mining-wallet-service - 创建用户钱包CONTRIBUTION 类型)

3.2 认种完成事件

Topic: planting.adoption.completed

interface AdoptionCompletedEvent {
  eventType: 'AdoptionCompleted';
  eventId: string;
  timestamp: string;
  payload: {
    adoptionId: string;
    accountSequence: string;
    treeCount: number;
    contributionPerTree: string;
    adoptionDate: string;
    provinceCode: string;
    cityCode: string;
  };
}

消费者

  • contribution-service - 触发贡献值计算

3.3 贡献值分配完成事件

Topic: contribution.distribution.completed

interface ContributionDistributionCompletedEvent {
  eventType: 'ContributionDistributionCompleted';
  eventId: string;
  timestamp: string;
  payload: {
    // 认种信息
    adoptionId: string;
    adopterAccountSequence: string;
    treeCount: number;
    adoptionDate: string;

    // 用户贡献值分配
    userContributions: {
      accountSequence: string;
      contributionType: 'PERSONAL' | 'TEAM_LEVEL' | 'TEAM_BONUS';
      amount: string;
      levelDepth?: number;      // 1-15 for TEAM_LEVEL
      bonusTier?: number;       // 1-3 for TEAM_BONUS
      effectiveDate: string;
      expireDate: string;
      sourceAdoptionId: string;
      sourceAccountSequence: string;
    }[];

    // 系统账户分配
    systemContributions: {
      accountType: 'OPERATION' | 'PROVINCE' | 'CITY' | 'HEADQUARTERS';
      amount: string;
      provinceCode?: string;
      cityCode?: string;
      neverExpires: boolean;
    }[];

    // 未分配(归总部)
    unallocatedToHeadquarters: {
      reason: string;
      amount: string;
      wouldBeAccountSequence?: string;
      levelDepth?: number;
      bonusTier?: number;
    }[];
  };
}

消费者

  • mining-wallet-service - 存储贡献值到钱包

3.4 贡献值入账事件

Topic: mining-wallet.contribution.credited

interface ContributionCreditedEvent {
  eventType: 'ContributionCredited';
  eventId: string;
  timestamp: string;
  payload: {
    accountSequence: string;
    walletType: 'CONTRIBUTION';
    amount: string;
    balanceAfter: string;
    transactionId: string;
    sourceType: 'ADOPTION_DISTRIBUTION';
    referenceId: string;
  };
}

消费者

  • mining-admin-service - 同步数据用于展示

四、Outbox 模式

4.1 Outbox 表结构

model OutboxEvent {
  id            BigInt   @id @default(autoincrement()) @map("outbox_id")

  eventType     String   @map("event_type") @db.VarChar(100)
  topic         String   @map("topic") @db.VarChar(100)
  key           String   @map("key") @db.VarChar(200)
  payload       Json     @map("payload")

  aggregateId   String   @map("aggregate_id") @db.VarChar(100)
  aggregateType String   @map("aggregate_type") @db.VarChar(50)

  status        String   @default("PENDING") @map("status") @db.VarChar(20)
  retryCount    Int      @default(0) @map("retry_count")
  maxRetries    Int      @default(10) @map("max_retries")
  lastError     String?  @map("last_error") @db.Text

  createdAt     DateTime @default(now()) @map("created_at")
  publishedAt   DateTime? @map("published_at")
  nextRetryAt   DateTime? @map("next_retry_at")

  @@index([status, createdAt])
  @@index([status, nextRetryAt])
  @@map("outbox_events")
}

4.2 退避策略最大4小时

重试次数 等待时间 累计时间
1 30秒 30秒
2 1分钟 1.5分钟
3 2分钟 3.5分钟
4 5分钟 8.5分钟
5 10分钟 18.5分钟
6 30分钟 48.5分钟
7 1小时 1小时48分
8 2小时 3小时48分
9 4小时 7小时48分
10 4小时(max) 11小时48分
const BACKOFF_INTERVALS = [
  30_000,      // 30 seconds
  60_000,      // 1 minute
  120_000,     // 2 minutes
  300_000,     // 5 minutes
  600_000,     // 10 minutes
  1_800_000,   // 30 minutes
  3_600_000,   // 1 hour
  7_200_000,   // 2 hours
  14_400_000,  // 4 hours (max)
  14_400_000,  // 4 hours (max)
];

4.3 幂等性保证4小时去重窗口

model ProcessedEvent {
  id            String   @id @default(uuid())
  eventId       String   @unique @map("event_id")
  eventType     String   @map("event_type")
  sourceService String   @map("source_service")
  processedAt   DateTime @default(now()) @map("processed_at")

  @@index([sourceService])
  @@index([processedAt])
  @@map("processed_events")
}
  • Redis 缓存4小时 TTL快速路径检查
  • DB 持久化ProcessedEvent 表24小时后清理
  • 双重检查:先查 Redis未命中再查 DB

五、服务职责划分

服务 职责 数据存储
auth-service 用户注册/迁移,发送 UserRegistered 事件 用户基础信息
planting-service 认种订单,发送 AdoptionCompleted 事件 认种订单
contribution-service 贡献值计算逻辑,解锁状态管理 计算明细、解锁事件(用于审计)
mining-wallet-service 贡献值存储,余额管理,过期处理 钱包余额、交易明细(分类账)
mining-admin-service 数据聚合展示 同步缓存

六、数据模型

6.1 contribution-service已实现

  • ContributionAccount - 用户贡献值账户(汇总)
  • ContributionRecord - 贡献值明细(审计)
  • UnlockEvent - 解锁事件记录
  • UnallocatedContribution - 未分配贡献值
  • SystemAccount - 系统账户(运营/省/市/总部)

6.2 mining-wallet-service需扩展

现有模型:

  • UserWallet - 用户钱包CONTRIBUTION 类型)
  • UserWalletTransaction - 交易明细(分类账)
  • SystemAccount - 系统账户

需要添加:

  • SystemAccount 添加 contributionBalance 字段
  • TransactionType 添加 CONTRIBUTION_CREDITCONTRIBUTION_EXPIRE 枚举值

七、实施步骤

Phase 1: mining-wallet-service 扩展

  1. 修改 schema 添加 SystemAccount 贡献值字段
  2. 实现 ContributionWalletService
  3. 实现 ContributionDistributionConsumer
  4. 实现 UserRegisteredConsumer创建用户钱包
  5. 实现 ContributionExpiryScheduler
  6. 实现 Outbox Scheduler4小时退避

Phase 2: contribution-service 扩展

  1. 实现 ContributionDistributionPublisherService
  2. 修改 ContributionCalculationService 调用发布器
  3. 测试完整的分配流程

Phase 3: auth-service 集成

  1. 确保 UserRegistered 事件正确发送
  2. 包含 referrerAccountSequence 信息

Phase 4: 历史数据迁移

  1. 批量处理1.0的历史认种数据
  2. 逐笔计算并发送分配事件
  3. 验证钱包余额正确性

八、关键流程

8.1 认种触发贡献值分配

1. planting-service 发送 AdoptionCompleted 事件
   ↓
2. contribution-service 消费事件
   ├─ 查询认种人的上线链条最多15级
   ├─ 查询各上线的解锁状态
   ├─ 计算贡献值分配
   └─ 写入 Outbox 表
   ↓
3. Outbox Scheduler 发送到 Kafka
   ↓
4. mining-wallet-service 消费 ContributionDistributionCompleted
   ├─ 幂等性检查
   ├─ 更新用户钱包余额
   ├─ 更新系统账户余额
   └─ 记录交易明细
   ↓
5. mining-admin-service 消费 ContributionCredited
   └─ 同步缓存数据

8.2 贡献值过期处理

1. 每日凌晨1点定时任务启动
   ↓
2. 查询 expireDate <= today 的交易记录
   ↓
3. 逐笔处理过期
   ├─ 扣减钱包余额
   ├─ 创建过期交易记录
   └─ 标记原交易为已过期

九、监控与告警

9.1 关键指标

  • Outbox 积压数量
  • 事件处理延迟
  • 重试次数分布
  • 失败事件数量

9.2 告警规则

  • Outbox 积压 > 1000 条
  • 事件处理延迟 > 5 分钟
  • 失败事件数量 > 10 条/小时