# 认种功能架构优化方案

## 概述

本文档描述认种（Planting）功能的架构优化方案，基于现有微服务架构进行扩展，实现 2199 USDT 的完整分配流程。

**核心原则：**
- 所有账户地址通过 MPC 按需生成
- 所有资金操作都是区块链转账（从认种人钱包转出）
- 不创建新服务，只扩展现有服务

---

## 一、2199 USDT 分配明细

每棵树认种费用 2199 USDT，分配如下：

| 序号 | 账户类型 | 金额(USDT) | 说明 |
|------|----------|------------|------|
| 1 | 成本账户 | 400 | 固定成本 |
| 2 | 运营账户 | 300 | 运营费用 |
| 3 | 总部社区账户 | 9 | 总部社区收益 |
| 4 | RWAD矿池 | 800 | 注入矿池（30天后开始挖矿） |
| 5 | 直接推荐人 | 500 | 仅直推，无多级 |
| 6 | 省公司区域权益 | 15 | 正式省公司（含1%算力） |
| 7 | 省公司团队权益 | 20 | 授权省公司 |
| 8 | 市公司区域权益 | 35 | 正式市公司（含2%算力） |
| 9 | 市公司团队权益 | 40 | 授权市公司 |
| 10 | 社区权益 | 80 | 社区授权 |
| **合计** | | **2199** | |

---

## 二、系统账户设计

### 2.1 系统账户类型

```typescript
enum SystemAccountType {
  COST_ACCOUNT = 'COST_ACCOUNT',           // 成本账户
  OPERATION_ACCOUNT = 'OPERATION_ACCOUNT', // 运营账户
  HQ_COMMUNITY = 'HQ_COMMUNITY',           // 总部社区账户
  RWAD_POOL_PENDING = 'RWAD_POOL_PENDING', // RWAD矿池待注入
  SYSTEM_PROVINCE = 'SYSTEM_PROVINCE',     // 系统省账户（无授权时）
  SYSTEM_CITY = 'SYSTEM_CITY',             // 系统市账户（无授权时）
}
```

### 2.2 数据库表设计（扩展 authorization-service）

```prisma
// ============================================
// 系统账户表
// 管理成本、运营、总部社区、矿池等系统级账户
// ============================================
model SystemAccount {
  id              BigInt   @id @default(autoincrement()) @map("account_id")

  // 账户类型
  accountType     String   @map("account_type") @db.VarChar(30)

  // 区域信息（仅 SYSTEM_PROVINCE/SYSTEM_CITY 需要）
  regionCode      String?  @map("region_code") @db.VarChar(10)
  regionName      String?  @map("region_name") @db.VarChar(50)

  // MPC 生成的钱包地址（按需生成）
  walletAddress   String?  @map("wallet_address") @db.VarChar(42)
  mpcPublicKey    String?  @map("mpc_public_key") @db.VarChar(130)

  // 余额
  usdtBalance     Decimal  @default(0) @map("usdt_balance") @db.Decimal(20, 8)
  hashpower       Decimal  @default(0) @map("hashpower") @db.Decimal(20, 8)

  // 累计统计
  totalReceived     Decimal @default(0) @map("total_received") @db.Decimal(20, 8)
  totalTransferred  Decimal @default(0) @map("total_transferred") @db.Decimal(20, 8)

  // 状态
  status          String   @default("ACTIVE") @map("status") @db.VarChar(20)

  createdAt       DateTime @default(now()) @map("created_at")
  updatedAt       DateTime @updatedAt @map("updated_at")

  ledgerEntries   SystemAccountLedger[]

  @@unique([accountType, regionCode], name: "uk_account_region")
  @@index([accountType], name: "idx_account_type")
  @@index([walletAddress], name: "idx_wallet_address")
  @@map("system_accounts")
}

// ============================================
// 系统账户流水表
// 记录系统账户的所有资金变动
// ============================================
model SystemAccountLedger {
  id              BigInt   @id @default(autoincrement()) @map("ledger_id")
  accountId       BigInt   @map("account_id")

  // 流水类型
  entryType       String   @map("entry_type") @db.VarChar(50)

  // 金额
  amount          Decimal  @map("amount") @db.Decimal(20, 8)
  balanceAfter    Decimal  @map("balance_after") @db.Decimal(20, 8)

  // 关联信息
  sourceOrderId   BigInt?  @map("source_order_id")   // 来源认种订单
  txHash          String?  @map("tx_hash") @db.VarChar(66)  // 链上交易哈希

  memo            String?  @map("memo") @db.VarChar(500)

  createdAt       DateTime @default(now()) @map("created_at")

  account         SystemAccount @relation(fields: [accountId], references: [id])

  @@index([accountId, createdAt(sort: Desc)], name: "idx_account_created")
  @@index([sourceOrderId], name: "idx_source_order")
  @@index([txHash], name: "idx_tx_hash")
  @@map("system_account_ledgers")
}
```

