本文目录导读:

在PHP项目中实现库存预警,核心思路是在业务逻辑的关键节点(如下单、入库、出库)检查库存数量,并与设定的预警阈值进行比较,当低于阈值时触发通知。
下面我将从数据表设计、核心逻辑实现、预警方式和最佳实践四个方面,为你提供一个完整的实现方案。
数据库表设计
你需要一个基础的库存表和预警规则表。
产品库存表 (products 或 inventory)
CREATE TABLE `products` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `sku` VARCHAR(100) UNIQUE NOT NULL, -- 库存单位编码 `stock_quantity` INT NOT NULL DEFAULT 0, -- 当前库存数量 `low_stock_threshold` INT NOT NULL DEFAULT 10, -- 低库存预警阈值 (低于10时预警) `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
预警日志表(可选,用于记录历史预警)
CREATE TABLE `stock_alerts` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`product_id` INT NOT NULL,
`alert_type` ENUM('low_stock', 'out_of_stock', 'overstock') NOT NULL,
`current_quantity` INT NOT NULL,
`threshold` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`is_notified` BOOLEAN DEFAULT FALSE, -- 标记是否已通知
FOREIGN KEY (`product_id`) REFERENCES `products`(`id`)
);
核心逻辑实现(PHP代码示例)
以下代码演示了在出库时检查并触发预警。
<?php
// 假设使用PDO连接数据库,$pdo 已初始化
/**
* 处理订单出库
* @param int $productId 产品ID
* @param int $quantity 出库数量
* @return array 结果
*/
function processOrderOutbound($productId, $quantity) {
global $pdo; // 实际项目中请使用依赖注入
try {
$pdo->beginTransaction();
// 1. 查询当前库存和预警阈值(加锁防止并发)
$sql = "SELECT id, stock_quantity, low_stock_threshold
FROM products
WHERE id = ? FOR UPDATE";
$stmt = $pdo->prepare($sql);
$stmt->execute([$productId]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
throw new Exception('产品不存在');
}
$newQuantity = $product['stock_quantity'] - $quantity;
// 2. 检查库存是否充足
if ($newQuantity < 0) {
throw new Exception('库存不足,无法出库');
}
// 3. 更新库存
$updateSql = "UPDATE products SET stock_quantity = ? WHERE id = ?";
$updateStmt = $pdo->prepare($updateSql);
$updateStmt->execute([$newQuantity, $productId]);
// 4. 触发库存预警检查
$alertResult = checkInventoryAndSendAlert($productId, $newQuantity, $product['low_stock_threshold']);
$pdo->commit();
return [
'success' => true,
'new_quantity' => $newQuantity,
'alert' => $alertResult
];
} catch (Exception $e) {
$pdo->rollBack();
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* 检查库存并发送预警
* @param int $productId
* @param int $currentQuantity
* @param int $threshold
* @return array
*/
function checkInventoryAndSendAlert($productId, $currentQuantity, $threshold) {
global $pdo;
$alertType = null;
$message = '';
if ($currentQuantity <= 0) {
$alertType = 'out_of_stock';
$message = "产品 ID:{$productId} 已缺货!";
} elseif ($currentQuantity <= $threshold) {
$alertType = 'low_stock';
$message = "产品 ID:{$productId} 库存紧张!当前库存: {$currentQuantity}, 预警阈值: {$threshold}";
}
if ($alertType) {
// 5. 记录预警日志(防止重复通知)
$logSql = "INSERT INTO stock_alerts (product_id, alert_type, current_quantity, threshold)
VALUES (?, ?, ?, ?)";
$logStmt = $pdo->prepare($logSql);
$logStmt->execute([$productId, $alertType, $currentQuantity, $threshold]);
// 6. 发送通知(邮件/短信/站内信)
sendAlertNotification($productId, $alertType, $message);
return ['alert_sent' => true, 'type' => $alertType, 'message' => $message];
}
return ['alert_sent' => false];
}
/**
* 发送预警通知(示例:保存到站内信表)
*/
function sendAlertNotification($productId, $alertType, $message) {
// 实际项目中,这里可以调用邮件API (如 Mailgun/SendGrid)
// 或短信API (如阿里云/腾讯云短信)
// 或写入站内信表
// file_put_contents('/tmp/stock_alert.log', date('Y-m-d H:i:s') . " - " . $message . PHP_EOL, FILE_APPEND);
// 示例:写入站内信表
// $insertSql = "INSERT INTO user_notifications (user_id, title, content) VALUES (?, ?, ?)";
// ...
}
预警触发与通知方式
-
同步触发(如上例):
- 缺点:通知过程(特别是发邮件/短信)会拖慢用户请求,如果通知API超时,会导致下单失败。
- 改进:将通知任务放入消息队列(如 Redis 的 List 或 RabbitMQ),由后台 Worker 异步处理。
-
异步任务方式:
// 当检测到预警时,不直接发送,而是将任务推入队列 $redis->lpush('stock_alert_queue', json_encode([ 'product_id' => $productId, 'alert_type' => 'low_stock', 'message' => $message ])); // 返回结果给用户,另一个进程(Woker)读取队列并发送邮件 -
定时任务(Cron Job):
- 适合离线检查或批量检查。
crontab -e:* * * * * /usr/bin/php /path/to/check_stock_alerts.php- 定时脚本代码:
// check_stock_alerts.php $sql = "SELECT id, name, stock_quantity, low_stock_threshold FROM products WHERE stock_quantity <= low_stock_threshold"; $products = $pdo->query($sql)->fetchAll();
foreach ($products as $product) { // 检查是否已在最近N分钟内发送过相同预警,避免重复 // 发送通知... }
最佳实践与注意事项
- 避免重复预警:
- 在
stock_alerts表中记录上次预警时间,同一产品同一类型的预警在短时间内(如1小时)不重复通知,可以使用created_at字段进行判断。
- 在
- 并发安全:
- 库存操作必须使用数据库事务和行锁(
FOR UPDATE),避免高并发下出现超卖或库存负数。
- 库存操作必须使用数据库事务和行锁(
- 不同产品不同阈值:
- 不是所有商品都用同一个阈值(昂贵配件阈值可设1,快消品阈值可设100),在
products表中为每个产品单独设置low_stock_threshold字段。
- 不是所有商品都用同一个阈值(昂贵配件阈值可设1,快消品阈值可设100),在
- 区分预警等级:
- 缺货(
out_of_stock)、低库存(low_stock)、高库存(overstock,避免资金积压),不同等级使用不同通知强度(如:低库存发邮件,缺货发短信)。
- 缺货(
- 使用消息队列:
对于中大型项目,强烈建议将通知操作异步化,使用 Redis、RabbitMQ 或 Beanstalkd 来处理,提升 API 响应速度。
- 监控与看板:
- 除了主动通知,建议在管理后台提供一个库存预警看板,用颜色(红色=缺货,黄色=低库存)直观展示所有异常商品。
| 方案 | 适合场景 | 优点 | 缺点 |
|---|---|---|---|
| 同步检查 | 小型项目,低并发 | 实现简单,实时性高 | 可能延迟用户请求 |
| 异步队列 | 中大型项目,高并发 | 响应快,解耦,稳定 | 需要维护队列组件 (Redis/RabbitMQ) |
| 定时任务 | 批量、非实时检查 | 实现简单,适合离线数据 | 实时性差,可能错过高峰预警 |
推荐的实现路径: 先用同步检查 + 定时任务快速实现(适合大多数中小企业),当业务量增长后再将通知部分改为消息队列异步。