WordPress柔性采购与库存预警系统开发教程
系统概述与设计思路
在当今电子商务快速发展的时代,库存管理成为企业运营的关键环节。WordPress作为全球最流行的内容管理系统,结合其强大的插件生态和自定义开发能力,可以构建出灵活高效的采购与库存预警系统。
本系统将实现以下核心功能:
- 实时库存监控与预警
- 智能采购建议生成
- 供应商管理集成
- 采购订单自动化处理
- 多仓库库存同步
系统采用模块化设计,便于后期功能扩展和维护。我们将使用WordPress自定义文章类型(CPT)存储产品信息,自定义数据库表记录库存变动,并结合REST API实现前后端数据交互。
环境准备与数据库设计
首先,我们需要在WordPress环境中创建必要的数据库表结构。在您的插件主文件中添加以下代码:
<?php
/**
* WordPress柔性采购与库存预警系统
* 数据库初始化模块
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class InventoryDB {
/**
* 创建库存相关数据表
*/
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 库存变动记录表
$table_inventory_log = $wpdb->prefix . 'inventory_log';
$sql_inventory_log = "CREATE TABLE IF NOT EXISTS $table_inventory_log (
id bigint(20) NOT NULL AUTO_INCREMENT,
product_id bigint(20) NOT NULL,
warehouse_id int(11) DEFAULT 1,
change_type varchar(50) NOT NULL COMMENT '变动类型: purchase, sale, adjust, return',
change_qty int(11) NOT NULL COMMENT '变动数量,正数为入库,负数为出库',
previous_qty int(11) NOT NULL COMMENT '变动前数量',
current_qty int(11) NOT NULL COMMENT '变动后数量',
reference_id bigint(20) DEFAULT NULL COMMENT '关联订单ID或采购单ID',
operator_id bigint(20) DEFAULT NULL COMMENT '操作员ID',
notes text COMMENT '备注信息',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_product (product_id),
KEY idx_created (created_at)
) $charset_collate;";
// 采购建议表
$table_purchase_suggest = $wpdb->prefix . 'purchase_suggestions';
$sql_purchase_suggest = "CREATE TABLE IF NOT EXISTS $table_purchase_suggest (
id bigint(20) NOT NULL AUTO_INCREMENT,
product_id bigint(20) NOT NULL,
suggested_qty int(11) NOT NULL COMMENT '建议采购数量',
reason_code varchar(100) NOT NULL COMMENT '建议原因: low_stock, seasonal, trend',
urgency_level tinyint(1) DEFAULT 1 COMMENT '紧急程度 1-5',
calculated_at datetime DEFAULT CURRENT_TIMESTAMP,
processed tinyint(1) DEFAULT 0 COMMENT '是否已处理',
processed_at datetime DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_product (product_id),
KEY idx_urgency (urgency_level)
) $charset_collate;";
// 供应商信息表
$table_suppliers = $wpdb->prefix . 'suppliers';
$sql_suppliers = "CREATE TABLE IF NOT EXISTS $table_suppliers (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(200) NOT NULL,
contact_person varchar(100) DEFAULT NULL,
phone varchar(50) DEFAULT NULL,
email varchar(100) DEFAULT NULL,
lead_time int(11) DEFAULT 7 COMMENT '交货周期(天)',
reliability_score decimal(3,2) DEFAULT 5.00 COMMENT '可靠性评分',
payment_terms text COMMENT '付款条件',
is_active tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_inventory_log);
dbDelta($sql_purchase_suggest);
dbDelta($sql_suppliers);
}
/**
* 删除数据表(用于插件卸载)
*/
public static function drop_tables() {
global $wpdb;
$tables = [
$wpdb->prefix . 'inventory_log',
$wpdb->prefix . 'purchase_suggestions',
$wpdb->prefix . 'suppliers'
];
foreach ($tables as $table) {
$wpdb->query("DROP TABLE IF EXISTS $table");
}
}
}
// 插件激活时创建表
register_activation_hook(__FILE__, ['InventoryDB', 'create_tables']);
// 插件卸载时删除表(根据需求选择是否启用)
// register_uninstall_hook(__FILE__, ['InventoryDB', 'drop_tables']);
?>
产品与库存管理模块
接下来,我们创建产品自定义文章类型并添加库存管理字段:
<?php
/**
* 产品与库存管理模块
*/
class ProductInventoryManager {
public function __construct() {
// 注册产品自定义文章类型
add_action('init', [$this, 'register_product_post_type']);
// 添加库存管理元框
add_action('add_meta_boxes', [$this, 'add_inventory_meta_box']);
// 保存库存数据
add_action('save_post_product', [$this, 'save_inventory_data'], 10, 2);
// 添加库存管理列
add_filter('manage_product_posts_columns', [$this, 'add_inventory_columns']);
add_action('manage_product_posts_custom_column', [$this, 'display_inventory_columns'], 10, 2);
}
/**
* 注册产品自定义文章类型
*/
public function register_product_post_type() {
$labels = [
'name' => '产品',
'singular_name' => '产品',
'menu_name' => '产品管理',
'add_new' => '添加产品',
'add_new_item' => '添加新产品',
'edit_item' => '编辑产品',
'new_item' => '新产品',
'view_item' => '查看产品',
'search_items' => '搜索产品',
'not_found' => '未找到产品',
'not_found_in_trash' => '回收站中无产品'
];
$args = [
'labels' => $labels,
'public' => true,
'has_archive' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => ['slug' => 'product'],
'capability_type' => 'post',
'hierarchical' => false,
'menu_position' => 30,
'menu_icon' => 'dashicons-products',
'supports' => ['title', 'editor', 'thumbnail', 'excerpt'],
'show_in_rest' => true
];
register_post_type('product', $args);
}
/**
* 添加库存管理元框
*/
public function add_inventory_meta_box() {
add_meta_box(
'inventory_management',
'库存管理',
[$this, 'render_inventory_meta_box'],
'product',
'side',
'high'
);
}
/**
* 渲染库存管理元框
*/
public function render_inventory_meta_box($post) {
// 获取现有库存数据
$current_stock = get_post_meta($post->ID, '_current_stock', true) ?: 0;
$min_stock = get_post_meta($post->ID, '_min_stock', true) ?: 10;
$max_stock = get_post_meta($post->ID, '_max_stock', true) ?: 100;
$supplier_id = get_post_meta($post->ID, '_primary_supplier', true);
// 添加安全验证
wp_nonce_field('save_inventory_data', 'inventory_nonce');
// 显示表单
?>
<div class="inventory-fields">
<p>
<label for="current_stock">当前库存:</label>
<input type="number" id="current_stock" name="current_stock"
value="<?php echo esc_attr($current_stock); ?>" class="widefat">
</p>
<p>
<label for="min_stock">最低库存预警:</label>
<input type="number" id="min_stock" name="min_stock"
value="<?php echo esc_attr($min_stock); ?>" class="widefat">
</p>
<p>
<label for="max_stock">最大库存量:</label>
<input type="number" id="max_stock" name="max_stock"
value="<?php echo esc_attr($max_stock); ?>" class="widefat">
</p>
<p>
<label for="primary_supplier">主要供应商:</label>
<?php $this->render_supplier_dropdown($supplier_id); ?>
</p>
<?php
// 显示库存状态
$this->display_stock_status($current_stock, $min_stock, $max_stock);
?>
</div>
<?php
}
/**
* 渲染供应商下拉列表
*/
private function render_supplier_dropdown($selected_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'suppliers';
$suppliers = $wpdb->get_results("SELECT id, name FROM $table_name WHERE is_active = 1");
echo '<select id="primary_supplier" name="primary_supplier" class="widefat">';
echo '<option value="">选择供应商</option>';
foreach ($suppliers as $supplier) {
$selected = ($supplier->id == $selected_id) ? 'selected' : '';
echo '<option value="' . esc_attr($supplier->id) . '" ' . $selected . '>';
echo esc_html($supplier->name);
echo '</option>';
}
echo '</select>';
}
/**
* 显示库存状态
*/
private function display_stock_status($current, $min, $max) {
$percentage = ($current / $max) * 100;
$status_class = 'good';
$status_text = '库存充足';
if ($current <= $min) {
$status_class = 'low';
$status_text = '库存不足';
} elseif ($current >= $max * 0.9) {
$status_class = 'high';
$status_text = '库存过高';
}
echo '<div class="stock-status ' . $status_class . '">';
echo '<strong>状态:</strong> ' . $status_text;
echo '<div class="stock-bar"><div class="stock-level" style="width:' . $percentage . '%;"></div></div>';
echo '</div>';
// 添加简单样式
echo '<style>
.stock-status { padding: 8px; margin: 10px 0; border-radius: 4px; }
.stock-status.low { background: #ffeaea; border-left: 4px solid #dc3232; }
.stock-status.good { background: #e7f7e7; border-left: 4px solid #46b450; }
.stock-status.high { background: #fff3e6; border-left: 4px solid #ffb900; }
.stock-bar { height: 10px; background: #f0f0f0; border-radius: 5px; margin-top: 5px; }
.stock-level { height: 100%; background: #0073aa; border-radius: 5px; }
</style>';
}
/**
* 保存库存数据
*/
public function save_inventory_data($post_id, $post) {
// 安全检查
if (!isset($_POST['inventory_nonce']) ||
!wp_verify_nonce($_POST['inventory_nonce'], 'save_inventory_data')) {
return;
}
// 权限检查
if (!current_user_can('edit_post', $post_id)) {
return;
}
// 自动保存检查
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// 保存库存数据
$fields = ['current_stock', 'min_stock', 'max_stock', 'primary_supplier'];
foreach ($fields as $field) {
if (isset($_POST[$field])) {
$value = sanitize_text_field($_POST[$field]);
update_post_meta($post_id, '_' . $field, $value);
}
}
// 记录库存变动
$this->log_inventory_change($post_id, 'adjust', $_POST['current_stock']);
}
/**
* 记录库存变动
*/
private function log_inventory_change($product_id, $type, $new_qty) {
global $wpdb;
$old_qty = get_post_meta($product_id, '_current_stock', true) ?: 0;
$change_qty = $new_qty - $old_qty;
if ($change_qty == 0) {
return; // 库存未变化,不记录
}
$table_name = $wpdb->prefix . 'inventory_log';
$wpdb->insert(
$table_name,
[
'product_id' => $product_id,
'change_type' => $type,
'change_qty' => $change_qty,
'previous_qty' => $old_qty,
'current_qty' => $new_qty,
'operator_id' => get_current_user_id(),
'created_at' => current_time('mysql')
],
['%d', '%s', '%d', '%d', '%d', '%d', '%s']
);
}
/**
* 添加库存管理列
*/
public function add_inventory_columns($columns) {
$new_columns = [];
foreach ($columns as $key => $value) {
$new_columns[$key] = $value;
if ($key === 'title') {
$new_columns['current_stock'] = '当前库存';
$new_columns['stock_status'] = '库存状态';
$new_columns['last_updated'] = '最后更新';
}
}
return $new_columns;
}
/**
* 显示库存管理列内容
*/
public function display_inventory_columns($column, $post_id) {
switch ($column) {
case 'current_stock':
$stock = get_post_meta($post_id, '_current_stock', true);
echo $stock ?: 0;
break;
case 'stock_status':
$current = get_post_meta($post_id, '_current_stock', true) ?: 0;
$min = get_post_meta($post_id, '_min_stock', true) ?: 10;
if ($current <= $min) {
echo '<span style="color: #dc3232; font-weight: bold;">需补货</span>';
} elseif ($current <= $min * 2) {
echo '<span style="color: #ffb900;">库存偏低</span>';
} else {
echo '<span style="color: #46b450;">正常</span>';
}
break;
case 'last_updated':
global $wpdb;
$table_name = $wpdb->prefix . 'inventory_log';
$last_update = $wpdb->get_var($wpdb->prepare(
"SELECT created_at FROM $table_name
WHERE product_id = %d
ORDER BY created_at DESC LIMIT 1",
$post_id
));
echo $last_update ? date('Y-m-d H:i', strtotime($last_update)) : '无记录';
break;
}
}
}
// 初始化产品库存管理器
new ProductInventoryManager();
?>
智能预警与采购建议模块
库存预警是系统的核心功能,下面我们实现智能预警算法:
<?php
/**
* 智能预警与采购建议模块
*/
class InventoryAlertSystem {
private $alert_thresholds = [
'critical' => 0.2, // 低于最低库存20%为紧急
'warning' => 0.5, // 低于最低库存50%为警告
'normal' => 1.0 // 低于最低库存100%为正常
];
public function __construct() {
// 每天检查库存
add_action('wp', [$this, 'schedule_daily_check']);
add_action('inventory_daily_check', [$this, 'check_all_products']);
// 添加管理页面
add_action('admin_menu', [$this, 'add_admin_pages']);
}
/**
* 安排每日检查任务
*/
public function schedule_daily_check() {
if (!wp_next_scheduled('inventory_daily_check')) {
wp_schedule_event(time(), 'daily', 'inventory_daily_check');
}
}
/**
* 检查所有产品库存
*/
public function check_all_products() {
$args = [
'post_type' => 'product',
'posts_per_page' => -1,
'post_status' => 'publish'
];
$products = get_posts($args);
foreach ($products as $product) {
$this->check_single_product($product->ID);
}
智能预警与采购建议模块(续)
<?php
/**
* 智能预警与采购建议模块(续)
*/
class InventoryAlertSystem {
// ... 前面的构造函数和基础方法保持不变 ...
/**
* 检查单个产品库存状态
*/
private function check_single_product($product_id) {
$current_stock = (int) get_post_meta($product_id, '_current_stock', true);
$min_stock = (int) get_post_meta($product_id, '_min_stock', true);
$max_stock = (int) get_post_meta($product_id, '_max_stock', true);
// 计算库存比率
$stock_ratio = $min_stock > 0 ? $current_stock / $min_stock : 1;
// 判断库存状态
if ($current_stock <= 0) {
$this->generate_purchase_suggestion($product_id, 'out_of_stock', 5);
$this->send_alert_notification($product_id, 'critical', '产品已售罄');
} elseif ($stock_ratio <= $this->alert_thresholds['critical']) {
$suggest_qty = $max_stock - $current_stock;
$this->generate_purchase_suggestion($product_id, 'critical_low', $suggest_qty, 5);
$this->send_alert_notification($product_id, 'critical', '库存严重不足');
} elseif ($stock_ratio <= $this->alert_thresholds['warning']) {
$suggest_qty = ceil(($max_stock - $current_stock) * 0.7);
$this->generate_purchase_suggestion($product_id, 'low_stock', $suggest_qty, 3);
$this->send_alert_notification($product_id, 'warning', '库存偏低');
}
// 检查是否需要生成基于销售趋势的建议
$this->check_sales_trend($product_id);
}
/**
* 生成采购建议
*/
private function generate_purchase_suggestion($product_id, $reason, $quantity, $urgency = 1) {
global $wpdb;
$table_name = $wpdb->prefix . 'purchase_suggestions';
// 检查是否已有未处理的相同建议
$existing = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table_name
WHERE product_id = %d
AND reason_code = %s
AND processed = 0",
$product_id, $reason
));
if ($existing > 0) {
return; // 已有相同建议,不再重复生成
}
$wpdb->insert(
$table_name,
[
'product_id' => $product_id,
'suggested_qty' => $quantity,
'reason_code' => $reason,
'urgency_level' => $urgency,
'calculated_at' => current_time('mysql')
],
['%d', '%d', '%s', '%d', '%s']
);
// 记录到系统日志
$this->log_system_event('purchase_suggestion_generated', [
'product_id' => $product_id,
'quantity' => $quantity,
'reason' => $reason
]);
}
/**
* 检查销售趋势
*/
private function check_sales_trend($product_id) {
global $wpdb;
$log_table = $wpdb->prefix . 'inventory_log';
// 获取最近30天的销售数据
$thirty_days_ago = date('Y-m-d H:i:s', strtotime('-30 days'));
$sales_data = $wpdb->get_results($wpdb->prepare(
"SELECT DATE(created_at) as sale_date,
SUM(ABS(change_qty)) as daily_sales
FROM $log_table
WHERE product_id = %d
AND change_type = 'sale'
AND change_qty < 0
AND created_at >= %s
GROUP BY DATE(created_at)
ORDER BY sale_date DESC
LIMIT 30",
$product_id, $thirty_days_ago
));
if (count($sales_data) < 7) {
return; // 数据不足,不进行趋势分析
}
// 计算平均日销量
$total_sales = 0;
foreach ($sales_data as $data) {
$total_sales += $data->daily_sales;
}
$avg_daily_sales = $total_sales / count($sales_data);
// 获取当前库存和供应商交货周期
$current_stock = (int) get_post_meta($product_id, '_current_stock', true);
$supplier_id = get_post_meta($product_id, '_primary_supplier', true);
$lead_time = $this->get_supplier_lead_time($supplier_id);
// 计算安全库存(考虑交货周期和销售波动)
$safety_stock = ceil($avg_daily_sales * $lead_time * 1.5);
// 如果当前库存低于安全库存,生成采购建议
if ($current_stock < $safety_stock) {
$min_stock = (int) get_post_meta($product_id, '_min_stock', true);
$suggest_qty = max($safety_stock - $current_stock, $min_stock - $current_stock);
if ($suggest_qty > 0) {
$this->generate_purchase_suggestion(
$product_id,
'trend_based',
$suggest_qty,
2
);
}
}
}
/**
* 获取供应商交货周期
*/
private function get_supplier_lead_time($supplier_id) {
if (empty($supplier_id)) {
return 7; // 默认7天
}
global $wpdb;
$table_name = $wpdb->prefix . 'suppliers';
$lead_time = $wpdb->get_var($wpdb->prepare(
"SELECT lead_time FROM $table_name WHERE id = %d",
$supplier_id
));
return $lead_time ?: 7;
}
/**
* 发送预警通知
*/
private function send_alert_notification($product_id, $level, $message) {
$product = get_post($product_id);
$current_stock = get_post_meta($product_id, '_current_stock', true);
$min_stock = get_post_meta($product_id, '_min_stock', true);
// 获取管理员邮箱
$admin_email = get_option('admin_email');
$subject = sprintf('[库存预警] %s - %s',
ucfirst($level),
$product->post_title
);
$body = sprintf(
"产品名称: %sn产品ID: %dn当前库存: %dn最低库存: %dn预警级别: %sn预警信息: %snn请登录系统查看详情: %s",
$product->post_title,
$product_id,
$current_stock,
$min_stock,
$level,
$message,
admin_url('admin.php?page=inventory-alerts')
);
// 发送邮件
wp_mail($admin_email, $subject, $body);
// 记录通知发送
$this->log_system_event('alert_notification_sent', [
'product_id' => $product_id,
'level' => $level,
'message' => $message,
'recipient' => $admin_email
]);
}
/**
* 记录系统事件
*/
private function log_system_event($event_type, $data = []) {
global $wpdb;
$log_table = $wpdb->prefix . 'inventory_log';
$wpdb->insert(
$log_table,
[
'product_id' => 0, // 0表示系统事件
'change_type' => 'system_event',
'change_qty' => 0,
'previous_qty' => 0,
'current_qty' => 0,
'notes' => json_encode([
'event' => $event_type,
'data' => $data,
'timestamp' => current_time('mysql')
]),
'created_at' => current_time('mysql')
],
['%d', '%s', '%d', '%d', '%d', '%s', '%s']
);
}
/**
* 添加管理页面
*/
public function add_admin_pages() {
add_menu_page(
'库存预警系统',
'库存预警',
'manage_options',
'inventory-alerts',
[$this, 'render_alerts_page'],
'dashicons-warning',
56
);
add_submenu_page(
'inventory-alerts',
'采购建议',
'采购建议',
'manage_options',
'purchase-suggestions',
[$this, 'render_suggestions_page']
);
}
/**
* 渲染预警页面
*/
public function render_alerts_page() {
global $wpdb;
$log_table = $wpdb->prefix . 'inventory_log';
// 获取最近的预警记录
$alerts = $wpdb->get_results(
"SELECT l.*, p.post_title
FROM $log_table l
LEFT JOIN {$wpdb->posts} p ON l.product_id = p.ID
WHERE l.change_type = 'system_event'
AND l.notes LIKE '%alert_notification_sent%'
ORDER BY l.created_at DESC
LIMIT 50"
);
?>
<div class="wrap">
<h1>库存预警系统</h1>
<div class="alert-summary">
<h2>今日预警统计</h2>
<?php $this->render_today_stats(); ?>
</div>
<div class="alert-list">
<h2>最近预警记录</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>时间</th>
<th>产品</th>
<th>预警级别</th>
<th>详细信息</th>
</tr>
</thead>
<tbody>
<?php foreach ($alerts as $alert):
$notes = json_decode($alert->notes, true);
?>
<tr>
<td><?php echo date('Y-m-d H:i', strtotime($alert->created_at)); ?></td>
<td>
<?php if ($alert->product_id > 0): ?>
<a href="<?php echo get_edit_post_link($alert->product_id); ?>">
<?php echo esc_html($alert->post_title ?: '产品#' . $alert->product_id); ?>
</a>
<?php else: ?>
系统事件
<?php endif; ?>
</td>
<td>
<?php
$level = $notes['data']['level'] ?? 'unknown';
$level_labels = [
'critical' => '<span class="dashicons dashicons-dismiss" style="color:#dc3232;"></span> 紧急',
'warning' => '<span class="dashicons dashicons-warning" style="color:#ffb900;"></span> 警告',
'normal' => '<span class="dashicons dashicons-yes" style="color:#46b450;"></span> 正常'
];
echo $level_labels[$level] ?? $level;
?>
</td>
<td><?php echo esc_html($notes['data']['message'] ?? ''); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<style>
.alert-summary {
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stat-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 20px;
}
.stat-card {
padding: 15px;
border-radius: 4px;
text-align: center;
}
.stat-card.critical {
background: #ffeaea;
border-left: 4px solid #dc3232;
}
.stat-card.warning {
background: #fff3e6;
border-left: 4px solid #ffb900;
}
.stat-card.normal {
background: #e7f7e7;
border-left: 4px solid #46b450;
}
.stat-number {
font-size: 32px;
font-weight: bold;
display: block;
margin-bottom: 5px;
}
</style>
<?php
}
/**
* 渲染今日统计
*/
private function render_today_stats() {
global $wpdb;
$log_table = $wpdb->prefix . 'inventory_log';
$today = date('Y-m-d');
$stats = $wpdb->get_results(
"SELECT
SUM(CASE WHEN notes LIKE '%critical%' THEN 1 ELSE 0 END) as critical_count,
SUM(CASE WHEN notes LIKE '%warning%' THEN 1 ELSE 0 END) as warning_count,
SUM(CASE WHEN notes LIKE '%normal%' THEN 1 ELSE 0 END) as normal_count
FROM $log_table
WHERE DATE(created_at) = '$today'
AND change_type = 'system_event'
AND notes LIKE '%alert_notification_sent%'"
);
$stat = $stats[0] ?? (object) [
'critical_count' => 0,
'warning_count' => 0,
'normal_count' => 0
];
?>
<div class="stat-cards">
<div class="stat-card critical">
<span class="stat-number"><?php echo $stat->critical_count; ?></span>
<span>紧急预警</span>
</div>
<div class="stat-card warning">
<span class="stat-number"><?php echo $stat->warning_count; ?></span>
<span>警告预警</span>
</div>
<div class="stat-card normal">
<span class="stat-number"><?php echo $stat->normal_count; ?></span>
<span>正常通知</span>
</div>
</div>
<?php
}
/**
* 渲染采购建议页面
*/
public function render_suggestions_page() {
global $wpdb;
$table_name = $wpdb->prefix . 'purchase_suggestions';
// 处理建议操作
if (isset($_POST['action']) && isset($_POST['suggestion_id'])) {
$this->process_suggestion_action($_POST['suggestion_id'], $_POST['action']);
}
// 获取未处理的采购建议
$suggestions = $wpdb->get_results(
"SELECT s.*, p.post_title,
pm1.meta_value as current_stock,
pm2.meta_value as min_stock,
pm3.meta_value as primary_supplier
FROM $table_name s
LEFT JOIN {$wpdb->posts} p ON s.product_id = p.ID
LEFT JOIN {$wpdb->postmeta} pm1 ON s.product_id = pm1.post_id AND pm1.meta_key = '_current_stock'
LEFT JOIN {$wpdb->postmeta} pm2 ON s.product_id = pm2.post_id AND pm2.meta_key = '_min_stock'
LEFT JOIN {$wpdb->postmeta} pm3 ON s.product_id = pm3.post_id AND pm3.meta_key = '_primary_supplier'
WHERE s.processed = 0
ORDER BY s.urgency_level DESC, s.calculated_at DESC"
);
?>
<div class="wrap">
<h1>采购建议</h1>
<div class="notice notice-info">
<p>系统根据库存情况和销售趋势自动生成的采购建议。请及时处理紧急建议。</p>
</div>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>产品名称</th>
<th>当前库存</th>
<th>建议数量</th>
<th>建议原因</th>
<th>紧急程度</th>
<th>生成时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($suggestions as $suggestion):
$reason_labels = [
'out_of_stock' => '已售罄',
'critical_low' => '库存严重不足',
'low_stock' => '库存偏低',
'trend_based' => '基于销售趋势'
];
?>
<tr>
<td>
<a href="<?php echo get_edit_post_link($suggestion->product_id); ?>">
<?php echo esc_html($suggestion->post_title ?: '产品#' . $suggestion->product_id); ?>
</a>
</td>
<td><?php echo $suggestion->current_stock ?: 0; ?></td>
<td><strong><?php echo $suggestion->suggested_qty; ?></strong></td>
<td><?php echo $reason_labels[$suggestion->reason_code] ?? $suggestion->reason_code; ?></td>
<td>
<?php
$urgency_stars = str_repeat('★', $suggestion->urgency_level);
$urgency_class = $suggestion->urgency_level >= 4 ? 'critical' :
($suggestion->urgency_level >= 3 ? 'warning' : 'normal');