---

## 三、MPC 按需生成地址流程

### 3.1 触发条件

当系统账户需要接收资金但尚未生成钱包地址时：

```typescript
async function ensureWalletAddress(systemAccount: SystemAccount): Promise<string> {
  if (systemAccount.walletAddress) {
    return systemAccount.walletAddress;
  }

  // 调用 MPC 服务生成地址
  const { publicKey, address } = await mpcService.generateAddress({
    accountType: 'SYSTEM',
    accountId: systemAccount.id.toString(),
  });

  // 更新系统账户
  await prisma.systemAccount.update({
    where: { id: systemAccount.id },
    data: {
      walletAddress: address,
      mpcPublicKey: publicKey,
    },
  });

  // 注册到 blockchain-service 进行监听
  await blockchainService.registerMonitoredAddress({
    address,
    accountType: 'SYSTEM',
    accountId: systemAccount.id,
  });

  return address;
}
```

### 3.2 用户账户按需生成

用户钱包地址在以下场景按需生成：
- 首次认种时（需要从钱包扣款）
- 首次充值时（需要收款地址）
- 查询收款地址时

---

## 四、认种完整流程

### 4.1 流程图

```
用户发起认种请求
    ↓
验证用户钱包余额 ≥ 2199 USDT × 数量
    ↓
创建认种订单（PENDING）
    ↓
计算分配方案（查询省市授权状态）
    ↓
执行链上转账（多笔转账）
    ├── 400 → 成本账户
    ├── 300 → 运营账户
    ├── 9 → 总部社区账户
    ├── 800 → RWAD矿池账户
    ├── 500 → 直接推荐人（作为待领取奖励）
    ├── 15+1%算力 → 省区域权益接收方
    ├── 20 → 省团队权益接收方
    ├── 35+2%算力 → 市区域权益接收方
    ├── 40 → 市团队权益接收方
    └── 80 → 社区权益接收方
    ↓
更新订单状态（COMPLETED）
    ↓
更新团队统计（referral-service）
    ↓
更新授权考核进度（authorization-service）
    ↓
30天后启动挖矿（矿池注入）
```

### 4.2 分配目标确定逻辑

```typescript
interface AllocationTarget {
  targetType: string;
  targetUserId?: bigint;      // 用户账户
  targetSystemAccountId?: bigint;  // 系统账户
  amount: Decimal;
  hashpowerPercent?: Decimal;
}

async function calculateAllocation(
  planterId: bigint,
  treeCount: number,
  provinceCode: string,
  cityCode: string,
): Promise<AllocationTarget[]> {
  const unitPrice = new Decimal(2199);
  const totalAmount = unitPrice.mul(treeCount);

  const allocations: AllocationTarget[] = [];

  // 1. 固定系统账户分配
  allocations.push(
    { targetType: 'COST_ACCOUNT', targetSystemAccountId: costAccountId, amount: new Decimal(400).mul(treeCount) },
    { targetType: 'OPERATION_ACCOUNT', targetSystemAccountId: operationAccountId, amount: new Decimal(300).mul(treeCount) },
    { targetType: 'HQ_COMMUNITY', targetSystemAccountId: hqCommunityAccountId, amount: new Decimal(9).mul(treeCount) },
    { targetType: 'RWAD_POOL', targetSystemAccountId: rwadPoolAccountId, amount: new Decimal(800).mul(treeCount) },
  );

  // 2. 直接推荐人（500 USDT）
  const referrer = await referralService.getDirectReferrer(planterId);
  if (referrer) {
    allocations.push({
      targetType: 'DIRECT_REFERRER',
      targetUserId: referrer.userId,
      amount: new Decimal(500).mul(treeCount),
    });
  } else {
    // 无推荐人，归入运营账户
    allocations.push({
      targetType: 'OPERATION_ACCOUNT',
      targetSystemAccountId: operationAccountId,
      amount: new Decimal(500).mul(treeCount),
    });
  }

  // 3. 省公司权益（15 区域 + 20 团队）
  const provinceAuth = await authorizationService.getActiveProvinceAuth(provinceCode);
  if (provinceAuth?.roleType === 'PROVINCE_COMPANY' && provinceAuth.benefitActive) {
    // 正式省公司 - 区域权益 15U + 1% 算力
    allocations.push({
      targetType: 'PROVINCE_REGION',
      targetUserId: provinceAuth.userId,
      amount: new Decimal(15).mul(treeCount),
      hashpowerPercent: new Decimal(1),
    });
  } else {
    // 无正式省公司，归入系统省账户
    const systemProvince = await getOrCreateSystemAccount('SYSTEM_PROVINCE', provinceCode);
    allocations.push({
      targetType: 'SYSTEM_PROVINCE',
      targetSystemAccountId: systemProvince.id,
      amount: new Decimal(15).mul(treeCount),
    });
  }

  // 授权省公司 - 团队权益 20U
  if (provinceAuth?.roleType === 'AUTH_PROVINCE_COMPANY' && provinceAuth.benefitActive) {
    allocations.push({
      targetType: 'PROVINCE_TEAM',
      targetUserId: provinceAuth.userId,
      amount: new Decimal(20).mul(treeCount),
    });
  } else {
    const systemProvince = await getOrCreateSystemAccount('SYSTEM_PROVINCE', provinceCode);
    allocations.push({
      targetType: 'SYSTEM_PROVINCE',
      targetSystemAccountId: systemProvince.id,
      amount: new Decimal(20).mul(treeCount),
    });
  }

  // 4. 市公司权益（35 区域 + 40 团队）- 类似省公司逻辑
  // ...

  // 5. 社区权益（80 USDT）
  const communityAuth = await authorizationService.getCommunityAuth(planterId);
  if (communityAuth && communityAuth.benefitActive) {
    allocations.push({
      targetType: 'COMMUNITY',
      targetUserId: communityAuth.userId,
      amount: new Decimal(80).mul(treeCount),
    });
  } else {
    allocations.push({
      targetType: 'OPERATION_ACCOUNT',
      targetSystemAccountId: operationAccountId,
      amount: new Decimal(80).mul(treeCount),
    });
  }

  return allocations;
}
```

