PHP+MySQL报修网站源码:工单状态流转与部署实战教程
简介面向电脑维修公司的报修网站源代码内置完整的在线报修功能与前台展示页面可帮助传统维修商搭建品牌官网让客户通过网页直接提交故障信息减轻电话沟通成本适应数字化获客需求。资源包共包含570个文件压缩后约1.9MB以gif动图、asp动态脚本、jpg图片、htm页面为主辅以css样式表、db数据库、swf动画、js脚本及asa配置等覆盖从前端展示到后台逻辑与数据存储的主要环节适合用于部署或二次开发。若具备基础ASP知识可直接修改栏目和数据库定制维修类型、价格展示与报修流程对于想学习动态网站开发的朋友也可通过这套代码研究经典ASP页面、数据库交互和整站结构。目前已有878人学习下载体量虽小但功能脉络完整是一款实用且值得参考的建站源码。1. 电脑维修公司的报修网站源码先搞清这套系统到底要替你做什么你打开某源码站看到“电脑维修公司带报修网站源码”这个标题第一反应大概是能提交报修单、能显示进度完事。这想法没错但只对了一半。真正的报修网站核心不在“提交”而在“流转”——一个报修单从用户手里提交之后要经过接单、派工、上门、维修、回访、关闭这条完整的链路每个环节都有对应的人要看到它、操作它。没有这套后台流转那个前台表单就是个只进不出的黑匣子用户提交完心里没底店里也照样靠手写单子排活。这套源码解决的是维修店日常经营里最真实的一个痛点报修电话一来前台记一张纸条师傅回来再对一遍纸条哪个单子做完没做完全凭脑子记。报修网站把这些搬上线让用户自己填故障、自己查进度店里统一在后台接单派单。适合两类人一类是自己开维修店、想花小成本把接单流程正规化的老板另一类是接外包开发的技术人员拿这套源码改一改交付给客户。下面按“业务流程 → 数据表 → 核心代码 → 部署 → 避坑 → 进阶”往下拆全部按实际能落地的做法讲。2. 先立业务模型再写代码报修单的状态机与数据表设计2.1 报修流程的六个状态节点从“待接单”到“已关闭”写报修系统与写普通展示站最大的不同是它有一个贯穿始终的业务主线工单状态。状态设计得不好后面后台每个功能都会跟着别扭。我建议用六个状态顺序固定状态值含义操作人触发动作0待接单用户提交报修单成功1已接单前台/管理员后台点击“接单”2维修中维修员接单后开始上门/到店维修3待回访管理员维修员提交完工记录4已完成管理员/前台回访无问题关闭工单5已取消用户/管理员任意状态可取消状态字段建议用tinyint存数字而不是直接存中文。原因有两个一是数字改起来灵活——将来想插入一个“待配件”状态只要把值改成 6不用动表结构二是前端渲染时用switch映射中文名和颜色待接单用橙色、已完成用绿色后端只认数字不认文字。很多早期源码在这里直接用varchar存“已完成”这种字符串看着直观但后面做统计报表时where status 已完成一旦打错字整条数据就漏掉了属于给自己埋雷。2.2 核心数据表设计报修单、客户、维修记录三张表怎么组织我不建议把报修系统做成一张大表存所有字段。客户信息、报修单、维修记录拆开职责更清楚而且后续要做“老客户回头率”“单个维修员工单量”这类统计时直接关联查询就行不用从一串 JSON 里往外掏。建表 SQL 按下面这种写法-- 客户表只存与客户本人相关的信息 CREATE TABLE customer ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 联系人姓名, phone varchar(20) NOT NULL COMMENT 手机号, address varchar(255) DEFAULT NULL COMMENT 上门地址到店维修可不填, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 报修单表业务主表一条记录代表一次报修 CREATE TABLE repair_order ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 工单号日期随机码, customer_id int(11) NOT NULL COMMENT 关联客户ID, device_type varchar(50) DEFAULT NULL COMMENT 设备类型笔记本/台式/显示器, fault_desc text COMMENT 故障描述, appointment_date date DEFAULT NULL COMMENT 预约日期, appointment_slot tinyint(1) DEFAULT NULL COMMENT 时段0上午 1下午 2晚上, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 状态0待接单 1已接单 2维修中 3待回访 4已完成 5已取消, assignee varchar(50) DEFAULT NULL COMMENT 维修员姓名, fee decimal(10,2) DEFAULT NULL COMMENT 维修费用, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_customer_id (customer_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修工单主表; -- 维修记录表一次报修可多条维修记录 CREATE TABLE repair_log ( id int(11) NOT NULL AUTO_INCREMENT, order_id int(11) NOT NULL COMMENT 报修单ID, action varchar(20) NOT NULL COMMENT 动作接单/开工/完工/回访, remark varchar(255) DEFAULT NULL COMMENT 备注完工时填维修内容与费用, operator varchar(50) NOT NULL COMMENT 操作人姓名, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工单状态变更日志;这里最核心的是repair_order表里的status、appointment_date和appointment_slot。order_no是给用户查进度用的凭据生成规则不能用简单的自增 ID否则用户输个 1、2、3 就能遍历别人的工单。常见做法是date(Ymd) . random_int(100000, 999999)撞号概率极低。updated_at用ON UPDATE CURRENT_TIMESTAMP自动更新这样后台列表按 “最后更新时间倒序” 排就能保证刚处理完的单子永远在最上面。2.3 为什么用 PHP MySQL 而不是 Java 或 Node小团队源码的选型逻辑市面上大量这类源码选 PHP MySQL不是没有道理。报修网站是典型的中低并发业务系统一个维修店一天能产生几十张报修单已经是忙疯了峰值并发撑死几十个请求。这种量级下PHP 的部署成本和维护成本是最低的——买一台 2 核 4G 的云服务器装个宝塔面板拖上去就能跑MySQL 和数据表就在手边。Java 那套要装 JDK、Maven、打包、配 TomcatNode 要处理 npm 依赖和进程守护对完全没有专职运维的维修店来说出一次问题就是一整天的生意停摆。我个人接这类外包时也会首选 PHP 交付给不懂技术的客户。不是 PHP 比别的语言高级而是它让客户后续找人改代码的门槛最低——随便一个做网站的小工作室都会 PHP客户不会被单一技术栈绑死。当然如果你自己擅长别的语言用 Spring Boot 写一个也没毛病只要把上节的三张表和状态流转理清楚业务逻辑都是一样的。真正决定这套源码好不好用的从来不是语言而是那张状态机有没有覆盖全流程。3. 用 PHP 把报修闭环跑起来前台表单、进度查询与后台工单流转3.1 前台报修表单用户提交时前端校验和后端过滤各管什么前台页面不需要做得花哨一个公司介绍、一个服务范围列表、一个报修表单够了。表单字段设置上我会限制成“用户必填四项”姓名、手机号、设备类型、故障描述。四项已经能支撑后续所有业务判断再加必填项就是给用户添堵。预约日期和时段做成选填能填最好不填就按“尽快联系”处理。!-- 前台报修表单关键片段 -- form idrepairForm methodpost actionsubmit_repair.php div label联系人姓名/label input typetext namename required maxlength20 /div div label手机号/label input typetel namephone required pattern1[3-9][0-9]{9} placeholder11位手机号 /div div label设备类型/label select namedevice_type required option value请选择/option option valuedesktop台式机/option option valuelaptop笔记本/option option valuemonitor显示器/option option valueother其他外设/option /select /div div label故障描述/label textarea namefault_desc required maxlength500 placeholder请描述故障现象如开机黑屏、蓝屏代码、进水等/textarea /div div label预约日期/label input typedate nameappointment_date /div div label时段/label select nameappointment_slot option value不指定/option option value0上午/option option value1下午/option option value2晚上/option /select /div button typesubmit提交报修/button /form用户点“提交”之后前端的required和pattern拦截掉明显不合法输入比如空名字、手机号少一位。但前端校验只是用户体验层面的——任何人用 curl 直接 POST 请求就能绕过前端。所以真正决定数据安全的逻辑在submit_repair.php里?php session_start(); require_once config.php; // 里面包含 PDO 连接 $pdo header(Content-Type: application/json); $name trim($_POST[name] ?? ); $phone trim($_POST[phone] ?? ); $device_type $_POST[device_type] ?? ; $fault_desc trim($_POST[fault_desc] ?? ); $appointment_date $_POST[appointment_date] ?? null; $appointment_slot $_POST[appointment_slot] ?? null; $errors []; // 服务端二次校验长度、格式、黑名单 if (mb_strlen($name) 2 || mb_strlen($name) 20) { $errors[] 姓名长度需在2-20字之间; } if (!preg_match(/^1[3-9][0-9]{9}$/, $phone)) { $errors[] 手机号格式不正确; } if (!in_array($device_type, [desktop, laptop, monitor, other], true)) { $errors[] 设备类型不合法; } if (mb_strlen($fault_desc) 5 || mb_strlen($fault_desc) 500) { $errors[] 故障描述需在5-500字之间; } if (!empty($errors)) { echo json_encode([code 0, msg implode(, $errors)]); exit; } // 生成工单号日期 6位随机码 $order_no date(Ymd) . random_int(100000, 999999); // 查询或创建客户记录 $stmt $pdo-prepare(SELECT id FROM customer WHERE phone ?); $stmt-execute([$phone]); $customer $stmt-fetch(); if ($customer) { $customer_id $customer[id]; // 更新一下地址等可变信息 $stmt $pdo-prepare(UPDATE customer SET name ?, address COALESCE(NULLIF(?, ), address) WHERE id ?); $stmt-execute([$name, $address ?? , $customer_id]); } else { $stmt $pdo-prepare(INSERT INTO customer (name, phone, address) VALUES (?, ?, ?)); $stmt-execute([$name, $address ?? , ]); $customer_id $pdo-lastInsertId(); } // 插入报修单 $stmt $pdo-prepare(INSERT INTO repair_order (order_no, customer_id, device_type, fault_desc, appointment_date, appointment_slot) VALUES (?, ?, ?, ?, ?, ?)); $stmt-execute([$order_no, $customer_id, $device_type, $fault_desc, $appointment_date, $appointment_slot]); echo json_encode([code 1, msg 提交成功, order_no $order_no]);两个容易被忽略的细节。第一个. $address ?? .这种写法是 PHP 7 的NULL 合并运算符意思是从$_POST里取不存在的字段时不会报undefined index的提示直接落空字符串。很多老源码还在用$_POST[address]不判空打开报错显示后满屏 Notice。第二个客户表用手机号做唯一匹配老客户再次报修时不重复建记录——这个设计在后续做“老客户回访”时非常省事直接WHERE phone ?拉出全部历史工单。3.2 报修进度查询用手机号加工单号查出状态用户报修后最关心的就是“我的单子到哪一步了”。进度查询页就是干这个的别把它设计得太复杂。一个工单号输入框一个手机号输入框点查询显示当前状态 状态变更时间轴。?php // query_status.php require_once config.php; $order_no trim($_GET[order_no] ?? ); $phone trim($_GET[phone] ?? ); // 参数合法性检查 if (!preg_match(/^\d{13}$/, $order_no) || !preg_match(/^1[3-9][0-9]{9}$/, $phone)) { echo json_encode([code 0, msg 工单号或手机号格式不正确]); exit; } // 关联查询工单号 手机号双条件匹配防止有人拿工单号遍历 $sql SELECT o.order_no, o.status, o.fault_desc, o.device_type, o.appointment_date, o.appointment_slot, o.assignee, o.fee, c.name AS customer_name FROM repair_order o INNER JOIN customer c ON o.customer_id c.id WHERE o.order_no ? AND c.phone ?; $stmt $pdo-prepare($sql); $stmt-execute([$order_no, $phone]); $order $stmt-fetch(PDO::FETCH_ASSOC); if (!$order) { echo json_encode([code 0, msg 未找到对应的报修单]); exit; } $status_map [ 0 待接单, 1 已接单, 2 维修中, 3 待回访, 4 已完成, 5 已取消 ]; // 取状态变更时间轴 $stmt $pdo-prepare(SELECT action, remark, operator, created_at FROM repair_log WHERE order_id ? ORDER BY created_at ASC); $stmt-execute([$order[id]]); $logs $stmt-fetchAll(PDO::FETCH_ASSOC); echo json_encode([ code 1, order [ order_no $order[order_no], status $status_map[$order[status]] ?? 未知状态, device_type $order[device_type], fault_desc $order[fault_desc], assignee $order[assignee] ?? 待分配, fee $order[fee] ?? 待定, timeline $logs ] ]);这里最关键的一条 SQL 是WHERE o.order_no ? AND c.phone ?。用户必须同时知道工单号和手机号才能查到进度少一个都查不出来。很多初次写的开发者只用工单号匹配攻击者随便试几个工单号就能看到别人的故障描述、家庭地址这是最典型的水平越权漏洞也是这种小系统最容易翻车的地方。查询时前端做个轮询每 30 秒拉一次接口状态一变页面上就刷出来用户体验完全不输那些要做 App 的方案。3.3 后台工单列表与状态流转接单、完工、回访怎么改状态后台是管理员和维修员用的页面。列表要解决的核心诉求是“一屏看清今天有哪些单、分别在什么状态”。我习惯在列表页做一个按状态筛选的 Tab待接单、待回访、维修中、全部。其中“待接单”和“待回访”是最高优先级——前者代表有没有人接后者代表有没有人收尾。?php // admin/order_list.php示意核心逻辑 require_once ../config.php; session_start(); // 鉴权未登录管理员直接跳走 if (empty($_SESSION[admin_id])) { header(Location: login.php); exit; } $status_filter $_GET[status] ?? ; $status_filter in_array($status_filter, [0, 1, 2, 3, 4, 5], true) ? $status_filter : ; $sql SELECT o.id, o.order_no, o.device_type, o.fault_desc, o.appointment_date, o.appointment_slot, o.status, o.assignee, o.fee, c.name, c.phone, c.address FROM repair_order o INNER JOIN customer c ON o.customer_id c.id; $params []; if ($status_filter ! ) { $sql . WHERE o.status ?; $params[] $status_filter; } $sql . ORDER BY o.created_at DESC LIMIT 50; $stmt $pdo-prepare($sql); $stmt-execute($params); $orders $stmt-fetchAll(PDO::FETCH_ASSOC); // 按预约日期分组显示没预约的单独一列放最前面方便今天优先安排 foreach ($orders as $order) { echo tr; echo td . htmlspecialchars($order[order_no]) . /td; echo td . htmlspecialchars($order[name]) . /td; echo td . htmlspecialchars($order[phone]) . /td; echo td . htmlspecialchars($order[device_type]) . /td; echo td . htmlspecialchars($order[fault_desc]) . /td; echo tdbutton onclickchangeStatus( . $order[id] . , . $order[status] . )流转/button/td; echo /tr; }状态流转的动作全部走changeStatus接口。接单、完工、回访本质都是同一件事更新主表状态 插入一条日志。区别只在“由谁操作”和“可转入的状态”。后台每次流转时检查一下当前状态防止用户同时开多个页面导致状态错乱?php // admin/change_status.php require_once ../config.php; session_start(); if (empty($_SESSION[admin_id])) { exit(json_encode([code 0, msg 未登录])); } $order_id (int)($_POST[order_id] ?? 0); $new_status (int)($_POST[status] ?? -1); $remark trim($_POST[remark] ?? ); $operator $_SESSION[admin_name]; // 状态流转白名单0-1, 1-2, 2-3, 3-4, 5 任意可取消 $allowed_transitions [ 0 [1, 5], 1 [2, 5], 2 [3, 5], 3 [4, 5], 4 [5] ]; // 读取当前状态 $stmt $pdo-prepare(SELECT status FROM repair_order WHERE id ?); $stmt-execute([$order_id]); $current $stmt-fetchColumn(); if ($current false) { exit(json_encode([code 0, msg 工单不存在])); } // 检查目标状态是否在允许列表里 if (!isset($allowed_transitions[$current]) || !in_array($new_status, $allowed_transitions[$current], true)) { exit(json_encode([code 0, msg 非法的状态流转])); } // 更新主表 $stmt $pdo-prepare(UPDATE repair_order SET status ?, updated_at NOW() WHERE id ?); $stmt-execute([$new_status, $order_id]); // 写日志 $stmt $pdo-prepare(INSERT INTO repair_log (order_id, action, remark, operator) VALUES (?, ?, ?, ?)); $stmt-execute([$order_id, $new_status, $remark, $operator]); echo json_encode([code 1, msg 操作成功]);状态流转白名单是这套后台的保险丝。没有白名单的话一个手滑就能把状态从“待回访”直接点成“已完成”回访环节被跳过。有了白名单2→4 这种跳级操作会被直接拒绝。完工时在备注里填维修内容和费用回访时在备注里填客户反馈这些最终都会拼进用户端的时间轴里。到这里报修的完整闭环已经通了用户提交 → 后台接单 → 维修员开工 → 完工登记 → 回访关闭每一步都留痕。4. 部署到一台服务器上环境配置、目录结构和 .htaccess 要点4.1 服务器环境LNMP 或宝塔面板的推荐配置这类源码部署最省心的方式是买一台云服务器装宝塔面板再在里面装 LNMPLinux Nginx MySQL PHP。不推荐用 Apache 更多是性能考虑——Nginx 对这种小站点的并发处理要好很多而且伪静态规则也很成熟。买服务器时不用追求高配报修网站 一个品牌展示页2 核 2G 都够跑好几年带宽选 3M 到 5M 就行图片不多的话 3M 足够。软件推荐版本说明CentOS / Ubuntu64 位任何一个都行按你熟悉的选Nginx1.18作为 Web 服务器和反向代理PHP7.2 7.4兼容性最好8.x 对老源码可能有函数兼容问题MySQL5.7 或 8.05.7 省内存8.0 性能更好二选一宝塔面板最新版免费版就够管理网站和数据库PHP 版本这里要特别提醒一句很多老源码还写着mysql_connect这种在 PHP 5 时代就废弃的函数如果你拿到一份源码先打开config.php看用的是mysqli还是PDO。如果是mysqliPHP 7.4 还能跑如果是mysql_函数只能改装 PHP 5.6 或者先花半小时改造成mysqli。这步不做后面打开页面直接白屏加一个“Call to undefined function mysql_connect”。我个人的习惯是拿到任何源码第一步就把数据库层统一改成PDO后面换库、预处理防注入都方便。4.2 目录结构与配置文件源码拿到手先改哪几个文件一份合格的报修网站源码拿到手后目录结构应该是清楚分层的不需要在里面翻半天找入口文件。我按自己交付的习惯给一个参考结构文件/目录职责拿到源码后要做什么config.php数据库连接、站点基础配置改数据库名、用户名、密码submit_repair.php前台报修表单提交接口一般不用改query_status.php前台进度查询接口一般不用改admin/login.php后台登录改默认账号密码admin/order_list.php后台工单列表一般不用改admin/change_status.php状态流转接口一般不用改uploads/用户上传图片目录确认目录有写权限install.sql建表语句导入数据库用config.php是这套源码的中枢神经。网上流传的源码里这一文件的编码格式最混乱。拿到手以后用 VS Code 或 Notepad 打开先把编码转成 UTF-8 无 BOM——很多乱码问题就是 UTF-8 带 BOM 导致 PHP 输出内容前多了一个看不见的字符接口返回的 JSON 直接解析失败。然后修改 DB 连接信息如下?php // config.php define(DB_HOST, 127.0.0.1); define(DB_NAME, repair_db); define(DB_USER, repair_user); define(DB_PASS, 你的强密码); define(DB_CHARSET, utf8mb4); try { $pdo new PDO( mysql:host . DB_HOST . ;dbname . DB_NAME . ;charset . DB_CHARSET, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES false ] ); } catch (PDOException $e) { exit(数据库连接失败请检查配置 . $e-getMessage()); }PDO::ATTR_EMULATE_PREPARES false这一行行参数很关键。它关闭 PDO 的预处理模拟功能强制 MySQL 原生预处理能有效防止 SQL 注入。很多源码里默认不写这个在旧版本 MySQL 上会有被拼接注入的风险。数据库用户名和密码不要用 root单独建一个repair_user账号只给业务库的增删改查权限万一源码有漏洞被拖库也影响不到服务器上其他数据。4.3 伪静态与路由器报修进度查询的 URL 重写后台接口和前端页面跑通之后还有一步收尾的活儿给前台页面配上像样的 URL。宝塔面板里默认建站用的是http://你的域名/index.php这种形式能用但链接丑也不利于别人记住。改伪静态规则后地址变成http://你的域名/query/20250101123456分享给客户的时候显得专业很多。Nginx 的伪静态规则写在站点配置文件的location块里。以进度查询页为例location / { if (!-e $request_filename) { rewrite ^/query/(\d{13})$ /query_status.php?order_no$1 last; rewrite ^/repair$ /repair.html last; rewrite ^/admin$ /admin/login.php last; } }那行rewrite ^/query/(\d{13})$ /query_status.php?order_no$1 last;的意思是只要 URL 匹配/query/后面跟着 13 位数字Nginx 就把这个请求转给query_status.php处理同时把$1里捕获到的数字作为order_no参数传进去。13 位数字正好对应Ymd 6位随机码的工单号格式规则写严一点还能顺便拦截掉格式不对的恶意请求。写伪静态时务必注意站点用的 PHP 运行方式要选 “Unix Socket”不要选 “TCP Socket”否则 Nginx 连不上 PHP 进程重写后的页面会报 502。这个坑常出现在宝塔面板部署时——面板默认配置如果是 TCP你需要去 PHP 设置页把运行方式切一下然后重启服务。502 一出现检查顺序是PHP 进程起没起 → Socket 路径对不对 → 站点配置有没有语法错误这三步能解决九成问题。5. 上线前必看的避坑清单那些让报修网站“看起来能用”实则翻车的细节5.1 用户手机号随便填回访电话全打到空气里现象前端pattern校验只挡了手输错误挡不住恶意填11111111111。结果后台一堆假手机号回访时拨出去全是空号统计报表里的“老客户回头率”也废了。原因没有做手机号真实性的校验。道理很简单表单上验证手机号格式只能证明它“长得像手机号”不能证明这个号码是本人的。解决至少加一个短信验证码。国内有很多短信服务商一条验证码几分钱报修场景下用户完全能接受“输入验证码后再提交”。如果必须投放这套源码给客户且客户不想付短信费退而求其次的做法是提交成功后加一步电话确认流程——后台“待接单”状态的单子先人工去电确认确认后再接单。流程绕一点但至少不会把假号码直接当成有效客户数据。5.2 只存了预约日期没存时间段师傅一天跑四趟空路现象用户填写了预约日期没选时段。早上九点师傅到门口用户不在家打电话一问才知道人家约的是晚上六点以后。原因数据表里只有appointment_date没有appointment_slot字段或者有字段但前台不展示不填写。这是报修类网站源码最常见的偷懒设计——把时间段当成可选项省掉。解决预约时段必须做成必选项至少分“上午/下午/晚上”三档。然后在后台列表按“日期时段”升序排列同一个时段的单子划给同一个维修员让师傅一次出门处理两三家。这个字段要在建表时留好后面加的话迁移数据很麻烦。5.3 后台接口裸奔改个状态不用登录现象一个维修员在手机上直接访问/admin/change_status.php传一个order_id和status发现居然能直接改状态什么都拦不住。原因很多源码把前端页面的登录拦截做好了但接口文件里没有鉴权。开发者误以为“只要能打开这个 URL 的页面就是登录用户”完全没意识到接口可以被直接调用。解决在每个接口 PHP 文件的第一行加会话检查参考前面admin/change_status.php里的session_start(); if (empty($_SESSION[admin_id]))写法。更稳妥的做法是给后台所有接口统一加一个admin_auth.php每个接口文件开头require_once admin_auth.php;一处维护、处处生效。上线前把后台所有接口的 URL 挨个用浏览器无痕模式试一遍没登录能不能直接调通这是验收清单里的固定一项。5.4 图片上传验了扩展名没验内容被传了个 php 上去现象用户上传故障照片的接口只检查了后缀是不是.jpg结果有人把一句话木马改名.jpg上传成功服务器被种了后门。原因文件上传校验只查了扩展名没查文件头。攻击者不需要构造什么高级 payload只要把 PHP 代码保存为shell.php.jpg如果代码只检查末尾扩展名就能绕过去。这是从老 PHP 教程里传下来的经典漏洞不少人还在踩。解决上传后不要用用户给的原始文件名重新生成一个随机文件名并且强制把文件按扩展名分类放到不同目录。同时校验文件头部字节——JPEG 文件头是FF D8 FFPNG 是89 50 4E 47用finfo_file()函数读 MIME 类型判断而不是只信后缀。最后在 Nginx 配置里禁用uploads目录的 PHP 执行权限location ~* ^/uploads/.*\.(php|php5)$ { deny all; }这一行配置写进站点配置文件然后重载uploads 目录里就算被传了个 PHP 文件上去执行的也只会是 403物理层面断掉后门这条路。血泪经验文件上传功能是所有源码里最值得花时间加固的地方没有之一。5.5 状态流转没写日志工单改错了连后悔药都没有现象管理员把一个工单从“维修中”直接点成了“已完成”后来发现这位客户的硬盘其实还在等配件想改回“维修中”但后台没有任何记录能证明这个单子之前到过哪一步、是谁改的。原因状态流转时只更新了主表的status字段没有写repair_log。等于每一步操作都没有留痕出了纠纷扯不清责任想恢复数据也找不到依据。解决强制在每次状态变更时写入repair_log字段包括订单 ID、动作、备注、操作人、时间。前面第三章的change_status.php已经写了这个逻辑部署时确认这套代码没被删减就行。另外如果客户说“我从来没取消过工单”日志里会清楚地记录是谁在什么时间取消的这比任何口头解释都有说服力。我给客户交付时会把“打开repair_log看操作记录”写进维护文档里算是一颗后悔药。6. 进阶把报修网站做成店里的第二台接单电话基础闭环跑通以后这套系统的价值还能再往上顶一层把它从“用户主动来查进度”的被动工具变成“店里有动作就主动告诉他”的主动通知渠道。这一步通常不需要改数据库结构核心是加一个消息推送环节。第一个必做的是状态变更通知。当后台把工单从“待接单”改为“已接单”时调一次企业微信机器人 webhook把工单号、预约时间、维修员姓名推送到客户的微信上。实现很轻一个curlPOST 就够?php // notify.php 片段发企业微信机器人通知 function send_wechat_notify($webhook_url, $content) { $data json_encode([ msgtype text, text [content $content] ]); $ch curl_init($webhook_url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_HTTPHEADER, [Content-Type: application/json]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response curl_exec($ch); curl_close($ch); return $response; } // 状态变为 1已接单时调用 $content 您的报修单 {$order_no} 已接单维修员 {$assignee} 将在 {$appointment_date} {$slot_text} 联系您。; send_wechat_notify(https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key你的key, $content);注意企业微信机器人只对企业内部群有效如果你想推给终端客户用的是企业微信“客户联系”的消息推送能力需要一个正式认证的企业微信每天有消息条数限制。小维修店初期用机器人推给内部群、再由前台手动转发给客户是最省钱的过渡做法。等单量大了再上认证。第二个建议是给派单逻辑加一个简单的自动分配状态从“待接单”变为“已接单”时按“当前维修中工单最少”的维修员来接而不是管理员手动指定。这个逻辑在change_status.php里加一个查 $pdo 统计的步骤把assignee字段在流转时自动填上即可。有人可能会问维修员水平不一样自动分配好不好我自己的判断是报修单只有两种——普通故障和疑难杂症。普通故障自动分疑难杂症加一个“跳过自动分配”的手动指派按钮就解决了不要把简单的事搞复杂。上线后第一周只看三个数据单日新增报修单数、平均接单时长从提交到接单的时间、回访完成率。这三个数能直接反映这套系统有没有真的用起来。如果新增单数上来了但接单时长很长说明后台没人盯新单去把通知配置检查一遍如果回访完成率低说明店里的流程还没完全迁到系统里需要店长在晨会上把“每天下班前清空待回访”立成硬规矩。技术上的代码我已经给你了但真正让这套源码产生价值的是把“用户在系统里留了单子店里真的有人接”这口气打通——网站本身只是工具业务习惯才是终点。我自己的项目里第一周永远盯后台的“待接单”列表到点清空这个习惯保持住报修网站才算真正落地。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →