一个通用的投流管理后台 Laravel 包,支持投手账号管理、数据概览、报表、投流链接管理等功能。采用多态关联设计,可适配任何业务场景(短剧、电商、游戏、教育等)。
- 📊 投手账号管理 - 创建、编辑、删除、启用/禁用投手账号
- 🔗 投流链接管理 - 管理投流链接,支持多平台(Facebook/TikTok/Google)
- 📈 数据概览 - 实时数据统计、今日/昨日对比、趋势图
- 📋 多维度报表 - 日报表、月报表、业绩报表、链接报表
- 👥 新进用户追踪 - 追踪投手带来的新用户及其充值情况
- 🎯 多态关联 - 支持关联任意产品模型(视频、商品、课程等)
- 🔔 事件系统 - 新用户注册、订单支付等事件可监听
- 将包放在项目同级目录:
your-workspace/
├── api-v1/ # 你的 Laravel 项目
└── agent/ # xditn/agent 包
- 在项目的
composer.json中添加本地仓库:
{
"repositories": [
{
"type": "path",
"url": "../agent",
"options": {
"symlink": true
}
}
],
"require": {
"xditn/agent": "*"
}
}- 执行安装:
composer update
php artisan agent:installcomposer require xditn/agent
php artisan agent:install配置文件位置:config/agent.php
return [
// 数据库表前缀(可选)
'table_prefix' => env('AGENT_TABLE_PREFIX', ''),
// 路由配置
'route' => [
'prefix' => env('AGENT_ROUTE_PREFIX', 'api/agent'), // API 前缀
'middleware' => ['api'], // 中间件
'enabled' => true, // 是否启用路由
],
// 投手默认密码
'default_password' => env('AGENT_DEFAULT_PASSWORD', '123456'),
];'models' => [
// 管理员模型(创建投手的管理员)
'admin' => 'Modules\\User\\Models\\User',
// 会员模型(投流带来的用户)
'member' => 'Modules\\Member\\Models\\Member',
// 订单模型(用于统计充值)
'order' => 'Modules\\Pay\\Models\\Order',
// 可推广的产品模型(多态关联)
'promotable' => [
'video' => 'Modules\\VideoSubscription\\Models\\Video', // 短剧
// 'product' => 'Modules\\Shop\\Models\\Product', // 商品
// 'course' => 'Modules\\Course\\Models\\Course', // 课程
],
// 充值包模型(多态关联)
'packages' => [
'recharge' => 'Modules\\Pay\\Models\\RechargeActivity', // 充值活动
// 'subscription' => 'Modules\\Pay\\Models\\SubscriptionPlan', // 订阅套餐
],
],// 广告平台
'platforms' => [
'facebook' => ['name' => 'Facebook', 'enabled' => true],
'tiktok' => ['name' => 'TikTok', 'enabled' => true],
'google' => ['name' => 'Google', 'enabled' => true],
],
// 投放方式
'delivery_types' => [
'knife' => '打刀',
'normal' => '常规',
],
// 应用平台
'app_platforms' => [
'h5_direct' => 'H5直投',
'pwa' => 'PWA',
'app' => 'APP',
],
// 归因平台
'attribution_platforms' => [
'facebook' => 'Facebook',
'appsflyer' => 'AppsFlyer',
'adjust' => 'Adjust',
'branch' => 'Branch',
],| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /agents |
投手列表(支持分页、搜索) |
| POST | /agents |
创建投手 |
| GET | /agents/{id} |
投手详情 |
| PUT | /agents/{id} |
更新投手 |
| DELETE | /agents/{id} |
删除投手 |
| DELETE | /agents |
批量删除(body: {ids: [1,2,3]}) |
| PUT | /agents/{id}/toggle |
启用/禁用投手 |
| PUT | /agents/{id}/reset-password |
重置密码 |
创建/更新投手参数:
{
"name": "投手名称",
"account": "登录账号",
"password": "密码(可选)",
"avatar": "头像URL",
"mobile": "手机号",
"email": "邮箱",
"remark": "备注",
"status": 1
}| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /links |
链接列表 |
| POST | /links |
创建链接 |
| GET | /links/{id} |
链接详情 |
| PUT | /links/{id} |
更新链接 |
| DELETE | /links/{id} |
删除链接 |
| DELETE | /links |
批量删除 |
| PUT | /links/{id}/toggle |
启用/禁用链接 |
| POST | /links/{id}/copy |
复制链接 |
| GET | /links/by-agent/{agentId} |
获取指定投手的链接 |
创建/更新链接参数:
{
"agent_id": 1,
"name": "链接名称",
"platform": "facebook",
"ad_account": "广告账户",
"pixel_id": "像素ID",
"attribution_platform": "facebook",
"pwa_link": "PWA链接",
"language": "en",
"delivery_type": "knife",
"delivery_product": "H5",
"app_platform": "h5_direct",
"app_product": "应用产品名称",
"promotable_type": "Modules\\VideoSubscription\\Models\\Video",
"promotable_id": 123,
"promotable_name": "小姨子的诱惑",
"first_package_type": "Modules\\Pay\\Models\\RechargeActivity",
"first_package_id": 1,
"second_package_type": "Modules\\Pay\\Models\\RechargeActivity",
"second_package_id": 2,
"extra_data": {
"coin_per_episode": 200,
"episode_count": 50,
"paid_episode_count": 5
},
"full_url": "https://xxx.com/content/xxx",
"link_remark": "备注",
"proxy_name": "代理商名称",
"material_name": "像素名称",
"status": 1
}| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /statistics/overview |
数据概览 |
| GET | /statistics/recharge-comparison |
今日/昨日充值对比 |
| GET | /statistics/top-countries |
前N国家地区充值排行 |
| GET | /statistics/new-user-comparison |
今日/昨日新用户对比 |
| GET | /statistics/trend |
趋势数据(近7天/30天) |
| GET | /statistics/cumulative |
累计数据 |
通用参数:
| 参数 | 说明 |
|---|---|
agent_id |
投手ID(可选,不传则统计全部) |
start_date |
开始日期(如 2024-01-01) |
end_date |
结束日期 |
days |
天数(趋势接口用) |
limit |
数量限制(排行接口用) |
overview 响应示例:
{
"code": 0,
"message": "success",
"data": {
"total_orders": 1234,
"total_recharge": 12345.67,
"success_rate": 85.5,
"yesterday_recharge": 1234.56,
"today": {
"orders": 123,
"recharge": 1234.56,
"new_users": 456
},
"breakdown": {
"google_recharge": 5000.00,
"ios_recharge": 3000.00,
"aggregate_recharge": 4345.67
}
}
}| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /reports/performance |
业绩报表 |
| GET | /reports/daily |
日报表 |
| GET | /reports/monthly |
月报表 |
| GET | /reports/link |
链接报表 |
日报表参数:
| 参数 | 说明 |
|---|---|
agent_id |
投手ID |
agent_name |
投手名称(模糊搜索) |
start_date |
开始日期 |
end_date |
结束日期 |
page |
页码 |
limit |
每页数量 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /new-users |
新进用户列表 |
| GET | /new-users/{id} |
用户详情 |
| PUT | /new-users/{id}/callback-status |
更新回传状态 |
| PUT | /new-users/batch/callback-status |
批量更新回传状态 |
列表参数:
| 参数 | 说明 |
|---|---|
agent_id |
投手ID |
link_id |
链接ID |
new_id |
新进ID(模糊搜索) |
country |
国家地区 |
system |
系统(h5/ios/android) |
callback_status |
回传状态 |
start_time |
开始时间 |
end_time |
结束时间 |
产品模型实现 PromotableInterface:
<?php
namespace Modules\VideoSubscription\Models;
use Illuminate\Database\Eloquent\Model;
use Xditn\Agent\Contracts\PromotableInterface;
class Video extends Model implements PromotableInterface
{
/**
* 获取产品名称(用于链接显示)
*/
public function getPromotableName(): string
{
return $this->title;
}
/**
* 获取产品扩展数据(存储到 extra_data)
*/
public function getPromotableExtraData(): array
{
return [
'video_id' => $this->id,
'video_name_cn' => $this->title_cn,
'episode_count' => $this->episode_count,
'coin_per_episode' => $this->coin_per_episode,
'paid_episode_count' => $this->paid_episode_count,
];
}
}会员模型实现 MemberInterface:
<?php
namespace Modules\Member\Models;
use Illuminate\Database\Eloquent\Model;
use Xditn\Agent\Contracts\MemberInterface;
class Member extends Model implements MemberInterface
{
public function getMemberId(): int|string
{
return $this->id;
}
public function getRegisteredAt(): int
{
return $this->created_at instanceof \Carbon\Carbon
? $this->created_at->timestamp
: (int) $this->created_at;
}
public function getMemberExtraData(): array
{
return [
'nickname' => $this->nickname,
'avatar' => $this->avatar,
'vip_level' => $this->vip_level,
];
}
}订单模型实现 OrderInterface:
<?php
namespace Modules\Pay\Models;
use Illuminate\Database\Eloquent\Model;
use Xditn\Agent\Contracts\OrderInterface;
class Order extends Model implements OrderInterface
{
public function getOrderAmount(): float
{
return (float) $this->amount;
}
public function getPaymentChannel(): string
{
// 返回 google / ios / aggregate
return match($this->payment_method) {
'google_pay' => 'google',
'apple_pay' => 'ios',
default => 'aggregate',
};
}
public function getOrderMemberId(): int|string
{
return $this->member_id;
}
public function getPaidAt(): ?int
{
return $this->paid_at;
}
public function isPaid(): bool
{
return $this->status === 'paid';
}
}在用户注册时记录投手信息:
<?php
namespace App\Services;
use Xditn\Agent\Services\AgentTrackingService;
class MemberService
{
public function __construct(
protected AgentTrackingService $trackingService
) {}
public function register(array $data): Member
{
// 创建会员
$member = Member::create($data);
// 如果有投流链接参数,记录新用户
if ($shortCode = request()->input('ref')) {
$link = $this->trackingService->getLinkByShortCode($shortCode);
if ($link) {
$this->trackingService->trackNewUser(
$link->agent_id,
$link->id,
$member,
[
'new_id' => $data['device_id'] ?? null,
'register_id' => $member->id,
'ip' => request()->ip(),
'country' => $this->getCountryFromIp(request()->ip()),
'system' => request()->input('platform', 'h5'),
'ad_series_name' => request()->input('campaign'),
'ad_group_name' => request()->input('adset'),
'ad_name' => request()->input('ad'),
]
);
}
}
return $member;
}
}在订单支付成功时记录:
<?php
namespace App\Services;
use Xditn\Agent\Services\AgentTrackingService;
class PaymentService
{
public function __construct(
protected AgentTrackingService $trackingService
) {}
public function handlePaymentSuccess(Order $order): void
{
// 获取会员关联的投手信息
$agentNewUser = AgentNewUser::where('member_id', $order->member_id)->first();
if ($agentNewUser) {
$this->trackingService->trackOrder(
$agentNewUser->agent_id,
$agentNewUser->link_id,
$order
);
}
}
}<?php
// EventServiceProvider.php
protected $listen = [
\Xditn\Agent\Events\NewUserRegistered::class => [
\App\Listeners\HandleNewUserRegistered::class,
],
\Xditn\Agent\Events\OrderPaid::class => [
\App\Listeners\HandleAgentOrderPaid::class,
],
];<?php
namespace App\Listeners;
use Xditn\Agent\Events\NewUserRegistered;
class HandleNewUserRegistered
{
public function handle(NewUserRegistered $event): void
{
$newUser = $event->newUser;
$agentId = $event->agentId;
$linkId = $event->linkId;
// 发送 Facebook Pixel 事件
// 发送通知等
}
}php artisan agent:install [--force]# 同步昨天的数据
php artisan agent:sync-statistics
# 同步指定日期
php artisan agent:sync-statistics --date=2024-01-01
# 同步最近7天
php artisan agent:sync-statistics --days=7
# 同步指定投手
php artisan agent:sync-statistics --agent=1// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
// 每天凌晨1点同步前一天的统计数据
$schedule->command('agent:sync-statistics')->dailyAt('01:00');
}| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| name | varchar(50) | 投手名称 |
| account | varchar(100) | 登录账号 |
| password | varchar | 密码 |
| avatar | varchar | 头像 |
| mobile | varchar(20) | 手机号 |
| varchar(100) | 邮箱 | |
| remark | varchar(500) | 备注 |
| status | tinyint | 状态 1-启用 0-禁用 |
| creator_id | bigint | 创建人ID |
| created_at | int | 创建时间 |
| updated_at | int | 更新时间 |
| deleted_at | int | 删除时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| agent_id | bigint | 投手ID |
| name | varchar(200) | 链接名称 |
| platform | varchar(50) | 广告平台 |
| promotable_type | varchar(200) | 推广产品类型(多态) |
| promotable_id | bigint | 推广产品ID |
| promotable_name | varchar(200) | 推广产品名称 |
| extra_data | json | 扩展数据 |
| short_code | varchar(50) | 短链接码 |
| ... | ... | 其他字段 |
| 字段 | 类型 | 说明 |
|---|---|---|
| agent_id | bigint | 投手ID |
| date | date | 日期 |
| new_users | int | 新用户数 |
| paying_users | int | 付费用户数 |
| google_recharge | decimal | Google充值 |
| ios_recharge | decimal | iOS充值 |
| aggregate_recharge | decimal | 聚合充值 |
| total_recharge | decimal | 总充值 |
| order_count | int | 订单数 |
| success_rate | decimal | 成功率 |
| new_user_pay_rate | decimal | 新用户付费率 |
| new_user_arpu | decimal | ARPU |
| rpd | decimal | RPD |
| 字段 | 类型 | 说明 |
|---|---|---|
| agent_id | bigint | 投手ID |
| link_id | bigint | 链接ID |
| member_type | varchar(200) | 会员类型(多态) |
| member_id | bigint | 会员ID |
| new_id | varchar(100) | 新进ID |
| register_id | varchar(100) | 注册ID |
| ip | varchar(50) | IP地址 |
| country | varchar(50) | 国家 |
| system | varchar(50) | 系统 |
| recent_recharge | decimal | 最近充值 |
| callback_status | varchar(50) | 回传状态 |
| extra_data | json | 扩展数据 |
| registered_at | int | 注册时间 |
extra_data 是 JSON 字段,用于存储不同业务场景的特殊数据:
短剧场景:
{
"coin_per_episode": 200,
"episode_count": 50,
"paid_episode_count": 5,
"video_name_cn": "小姨子的诱惑"
}电商场景:
{
"product_category": "服装",
"product_price": 99.00,
"sku_id": "SKU123456"
}游戏场景:
{
"game_server": "S1",
"game_level": 10,
"vip_level": 5
}MIT