---

## 五、24小时待领取奖励机制

### 5.1 奖励状态流转

```
PENDING（待领取）
    ↓ 用户认种
CLAIMABLE（可结算）
    ↓ 用户结算
SETTLED（已结算）

PENDING（待领取）
    ↓ 24小时未认种
EXPIRED（已过期）
```

### 5.2 reward-service 现有表结构

```prisma
model RewardLedgerEntry {
  // 奖励状态: PENDING → CLAIMABLE → SETTLED 或 PENDING → EXPIRED
  rewardStatus    String   @default("PENDING")

  expireAt        DateTime?  // 24小时后过期时间
  claimedAt       DateTime?  // 用户认种时间（领取）
  settledAt       DateTime?  // 结算时间
  expiredAt       DateTime?  // 实际过期时间
}
```

### 5.3 定时任务

```typescript
// 每分钟检查过期奖励
@Cron('* * * * *')
async checkExpiredRewards() {
  const expiredRewards = await prisma.rewardLedgerEntry.findMany({
    where: {
      rewardStatus: 'PENDING',
      expireAt: { lt: new Date() },
    },
  });

  for (const reward of expiredRewards) {
    await prisma.$transaction([
      // 更新奖励状态
      prisma.rewardLedgerEntry.update({
        where: { id: reward.id },
        data: {
          rewardStatus: 'EXPIRED',
          expiredAt: new Date(),
        },
      }),
      // 更新用户汇总
      prisma.rewardSummary.update({
        where: { userId: reward.userId },
        data: {
          pendingUsdt: { decrement: reward.usdtAmount },
          expiredTotalUsdt: { increment: reward.usdtAmount },
        },
      }),
    ]);

    // 将过期金额转入运营账户
    await transferToOperationAccount(reward.usdtAmount, reward.id);
  }
}
```

---

## 六、授权考核规则

### 6.1 省市公司授权考核

| 角色 | 初始目标 | 月度考核 | 权益 |
|------|----------|----------|------|
| 授权省公司 | 500棵 | 阶梯递增 | 团队权益 20U |
| 正式省公司 | 已达标 | 无 | 区域权益 15U + 1%算力 |
| 授权市公司 | 200棵 | 阶梯递增 | 团队权益 40U |
| 正式市公司 | 已达标 | 无 | 区域权益 35U + 2%算力 |

### 6.2 阶梯考核目标（authorization-service 已有）

```prisma
model LadderTargetConfig {
  roleType         RoleType
  monthIndex       Int      // 第几个月
  monthlyTarget    Int      // 当月目标
  cumulativeTarget Int      // 累计目标
}
```

### 6.3 社区考核

