docs: add planting architecture optimization documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
781721a659
commit
27bd9d9d0a
|
|
@ -0,0 +1,550 @@
|
|||
# 认种功能架构优化方案
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述认种(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*
|
||||
Loading…
Reference in New Issue