小游戏创作者经济生态设计:插件商店、收益分润与创作者成长体系

深入讲解小游戏平台创作者经济生态的架构设计。涵盖插件商店技术架构(版本管理、签名验证、依赖解析)、收益分润算法(阶梯抽成与动态分成)、创作者等级体系(GMV/质量/活跃三维评估)、UGC 内容审核流水线。提供 MarketplaceEngine 核心 TypeScript 实现与智能推荐算法。

小游戏创作者经济生态设计

一、创作者经济的核心逻辑

创作者经济(Creator Economy)是小游戏平台从"工具"进化为"生态"的关键跃迁。MiniPlay Studio 的创作者经济围绕三个飞轮运转:

graph LR
    A[创作者生产<br/>插件/模板/素材] --> B[开发者消费<br/>提升效率]
    B --> C[平台抽成<br/>可持续发展]
    C --> A
    B --> D[开发者成为创作者<br/>再生产]
    D --> A

    style A fill:#e3f2fd
    style B fill:#e8f5e9
    style C fill:#fff3e0
    style D fill:#fce4ec

核心指标

指标健康值计算方式
创作者渗透率> 15%有发布行为的开发者 / 总开发者数
内容复用率> 40%使用插件/模板的游戏 / 总发布游戏
创作者留存率> 60%3 个月内有收益的创作者占比
GMV / 创作者> ¥500/月月总交易额 / 活跃创作者数
内容审核通过率> 85%通过审核数 / 提交审核数

二、插件商店技术架构

2.1 核心数据模型

interface Plugin {
  id: string;
  name: string;
  slug: string;
  description: string;
  authorId: string;
  version: string; // SemVer
  versions: PluginVersion[];
  category: 'ui' | 'physics' | 'audio' | 'ai' | 'utility' | 'template';
  tags: string[];
  pricing: PricingModel;
  stats: PluginStats;
  status: 'pending' | 'approved' | 'rejected' | 'suspended';
  createdAt: number;
  updatedAt: number;
}

interface PluginVersion {
  version: string;
  changelog: string;
  minEngineVersion: string;
  dependencies: Record<string, string>; // pluginId -> semver range
  packageUrl: string;
  signature: string; // ECDSA 签名
  fileHash: string; // SHA-256
  size: number;
  downloads: number;
  publishedAt: number;
}

type PricingModel =
  | { type: 'free' }
  | { type: 'one-time'; price: number; currency: 'CNY' | 'USD' }
  | { type: 'subscription'; price: number; currency: 'CNY' | 'USD'; interval: 'month' | 'year' }
  | { type: 'revenue-share'; percentage: number }; // 使用插件的游戏收入分润

interface PluginStats {
  downloads: number;
  ratings: { avg: number; count: number; distribution: number[] };
  revenue: number;
  activeInstalls: number;
}

2.2 版本管理与依赖解析

class DependencyResolver {
  private registry: Map<string, Plugin>;

  resolve(pluginId: string, versionRange: string): PluginVersion | null {
    const plugin = this.registry.get(pluginId);
    if (!plugin) return null;

    // SemVer 匹配
    const matched = plugin.versions.find(v =>
      semverSatisfies(v.version, versionRange)
    );
    return matched || null;
  }

  resolveTree(requirements: Record<string, string>): {
    resolved: Map<string, string>; // pluginId -> version
    conflicts: Array<{ pluginId: string; wanted: string[] }>;
  } {
    const resolved = new Map<string, string>();
    const conflicts: Array<{ pluginId: string; wanted: string[] }> = [];
    const queue = Object.entries(requirements);
    const wanted = new Map<string, Set<string>>();

    while (queue.length > 0) {
      const [id, range] = queue.shift()!;

      if (!wanted.has(id)) wanted.set(id, new Set());
      wanted.get(id)!.add(range);

      if (resolved.has(id)) {
        const current = resolved.get(id)!;
        if (!semverSatisfies(current, range)) {
          conflicts.push({ pluginId: id, wanted: Array.from(wanted.get(id)!) });
        }
        continue;
      }

      const version = this.resolve(id, range);
      if (version) {
        resolved.set(id, version.version);
        // 递归解析传递依赖
        for (const [depId, depRange] of Object.entries(version.dependencies)) {
          queue.push([depId, depRange]);
        }
      }
    }

    return { resolved, conflicts };
  }

  // 检测循环依赖
  detectCycles(requirements: Record<string, string>): string[][] {
    const graph = new Map<string, string[]>();
    const visited = new Set<string>();
    const cycles: string[][] = [];

    const buildGraph = (id: string, range: string) => {
      const v = this.resolve(id, range);
      if (!v) return;
      graph.set(id, Object.keys(v.dependencies));
      for (const [dep, depRange] of Object.entries(v.dependencies)) {
        if (!graph.has(dep)) buildGraph(dep, depRange);
      }
    };

    for (const [id, range] of Object.entries(requirements)) {
      buildGraph(id, range);
    }

    // DFS 找环
    const dfs = (node: string, path: string[], seen: Set<string>) => {
      if (seen.has(node)) {
        const cycleStart = path.indexOf(node);
        cycles.push(path.slice(cycleStart).concat(node));
        return;
      }
      if (!graph.has(node)) return;

      path.push(node);
      seen.add(node);
      for (const dep of graph.get(node)!) {
        dfs(dep, [...path], new Set(seen));
      }
    };

    for (const node of graph.keys()) {
      if (!visited.has(node)) {
        dfs(node, [], new Set());
      }
    }

    return cycles;
  }
}

// 简化版 SemVer 匹配
function semverSatisfies(version: string, range: string): boolean {
  const [vMajor, vMinor, vPatch] = version.split('.').map(Number);
  if (range.startsWith('^')) {
    const [rMajor, rMinor, rPatch] = range.slice(1).split('.').map(Number);
    if (vMajor !== rMajor) return false;
    if (vMajor === 0) {
      return vMinor >= rMinor && (vMinor > rMinor || vPatch >= rPatch);
    }
    return vMinor > rMinor || (vMinor === rMinor && vPatch >= rPatch);
  }
  if (range.startsWith('~')) {
    const [rMajor, rMinor, rPatch] = range.slice(1).split('.').map(Number);
    return vMajor === rMajor && vMinor === rMinor && vPatch >= rPatch;
  }
  return version === range;
}

2.3 签名验证与安全

import { createVerify } from 'crypto';

class PluginSecurity {
  private publicKey: string;

  verifySignature(pluginVersion: PluginVersion): boolean {
    const verify = createVerify('SHA256');
    verify.update(pluginVersion.fileHash);
    return verify.verify(this.publicKey, pluginVersion.signature, 'base64');
  }

