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

72 KiB
Raw Permalink Blame History

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 领域实体

// 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

// 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 撮合引擎

// 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 链上事件监听

// 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用户可理解的操作。

// 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规范

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核心表

-- 用户表
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. 部署架构

# 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 交易手续费(核心收入)

// 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收益计算与分配

// 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 退款机制

// 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 做市商准入与管理

// 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

// 低延迟专用接口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检测

// 做市商行为监控
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. 三因子定价引擎

// 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 信用评分引擎

# 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 价格预测模型

# 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 异常检测系统

# 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优先级

# 协同过滤 + 内容推荐 混合推荐
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检测规则引擎

// 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 交易图谱分析

// 构建用户间交易关系图谱
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自动生成

// 可疑交易报告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合规服务

// 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

// 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. 税务合规

// 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

// 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. 数据留存与审计

// 数据保留策略
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. 券生命周期管理

// 券状态流转
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. 争议与纠纷处理

// 争议处理流程
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. 客服系统

// 工单系统
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应急流程

// 自动化应急响应
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 备份与故障转移

# 数据库备份策略
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. 映射表安全方案

手机号→地址映射表是最高价值安全资产,被篡改将导致资产错误转移。

// 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. 发行方违约处理

// 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. 多币种与法币通道

// 多稳定币支持
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. 链上对账

// 链上数据 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五步法

// 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

// 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对账

// 链上资产余额 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 规则引擎

// 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 有效期冲突检测

// 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 稳定币合规

// 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

// 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预留

// 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. 商业保险需求跟踪

// 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预留)/商业保险跟踪