Skip to content
Back to skills

1368 Bot Margin Analysis Ce61c4fa

ASecurity

分析每个 bot 的盈利能力,计算各个 bot 的收入、成本和毛利率。

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
developmentjavascriptjavashellsql

Works with

  • mcp

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 1368-bot-margin-analysis_ce61c4fa --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 1368 Bot Margin Analysis Ce61c4fa?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 1368 Bot Margin Analysis Ce61c4fa
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-1368-bot-margin-analysis-ce61c4fa/badge)](https://www.skillsdirectory.com/skills/tools-only-1368-bot-margin-analysis-ce61c4fa)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Bot 毛利率分析报告

## 目标

分析每个 bot 的盈利能力,计算各个 bot 的收入、成本和毛利率。

如果在 base44 运行,则 @base44_prompt_mcphub.md。

## 参数

| 参数 | 说明 | 默认值 |
|------|------|--------|
| `start_date` | 开始日期 (YYYY-MM-DD) | 2025-12-13 |
| `end_date` | 结束日期 (YYYY-MM-DD) | 今天 |

## 数据源

### 收入表
- `my_shell_prod.user_subscription_stripe_orders` - Stripe 订单
- `my_shell_prod.user_subscription_paypal_orders` - PayPal 订单
- `my_shell_prod.apple_used_transactions` - Apple IAP 订单

### 任务表
- `my_shell_prod.art_task` - 任务表,包含 bot 使用记录和成本数据

## 核心指标

**Bot 毛利率 = (Bot 收入 - Bot 成本) / Bot 收入 × 100%**

- **Bot 收入**: 通过 Last-touch 优化版归因模型分配到各个 bot 的收入
- **Bot 成本**: 该 bot 产生的 `actual_energy_cost` 总和
- **Bot 毛利润**: Bot 收入 - Bot 成本
- **Bot 毛利率**: Bot 毛利润 / Bot 收入 × 100%

---

## 实施步骤

### 性能优化说明 ⚡

**原始方案存在的性能问题**:
- ❌ 分3次查询订单 + 1次查询任务 = 4次数据库往返
- ❌ 传输数百万行任务数据到应用层
- ❌ 应用层使用 O(n²) 复杂度的嵌套循环归因
- ⏱️ **预计耗时**: 60-180秒

**优化后的方案(使用SQL归因)**:
- ✅ 使用单个 CTE 查询完成订单合并和归因计算
- ✅ 只传输汇总后的 bot 级数据(几百行 vs 数百万行)
- ✅ 数据传输量减少 99.9%+
- ✅ 时间复杂度从 O(n²) 降到 O(n log n)
- ⚡ **预计耗时**: 15-45秒(**性能提升 3-10 倍**)

**实施步骤**:
1. **Step 1**: 使用 SQL CTE 完成归因计算(下方查询)
2. **Step 2**: 查询成本数据
3. **Step 3**: 应用层合并收入和成本,计算毛利率

---

## 归因时间窗口说明 ⏰

### 时间窗口设置

| 窗口类型 | 时间范围 | 说明 |
|---------|---------|------|
| **订单窗口** | start_date 至 end_date | 统计的订单时间范围 |
| **任务窗口** | start_date - 7天 至 end_date + 7天 | 用于归因的任务时间范围 |

### 为什么扩展任务窗口?

**向前7天**:捕获用户在付费前的使用行为
- 场景:用户在12月20日试用/使用,觉得好用后在12月25日付费购买
- 效果:能将订单归因到用户付费前使用的bot(即使使用时间不在统计窗口内)
- 注意:这不是"续费"场景(月度订阅不会7天内续费),而是"试用转付费"场景

**向后7天**:捕获"先付费后使用"的用户
- 场景:用户12月25日购买,但12月26-30日才开始使用
- 效果:能将订单归因到用户首次使用的bot
- 典型:新用户先买后用,或囤积能量包后续使用

### 预期效果

根据历史数据测试(2025-12-23至12-25):
- **归因覆盖率**:70-80%(±7天窗口)
- **覆盖率提升**:相比原始窗口提升约19%
- **边界影响**:分析起始/结束日期可能受窗口影响,建议按周或按月运行

---

## Step 1: 收入归因(Last-touch 优化版 - SQL实现)

**归因原理**:
- **情况1**: 订单前有使用记录 → 归因给用户在付费前最后使用的 bot
- **情况2**: 订单前无使用记录 → 归因给用户在付费后第一次使用的 bot
- **情况3**: 前后都没有使用记录 → 忽略该订单

**性能优势**:
- ✅ 在数据库层完成归因计算,避免传输百万级任务数据
- ✅ 数据传输量减少 99.9%(从200MB降到50KB)
- ✅ 时间复杂度从 O(n²) 降到 O(n log n)
- ✅ 查询时间从60-180秒降到15-45秒(快3-10倍)

### 归因查询

```sql
-- ====================================================================
-- Bot 毛利率分析 - 收入归因查询(SQL优化版)
-- 功能: 使用 Last-touch 优化版归因模型,将订单收入分配到各个 bot
-- 性能: 相比应用层归因,减少99.9%数据传输,性能提升 3-10 倍
-- ====================================================================

-- --------------------------------------------------------------------
-- Step 1: 合并所有订单源(Stripe + PayPal + Apple IAP)
-- 说明: 将三个订单表合并为统一格式,方便后续归因计算
-- 注意: Apple IAP 按原价计算,未计算折扣码优惠
-- --------------------------------------------------------------------
WITH orders AS (
  -- Stripe 订单
  SELECT
    user_id,
    amount,
    created_date,
    'stripe' as source
  FROM my_shell_prod.user_subscription_stripe_orders
  WHERE status = 'ORDER_STATUS_SUCCESS'
    AND amount >= 0
    AND created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'

  UNION ALL

  -- PayPal 订单(需要特殊处理能量包价格)
  SELECT
    user_id,
    CASE
      WHEN biz_type = 'ENERGY' AND paypal_price_id = '500' THEN 6.99
      WHEN biz_type = 'ENERGY' AND paypal_price_id = '2000' THEN 20.99
      ELSE amount
    END as amount,
    created_date,
    'paypal' as source
  FROM my_shell_prod.user_subscription_paypal_orders
  WHERE status = 'ORDER_STATUS_SUCCESS'
    AND created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'

  UNION ALL

  -- Apple IAP 订单(按 product_id 映射价格)
  -- 注意: 这里是原价,未考虑促销码折扣
  SELECT
    user_id,
    CASE product_id
      WHEN 'PLAYER_MONTHLY' THEN 6.99
      WHEN 'PLAYER_YEARLY' THEN 58.99
      WHEN 'DEVELOPER_MONTHLY' THEN 59.99
      WHEN 'DEVELOPER_YEARLY' THEN 499.99
      WHEN '33OFF_DEVELOPER_MONTHLY' THEN 39.99
      WHEN 'energy_500' THEN 6.99
      WHEN 'energy_2000' THEN 20.99
      ELSE 0
    END as amount,
    created_date,
    'apple' as source
  FROM my_shell_prod.apple_used_transactions
  WHERE created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'
),

-- --------------------------------------------------------------------
-- Step 2: 获取所有已完成任务(用于归因)
-- 说明: 只统计 status='done' 的任务,因为归因基于用户实际完成的使用行为
-- 注意: 这里与成本计算不同(成本包括 cancel 状态)
-- 时间窗口: 订单窗口前7天 + 订单窗口后7天
--   - 向前7天: 捕获用户付费前的试用/使用行为(试用转付费场景)
--   - 向后7天: 捕获"先付费后使用"的用户(囤积能量后续使用)
-- --------------------------------------------------------------------
tasks AS (
  SELECT
    user_id,
    slug_id,
    created_date
  FROM my_shell_prod.art_task
  WHERE status = 'done'
    -- ✅ 扩展时间窗口:订单窗口前7天 + 订单窗口后7天
    AND created_date >= DATE_SUB('{start_date}', INTERVAL 7 DAY)
    AND created_date <= DATE_ADD('{end_date}', INTERVAL 7 DAY)
),

-- --------------------------------------------------------------------
-- Step 3: Last-touch 归因(情况1: 订单前最后一个任务)
-- 逻辑: 找到每个订单对应用户在付费前最后使用的 bot
-- 实现: 使用 ROW_NUMBER() 窗口函数按 (user_id, 订单时间) 分组,取 created_date 最大的任务
-- --------------------------------------------------------------------
last_touch_before AS (
  SELECT
    user_id,
    amount,
    order_date,
    source,
    attributed_bot,
    attribution_type
  FROM (
    SELECT
      o.user_id,
      o.amount,
      o.created_date as order_date,
      o.source,
      t.slug_id as attributed_bot,
      'before' as attribution_type,  -- 标记归因类型,便于调试
      ROW_NUMBER() OVER (
        PARTITION BY o.user_id, o.created_date
        ORDER BY t.created_date DESC  -- 按任务时间降序,取最后一个
      ) as rn
    FROM orders o
    INNER JOIN tasks t
      ON o.user_id = t.user_id
      AND t.created_date <= o.created_date  -- 任务时间 <= 订单时间
  ) ranked
  WHERE rn = 1
),

-- --------------------------------------------------------------------
-- Step 4: First-touch 归因(情况2: 订单后第一个任务)
-- 逻辑: 仅针对"订单前无任务"的情况,归因给订单后第一次使用的 bot
-- 使用场景: 新用户先付费再使用的情况
-- 实现: 使用 ROW_NUMBER() 窗口函数和 NOT EXISTS 排除订单前有任务的用户
-- --------------------------------------------------------------------
first_touch_after AS (
  SELECT
    user_id,
    amount,
    order_date,
    source,
    attributed_bot,
    attribution_type
  FROM (
    SELECT
      o.user_id,
      o.amount,
      o.created_date as order_date,
      o.source,
      t.slug_id as attributed_bot,
      'after' as attribution_type,  -- 标记归因类型,便于调试
      ROW_NUMBER() OVER (
        PARTITION BY o.user_id, o.created_date
        ORDER BY t.created_date ASC  -- 按任务时间升序,取第一个
      ) as rn
    FROM orders o
    INNER JOIN tasks t
      ON o.user_id = t.user_id
      AND t.created_date > o.created_date  -- 任务时间 > 订单时间
    WHERE NOT EXISTS (
      -- 关键: 排除"订单前有任务"的情况(这些已在 last_touch_before 中处理)
      SELECT 1
      FROM tasks t2
      WHERE t2.user_id = o.user_id
        AND t2.created_date <= o.created_date
    )
  ) ranked
  WHERE rn = 1
),

-- --------------------------------------------------------------------
-- Step 5: 合并两种归因结果
-- 说明: last_touch_before 和 first_touch_after 互斥,不会重复
-- --------------------------------------------------------------------
attributed_orders AS (
  SELECT * FROM last_touch_before
  UNION ALL
  SELECT * FROM first_touch_after
)

-- --------------------------------------------------------------------
-- Step 6: 按 bot 汇总归因收入
-- 输出: 每个 bot 的归因收入、订单数量
-- 用途: 后续与成本数据合并,计算毛利率
-- --------------------------------------------------------------------
SELECT
  attributed_bot as slug_id,
  SUM(amount) as attributed_revenue,
  COUNT(*) as attributed_order_count,
  -- 调试信息(可选)
  COUNT(CASE WHEN attribution_type = 'before' THEN 1 END) as before_orders,
  COUNT(CASE WHEN attribution_type = 'after' THEN 1 END) as after_orders
FROM attributed_orders
GROUP BY attributed_bot
ORDER BY attributed_revenue DESC;
```

### 归因覆盖率验证(可选,用于调试)

```sql
-- 查询归因覆盖情况
WITH orders AS (
  -- ... (与上面相同的订单合并逻辑)
),
tasks AS (
  -- ... (与上面相同的任务查询逻辑)
),
attributed AS (
  -- ... (与上面相同的归因逻辑)
)
SELECT
  COUNT(DISTINCT CONCAT(o.user_id, '|', o.created_date)) as total_orders,
  COUNT(DISTINCT CONCAT(a.user_id, '|', a.order_date)) as attributed_orders,
  SUM(o.amount) as total_revenue,
  COALESCE(SUM(a.amount), 0) as attributed_revenue,
  ROUND(COUNT(DISTINCT CONCAT(a.user_id, '|', a.order_date)) * 100.0 /
        COUNT(DISTINCT CONCAT(o.user_id, '|', o.created_date)), 2) as coverage_rate_pct
FROM orders o
LEFT JOIN attributed a ON o.user_id = a.user_id AND o.created_date = a.order_date;
```

---

## Step 2: 查询成本数据

按 bot 汇总成本。

```sql
SELECT
  CAST(bot_id AS CHAR) as bot_id,  -- 将 bot_id 转换为字符串,避免 JavaScript 精度丢失
  slug_id,
  MAX(bot_name) as bot_name,
  SUM(actual_energy_cost) / 100 as bot_cost
FROM my_shell_prod.art_task
WHERE status IN ('done', 'cancel')
  AND created_date >= '{start_date} 00:00:00'
  AND created_date <= '{end_date} 23:59:59'
GROUP BY bot_id, slug_id
ORDER BY bot_cost DESC;
```

**注意**:
- 成本计算包括 `done` 和 `cancel` 状态的任务(取消的任务也产生了成本)
- 与归因模型的任务状态不同(归因只用 `done`)
- `actual_energy_cost` 单位是美分,需要除以 100

---

## Step 3: 合并收入和成本,计算毛利率

**说明**:
- Step 1 的 SQL 查询已返回按 bot 汇总的归因收入
- Step 2 的 SQL 查询已返回按 bot 汇总的成本
- 这里只需要在应用层合并两个结果集并计算毛利率

```javascript
// ====================================================================
// 合并收入和成本数据,计算毛利率
// ====================================================================

// 1. 创建归因收入映射 (slug_id -> revenue data)
// revenueData 是 Step 1 归因查询的结果
const revenueMap = new Map();
for (const row of revenueData) {
  revenueMap.set(row.slug_id, {
    revenue: parseFloat(row.attributed_revenue),
    order_count: row.attributed_order_count
  });
}

// 2. 创建成本映射 (slug_id -> cost data)
// costData 是 Step 2 成本查询的结果
const costMap = new Map();
for (const row of costData) {
  costMap.set(row.slug_id, {
    bot_id: row.bot_id,
    bot_name: row.bot_name,
    cost: parseFloat(row.bot_cost)
  });
}

// 3. 获取所有涉及的 bot(有收入或有成本)
const allBotIds = new Set([
  ...revenueMap.keys(),
  ...costMap.keys()
]);

// 4. 计算每个 bot 的毛利率
const botMargins = [];
for (const slug_id of allBotIds) {
  // 获取收入数据(如果没有归因收入则为0)
  const revenueData = revenueMap.get(slug_id) || { revenue: 0, order_count: 0 };

  // 获取成本数据(如果没有成本则为0)
  const costData = costMap.get(slug_id) || {
    bot_id: null,
    bot_name: 'Unknown',
    cost: 0
  };

  // 计算毛利润和毛利率
  const revenue = revenueData.revenue;
  const cost = costData.cost;
  const grossProfit = revenue - cost;
  const grossMargin = revenue > 0 ? (grossProfit / revenue * 100) : -100;

  botMargins.push({
    bot_id: costData.bot_id,
    bot_name: costData.bot_name,
    slug_id: slug_id,
    revenue: revenue.toFixed(2),
    cost: cost.toFixed(2),
    gross_profit: grossProfit.toFixed(2),
    gross_margin: grossMargin.toFixed(2),
    order_count: revenueData.order_count
  });
}

// 5. 按收入降序排序(收入高的 bot 排在前面)
botMargins.sort((a, b) => parseFloat(b.revenue) - parseFloat(a.revenue));

// 6. 计算汇总指标(需要先查询总订单收入和未归因收入)
const totalAttributedRevenue = botMargins.reduce((sum, b) => sum + parseFloat(b.revenue), 0);
const totalCost = botMargins.reduce((sum, b) => sum + parseFloat(b.cost), 0);
const totalGrossProfit = totalAttributedRevenue - totalCost;
const overallMargin = totalAttributedRevenue > 0 ? (totalGrossProfit / totalAttributedRevenue * 100) : 0;

// 7. 查询总订单收入和未归因收入(用于验证)
// 需要运行Step 4的SQL查询获取:
// - total_order_revenue: 所有订单的总收入
// - total_unattributed_revenue: 未归因订单的总收入
// - never_used_revenue: 从未使用的订单收入
// - outside_window_revenue: 窗口外使用的订单收入

// 验证公式
const isValid = Math.abs(totalOrderRevenue - (totalAttributedRevenue + totalUnattributedRevenue)) < 0.01;
const coveragePct = (totalAttributedRevenue / totalOrderRevenue * 100).toFixed(2);
const unattributedPct = (totalUnattributedRevenue / totalOrderRevenue * 100).toFixed(2);

console.log('======================== 收入归因情况 ========================');
console.log(`总订单收入: $${totalOrderRevenue.toFixed(2)}`);
console.log(`  - 已归因收入: $${totalAttributedRevenue.toFixed(2)} (${coveragePct}%)`);
console.log(`  - 未归因收入: $${totalUnattributedRevenue.toFixed(2)} (${unattributedPct}%)`);
console.log('');
console.log('验证公式: 总订单收入 = 已归因收入 + 未归因收入');
console.log(`验证结果: $${totalOrderRevenue.toFixed(2)} = $${totalAttributedRevenue.toFixed(2)} + $${totalUnattributedRevenue.toFixed(2)} ${isValid ? '✓' : '✗'}`);
console.log('');
console.log('未归因订单详情:');
console.log(`  - 从未使用: ${neverUsedOrders}个订单, $${neverUsedRevenue.toFixed(2)}`);
console.log(`  - 窗口外使用: ${outsideWindowOrders}个订单, $${outsideWindowRevenue.toFixed(2)}`);
console.log('');
console.log('======================== 毛利率分析 ========================');
console.log(`总成本: $${totalCost.toFixed(2)}`);
console.log(`毛利润(基于归因收入): $${totalGrossProfit.toFixed(2)}`);
console.log(`整体毛利率(基于归因收入): ${overallMargin.toFixed(2)}%`);
```

---

## Step 4: 查询未归因订单(用于验证和调试)

### 查询未归因订单统计

```sql
-- ====================================================================
-- 未归因订单统计查询
-- 用途: 统计未能归因到bot的订单,用于验证总收入完整性
-- ====================================================================

WITH orders AS (
  -- 合并所有订单(Stripe + PayPal + Apple IAP)
  -- ... (使用与Step 1相同的订单合并逻辑)
  SELECT
    user_id,
    amount,
    created_date,
    'stripe' as source
  FROM my_shell_prod.user_subscription_stripe_orders
  WHERE status = 'ORDER_STATUS_SUCCESS'
    AND amount >= 0
    AND created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'

  UNION ALL

  SELECT
    user_id,
    CASE
      WHEN biz_type = 'ENERGY' AND paypal_price_id = '500' THEN 6.99
      WHEN biz_type = 'ENERGY' AND paypal_price_id = '2000' THEN 20.99
      ELSE amount
    END as amount,
    created_date,
    'paypal' as source
  FROM my_shell_prod.user_subscription_paypal_orders
  WHERE status = 'ORDER_STATUS_SUCCESS'
    AND created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'

  UNION ALL

  SELECT
    user_id,
    CASE product_id
      WHEN 'PLAYER_MONTHLY' THEN 6.99
      WHEN 'PLAYER_YEARLY' THEN 58.99
      WHEN 'DEVELOPER_MONTHLY' THEN 59.99
      WHEN 'DEVELOPER_YEARLY' THEN 499.99
      WHEN '33OFF_DEVELOPER_MONTHLY' THEN 39.99
      WHEN 'energy_500' THEN 6.99
      WHEN 'energy_2000' THEN 20.99
      ELSE 0
    END as amount,
    created_date,
    'apple' as source
  FROM my_shell_prod.apple_used_transactions
  WHERE created_date >= '{start_date} 00:00:00'
    AND created_date <= '{end_date} 23:59:59'
),

tasks AS (
  SELECT
    user_id,
    slug_id,
    created_date
  FROM my_shell_prod.art_task
  WHERE status = 'done'
    AND created_date >= DATE_SUB('{start_date}', INTERVAL 7 DAY)
    AND created_date <= DATE_ADD('{end_date}', INTERVAL 7 DAY)
),

-- 使用与Step 1相同的归因逻辑
-- ... (last_touch_before, first_touch_after, attributed)

attributed AS (
  -- ... (省略,使用Step 1的归因结果)
)

-- 统计未归因订单
SELECT
  DATE(o.created_date) as order_date,
  COUNT(*) as unattributed_orders,
  ROUND(SUM(o.amount), 2) as unattributed_revenue,
  -- 分类统计
  COUNT(CASE WHEN NOT EXISTS (
    SELECT 1 FROM my_shell_prod.art_task t
    WHERE t.user_id = o.user_id
      AND t.status = 'done'
  ) THEN 1 END) as never_used_orders,
  ROUND(SUM(CASE WHEN NOT EXISTS (
    SELECT 1 FROM my_shell_prod.art_task t
    WHERE t.user_id = o.user_id
      AND t.status = 'done'
  ) THEN o.amount ELSE 0 END), 2) as never_used_revenue
FROM orders o
WHERE NOT EXISTS (
  SELECT 1 FROM attributed a
  WHERE a.user_id = o.user_id
    AND a.order_date = o.created_date
)
GROUP BY DATE(o.created_date)
ORDER BY order_date;
```

### 未归因订单详情

```sql
-- 列出具体的未归因订单(用于分析和调试)
SELECT
  o.source,
  o.user_id,
  ROUND(o.amount, 2) as amount,
  o.created_date as order_time,
  -- 判断原因
  CASE
    WHEN NOT EXISTS (
      SELECT 1 FROM my_shell_prod.art_task t
      WHERE t.user_id = o.user_id
        AND t.status = 'done'
    ) THEN '从未使用'
    ELSE '窗口外使用'
  END as reason,
  -- 用户历史任务数
  (SELECT COUNT(*) FROM my_shell_prod.art_task t
   WHERE t.user_id = o.user_id
     AND t.status = 'done') as total_tasks
FROM orders o
WHERE NOT EXISTS (
  SELECT 1 FROM attributed a
  WHERE a.user_id = o.user_id
    AND a.order_date = o.created_date
)
ORDER BY o.amount DESC, o.created_date
LIMIT 100;
```

---

## 输出格式

### CSV 格式

```csv
Bot ID,Bot名称,Slug ID,收入,成本,毛利润,毛利率(%)
12345,Ghibli Diffusion,ghibli-diffusion,1250.50,800.00,450.50,36.02
67890,Flux Dev,flux-dev,890.30,600.00,290.30,32.61
11111,SDXL Lightning,sdxl-lightning,750.00,550.00,200.00,26.67
22222,Anime Diffusion,anime-diffusion,650.00,400.00,250.00,38.46
33333,Portrait Master,portrait-master,500.00,450.00,50.00,10.00
44444,Free Bot,free-bot,0.00,300.00,-300.00,-100.00
```

### 汇总指标

```
分析时间范围: {start_date} 至 {end_date}

======================== 收入归因情况 ========================
总订单收入: ${total_order_revenue}
  - 已归因收入: ${total_attributed_revenue} ({coverage_pct}%)
  - 未归因收入: ${total_unattributed_revenue} ({unattributed_pct}%)

验证公式: 总订单收入 = 已归因收入 + 未归因收入
验证结果: ${total_order_revenue} = ${total_attributed_revenue} + ${total_unattributed_revenue} ✓

未归因订单详情:
  - 从未使用: {never_used_orders}个订单, ${never_used_revenue}
  - 窗口外使用: {outside_window_orders}个订单, ${outside_window_revenue}

======================== 毛利率分析 ========================
总成本: ${total_cost}
毛利润(基于归因收入): ${gross_profit}
整体毛利率(基于归因收入): {overall_margin}%

Top 5 高毛利率 Bot (毛利率 > 50%):
1. Bot A: 收入 $1,200, 成本 $400, 毛利率 66.67%
2. Bot B: 收入 $800, 成本 $300, 毛利率 62.50%
3. Bot C: 收入 $600, 成本 $250, 毛利率 58.33%
4. Bot D: 收入 $500, 成本 $200, 毛利率 60.00%
5. Bot E: 收入 $400, 成本 $180, 毛利率 55.00%

Bottom 5 低毛利率 Bot (毛利率 < 20%):
1. Bot X: 收入 $500, 成本 $450, 毛利率 10.00%
2. Bot Y: 收入 $300, 成本 $280, 毛利率 6.67%
3. Bot Z: 收入 $200, 成本 $180, 毛利率 10.00%

负毛利率 Bot (毛利率 < 0,需要优化或下线):
1. Free Bot: 收入 $0, 成本 $300, 毛利率 -100.00%
2. Bot W: 收入 $100, 成本 $150, 毛利率 -50.00%
```

---

## 关键注意事项

1. **Apple IAP 收入**: 按原价计算,**未计算折扣码优惠**,实际收入可能更低

2. **任务状态差异**:
   - **归因模型**: 使用 `status = 'done'` 的任务(只统计完成的任务)
   - **成本计算**: 使用 `status IN ('done', 'cancel')` 的任务(包括取消的任务)
   - **差异原因**: 取消的任务产生成本但不影响用户付费决策

3. **时区**: 数据库使用 `Asia/Shanghai` 时区,确保日期范围一致

4. **无收入 Bot**: 免费用户使用的 bot 会有成本但无收入,毛利率为 -100%

5. **归因逻辑**:
   - 优先归因给订单前最后使用的 bot(关键决策触点)
   - 如果订单前无使用记录,归因给订单后第一次使用的 bot(新用户先付费场景)
   - 前后都没有使用记录的订单会被忽略

6. **未归因订单(正常现象)**:
   - **预期未归因率**: 20-30%
   - **主要原因**:
     - 从未使用: 用户付费但从未使用任何bot(占未归因的90-95%)
     - 窗口外使用: 用户最后使用时间超过±7天窗口(占未归因的5-10%)
   - **验证方法**: 使用Step 4的查询验证 `总订单收入 = 已归因收入 + 未归因收入`
   - **异常情况**: 如果未归因率 > 40%,需要检查:
     - 时间窗口设置是否合理
     - 任务数据是否完整
     - 是否有大量新用户注册但未使用

7. **归因覆盖率**:
   - **目标覆盖率**: 70-80%
   - **低于65%**: 需要检查数据质量或扩展时间窗口
   - **高于85%**: 窗口可能过大,存在误归因风险

---

## 性能对比总结

### 优化前 vs 优化后

| 指标 | 优化前(JavaScript归因) | 优化后(SQL归因) | 提升 |
|------|------------------------|------------------|------|
| **查询次数** | 4次(3个订单表 + 1个任务表) | 1次(CTE合并) | **减少75%** |
| **数据传输** | ~100万行任务数据(~200MB) | ~500行bot汇总(~50KB) | **减少99.9%** |
| **算法复杂度** | O(订单数 × 任务数) ≈ O(n²) | O(n log n) | **大幅优化** |
| **执行时间** | 60-180秒 | 15-45秒 | **快3-10倍** |
| **内存占用** | ~200-500MB | ~10MB | **减少95%** |

### 核心优化点

1. ✅ **数据库层归因**: 避免传输百万级原始数据到应用层
2. ✅ **CTE优化**: 单个查询完成订单合并和归因计算
3. ✅ **算法优化**: 从 O(n²) 嵌套循环降到 O(n log n)
4. ✅ **内存优化**: 只在应用层处理汇总后的结果集
5. ✅ **网络优化**: 数据传输量减少 99.9%+

---

## 与现有脚本的关系

- **gross-margin-analysis.md**: 整体业务毛利率分析(收入来源)
- **本文档**: Bot 级别毛利率分析,通过归因模型分配收入到各个 bot

---

## 常见问题 FAQ

**Q1: 为什么不在应用层做归因?**
A: 数据量太大。传输100万行任务数据需要几十秒,且应用层归因算法复杂度高(O(n²))。数据库层归因直接在数据源计算,减少99.9%的数据传输,性能提升3-10倍。

**Q2: 查询会很慢吗(没有索引的情况下)?**
A: 虽然没有专门的索引,但优化后的方案仍然比原方案快很多,因为:
- 减少了4次查询到1次
- 避免传输百万级数据到应用层
- 数据库会使用表的主键索引
- 预计执行时间15-45秒,比原方案的60-180秒快3-10倍

**Q3: DISTINCT ON 和 GROUP BY 有什么区别?**
A: DISTINCT ON 可以按指定列分组,同时保留完整行(如订单金额)。GROUP BY 需要聚合函数,不适合我们的归因场景。

**Q4: 为什么要分 before 和 after 两种归因?**
A: 因为存在"新用户先付费再使用"的情况。如果只用 before 归因,这部分订单会被丢失。

**Q5: 如何验证归因结果的正确性?**
A: 使用"归因覆盖率验证"查询(见Step 1末尾),检查归因覆盖率。正常应该在80-95%之间。如果过低,需要调整归因模型。

**Q6: 可以按天或按周运行吗?**
A: 可以。建议按周或按月运行。数据量越大,SQL归因相比应用层归因的性能优势越明显。

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.