  // 静态代码安全检查
  async scanCode(packageBuffer: ArrayBuffer): Promise<SecurityReport> {
    const code = new TextDecoder().decode(packageBuffer);
    const risks: SecurityRisk[] = [];

    // 检测危险 API
    const dangerousPatterns = [
      { pattern: /eval\s*\(/, level: 'critical', desc: 'Uses eval()' },
      { pattern: /document\.write/, level: 'high', desc: 'Uses document.write' },
      { pattern: /fetch\s*\(/, level: 'medium', desc: 'Network requests detected' },
      { pattern: /localStorage/, level: 'low', desc: 'Local storage access' },
      { pattern: /new\s+Function/, level: 'critical', desc: 'Dynamic code execution' },
    ];

    for (const { pattern, level, desc } of dangerousPatterns) {
      if (pattern.test(code)) {
        risks.push({ level, description: desc, line: this.findLine(code, pattern) });
      }
    }

    return { passed: !risks.some(r => r.level === 'critical'), risks };
  }

  private findLine(code: string, pattern: RegExp): number {
    const match = code.match(pattern);
    if (!match) return -1;
    return code.slice(0, match.index).split('\n').length;
  }
}

interface SecurityReport {
  passed: boolean;
  risks: SecurityRisk[];
}

interface SecurityRisk {
  level: 'critical' | 'high' | 'medium' | 'low';
  description: string;
  line: number;
}

三、收益分润算法

3.1 阶梯抽成模型

interface RevenueTier {
  minGMV: number;
  maxGMV: number;
  platformFee: number; // 平台抽成比例
  creatorShare: number; // 创作者分成比例
}

const REVENUE_TIERS: RevenueTier[] = [
  { minGMV: 0, maxGMV: 1000, platformFee: 0.15, creatorShare: 0.85 },
  { minGMV: 1000, maxGMV: 5000, platformFee: 0.20, creatorShare: 0.80 },
  { minGMV: 5000, maxGMV: 20000, platformFee: 0.25, creatorShare: 0.75 },
  { minGMV: 20000, maxGMV: Infinity, platformFee: 0.30, creatorShare: 0.70 },
];

class RevenueCalculator {
  calculateSplit(creatorGMV: number): { platform: number; creator: number } {
    const tier = REVENUE_TIERS.find(t => creatorGMV >= t.minGMV && creatorGMV < t.maxGMV)
      || REVENUE_TIERS[REVENUE_TIERS.length - 1];

    // 如果是跨阶梯,分段计算
    let platformTotal = 0;
    let creatorTotal = 0;
    let remaining = creatorGMV;

    for (const t of REVENUE_TIERS) {
      if (remaining <= 0) break;
      const tierAmount = Math.min(remaining, t.maxGMV - t.minGMV);
      platformTotal += tierAmount * t.platformFee;
      creatorTotal += tierAmount * t.creatorShare;
      remaining -= tierAmount;
    }

    return { platform: platformTotal, creator: creatorTotal };
  }

  // 考虑创作者等级的加成
  calculateWithLevelBonus(
    creatorGMV: number,
    creatorLevel: number
  ): { platform: number; creator: number } {
    const base = this.calculateSplit(creatorGMV);
    // 高等级创作者获得额外 2–5% 分成
    const levelBonus = Math.min(creatorLevel * 0.005, 0.05);
    const bonus = creatorGMV * levelBonus;

    return {
      platform: base.platform - bonus,
      creator: base.creator + bonus,
    };
  }
}

3.2 交易流水与结算

interface Transaction {
  id: string;
  pluginId: string;
  buyerId: string;
  sellerId: string;
  amount: number;
  currency: string;
  platformFee: number;
  creatorEarnings: number;
  status: 'pending' | 'completed' | 'refunded';
  createdAt: number;
  settledAt?: number;
}

class SettlementEngine {
  private transactions: Transaction[] = [];

  recordSale(plugin: Plugin, buyerId: string): Transaction {
    const price = plugin.pricing.type === 'one-time'
      ? (plugin.pricing as any).price
      : 0;

    const calc = new RevenueCalculator();
    const split = calc.calculateWithLevelBonus(price, 5); // 假设等级 5

    const tx: Transaction = {
      id: `tx_${Date.now()}_${Math.random().toString(36).slice(2)}`,
      pluginId: plugin.id,
      buyerId,
      sellerId: plugin.authorId,
      amount: price,
      currency: (plugin.pricing as any).currency || 'CNY',
      platformFee: split.platform,
      creatorEarnings: split.creator,
      status: 'completed',
      createdAt: Date.now(),
    };

    this.transactions.push(tx);
    return tx;
  }

  // 月结算
  generateMonthlyReport(creatorId: string, year: number, month: number): MonthlyReport {
    const start = new Date(year, month - 1, 1).getTime();
    const end = new Date(year, month, 1).getTime();

    const txs = this.transactions.filter(t =>
      t.sellerId === creatorId &&
      t.createdAt >= start &&
      t.createdAt < end &&
      t.status === 'completed'
    );

    return {
      totalSales: txs.reduce((s, t) => s + t.amount, 0),
      platformFees: txs.reduce((s, t) => s + t.platformFee, 0),
      creatorEarnings: txs.reduce((s, t) => s + t.creatorEarnings, 0),
      refundRate: this.calculateRefundRate(txs),
      transactions: txs.length,
    };
  }

  private calculateRefundRate(txs: Transaction[]): number {
    const refunded = txs.filter(t => t.status === 'refunded').length;
    return txs.length > 0 ? refunded / txs.length : 0;
  }
}

interface MonthlyReport {
  totalSales: number;
  platformFees: number;
  creatorEarnings: number;
  refundRate: number;
  transactions: number;
}

四、创作者等级体系

4.1 三维评估模型

interface CreatorScore {
  gmv: number;      // 0–40 分
  quality: number;  // 0–35 分
  activity: number; // 0–25 分
  total: number;    // 0–100 分
  level: number;    // 1–10 级
}

class CreatorLevelEngine {
  calculateScore(creator: CreatorProfile): CreatorScore {
    const gmvScore = Math.min(creator.monthlyGMV / 500, 40); // 2万 GMV 满分

    const qualityScore = (
      (creator.avgRating / 5) * 15 +      // 评分占 15 分
      (1 - creator.refundRate) * 10 +     // 退款率占 10 分
      (creator.supportResponseTime < 24 ? 10 : 5) // 响应时间占 10 分
    );

    const activityScore = (
      (Math.min(creator.monthlyUpdates, 10) / 10) * 10 +    // 更新频率
      (Math.min(creator.communityPosts, 50) / 50) * 8 +     // 社区互动
      (creator.hasTutorial ? 7 : 0)                          // 教程贡献
    );

    const total = gmvScore + qualityScore + activityScore;

    return {
      gmv: gmvScore,
      quality: qualityScore,
      activity: activityScore,
      total,
      level: this.scoreToLevel(total),
    };
  }

  private scoreToLevel(score: number): number {
    if (score >= 90) return 10;
    if (score >= 80) return 9;
    if (score >= 70) return 8;
    if (score >= 60) return 7;
    if (score >= 50) return 6;
    if (score >= 40) return 5;
    if (score >= 30) return 4;
    if (score >= 20) return 3;
    if (score >= 10) return 2;
    return 1;
  }

  getLevelBenefits(level: number): LevelBenefits {
    const benefits: Record<number, LevelBenefits> = {
      1: { feeDiscount: 0, priorityReview: false, featuredSpot: false, apiQuota: 100 },
      3: { feeDiscount: 0.02, priorityReview: false, featuredSpot: false, apiQuota: 500 },
      5: { feeDiscount: 0.03, priorityReview: true, featuredSpot: false, apiQuota: 2000 },
      7: { feeDiscount: 0.04, priorityReview: true, featuredSpot: true, apiQuota: 5000 },
      10: { feeDiscount: 0.05, priorityReview: true, featuredSpot: true, apiQuota: 20000 },
    };
    return benefits[level] || benefits[1];
  }
}

interface CreatorProfile {
  id: string;
  monthlyGMV: number;
  avgRating: number;
  refundRate: number;
  supportResponseTime: number; // hours
  monthlyUpdates: number;
  communityPosts: number;
  hasTutorial: boolean;
}

interface LevelBenefits {
  feeDiscount: number;    // 抽成减免比例
  priorityReview: boolean; // 优先审核
  featuredSpot: boolean;   // 推荐位
  apiQuota: number;        // API 调用额度
}

五、UGC 内容审核流水线

5.1 三层审核架构

graph TD
    A[创作者提交] --> B{机审层<br/><30s}
    B -->|通过| C{速审层<br/><30min}
    B -->|高风险| D[自动拒绝]
    C -->|通过| E[上架]
    C -->|可疑| F{众审层<br/>社区自治}
    C -->|违规| G[人工复审]
    F -->|通过| E
    F -->|拒绝| G
    G -->|确认违规| H[下架/处罚]
    G -->|误判| E

5.2 审核引擎实现

interface ModerationResult {
  decision: 'approve' | 'reject' | 'manual_review';
  confidence: number; // 0–1
  reasons: string[];
  autoFlags: AutoFlag[];
}

interface AutoFlag {
  type: 'code_risk' | 'image_nsfw' | 'text_sensitive' | 'copyright' | 'spam';
  severity: 'low' | 'medium' | 'high' | 'critical';
  details: string;
}

class ModerationEngine {
  private codeScanner: PluginSecurity;
  private imageClassifier: ImageClassifier;
  private textFilter: TextFilter;

  async moderate(plugin: Plugin, packageBuffer: ArrayBuffer): Promise<ModerationResult> {
    const flags: AutoFlag[] = [];

    // 1. 代码扫描
    const secReport = await this.codeScanner.scanCode(packageBuffer);
    for (const risk of secReport.risks) {
      flags.push({
        type: 'code_risk',
        severity: risk.level as any,
        details: risk.description,
      });
    }

    // 2. 截图/图标审查(如果有)
    if (plugin.screenshots?.length) {
      for (const screenshot of plugin.screenshots) {
        const imgResult = await this.imageClassifier.classify(screenshot);
        if (imgResult.nsfw > 0.7) {
          flags.push({ type: 'image_nsfw', severity: 'high', details: 'NSFW content detected' });
        }
      }
    }

    // 3. 文本审查
    const textToCheck = `${plugin.name} ${plugin.description} ${plugin.tags.join(' ')}`;
    const textResult = this.textFilter.check(textToCheck);
    if (textResult.sensitiveWords.length > 0) {
      flags.push({
        type: 'text_sensitive',
        severity: textResult.sensitiveWords.some((w: any) => w.level === 'critical') ? 'critical' : 'medium',
        details: `Sensitive words: ${textResult.sensitiveWords.map((w: any) => w.word).join(', ')}`,
      });
    }

    // 4. 决策
    const criticalFlags = flags.filter(f => f.severity === 'critical');
    const highFlags = flags.filter(f => f.severity === 'high');

    if (criticalFlags.length > 0) {
      return {
        decision: 'reject',
        confidence: 0.95,
        reasons: criticalFlags.map(f => f.details),
        autoFlags: flags,
      };
    }

    if (highFlags.length > 0) {
      return {
        decision: 'manual_review',
        confidence: 0.7,
        reasons: highFlags.map(f => f.details),
        autoFlags: flags,
      };
    }

    return {
      decision: 'approve',
      confidence: 0.95,
      reasons: [],
      autoFlags: flags,
    };
  }
}

六、智能推荐算法

class PluginRecommendation {
  // 协同过滤:基于用户相似度
  getCollaborativeRecommendations(userId: string, count = 10): Plugin[] {
    // 找到相似用户
    const similarUsers = this.findSimilarUsers(userId);

    // 收集相似用户下载但当前用户未下载的插件
    const candidates = new Map<string, number>();
    for (const simUser of similarUsers) {
      for (const pluginId of simUser.downloadedPlugins) {
        if (!this.hasDownloaded(userId, pluginId)) {
          candidates.set(pluginId, (candidates.get(pluginId) || 0) + simUser.similarity);
        }
      }
    }

    // 按相似度加权排序
    return Array.from(candidates.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, count)
      .map(([id]) => this.getPlugin(id)!);
  }

  // 内容推荐:基于项目特征
  getContentRecommendations(userId: string, count = 10): Plugin[] {
    const userProfile = this.getUserProfile(userId);
    const allPlugins = this.getAllActivePlugins();

    return allPlugins
      .map(p => ({
        plugin: p,
        score: this.calculateContentScore(userProfile, p),
      }))
      .sort((a, b) => b.score - a.score)
      .slice(0, count)
      .map(r => r.plugin);
  }

  private calculateContentScore(profile: UserProfile, plugin: Plugin): number {
    let score = 0;
    // 类别匹配
    if (profile.preferredCategories.includes(plugin.category)) score += 3;
    // 标签匹配
    const matchedTags = plugin.tags.filter(t => profile.preferredTags.includes(t));
    score += matchedTags.length * 0.5;
    // 热度加权
    score += Math.log(plugin.stats.downloads + 1) * 0.1;
    // 评分加权
    score += plugin.stats.ratings.avg * 0.2;
    return score;
  }
}

七、创作者经济生态全景

graph TB
    subgraph "创作者侧"
        C1[创作插件/模板]
        C2[定价策略]
        C3[版本迭代]
    end

    subgraph "平台侧"
        P1[审核流水线]
        P2[签名验证]
        P3[推荐算法]
        P4[结算系统]
    end

    subgraph "消费者侧"
        U1[浏览商店]
        U2[一键安装]
        U3[评价反馈]
    end

    C1 --> P1
    C2 --> P4
    C3 --> P1
    P1 --> P2
    P2 --> P3
    P3 --> U1
    U1 --> U2
    U2 --> P4
    U3 --> P3
    P4 --> C1

八、总结与关键指标

维度设计要点
插件商店SemVer 版本管理 + DAG 依赖解析 + ECDSA 签名验证
收益分润阶梯式动态抽成 + 等级加成 + 月结算
创作者等级GMV/质量/活跃三维评估,unlock 差异化权益
内容审核机审 + 速审 + 众审三层,违规分级处理
智能推荐协同过滤 + 内容特征,双路召回

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「games」更多文章