多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

PHP+MySQL电脑维修报修网站源码:工单管理系统完整实现

PHP+MySQL电脑维修报修网站源码:工单管理系统完整实现 简介一套面向电脑维修公司的网站源码核心亮点是内置在线报修功能用户可直接在网页提交故障信息帮助传统维修门店建立线上服务入口提升接单响应效率。源码由ASP动态页面、HTML/CSS前端页面、JavaScript交互脚本构成并搭配GIF、JPG、SWF等图片与动画素材同时附带数据库及配置文件目录覆盖前台展示、报修表单、后台管理等模块。压缩包共570个文件整包仅1.9MB部署门槛低。目前已有877人学习下载。对于网站开发学习者而言可从中梳理ASP数据库的整站请求处理流程尤其能观察报修数据从页面采集到入库存储的实现细节对于维修公司也可直接部署后二次开发定制品牌页面、报修字段与后台通知方式快速获得一套轻量实用的业务站点。 电脑维修公司最头疼的事不是修不好电脑而是报修流程乱成一锅粥。客户电话打进来手忙脚乱记个地址等师傅上门才发现型号没问清、故障现象没记全甚至漏单。我这里整理了一套自用的电脑维修公司带报修网站的源码方案覆盖从客户提交报修、后台派单、维修进度跟踪到完工归档的完整闭环适合一两百台月维修量的小型维修门店直接部署使用也适合想自己接单搞维修工作室的开发者拿去改造。这套东西是我连续踩了几个星期的坑才打磨顺的不是那种只能看不能跑的演示项目。下面从设计思路、核心功能、数据库建模到实际编码逐个环节拆开讲代码都是可以直接照抄的水平。1. 项目整体设计与思路拆解1.1 为什么不做成小程序或直接上现成工单系统一开始我也纠结过市面上现成的工单系统比如某些 SaaS 平台直接租用不就行了但实际跑了一圈下来维修门店的报修场景跟软件公司的IT工单场景差别很大现成系统普遍死板而且客户提交报修时还要注册账号、填一堆东西门槛太高。自己做一套网站源码的好处很明显一是报修入口可以放到微信公众号菜单、官网首页、甚至二维码桌牌上客户扫码即填二是工单状态可以完全按维修店的习惯定制比如“检测中”“等配件”“已修复待取件”这种颗粒度现成系统根本给不了三是数据在自己手里客户修过什么、换了什么配件、收了多少人工费全都能沉淀下来做二次营销。小程序虽然体验好但审核周期和开发成本不是小门店能接受的网站源码部署到自己服务器上挂个域名当天就能用。1.2 整体功能划分与角色权限这套源码我拆成了两个大端客户报修前台和管理员后台。前台要极简后台要够用。客户在首页能看到维修服务范围、价目说明最关键的是两个入口一个是“在线报修”按钮点进去填故障设备信息另一个是“维修进度查询”输入报修单号就能看到当前状态。两个入口分开是因为客户不想为了查个进度去注册登录直接输单号最省事。管理后台按角色分为两类账号管理员和维修工程师。管理员负责派单、定价、统计维修工程师只看到分配给自己的工单能修改维修状态、填写维修记录和更换配件清单。后台不设财务角色小门店没必要收款统计让管理员一个人看就够了。1.3 技术选型能用就行但不将就这套源码我采用 PHP MySQL 的组合前台页面用原生 HTML/CSS/JavaScript后台管理界面用轻量的 Bootstrap 框架。选 PHP 的理由很直接虚拟主机就能跑部署成本极低不挑服务器环境而且源码逻辑清晰真的出了问题随便找个会 PHP 的人都能改。如果读者本身熟 Node.js 或 Python照同样的思路换语言实现完全没问题核心是业务建模的思路不是某个语言的语法。数据库用 MySQL 5.7 以上版本编码统一 utf8mb4避免客户填的生僻字或者 emoji 存进去变问号。不过要注意PHP 7.4 已经停止安全维护了新部署建议直接上 PHP 8.2代码里我尽量避免用旧版特性和不安全的写法。2. 核心功能细节解析与实操要点2.1 报修单字段设计少而准报修表单设计说起来就一条原则客户少打字字段少而准。我调试字段时砍了四轮才定稿最终保留下来的字段如下字段表单控件为什么必须有客户姓名单行文本上门时要称呼也方便统计联系电话单行文本联系用要前端校验11位手机号设备类型下拉选择笔记本/台式机/一体机/打印机/其他品牌型号单行文本师傅出门前能查资料备配件故障描述多行文本核心字段我做了关键词提示期望上门时间日期选择减少反复电话沟通成本所在区域下拉选择按区域派单降低师傅跑路成本关于故障描述这个字段我要多说两句。如果只给一个文本框客户基本写不了几句维修师傅还得打电话追问。我的做法是在文本框下方加了几个常用故障标签蓝屏、不开机、进系统慢、异响、进水、软件安装客户点一下标签就自动填入描述框可以多选组合。这个互动设计很简单但实测能把有效故障信息的比例从四成提升到八成师傅不用再浪费一轮沟通成本。2.2 工单编号规则与状态机设计工单编号我建议用WX 年月日 当日序号的格式例如 WX20250617003一眼就能看出是哪天接的单、当天第几单。这个编号同时也是客户查询进度用的凭证不需要额外生成查询密码降低客户操作负担。在数据库里给这列加上唯一索引并发提交时也不会重号。工单状态是整个系统的核心我设计了七个状态节点待受理客户提交成功已派单管理员指派给工程师检测中工程师已联系客户并开始检测待配件需要订货等配件维修中配件到货正在修已修复待取件客户可以上门取走已完成客户取件订单关闭已取消客户或管理员取消每个状态变更时系统自动记录操作人、操作时间和备注。这样一来客户在查询页看到的不只是一个干巴巴的“维修中”而是能看到“6月17日 10:30 已联系客户确认故障为硬盘损坏等待配件到货”体验感完全不一样。2.3 后台工单看板与派单逻辑后台首页我设计成一个工单看板按状态分组展示当日工单卡片卡片上显示客户信息、设备信息、故障摘要和当前状态徽标。管理员看板一眼扫过去就知道今天有几单要派、几单在等配件、几单可以通知取件。这个比传统的表格列表直观太多实际用下来管理员每天的派单操作能控制在十分钟以内。派单逻辑这里有个细节每个工程师可以设置自己负责的电脑品牌或者技能标签比如擅长苹果本、擅长数据恢复管理员在派单时能看到匹配度提示。这套逻辑我没有做自动派单因为小门店的派单要考虑师傅当前位置、当日工作量等动态因素人工派单加标签提示是效率最高、最不容易出错的方案。注意状态变更和派单操作必须有权限校验工程师不能把工单派给另一个人不能修改报价金额这些操作只对管理员开放。我遇到过工程师误操作导致客户被多收费的折腾事后来干脆把权限写死工程师只能改技术相关的状态和备注。3. 实操过程与核心环节实现3.1 数据库表结构设计整站一共五张核心表用户表admins、客户表customers、工单表orders、工单日志表order_logs、配件更换表parts。其中工单表的设计我要重点展示一下这是整套系统的中枢。CREATE TABLE orders ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(20) NOT NULL COMMENT 工单编号, customer_name varchar(50) NOT NULL COMMENT 客户姓名, customer_phone varchar(20) NOT NULL COMMENT 联系电话, device_type varchar(20) NOT NULL COMMENT 设备类型, device_brand varchar(100) DEFAULT NULL COMMENT 品牌型号, fault_desc text NOT NULL COMMENT 故障描述, expect_time date DEFAULT NULL COMMENT 期望上门时间, district varchar(50) DEFAULT NULL COMMENT 所在区域, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 状态:0待受理 1已派单 2检测中 3待配件 4维修中 5待取件 6已完成 7已取消, engineer_id int(11) DEFAULT NULL COMMENT 维修工程师ID, quoted_price decimal(10,2) DEFAULT 0.00 COMMENT 报价金额, remark text COMMENT 管理员备注, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_status (status), KEY idx_phone (customer_phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修工单表;工单状态字段我用tinyint而不是字符串排序快、存储小但对应的状态文案在代码里统一映射不散落在 SQL 查询里。电话号码单独建了索引因为客户来电查询时通常只报手机号。3.2 前台报修提交接口实现前端提交报修表单后PHP 接收数据要做三层校验必填项不能为空、手机号格式正确、故障描述长度不少于10个字。同时后端加了一个非常简单的频率限制——同一个 IP 一小时内只能提交5次报修单防止有人恶意刷单导致后台堆满垃圾工单。// submit_order.php 核心处理逻辑简化版 session_start(); // 简单频率控制同一IP一小时内最多5单 $ip $_SERVER[REMOTE_ADDR]; $key repair_ . $ip; $count (int)($_SESSION[$key][count] ?? 0); $hour $_SESSION[$key][hour] ?? date(Y-m-d H:00:00); if ($hour ! date(Y-m-d H:00:00)) { $_SESSION[$key] [count 0, hour date(Y-m-d H:00:00)]; $count 0; } if ($count 5) { exit(json_encode([code 403, msg 报修提交太频繁请一小时后再试或直接电话联系])); } $_SESSION[$key][count] $count 1; // 字段校验 $name trim($_POST[name] ?? ); $phone trim($_POST[phone] ?? ); $deviceType trim($_POST[device_type] ?? ); $faultDesc trim($_POST[fault_desc] ?? ); if ($name || mb_strlen($name) 20) exit(json_encode([code 400, msg 请填写正确的姓名])); if (!preg_match(/^1[3-9]\d{9}$/, $phone)) exit(json_encode([code 400, msg 请填写正确的11位手机号])); if (mb_strlen($faultDesc) 10) exit(json_encode([code 400, msg 请详细描述故障现象至少10个字])); // 生成工单号WX 日期 当天序号 $datePrefix date(Ymd); $stmt $pdo-prepare(SELECT COUNT(*) FROM orders WHERE order_no LIKE ?); $stmt-execute([$datePrefix . %]); $seq $stmt-fetchColumn() 1; $orderNo WX . $datePrefix . str_pad($seq, 3, 0, STR_PAD_LEFT); // 写入数据库 $stmt $pdo-prepare(INSERT INTO orders (order_no, customer_name, customer_phone, device_type, device_brand, fault_desc, expect_time, district, status, created_at) VALUES (?,?,?,?,?,?,?,?,0,NOW())); $stmt-execute([$orderNo, $name, $phone, $deviceType, $_POST[brand] ?? , $faultDesc, $_POST[expect_time] ?? null, $_POST[district] ?? ]); exit(json_encode([code 200, msg 报修提交成功您的工单号为 . $orderNo, order_no $orderNo]));这里要提醒一个细节工单号生成并发的场景下用 COUNT 再加一的方式在极端情况下会重号因为两个请求可能同时读到同样的 COUNT 值。小门店一天的报修量不超过几十单并发概率极低加上数据库唯一索引兜底真撞了会抛出异常人工处理一下即可。如果业务量大到每天几百单建议换用 Redis 原子自增或者数据库序列。3.3 客户查询进度页实现查询页只需要一个输入框客户输入工单号就能看到当前状态、工程师姓名和联系方式和状态时间线。时间线就是查 order_logs 表按时间倒序把每条操作记录展示出来。// query_order.php $orderNo trim($_GET[order_no] ?? ); if ($orderNo ) exit(请输入工单号); $stmt $pdo-prepare(SELECT o.*, a.real_name FROM orders o LEFT JOIN admins a ON o.engineer_id a.id WHERE o.order_no ?); $stmt-execute([$orderNo]); $order $stmt-fetch(PDO::FETCH_ASSOC); if (!$order) exit(工单号不存在请核对后重试); $statusMap [0 待受理, 1 已派单, 2 检测中, 3 待配件, 4 维修中, 5 已修复待取件, 6 已完成, 7 已取消]; // 查询日志 $stmt2 $pdo-prepare(SELECT * FROM order_logs WHERE order_id ? ORDER BY id DESC); $stmt2-execute([$order[id]]); $logs $stmt2-fetchAll(PDO::FETCH_ASSOC);3.4 后台状态流转实现后台的每一个状态变更操作我都封装成一个共用函数自动写入操作日志。这样做的好处是状态变更的口径统一日志不会漏记后续追责或者客户质疑时都有据可查。function changeOrderStatus(PDO $pdo, int $orderId, int $newStatus, int $operatorId, string $logMsg ) { // 查询当前工单状态 $stmt $pdo-prepare(SELECT status FROM orders WHERE id ? FOR UPDATE); $stmt-execute([$orderId]); $currentStatus $stmt-fetchColumn(); // 简单的状态机校验状态只能按预设路径流转 $allowedTrans [ 0 [1, 7], // 待受理 - 已派单/已取消 1 [2, 7], // 已派单 - 检测中/已取消 2 [3, 4, 6], // 检测中 - 待配件/维修中/超出检测直接完成 3 [4], // 待配件 - 维修中 4 [5], // 维修中 - 待取件 5 [6], // 待取件 - 已完成 ]; if (!isset($allowedTrans[$currentStatus]) || !in_array($newStatus, $allowedTrans[$currentStatus])) { throw new Exception(非法状态流转{$currentStatus} - {$newStatus}); } // 更新状态 $stmt $pdo-prepare(UPDATE orders SET status ?, updated_at NOW() WHERE id ?); $stmt-execute([$newStatus, $orderId]); // 写日志 $stmtLog $pdo-prepare(INSERT INTO order_logs (order_id, operator_id, from_status, to_status, log_msg, created_at) VALUES (?,?,?,?,?,NOW())); $stmtLog-execute([$orderId, $operatorId, $currentStatus, $newStatus, $logMsg]); }注意到我在查询当前状态时用了FOR UPDATE行锁这是为了防止两个管理员同时操作同一个工单导致状态覆盖。小团队无所谓但养成好习惯执行加锁不会有坏处。3.5 部署上线从源码到能访问代码写完之后部署流程我简化成四步。第一步准备一台服务器或虚拟主机PHP 8.2 和 MySQL 5.7 及以上版本第二步把源码上传到站点根目录导入 mydb.sql 初始化数据库第三步修改 config.php 里的数据库连接信息和站点 URL第四步访问后台 /admin 登录创建工程师账号初始化服务区域和品牌标签。整个过程不超过半小时。如果是本地测试用 phpstudy 或者 Laragon 这类集成环境一键启动 Apache MySQL五秒就能跑起来。我个人推荐 Laragon跟 phpstudy 不一样的是它对 PHP 多版本切换支持得特别好而且不会在系统里注册一堆服务删掉就是纯绿色版不污染环境。4. 常见问题与排查技巧实录4.1 JSON 接口返回乱码前端报修提交时Ajax 请求拿到 JSON 返回中文全部变成 \uXXXX 这种转义形式或者直接乱码。绝大多数情况下是少了这句响应头header(Content-Type: application/json; charsetutf-8);。注意 PHP 这边文件本身也必须是 UTF-8 无 BOM 格式如果你用记事本编辑过 PHP 文件存成了带 BOM 的 UTF-8接口返回的前几个字符会被 BOM 占位JSON 解析直接失败。我当时排查了半天最后用 Notepad 把所有 PHP 文件转成 UTF-8 无 BOM 格式就好了。经验就是PHP 文件统一用 VS Code 或 Sublime 编辑永远别用系统自带记事本碰编码问题。4.2 客户提交报修后收不到任何通知系统上线第一周就有客户投诉说报修提交成功了但没人联系。排查之后发现是邮件通知的 SMTP 配置没生效报错日志被 PHP 默认设置吞掉了。这个问题的解决办法分两层即时层客户提交成功后页面直接显示“我们将在30分钟内电话联系您”培养客户预期可靠层工单系统接入短信通知服务商在 admin 后台开放通知开关新工单到达时管理员手机会收到短信提醒。小门店我不建议一上来就搞复杂的消息队列或者 WebSocket 推送短信接口是最省心的一个月几百条成本不到一顿饭钱但客户的感知完全不一样。接入短信的时候要记得在后台设置接口异常报警万一短信通道欠费或者故障至少要能看到失败日志。4.3 工程师误操作导致状态混乱之前提到状态机的硬校验实际部署后最大的变化就是误操作基本绝迹了。但是这里有一个边界情况要注意如果系统里要支持“回退”操作比如客户已取件但发现还有问题客户又把电脑送回来状态机里的单向流转就不够用了。我后来在后台单独加了一个“重新受理”按钮权限只给管理员这个操作不走通用的状态机校验直接强制把工单状态从已完成改回维修中同时插入一条日志说明原因。特殊通道必须留但只对最信任的管理员开放。4.4 客户查询页的手机号脱敏问题有客户反映在查询页能看到维修工程师的完整手机号虽然方便了联系但工程师隐私也有一定风险。折中的做法是查询页展示前3位和后4位中间打码客户点击“拨打电话”按钮后由系统后端反向代理拨号或者直接引导客户联系门店前台。但小门店没有呼叫中心系统直接展示完整手机号加上工程师本人都同意实际操作中算是可接受的方案。如果你比较在意隐私建议加上隐藏开关在后台设置里让门店自定义是否展示完整号码。4.5 数据库连接被频繁报错导致白屏高峰期来了十来个客户同时提交报修网站突然白屏。查了 PHP 错误日志发现是数据库连接数打满了一看代码原来是每次请求都新建 PDO 连接而且脚本结束前没有释放。解决办法很简单用单例模式封装数据库连接同一个请求生命周期内共享同一个连接这样十几路并发也不会超过 MySQL 的默认连接上限。如果预期用户量还会涨那就要考虑把 PHP-FPM 的进程数和 MySQL 的 max_connections 调成匹配参数这两个值一方太高一方太低都会出问题。5. 一些实操心得与后续扩展方向这套报修系统的第一版我大概花了一周写完但真正稳定跑起来花了大半个月主要原因就是上线前没有把边界情况全部想到比如并发重号、状态误操作、编码坑。现在每写一块业务我都会强制自己列一个“用户瞎操作会怎么点”的清单逐条过一遍再动手写代码。如果你要把这套系统用在真实门店建议跑通第一单之后再做两件事。第一把维修价格做成后台可配置的结合故障类型和配件成本自动生成报价单减少口头报价的扯皮第二在工单完成后加一个评价短信入口引导客户在微信里留个好评这对门店的口碑积累非常有用。我自己后来还加了一个简单的配件备货预警库存低于阈值自动提醒采购看着很不起眼但省了不止一次被客户催着问“配件什么时候能到”的尴尬。最后再分享一个小技巧工单日志表的数据会越积越多记得按月做归档把三个月前的已完工单转到 orders_archive 历史表查询页默认只查最近三个月的单后台速度会明显快一截。代码里只需要在查询条件里加一下created_at的范围过滤归档用 MySQL 的事件调度器定时跑不用人肉操作。这套系统没有任何花哨的黑科技但每一个功能点都是围绕实际维修业务打磨出来的。照着这个思路撸一套以你熟悉的技术栈实现一遍比直接下载一套所谓的“完整源码”有意义得多——因为你会真正搞清楚每行代码为什么存在。本文还有配套的精品资源点击获取
返回列表