尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

PHP防伪证书系统实战:防伪码生成与查询部署全攻略

发布时间:2026/9/26 20:29:51

资讯中心
01
ARTICLE

PHP防伪证书系统实战:防伪码生成与查询部署全攻略

PHP防伪证书系统实战:防伪码生成与查询部署全攻略
简介PHP在线生成查询产品防伪证书系统源码是一套面向中小企业、建站开发者以及电商运营者的完整防伪证书管理方案内置证书模板管理、防伪码在线生成、真伪查询与后台权限控制等模块部署在PHP与MySQL环境下即可使用。整个资源包共收有1017个文件压缩后大小133.2MB内部除大量图片素材外还包含前端样式、交互脚本、后端逻辑、完整页面以及PSD源文件、字体文件和表格数据目录划分清晰便于模板替换与二次开发同时可见Bootstrap、AmazeUI等前端框架资源界面定制难度较低。系统自带90套授权证书模板配合PSD公章模板可自由调整板式后台按管理员与代理商两个层级设计既能统一发放证书也可让代理使用独立入口维护自身授权数据结合可视化安装向导及预设初始账号部署门槛较低。目前已有540人学习下载对于希望快速上线防伪查询功能、减少重复开发的个人或团队来说是一份可直接参考和改写的源码包。1. 一套 PHP 在线防伪证书系统到底解决了什么问题先聊结论把“PHP在线生成查询产品防伪证书系统源码.zip”倒腾上线核心不在于证书那步“打印排版”而在于你生成的那串防伪码经不经得起推敲、查询接口会不会被刷、首次查询判定准不准。我经手过几个中小品牌的自建防伪项目最常出问题的不是证书样式而是码被批量猜出来、或者真码被恶意查询后“假多次”把顾客吓跑。这套方案适合正在给工厂、电商渠道搭建验真页面的开发者也适合想摆脱第三方 SaaS 按年计费、把数据收进自有数据库的品牌方。第 2 章先讲清码表和查询流程的设计逻辑第 3 章落地部署第 4 章拆关键代码怎么改第 5 章列出我踩过的高频坑第 6 章给批量生成和导出 PDF 的进阶做法。新手顺着走能完成上线闭环熟手可以直接跳到第 4 章对照参数。2. 防伪系统的底层设计先定码规则再谈界面和功能2.1 防伪码编码规则去掉易混字符加入校验位防伪码是这个系统的心脏。证书做得再漂亮码能被批量猜出来整套体系就报废了。这里我采用的是 16 位大写字母加数字的混合码不走纯数字因为纯数字分布空间虽然不小但暴力脚本更容易按位枚举混合字母数字会把攻击面撑大好几个量级。按 32 个字符集的 16 位组合来算商品被逐个猜中的概率已经低到可以忽略。不过光把码随机出来也不行字符表要动手脚印刷和手输两个场景都要照顾// 剔除 0/O、1/I避免用户在输入时搞混 $chars ABCDEFGHJKLMNPQRSTUVWXYZ23456789; $code ; for ($i 0; $i 16; $i) { $code . $chars[random_int(0, strlen($chars) - 1)]; }这串代码的逻辑很简单从去掉易混字符的字符表里按位随机取 16 次。关键在于函数选型random_int 是 PHP 7 起内置的加密安全随机源底层走的是操作系统的 CSPRNGrand() 和 mt_rand() 不是不能用是它们属于可预测的伪随机序列攻击者拿到几个样本就能推演后续。防伪码生成这类场景没有商量余地必须用 random_int。另外建议在系统设计里做一个可选开关数据库里不存明文码而是存 HMAC-SHA256(code, APP_KEY) 的摘要。查询时先把用户输入扩成摘要再比对这样即使库被拖走攻击者也没法批量制造真证书。后台导出和打印仍然用明文码消费者查询流程感知不到差异。这个设计要花一次数据表迁移的代价但带来的安全收益值回票价。2.2 数据表设计产品、证书、查询日志三张表怎么搭源码包里最怕乱。下面这套三表结构是这类防伪系统的常见做法product 管产品certificate 管防伪码和证书状态query_log 管每次查询痕迹。直接能用的建表语句CREATE TABLE product ( id int unsigned NOT NULL AUTO_INCREMENT, name varchar(120) NOT NULL COMMENT 产品名称, spec varchar(120) DEFAULT COMMENT 规格型号, manufacturer varchar(120) DEFAULT COMMENT 生产商, created_at datetime NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT产品表; CREATE TABLE certificate ( id int unsigned NOT NULL AUTO_INCREMENT, code varchar(64) NOT NULL COMMENT 防伪码或 HMAC 摘要, product_id int unsigned NOT NULL, batch_no varchar(64) DEFAULT COMMENT 批次号, issue_date date DEFAULT NULL COMMENT 证书签发日期, producer varchar(120) DEFAULT COMMENT 认证机构/厂家名称, query_count int NOT NULL DEFAULT 0 COMMENT 已查询次数, first_query_at datetime DEFAULT NULL, first_query_ip varchar(64) DEFAULT NULL, status tinyint NOT NULL DEFAULT 1 COMMENT 1有效 0作废, PRIMARY KEY (id), UNIQUE KEY uk_code (code), KEY idx_product (product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT防伪证书表; CREATE TABLE query_log ( id bigint unsigned NOT NULL AUTO_INCREMENT, certificate_id int unsigned NOT NULL, query_time datetime NOT NULL, ip varchar(64) DEFAULT , ua varchar(255) DEFAULT , result_type tinyint DEFAULT 1 COMMENT 1首次 2重复 3无效码 4已注销, PRIMARY KEY (id), KEY idx_cert_time (certificate_id, query_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT查询日志表;三张表的关系一眼能看懂product 一对多 certificatecertificate 一对多 query_log。这里有两个容易被忽略的细节。第一code 字段我故意写成 varchar(64)因为如果走 2.1 提到的 HMAC 存储方案摘要长度会超过明文码新建表时多留一点容量后面改字段类型就不用锁表。第二证书表 code 的唯一索引必须建这是防重码的最后一道物理防线程序里的检查永远可能因为并发漏掉数据库约束不会。2.3 查询判定流程首次、多次、无效三种结果怎么分消费者扫码之后返回的结果不要搞出太多花样三类足够首次查询、再次查询、无效码。“首次查询”是信任锚顾客买到正品第一次扫应该看到清晰的正品提示“再次查询”要带上首次查询时间和次数防止造假者把同一张证书截图到处发“无效码”就是格式不对或查无此码。完整的判定顺序是这样的服务端拿到码去掉空格、统一转大写、用正则判断是不是 16 位混合字符然后进数据库查证书表并加行锁查不到就返回无效码查到了看 status 是不是作废有效再看 query_count 是不是 0是 0 返回首次查询同时更新次数、首次时间和首次 IP不是 0 返回重复查询。整个流程不需要引入复杂的规则引擎一个事务加三个分支就能解决。这里的行锁是关键我放在 4.2 详细写。如果两个请求几乎同时到达没有锁就会同时读到 query_count0结果同一个码被扫了十次十次都显示首次验证。消费者觉得查询系统不靠谱品牌方也没法拿查询次数作为维权证据。查询日志表每次都要落一条记录首次查询之后每次重复查询也记录 result_type2后面做防刷分析时数据才全季度报表才说得清哪个区域在密集扫无效码。2.4 证书模板与二维码HTML 渲染到底够不够用证书展示有两种落地方式一种是把证书直接做成 HTML 页面二维码嵌在页面里消费者输入码后看到的是电子证书另一种是后台把证书生成成 PDF让品牌方下载打印贴在包装盒内。从源码包实现成本看HTML 模板是主流因为可编辑、省事、方便塞进微信落地页字体和排版差异用内联样式写死就能压住。二维码由后端生成 PNG 后落到模板指定位置推荐用 PHP QR Code 这类轻量库一次生成存 file不要每次请求都重新渲染否则并发起来 IO 会拖慢整条链路。如果需要打印成实体证书PDF 才是可交付的最终形式但它有一个中文字体的硬坑我会放到第 6 章展开。先跑通查询链路比纠结证书样式重要得多模板随时能改查询逻辑错了是信任问题。3. 用 zip 源码包在服务器上跑通的完整步骤3.1 环境要求与 zip 解压后的目录把 zip 下载下来以后先对一下环境清单避免后面连环翻车检查项建议值备注PHP 版本7.4 ~ 8.2random_int 在 PHP 7 起内置PHP 扩展pdo_mysql、gd、mbstring二维码库依赖 gd/freetypeMySQL5.7 或 8.0utf8mb4 下 5.6 索引长度受限Web 服务器Apache / Nginx注意伪静态规则差异文件权限www 用户可写 uploads/Windows 主机要另给 IUSR 权限解压这一步看着简单实际上经常有人把文件夹层级搞错。用 unzip 解压到站点根目录后要确认直接看到 admin、api、templates 这类目录而不是再包一层同名文件夹。示例命令cd /var/www/html unzip /tmp/php_fangwei_src.zip ls -la目录结构示意如下和常见源码包的布局基本一致站点根目录/ ├── admin/ # 后台管理登录、产品维护、发码 ├── api/ │ ├── generate.php # 生成防伪码/证书 │ └── query.php # 消费者查询入口 ├── templates/ │ ├── cert.html # 证书模板 │ └── query_result.html # 查询结果展示页 ├── uploads/ # 二维码、PDF 导出临时目录 ├── config.php # 数据库、站点参数 ├── install.sql # 初始化 SQL └── README.txt # 部署说明拿到压缩包后先别急着改代码打开 README.txt 或 config.php 看一眼确认它有没有自带 install.sql。有 install.sql 就走命令行导入没有就找后台的安装向导入口。这两个初始化路径决定了后面步骤的顺序顺序错容易造成表结构重复或字段缺失。3.2 数据库初始化与 config 配置初始化数据库的推荐做法是命令行干净并且能看到每一步日志mysql -u root -p -e CREATE DATABASE fangwei DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -u root -p fangwei /var/www/html/install.sql注意一个细节命令行里直接带密码会留在 shell 的历史记录里交互式输入更安全。导入完成后检查一下表是否建齐mysql -u root -p fangwei -e SHOW TABLES;正常能看到 product、certificate、query_log 三张表。接着改 config.php站点参数决定二维码能不能被手机正确打开// config.php 常见写法 return [ db_host 127.0.0.1, db_name fangwei, db_user fangwei_user, db_pass 换成自己的强密码, base_url https://fw.example.com, // 部署域名 admin_user admin, // 后台账号 admin_pass_hash , // 用 password_hash 生成 ];base_url 是二维码生成时的根地址本地开发写成 http://localhost 没问题上线忘改就麻烦了手机扫出来全是内网地址。建议把 config.php 里这行加粗标注上线前强制检查协议和域名。后台密码策略不能偷懒防伪系统后台一旦被外人登进去他可以批量生成真证书后果比丢货严重得多。3.3 站点伪静态和文件权限两项必查如果你用 Nginx而源码包带的是 Apache 的 .htaccess伪静态会静默失效访问短地址直接 404。我的习惯是默认让代码走带问号的完整 URL例如 /api/query.php?codeXXX这样在虚拟主机和云主机上都能跑不依赖重写规则。要缩地址再单独配 Nginx rewritelocation /v/ { rewrite ^/v/([A-Za-z0-9]{16})$ /api/query.php?code$1 last; }配置完记得先检查再重载nginx -t nginx -s reload文件权限集中在两个点站点目录属主和 uploads 可写。Linux 下用一条命令处理chown -R www-data:www-data /var/www/html chmod -R 755 /var/www/html chmod -R 775 /var/www/html/uploadsWindows 的 IIS 主机是另一个翻车高发地uploads 目录需要在 IIS 管理器里给 IUSR 账号加修改权限否则生成二维码时直接白屏错误日志里全是“Permission denied”。提示上线后第一次创建后台管理员时把初始密码立刻换掉。防伪系统后台的失守等于直接给别人发真证书的能力。3.4 首次上线验证从生成到扫码的完整链路部署完成先别急着灌真实产品数据做一轮自测顺序如下浏览器打开 /admin/用初始账号登录后台产品管理里新增一件测试产品录入名称、规格、批次在防伪码管理里选“生成”输入数量 10确认后台返回 10 条不重复的码复制其中一条码请求 /api/query.php?codeXXXX确认返回 first同一浏览器再请求一次确认返回 repeat且带着首次查询时间用一个坏码请求一次确认返回 invalid手机扫码实测确认微信扫一扫能打开二维码里的 URL 并正常渲染页面。这七步全部过了最小闭环才算成立。第 4、5 步的 first/repeat 判断是把 2.3 的流程真正跑实。如果查询接口直接抛 500 或返回空白页先看 PHP 错误日志绝大多数是数据库账号连不上或表名对不上。不要跳过第 6 步无效码分支经常因为正则写错而把合法码也判成非法这一步能暴露字符集匹配问题。4. 核心代码拆解生成、查询、出证三段流程怎么改4.1 生成端随机码生成的兜底逻辑源码包里负责生成防伪码的地方一般在后台控制器里公开接口侧未必直接暴露。这里我把生成逻辑封装成一个 PHP 类方便你在后台任意位置复用?php // api/helper/FengweiCode.php —— 提供防伪码生成与入库 class FengweiCode { private PDO $pdo; public function __construct(PDO $pdo) { $this-pdo $pdo; } /** * 批量生成防伪码并入库 * param int $productId 产品ID * param int $quantity 数量 * return array 本次生成的明文码仅用于后台展示 */ public function generate(int $productId, int $quantity): array { $chars ABCDEFGHJKLMNPQRSTUVWXYZ23456789; $insert $this-pdo-prepare( INSERT INTO certificate (code, product_id, issue_date) VALUES (?, ?, ?) ); $generated []; $this-pdo-beginTransaction(); for ($i 0; $i $quantity; $i) { while (true) { $code ; for ($j 0; $j 16; $j) { $code . $chars[random_int(0, strlen($chars) - 1)]; } try { $insert-execute([$code, $productId, date(Y-m-d)]); break; } catch (PDOException $e) { // 唯一键冲突(23000)说明撞码了,重新生成;其它异常直接抛出 if ($e-getCode() ! 23000) { throw $e; } } } $generated[] $code; } $this-pdo-commit(); return $generated; } }逻辑说明内层 while 的撞码重试机制依赖 certificate.code 的唯一索引这是最后一道物理防线。random_int 的随机性基本不会撞但一旦撞了能立刻发现并换码不会在表里留下脏数据。外层事务把所有插入包在一起要么全部成功要么全部回滚避免后台显示生成了 5000 条、实际落库只有 4988 条。如果你采纳了 2.1 的 HMAC 存储方案execute 里的第二个参数就改成hash_hmac(sha256, $code, APP_KEY)返回给后台打印贴标的仍然是明文 $code。需要注意查询接口要做同一颗哈希再比对前后两处算法必须完全一致这个一致性建议抽成公共函数。4.2 查询端行锁 三次分支的判断写法查询端是消费者直接面对的部分判断要严谨响应要快。下面是完整的一次请求写法?php // api/query.php —— 消费者扫码查询入口 require __DIR__ . /../config.php; header(Content-Type: application/json; charsetutf-8); $code strtoupper(trim($_GET[code] ?? )); if (!preg_match(/^[A-Z0-9]{16}$/, $code)) { exit(json_encode([result invalid, msg 防伪码格式不正确], JSON_UNESCAPED_UNICODE)); } $pdo new PDO(DB_DSN, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, ]); $pdo-beginTransaction(); $stmt $pdo-prepare( SELECT id, product_id, query_count, first_query_at, first_query_ip, status FROM certificate WHERE code ? FOR UPDATE ); $stmt-execute([$code]); $cert $stmt-fetch(); if (!$cert || $cert[status] ! 1) { $pdo-commit(); exit(json_encode([result invalid, msg 未查询到该防伪码], JSON_UNESCAPED_UNICODE)); } $now date(Y-m-d H:i:s); $ip getClientIp(); $ua $_SERVER[HTTP_USER_AGENT] ?? ; // 分支一首次查询 if ($cert[query_count] 0) { $update $pdo-prepare( UPDATE certificate SET query_count 1, first_query_at ?, first_query_ip ? WHERE id ? ); $update-execute([$now, $ip, $cert[id]]); $log $pdo-prepare( INSERT INTO query_log (certificate_id, query_time, ip, ua, result_type) VALUES (?,?,?,?,1) ); $log-execute([$cert[id], $now, $ip, $ua]); $pdo-commit(); exit(json_encode([result first, msg 验证通过该产品为正品], JSON_UNESCAPED_UNICODE)); } // 分支二再次查询 $pdo-commit(); exit(json_encode([ result repeat, msg 防伪码已于 . $cert[first_query_at] . 验证过请核对, first_query_at $cert[first_query_at], first_query_ip $cert[first_query_ip], ], JSON_UNESCAPED_UNICODE));前几行正则过滤相当重要它把格式不对的流量挡在数据库之外扫码枪误扫、恶意脚本乱注都省了一次查询。中间的 FOR UPDATE 是行锁两个并发请求同时进来时第二个必须等第一个提交后重新读行看到的就是已更新过的 query_count1不会误判成首次。这是整段代码里最能体现系统严谨性的地方。事务边界的处理也值得说明分支一里先更新证书表、再插入日志、最后才 commit。如果两步之间发生异常事务回滚日志也不会残留证书状态和日志记录永远保持一致。重复查询分支同样建议落一条 result_type2 的日志做防刷分析时数据才全我在这段代码里为保持接口轻量留了注释位生产环境建议补上。4.3 出证端模板占位替换和二维码拼接证书展示这一层最核心的就是二维码生成和模板占位替换。二维码我用 PHP QR Code 库关键代码是这样?php require_once __DIR__ . /../libs/phpqrcode.php; require __DIR__ . /../config.php; $code strtoupper($_GET[code] ?? ); $queryUrl BASE_URL . /api/query.php?code . urlencode($code); // 生成到临时目录后续由模板直接引用 QRcode::png($queryUrl, __DIR__ . /../uploads/qr/ . $code . .png, QR_ECLEVEL_M, 6);第三个参数 QR_ECLEVEL_M 是容错级别 M意味着二维码即使被印刷磨损一部分仍能识别。证书纸和包装盒上的码一定要留容错余量别用最低档 L。第四个参数 6 是放大倍数打在标签上建议至少 5手机在 20-30cm 距离扫才不费劲。二维码内容用绝对 URL不能用相对路径手机摄像头没有“当前页面”的概念。证书内容我用 HTML 模板加 strtr 做不引入前端框架$html file_get_contents(__DIR__ . /../templates/cert.html); $vars [ {code} $code, {product} $product[name], {spec} $product[spec], {batch} $cert[batch_no], {manufacturer} $cert[producer], {issue_date} $cert[issue_date], {qr} /uploads/qr/ . $code . .png, ]; $html strtr($html, $vars); file_put_contents(__DIR__ . /../uploads/certs/ . $code . .html, $html);strtr 和 str_replace 的区别在于它是全局替换且支持数组映射不会出现占位符被二次替换的边界问题。证书文件生成成静态 HTML 后后续分享、打印、下载都不用再经过 PHP 渲染对并发体验更友好。如果你要批量出证这个逻辑放进循环里加一个文件是否已存在的判断即可。4.4 这套源码里最值得调的 5 个参数参数位置建议值调整场景防伪码长度FengweiCode 里的 1616-20码被批量猜时长一点但打印识别成本会上升字符集$chars 字符串去掉 0/O/1/I有手输场景就保留 32 字符集二维码容错QRcode::png 第三参M 或 Q证书纸反光严重时用 Qbase_urlconfig.php线上域名本地与线上切换必改查询上限query.php 分支判断默认无上限高价值商品加“超过 5 次提示”大多数源码包把 16 这个数写死在生成循环里。如果你打算长期运营建议把它提升到 config.php 里做成 GENERATE_LENGTH 配置项后续要调长度就不用动函数代码也方便多个后台入口共用同一套生成策略。字符集也是同理代码里写死最容易漏配置化之后前端校验和后台生成能保证用的是同一张字符表。5. 避坑与排查防伪系统的 5 个高频坑5.1 并发查询时“首次验证”被误判两次现象同一张防伪码被消费者和店员几乎同时扫码两次都返回“首次查询”后台 query_count 却只累计了 1 次。原因查询接口没有对防伪码行做锁或原子更新。两个并发请求同时读到 query_count0各自进入首次查询分支各自输出“首次验证”。这类问题只在流量上来以后才暴露测试环境单请求根本测不出来。解决把查询逻辑改成事务加 SELECT ... FOR UPDATE像 4.2 段那样让第二个请求等第一个 commit 后重新读行。如果用的是 MySQL 8也可以考虑原子自增 UPDATE但自增语句拿不到“自增前是否 0”的判定状态所以行锁在需要区分首次和重复的场景里仍然是更可靠的做法。5.2 中文乱码PHP 文件与 MySQL 表字符集不一致现象证书页面产品名显示成问号或“???”后台录入中文后查出来全是乱码但英文内容完全正常。原因站点 PHP 文件保存成 UTF-8 无 BOM但 MySQL 表是 latin1 或旧的 utf8其实是 utf8mb3要么 PDO 连接串没写 charset要么 install.sql 建表时漏了 DEFAULT CHARSET。乱码通常在第一次导入数据时就埋下了。解决建表统一用 utf8mb4PDO 连接时显式指定$pdo new PDO( mysql:host127.0.0.1;dbnamefangwei;charsetutf8mb4, DB_USER, DB_PASS, [PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION] );再补一个隐蔽点PHP 源文件本身必须是无 BOM 的 UTF-8带 BOM 的文件在最顶层输出的 JSON 前会多一个不可见字符证书展示页顶部多一行空白排查半天最后定位到 BOM 的案例不算少。Windows 笔记本编辑过的文件特别容易踩这个。5.3 二维码扫出来是 404 或跳到错误域名现象手机扫码后要么提示 404要么打开的是 localhost 或内网地址。原因config.php 里的 base_url 还是本地开发值或者 Nginx 把 /api/query.php 这种地址交给了静态文件处理没有转发给 PHP-FPM或者反向代理只配了 / 路径没放行 /api 子路径。解决上线前无痕模式访问https://你的域名/api/query.php?codeTEST能正常返回 JSON 再生成二维码。404 时先确认手机访问的是不是完整绝对 URL再查 Nginx 的 PHP 转发配置常见漏配是location ~ \.php$块不存在或缺少$request_filename。二维码里绝不使用相对路径这是移动端和 PC 端行为差异最容易翻车的点。5.4 防伪码被批量猜解随机源不安全的后果现象后台发现大量外部 IP 短时间内循环查询无效码某批码被撞出几百条真码造假者打印出伪造证书流入市场。原因源码包老版本里用了 rand()、mt_rand() 或固定种子生成防伪码或者码里直接包含可预测的流水号攻击者按规则就能拼出后续的码。随机性一旦可复现整个防伪体系就是一层纸。解决生成必须用 random_int这是底线。存储层面按 2.1 的方案对码做不可逆哈希即使数据库泄露也不能批量制造证书。另外建议在查询端加频率限制单 IP 每分钟超过 30 次无效查询直接拒绝一小时这能显著抬高批量枚举的成本。5.5 批量发码卡死插入方式与事务边界问题现象后台一次生成 2 万条码页面长时间无响应浏览器请求被 MySQL 超时切断最后库里只剩一半记录。原因同一个事务里插入两万条数据事务日志膨胀InnoDB 锁竞争加剧PHP 进程长期占用连接被 MySQL 的锁等待超时杀掉已插入未提交的部分全部回滚。实际生成数量远小于界面填写的数量。解决把大事务拆成小批次每 500 条提交一次循环里及时释 PDO 语句资源。批量执行时还可以先不开启事务依靠唯一索引来兜底撞码时单独重试。两万条以上的生成不建议放在网页同步请求里做应该走命令行脚本或任务队列第 6 章给的 CLI 骨架就是为这个准备的。6. 进阶用 CLI 脚本离线发码并把证书导出成 PDF到这里部署和常规使用已经收尾。最后一个技巧给发码规模往上走的团队改用 CLI 脚本做离线批量生成既不给 PHP-FPM 增加长请求压力又能在生成后统一归档。#!/usr/bin/env php ?php // bin/issue_cert.php —— 命令行批量发码 require __DIR__ . /../config.php; require __DIR__ . /../api/helper/FengweiCode.php; $productId (int)($argv[1] ?? 0); $quantity (int)($argv[2] ?? 1000); if ($productId 0 || $quantity 0) { fwrite(STDERR, 用法: php issue_cert.php 产品ID 数量\n); exit(1); } $pdo new PDO(DB_DSN, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, ]); $gen new FengweiCode($pdo); $chunk 500; for ($i 0; $i $quantity; $i $chunk) { $n min($chunk, $quantity - $i); $codes $gen-generate($productId, $n); fwrite(STDOUT, 已生成 $n 个累计 . ($i $n) . 个\n); } echo 完成\n;跑完以后去数据库里核对实际数量这是最直接的验证方式mysql -u root -p fangwei -e SELECT COUNT(*) FROM certificate WHERE product_id 123;导出 PDF 时最容易翻车的是中文字体TCPDF 默认字体不含中文字库输出全是白盒子。需要先用 addTTFfont 把思源黑体或系统自带中文字体转成字体描述文件再在 SetFont 里指定字体名。字体文件别放在公共目录避免被人下载拿去二次套用。这套系统跑两三年以后最能体现价值的反而不是证书多好看而是 query_log 累积出来的行为数据哪些码被反复扫、哪个区域频繁查无效码、哪个批次发出去很久没人查。我自己的习惯是每季度导一次日志做聚合把首次查询率掉得异常的产品单独拎出来查渠道和批次。防伪系统的意义不在拦截所有造假者而在让造假成本高到追不上真品利润。希望这些经验能帮你少走弯路。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。