本文目录导读:

- 核心思路
- 方案一:基于MySQL定时任务 + 邮件/短信(适合中小型项目)
- 方案二:事件驱动 + Redis Pub/Sub(适合中等规模)
- 方案三:用户端的“到货通知”(用户订阅模式)
- 方案四:推送通知(WebSocket / SSE)(面向C端用户实时提醒)
- 进阶方案:实时库存 + 消息队列 + 多渠道
- 选型建议
在PHP项目中实现缺货提醒,通常需要结合库存监控、通知机制以及用户偏好设置,以下是几种常见的实现方案,从简单到复杂:
核心思路
- 监控库存:每次订单生成或库存变动时,检查当前库存量。
- 判断阈值:如果库存低于预设的“安全库存”或“缺货线”,则触发提醒。
- 发送通知:通过邮件、短信、站内信或WebSocket推送通知相关方(管理员、用户)。
基于MySQL定时任务 + 邮件/短信(适合中小型项目)
这是最传统且无需额外服务的方法,适合库存变动不频繁的场景。
数据库设计
在products表添加min_stock(最低库存预警线)字段。
在orders表(或库存日志表)记录每次扣减。
创建PHP脚本
<?php
// check_low_stock.php
require_once 'config.php'; // 数据库连接等配置
$threshold = 10; // 假设低于10件为缺货
// 查询库存低于阈值的商品
$sql = "SELECT id, name, stock, min_stock
FROM products
WHERE stock < ? AND stock > 0"; // 排除无库存的
$stmt = $pdo->prepare($sql);
$stmt->execute([$threshold]);
$lowStockItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($lowStockItems)) {
// 发送邮件给管理员
$to = "admin@example.com";
$subject = "库存预警提醒";
$message = "以下商品库存不足:\n\n";
foreach ($lowStockItems as $item) {
$message .= "商品ID: {$item['id']} - {$item['name']} (库存: {$item['stock']})\n";
}
mail($to, $subject, $message);
// 可选:记录到提醒日志表
}
设置Cron任务(Linux)或计划任务(Windows)
# 每5分钟执行一次检查 */5 * * * * /usr/bin/php /path/to/check_low_stock.php
缺点:
- 实时性差(取决于Cron执行间隔)。
- 高并发下可能漏发(因库存变化快)。
- 无法主动推送给用户。
事件驱动 + Redis Pub/Sub(适合中等规模)
利用Redis的发布/订阅或队列,在库存变更时立即触发提醒。
库存更新时发布事件
// 当扣减库存时(例如在订单创建成功后的代码中)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 检查更新后的库存
$newStock = $productModel->getStock($productId);
$threshold = 10;
if ($newStock < $threshold && $newStock > 0) {
$event = json_encode([
'type' => 'low_stock',
'product_id' => $productId,
'current_stock' => $newStock,
'timestamp' => time()
]);
// 发布到频道
$redis->publish('stock_alert', $event);
}
后台Worker订阅并处理
// worker.php (独立进程运行)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->subscribe(['stock_alert'], function ($redis, $channel, $message) {
$data = json_decode($message, true);
// 发送邮件/短信/钉钉/企业微信通知
sendAlertEmail($data['product_id'], $data['current_stock']);
// 记录日志
logAlert($data);
});
通过php worker.php在后台常驻运行,或使用Supervisor管理。
优点:实时性强,无需轮询数据库。
用户端的“到货通知”(用户订阅模式)
如果想让用户主动订阅缺货商品,并在到货时提醒他们:
用户订阅表
CREATE TABLE `stock_subscriptions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`email` varchar(255) NOT NULL,
`status` tinyint(4) DEFAULT '0', -- 0:等待中, 1:已通知
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `user_product` (`user_id`, `product_id`)
);
补货时触发
// 当补货操作完成(入库、退货、供应商送货)
public function restockProduct($productId, $quantity) {
// 更新库存...
$product->stock += $quantity;
$product->save();
// 检查是否有用户订阅了该商品
$subscriptions = StockSubscription::where('product_id', $productId)
->where('status', 0)
->get();
// 如果库存从0变为>0,且用户订阅存在
if ($product->stock > 0 && $subscriptions->count() > 0) {
foreach ($subscriptions as $sub) {
// 发送邮件给订阅用户
$this->sendBackInStockEmail($sub->email, $product);
// 标记为已通知
$sub->status = 1;
$sub->save();
}
}
}
优化:使用队列(如RabbitMQ、Beanstalkd)将邮件发送任务异步化,避免阻塞补货流程。
推送通知(WebSocket / SSE)(面向C端用户实时提醒)
如果需要在前端实时显示“库存紧张”或缺货,推荐使用Server-Sent Events (SSE)或WebSocket。
使用SSE示例(PHP Server-Sent Events)
// sse.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
$lastId = 0;
while (true) {
// 每次循环检查当前登录用户关注的商品库存
$userId = $_SESSION['user_id']; // 或从JWT获取
$lowStockProducts = $db->query("
SELECT p.id, p.name, p.stock
FROM products p
JOIN user_wishlist uw ON p.id = uw.product_id
WHERE uw.user_id = ? AND p.stock < p.min_stock
", [$userId])->fetchAll();
if (!empty($lowStockProducts)) {
echo "id: " . ++$lastId . "\n";
echo "event: low_stock\n";
echo "data: " . json_encode($lowStockProducts) . "\n\n";
ob_flush();
flush();
}
sleep(10); // 每10秒检查一次
}
前端通过EventSource接收:(但注意SSE对缺货场景的轮询效率较低,更适合推送新消息)
进阶方案:实时库存 + 消息队列 + 多渠道
| 组件 | 技术选型 | 用途 |
|---|---|---|
| 库存扣减 | Redis + Lua脚本(原子操作) | 保证高并发下库存准确 |
| 事件触发 | RabbitMQ/Redis List | 异步处理库存变更事件 |
| 通知分发 | PHP Worker (Supervisor) | 轮询队列,区分通知类型 |
| 通知渠道 | Mailgun/SendGrid (邮件), Twilio (短信), 钉钉/企微机器人 | 根据重要性选择渠道 |
| 用户偏好 | 数据库表 | 用户选择“邮件/短信/App推送” |
示例:RabbitMQ消费者
// consumer.php
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('stock_alert_queue', false, true, false, false);
$callback = function ($msg) {
$data = json_decode($msg->body, true);
if ($data['type'] === 'low_stock') {
// 根据用户设置的通知方式发送
if ($data['notification_type'] === 'email') {
sendEmail($data['user_email'], $data['message']);
} elseif ($data['notification_type'] === 'sms') {
sendSMS($data['phone'], $data['message']);
}
}
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
$channel->basic_consume('stock_alert_queue', '', false, false, false, false, $callback);
while ($channel->is_consuming()) {
$channel->wait();
}
选型建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 小型电商站,管理员关心缺货 | Cron + 邮件 | 简单,无需额外组件 |
| 用户可订阅到货通知 | 订阅表 + 队列 | 体验好,用户主动参与 |
| 实时库存页面显示“紧张” | WebSocket/SSE | 前端实时感知 |
| 高并发、大流量(秒杀后) | 方案二/进阶:Redis + RabbitMQ | 解耦,可靠,不拖垮数据库 |
| 需要多渠道通知(邮件、短信、App) | 进阶方案:消息队列 + 渠道适配器 | 灵活扩展 |
关键提醒:
- 对于到货通知,务必在补货成功、库存从0变为>0时触发,而非每次库存变化都触发,避免骚扰用户。
- 邮件通知最好加入退订链接,符合GDPR/反垃圾邮件法规。
- 高并发下,建议使用Redis + Lua扣减库存,避免数据库行锁和性能问题。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播