本文目录导读:

使用PHP实现采购计划(采购申请与审批)通常需要一个完整的Web应用,包含数据库、后端逻辑和前端界面。
下面是一个从零开始的标准化实现方案,涵盖了需求分析、数据库设计、核心代码示例和业务流程,你可以根据这个框架来构建自己的项目。
核心功能与业务流程
一个简单的采购计划系统通常包含以下流程:
- 需求发起:各部门提交采购申请单。
- 审批流转:部门主管 -> 财务 -> 总经理(或多级审批)。
- 计划汇总:审批通过的申请汇总成“采购计划”。
- 采购执行:计划单转为采购订单(可选,本方案聚焦于计划生成)。
数据库设计 (MySQL)
这是最关键的部分,至少需要三张核心表。
采购申请表 (purchase_requests)
CREATE TABLE `purchase_requests` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pr_number` varchar(50) NOT NULL COMMENT '申请单号,如 PR-20231027-001', `applicant_id` int(11) NOT NULL COMMENT '申请人(关联用户表)', `department_id` int(11) DEFAULT NULL COMMENT '部门ID', varchar(200) NOT NULL COMMENT '采购事由/标题', `total_amount` decimal(12,2) DEFAULT '0.00' COMMENT '预估总金额', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态: 0-草稿, 1-待主管审批, 2-待财务审批, 3-已批准, 4-已拒绝', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `pr_number` (`pr_number`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
申请明细表 (purchase_request_items)
CREATE TABLE `purchase_request_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `request_id` int(11) NOT NULL COMMENT '关联申请单ID', `material_name` varchar(100) NOT NULL COMMENT '物料/服务名称', `specification` varchar(255) DEFAULT NULL COMMENT '规格型号', `quantity` int(11) NOT NULL COMMENT '数量', `unit` varchar(20) DEFAULT NULL COMMENT '单位', `estimated_price` decimal(10,2) DEFAULT NULL COMMENT '预估单价', `remark` text COMMENT '备注/用途说明', PRIMARY KEY (`id`), KEY `request_id` (`request_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
审批记录表 (approval_logs)
CREATE TABLE `approval_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`request_id` int(11) NOT NULL COMMENT '申请单ID',
`approver_id` int(11) NOT NULL COMMENT '审批人ID',
`action` enum('approve','reject','return') NOT NULL COMMENT '动作',
`comment` text COMMENT '审批意见',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
提示:如果系统复杂,建议将“状态”独立成一张 workflow_steps(工作流步骤)表,方便配置多级审批。
PHP 后端核心代码示例 (面向过程 / Laravel风格思路)
假设你使用 原生PHP 或 ThinkPHP/Laravel 框架。
创建采购申请 (Controller - create 方法)
// 接收 POST 数据
$title = $_POST['title'];
$items = $_POST['items']; // 这是一个数组,包含多个物料的 json
// 1. 生成唯一的申请单号
$pr_number = 'PR-' . date('Ymd') . '-' . str_pad($this->getNextSequence(), 4, '0', STR_PAD_LEFT);
// 2. 开启事务
$this->db->beginTransaction();
try {
// 3. 插入主表
$sql = "INSERT INTO purchase_requests (pr_number, applicant_id, title, total_amount, status) VALUES (?, ?, ?, ?, 0)";
$this->db->execute($sql, [$pr_number, $_SESSION['user_id'], $title, 0]);
$request_id = $this->db->lastInsertId();
$total = 0;
// 4. 循环插入明细表
foreach ($items as $item) {
$sql = "INSERT INTO purchase_request_items (request_id, material_name, specification, quantity, unit, estimated_price) VALUES (?, ?, ?, ?, ?, ?)";
$this->db->execute($sql, [$request_id, $item['name'], $item['spec'], $item['qty'], $item['unit'], $item['price']]);
$total += $item['qty'] * $item['price'];
}
// 5. 更新主表的预估总金额
$sql = "UPDATE purchase_requests SET total_amount = ? WHERE id = ?";
$this->db->execute($sql, [$total, $request_id]);
$this->db->commit();
echo json_encode(['success' => true, 'message' => '采购申请已提交']);
} catch (Exception $e) {
$this->db->rollback();
echo json_encode(['success' => false, 'message' => '提交失败: ' . $e->getMessage()]);
}
审核操作 (Controller - approve 方法)
public function approve($request_id) {
$action = $_POST['action']; // 'approve' 或 'reject'
$comment = $_POST['comment'];
$current_user_id = $_SESSION['user_id'];
$current_role = $this->getUserRole($current_user_id);
// 1. 获取当前申请单状态
$request = $this->db->fetch("SELECT * FROM purchase_requests WHERE id = ?", [$request_id]);
// 2. 权限校验(只有主管才能批准待主管审批的状态)
if ($current_role == 'manager' && $request['status'] == 1) {
$new_status = ($action == 'approve') ? 2 : 4; // 转到财务审批 或 拒绝
} elseif ($current_role == 'finance' && $request['status'] == 2) {
$new_status = ($action == 'approve') ? 3 : 4; // 最终批准 或 拒绝
} else {
die('权限不足或状态异常');
}
// 3. 更新申请单状态
$sql = "UPDATE purchase_requests SET status = ? WHERE id = ?";
$this->db->execute($sql, [$new_status, $request_id]);
// 4. 记录审批日志
$sql = "INSERT INTO approval_logs (request_id, approver_id, action, comment) VALUES (?, ?, ?, ?)";
$this->db->execute($sql, [$request_id, $current_user_id, $action, $comment]);
// 5. 如果最终批准,可以自动生成“采购计划单” (可选)
if ($new_status == 3) {
$this->generatePurchasePlan($request_id);
}
echo json_encode(['success' => true]);
}
前端界面 (HTML + JavaScript/jQuery)
采购申请表单
<form id="purchaseForm">
<div>
<label>采购事由:</label>
<input type="text" name="title" required>
</div>
<div id="itemsContainer">
<div class="item-row">
<input type="text" name="items[0][name]" placeholder="物料名称" required>
<input type="text" name="items[0][spec]" placeholder="规格">
<input type="number" name="items[0][qty]" placeholder="数量" required>
<input type="text" name="items[0][unit]" placeholder="单位" value="个">
<input type="number" step="0.01" name="items[0][price]" placeholder="预估单价" required>
</div>
</div>
<button type="button" onclick="addItem()">添加物料</button>
<button type="submit">提交申请</button>
</form>
审批列表界面
<table>
<thead>
<tr>
<th>单号</th>
<th>申请人</th>
<th>总金额</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($requests as $req): ?>
<tr>
<td><?= $req['pr_number'] ?></td>
<td><?= $req['applicant_name'] ?></td>
<td><?= $req['total_amount'] ?></td>
<td>
<?php
$status_map = [0 => '草稿', 1 => '待主管审批', 2 => '待财务审批', 3 => '已批准', 4 => '已拒绝'];
echo $status_map[$req['status']];
?>
</td>
<td>
<!-- 审批按钮,通常通过 session 判断当前用户角色来控制显示 -->
<button onclick="approve(<?= $req['id'] ?>, 'approve')">通过</button>
<button onclick="approve(<?= $req['id'] ?>, 'reject')">拒绝</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
注意事项与进阶优化
-
安全性:
- 所有SQL操作必须使用预处理语句(PDO或MySQLi prepared statements)防止SQL注入。
- 权限管理:在每一个controller方法开头检查用户角色(如
if($_SESSION['role'] != 'manager') die('无权限');)。
-
工作流复杂性:
- 上述代码是硬编码的审批流程,如果公司需要灵活的审批流(如超过3000元需总经理审批),你需要设计一个工作流引擎表:
workflow_rules表:存储条件(金额范围)、当前角色、下一步角色。- 审批时通过动态查询该表来决定
$new_status。
- 上述代码是硬编码的审批流程,如果公司需要灵活的审批流(如超过3000元需总经理审批),你需要设计一个工作流引擎表:
-
状态机模式:
- 强烈建议使用状态机来管理
status字段的变化,而不是用if...else,可以编写一个StateMachine类,定义每个状态允许的转换,避免逻辑混乱。
- 强烈建议使用状态机来管理
-
框架选择:
- Laravel:内置了
Gate(权限)、Event(记录日志)、Model(ORM事务),非常适合此类业务系统。 - ThinkPHP 6+:国内常用,快速开发。
- Laravel:内置了
-
效率:
- 当数据量较大时(如10万条申请),需要对
purchase_requests表根据status和created_at建立复合索引。
- 当数据量较大时(如10万条申请),需要对
一个可以直接运行的采购计划系统,核心思路是:
- 数据流:表单提交 -> 数据库事务写入(主表+明细表) -> 审批后更新状态 -> 生成计划。
- 代码基础:安全(防SQL注入) + 事务 + 状态控制。
- 关键文件:
控制器(CRUD逻辑)+模型(数据库操作)+视图(表单与列表)。
你可以先按照这个结构搭建一个最小可行产品(MVP),然后逐步增加:邮件通知、导出Excel、与仓库系统对接等功能。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播