npm package: @skywu/ad-manager-sdk
Description: A powerful ad task management SDK with multi-language support (40 languages), multi-theme system (4 themes), currency localization (50+ currencies), and dynamic task loading
Author: Ad Manager Platform
Latest version: 1.9.2

# 🎯 Ad Manager SDK

> 一个功能强大的广告任务管理 SDK，让您的应用轻松集成广告任务系统，支持多语言、多主题和货币本地化。

[![npm version](https://img.shields.io/npm/v/@skywu/ad-manager-sdk.svg)](https://www.npmjs.com/package/@skywu/ad-manager-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## �� 目录

- [核心特性](#-核心特性)
- [安装](#-安装)
- [快速开始](#-快速开始)
- [完整集成示例](#-完整集成示例)
- [API 文档](#-api-文档)
- [事件回调详解](#-事件回调详解)
- [后端集成示例](#-后端集成示例)
- [多语言支持](#-多语言支持)
- [主题定制](#-主题定制)
- [常见问题](#-常见问题)

---

## ✨ 核心特性

### 🌍 多语言支持（40 种语言）
- ✅ 自动翻译广告内容（标题、描述）
- ✅ UI 文案本地化
- ✅ 支持 40 种语言：中文、英语、日语、韩语、西班牙语、法语、德语、阿拉伯语等
- ✅ 基于 OpenAI 的高质量翻译

### 🎨 多主题系统（4 种主题）
- **MODERN**：现代简约风格（默认）
- **MINIMAL**：极简主义风格
- **COLORFUL**：多彩活力风格
- **DARK**：暗黑科技风格

### 💰 货币本地化（50+ 种货币）
- ✅ 自动检测用户国家
- ✅ 显示本地货币（如：¥700、$100、€90）
- ✅ 实时汇率转换
- ✅ 回调数据统一为 USD

### 📱 广告网络集成
- **Monetag**：Rewarded Interstitial、Direct Link、Smart Link
- **Monetizer**：Rewarded Video
- 支持自定义广告网络

### 🔄 智能任务管理
- ✅ 自动轮询任务状态
- ✅ 页面关闭后恢复弹窗
- ✅ 已完成任务持久化（24 小时）
- ✅ 防止重复完成

---

## 📦 安装

### NPM
\`\`\`bash
npm install @skywu/ad-manager-sdk
\`\`\`

### Yarn
\`\`\`bash
yarn add @skywu/ad-manager-sdk
\`\`\`

### PNPM
\`\`\`bash
pnpm add @skywu/ad-manager-sdk
\`\`\`

---

## 🚀 快速开始

### 1. Vue 3 应用

\`\`\`vue
<template>
  <div>
    <AdTaskList
      api-key="your-api-key"
      end-user-id="user-123"
      locale="zh-CN"
      country="CN"
      @reward="handleReward"
    />
  </div>
</template>

<script setup>
import { AdTaskList } from '@skywu/ad-manager-sdk'
import '@skywu/ad-manager-sdk/style.css'

async function handleReward(event) {
  console.log('用户获得奖励:', event)
  
  // 调用后端 API 发放奖励
  await fetch('/api/user/reward', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      userId: event.endUserId,
      taskId: event.taskId,
      adId: event.adConfigId,
      amount: event.reward,
      adTitle: event.adTitle,
      adNetwork: event.adNetwork,
      completedAt: event.completedAt
    })
  })
}
</script>
\`\`\`

---

### 2. Nuxt 3 应用

**步骤 1：创建插件** \`plugins/ad-manager.client.ts\`

\`\`\`typescript
import { AdTaskList } from '@skywu/ad-manager-sdk'
import '@skywu/ad-manager-sdk/style.css'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component('AdTaskList', AdTaskList)
})
\`\`\`

**步骤 2：使用组件**

\`\`\`vue
<template>
  <ClientOnly>
    <AdTaskList
      :api-key="apiKey"
      :end-user-id="userId"
      locale="zh-CN"
      @reward="handleReward"
    />
  </ClientOnly>
</template>

<script setup>
const apiKey = 'your-api-key'
const userId = 'user-123'

async function handleReward(event) {
  await $fetch('/api/user/reward', {
    method: 'POST',
    body: event
  })
  alert(\`恭喜！您获得了 $\${event.reward} 奖励\`)
}
</script>
\`\`\`

---

## 💡 完整集成示例

### Telegram 小程序集成（带全局任务悬浮按钮）

\`\`\`vue
<template>
  <div class="telegram-app">
    <h1>我的积分：{{ userPoints }}</h1>

    <!-- 广告任务列表 -->
    <AdTaskList
      :api-key="apiKey"
      :end-user-id="telegramUserId"
      :end-user-registered-at="userRegisteredAt"
      locale="zh-CN"
      country="CN"
      @reward="handleReward"
    />

    <!-- 全局任务悬浮按钮 -->
    <GlobalTasksIcon
      ref="globalTasksIconRef"
      @click="showGlobalTasks = true"
    />

    <!-- 全局任务弹窗 -->
    <GlobalTasksModal
      :visible="showGlobalTasks"
      :api-key="apiKey"
      :end-user-id="telegramUserId"
      :country="country"
      :mini-app-bot-username="miniAppBotUsername"
      @reward="handleReward"
      @all-completed="handleAllCompleted"
      @close="showGlobalTasks = false"
    />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { AdTaskList, GlobalTasksIcon, GlobalTasksModal } from '@skywu/ad-manager-sdk'
import '@skywu/ad-manager-sdk/style.css'

const apiKey = 'your-api-key'
const telegramUserId = ref('')
const userRegisteredAt = ref('')
const userPoints = ref(0)
const country = ref('CN')
const miniAppBotUsername = 'your_mini_app_bot'
const showGlobalTasks = ref(false)
const globalTasksIconRef = ref(null)

onMounted(async () => {
  // 获取 Telegram 用户信息
  const tg = window.Telegram?.WebApp
  if (tg) {
    tg.ready()
    telegramUserId.value = tg.initDataUnsafe?.user?.id?.toString() || 'guest'
    country.value = tg.initDataUnsafe?.user?.language_code?.toUpperCase() || 'CN'
  }
  
  // 加载用户数据
  await loadUserData()
})

async function loadUserData() {
  const response = await fetch(\`/api/user/\${telegramUserId.value}\`)
  const data = await response.json()
  userPoints.value = data.points
  userRegisteredAt.value = data.createdAt
}

async function handleReward(event) {
  try {
    const response = await fetch('/api/user/reward', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        userId: event.endUserId,
        taskId: event.taskId,
        adConfigId: event.adConfigId,
        amount: event.reward,
        adTitle: event.adTitle,
        adNetwork: event.adNetwork,
        taskType: event.taskType, // 'AD_TASK' | 'SUBSCRIBE_BOT' | 'JOIN_CHANNEL'
        completedAt: event.completedAt
      })
    })

    const data = await response.json()

    if (data.success) {
      userPoints.value = data.newPoints
      window.Telegram?.WebApp?.showAlert(
        \`恭喜！您获得了 \${event.reward} 积分\`
      )
    }
  } catch (error) {
    console.error('发放奖励失败:', error)
  }
}

// 所有全局任务完成后，隐藏悬浮按钮
function handleAllCompleted() {
  globalTasksIconRef.value?.hide()
  window.Telegram?.WebApp?.showAlert('🎉 恭喜！您已完成所有全局任务！')
}
</script>
\`\`\`

**关键点：**
1. 使用 `ref` 获取 `GlobalTasksIcon` 组件实例
2. 监听 `GlobalTasksModal` 的 `@all-completed` 事件
3. 调用 `globalTasksIconRef.value?.hide()` 隐藏悬浮按钮
4. 悬浮按钮会自动保存状态到 `localStorage`，下次不再显示

\`\`\`

---

## 📖 API 文档

### Props

| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| \`apiKey\` | \`string\` | ✅ | - | 发布商 API Key（从后台获取） |
| \`endUserId\` | \`string\` | ✅ | - | 终端用户唯一标识 |
| \`endUserRegisteredAt\` | \`string\` | ❌ | - | 用户注册时间（ISO 8601 格式） |
| \`apiBaseUrl\` | \`string\` | ❌ | - | API 基础 URL（自定义服务器地址） |
| \`monetagSdkUrl\` | \`string\` | ❌ | - | Monetag SDK URL（自定义广告脚本） |
| \`locale\` | \`Locale\` | ❌ | \`'zh-CN'\` | 语言代码（见下方支持列表） |
| \`country\` | \`string\` | ❌ | - | 国家代码（ISO 3166-1 alpha-2） |
| \`theme\` | \`SdkTheme\` | ❌ | \`'MODERN'\` | UI 主题（MODERN/MINIMAL/COLORFUL/DARK） |
| \`debug\` | \`boolean\` | ❌ | \`false\` | 调试模式（开启后打印详细日志） |

---

## 🎉 事件回调详解

### \`@reward\` 事件

当用户成功完成任务并获得奖励时触发。

#### 事件对象结构

\`\`\`typescript
interface RewardEvent {
  taskId: string          // 任务 ID
  adConfigId: string      // 广告配置 ID（即 adId）
  endUserId: string       // 终端用户 ID
  reward: number          // 奖励金额（USD，数字类型）
  adTitle: string         // 广告标题
  adNetwork: string       // 广告网络（MONETAG/MONETIZER）
  completedAt: string     // 完成时间（ISO 8601 格式）
}
\`\`\`

#### 示例数据

\`\`\`json
{
  "taskId": "550e8400-e29b-41d4-a716-446655440000",
  "adConfigId": "660e8400-e29b-41d4-a716-446655440001",
  "endUserId": "user-123",
  "reward": 10,
  "adTitle": "下载游戏赢大奖",
  "adNetwork": "MONETAG",
  "completedAt": "2025-10-08T10:30:00.000Z"
}
\`\`\`

---

## 🔧 后端集成示例

### Node.js + Express

\`\`\`javascript
app.post('/api/user/reward', async (req, res) => {
  const { userId, taskId, adConfigId, amount, completedAt } = req.body
  
  try {
    // 1. 验证任务是否已发放（防止重复）
    const existingReward = await db.rewards.findOne({ taskId })
    if (existingReward) {
      return res.status(400).json({ error: '奖励已发放' })
    }
    
    // 2. 记录奖励
    await db.rewards.create({
      userId,
      taskId,
      adConfigId,
      amount,
      completedAt,
      createdAt: new Date()
    })
    
    // 3. 更新用户余额
    await db.users.update(
      { id: userId },
      { $inc: { balance: amount } }
    )
    
    // 4. 获取新余额
    const user = await db.users.findOne({ id: userId })
    
    res.json({
      success: true,
      newBalance: user.balance
    })
  } catch (error) {
    console.error('发放奖励失败:', error)
    res.status(500).json({ error: '服务器错误' })
  }
})
\`\`\`

### Python + FastAPI

\`\`\`python
@app.post("/api/user/reward")
async def give_reward(reward_data: RewardData):
    # 1. 验证任务是否已发放
    existing = await db.rewards.find_one({"task_id": reward_data.task_id})
    if existing:
        raise HTTPException(status_code=400, detail="奖励已发放")
    
    # 2. 记录奖励
    await db.rewards.insert_one({
        "user_id": reward_data.user_id,
        "task_id": reward_data.task_id,
        "ad_config_id": reward_data.ad_config_id,
        "amount": reward_data.amount,
        "completed_at": reward_data.completed_at,
        "created_at": datetime.now()
    })
    
    # 3. 更新用户余额
    await db.users.update_one(
        {"id": reward_data.user_id},
        {"$inc": {"balance": reward_data.amount}}
    )
    
    # 4. 返回新余额
    user = await db.users.find_one({"id": reward_data.user_id})
    return {"success": True, "new_balance": user["balance"]}
\`\`\`

### PHP + Laravel

\`\`\`php
Route::post('/api/user/reward', function (Request $request) {
    $validated = $request->validate([
        'userId' => 'required|string',
        'taskId' => 'required|string',
        'adConfigId' => 'required|string',
        'amount' => 'required|numeric',
        'completedAt' => 'required|date'
    ]);
    
    // 1. 验证任务是否已发放
    $existing = Reward::where('task_id', $validated['taskId'])->first();
    if ($existing) {
        return response()->json(['error' => '奖励已发放'], 400);
    }
    
    // 2. 记录奖励
    Reward::create([
        'user_id' => $validated['userId'],
        'task_id' => $validated['taskId'],
        'ad_config_id' => $validated['adConfigId'],
        'amount' => $validated['amount'],
        'completed_at' => $validated['completedAt']
    ]);
    
    // 3. 更新用户余额
    $user = User::find($validated['userId']);
    $user->balance += $validated['amount'];
    $user->save();
    
    return response()->json([
        'success' => true,
        'new_balance' => $user->balance
    ]);
});
\`\`\`

---

## 🌍 多语言支持

### 支持的语言列表

| 语言代码 | 语言名称 | 语言代码 | 语言名称 |
|---------|---------|---------|---------|
| zh-CN | 简体中文 | en-US | 英语 |
| es-ES | 西班牙语 | hi-IN | 印地语 |
| ar-SA | 阿拉伯语 | pt-BR | 葡萄牙语 |
| bn-BD | 孟加拉语 | ru-RU | 俄语 |
| ja-JP | 日语 | pa-IN | 旁遮普语 |
| de-DE | 德语 | jv-ID | 爪哇语 |
| ko-KR | 韩语 | fr-FR | 法语 |
| te-IN | 泰卢固语 | mr-IN | 马拉地语 |
| tr-TR | 土耳其语 | ta-IN | 泰米尔语 |
| vi-VN | 越南语 | ur-PK | 乌尔都语 |
| it-IT | 意大利语 | th-TH | 泰语 |
| gu-IN | 古吉拉特语 | pl-PL | 波兰语 |
| uk-UA | 乌克兰语 | ml-IN | 马拉雅拉姆语 |
| kn-IN | 卡纳达语 | or-IN | 奥里亚语 |
| my-MM | 缅甸语 | fa-IR | 波斯语 |
| nl-NL | 荷兰语 | ro-RO | 罗马尼亚语 |
| id-ID | 印尼语 | cs-CZ | 捷克语 |
| el-GR | 希腊语 | sv-SE | 瑞典语 |
| hu-HU | 匈牙利语 | he-IL | 希伯来语 |
| fi-FI | 芬兰语 | zh-TW | 繁体中文 |

---

## 🎨 主题定制

### MODERN（现代风格）
\`\`\`vue
<AdTaskList theme="MODERN" />
\`\`\`
- 渐变色背景
- 圆角卡片
- 柔和阴影
- 适合：现代应用

### MINIMAL（极简风格）
\`\`\`vue
<AdTaskList theme="MINIMAL" />
\`\`\`
- 扁平设计
- 黑白灰配色
- 细线条
- 适合：专业应用

### COLORFUL（多彩风格）
\`\`\`vue
<AdTaskList theme="COLORFUL" />
\`\`\`
- 鲜艳色彩
- 卡片式布局
- 活力设计
- 适合：游戏应用

### DARK（暗黑风格）
\`\`\`vue
<AdTaskList theme="DARK" />
\`\`\`
- 深色背景
- 霓虹色高亮
- 科技感
- 适合：夜间模式

---

## ❓ 常见问题

### 1. 如何获取 API Key？

登录发布商后台 → SDK 设置 → 复制 API Key

### 2. reward 事件中的金额是什么货币？

始终是 **USD（美元）**，数字类型。SDK 显示的是本地货币，但回调数据统一为 USD。

### 3. 如何防止重复发放奖励？

在后端使用 \`taskId\` 作为唯一标识，发放前检查是否已存在。

### 4. 用户关闭页面后奖励会丢失吗？

不会。SDK 会将待显示的弹窗保存到 localStorage，重新打开页面时自动恢复。

### 5. 如何自定义样式？

可以通过 CSS 覆盖默认样式，或使用 4 种内置主题。

### 6. 支持哪些广告网络？

目前支持 Monetag 和 Monetizer，未来会支持更多广告网络。

### 7. 如何调试？

设置 \`:debug="true"\` 开启调试模式，控制台会打印详细日志。

---

## 📄 License

MIT © [Ad Manager Platform](https://github.com/ad-manager/sdk)

---

## 🤝 支持

- 📧 Email: support@ad-manager.com
- 💬 Discord: [Join our community](https://discord.gg/ad-manager)
- 📖 Documentation: [Full Docs](https://docs.ad-manager.com)

---

**Made with ❤️ by Ad Manager Team**