ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

全新UI自助图文打印系统:PHP后端从数据库到部署全解析

全新UI自助图文打印系统:PHP后端从数据库到部署全解析 简介这是一套采用全新UI设计的自助图文打印系统小程序源码包含PHP后端与前端小程序适合需要搭建线上打印服务的开发者或商家。前端基于微信小程序后端基于ThinkPHP附带安装部署教程覆盖域名配置、HTTPS开启、数据库修改等关键步骤。压缩包共2000个文件约72.59MB以JavaScript文件为主1660个配合HTML、CSS、JSON等前端资源以及Markdown文档和说明文件便于查看与二次开发。目前在CSDN已有580人学习下载。资源提供完整前后端代码、后端环境配置说明NginxPHP7.4MySQL5.6需sg11扩展、数据库文件位置及后台登录信息并附前端导入微信开发者工具及合法域名配置指引。读者可基于此快速搭建一套可运行的图文打印系统并参考UI与业务逻辑进行个性化改造。1. 全新UI自助图文打印系统小程序源码 PHP后端从一张上传文件开始的生意我见过不少想做自助打印的人一开始把注意力全放在“UI界面好不好看”上结果后端接口和订单状态一塌糊涂图传上来了店员却不知道要不要印。这里说的全新UI自助图文打印系统小程序源码 PHP后端 附教程本质是一条完整的链路——小程序负责客户选文件、选参数、付款PHP后端负责生成打印任务、算价格、回调发货教程负责把部署和配置讲得能落地。它适合两类人一类是学校附近或图文店想接小程序订单的店主另一类是接外包项目、需要快速给客户交付可运行系统的开发者。今天这篇就按这个链路把数据库、接口、部署和坑一次说透。2. PHP后端先立住数据库、订单状态机和登录手机号接口是怎样合在一起跑的自助打印小程序看着不复杂但真正做起来前端UI只是壳后端必须同时管住登录、价格、订单、打印任务、支付回调五件事。这一章我先讲后端的边界如何划分再给出一套能直接建表的关系型数据库设计最后落到微信小程序登录获取手机号这个最容易挡住新手的第一步。2.1 自助打印系统的后端边界PHP后端该管哪几件事很多人把这类系统做成一个“上传文件给打印机发消息”的小玩具结果生意一开张就崩。原因在于自助图文打印系统不是简单把文件扔给打印机它要处理的是“客户操作和门店执行之间的账目核对”。后端至少要拆成五个模块用户身份、价格计算、订单流转、任务分发、支付回调。用户身份解决“这个人是谁、手机号能不能联系上”价格计算要按纸型、色彩、单双面、份数动态算出金额订单流转要记录从待支付到打印完成的每次状态变化任务分发要决定哪个打印队列接收这个文件支付回调是微信支付往你的后端发来支付成功通知后端拿到回调才能把订单置为待打印。这五个模块中最容易被忽略的是任务分发因为它不像支付那样有现成SDK却直接决定你的打印店一天能接多少单。自助打印小程序不像小程序商城那样有固定的SKU和库存表商品的参数是组合出来的A4黑白单面是0.5元A4彩色双面可能就是1.5元再加上份数、页数前端拿到的是一个计算好的总价。前后端分离项目实战里的常见毛病是把价格计算放在小程序里后端只存总额。这样做一旦门店调价小程序不发新版本就没法生效。我的习惯是前端只负责展示所有金额计算都在PHP后端完成并且每次报价都要带一个价格版本号订单生成时锁定当时价格避免用户下了单再看账单时金额变了。UI层和PHP后端分离以后还有一个好处小程序端可以专心把页面做得清爽换UI框架、换主题都不用动业务逻辑。后端接口保持稳定前端只要保证请求路径和参数名对得上就可以单独迭代。很多“源码包带UI”的项目UI层和后端耦合得很深改个按钮颜色都要翻PHP文件这是最初设计时就该避免的。2.2 给订单建模用户表、打印任务表和价格配置表怎么设计回到可执行的层面我一般会建四张核心表用户表、门店打印机表、打印订单表、价格配置表。辅助的表还有支付流水表、退款记录表和登录日志但前四张决定系统能不能跑通。用户表不存微信原始openid以外的敏感信息手机号单独存加密后的值打印机表记录设备和门店的关系订单表是整个系统的状态中心价格配置表不写死价格方便门店后台随时调。下面是一份能直接执行的建表SQL结构上兼顾了查询速度和状态追溯CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, openid VARCHAR(64) NOT NULL DEFAULT , mobile VARCHAR(32) NOT NULL DEFAULT COMMENT 加密后的手机号, nickname VARCHAR(64) DEFAULT , status TINYINT NOT NULL DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE printer ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, store_id INT UNSIGNED NOT NULL DEFAULT 0, printer_name VARCHAR(64) NOT NULL DEFAULT , ip_address VARCHAR(64) NOT NULL DEFAULT , port INT NOT NULL DEFAULT 9100, status TINYINT NOT NULL DEFAULT 1 COMMENT 1在线 0离线, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE print_order ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL DEFAULT , user_id INT UNSIGNED NOT NULL, printer_id INT UNSIGNED NOT NULL, file_path VARCHAR(255) NOT NULL DEFAULT , pages INT NOT NULL DEFAULT 1, copies INT NOT NULL DEFAULT 1, color_type TINYINT NOT NULL DEFAULT 1 COMMENT 1黑白 2彩色, paper_size VARCHAR(16) NOT NULL DEFAULT A4, amount_fen INT NOT NULL DEFAULT 0 COMMENT 金额单位分, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2待打印 3打印中 4完成 -1取消 -2退款, fail_reason VARCHAR(255) NOT NULL DEFAULT , created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user (user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE price_config ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, store_id INT UNSIGNED NOT NULL DEFAULT 0, paper_size VARCHAR(16) NOT NULL DEFAULT A4, color_type TINYINT NOT NULL DEFAULT 1, price_per_page_fen INT NOT NULL DEFAULT 50, status TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段SQL里值得说的是amount_fen金额一律用“分”存整数避免浮点计算带来的精度问题。print_order.status用整数状态位后面在PHP枚举里映射成文案前端不需要知道数字是什么意思。fail_reason字段很关键打印失败时把打印机返回的错误信息写进去否则售后时只能靠客户截图。参数上注意两点user.mobile存的是加密值而不是明文这样即便数据库被拖走手机号也不是裸奔的print_order.order_no要设置唯一索引订单号生成规则用“日期门店ID随机串”目的是并发时不容易冲突。price_config不需要存总价每次下单时用页数、份数、单价计算并把当时的单价快照写进订单扩展字段防止用户打印一半改价产生纠纷。2.3 微信小程序登录获取手机号后端要做code换session和手机号解密小程序端的第一步不是上传文件而是登录。微信小程序登录获取手机号的标准链路是前端调用wx.login拿code再把code发到PHP后端后端通过code2session接口换openid和session_key之后用户点击“手机号快捷登录”时前端拿到iv和encryptedData注意还有获取手机号的code一并交给后端解密。代码上我建议拆成两个接口一个处理登录换openid一个处理手机号解密。登录接口的PHP骨架大致是这样public function login(Request $request) { $code $request-input(code); $appid config(wechat.mini.appid); $secret config(wechat.mini.secret); $url https://api.weixin.qq.com/sns/jscode2session? . appid{$appid}secret{$secret}js_code{$code}grant_typeauthorization_code; $resp file_get_contents($url); $data json_decode($resp, true); if (isset($data[errcode]) $data[errcode] ! 0) { return $this-fail(登录失败); } $user User::firstOrCreate([openid $data[openid]]); $token bin2hex(random_bytes(32)); // 建议把token写入redis或cache设置7天过期 cache()-put(user_token_ . $token, $user-id, 604800); return $this-success([ token $token, user_id $user-id, ]); }这里用了file_get_contents调微信接口简单直接但生产环境建议换成curl并加超时设置比如超时3秒。返回里的token是后端自己生成的不透明token不要让小程序直接拿openid当前端身份凭证否则别人改个openid就变成另一个人。第一次登录用firstOrCreate很合适用户不存在就创建已经存在就复用。手机号解密接口的PHP代码如下public function decryptMobile(Request $request) { $encryptedData $request-input(encrypted_data); $iv $request-input(iv); $sessionKey $this-getSessionKeyByToken($request-input(token)); $aesKey base64_decode($sessionKey); $aesIv base64_decode($iv); $aesCipher base64_decode($encryptedData); $result openssl_decrypt($aesCipher, AES-128-CBC, $aesKey, OPENSSL_RAW_DATA, $aesIv); $result json_decode($result, true); if (!$result || isset($result[purePhoneNumber]) false) { return $this-fail(手机号解密失败); } User::where(id, $request-input(user_id)) -update([mobile encrypt($result[purePhoneNumber])]); return $this-success([mobile $result[purePhoneNumber]]); }这段代码的关键是session_key只能用来解密绝不能返回给前端。openssl_decrypt的算法是AES-128-CBC填充方式微信要求OPENSSL_RAW_DATA如果你的PHP版本或扩展不一样经常会在这里出现解密失败。解密成功以后手机号用Laravel的encrypt函数入库这样就算日志里打了用户信息看到的也是一串密文。参数上要注意手机号解密用的code和登录code是两回事小程序端需要分别获取。iv每次点击都会变不能缓存。如果解密总是失败先检查session_key是否过期微信小程序里session_key有效期不确定用户长期不打开小程序再次进入时后端应该让它重新走一遍登录流程。3. 小程序UI层对接PHP后端传图下单和登录态失效处理的三段关键代码后端接口写好以后进入小程序UI层对接的阶段。这一章把UI层的页面结构、网络请求封装和文件上传三件事讲明白。很多人在这里翻车就是因为直接把wx.request撒在每一个页面里登录态失效时没有任何统一处理用户看到的报错永远是“网络错误”。3.1 小程序UI层页面结构用UI框架把自助打印流程拆成四个页面自助打印的小程序端不需要做成商城那样几十个页面核心流程四页就够首页选择打印类型、上传文件页、确认订单页、订单列表页。再加上一个“我的”页面放个人信息和联系客服入口已经能覆盖九成场景。UI层独立的好处是可以选一套现成的UI框架比如Vant Weapp或者uni-app自带的组件这样按钮、弹窗、加载状态统一不需要自己写样式。我一般会用原生微信小程序配合一套轻量组件库。原因是自助打印项目的交互复杂度不高没必要为了几个弹窗引入重框架但列表和表单控件建议用现成组件能减少UI界面卡顿的隐患。页面之间的参数传递不要用全局变量存文件路径小程序在iOS上会回收不活跃页面的内存全局变量一丢文件就找不回来了。正确做法是上传页拿到路径后立即把文件上传到PHP后端页面只保存服务器返回的file_id。页面结构上每个页面都有一个独立的onLoad方法拉取数据这看起来重复但好处是逻辑清晰。首页拉取价格配置和打印机状态确认页拉取订单详情和支付参数订单列表页分页加载历史订单。UI层和后端接口的映射文件我习惯放在utils/api.js里统一管理路径否则后端改了URL你要挨个页面去找。所谓“全新UI”的落地重点不在于把颜色换新而是把打印流程里的反馈状态做清楚。比如上传成功后按钮变成可点击的绿色而不是只弹一个toast订单列表里每个状态都有色块区分。这些反馈全由后端返回的状态值驱动前端不能自己猜。3.2 封装request请求和状态码拦截别让登录失效把整个页面拖崩小程序原生的wx.request不支持Promise直接写在页面里会出现“回调地狱”而且没法统一处理token过期。我的做法是封装一个request函数所有页面都从这里面调用。返回结构统一为{code, msg, data}code为0是成功401是登录失效403是无权限。小程序拿到401后不再跳页面而是清掉本地token直接跳登录页然后返回用户刚才想进的页面。下面是一段可以直接放进utils/request.js的封装代码const request (url, method GET, data {}) { return new Promise((resolve, reject) { const token wx.getStorageSync(token); wx.request({ url: getApp().globalData.baseUrl url, method: method, data: data, header: { Content-Type: application/json, Authorization: Bearer token }, success: (res) { if (res.data.code 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); reject(res.data); } else if (res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: (err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); }; module.exports { request };这段代码的逻辑是每个请求都带上本地存的token后端只要返回code401说明token无效或过期前端立刻清掉旧token并且不继续执行业务逻辑直接跳登录。整体用Promise封装页面里可以await this.$request(/api/order/list)这样写不用再嵌套回调。参数说明里有个容易被忽略的点getApp().globalData.baseUrl在开发环境写http://127.0.0.1/上线前必须改成HTTPS域名并且要在小程序后台配置合法域名。wx.request的header里不要写死Content-Type为application/json因为上传文件时表单类型不同统一封装后上传接口需要单独设置header。还有一点fail分支里的“网络异常”提示在真正上线时建议区分超时和断网比如用err.errMsg里是否包含timeout来判断。3.3 上传文件和打印参数一起提交wx.uploadFile的formData用法上传文件是自助打印系统里最容易出bug的环节。很多人先wx.uploadFile传文件再调接口传打印参数结果订单里文件有路径参数却丢了查半天发现是第二个请求还没有回来订单已经创建了。正确做法是把文件路径和订单参数放进同一次wx.uploadFile请求里PHP后端一次性接收文件和表单字段创建订单。下面是一段上传文件到后端的示例代码const uploadFile (filePath, orderParams) { return new Promise((resolve, reject) { const token wx.getStorageSync(token); wx.uploadFile({ url: getApp().globalData.baseUrl /api/order/upload, filePath: filePath, name: file, formData: Object.assign({ token: token }, orderParams), success: (res) { const data JSON.parse(res.data); if (data.code 0) { resolve(data.data); } else { wx.showToast({ title: data.msg, icon: none }); reject(data); } }, fail: (err) { wx.showToast({ title: 上传失败, icon: none }); reject(err); } }); }); };这里的关键是filePath直接传小程序拍照或从相册选出来的本地路径name: file要跟PHP端的接收字段名保持一致。formData里除了token还要带上printer_id、pages、copies、color_type、paper_size这些订单参数。PHP端在接收文件时同时从$_POST里读这些参数一个请求就能完成“文件入库订单创建”。参数说明时记得wx.uploadFile的返回值res.data在开发者工具里是字符串不是对象所以要先JSON.parse。后端接口接收的$_FILES[file]里会有error字段PHP端必须先检查这个字段再判断移动文件到业务目录。还有一个容易踩的坑是orderParams里不能放嵌套数组formData不支持复杂类型所有参数都应该是字符串或数字如果确实要传多个文件就循环调用上传接口而不是塞一个数组进去。4. 本地跑通和服务器部署PHP环境参数、Nginx伪静态与上传目录权限拿到源码以后第一步不是改代码而是把环境跑通。这一章我从本地开发环境说到服务器部署重点解决三件事PHP版本和扩展匹配、Nginx伪静态和PHP-FPM的边界、上传目录的安全权限。这三件事任何一个没做对教程里再详细也救不了你。4.1 从源码压缩包到本地跑通先看PHP版本和扩展自助打印系统的PHP后端多半跑在Laravel或ThinkPHP这类框架上也有可能是一个结构简单的原生PHP项目。不管哪种你拿到压缩包后第一件事是看composer.json或者入口文件。Laravel项目要PHP 8.0以上ThinkPHP 6也要求7.4以上如果代码用了match或者构造函数属性提升那PHP版本至少在8.0。用命令行执行php -v确认版本后再执行php -m看扩展。常见的依赖扩展是pdo_mysql、openssl、mbstring、gd、curl、fileinfo。其中fileinfo最容易被忽略上传文件时识别MIME类型会用到它gd在很多打印项目里用来生成缩略图或水印。缺扩展会直接导致接口报“Call to undefined function”这种错误在浏览器里看到时很难第一时间想到是扩展缺失。本地环境我一般直接用php -S 127.0.0.1:8000起个内置服务器先跑通控制器逻辑。框架的路由要单独配置Laravel的入口指向public/index.php所以命令是php -S 127.0.0.1:8000 -t public。这一步不追求快而是最快暴露PHP版本和扩展问题。本地跑通了再上服务器能减少一半的部署时间。注意内置服务器只用于开发调试不要直接用线上流量。运行起来之后先访问健康检查接口确认数据库连接、缓存连接都没问题再继续。4.2 Nginx伪静态与PHP-FPM的边界配置接口404和502都出在这里部署到Linux服务器上常见组合是Nginx PHP-FPM。接口404不是PHP代码问题多半是伪静态没配置好接口502则是Nginx连不上PHP-FPM或者fastcgi_pass地址写错了。这两类错误是自助打印部署教程里最常提到的问题但很少有人把两者分开讲。Nginx配置里一个最小可用的站点配置是这样server { listen 80; server_name your-domain.com; root /var/www/print/public; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } location ~* \.(jpg|jpeg|png|gif|pdf)$ { expires 30d; } }这里的try_files是关键改成try_files $uri $uri/ /index.php?$query_string后所有不存在的路径都会交给前端控制器PHP框架才能解析路由。很多教程只写前端分离的客户端路由导致服务端发来的请求直接404。fastcgi_pass用Unix socket还是TCP? 在同一台机器上建议用socket延迟更低如果在Docker里跨容器访问才改用127.0.0.1:9000。还有一个参数位最容易忽视fastcgi_param SCRIPT_FILENAME。如果写成$document_root$fastcgi_script_name而项目根路径不对就会出现“File not found”的提示实际是路径拼接错了。上传大文件时还要在http段或server段加client_max_body_size 20m;否则传PDF超过默认1M就直接413。4.3 上传目录的安全边界让PHP跑在它该跑的位置自助打印系统最危险的地方不是支付而是上传目录。如果用户传上去的文件能被当作PHP脚本执行攻击者上传一个shell.php服务器就沦陷了。这是这类源码包里最常见的安全漏洞。解决思路不是让代码“防御”而是从目录层面把上传目录和PHP执行目录隔离。首先上传目录放在public/uploads下访问权限正常开放给nginx然后在这个目录的Nginx配置里显式禁止执行PHP脚本location ^~ /uploads/ { location ~ \.php$ { deny all; } }这段配置的意思是所有/uploads/开头的请求都会先命中这个location里面如果包含.php则直接拒绝返回403。deny all不会影响正常图片下载因为图片不经过PHP解释器。接下来再配合操作系统的目录权限让运行PHP的用户只能写这个目录不能写其他业务目录。chown时把uploads的owner设为www-data其他目录的owner设为项目管理员这样即使上传的PHP脚本没有被Nginx拦截也会因为没有写权限而无法产生后续文件。数据库连接配置里也要注意不要把数据库密码写在代码里然后提交到公开仓库。习惯是放到.env文件并在Nginx的location配置里把.env访问挡掉。我的习惯是额外在Nginx加一条规则location ~ /\.env { deny all; }。因为很多源码包把配置写在根目录一旦被访问数据库密码直接裸奔。5. 自助图文打印系统避坑记录UI卡顿、跨域和支付回调的五个踩坑现场这一章写的是我实际会踩到的坑也是自助图文打印系统最常出问题的五个位置。每条按现象、原因、解决思路来写。看完你会觉得很多“玄学”问题其实都是配置和边界没处理好。5.1 UI界面卡顿不是手机问题是列表没有分页和懒加载现象小程序在订单列表页不断往下滑过了五十条以后明显变卡有些安卓机上甚至白屏。原因订单列表一次性把后端返回的所有数据全渲染出来没有分页和懒加载。自助打印系统的订单往往包含图片路径、文件大小、状态标签一个订单就是一个复杂组件渲染几十个DOM节点还能撑住到了几百个就一定卡。解决后端接口改成分页返回默认一页20条小程序端用“上拉触底加载”的方式追加下一页。数据量再大时订单列表里的文件缩略图不要用原图后端在生成订单时或访问时压缩一张标准图。UI层只负责把has_more字段作为是否还能继续翻页的依据避免请求第100页还在返回数据。后端分页接口一般返回{list, page, has_more}。我用过最省事的做法是前端定义一个page变量每次请求成功后自增同时把返回值里的list用concat追加到现有数组。不要用setData直接改整个数组那样会触发整个列表重新渲染要setData({ list[0].status: 5 })这种路径写法只更新变化的那一项。5.2 小程序请求接口报错跨域和合法域名是两件事现象开发时PHP接口用http://127.0.0.1:8000访问正常小程序一请求就报“url not in domain list”或者直接fail。原因这是小程序平台的合法域名校验不是跨域问题。很多人拿到报错就去后端加跨域头其实小程序不存在浏览器跨域限制是微信客户端强制要求请求域名必须在小程序后台白名单里。解决开发阶段在开发者工具里勾选“不校验合法域名”本地跑通业务上线前一定要把PHP后端部署到HTTPS域名并到微信公众平台配置“request合法域名”和“uploadFile合法域名”。如果后端还有Web管理后台访问那才是真正的跨域PHP里加几行响应头就行但小程序这个场景不要混淆。还有一点wx.request的url必须是完整的https://协议不能只写/api/xxx。PHP端如果部署了强制跳转HTTPS要确认静态资源和接口都在HTTPS下没有混合内容。我第一次交付时就是漏掉了合法域名配置客户真机上扫码一直转圈小程序后台数据包能看到请求全部失败最后查了半天才发现是白名单没加。5.3 支付异步回调收不到先查服务器端口和验签顺序现象微信支付下单成功用户的钱扣了后台订单状态还停在“待支付”只能在数据库里手工改状态。原因支付回调URL被防火墙挡了或者异步通知里的验签逻辑写错微信重试几次后放弃。更隐蔽的情况是教程里的回调地址写的是http://ip:8080/notify没有域名没有证书微信服务器请求过不去。解决回调地址必须是外网能访问的HTTPS地址且不能放在登录态保护的路由里。微信支付的回调通知不是用户发起的不会有token后端收到通知后先拿签名做校验校验通过再查订单、改状态。排查时先看Nginx访问日志有没有来自微信支付IP的POST请求如果有再看PHP日志是不是验签失败。验签顺序也经常写反。正确做法是先商户平台下载微信支付API证书用证书里的公钥验签验签通过后再更新订单。如果先用订单号去查单再验签单子没查到就会直接返回“失败”微信会继续重试重试期间订单其实已经在库里了。另外回调处理完以后要返回{code:SUCCESS}给微信不要返回空字符串返回值格式错了微信同样会重试。5.4 打印机连接超时串口、HTTP、队列三处超时要分开调现象一台打印机明明是通电的上传文件以后界面一直转圈后台看任务队列一直卡在“打印中”。原因打印任务下发时只设了一个笼统的超时时间打印机响应慢一点就断开了或者打印机走的是局域网HTTP接口但PHP进程没有连上打印机的网络。自助打印系统里打印机的连接方式有串口、USB转网络、HTTP接口三者的超时行为完全不同。解决串口打印要按波特率和数据位配置不要用默认值网络打印要区分“连接超时”和“发送超时”前者设2秒后者设10秒HTTP接口要确认打印机的IP和端口在PHP服务器上能连通不能只在小程序端测通。我一般会在打印队列里加一个“尝试次数”字段失败一次就在任务表里标记一次超过三次再进入人工处理而不是让任务无限卡住。排查时用一句telnet 192.168.1.200 9100先从服务器上看端口通不通再检查PHP的fsockopen超时参数。很多打印机自带的状态查询接口队列在打印前先轮询一次打印机在线状态比“发出去失败了再补救”要靠谱得多。5.5 上传图片时提示文件过大或格式不对PHP默认参数在偷着拦现象用户在小程序里上传一个5MB的PDF前端已经传完后端却返回“文件过大”。原因PHP默认的上传大小是2Mpost_max_size和upload_max_filesize都没调文件到了PHP就被扔掉了。更隐蔽的是max_file_uploads或max_input_time限制导致多文件上传时只有第一张成功。解决修改PHP配置。upload_max_filesize、post_max_size、max_execution_time、max_input_time这四个参数要配套调。post_max_size要大于upload_max_filesize否则传一个刚好接近上限的文件会被当成POST数据超限拦下。改完配置后重启PHP-FPM再用phpinfo()确认参数生效。格式不对也是同一类的坑。有的打印机不识别PDF需要后端装Ghostscript把PDF转成图片有的教程让你直接传PDF打印店里却出乱码。解决方式是后端在订单生成时先解析文件头判断真实格式前端传参里的扩展名不能信。我这里会先在本地测一遍“5MB的PDF转图片”耗时多少如果超过10秒就要给用户一个“正在处理”的反馈而不是让他傻等。6. 交付前给项目补上重试和验收清单打印队列、订单推送和习惯检查6.1 打印队列失败重试给每台打印机加一个任务状态机自助打印上线以后真正让你睡不好觉的不是订单量而是“这一单到底打没打”。我的做法是在订单表里增加retry_count和last_try_at字段定时任务扫描状态为“待打印”且last_try_at超过两分钟的任务重新提交到打印机。重试次数超过三次后任务变成“需人工”并给店长发一条模板消息而不是无限重试。重试代码要注意幂等同一个order_no不能因为重试被打印两次。解决办法是在打印机返回“已接收”后立刻把订单状态改成“打印中”重试任务只认“待打印”状态。这个小改动比任何高级队列都管用。6.2 客户验收清单教程写不出但能少吵架的交付物交付源码时我给客户写一份清单浏览器能访问后台吗小程序真机上下单后支付回调是否更新状态打印队列失败重试是否生效上传目录是否禁止PHP执行每个勾选都有对应操作步骤。教程教人跑通清单教人验收前者不一定覆盖边界后者才是交付的安全网。6.3 结尾技巧习惯性查看PHP错误日志别盯着屏幕“看起来正常”我自己的习惯是部署完毕后先看一期PHP错误日志再关掉日志而不是只在页面上看“200 OK”。PHP的display_errors在线上要关掉但error_log要开着否则很多接口失败只显示500你根本不知道是哪个文件哪一行报错。花了最多时间的调试往往都是日志没看。这套系统做下来我最大的教训是UI界面卡顿、支付回调、上传限制这些“小问题”比业务逻辑更消耗交付时间。把状态机、超时和错误日志这三样东西提前安排好后面真的能少去很多次现场。希望帮到你。本文还有配套的精品资源点击获取
返回列表