gcx/docs/guides/05-后端开发指南.md

2200 lines
72 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.

# Genex 后端开发指南
> DDD + Clean Architecture + 微服务 | NestJS + Go + Kong + PostgreSQL
---
## 1. 技术栈总览
| 层级 | 技术 | 用途 |
|------|------|------|
| **API网关** | Kong | 限流、认证、路由、负载均衡 |
| **主要业务服务** | NestJS (TypeScript) | 业务逻辑微服务 |
| **高性能服务** | Go | 撮合引擎、链上事件监听、高频任务 |
| **翻译层** | Go + Redis | 地址映射、Gas代付、术语翻译 |
| **数据库** | PostgreSQL 15+ | 核心业务数据 |
| **时序数据库** | TimescaleDB | 行情数据、监控指标 |
| **缓存** | Redis Cluster | 热点数据、会话、订单簿缓存 |
| **消息队列** | Kafka | 事件驱动、链上事件监听 |
| **搜索** | Elasticsearch | 全文检索、日志分析 |
| **AI/ML** | FastAPI (Python) | 信用评分、价格预测、异常检测 |
---
## 2. 架构设计:三层五域
### 2.1 五大业务域
| 域 | 服务名 | 语言 | 职责 |
|----|--------|------|------|
| D1 发行域 | `issuer-service` | NestJS | 发行方入驻、券管理、发行审批 |
| D2 交易域 | `trading-service` | Go | 撮合引擎、订单簿、做市商 |
| D3 清算域 | `clearing-service` | NestJS + Go | 链上结算、兑付清算、退款 |
| D4 合规域 | `compliance-service` | NestJS | KYC/AML、OFAC、Travel Rule |
| D5 AI域 | `ai-service` | FastAPI | 信用评分、定价、异常检测 |
| 通用 | `user-service` | NestJS | 用户注册、认证、Profile |
| 通用 | `translate-service` | Go | UX翻译层地址映射、Gas代付 |
| 通用 | `notification-service` | NestJS | 消息推送、邮件、短信 |
| 通用 | `chain-indexer` | Go | 链上事件监听、数据索引 |
### 2.2 服务间通信
```
┌──────────────────────────────────────────────┐
│ Kong API Gateway │
│ 限流 | OAuth 2.1 + JWT | 路由 | 负载均衡 │
├──────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
│ │ user │ │ issuer │ │ trading │ │
│ │ service │ │ service │ │ service(Go)│ │
│ └────┬────┘ └────┬─────┘ └─────┬──────┘ │
│ │ │ │ │
│ └────────────┼──────────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Kafka │ │
│ └─────┬─────┘ │
│ ┌────────────┼───────────────┐ │
│ ┌────┴────┐ ┌─────┴──────┐ ┌─────┴─────┐ │
│ │clearing │ │compliance │ │ai-service │ │
│ │service │ │service │ │(FastAPI) │ │
│ └─────────┘ └────────────┘ └───────────┘ │
└──────────────────────────────────────────────┘
```
- **同步通信**gRPC服务间直接调用如撮合→结算
- **异步通信**Kafka事件驱动链上事件、通知、审计日志
---
## 3. DDD + Clean ArchitectureNestJS服务
### 3.1 模块结构以issuer-service为例
```
issuer-service/
├── src/
│ ├── main.ts
│ ├── app.module.ts
│ ├── domain/ # 领域层(纯业务逻辑,零外部依赖)
│ │ ├── entities/
│ │ │ ├── issuer.entity.ts # 发行方聚合根
│ │ │ ├── coupon-template.entity.ts
│ │ │ └── credit-rating.vo.ts # 值对象
│ │ ├── repositories/
│ │ │ └── issuer.repository.ts # Repository接口
│ │ ├── services/
│ │ │ ├── issuer-domain.service.ts
│ │ │ └── credit-scoring.service.ts
│ │ └── events/
│ │ ├── issuer-approved.event.ts
│ │ └── coupon-issued.event.ts
│ ├── application/ # 应用层(用例编排)
│ │ ├── commands/
│ │ │ ├── register-issuer.command.ts
│ │ │ ├── create-coupon.command.ts
│ │ │ └── handlers/
│ │ ├── queries/
│ │ │ ├── get-issuer.query.ts
│ │ │ └── handlers/
│ │ └── dto/
│ │ ├── create-issuer.dto.ts
│ │ └── create-coupon.dto.ts
│ ├── infrastructure/ # 基础设施层(外部实现)
│ │ ├── persistence/
│ │ │ ├── issuer.repository.impl.ts
│ │ │ ├── issuer.orm-entity.ts
│ │ │ └── typeorm.config.ts
│ │ ├── messaging/
│ │ │ └── kafka-publisher.ts
│ │ └── external/
│ │ └── chain-client.ts # 链上交互
│ └── interfaces/ # 接口层HTTP/gRPC
│ ├── http/
│ │ ├── issuer.controller.ts
│ │ └── coupon.controller.ts
│ └── grpc/
│ └── issuer.grpc-controller.ts
├── test/
└── package.json
```
### 3.2 领域实体
```typescript
// src/domain/entities/issuer.entity.ts
export class Issuer {
readonly id: string;
readonly companyName: string;
private _creditRating: CreditRating;
private _issuanceQuota: number;
private _tier: IssuerTier;
private _status: IssuerStatus;
// 信用评分四因子
updateCreditScore(metrics: CreditMetrics): void {
const score =
0.35 * metrics.redemptionRate +
0.25 * (1 - metrics.breakageRatio) +
0.20 * Math.log(metrics.marketTenure + 1) / Math.log(37) +
0.20 * metrics.userSatisfaction;
this._creditRating = CreditRating.fromScore(score);
this._issuanceQuota = this._creditRating.calculateQuota();
}
// 发券校验
canIssueCoupon(params: CreateCouponParams): Result<void> {
if (this._status !== IssuerStatus.ACTIVE)
return Result.fail('发行方状态异常');
if (params.totalValue > this.remainingQuota)
return Result.fail('超出发行额度');
if (params.expiryDays > 365)
return Result.fail('Utility Track有效期不得超过12个月');
return Result.ok();
}
}
```
### 3.3 应用层Command
```typescript
// src/application/commands/handlers/create-coupon.handler.ts
@CommandHandler(CreateCouponCommand)
export class CreateCouponHandler {
constructor(
private issuerRepo: IssuerRepository,
private eventBus: EventBus,
) {}
async execute(command: CreateCouponCommand): Promise<CouponDraft> {
const issuer = await this.issuerRepo.findById(command.issuerId);
if (!issuer) throw new NotFoundException('发行方不存在');
// 领域校验
const validation = issuer.canIssueCoupon(command.params);
if (validation.isFailure) throw new BadRequestException(validation.error);
// 创建券草稿(待审核)
const draft = CouponDraft.create(issuer, command.params);
await this.issuerRepo.saveCouponDraft(draft);
// 发布领域事件
this.eventBus.publish(new CouponDraftCreatedEvent(draft));
return draft;
}
}
```
---
## 4. Go 高性能服务
### 4.1 撮合引擎
```go
// trading-service/internal/matching/engine.go
type MatchingEngine struct {
orderBooks map[string]*OrderBook // couponId → OrderBook
mu sync.RWMutex
}
type OrderBook struct {
Bids *redblacktree.Tree // 买单:价格降序
Asks *redblacktree.Tree // 卖单:价格升序
}
func (e *MatchingEngine) SubmitOrder(order *Order) []*Trade {
e.mu.Lock()
defer e.mu.Unlock()
book := e.getOrCreateBook(order.CouponID)
// Utility Track价格校验
if order.CouponType == CouponTypeUtility && order.Price > order.FaceValue {
return nil // 拒绝溢价单
}
// 撮合:价格优先 → 时间优先
trades := book.Match(order)
// 成交后发布事件到Kafka
for _, trade := range trades {
e.publisher.Publish("trade.matched", trade)
}
return trades
}
```
### 4.2 链上事件监听
```go
// chain-indexer/internal/indexer/indexer.go
type ChainIndexer struct {
ethClient *ethclient.Client
kafka sarama.SyncProducer
contracts map[string]common.Address
}
func (i *ChainIndexer) Start(ctx context.Context) {
// 监听Genex Chain事件
query := ethereum.FilterQuery{
Addresses: []common.Address{
i.contracts["CouponFactory"],
i.contracts["Settlement"],
i.contracts["Redemption"],
},
}
logs := make(chan types.Log)
sub, _ := i.ethClient.SubscribeFilterLogs(ctx, query, logs)
for {
select {
case log := <-logs:
event := i.parseEvent(log)
i.kafka.SendMessage(&sarama.ProducerMessage{
Topic: "chain.events",
Value: sarama.ByteEncoder(event.Marshal()),
})
case err := <-sub.Err():
log.Error("subscription error", "err", err)
// 重连逻辑
case <-ctx.Done():
return
}
}
}
```
---
## 5. UX翻译层
> 核心创新将所有Web3操作翻译为Web2用户可理解的操作。
```go
// translate-service/internal/service/translate.go
type TranslateService struct {
mappingDB *redis.Client // 手机号→链上地址映射
mpcClient MPCClient // MPC钱包服务
paymaster PaymasterClient // Gas代付服务
}
// 手机号→链上地址
func (s *TranslateService) ResolveAddress(phone string) (common.Address, error) {
addr, err := s.mappingDB.Get(ctx, "phone:"+phone).Result()
if err == redis.Nil {
return common.Address{}, ErrUserNotFound
}
return common.HexToAddress(addr), nil
}
// 购买券:法币→链上原子交换
func (s *TranslateService) ExecutePurchase(req PurchaseRequest) (*PurchaseResult, error) {
// 1. 法币支付确认
// 2. 法币→稳定币转换
// 3. 调用Settlement合约原子交换Gas由Paymaster代付
// 4. 返回"订单号"映射TX Hash
}
```
---
## 6. API设计
### 6.1 RESTful规范
```yaml
API规范:
风格: RESTful
版本控制: URL路径 /api/v1/
认证: OAuth 2.1 + JWT
限流:
普通用户: 100 req/min
发行方: 1,000 req/min
做市商: 10,000 req/min
响应格式: JSON
错误处理: RFC 7807 Problem Details
```
### 6.2 核心API端点
| 域 | 路径前缀 | 主要接口 |
|-----|---------|---------|
| 用户 | `/api/v1/users` | 注册、登录、KYC、资产查询 |
| 券 | `/api/v1/coupons` | 发行、查询、转让、核销 |
| 交易 | `/api/v1/trades` | 挂单、撤单、成交、历史 |
| 支付 | `/api/v1/payments` | 充值、提现、法币转换 |
| 发行方 | `/api/v1/issuers` | 入驻、审核、数据统计 |
| 合规 | `/api/v1/compliance` | KYC、AML、审计日志 |
| 翻译层 | `/api/v1/translate` | 地址映射、Gas代付 |
---
## 7. 数据库设计
### 7.1 PostgreSQL核心表
```sql
-- 用户表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
phone VARCHAR(20) UNIQUE,
email VARCHAR(100) UNIQUE,
kyc_level SMALLINT NOT NULL DEFAULT 0,
wallet_mode VARCHAR(10) NOT NULL DEFAULT 'standard',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 发行方表
CREATE TABLE issuers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_name VARCHAR(200) NOT NULL,
business_license VARCHAR(100),
credit_rating VARCHAR(5) NOT NULL DEFAULT 'BBB',
credit_score NUMERIC(5,2) NOT NULL DEFAULT 60.00,
issuance_quota NUMERIC(15,2) NOT NULL DEFAULT 100000,
tier VARCHAR(10) NOT NULL DEFAULT 'silver',
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 券表(链下缓存,权威数据在链上)
CREATE TABLE coupons (
id UUID PRIMARY KEY,
chain_token_id BIGINT UNIQUE, -- 链上Token ID
issuer_id UUID REFERENCES issuers(id),
face_value NUMERIC(12,2) NOT NULL,
current_price NUMERIC(12,2),
expiry_date DATE NOT NULL,
coupon_type VARCHAR(10) NOT NULL DEFAULT 'utility',
status VARCHAR(20) NOT NULL DEFAULT 'available',
resale_count SMALLINT NOT NULL DEFAULT 0,
max_resale_count SMALLINT NOT NULL DEFAULT 3,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 地址映射表(翻译层核心)
CREATE TABLE address_mappings (
user_id UUID REFERENCES users(id),
chain_address VARCHAR(42) NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (user_id)
);
```
---
## 8. 安全规范
| 领域 | 规范 |
|------|------|
| 传输 | TLS 1.3 + 服务间mTLS |
| 认证 | OAuth 2.1 + JWT短有效期 + Refresh Token |
| 授权 | RBAC |
| 数据加密 | AES-256敏感字段HSM管理密钥 |
| API安全 | Rate Limiting + CORS + CSRF防护 |
| SQL注入 | ORM参数化查询TypeORM / sqlx |
| 审计 | 全链路操作日志,不可篡改 |
---
## 9. 部署架构
```yaml
# Kubernetes部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: issuer-service
spec:
replicas: 3
template:
spec:
containers:
- name: issuer-service
image: genex/issuer-service:latest
resources:
requests: { cpu: "250m", memory: "512Mi" }
limits: { cpu: "1000m", memory: "1Gi" }
env:
- name: DB_HOST
valueFrom:
configMapKeyRef: { name: db-config, key: host }
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: db-secrets, key: password }
```
### 多区域部署
| 区域 | 部署 | 角色 |
|------|------|------|
| AWS美国 | 全量服务 + 主数据库 + Genex Chain主验证节点 |
| AWS新加坡 | 亚太服务 + 灾备数据库 + 区域验证节点 |
| 香港 | 监管节点 + 审计接口 |
---
## 10. 监控与可观测性
| 组件 | 工具 | 用途 |
|------|------|------|
| 指标 | Prometheus + Grafana | 服务指标、业务指标 |
| 日志 | ELK Stack | 日志收集分析 |
| 链路追踪 | Jaeger | 分布式调用追踪 |
| 告警 | AlertManager + PagerDuty | 异常告警 |
---
## 11. 手续费与收入计算
### 11.1 交易手续费(核心收入)
```typescript
// src/domain/services/fee-calculation.service.ts
export class FeeCalculationService {
// Maker-Taker模型
private readonly FEE_RATES = {
taker: { buy: 0.005, sell: 0.005 }, // Taker 0.5%
maker: { buy: 0.001, sell: 0.001 }, // Maker 0.1%(做市商激励)
};
// 发行方分层手续费率
private readonly ISSUER_FEE_RATES: Record<IssuerTier, number> = {
silver: 0.015, // 白银 1.5%
gold: 0.012, // 黄金 1.2%
platinum: 0.010, // 铂金 1.0%
diamond: 0.008, // 钻石 0.8%
};
calculateTradeFee(trade: Trade): TradeFees {
const isMaker = (side: 'buyer' | 'seller') =>
trade[side].isMaker;
return {
buyerFee: trade.price * (isMaker('buyer') ? this.FEE_RATES.maker.buy : this.FEE_RATES.taker.buy),
sellerFee: trade.price * (isMaker('seller') ? this.FEE_RATES.maker.sell : this.FEE_RATES.taker.sell),
};
}
calculateIssuanceFee(issuer: Issuer, totalValue: number): number {
// 新入驻首月享黄金层级
const tier = issuer.isFirstMonth ? 'gold' : issuer.tier;
return totalValue * this.ISSUER_FEE_RATES[tier];
}
}
```
### 11.2 Breakage收益计算与分配
```typescript
// src/domain/services/breakage.service.ts
export class BreakageService {
/**
* 券过期后Breakage计算
* 未使用的券面值 → 发行方Breakage收入
* 平台按比例分润(发行服务协议约定)
*/
async processExpiredCoupons(): Promise<void> {
const expired = await this.couponRepo.findExpiredUnprocessed();
for (const coupon of expired) {
const breakageAmount = coupon.faceValue;
const platformShare = breakageAmount * 0.10; // 平台分润10%(可配置)
const issuerShare = breakageAmount - platformShare;
await this.financeService.recordBreakage({
couponId: coupon.id,
issuerId: coupon.issuerId,
totalAmount: breakageAmount,
platformShare,
issuerShare,
expiredAt: coupon.expiryDate,
});
// 更新券状态
await this.couponRepo.markBreakageProcessed(coupon.id);
// 更新发行方信用评分Breakage率因子
this.eventBus.publish(new CouponExpiredEvent(coupon));
}
}
}
// 定时任务:每日批量处理过期券
@Cron('0 2 * * *') // 每天凌晨2点
async handleExpiredCoupons() {
await this.breakageService.processExpiredCoupons();
}
```
### 11.3 退款机制
```typescript
// src/domain/services/refund.service.ts
interface RefundPolicy {
primaryMarket: {
windowDays: number; // 发行方可配退款窗口默认7天
fullRefund: boolean; // 一级市场全额退(含手续费)
};
secondaryMarket: {
requireArbitration: true; // 二级市场需仲裁
feeRefund: false; // 手续费不退
};
}
export class RefundService {
async processRefund(request: RefundRequest): Promise<RefundResult> {
// 已核销/已过期券不可退
if (['redeemed', 'expired'].includes(request.coupon.status)) {
throw new BadRequestException('已核销或已过期券不可退款');
}
// 一级市场退款:发行方退款窗口内
if (request.type === 'primary') {
const withinWindow = this.isWithinRefundWindow(request);
if (!withinWindow) throw new BadRequestException('超出退款期限');
// 调用Settlement合约反向原子交换
return this.chainClient.executeRefund(request);
}
// 二级市场退款:需仲裁裁决
if (request.type === 'secondary') {
return this.disputeService.createRefundCase(request);
}
}
}
```
---
## 12. 做市商系统
### 12.1 做市商准入与管理
```typescript
// src/domain/entities/market-maker.entity.ts
export class MarketMaker {
id: string;
userId: string;
kycLevel: KycLevel; // 必须KYC L3
depositAmount: number; // 最低保证金
status: 'active' | 'suspended' | 'terminated';
obligations: MarketMakerObligations;
}
interface MarketMakerObligations {
minSpreadBps: number; // 最大价差(基点)
minDepthPerSide: number; // 单边最小挂单深度
uptimePercent: number; // 最低在线时间 (95%)
maxResponseMs: number; // 报价响应时间上限
}
```
### 12.2 做市商专用API
```typescript
// 低延迟专用接口10,000 req/min
@Controller('api/v1/mm')
export class MarketMakerController {
@Post('batch-orders')
async batchOrders(@Body() dto: BatchOrderDto) {
// 批量挂单/撤单单次最多100单
}
@Get('orderbook/:couponId')
async getOrderBook(@Param('couponId') id: string) {
// 实时订单簿快照
}
@Ws('mm/stream')
async streamMarketData() {
// WebSocket低延迟行情推送
}
}
```
### 12.3 Spoofing/Layering检测
```typescript
// 做市商行为监控
export class MarketManipulationDetector {
/**
* Spoofing检测大量挂单后短时间内撤单
* Layering检测多层挂单制造虚假深度
*/
detect(orders: OrderActivity[]): ManipulationAlert[] {
const alerts: ManipulationAlert[] = [];
// 挂单→撤单比率异常(>80%撤单率)
const cancelRate = orders.filter(o => o.cancelled).length / orders.length;
if (cancelRate > 0.8) {
alerts.push({ type: 'spoofing', severity: 'high', cancelRate });
}
// 短时间多层挂单检测
const layeringPattern = this.detectLayeringPattern(orders);
if (layeringPattern) alerts.push(layeringPattern);
return alerts;
}
}
```
---
## 13. 三因子定价引擎
```typescript
// src/domain/services/pricing.service.ts
export class PricingService {
/**
* P = F × (1 - dt - rc - lp)
* dt = Time Discount = f(剩余有效期)
* rc = Credit Risk = f(发行方信用评分)
* lp = Liquidity Premium = f(市场供需)
*/
calculateSuggestedPrice(coupon: Coupon, issuer: Issuer, market: MarketData): PriceSuggestion {
const F = coupon.faceValue;
const totalDays = differenceInDays(coupon.expiryDate, coupon.issuedDate);
const remainDays = differenceInDays(coupon.expiryDate, new Date());
// 时间折扣 (0% - 15%)
const dt = ((totalDays - remainDays) / totalDays) * 0.15;
// 信用风险溢价 (0% - 20%)
const CREDIT_MAP: Record<CreditRating, number> = {
AAA: 0, AA: 0.03, A: 0.06, BBB: 0.10, BB: 0.15,
};
const rc = CREDIT_MAP[issuer.creditRating] || 0.20;
// 流动性溢价 (-5% - 10%)
const supplyDemandRatio = (market.sellOrders - market.buyOrders) / (market.totalOrders || 1);
const lp = Math.max(-0.05, Math.min(0.10, supplyDemandRatio * 0.1));
const price = F * (1 - dt - rc - lp);
// Utility Track: 价格上限 = 面值
const cappedPrice = coupon.type === 'utility' ? Math.min(price, F) : price;
return { suggestedPrice: cappedPrice, factors: { dt, rc, lp } };
}
}
```
---
## 14. AI/ML 模型服务
### 14.1 信用评分引擎
```python
# ai-service/models/credit_scoring.py
import lightgbm as lgb
from sklearn.model_selection import cross_val_score
class CreditScoringModel:
"""
发行方信用评分:四因子基础 + ML增强
特征核销率、Breakage率、市场存续月数、用户满意度、历史违约、行业
"""
def __init__(self):
self.model = lgb.LGBMRegressor(
n_estimators=500, learning_rate=0.05,
max_depth=6, num_leaves=31,
)
def predict_credit_score(self, issuer_features: dict) -> float:
# 四因子基础分
base = (0.35 * issuer_features['redemption_rate'] +
0.25 * (1 - issuer_features['breakage_ratio']) +
0.20 * math.log(issuer_features['tenure_months'] + 1) / math.log(37) +
0.20 * issuer_features['user_satisfaction'])
# ML调整基于更多特征
ml_adjustment = self.model.predict([self._extract_features(issuer_features)])[0]
return min(100, max(0, base * 100 * 0.7 + ml_adjustment * 0.3))
```
### 14.2 价格预测模型
```python
# ai-service/models/price_prediction.py
from prophet import Prophet
import torch.nn as nn
class LSTMPricePredictor(nn.Module):
"""LSTM序列预测历史价格→未来价格走势"""
def __init__(self, input_size=5, hidden_size=64, num_layers=2):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, 1)
def forward(self, x):
out, _ = self.lstm(x)
return self.fc(out[:, -1, :])
class ProphetSeasonalPredictor:
"""Prophet周期性预测节假日/季节性价格模式"""
def predict(self, history: pd.DataFrame, periods: int = 30):
model = Prophet(yearly_seasonality=True, weekly_seasonality=True)
model.fit(history)
future = model.make_future_dataframe(periods=periods)
return model.predict(future)
```
### 14.3 异常检测系统
```python
# ai-service/models/anomaly_detection.py
from sklearn.ensemble import IsolationForest
class TransactionAnomalyDetector:
"""
异常交易检测Isolation Forest + 规则引擎
实时消费Kafka trade.events标记可疑交易
"""
def __init__(self):
self.isolation_forest = IsolationForest(
contamination=0.01, n_estimators=200, random_state=42
)
def detect(self, transaction: dict) -> AnomalyResult:
features = self._extract_features(transaction)
# ML检测
score = self.isolation_forest.decision_function([features])[0]
# 规则引擎检测
rule_alerts = self._apply_rules(transaction)
return AnomalyResult(
ml_score=score,
is_anomaly=score < -0.5 or len(rule_alerts) > 0,
alerts=rule_alerts,
)
def _apply_rules(self, tx: dict) -> list:
alerts = []
if tx['amount'] > 10000: alerts.append('large_amount')
if tx['frequency_1h'] > 20: alerts.append('high_frequency')
if tx['geolocation_change']: alerts.append('geo_anomaly')
return alerts
```
### 14.4 智能推荐引擎P2优先级
```python
# 协同过滤 + 内容推荐 混合推荐
class CouponRecommender:
def recommend(self, user_id: str, top_k: int = 10):
# 协同过滤:相似用户购买历史
cf_scores = self.collaborative_filter(user_id)
# 内容推荐:基于券属性(品牌、类别、折扣率)
content_scores = self.content_based(user_id)
# 混合加权
return self.blend(cf_scores, content_scores, top_k)
```
---
## 15. AML反洗钱系统
### 15.1 AML检测规则引擎
```typescript
// compliance-service/src/domain/services/aml-detection.service.ts
export class AmlDetectionService {
// 已识别洗钱路径检测
private readonly RULES: AmlRule[] = [
// 1. 买券洗钱脏钱买券→P2P转给另一账户→卖出提现
{ name: 'buy_transfer_withdraw', detect: this.detectBuyTransferWithdraw },
// 2. 分散洗钱一个账户→P2P分散转给大量小账户
{ name: 'fan_out', detect: this.detectFanOut },
// 3. 发行方自洗:关联账户自买自卖
{ name: 'self_dealing', detect: this.detectSelfDealing },
// 4. 跨境洗钱A国买→P2P转B国→B国提现
{ name: 'cross_border', detect: this.detectCrossBorder },
// 5. Structuring拆分交易规避阈值
{ name: 'structuring', detect: this.detectStructuring },
];
async detectFanOut(userId: string, window: TimeWindow): Promise<Alert | null> {
const transfers = await this.getP2PTransfers(userId, window);
const uniqueRecipients = new Set(transfers.map(t => t.recipientId));
// 24h内向>10个不同账户P2P转移
if (uniqueRecipients.size > 10) {
return { type: 'fan_out', severity: 'high', details: { recipients: uniqueRecipients.size } };
}
return null;
}
async detectStructuring(userId: string, window: TimeWindow): Promise<Alert | null> {
const txs = await this.getTransactions(userId, window);
// 检测是否有多笔接近$3,000阈值的交易$2,500-$2,999
const nearThreshold = txs.filter(t => t.amount >= 2500 && t.amount < 3000);
if (nearThreshold.length >= 3) {
return { type: 'structuring', severity: 'critical', details: { count: nearThreshold.length } };
}
return null;
}
}
```
### 15.2 交易图谱分析
```typescript
// 构建用户间交易关系图谱
export class TransactionGraphService {
async buildGraph(timeWindow: TimeWindow): Promise<TransactionGraph> {
const edges = await this.tradeRepo.getTransferEdges(timeWindow);
const graph = new Graph();
for (const edge of edges) {
graph.addEdge(edge.sender, edge.receiver, {
amount: edge.amount, timestamp: edge.timestamp,
});
}
// 检测环形转移
const cycles = graph.detectCycles();
// 检测扇入/扇出模式
const fanPatterns = graph.detectFanPatterns(threshold: 10);
// 关联账户聚类
const clusters = graph.communityDetection();
return { graph, cycles, fanPatterns, clusters };
}
}
```
### 15.3 SAR自动生成
```typescript
// 可疑交易报告Suspicious Activity Report
export class SarService {
async generateSar(alert: AmlAlert): Promise<SarReport> {
return {
filingType: 'initial',
subjectInfo: await this.getUserInfo(alert.userId),
suspiciousActivity: {
dateRange: alert.timeWindow,
amount: alert.totalAmount,
instruments: 'Digital Coupon Assets',
description: this.generateNarrative(alert),
},
// FinCEN BSA E-Filing格式
};
}
}
```
---
## 16. OFAC合规服务
```typescript
// compliance-service/src/infrastructure/external/ofac.service.ts
export class OfacService {
private sdnList: Map<string, SdnEntry> = new Map();
// 名单同步24h内更新
@Cron('0 */6 * * *') // 每6小时同步
async syncSdnList(): Promise<void> {
// 主选Chainalysis备选Elliptic/TRM Labs
const entries = await this.chainalysis.getLatestSdnList();
this.sdnList = new Map(entries.map(e => [e.id, e]));
this.logger.log(`OFAC SDN list synced: ${entries.length} entries`);
}
// 注册时筛查
async screenOnRegistration(user: UserRegistrationDto): Promise<OfacScreenResult> {
return this.screenPerson({
name: user.fullName,
nationality: user.nationality,
address: user.address,
dateOfBirth: user.dob,
});
}
// 每笔交易实时筛查
async screenTransaction(buyer: string, seller: string): Promise<OfacScreenResult> {
const buyerResult = await this.screenAddress(buyer);
const sellerResult = await this.screenAddress(seller);
if (buyerResult.isMatch || sellerResult.isMatch) {
// 命中:立即冻结 + 上报
await this.freezeAndReport(buyerResult, sellerResult);
}
return { buyerResult, sellerResult };
}
// P2P转移链上地址筛查
async screenChainAddress(address: string): Promise<boolean> {
return this.chainalysis.checkAddress(address);
}
}
```
---
## 17. Travel Rule合规TRISA/TRP
```typescript
// compliance-service/src/domain/services/travel-rule.service.ts
export class TravelRuleService {
/**
* FATF Travel Rule: ≥$3,000转移需传递身份信息
* 接入TRISA协议实现跨平台信息传递
*/
async processP2PTransfer(transfer: P2PTransferRequest): Promise<void> {
if (transfer.amount >= 3000) {
// 验证双方KYC≥L2
await this.verifyKycLevel(transfer.senderId, KycLevel.L2);
await this.verifyKycLevel(transfer.receiverId, KycLevel.L2);
// 身份信息哈希写入链上
const senderHash = this.hashIdentity(await this.getKycInfo(transfer.senderId));
const receiverHash = this.hashIdentity(await this.getKycInfo(transfer.receiverId));
await this.chainClient.recordTravelRule(
transfer.senderAddress, transfer.receiverAddress,
senderHash, receiverHash,
);
// TRISA跨平台传递如接收方在其他平台
if (transfer.isExternalReceiver) {
await this.trisaClient.sendTravelRuleInfo({
originator: await this.getOriginatorInfo(transfer.senderId),
beneficiary: await this.getBeneficiaryInfo(transfer.receiverId),
});
}
}
}
}
```
---
## 18. 税务合规
```typescript
// compliance-service/src/domain/services/tax.service.ts
export class TaxComplianceService {
// IRS Form 1099-DA / 1099-B 生成
async generate1099(userId: string, taxYear: number): Promise<Tax1099Report> {
const trades = await this.tradeRepo.getUserTrades(userId, taxYear);
const proceeds = trades.reduce((sum, t) => sum + t.sellPrice, 0);
const costBasis = trades.reduce((sum, t) => sum + t.buyPrice, 0);
return {
form: proceeds > 600 ? '1099-DA' : null, // >$600 threshold
recipientTIN: await this.getUserTIN(userId),
grossProceeds: proceeds,
costBasis,
gainOrLoss: proceeds - costBasis,
transactions: trades.map(t => this.formatTaxTransaction(t)),
};
}
// FATCA跨境税务外国账户持有人
async checkFatcaObligation(userId: string): Promise<boolean> {
const user = await this.userRepo.findById(userId);
return user.nationality !== 'US' && user.hasUsSourceIncome;
}
// 发行方Breakage收入税务处理
async generateIssuerTaxReport(issuerId: string, taxYear: number): Promise<IssuerTaxReport> {
const breakageIncome = await this.financeRepo.getBreakageIncome(issuerId, taxYear);
const salesRevenue = await this.financeRepo.getSalesRevenue(issuerId, taxYear);
return { breakageIncome, salesRevenue, totalTaxableIncome: breakageIncome + salesRevenue };
}
}
```
---
## 19. 数据隐私合规CCPA/GDPR
```typescript
// user-service/src/domain/services/privacy.service.ts
export class PrivacyService {
/**
* 用户数据删除流程:
* 链上仅地址和哈希不可删除但不构成PII
* 链下PII全部可删除
*
* 例外AML/BSA法规要求交易记录保留≥5年
*/
async processDeleteRequest(userId: string): Promise<DeleteResult> {
// 检查数据保留义务
const retentionCheck = await this.checkRetentionObligation(userId);
if (retentionCheck.hasActiveObligation) {
return { status: 'deferred', reason: 'AML/BSA 5年保留期未满', deferUntil: retentionCheck.expiresAt };
}
// 1. 删除链下所有PII
await this.userRepo.deletePersonalData(userId); // 姓名、手机号、邮箱、身份证
await this.kycRepo.deleteKycMaterials(userId); // KYC资料人脸、证件照
// 2. 删除映射表记录
await this.mappingRepo.deleteMapping(userId); // 手机号→地址映射
// 3. 链上地址变为匿名(不可逆删除映射后地址无法关联到人)
// 4. Travel Rule链下明文删除链上仅哈希不可逆
return { status: 'completed', deletedAt: new Date() };
}
}
```
---
## 20. 数据留存与审计
```typescript
// 数据保留策略
const DATA_RETENTION_POLICIES = {
transactionRecords: { minYears: 5, source: 'BSA/AML' },
kycDocuments: { minYears: 5, source: 'BSA/CDD' },
sarReports: { minYears: 5, source: 'FinCEN' },
auditLogs: { minYears: 7, source: 'SOX' },
chainData: { minYears: Infinity, source: 'blockchain' },
};
// 审计日志append-only不可篡改
export class AuditLogService {
async log(entry: AuditEntry): Promise<void> {
await this.appendOnlyStore.write({
timestamp: new Date(),
actor: entry.actorId,
action: entry.action,
resource: entry.resource,
details: entry.details,
ipAddress: entry.ip,
hash: this.computeChainHash(entry), // 链式哈希防篡改
});
}
}
```
---
## 21. 券生命周期管理
```typescript
// 券状态流转
enum CouponStatus {
MINTED = 'minted', // 已铸造(链上)
LISTED = 'listed', // 已上架
SOLD = 'sold', // 已售出
IN_CIRCULATION = 'in_circulation', // 流通中(二级市场)
REDEEMED = 'redeemed', // 已兑付
EXPIRED = 'expired', // 已过期
RECALLED = 'recalled', // 已召回
}
// 过期自动处理
@Cron('0 0 * * *') // 每日凌晨
async processExpiredCoupons(): Promise<void> {
const expiring = await this.couponRepo.findExpiringToday();
for (const coupon of expiring) {
await this.couponRepo.updateStatus(coupon.id, CouponStatus.EXPIRED);
// Breakage收益计算
await this.breakageService.calculateAndDistribute(coupon);
// 通知持有人
await this.notificationService.send(coupon.holderId, {
type: 'coupon_expired',
couponName: coupon.name,
});
}
}
```
---
## 22. 争议与纠纷处理
```typescript
// 争议处理流程
interface DisputeCase {
id: string;
type: 'buyer_complaint' | 'seller_complaint' | 'refund_request';
status: 'submitted' | 'evidence_collection' | 'arbitration' | 'resolved' | 'escalated';
buyerId: string;
sellerId: string;
orderId: string;
chainEvidence: ChainEvidence[]; // 链上不可篡改证据
slaDeadline: Date; // 24h响应72h处理
}
export class DisputeService {
async createDispute(dto: CreateDisputeDto): Promise<DisputeCase> {
const chainEvidence = await this.chainClient.getTransactionProof(dto.orderId);
const dispute = DisputeCase.create({ ...dto, chainEvidence });
// SLA24h内响应
await this.slaService.scheduleReminder(dispute.id, '24h');
return dispute;
}
async arbitrate(disputeId: string, decision: ArbitrationDecision): Promise<void> {
if (decision.refundApproved) {
await this.refundService.processRefund(decision.refundDetails);
}
}
}
```
---
## 23. 客服系统
```typescript
// 工单系统
interface Ticket {
id: string;
userId: string;
category: 'transaction' | 'account' | 'coupon' | 'compliance' | 'other';
priority: 'low' | 'medium' | 'high' | 'urgent';
status: 'open' | 'in_progress' | 'waiting_user' | 'resolved' | 'closed';
sla: { responseTime: '24h', resolutionTime: '72h' };
}
// 发行方专属客服通道(铂金/钻石层级)
export class IssuerSupportService {
getDedicatedChannel(issuer: Issuer): SupportChannel {
if (['platinum', 'diamond'].includes(issuer.tier)) {
return { type: 'dedicated', responseTime: '1h', manager: this.getAccountManager(issuer.id) };
}
return { type: 'standard', responseTime: '24h' };
}
}
```
---
## 24. 安全事件响应计划
### 24.1 事件分级
| 级别 | 定义 | 响应时限 | 示例 |
|------|------|---------|------|
| **P0** | 资产被盗/合约漏洞被利用 | 15分钟内启动 | MPC密钥泄露、合约攻击 |
| **P1** | 数据泄露/系统被入侵 | 1小时内响应 | 用户数据泄露、映射表篡改 |
| **P2** | 局部服务异常/可疑活动 | 4小时内响应 | API攻击、异常登录 |
| **P3** | 潜在风险/安全隐患 | 24小时内评估 | 依赖库漏洞 |
### 24.2 P0应急流程
```typescript
// 自动化应急响应
export class IncidentResponseService {
async handleP0Incident(incident: SecurityIncident): Promise<void> {
// 1. 自动触发紧急冻结Governance合约
await this.chainClient.triggerEmergencyFreeze(incident.affectedAddresses);
// 2. 通知安全团队PagerDuty
await this.pagerDuty.trigger({
severity: 'critical',
description: incident.description,
});
// 3. 保全证据
await this.forensics.preserveEvidence({
logs: await this.collectLogs(incident.timeRange),
chainState: await this.chainClient.getStateSnapshot(),
systemSnapshot: await this.infrastructure.getSnapshot(),
});
// 4. 用户通知≤24hCCPA要求72h内通知受影响用户
await this.notificationService.scheduleBreachNotification(incident);
}
}
```
### 24.3 Bug Bounty
| 严重程度 | 奖励范围 | 示例 |
|---------|---------|------|
| Critical | $50K-$100K | 合约资金盗取、MPC密钥泄露 |
| High | $10K-$50K | 权限绕过、数据泄露 |
| Medium | $2K-$10K | 信息泄露、DoS |
| Low | $500-$2K | 低影响漏洞 |
---
## 25. 灾难恢复与业务连续性
### 25.1 DR指标
| 指标 | 目标 |
|------|------|
| RPO恢复点目标 | < 1分钟 |
| RTO恢复时间目标 | < 15分钟 |
### 25.2 备份与故障转移
```yaml
# 数据库备份策略
postgresql:
replication: streaming_replication # 实时同步至备用区域
backup:
full: weekly # 每周全量备份
incremental: every_15_minutes # 每15分钟增量备份
retention: 90_days # 备份保留90天
failover:
automatic: true # 自动故障转移
health_check_interval: 5s
# 链下数据快照
orderbook:
snapshot: every_5_minutes # 订单簿快照
wal: continuous # 预写日志持续记录
# 链上数据:区块链本身分布式存储,天然灾备
genex_chain:
validators: 3 # ≥3自有验证节点不同地理位置
third_party_rpc: [alchemy, quicknode] # 备用RPC节点
```
### 25.3 业务连续性
| 故障场景 | 应对策略 |
|---------|---------|
| 交易系统故障 | 自动切换备用撮合引擎未完成订单状态保护 |
| 链节点故障 | 多节点冗余(≥3个自有 + 第三方RPC备用 |
| 法币通道故障 | 2家支付服务商自动热备切换 |
| MPC密钥服务故障 | 密钥分片跨地理位置存储Fireblocks/Fordefi热备 |
| 年度DR演练 | 每年1次全量灾难恢复演练并记录 |
---
## 26. 映射表安全方案
> 手机号→地址映射表是最高价值安全资产,被篡改将导致资产错误转移。
```typescript
// translate-service/src/security/mapping-security.service.ts
export class MappingSecurityService {
/**
* 映射记录创建/修改需多方签名:
* 平台服务器 + HSM + 第三方审计节点
*/
async createMapping(userId: string, chainAddress: string): Promise<void> {
// 1. MPC多方签名
const signature = await this.mpcSigner.multiSign({
parties: ['platform_server', 'hsm_module', 'audit_node'],
threshold: 2, // 至少2方签名
data: { userId, chainAddress },
});
// 2. 写入加密数据库AES-256HSM管理密钥
await this.encryptedStore.write(userId, chainAddress, signature);
// 3. 记录到append-only审计日志
await this.auditLog.append({
action: 'mapping_created',
userId, chainAddress, signature,
});
}
// 定期链上锚定Merkle Root
@Cron('0 * * * *') // 每小时
async anchorToChain(): Promise<void> {
const merkleRoot = await this.computeMappingMerkleRoot();
await this.chainClient.anchorMerkleRoot(merkleRoot);
}
// 完整性校验
async verifyIntegrity(): Promise<boolean> {
const currentRoot = await this.computeMappingMerkleRoot();
const chainRoot = await this.chainClient.getLatestMerkleRoot();
return currentRoot === chainRoot;
}
}
```
---
## 27. 发行方违约处理
```typescript
// issuer-service/src/domain/services/default-handling.service.ts
export class IssuerDefaultService {
async handleDefault(issuerId: string, severity: 'minor' | 'major' | 'critical'): Promise<void> {
switch (severity) {
case 'minor':
// 降级 + 额度缩减
await this.issuerRepo.downgradeCreditRating(issuerId, 1);
break;
case 'major':
// 冻结发行 + 启用保障资金
await this.issuerRepo.freezeIssuance(issuerId);
if (await this.hasGuaranteeFund(issuerId)) {
await this.activateGuaranteeFund(issuerId);
}
break;
case 'critical':
// 跑路:冻结账户 + 链上标记风险券 + 通知持有人
await this.issuerRepo.freezeAccount(issuerId);
await this.chainClient.markRiskCoupons(issuerId);
await this.notifyAllHolders(issuerId, '发行方异常,请关注券状态');
break;
}
}
}
```
---
## 28. 多币种与法币通道
```typescript
// 多稳定币支持
interface StablecoinConfig {
primary: 'USDC';
secondary: 'USDT';
oracle: 'chainlink'; // 汇率预言机
}
// 法币通道热备
export class FiatGatewayService {
private providers: FiatProvider[] = [
{ name: 'primary_bank', priority: 1, status: 'active' },
{ name: 'backup_processor', priority: 2, status: 'standby' },
];
async processPayment(request: PaymentRequest): Promise<PaymentResult> {
for (const provider of this.providers.sort((a, b) => a.priority - b.priority)) {
try {
if (provider.status === 'active' || provider.status === 'standby') {
return await provider.process(request);
}
} catch (error) {
this.logger.error(`Fiat provider ${provider.name} failed, trying next`);
provider.status = 'failed';
continue; // 自动切换
}
}
throw new ServiceUnavailableException('All fiat providers unavailable');
}
}
// 汇率管理
export class ExchangeRateService {
async getRate(from: string, to: string): Promise<ExchangeRate> {
// Oracle获取实时汇率
const rate = await this.oracleClient.getRate(from, to);
return { rate: rate.value, lockedUntil: addMinutes(new Date(), 15) }; // 锁定15分钟
}
}
```
---
## 29. 链上对账
```typescript
// 链上数据 vs 链下账本对账
export class ReconciliationService {
// 实时对账(链上数据即账本)
async reconcile(): Promise<ReconciliationResult> {
const chainBalances = await this.chainClient.getAllBalances();
const dbBalances = await this.financeRepo.getAllBalances();
const discrepancies: Discrepancy[] = [];
for (const [userId, chainBal] of chainBalances) {
const dbBal = dbBalances.get(userId);
if (Math.abs(chainBal - dbBal) > 0.01) {
discrepancies.push({ userId, chainBalance: chainBal, dbBalance: dbBal });
}
}
if (discrepancies.length > 0) {
await this.alertService.sendAlert('reconciliation_mismatch', discrepancies);
}
return { totalChecked: chainBalances.size, discrepancies };
}
}
```
---
## 30. 容量规划
| 阶段 | 用户规模 | 日交易量 | 基础设施 |
|------|---------|---------|---------|
| Phase 1 (基础平台) | 10万 | 50万笔 | 单区域K8s3节点 |
| Phase 2 (商业化) | 100万 | 500万笔 | 双区域K8s + 热备5+节点 |
| Phase 3 (金融化) | 1,000万 | 5,000万笔 | 多区域集群 + GCFN节点 |
### 关键性能指标
| 指标 | 目标 |
|------|------|
| API响应时间P99 | < 200ms |
| 撮合引擎延迟 | < 10ms |
| 翻译层额外耗时 | < 50ms |
| 系统可用性 | > 99.9% SLA |
---
## 31. SDK开发计划
| SDK | 语言/平台 | 功能范围 | 优先级 |
|-----|----------|---------|--------|
| genex-js | JavaScript/Node.js | 全功能(发行、交易、核销、数据查询) | P0 |
| genex-java | Java/Android | 全功能 + Android原生集成 | P0 |
| genex-python | Python | 数据分析、信用查询、批量操作 | P1 |
| genex-swift | Swift/iOS | 移动端核销、用户端功能 | P1 |
| genex-go | Go | 高性能服务端集成、做市商接入 | P2 |
### 开发者门户
- 沙箱环境Testnet API + 测试券 + 测试USDC
- API文档OpenAPI 3.0 + Swagger UI
- SDK快速入门指南
- Webhook事件订阅
---
## 32. 会计处理ASC 606 / GAAP
### 32.1 收入确认框架ASC 606五步法
```typescript
// clearing-service/src/domain/services/accounting.service.ts
/**
* ASC 606 五步法收入确认:
* 1. 识别合同 → 券发行/交易成交
* 2. 识别履约义务 → 券兑付义务(发行方)/ 平台服务义务
* 3. 确定交易价格 → 发行价/成交价
* 4. 分配交易价格 → 各履约义务
* 5. 确认收入 → 履约义务满足时
*/
export class AccountingService {
/**
* 券发行时:发行方收到资金 → 记入递延负债Deferred Revenue
* 券核销时:履约义务满足 → 递延负债转为已确认收入
* 券过期时Breakage收入确认ASC 606-10-55-48~51
*/
async recordIssuance(event: CouponIssuedEvent): Promise<JournalEntry[]> {
const entries: JournalEntry[] = [];
// 发行方视角:收到现金,形成递延负债
entries.push({
date: event.issuedAt,
debit: { account: 'cash', amount: event.totalSalesAmount },
credit: { account: 'deferred_revenue', amount: event.totalSalesAmount },
memo: `券发行 Batch#${event.batchId},发行方${event.issuerId}`,
reference: event.txHash,
});
// 平台视角:发行服务费收入(立即确认,平台履约义务已完成)
const issuanceFee = event.totalSalesAmount * event.feeRate;
entries.push({
date: event.issuedAt,
debit: { account: 'accounts_receivable_issuer', amount: issuanceFee },
credit: { account: 'revenue_issuance_fee', amount: issuanceFee },
memo: `发行服务费 ${event.feeRate * 100}%`,
reference: event.txHash,
});
await this.journalRepo.batchSave(entries);
return entries;
}
async recordRedemption(event: CouponRedeemedEvent): Promise<JournalEntry[]> {
const entries: JournalEntry[] = [];
// 发行方视角:券核销 → 递延负债转为已确认收入
entries.push({
date: event.redeemedAt,
debit: { account: 'deferred_revenue', amount: event.faceValue },
credit: { account: 'revenue_earned', amount: event.faceValue },
memo: `券核销 Token#${event.tokenId}`,
reference: event.txHash,
});
await this.journalRepo.batchSave(entries);
return entries;
}
async recordTrade(event: TradeSettledEvent): Promise<JournalEntry[]> {
const entries: JournalEntry[] = [];
// 平台视角:交易手续费收入(成交即确认)
entries.push({
date: event.settledAt,
debit: { account: 'cash_stablecoin', amount: event.buyerFee + event.sellerFee },
credit: { account: 'revenue_trading_fee', amount: event.buyerFee + event.sellerFee },
memo: `交易手续费 Order#${event.orderId}`,
reference: event.txHash,
});
await this.journalRepo.batchSave(entries);
return entries;
}
}
```
### 32.2 Breakage收入确认ASC 606-10-55-48~51
```typescript
// clearing-service/src/domain/services/breakage-accounting.service.ts
export class BreakageAccountingService {
/**
* ASC 606 Breakage收入确认规则
* - 如果发行方预期部分券不会被兑付Breakage且Breakage金额可合理估计
* → 在券生命周期内按比例确认Breakage收入proportional method
* - 如果Breakage不可合理估计
* → 在券到期/权利失效时一次性确认remote method
*
* 平台策略初期采用remote method到期时确认积累足够历史数据后切换proportional method
*/
private readonly RECOGNITION_METHOD: 'proportional' | 'remote' = 'remote';
async processBreakageAccounting(coupon: ExpiredCoupon): Promise<JournalEntry[]> {
const entries: JournalEntry[] = [];
if (this.RECOGNITION_METHOD === 'remote') {
// Remote method: 到期时一次性确认
// 发行方侧:递延负债 → Breakage收入
entries.push({
date: coupon.expiryDate,
debit: { account: 'deferred_revenue', amount: coupon.faceValue },
credit: { account: 'revenue_breakage', amount: coupon.faceValue },
memo: `Breakage收入确认remote methodToken#${coupon.tokenId}`,
});
// 平台侧Breakage分润按协议比例
const platformShare = coupon.faceValue * this.PLATFORM_BREAKAGE_SHARE;
entries.push({
date: coupon.expiryDate,
debit: { account: 'accounts_receivable_breakage', amount: platformShare },
credit: { account: 'revenue_breakage_share', amount: platformShare },
memo: `平台Breakage分润 ${this.PLATFORM_BREAKAGE_SHARE * 100}%`,
});
}
await this.journalRepo.batchSave(entries);
return entries;
}
/**
* 递延负债余额报表(监管/审计用)
* 展示平台上所有未兑付券的递延负债总额
*/
async getDeferredRevenueReport(asOfDate: Date): Promise<DeferredRevenueReport> {
const outstandingCoupons = await this.couponRepo.findOutstanding(asOfDate);
const totalDeferred = outstandingCoupons.reduce((sum, c) => sum + c.faceValue, 0);
const byIssuer = this.groupByIssuer(outstandingCoupons);
return {
asOfDate,
totalDeferredRevenue: totalDeferred,
byIssuer,
byMaturity: this.groupByMaturityBucket(outstandingCoupons),
recognitionMethod: this.RECOGNITION_METHOD,
};
}
}
// 会计科目表Chart of Accounts
const CHART_OF_ACCOUNTS = {
// 资产类
cash: '1001 现金及等价物',
cash_stablecoin: '1002 稳定币资产USDC/USDT',
accounts_receivable_issuer: '1101 应收账款-发行方',
accounts_receivable_breakage: '1102 应收账款-Breakage分润',
// 负债类
deferred_revenue: '2001 递延收入(券未兑付负债)',
user_deposits: '2002 用户托管资金',
guarantee_funds_held: '2003 发行方保障资金(代管)',
// 收入类
revenue_trading_fee: '4001 交易手续费收入',
revenue_issuance_fee: '4002 发行服务费收入',
revenue_breakage_share: '4003 Breakage分润收入',
revenue_vas: '4004 增值服务收入',
revenue_earned: '4005 已确认收入(发行方侧)',
revenue_breakage: '4006 Breakage收入发行方侧',
};
```
### 32.3 链上数据与GAAP对账
```typescript
// 链上资产余额 vs GAAP账面价值对账
export class ChainGaapReconciliationService {
async reconcile(period: AccountingPeriod): Promise<ReconciliationReport> {
// 1. 链上数据汇总
const chainData = {
totalCouponsOutstanding: await this.chainClient.getTotalOutstandingCoupons(),
totalStablecoinHeld: await this.chainClient.getTotalStablecoinBalance(),
totalGuaranteeFunds: await this.chainClient.getTotalGuaranteeFunds(),
};
// 2. GAAP账面数据
const gaapData = {
deferredRevenue: await this.accountingRepo.getDeferredRevenue(period.endDate),
userDeposits: await this.accountingRepo.getUserDeposits(period.endDate),
guaranteeFunds: await this.accountingRepo.getGuaranteeFunds(period.endDate),
};
// 3. 差异分析
const discrepancies = this.computeDiscrepancies(chainData, gaapData);
return {
period,
chainData,
gaapData,
discrepancies,
isClean: discrepancies.length === 0,
auditorNotes: discrepancies.map(d => this.generateAuditNote(d)),
};
}
}
```
---
## 33. 消费者保护法合规FTC / UDAAP / CARD Act
### 33.1 FTC Act Section 5 / Dodd-Frank UDAAP 规则引擎
```typescript
// compliance-service/src/domain/services/consumer-protection.service.ts
export class ConsumerProtectionService {
/**
* FTC Act Section 5: 禁止不公平或欺骗性商业行为
* Dodd-Frank UDAAP: 禁止Unfair, Deceptive, or Abusive Acts or Practices
*
* 平台作为券交易市场,对发行方的券描述负审核责任
*/
private readonly RULES: ConsumerProtectionRule[] = [
// 券信息披露完整性检查
{
name: 'disclosure_completeness',
check: (coupon: CouponListing) => {
const required = ['faceValue', 'expiryDate', 'useConditions', 'issuerCreditRating'];
const missing = required.filter(f => !coupon[f]);
return missing.length === 0
? { pass: true }
: { pass: false, violation: `缺少必要披露: ${missing.join(',')}` };
},
},
// 定价不得误导(折扣率标注准确性)
{
name: 'pricing_accuracy',
check: (coupon: CouponListing) => {
const actualDiscount = 1 - coupon.currentPrice / coupon.faceValue;
const labeledDiscount = coupon.displayedDiscount;
return Math.abs(actualDiscount - labeledDiscount) <= 0.01
? { pass: true }
: { pass: false, violation: `标注折扣${labeledDiscount}与实际折扣${actualDiscount}不符` };
},
},
// 发行方信用等级标注准确性
{
name: 'credit_rating_accuracy',
check: (coupon: CouponListing) => {
const currentRating = coupon.issuer.creditRating;
const displayedRating = coupon.displayedCreditRating;
return currentRating === displayedRating
? { pass: true }
: { pass: false, violation: `展示信用等级${displayedRating}与实际${currentRating}不符` };
},
},
// 退款政策透明化
{
name: 'refund_policy_disclosure',
check: (coupon: CouponListing) => {
return coupon.refundPolicy && coupon.refundPolicy.isVisible
? { pass: true }
: { pass: false, violation: '退款政策未明确展示' };
},
},
];
async auditListing(coupon: CouponListing): Promise<ConsumerProtectionAuditResult> {
const violations = this.RULES
.map(rule => ({ rule: rule.name, result: rule.check(coupon) }))
.filter(r => !r.result.pass);
if (violations.length > 0) {
await this.alertService.sendViolationAlert(coupon.id, violations);
}
return { couponId: coupon.id, pass: violations.length === 0, violations };
}
// AI辅助虚假宣传检测批量扫描
@Cron('0 6 * * *') // 每日凌晨6点
async batchScanListings(): Promise<void> {
const activeListings = await this.couponRepo.findActiveListings();
for (const listing of activeListings) {
const result = await this.auditListing(listing);
if (!result.pass) {
await this.createEnforcementCase(listing, result.violations);
}
}
}
}
```
### 33.2 CARD Act 有效期冲突检测
```typescript
// compliance-service/src/domain/services/card-act.service.ts
export class CardActComplianceService {
/**
* CARD Act (Credit CARD Act of 2009) 礼品卡条款:
* - 有效期不得少于5年
* - 不得收取休眠费dormancy/inactivity fees
*
* 冲突Utility Track强制有效期≤12个月SEC合规
* 解决:论证"消费型券≠Gift Card"(券是发行方预付债务工具,非零售商礼品卡)
*
* 本服务实现分类检测如果券可能被认定为Gift Card则触发合规审查
*/
async checkCardActApplicability(coupon: CouponTemplate): Promise<CardActCheckResult> {
// Gift Card特征匹配
const giftCardIndicators = {
hasFixedFaceValue: coupon.faceValue > 0 && !coupon.isPercentageDiscount,
isOpenLoop: !coupon.allowedStores || coupon.allowedStores.length === 0,
isStoreBranded: coupon.allowedStores && coupon.allowedStores.length > 0,
labeledAsGiftCard: /gift\s*card|礼品卡|礼券/i.test(coupon.name + coupon.description),
isPreloaded: coupon.couponCategory === 'stored_value',
};
const isLikelyGiftCard = giftCardIndicators.labeledAsGiftCard ||
(giftCardIndicators.hasFixedFaceValue && giftCardIndicators.isPreloaded);
if (isLikelyGiftCard && coupon.couponType === 'utility') {
// 冲突检测Utility Track ≤12个月 vs CARD Act ≥5年
const expiryDays = differenceInDays(coupon.expiryDate, coupon.issuedDate);
if (expiryDays < 365 * 5) {
return {
isConflict: true,
couponId: coupon.id,
conflictType: 'CARD_ACT_VS_UTILITY_TRACK',
detail: `券"${coupon.name}"可能被认定为Gift CardCARD Act要求≥5年有效期但Utility Track限制≤12个月`,
recommendation: [
'1. 修改券名称/描述,避免使用"礼品卡/Gift Card"字样',
'2. 或在法律意见书中论证该券为"预付债务工具"而非"零售礼品卡"',
'3. 或将该券移出Utility Track按CARD Act规则设置≥5年有效期',
],
requiresLegalReview: true,
};
}
}
// 休眠费检查(平台不收取,但检查发行方设置)
if (coupon.inactivityFee && coupon.inactivityFee > 0) {
return {
isConflict: true,
couponId: coupon.id,
conflictType: 'CARD_ACT_DORMANCY_FEE',
detail: 'CARD Act禁止对礼品卡/预付卡收取休眠费',
recommendation: ['移除休眠费设置'],
requiresLegalReview: false,
};
}
return { isConflict: false, couponId: coupon.id };
}
// 发行审核时自动触发
async preIssuanceCardActCheck(template: CouponTemplate): Promise<void> {
const result = await this.checkCardActApplicability(template);
if (result.isConflict) {
// 标记为需要法律审查,暂停发行审批
await this.issuerRepo.flagForLegalReview(template.id, result);
throw new ComplianceException(`CARD Act合规冲突: ${result.detail}`);
}
}
}
```
### 33.3 GENIUS Act 稳定币合规
```typescript
// compliance-service/src/domain/services/genius-act.service.ts
export class GeniusActComplianceService {
/**
* GENIUS Act (2025年签署) — 稳定币监管法:
* 平台策略使用第三方合规稳定币USDC/USDT不自行发行
* 合规责任:确保出入金通道对接的稳定币发行方持有合规牌照
*/
private readonly APPROVED_STABLECOINS: StablecoinInfo[] = [
{
symbol: 'USDC', issuer: 'Circle', license: 'NY BitLicense + State MTLs',
reserveAudit: 'Deloitte (monthly attestation)', compliant: true,
},
{
symbol: 'USDT', issuer: 'Tether', license: 'Various',
reserveAudit: 'BDO Italia', compliant: true,
},
{
symbol: 'PYUSD', issuer: 'PayPal/Paxos', license: 'NYDFS regulated',
reserveAudit: 'Withum', compliant: true,
},
];
async validateStablecoinCompliance(stablecoinAddress: string): Promise<boolean> {
const info = this.APPROVED_STABLECOINS.find(s =>
s.contractAddress === stablecoinAddress
);
if (!info || !info.compliant) {
throw new ComplianceException('不支持的或不合规的稳定币');
}
return true;
}
// 季度审查:更新稳定币合规状态
@Cron('0 0 1 */3 *') // 每季度1号
async quarterlyStablecoinReview(): Promise<void> {
for (const coin of this.APPROVED_STABLECOINS) {
const latestAttestation = await this.fetchLatestReserveAttestation(coin.issuer);
coin.compliant = latestAttestation.isClean;
if (!coin.compliant) {
await this.alertService.sendCriticalAlert(
`稳定币${coin.symbol}储备审计异常,考虑暂停出入金通道`
);
}
}
}
}
```
---
## 34. 州级合规路由MTL / BitLicense / DFAL
```typescript
// compliance-service/src/domain/services/state-compliance.service.ts
export class StateComplianceService {
/**
* 美国49州+DC各自有独立的Money Transmitter License (MTL)要求
* 不同州的交易限额、KYC要求、报告义务不同
* 本服务根据用户所在州动态应用对应合规规则
*/
private readonly STATE_RULES: Record<string, StateComplianceRule> = {
NY: {
state: 'New York',
license: 'BitLicense (NYDFS)',
status: 'required',
additionalKyc: true, // NY要求增强KYC
maxSingleTx: 10000, // 单笔上限USD
reportingThreshold: 3000, // 报告阈值
custodyRequirements: 'NYDFS托管规则客户资产必须隔离托管',
restrictions: ['不允许匿名交易', '需额外的NY居民声明'],
},
CA: {
state: 'California',
license: 'DFAL (2026年7月生效)',
status: 'pending_regulation',
additionalKyc: false,
maxSingleTx: null, // 无额外限制
reportingThreshold: 3000,
custodyRequirements: 'DFAL框架下的托管要求待细则',
restrictions: ['CCPA增强隐私要求'],
},
TX: {
state: 'Texas',
license: 'MTL (Texas Dept of Banking)',
status: 'required',
additionalKyc: false,
maxSingleTx: null,
reportingThreshold: 3000,
custodyRequirements: '标准MTL托管要求',
restrictions: [],
},
FL: {
state: 'Florida',
license: 'MTL (OFR)',
status: 'required',
additionalKyc: false,
maxSingleTx: null,
reportingThreshold: 3000,
custodyRequirements: '标准MTL托管要求',
restrictions: [],
},
// ... 其他州配置配置化管理通过Admin后台维护
};
// 默认规则(适用于未单独配置的州)
private readonly DEFAULT_RULE: StateComplianceRule = {
state: 'Default',
license: 'MTL (standard)',
status: 'required',
additionalKyc: false,
maxSingleTx: null,
reportingThreshold: 3000,
custodyRequirements: '标准MTL托管要求',
restrictions: [],
};
// 受限州(暂不服务,直到获得相应牌照)
private readonly RESTRICTED_STATES = ['NY', 'HI']; // NY需BitLicenseHI暂不服务
async checkStateCompliance(userId: string, transaction: TransactionRequest): Promise<StateCheckResult> {
const user = await this.userRepo.findById(userId);
const state = user.residenceState;
// 1. 检查是否为受限州
if (this.RESTRICTED_STATES.includes(state)) {
const hasLicense = await this.licenseRepo.hasActiveLicense(state);
if (!hasLicense) {
return {
allowed: false,
reason: `暂不支持${state}州用户交易,待取得${this.STATE_RULES[state]?.license || 'MTL'}牌照`,
};
}
}
// 2. 获取州规则
const rule = this.STATE_RULES[state] || this.DEFAULT_RULE;
// 3. 单笔限额检查
if (rule.maxSingleTx && transaction.amount > rule.maxSingleTx) {
return {
allowed: false,
reason: `${state}州单笔交易上限$${rule.maxSingleTx}`,
};
}
// 4. 增强KYC检查如NY
if (rule.additionalKyc && user.kycLevel < KycLevel.L2) {
return {
allowed: false,
reason: `${state}州要求KYC L2+`,
action: 'upgrade_kyc',
};
}
// 5. 州级报告义务
if (transaction.amount >= rule.reportingThreshold) {
await this.stateReportingService.queueReport(state, transaction);
}
return { allowed: true, appliedRule: rule };
}
// 牌照状态管理
async getLicenseStatus(): Promise<LicenseStatusReport> {
const allStates = Object.keys(this.STATE_RULES);
const statuses = await Promise.all(
allStates.map(async (state) => ({
state,
required: this.STATE_RULES[state].license,
obtained: await this.licenseRepo.hasActiveLicense(state),
expiryDate: await this.licenseRepo.getLicenseExpiry(state),
renewalDue: await this.licenseRepo.isRenewalDue(state),
}))
);
return { statuses, restrictedStates: this.RESTRICTED_STATES };
}
}
```
---
## 35. SEC证券合规Phase 4预留
```typescript
// compliance-service/src/domain/services/securities-compliance.service.ts
/**
* Securities Track合规服务Phase 4启用
*
* 前提条件:
* 1. 券的法律属性意见书已出具
* 2. 如需Broker-Dealer注册已完成SEC注册
* 3. 如需ATS注册已提交Form ATS
*
* 豁免路径SRS 1.5-1.6
* - Reg D (Rule 506(b/c)): 面向合格投资者,无公开募集,无发行上限
* - Reg A+ (Tier 2): 面向所有投资者,年发行上限$75M需SEC审查
* - Reg CF: 众筹豁免,年上限$5M需通过注册融资门户
*/
export class SecuritiesComplianceService {
private readonly PHASE4_ENABLED = false; // Phase 4前保持关闭
// Reg D 506(c) 合格投资者验证
async verifyAccreditedInvestor(userId: string): Promise<AccreditationResult> {
if (!this.PHASE4_ENABLED) throw new Error('Securities Track not enabled');
const user = await this.userRepo.findById(userId);
// SEC Rule 501(a) 合格投资者标准
const criteria = {
// 个人:年收入>$200K或与配偶合计>$300K连续2年
incomeTest: user.annualIncome >= 200000 || user.jointIncome >= 300000,
// 个人:净资产>$1M不含主要住所
netWorthTest: user.netWorthExcludingPrimaryResidence >= 1000000,
// 专业认证Series 7/65/82等
professionalTest: user.hasFinancialLicense,
// 实体:资产>$5M
entityTest: user.isEntity && user.entityAssets >= 5000000,
};
const isAccredited = criteria.incomeTest || criteria.netWorthTest ||
criteria.professionalTest || criteria.entityTest;
return {
userId,
isAccredited,
verificationMethod: 'self_certification', // 初期自认证,后期第三方验证
verifiedAt: new Date(),
validUntil: addMonths(new Date(), 12), // 12个月有效
criteria,
};
}
// Reg A+ 投资限额检查
async checkRegAInvestmentLimit(userId: string, amount: number): Promise<boolean> {
if (!this.PHASE4_ENABLED) throw new Error('Securities Track not enabled');
const user = await this.userRepo.findById(userId);
// Reg A+ Tier 2: 非合格投资者年投资上限 = max(年收入, 净资产) × 10%
if (!user.isAccredited) {
const limit = Math.max(user.annualIncome, user.netWorth) * 0.10;
const ytdInvestment = await this.investmentRepo.getYtdTotal(userId);
return (ytdInvestment + amount) <= limit;
}
return true; // 合格投资者无限额
}
// Form ATS报告义务
async generateAtsReport(quarter: string): Promise<AtsQuarterlyReport> {
if (!this.PHASE4_ENABLED) throw new Error('Securities Track not enabled');
return {
quarter,
totalSecuritiesTraded: await this.tradeRepo.getSecuritiesVolume(quarter),
uniqueParticipants: await this.tradeRepo.getUniqueSecuritiesTraders(quarter),
topSecurities: await this.tradeRepo.getTopSecuritiesByVolume(quarter, 10),
// SEC Form ATS-N 披露要求
atsOperations: {
orderTypes: ['limit', 'market'],
matchingLogic: 'price-time priority',
feeSchedule: this.getSecuritiesFeeSchedule(),
},
};
}
}
```
---
## 36. 商业保险需求跟踪
```typescript
// compliance-service/src/domain/services/insurance-tracking.service.ts
/**
* 商业保险管理Nasdaq上市审计要求
* IPO前12个月完成所有保险采购
*/
export class InsuranceTrackingService {
private readonly REQUIRED_POLICIES: InsurancePolicyRequirement[] = [
{
type: 'D&O',
name: '董事及高管责任险 (Directors & Officers)',
description: '董事/高管因管理决策被诉的法律费用与赔偿',
minCoverage: 'match_market_cap', // 覆盖额度匹配公司市值
requiredBy: 'IPO前12个月',
status: 'not_purchased',
carriers: ['AIG', 'Chubb', 'Berkshire Hathaway'],
},
{
type: 'E&O',
name: '专业责任险 (Errors & Omissions)',
description: '平台服务失误导致用户损失(信用评级错误、交易撮合问题)',
minCoverage: 10_000_000, // $10M
requiredBy: 'IPO前12个月',
status: 'not_purchased',
carriers: ['Lloyd\'s', 'AIG', 'Travelers'],
},
{
type: 'CYBER',
name: '网络安全险 (Cyber Liability)',
description: '数据泄露、黑客攻击、勒索软件损失与响应成本',
minCoverage: 50_000_000, // $50M覆盖MPC托管的全部用户资产
requiredBy: 'IPO前12个月',
status: 'not_purchased',
carriers: ['Coalition', 'Beazley', 'AIG'],
notes: '需覆盖数字资产托管风险,保费较高',
},
{
type: 'FIDELITY',
name: '忠诚保证金 (Fidelity Bond)',
description: '内部员工欺诈、盗窃数字资产',
minCoverage: 5_000_000, // $5M
requiredBy: 'IPO前12个月',
status: 'not_purchased',
carriers: ['Travelers', 'Hartford', 'CNA'],
},
{
type: 'GL',
name: '商业综合险 (General Liability)',
description: '一般商业责任',
minCoverage: 2_000_000, // $2M
requiredBy: 'IPO前12个月',
status: 'not_purchased',
carriers: ['Progressive', 'Hartford'],
},
];
async getInsuranceStatus(): Promise<InsuranceStatusReport> {
const policies = await this.policyRepo.findAll();
return {
requiredPolicies: this.REQUIRED_POLICIES.map(req => {
const purchased = policies.find(p => p.type === req.type);
return {
...req,
status: purchased ? 'active' : 'not_purchased',
policyNumber: purchased?.policyNumber,
effectiveDate: purchased?.effectiveDate,
expiryDate: purchased?.expiryDate,
coverageAmount: purchased?.coverageAmount,
annualPremium: purchased?.annualPremium,
isAdequate: purchased
? this.isCoverageAdequate(req, purchased)
: false,
};
}),
allRequirementsMet: this.REQUIRED_POLICIES.every(
req => policies.some(p => p.type === req.type && p.status === 'active')
),
};
}
// 保险到期提醒
@Cron('0 9 * * 1') // 每周一上午9点
async checkExpiringPolicies(): Promise<void> {
const policies = await this.policyRepo.findExpiringWithin(90); // 90天内到期
for (const policy of policies) {
await this.alertService.sendAlert({
type: 'insurance_expiring',
severity: 'high',
message: `${policy.name}将于${policy.expiryDate}到期,请及时续期`,
});
}
}
}
```
---
*文档版本: v2.1*
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
*技术栈: NestJS + Go + Kong + PostgreSQL + Kafka + Redis*
*更新: v2.1补充会计处理(ASC 606/递延负债/GAAP对账)/消费者保护法(FTC/UDAAP/CARD Act冲突检测/GENIUS Act)/州级合规路由(MTL/BitLicense/DFAL)/SEC证券合规(Reg D/A+/CF预留)/商业保险跟踪*