410 lines
15 KiB
Markdown
410 lines
15 KiB
Markdown
# 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 条/小时
|