本文目录导读:

在PHP项目中实现一套完整的报废管理功能,通常需要覆盖申请、审批、执行、归档这几个核心环节,下面是一个通用的设计思路和核心代码实现示例,你可以根据自己的业务逻辑进行调整。
数据库设计(核心表)
至少需要两张表:一张报废申请表,一张报废明细表(如果是单条物品报废,也可以合并)。
-- 1. 报废主表 CREATE TABLE `scrap_orders` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `order_no` varchar(64) NOT NULL COMMENT '报废单号', `dept_id` int(11) NOT NULL COMMENT '申请部门', `applicant_id` int(11) NOT NULL COMMENT '申请人', `total_amount` decimal(10,2) DEFAULT '0.00' COMMENT '报废总价值', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0-待审批, 1-审批通过, 2-已驳回, 3-已完成', `remark` text COMMENT '备注', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 2. 报废明细表 CREATE TABLE `scrap_order_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_id` int(11) NOT NULL COMMENT '关联主表ID', `asset_id` int(11) NOT NULL COMMENT '资产编号', `asset_name` varchar(255) NOT NULL COMMENT '资产名称', `spec` varchar(255) DEFAULT NULL COMMENT '规格型号', `quantity` int(11) NOT NULL DEFAULT '1' COMMENT '报废数量', `unit_price` decimal(10,2) DEFAULT '0.00' COMMENT '原值', `scrap_reason` varchar(500) DEFAULT NULL COMMENT '报废原因', `disposal_method` tinyint(4) DEFAULT '0' COMMENT '处置方式:0-销毁, 1-变卖, 2-回收', PRIMARY KEY (`id`), KEY `order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
后端PHP核心逻辑
建议使用 MVC 结构,这里以 ThinkPHP / Laravel 风格为例,显示核心业务逻辑:
报废单申请(创建)
// ScrapService.php
public function createScrapOrder($data, $items)
{
// 开启事务
DB::beginTransaction();
try {
// 生成单号:日期 + 随机数
$orderNo = 'SCRAP-' . date('Ymd') . '-' . mt_rand(1000, 9999);
// 1. 写入主表
$order = new ScrapOrder();
$order->order_no = $orderNo;
$order->dept_id = $data['dept_id'];
$order->applicant_id = auth()->id();
$order->total_amount = array_sum(array_column($items, 'unit_price'));
$order->status = 0; // 待审批
$order->remark = $data['remark'] ?? '';
$order->save();
// 2. 写入明细表
foreach ($items as $item) {
$orderItem = new ScrapOrderItem();
$orderItem->order_id = $order->id;
$orderItem->asset_id = $item['asset_id'];
$orderItem->asset_name = $item['asset_name'];
$orderItem->spec = $item['spec'] ?? '';
$orderItem->quantity = $item['quantity'];
$orderItem->unit_price = $item['unit_price'];
$orderItem->scrap_reason = $item['scrap_reason'];
$orderItem->disposal_method = $item['disposal_method'] ?? 0;
$orderItem->save();
// 可选:锁定对应的资产,防止重复报废
// Asset::where('id', $item['asset_id'])->update(['status' => 'scrap_pending']);
}
DB::commit();
return ['code' => 1, 'msg' => '申请成功', 'order_id' => $order->id];
} catch (\Exception $e) {
DB::rollBack();
return ['code' => 0, 'msg' => '申请失败: ' . $e->getMessage()];
}
}
审批流程(简单版)
// ScrapService.php
public function approveOrder($orderId, $approveResult, $approveRemark = '')
{
$order = ScrapOrder::find($orderId);
if (!$order) {
return ['code' => 0, 'msg' => '单据不存在'];
}
if ($order->status != 0) {
return ['code' => 0, 'msg' => '当前状态不可审批'];
}
DB::beginTransaction();
try {
if ($approveResult == 1) { // 通过
$order->status = 1; // 审批通过
// 处理资产状态:将资产标记为“已报废”
foreach ($order->items as $item) {
Asset::where('id', $item->asset_id)->update(['status' => 'scrapped']);
}
} else { // 驳回
$order->status = 2;
// 解冻资产状态(如果有锁定)
foreach ($order->items as $item) {
Asset::where('id', $item->asset_id)->update(['status' => 'normal']);
}
}
$order->approve_remark = $approveRemark;
$order->approver_id = auth()->id();
$order->approved_at = now();
$order->save();
DB::commit();
return ['code' => 1, 'msg' => '操作成功'];
} catch (\Exception $e) {
DB::rollBack();
return ['code' => 0, 'msg' => '操作失败: ' . $e->getMessage()];
}
}
资产列表查询(供选择报废资产)
// 获取可报废的资产列表(未被报废、未被借出等)
public function getScrappableAssets($params)
{
$query = Asset::where('status', 'normal')
->where('use_status', 'idle'); // 闲置状态
if (!empty($params['keyword'])) {
$query->where(function($q) use ($params) {
$q->where('asset_name', 'like', '%'.$params['keyword'].'%')
->orWhere('asset_no', 'like', '%'.$params['keyword'].'%');
});
}
return $query->paginate($params['per_page'] ?? 20);
}
前端页面(以Vue为例,仅示意)
通常你需要以下页面:
| 页面 | 功能 |
|---|---|
scrap/create.blade.php |
选择资产 -> 填写原因 -> 提交申请 |
scrap/list.blade.php |
我的申请 / 待我审批 / 全部记录 |
scrap/detail.blade.php |
查看详情 + 审批按钮(根据角色) |
核心交互逻辑(Vue + Element UI 示例):
// 1. 申请页面:多选资产提交
async submitScrap() {
if (this.selectedAssets.length === 0) {
this.$message.warning('请选择至少一项资产');
return;
}
const items = this.selectedAssets.map(asset => ({
asset_id: asset.id,
asset_name: asset.name,
spec: asset.spec,
quantity: 1,
unit_price: asset.price,
scrap_reason: this.reason,
disposal_method: this.disposalMethod
}));
const res = await axios.post('/api/scrap/create', {
dept_id: this.deptId,
remark: this.remark,
items: items
});
if (res.data.code == 1) {
this.$message.success('提交成功');
this.$router.push('/scrap/list');
}
}
权限控制建议
- 普通用户:只能查看自己的申请,只能操作“待审批”状态的单据(不能修改已审批的)。
- 审批人:可以查看所有待审批单据,进行“通过”或“驳回”。
- 管理员:可以查看/导出所有记录,进行最终归档操作。
在控制器中检查权限:
// 在审批方法前
$this->authorize('approve_scrap'); // 使用Laravel Policy或自定义中间件
扩展功能(根据需求)
- 报废单打印:使用
dompdf或TCPDF生成PDF。 - 资产残值回收:如果报废后需要变卖,增加“回收金额”、“买家信息”字段。
- 流程审批流:支持多级审批(部门主管 -> 财务 -> 总经理),可以使用
审批流表+状态机实现。 - Excel导出:使用
PhpSpreadsheet导出所有申请明细。 - 通知机制:提交申请后,通过邮件/钉钉/企业微信通知审批人。
安全问题注意
- 报废操作涉及资产状态变更,必须使用事务保证数据一致性。
- 禁止前端直接传
资产ID修改资产状态,所有变更应在后端校验。 - 对于批量操作,注意 SQL注入 和 越权 问题(尤其是 IDOR,即不安全的直接对象引用)。
如果你使用的是 ThinkPHP 5/6/8 或 Laravel 框架,我可以根据你的具体框架提供更贴合框架语法的代码示例,如果你们的业务包含 多级审批 或 工作流引擎,实现会更复杂一些,需要结合 审批节点表 和 审批记录表 来做状态推进。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播