本文目录导读:

实现药品库存管理是药品管理系统的核心功能,在PHP项目中,通常需要结合数据库设计、业务逻辑、安全控制以及合规性(如GSP认证)来设计。
以下是一个完整的实现方案,分为数据库设计、核心业务逻辑、代码实现示例和高级注意事项四个部分。
数据库设计(核心)
药品管理比普通商品更严格,需要记录批次、效期、批准文号等。
-- 1. 药品信息表 (products) CREATE TABLE `products` ( `id` int(11) NOT NULL AUTO_INCREMENT, `drug_code` varchar(50) NOT NULL COMMENT '药品编码', `drug_name` varchar(100) NOT NULL COMMENT '通用名', `specification` varchar(100) DEFAULT NULL COMMENT '规格 (如: 0.25g*12片)', `manufacturer` varchar(200) DEFAULT NULL COMMENT '生产厂家', `approval_number` varchar(50) DEFAULT NULL COMMENT '批准文号', `unit` varchar(20) DEFAULT '盒' COMMENT '单位', `category_id` int(11) DEFAULT NULL COMMENT '分类', `min_stock` int(11) DEFAULT 0 COMMENT '最低库存预警', `max_stock` int(11) DEFAULT 0 COMMENT '最高库存限制', `status` tinyint(1) DEFAULT 1 COMMENT '0下架 1上架', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `drug_code` (`drug_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 2. 库存批次表 (最核心,记录每个批次) CREATE TABLE `inventory_batches` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL, `batch_no` varchar(50) NOT NULL COMMENT '批号', `expiry_date` date NOT NULL COMMENT '有效期至', `quantity` int(11) NOT NULL DEFAULT 0 COMMENT '当前库存数量', `purchase_price` decimal(10,2) DEFAULT NULL COMMENT '采购单价', `sale_price` decimal(10,2) DEFAULT NULL COMMENT '零售单价', `warehouse_location` varchar(50) DEFAULT NULL COMMENT '库位 (如A-01-03)', `supplier_id` int(11) DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_expiry` (`expiry_date`), INDEX `idx_product_batch` (`product_id`, `batch_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 3. 库存变动流水表 (审计和追溯) CREATE TABLE `stock_movements` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL, `batch_id` int(11) NOT NULL, `type` varchar(20) NOT NULL COMMENT '入库/出库/报损/退货', `quantity` int(11) NOT NULL COMMENT '变动数量 (正数入库,负数出库)', `before_quantity` int(11) DEFAULT NULL, `after_quantity` int(11) DEFAULT NULL, `ref_order_no` varchar(100) DEFAULT NULL COMMENT '关联单号 (采购单/销售单)', `operator_id` int(11) DEFAULT NULL COMMENT '操作人', `remark` text, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_product` (`product_id`), INDEX `idx_batch` (`batch_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心业务逻辑(PHP实现)
入库流程(采购入库)
需要处理的逻辑:增加批次库存、记录流水、校验效期。
// WarehouseService.php (仓库服务类)
public function stockIn(int $productId, string $batchNo, string $expiryDate, int $quantity, float $price, int $operatorId): bool
{
$this->db->beginTransaction();
try {
// 1. 检查是否有相同批号 (有的系统允许合并,有的要求一盒一批)
$batch = $this->db->query(
"SELECT id, quantity FROM inventory_batches
WHERE product_id = ? AND batch_no = ?",
[$productId, $batchNo]
)->fetch();
if ($batch) {
// 同批号追加数量
$this->db->update('inventory_batches', [
'quantity' => $batch['quantity'] + $quantity
], ['id' => $batch['id']]);
$batchId = $batch['id'];
} else {
// 新增批次记录
$batchId = $this->db->insert('inventory_batches', [
'product_id' => $productId,
'batch_no' => $batchNo,
'expiry_date' => $expiryDate,
'quantity' => $quantity,
'purchase_price' => $price,
'sale_price' => 0 // 后续定价
]);
}
// 2. 记录流水
$this->db->insert('stock_movements', [
'product_id' => $productId,
'batch_id' => $batchId,
'type' => 'in',
'quantity' => $quantity,
'before_quantity' => $batch['quantity'] ?? 0,
'after_quantity' => ($batch['quantity'] ?? 0) + $quantity,
'ref_order_no' => 'PO-20241001-001',
'operator_id' => $operatorId
]);
$this->db->commit();
return true;
} catch (\Exception $e) {
$this->db->rollback();
// 记录错误日志
throw $e;
}
}
出库流程(销售出库)
关键点:先进先出(FIFO),必须优先消耗近效期批次。
// 获取出库批次列表 (FIFO)
public function getOutboundBatches(int $productId, int $neededQuantity): array
{
// SQL: 先按有效期排序,再按入库时间排序
$batches = $this->db->query(
"SELECT id, batch_no, expiry_date, quantity
FROM inventory_batches
WHERE product_id = ? AND quantity > 0 AND expiry_date >= CURDATE()
ORDER BY expiry_date ASC, created_at ASC",
[$productId]
)->fetchAll();
$allocations = [];
$remaining = $neededQuantity;
foreach ($batches as $batch) {
if ($remaining <= 0) break;
$take = min($batch['quantity'], $remaining);
$allocations[] = [
'batch_id' => $batch['id'],
'batch_no' => $batch['batch_no'],
'quantity' => $take,
'expiry_date' => $batch['expiry_date']
];
$remaining -= $take;
}
if ($remaining > 0) {
throw new \Exception("库存不足,缺少 {$remaining} 件");
}
return $allocations;
}
// 执行出库
public function stockOut(int $productId, int $quantity, int $operatorId): void
{
$allocations = $this->getOutboundBatches($productId, $quantity);
$this->db->beginTransaction();
try {
foreach ($allocations as $alloc) {
// 扣减批次库存
$this->db->execute(
"UPDATE inventory_batches
SET quantity = quantity - ?
WHERE id = ? AND quantity >= ?",
[$alloc['quantity'], $alloc['batch_id'], $alloc['quantity']]
);
// 记录流水(注意出库数量为负数)
$this->db->insert('stock_movements', [
'product_id' => $productId,
'batch_id' => $alloc['batch_id'],
'type' => 'out',
'quantity' => -$alloc['quantity'],
'ref_order_no' => 'SO-20241001-001',
'operator_id'=> $operatorId
]);
}
$this->db->commit();
} catch (\Exception $e) {
$this->db->rollback();
throw $e;
}
}
效期预警与近效期锁定
// 每日定时任务(CronJob)
public function checkExpiryWarnings(): void
{
// 近效期提醒(比如90天内到期)
$warningDate = date('Y-m-d', strtotime('+90 days'));
$expiredDate = date('Y-m-d');
// 1. 锁定过期药品(自动设置库存为0或下架)
$this->db->execute(
"UPDATE inventory_batches
SET quantity = 0, status = 'expired'
WHERE expiry_date < ? AND quantity > 0",
[$expiredDate]
);
// 2. 发出近效期告警(插入告警表)
$warnings = $this->db->query(
"SELECT p.drug_name, b.batch_no, b.quantity, b.expiry_date
FROM inventory_batches b
JOIN products p ON p.id = b.product_id
WHERE b.expiry_date BETWEEN CURDATE() AND ?
AND b.quantity > 0",
[$warningDate]
)->fetchAll();
// 发送通知(邮件/系统消息)
foreach ($warnings as $w) {
// 记录到 warning_logs 表
}
}
前端页面功能建议
用PHP + Bootstrap 构建管理界面,至少包含:
| 功能模块 | 说明 |
|---|---|
| 药品档案 | CRUD,支持上传说明书、图片 |
| 入库单 | 选择药品、填写批号/效期/数量,支持Excel导入 |
| 出库单 | 选择药品,自动分配批次(FIFO),打印单据 |
| 库存查询 | 可查:按药品/库位/批次/效期,显示当前库存 |
| 预警看板 | 近效期Top10、库存不足、过期药品 |
| 盘点模块 | 生成盘点差异表,自动调账 |
| 流水追溯 | 查询某个药品所有历史操作记录 |
高可用与安全建议
-
并发安全
- 使用
SELECT ... FOR UPDATE锁定批次行,避免超卖。 - PHP代码层面建议用事务+行级锁。
- 使用
-
合规性(GSP)
- 采购记录、销售记录不可删除(软删除或归档)。
- 批号、效期必须追踪到每一盒(如果是拆零销售,需要额外设计拆零表)。
- 记录操作日志:谁、什么时候、做了什么。
-
性能优化
- 库存查询可用 Redis 缓存总库存数量,但批次明细必须查库。
- 定时清理过期批次数据(转移到归档表)。
stock_movements表要按月份或年份分区。
-
权限控制
入库/出库/报损/盘点/调价,应在PHP后端进行角色权限验证(如RBAC)。
技术选型建议
- 框架:Laravel(推荐,Eloquent ORM + 队列 + 任务调度)或 ThinkPHP。
- 数据库:MySQL/PostgreSQL(支持事务和行锁)。
- 队列:处理大批量入库/出库请求(防止超时)。
- 报表:PHPExcel/PhpSpreadsheet 导出出入库报表。
一个完整的简化示例(用 Laravel)
// InventoryController.php
public function storeIn(Request $request)
{
$validated = $request->validate([
'product_id' => 'required|exists:products,id',
'batch_no' => 'required|string',
'expiry_date' => 'required|date|after:today',
'quantity' => 'required|integer|min:1',
'purchase_price' => 'required|numeric|min:0',
]);
app(InventoryService::class)->stockIn(
$validated['product_id'],
$validated['batch_no'],
$validated['expiry_date'],
$validated['quantity'],
$validated['purchase_price'],
auth()->id()
);
return redirect()->back()->with('success', '入库成功');
}
药品库存管理的核心难点在于:
- 批次与效期的管理(FIFO算法)。
- 数据一致性(事务保护,防止并发冲突)。
- 合规追溯(流水表设计)。
建议先搭建好数据库结构和核心的服务层(Service),再开发具体的界面,如果你的项目规模较小,可以用类似上面的简化方案;如果业务量大,建议考虑微服务架构拆分库存服务。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播