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

410 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.

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