- 认种 1 棵后获得社区授权
- 每月保持认种 10 棵维持权益
- 连续 2 个月不达标，降级为普通用户

---

## 七、龙虎榜规则

### 7.1 排名计算（referral-service 已有）

```prisma
model TeamStatistics {
  // 龙虎榜分值 = 团队总认种量 - 最大单个直推团队认种量
  maxSingleTeamPlantingCount        Int @default(0)
  effectivePlantingCountForRanking  Int @default(0)
}
```

### 7.2 计算公式

```typescript
function calculateLeaderboardScore(userId: bigint): number {
  const stats = await getTeamStatistics(userId);

  // 有效分值 = 团队总认种 - 最大单线团队认种
  return stats.totalTeamPlantingCount - stats.maxSingleTeamPlantingCount;
}
```

---

## 八、服务职责划分

| 服务 | 职责 |
|------|------|
| **planting-service** | 认种订单管理、分配计算、矿池注入批次 |
| **wallet-service** | 用户钱包余额、链上转账执行 |
| **blockchain-service** | 地址监听、交易广播、MPC 交互 |
| **reward-service** | 奖励流水、24h过期处理、结算 |
| **referral-service** | 推荐关系、团队统计、龙虎榜 |
| **authorization-service** | 省市社区授权、考核进度、**系统账户管理** |
| **identity-service** | 用户身份、MPC 钱包创建 |

---

## 九、待实现功能清单

### 9.1 authorization-service 扩展

- [ ] 添加 `SystemAccount` 表
- [ ] 添加 `SystemAccountLedger` 表
- [ ] 实现系统账户 CRUD API
- [ ] 实现按需生成 MPC 地址逻辑

### 9.2 planting-service 扩展

- [ ] 实现完整分配计算逻辑
- [ ] 集成 wallet-service 执行链上转账
- [ ] 集成 authorization-service 查询授权状态
- [ ] 集成 referral-service 查询推荐人

### 9.3 reward-service 扩展

- [ ] 实现 24 小时过期定时任务
- [ ] 实现用户认种触发奖励领取
- [ ] 实现过期奖励转入运营账户

### 9.4 wallet-service 扩展

- [ ] 实现批量链上转账 API
- [ ] 支持系统账户转账

### 9.5 blockchain-service 扩展

- [ ] 支持系统账户地址监听
- [ ] 实现 MPC 签名批量交易

---

## 十、数据流示意

```
┌─────────────────────────────────────────────────────────────────────────┐
│                           认种数据流                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  用户钱包 (wallet-service)                                                │
│      │                                                                   │
│      │ 2199 USDT × N                                                    │
│      ▼                                                                   │
│  planting-service                                                        │
│      │                                                                   │
│      ├─── 400 ──→ 成本账户 (authorization-service.SystemAccount)          │
│      ├─── 300 ──→ 运营账户 (authorization-service.SystemAccount)          │
│      ├───   9 ──→ 总部社区 (authorization-service.SystemAccount)          │
│      ├─── 800 ──→ RWAD矿池 (authorization-service.SystemAccount)          │
│      ├─── 500 ──→ 直推人钱包 (wallet-service) → reward-service (待领取)    │
│      ├───  15 ──→ 省区域 (用户钱包 或 系统省账户)                           │
│      ├───  20 ──→ 省团队 (用户钱包 或 系统省账户)                           │
│      ├───  35 ──→ 市区域 (用户钱包 或 系统市账户)                           │
│      ├───  40 ──→ 市团队 (用户钱包 或 系统市账户)                           │
│      └───  80 ──→ 社区 (用户钱包 或 运营账户)                              │
│                                                                          │
│  所有转账通过 blockchain-service 执行链上交易                               │
│  所有地址通过 MPC 按需生成                                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

---

## 附录：关键配置

### A. 系统账户初始化

```sql
-- 初始化固定系统账户（地址按需生成）
INSERT INTO system_accounts (account_type, region_code, status) VALUES
  ('COST_ACCOUNT', NULL, 'ACTIVE'),
  ('OPERATION_ACCOUNT', NULL, 'ACTIVE'),
  ('HQ_COMMUNITY', NULL, 'ACTIVE'),
  ('RWAD_POOL_PENDING', NULL, 'ACTIVE');
```

### B. 环境变量

```env
# MPC 服务配置
MPC_SERVICE_URL=http://mpc-service:3010

# 认种相关
PLANTING_UNIT_PRICE=2199
REWARD_EXPIRE_HOURS=24
MINING_START_DELAY_DAYS=30
```

---

*文档版本: v1.0*
*创建日期: 2025-12-08*
*最后更新: 2025-12-08*
