# 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`

```typescript
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`

```typescript
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`

```typescript
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`

```typescript
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 表结构

```prisma
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分 |

```typescript
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小时去重窗口）

```prisma
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_CREDIT`、`CONTRIBUTION_EXPIRE` 枚举值

---

## 七、实施步骤

### Phase 1: mining-wallet-service 扩展
1. 修改 schema 添加 SystemAccount 贡献值字段
2. 实现 ContributionWalletService
3. 实现 ContributionDistributionConsumer
4. 实现 UserRegisteredConsumer（创建用户钱包）
5. 实现 ContributionExpiryScheduler
6. 实现 Outbox Scheduler（4小时退避）

### 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 条/小时
