xditn / agent
通用投流管理后台包 - 投手账号管理、数据概览、报表、投流链接管理
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/xditn/agent
Requires
- php: ^8.2
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
This package is auto-updated.
Last update: 2026-02-04 14:43:45 UTC
README
一个通用的投流管理后台 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:install
方式二:Composer 仓库
composer 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', ],
📡 API 接口文档
投手管理 /api/agent/agents
| 方法 | 路径 | 说明 |
|---|---|---|
| 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
}
投流链接管理 /api/agent/links
| 方法 | 路径 | 说明 |
|---|---|---|
| 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
}
数据统计 /api/agent/statistics
| 方法 | 路径 | 说明 |
|---|---|---|
| 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
}
}
}
报表 /api/agent/reports
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /reports/performance |
业绩报表 |
| GET | /reports/daily |
日报表 |
| GET | /reports/monthly |
月报表 |
| GET | /reports/link |
链接报表 |
日报表参数:
| 参数 | 说明 |
|---|---|
agent_id |
投手ID |
agent_name |
投手名称(模糊搜索) |
start_date |
开始日期 |
end_date |
结束日期 |
page |
页码 |
limit |
每页数量 |
新进用户 /api/agent/new-users
| 方法 | 路径 | 说明 |
|---|---|---|
| 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 |
结束时间 |
🔌 在项目中集成
1. 实现多态关联接口
产品模型实现 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'; } }
2. 追踪新用户注册
在用户注册时记录投手信息:
<?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; } }
3. 追踪订单支付
在订单支付成功时记录:
<?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 ); } } }
4. 监听事件
<?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'); }
📊 数据库表结构
agents - 投手表
| 字段 | 类型 | 说明 |
|---|---|---|
| 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 | 删除时间 |
agent_links - 投流链接表
| 字段 | 类型 | 说明 |
|---|---|---|
| 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_statistics_daily - 日统计表
| 字段 | 类型 | 说明 |
|---|---|---|
| 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_new_users - 新进用户表
| 字段 | 类型 | 说明 |
|---|---|---|
| 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)
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
}
📝 License
MIT