文章目录[隐藏]
网络传媒WordPress站点柔性广告库存管理教程
引言:为什么需要柔性广告库存管理?
在当今数字媒体环境中,网络传媒站点面临着广告收入最大化的持续挑战。传统的固定广告位管理方式已无法满足多样化的广告需求,特别是在应对季节性流量波动、突发新闻事件或特殊营销活动时。柔性广告库存管理系统允许站点管理员根据实时需求动态调整广告展示策略,从而提高填充率、优化用户体验并最大化广告收入。
本教程将指导您如何在WordPress站点上实现一个灵活、高效的广告库存管理系统,包含完整的代码实现和最佳实践建议。
一、系统架构设计
1.1 核心组件规划
我们的柔性广告库存管理系统将包含以下核心组件:
- 广告位管理:定义不同尺寸和位置的广告容器
- 库存控制:基于时间、流量和内容的动态分配逻辑
- 优先级系统:处理广告客户的不同优先级需求
- 数据分析:跟踪广告表现并优化库存分配
1.2 数据库设计
我们需要创建几个自定义数据库表来存储广告库存信息:
/**
* 创建广告库存管理所需的数据库表
* 这段代码应添加到插件激活钩子中
*/
function create_ad_inventory_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 广告位表
$ad_zones_table = $wpdb->prefix . 'ad_zones';
$ad_zones_sql = "CREATE TABLE IF NOT EXISTS $ad_zones_table (
zone_id mediumint(9) NOT NULL AUTO_INCREMENT,
zone_name varchar(100) NOT NULL,
zone_description text,
zone_size varchar(50) NOT NULL,
zone_location varchar(100) NOT NULL,
default_price decimal(10,2) DEFAULT 0.00,
max_daily_impressions int DEFAULT 10000,
is_active tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (zone_id)
) $charset_collate;";
// 广告库存表
$ad_inventory_table = $wpdb->prefix . 'ad_inventory';
$ad_inventory_sql = "CREATE TABLE IF NOT EXISTS $ad_inventory_table (
inventory_id mediumint(9) NOT NULL AUTO_INCREMENT,
zone_id mediumint(9) NOT NULL,
advertiser_id mediumint(9) NOT NULL,
start_date date NOT NULL,
end_date date NOT NULL,
allocated_impressions int NOT NULL,
delivered_impressions int DEFAULT 0,
priority_level tinyint(1) DEFAULT 3,
ad_code text NOT NULL,
is_active tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (inventory_id),
KEY zone_id (zone_id),
KEY date_range (start_date, end_date)
) $charset_collate;";
// 广告表现日志表
$ad_performance_table = $wpdb->prefix . 'ad_performance';
$ad_performance_sql = "CREATE TABLE IF NOT EXISTS $ad_performance_table (
log_id bigint(20) NOT NULL AUTO_INCREMENT,
inventory_id mediumint(9) NOT NULL,
impression_date date NOT NULL,
impression_time time NOT NULL,
page_url varchar(500) NOT NULL,
user_ip varchar(45),
user_agent text,
PRIMARY KEY (log_id),
KEY inventory_date (inventory_id, impression_date)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($ad_zones_sql);
dbDelta($ad_inventory_sql);
dbDelta($ad_performance_sql);
}
register_activation_hook(__FILE__, 'create_ad_inventory_tables');
二、广告位管理模块实现
2.1 创建广告位管理界面
/**
* 广告位管理类
* 处理广告位的CRUD操作
*/
class AdZoneManager {
/**
* 添加新广告位
* @param array $zone_data 广告位数据
* @return int|false 成功返回zone_id,失败返回false
*/
public function add_zone($zone_data) {
global $wpdb;
$table_name = $wpdb->prefix . 'ad_zones';
// 验证必需字段
$required_fields = ['zone_name', 'zone_size', 'zone_location'];
foreach ($required_fields as $field) {
if (empty($zone_data[$field])) {
return false;
}
}
// 设置默认值
$defaults = [
'zone_description' => '',
'default_price' => 0.00,
'max_daily_impressions' => 10000,
'is_active' => 1
];
$zone_data = wp_parse_args($zone_data, $defaults);
// 插入数据
$result = $wpdb->insert(
$table_name,
$zone_data,
['%s', '%s', '%s', '%s', '%f', '%d', '%d']
);
return $result ? $wpdb->insert_id : false;
}
/**
* 获取所有广告位
* @param bool $active_only 是否只获取活跃广告位
* @return array 广告位数组
*/
public function get_all_zones($active_only = true) {
global $wpdb;
$table_name = $wpdb->prefix . 'ad_zones';
$where_clause = $active_only ? "WHERE is_active = 1" : "";
$query = "SELECT * FROM $table_name $where_clause ORDER BY zone_location, zone_name";
return $wpdb->get_results($query, ARRAY_A);
}
/**
* 根据ID获取广告位
* @param int $zone_id 广告位ID
* @return array|null 广告位数据或null
*/
public function get_zone_by_id($zone_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'ad_zones';
$query = $wpdb->prepare(
"SELECT * FROM $table_name WHERE zone_id = %d",
$zone_id
);
return $wpdb->get_row($query, ARRAY_A);
}
}
/**
* 在WordPress后台创建广告位管理页面
*/
function ad_inventory_admin_menu() {
add_menu_page(
'广告库存管理', // 页面标题
'广告库存', // 菜单标题
'manage_options', // 权限要求
'ad-inventory', // 菜单slug
'ad_inventory_main_page', // 回调函数
'dashicons-money', // 图标
30 // 位置
);
add_submenu_page(
'ad-inventory',
'广告位管理',
'广告位',
'manage_options',
'ad-zones',
'ad_zones_management_page'
);
}
add_action('admin_menu', 'ad_inventory_admin_menu');
/**
* 广告位管理页面回调函数
*/
function ad_zones_management_page() {
$zone_manager = new AdZoneManager();
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_zone'])) {
$zone_data = [
'zone_name' => sanitize_text_field($_POST['zone_name']),
'zone_description' => sanitize_textarea_field($_POST['zone_description']),
'zone_size' => sanitize_text_field($_POST['zone_size']),
'zone_location' => sanitize_text_field($_POST['zone_location']),
'default_price' => floatval($_POST['default_price']),
'max_daily_impressions' => intval($_POST['max_daily_impressions'])
];
$result = $zone_manager->add_zone($zone_data);
if ($result) {
echo '<div class="notice notice-success"><p>广告位添加成功!</p></div>';
} else {
echo '<div class="notice notice-error"><p>广告位添加失败,请检查必填字段。</p></div>';
}
}
// 获取所有广告位
$zones = $zone_manager->get_all_zones(false);
// 显示管理界面
?>
<div class="wrap">
<h1>广告位管理</h1>
<div class="card">
<h2>添加新广告位</h2>
<form method="post" action="">
<table class="form-table">
<tr>
<th><label for="zone_name">广告位名称</label></th>
<td><input type="text" id="zone_name" name="zone_name" required class="regular-text"></td>
</tr>
<tr>
<th><label for="zone_description">描述</label></th>
<td><textarea id="zone_description" name="zone_description" rows="3" class="large-text"></textarea></td>
</tr>
<tr>
<th><label for="zone_size">广告尺寸</label></th>
<td>
<select id="zone_size" name="zone_size" required>
<option value="">选择尺寸</option>
<option value="300x250">中矩形 - 300x250</option>
<option value="728x90">横幅 - 728x90</option>
<option value="300x600">半页广告 - 300x600</option>
<option value="970x250">大横幅 - 970x250</option>
<option value="320x100">移动横幅 - 320x100</option>
</select>
</td>
</tr>
<tr>
<th><label for="zone_location">位置标识</label></th>
<td><input type="text" id="zone_location" name="zone_location" required class="regular-text" placeholder="如:header, sidebar, article_top"></td>
</tr>
<tr>
<th><label for="default_price">默认价格(元/千次展示)</label></th>
<td><input type="number" id="default_price" name="default_price" step="0.01" min="0" value="0.00" class="small-text"></td>
</tr>
<tr>
<th><label for="max_daily_impressions">每日最大展示量</label></th>
<td><input type="number" id="max_daily_impressions" name="max_daily_impressions" min="1" value="10000" class="small-text"></td>
</tr>
</table>
<?php submit_button('添加广告位', 'primary', 'add_zone'); ?>
</form>
</div>
<div class="card">
<h2>现有广告位</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>尺寸</th>
<th>位置</th>
<th>默认价格</th>
<th>最大展示量</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($zones)): ?>
<tr><td colspan="8">暂无广告位,请添加第一个广告位。</td></tr>
<?php else: ?>
<?php foreach ($zones as $zone): ?>
<tr>
<td><?php echo esc_html($zone['zone_id']); ?></td>
<td><?php echo esc_html($zone['zone_name']); ?></td>
<td><?php echo esc_html($zone['zone_size']); ?></td>
<td><?php echo esc_html($zone['zone_location']); ?></td>
<td><?php echo number_format($zone['default_price'], 2); ?> 元</td>
<td><?php echo number_format($zone['max_daily_impressions']); ?></td>
<td><?php echo $zone['is_active'] ? '活跃' : '禁用'; ?></td>
<td>
<a href="?page=ad-zones&action=edit&zone_id=<?php echo $zone['zone_id']; ?>">编辑</a> |
<a href="?page=ad-zones&action=toggle&zone_id=<?php echo $zone['zone_id']; ?>"><?php echo $zone['is_active'] ? '禁用' : '启用'; ?></a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php
}
三、智能广告投放引擎
3.1 核心投放算法
/**
* 智能广告投放引擎
* 根据多种因素动态选择最佳广告
*/
class AdDeliveryEngine {
/**
* 获取适合当前请求的广告
* @param string $zone_location 广告位位置
* @param array $context 上下文信息(页面类型、内容分类等)
* @return string 广告HTML代码
*/
public function get_ad_for_zone($zone_location, $context = []) {
global $wpdb;
$inventory_table = $wpdb->prefix . 'ad_inventory';
$zones_table = $wpdb->prefix . 'ad_zones';
$current_date = current_time('Y-m-d');
// 构建查询:选择符合条件且未达到展示上限的广告
$query = $wpdb->prepare(
"SELECT inv.*, z.zone_size
FROM $inventory_table AS inv
INNER JOIN $zones_table AS z ON inv.zone_id = z.zone_id
WHERE z.zone_location = %s
AND z.is_active = 1
AND inv.is_active = 1
AND inv.start_date <= %s
AND inv.end_date >= %s
AND inv.delivered_impressions < inv.allocated_impressions
ORDER BY
-- 优先级排序(1为最高)
inv.priority_level ASC,
-- 剩余库存比例(优先使用库存充足的广告)
(inv.allocated_impressions - inv.delivered_impressions) / inv.allocated_impressions DESC,
-- 随机因子,避免总是显示同一个广告
RAND()
LIMIT 1",
$zone_location,
$current_date,
$current_date
);
$ad = $wpdb->get_row($query, ARRAY_A);
if (!$ad) {
// 如果没有找到广告,返回默认广告或占位符
return $this->get_default_ad($zone_location);
}
// 更新展示计数
$this->record_impression($ad['inventory_id']);
// 记录表现数据
$this->log_performance($ad['inventory_id']);
return $ad['ad_code'];
}
/**
* 记录广告展示
* @param int $inventory_id 库存ID
*/
private function record_impression($inventory_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'ad_inventory';
$wpdb->query(
$wpdb->prepare(
"UPDATE $table_name
SET delivered_impressions = delivered_impressions + 1
WHERE inventory_id = %d",
$inventory_id
)
);
}
/**
* 记录广告表现数据
* @param int $inventory_id 库存ID
*/
private function log_performance($inventory_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'ad_performance';
$log_data = [
'inventory_id' => $inventory_id,
'impression_date' => current_time('Y-m-d'),
'impression_time' => current_time('H:i:s'),
'page_url' => $_SERVER['REQUEST_URI'],
'user_ip' => $this->get_client_ip(),
'user_agent' => $_SERVER['HTTP_USER_AGENT']
];
$wpdb->insert($table_name, $log_data);
}
/**
* 获取客户端IP
* @return string IP地址
*/
private function get_client_ip() {
$ip_keys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'];
foreach ($ip_keys as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
* 获取默认广告
* @param string $zone_location 广告位位置
* @return string 默认广告HTML
*/
private function get_default_ad($zone_location) {
// 根据位置返回不同的默认广告
$default_ads = [
'header' => '<div class="ad-placeholder header-ad">广告位招租<br>联系:ad@example.com</div>',
'sidebar' => '<div class="ad-placeholder sidebar-ad">您的广告可以出现在这里</div>',
'article_top' => '<div class="ad-placeholder article-ad">优质广告位 available</div>'
];
return $default_ads[$zone_location] ?? '<div class="ad-placeholder">广告位</div>';
}
}
/**
* 前端广告显示短代码
* @param array $atts 短代码属性
* @return string 广告HTML
*/
function ad_display_shortcode($atts) {
$atts = shortcode_atts([
'location' => 'sidebar',
'class' => 'custom-ad',
'width' => '',
'height' => ''
], $atts, 'ad_display');
$engine = new AdDeliveryEngine();
$ad_html = $engine->get_ad_for_zone($atts['location']);
// 添加包装容器
$wrapper_style = '';
if (!empty($atts['width'])) {
$wrapper_style .= 'width:' . esc_attr($atts['width']) . ';';
}
if (!empty($atts['height'])) {
$wrapper_style .= 'height:' . esc_attr($atts['height']) . ';';
}
return sprintf(
'<div class="ad-container %s" style="%s" data-ad-location="%s">%s</div>',
esc_attr($atts['class']),
$wrapper_style,
esc_attr($atts['location']),
$ad_html
);
}
add_shortcode('ad_display', 'ad_display_shortcode');
/**
* 在文章内容中自动插入广告
*/
function auto_insert_ads_in_content($content) {
if (!is_single() || !in_the_loop() || !is_main_query()) {
return $content;
}
$engine = new AdDeliveryEngine();
// 根据段落位置插入广告
$paragraphs = explode('</p>', $content);
$total_paragraphs = count($paragraphs);
if ($total_paragraphs > 6) {
// 在第三段后插入广告
$ad_position_1 = 3;
// 在倒数第三段前插入广告
$ad_position_2 = $total_paragraphs - 3;
$ad_html_1 = $engine->get_ad_for_zone('article_middle');
$ad_html_2 = $engine->get_ad_for_zone('article_bottom');
$ad_insert_1 = '</p><div class="article-ad-middle">' . $ad_html_1 . '</div><p>';
$ad_insert_2 = '</p><div class="article-ad-bottom">' . $ad_html_2 . '</div><p>';
$paragraphs[$ad_position_1 - 1] .= $ad_insert_1;
$paragraphs[$ad_position_2 - 1] .= $ad_insert_2;
$content = implode('</p>', $paragraphs);
}
return $content;
}
add_filter('the_content', 'auto_insert_ads_in_content', 15);
四、库存预测与动态定价
4.1 基于历史数据的库存预测
/**
* 库存预测与优化类
*/
class InventoryForecaster {
/**
* 预测未来日期的库存需求
* @param int $zone_id 广告位ID
* @param string $start_date 开始日期
* @param string $end_date 结束日期
* @return array 预测数据
*/
public function forecast_inventory($zone_id, $start_date, $end_date) {
global $wpdb;
$performance_table = $wpdb->prefix . 'ad_performance';
$inventory_table = $wpdb->prefix . 'ad_inventory';
// 获取历史表现数据(最近90天)
$history_query = $wpdb->prepare(
"SELECT
DAYNAME(impression_date) as day_of_week,
AVG(daily_impressions) as avg_impressions
FROM (
SELECT
impression_date,
COUNT(*) as daily_impressions
FROM $performance_table AS p
INNER JOIN $inventory_table AS i ON p.inventory_id = i.inventory_id
WHERE i.zone_id = %d
AND impression_date >= DATE_SUB(%s, INTERVAL 90 DAY)
GROUP BY impression_date
) AS daily_stats
GROUP BY day_of_week
ORDER BY FIELD(day_of_week, 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')",
$zone_id,
$start_date
);
$historical_patterns = $wpdb->get_results($history_query, ARRAY_A);
// 获取季节性调整因子(基于月份)
$monthly_factors = $this->calculate_monthly_factors($zone_id);
// 生成预测
$predictions = [];
$current_date = new DateTime($start_date);
$end_date_obj = new DateTime($end_date);
while ($current_date <= $end_date_obj) {
$date_str = $current_date->format('Y-m-d');
$day_of_week = $current_date->format('l');
$month = $current_date->format('n');
// 查找对应星期几的平均展示量
$base_impressions = 10000; // 默认值
foreach ($historical_patterns as $pattern) {
if ($pattern['day_of_week'] === $day_of_week) {
$base_impressions = $pattern['avg_impressions'];
break;
}
}
// 应用月度调整因子
$month_factor = $monthly_factors[$month] ?? 1.0;
$predicted_impressions = round($base_impressions * $month_factor);
// 特殊日期调整(节假日等)
$special_factor = $this->get_special_date_factor($date_str);
$predicted_impressions = round($predicted_impressions * $special_factor);
$predictions[$date_str] = [
'predicted_impressions' => $predicted_impressions,
'available_impressions' => $this->calculate_available_impressions($zone_id, $date_str),
'recommended_price' => $this->calculate_dynamic_price($predicted_impressions, $date_str)
];
$current_date->modify('+1 day');
}
return $predictions;
}
/**
* 计算月度调整因子
*/
private function calculate_monthly_factors($zone_id) {
global $wpdb;
$performance_table = $wpdb->prefix . 'ad_performance';
$inventory_table = $wpdb->prefix . 'ad_inventory';
$query = $wpdb->prepare(
"SELECT
MONTH(impression_date) as month,
AVG(daily_count) / AVG(avg_daily) as factor
FROM (
SELECT
impression_date,
MONTH(impression_date) as month_num,
COUNT(*) as daily_count,
AVG(COUNT(*)) OVER () as avg_daily
FROM $performance_table AS p
INNER JOIN $inventory_table AS i ON p.inventory_id = i.inventory_id
WHERE i.zone_id = %d
AND impression_date >= DATE_SUB(CURDATE(), INTERVAL 365 DAY)
GROUP BY impression_date
) AS monthly_data
GROUP BY month
ORDER BY month",
$zone_id
);
$results = $wpdb->get_results($query, ARRAY_A);
$factors = [];
foreach ($results as $row) {
$factors[$row['month']] = floatval($row['factor']);
}
// 填充缺失的月份
for ($i = 1; $i <= 12; $i++) {
if (!isset($factors[$i])) {
$factors[$i] = 1.0;
}
}
return $factors;
}
/**
* 获取特殊日期调整因子
*/
private function get_special_date_factor($date_str) {
$special_dates = [
'12-25' => 0.5, // 圣诞节
'01-01' => 0.6, // 元旦
'10-01' => 1.2, // 国庆节
'11-11' => 1.5, // 双十一
];
$date_key = date('m-d', strtotime($date_str));
return $special_dates[$date_key] ?? 1.0;
}
/**
* 计算可用展示量
*/
private function calculate_available_impressions($zone_id, $date_str) {
global $wpdb;
$zones_table = $wpdb->prefix . 'ad_zones';
$inventory_table = $wpdb->prefix . 'ad_inventory';
// 获取广告位最大展示量
$zone_query = $wpdb->prepare(
"SELECT max_daily_impressions FROM $zones_table WHERE zone_id = %d",
$zone_id
);
$max_impressions = $wpdb->get_var($zone_query);
// 计算已分配的展示量
$allocated_query = $wpdb->prepare(
"SELECT SUM(allocated_impressions)
FROM $inventory_table
WHERE zone_id = %d
AND start_date <= %s
AND end_date >= %s
AND is_active = 1",
$zone_id,
$date_str,
$date_str
);
$allocated = $wpdb->get_var($allocated_query) ?? 0;
return max(0, $max_impressions - $allocated);
}
/**
* 计算动态价格
*/
private function calculate_dynamic_price($predicted_impressions, $date_str) {
$base_price = 100.00; // 基础价格(元/千次展示)
// 供需调整:需求越高,价格越高
$demand_factor = $predicted_impressions / 10000; // 基于预测展示量
// 时间调整:越接近当前日期,价格越高
$days_until = (strtotime($date_str) - time()) / (60 * 60 * 24);
$time_factor = max(0.5, 2 - ($days_until / 30)); // 30天内线性调整
// 星期调整:周末价格更高
$day_of_week = date('N', strtotime($date_str));
$day_factor = ($day_of_week >= 6) ? 1.2 : 1.0; // 周末加价20%
$final_price = $base_price * $demand_factor * $time_factor * $day_factor;
// 限制价格范围
return round(max(50, min(500, $final_price)), 2);
}
}
五、实时监控与报警系统
5.1 监控仪表板实现
/**
* 广告库存监控类
*/
class AdInventoryMonitor {
/**
* 获取实时库存状态
* @return array 库存状态数据
*/
public function get_realtime_status() {
global $wpdb;
$zones_table = $wpdb->prefix . 'ad_zones';
$inventory_table = $wpdb->prefix . 'ad_inventory';
$performance_table = $wpdb->prefix . 'ad_performance';
$today = current_time('Y-m-d');
$query = "
SELECT
z.zone_id,
z.zone_name,
z.zone_location,
z.max_daily_impressions,
COALESCE(SUM(i.allocated_impressions), 0) as total_allocated,
COALESCE(SUM(i.delivered_impressions), 0) as total_delivered,
COALESCE(COUNT(DISTINCT p.inventory_id), 0) as active_campaigns,
(
SELECT COUNT(*)
FROM $performance_table
WHERE DATE(impression_date) = %s
AND inventory_id IN (
SELECT inventory_id
FROM $inventory_table
WHERE zone_id = z.zone_id
)
) as today_impressions
FROM $zones_table z
LEFT JOIN $inventory_table i ON z.zone_id = i.zone_id
AND i.is_active = 1
AND i.start_date <= %s
AND i.end_date >= %s
LEFT JOIN $performance_table p ON i.inventory_id = p.inventory_id
AND DATE(p.impression_date) = %s
WHERE z.is_active = 1
GROUP BY z.zone_id, z.zone_name, z.zone_location, z.max_daily_impressions
ORDER BY z.zone_location
";
return $wpdb->get_results(
$wpdb->prepare($query, $today, $today, $today, $today),
ARRAY_A
);
}
/**
* 检查库存异常并发送警报
*/
public function check_and_alert_anomalies() {
$status_data = $this->get_realtime_status();
$alerts = [];
foreach ($status_data as $zone) {
$utilization_rate = ($zone['total_allocated'] / $zone['max_daily_impressions']) * 100;
// 检查高利用率
if ($utilization_rate > 90) {
$alerts[] = [
'type' => 'high_utilization',
'zone' => $zone['zone_name'],
'rate' => round($utilization_rate, 1),
'message' => "广告位 {$zone['zone_name']} 利用率已达 " . round($utilization_rate, 1) . "%,接近饱和"
];
}
// 检查低填充率
if ($zone['total_allocated'] > 0) {
$fill_rate = ($zone['today_impressions'] / $zone['total_allocated']) * 100;
if ($fill_rate < 30 && $zone['today_impressions'] > 100) {
$alerts[] = [
'type' => 'low_fill_rate',
'zone' => $zone['zone_name'],
'rate' => round($fill_rate, 1),
'message' => "广告位 {$zone['zone_name']} 填充率仅 " . round($fill_rate, 1) . "%,需要优化"
];
}
}
// 检查即将到期的广告活动
$expiring_campaigns = $this->get_expiring_campaigns($zone['zone_id']);
if (!empty($expiring_campaigns)) {
$alerts[] = [
'type' => 'campaign_expiring',
'zone' => $zone['zone_name'],
'count' => count($expiring_campaigns),
'message' => "广告位 {$zone['zone_name']} 有 " . count($expiring_campaigns) . " 个广告活动即将到期"
];
}
}
// 发送警报
if (!empty($alerts)) {
$this->send_alerts($alerts);
}
return $alerts;
}
/**
* 获取即将到期的广告活动
*/
private function get_expiring_campaigns($zone_id) {
global $wpdb;
$inventory_table = $wpdb->prefix . 'ad_inventory';
$three_days_later = date('Y-m-d', strtotime('+3 days'));
$query = $wpdb->prepare(
"SELECT inventory_id, advertiser_id, end_date
FROM $inventory_table
WHERE zone_id = %d
AND is_active = 1
AND end_date BETWEEN CURDATE() AND %s
ORDER BY end_date ASC",
$zone_id,
$three_days_later
);
return $wpdb->get_results($query, ARRAY_A);
}
/**
* 发送警报通知
*/
private function send_alerts($alerts) {
$admin_email = get_option('admin_email');
$subject = '广告库存系统警报 - ' . date('Y-m-d H:i:s');
$message = "广告库存系统检测到以下异常情况:nn";
foreach ($alerts as $alert) {
$message .= "● {$alert['message']}n";
}
$message .= "n请登录管理后台查看详情:n";
$message .= admin_url('admin.php?page=ad-inventory');
// 发送邮件
wp_mail($admin_email, $subject, $message);
// 同时记录到系统日志
error_log('广告库存警报:' . print_r($alerts, true));
}
/**
* 生成库存报告
*/
public function generate_daily_report() {
$status_data = $this->get_realtime_status();
$report_date = current_time('Y-m-d');
$report = [
'report_date' => $report_date,
'total_zones' => count($status_data),
'total_impressions' => 0,
'total_revenue' => 0,
'zones' => []
];
foreach ($status_data as $zone) {
$zone_revenue = $this->calculate_zone_revenue($zone['zone_id'], $report_date);
$report['zones'][] = [
'name' => $zone['zone_name'],
'location' => $zone['zone_location'],
'allocated' => $zone['total_allocated'],
'delivered' => $zone['today_impressions'],
'utilization' => round(($zone['total_allocated'] / $zone['max_daily_impressions']) * 100, 1),
'fill_rate' => $zone['total_allocated'] > 0 ?
round(($zone['today_impressions'] / $zone['total_allocated']) * 100, 1) : 0,
'revenue' => $zone_revenue
];
$report['total_impressions'] += $zone['today_impressions'];
