最新ThinkPHP版本实现证书查询系统,实现批量数据导入,自动生成电子证书

news2024/11/18 15:22:43

前提:朋友弄了一个培训机构,培训考试合格后,给发证书,需要一个证书查询系统。委托我给弄一个,花了几个晚上给写的证书查询系统。

实现功能:

  1. 前端按照姓名+手机号码进行证书查询
  2. 证书信息展示+证书展示,支持点击下载
  3. 后端证书信息录入+带一寸照片数据批量导入

技术栈:
4. ThinkPHP8.0
5. MySQL8.0.12
6. layui

有几个技术点,得记录下:
1、给一个证书模板、一寸照片、两个电子印章图片,这个技术点是:GD库添加水印技术,水印添加也包括文字水印

添加扩展:

"ext-gd": "*",

相关代码:

// 向不同格式的图片中间画一个字符串(也是文字水印)
public function image()
 {
     // $id = Request::param('id');
     $one = Db::table('certificate')->where(Request::param())->find();


     // $this->pic($one['id']);

     /*$one = [
         "certificate_no" => "440420230078",
         "create_time" => "2022-10-10",
         "end_time" => "2032-10-10",
         "id" => 8,
         "id_no" => "4404202206223919",
         "id_type" => "身份证",
         "institution" => "智能研究院",
         "job" => "电工",
         "name" => "曹操",
         "occupation" => "低压电工证",
         "phone" => "13588889999",
         "sex" => "男",
         "skill_level" => "中级",
         "start_time" => "2022-10-10"
     ];*/
     // 图片路径
     $imagePath = './static/images/zsmb.png';
     // 图片信息
     $imageInfo = getimagesize($imagePath);
     //  [0] => 2000 [1] => 1414 [2] => 3 [3] => width="2000" height="1414" [bits] => 8 [mime] => image/png

     // 图片扩展名
     $imageExtension = image_type_to_extension($imageInfo[2], false);
     // png

     // 获取图片
     $func = 'imagecreatefrom' . $imageExtension;
     // imagecreatefrompng
     $image = $func($imagePath);
     /***************************** 文字水印开始 ********************************/
     // 水印字体
     $font = './static/fonts/SourceHanSansSC-Medium-2.otf';
     // 水印文字
     // $content = '曹操';
     // 水印颜色
     $color = imagecolorallocatealpha($image, 0, 0, 0, 30);
     // 添加水印
     imagettftext($image, 22, 0, 1450, 650, $color, $font, $one['name']);
     imagettftext($image, 22, 0, 1450, 740, $color, $font, $one['id_type']);
     imagettftext($image, 22, 0, 1450, 830, $color, $font, $one['id_no']);
     imagettftext($image, 22, 0, 1450, 910, $color, $font, $one['occupation']);
     imagettftext($image, 22, 0, 1480, 985, $color, $font, $one['job']);
     imagettftext($image, 22, 0, 1480, 1075, $color, $font, $one['skill_level']);
     imagettftext($image, 22, 0, 1450, 1155, $color, $font, $one['certificate_no']);
     imagettftext($image, 22, 0, 750, 1080, $color, $font, date("Y年n月j日", strtotime($one['create_time'])));
     imagettftext($image, 22, 0, 300, 1080, $color, $font, date("Y年n月j日", strtotime($one['create_time'])));
     imagettftext($image, 22, 0, 500, 498, $color, $font, $one['institution']);

     /***************************** 文字水印开始 ********************************/

     /***************************** 图片水印开始 ********************************/
     // 获取水印图片
     $watermarkPath = './static/images/water.png';
     $watermarkInfo = getimagesize($watermarkPath);
     $watermarkExtension = image_type_to_extension(getimagesize($watermarkPath)[2], false);
     $func = 'imagecreatefrom' . $watermarkExtension;
     $watermark = $func($watermarkPath);
     // 添加图片水印
     imagecopymerge($image, $watermark, 1280, 180, 0, 0, $watermarkInfo[0], $watermarkInfo[1], 100);
     // 销毁水印图片
     imagedestroy($watermark);

     // 生成二维码
     $qr = "./static/images/qr.png";
     $str = 'http://thinkcms.cc/index/index/image?id=' . $one['id'];
     $img_data = \tekintian\TekinQR::getQRImg($str, 5, null, 3);
     file_put_contents($qr, $img_data);

     $watermarkInfo = getimagesize($qr);
     $watermarkExtension = image_type_to_extension(getimagesize($qr)[2], false);
     $func = 'imagecreatefrom' . $watermarkExtension;
     $watermark = $func($qr);
     // 添加图片水印
     imagecopymerge($image, $watermark, 1580, 370, 0, 0, $watermarkInfo[0], $watermarkInfo[1], 100);
     // 销毁水印图片
     imagedestroy($watermark);

     $watermarkPath = './static/images/nyyz.png';
     $watermarkInfo = getimagesize($watermarkPath);
     $watermarkExtension = image_type_to_extension(getimagesize($watermarkPath)[2], false);
     $func = 'imagecreatefrom' . $watermarkExtension;
     $watermark = $func($watermarkPath);
     // 添加图片水印
     imagecopymerge($image, $watermark, 220, 850, 0, 0, $watermarkInfo[0], $watermarkInfo[1], 100);
     // 销毁水印图片
     imagedestroy($watermark);

     $watermarkPath = './static/images/gjyz.png';
     $watermarkInfo = getimagesize($watermarkPath);
     $watermarkExtension = image_type_to_extension(getimagesize($watermarkPath)[2], false);
     $func = 'imagecreatefrom' . $watermarkExtension;
     $watermark = $func($watermarkPath);
     // 添加图片水印
     imagecopymerge($image, $watermark, 720, 850, 0, 0, $watermarkInfo[0], $watermarkInfo[1], 100);
     // 销毁水印图片
     imagedestroy($watermark);

     /***************************** 图片水印开始 ********************************/

     // 输出图片
     header('Content-Type:' . $imageInfo['mime']);
     $func = 'image' . $imageExtension;
     $func($image, null, 6);
     // 销毁图片
     imagedestroy($image);
     exit;
 }

在这里插入图片描述

2、证书里的二维码,扫码就能扫码出来,是PHP生成二维码,其二维码中间也是支持加logo的

添加扩展:

"tekintian/phpqrcode": "^1.1",

相关代码:

// 生成二维码
$qr = "./static/images/qr.png";
// 这个本地测试域名,不是外网,扫码不了,上线改成外网扫了才有效
$str = 'http://thinkcms.cc/index/index/image?id=' . $one['id'];
$img_data = \tekintian\TekinQR::getQRImg($str, 5, null, 3);
file_put_contents($qr, $img_data);

3、后台实现带图片excel数据导入,这个摸索了点时间,因为PHPExcel停止维护了,改了半天源码还有问题,最后放弃了,换了一个扩展。

添加扩展:

"phpoffice/phpspreadsheet": "^1.20.0"

相关代码:

/**
 * 字母序列化为数字
 */
public function ABC2decimal($abc)
{
    $ten = 0;
    $len = strlen($abc);
    for ($i = 1; $i <= $len; $i++) {
        $char = substr($abc, 0 - $i, 1);//反向获取单个字符
        $int = ord($char);
        $ten += ($int - 65) * pow(26, $i - 1);
    }
    return $ten;
}


public function excel()
{
    if ($file = request()->file('excel')) {
        try {
            $saveName = Filesystem::disk('public')->putFile('/static/upload/excels', $file);
            if (!is_file($saveName)) {
                return json(['code' => 1, 'msg' => '文件不存在', 'data' => null]);
            }
            $ext = pathinfo($saveName, PATHINFO_EXTENSION);
            // xls不支持图片导入
            // if (!in_array($ext, ['xlsx', 'xls'])) {
            if (!in_array($ext, ['xlsx'])) {
                return json(['code' => 1, 'msg' => '文件类型不正确', 'data' => null]);
            }
            // 有两种格式,xlsx和xls
            if ($ext == 'xlsx') {
                $objReader = IOFactory::createReader('Xlsx');
            } else {
                $objReader = IOFactory::createReader('Xls');
            }
            // 图片保存路径
            $imageFilePath1 = root_path() . '/public/'; // 图片保存目录
            $imageFilePath2 = 'static/upload/images/' . date("Ymd") . '/';
            $imageFilePath = $imageFilePath1 . $imageFilePath2;
            if (!file_exists($imageFilePath)) {
                mkdir("$imageFilePath", 0777, true);
            }
            // 载入excel文件
            $excel = $objReader->load($saveName);
            // 读取第一张表
            $sheet = $excel->getActiveSheet();
            // 读取总行数
            $highestRow = $sheet->getHighestRow();
            // 读取第一张表转换成数组
            $data = $sheet->toArray();

            // 处理图片
            foreach ($sheet->getDrawingCollection() as $drawing) {
                list($startColumn, $startRow) = Coordinate::coordinateFromString($drawing->getCoordinates());
                $imageFileName = $drawing->getIndexedFilename();  // 获取文件名
                switch ($drawing->getExtension()) {
                    case 'jpg':
                    case 'jpeg':
                        $source = imagecreatefromjpeg($drawing->getPath());
                        imagejpeg($source, $imageFilePath . $imageFileName);
                        break;
                    case 'gif':
                        $source = imagecreatefromgif($drawing->getPath());
                        imagegif($source, $imageFilePath . $imageFileName);
                        break;
                    case 'png':
                        $source = imagecreatefrompng($drawing->getPath());
                        imagepng($source, $imageFilePath . $imageFileName);
                        break;
                }
                $startColumn = $this->ABC2decimal($startColumn);
                $data[$startRow - 1][$startColumn] = $imageFilePath2 . $imageFileName;
            }

            // 数据写入数据库
            $add_data = [];
            for ($i = 1; $i <= $highestRow - 1; $i++) {
                $add_data[$i]['institution'] = $data[$i][0];
                $add_data[$i]['name'] = $data[$i][1];
                $add_data[$i]['sex'] = $data[$i][2];
                $add_data[$i]['phone'] = $data[$i][3];
                $add_data[$i]['id_type'] = $data[$i][4];
                $add_data[$i]['id_no'] = $data[$i][5];
                $add_data[$i]['occupation'] = $data[$i][6];
                $add_data[$i]['job'] = $data[$i][7];
                $add_data[$i]['skill_level'] = $data[$i][8];
                $add_data[$i]['certificate_no'] = $data[$i][9];
                $add_data[$i]['create_time'] = $data[$i][10];
                $add_data[$i]['start_time'] = $data[$i][11];
                $add_data[$i]['end_time'] = $data[$i][12];
                $add_data[$i]['path'] = $data[$i][13];
            }

            // 数据插入数据库
            $success_count = Db::table('certificate')->insertAll($add_data);
            if ($success_count > 0) {
                return json(['code' => 0, 'msg' => '数据插入成功', 'data' => null]);
            } else {
                return json(['code' => 1, 'msg' => '数据插入失败', 'data' => null]);
            }
        } catch (\Exception $e) {
            return json(['code' => 1, 'msg' => $e->getMessage(), 'data' => null]);
        }
    } else {
        return json(['code' => 1, 'msg' => '上传文件不能为空', 'data' => null]);
    }
}
  1. ThinkPHP8.0开启session,没有采取前后端分离,就是前后端分离后,SEO不友好,再去前端优化搜索,那还不如直接MVC来的快,所以用到了session。根据官方手册没有配置成功,session没有生效,后面尝试了下面的方法,两者都进行配置才生效。这里记录下。

全局config配置middleware.php中

<?php
// 中间件配置
return [
    // 别名或分组
    'alias'    => [],
    // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
    'priority' => ['think\middleware\SessionInit'],
];

后端应用middleware.php中

<?php
// 这是系统自动生成的middleware定义文件
return [
	// 无关代码省略...
    \think\middleware\SessionInit::class
];

弄了写数据模拟下,最终实现的功能成果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1368013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0108作业

#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {this->setWindowTitle("腾讯会议");this->resize(470,800);//设置界面大小this->setFixedSize(470,800);//锁定界面大小this->setStyleSheet("background-color:w…

老板向我请教Transformer的原理,我没讲清

最近&#xff0c;一直跟别人讲大语言模型带来的AIGC是巨变&#xff0c;涉及了多个领域&#xff0c;并且谈了我们工作和生活中可以利用的地方&#xff0c;以及预测2024年大语言模型将在哪些领域爆发。这时&#xff0c;老板过来了&#xff0c;就聊&#xff0c;问&#xff0c;谈到…

解决:Unity : Error while downloading Asset Bundle: Couldn‘t move cache data 问题

目录 问题&#xff1a; 尝试 问题得到解决 我的解释 问题&#xff1a; 最近游戏要上线&#xff0c;发现一个现象&#xff0c;部分机型在启动的时候闪退或者黑屏&#xff0c;概率是5%左右&#xff0c;通过Bugly只有个别机型才有这个现象&#xff0c;其实真实情况比这严重的多…

【操作系统】复习汇总(各章节知识图谱)

第1章&#xff1a; 第2章&#xff1a; 第3章&#xff1a; 第4章&#xff1a; 第5章&#xff1a; 第6章&#xff1a; 第7章&#xff1a; 第8章&#xff1a; 第9章&#xff1a;

如何用GPT/GPT4完成AI绘图和论文写作?

详情点击链接&#xff1a;如何用GPT/GPT4完成AI绘图和论文写作&#xff1f; 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Clau…

Vue使用printJS导出网页为pdf、printJS导出pdf

先放几个参考链接 感谢&#xff01; Vue使用PrintJS实现页面打印功能_vue print.js 设置打印pdf的大小-CSDN博客 前台导出pdf经验汇总 &#xff08;html2canvas.js和浏览器自带的打印功能-print.js&#xff09;以及后台一些导出pdf的方法_iqc后台管理系统怎么做到导出pdf-CSD…

3D模型UV展开原理

今年早些时候&#xff0c;我为 MAKE 杂志写了一篇教程&#xff0c;介绍如何制作视频游戏角色的毛绒动物。 该技术采用给定的角色 3D 模型及其纹理&#xff0c;并以编程方式生成缝纫图案。 虽然我已经编写了一般摘要并将源代码上传到 GitHub&#xff0c;但我在这里编写了对使这一…

3D软件坐标系速查【左手/右手】

本文介绍不同3D软件的世界坐标系之间的差异及其工作原理。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 基本上&#xff0c;游戏引擎和3…

西电期末考点总结

一.“打擂台” 介绍 打擂台用于找到一个数组中的最值问题&#xff0c;先设置一个虚拟擂主&#xff0c;并保证他是“最弱的”&#xff0c;然后遍历数组&#xff0c;找到“更强的”数据&#xff0c;就交换擂主&#xff0c;“打”到最后的“擂主”就是最值数据 相关题目 1004.…

【服务器数据恢复】Raid5热备盘同步失败导致lvm结构损坏的数据恢复案例

服务器数据恢复环境&#xff1a; 两组由4块磁盘组建的raid5磁盘阵列&#xff0c;两组raid5阵列划分为lun并组成了lvm结构&#xff0c;ext3文件系统。 服务器故障&#xff1a; 一组raid5阵列中的一块硬盘离线&#xff0c;热备盘自动上线并开始同步数据。在热备盘完成同步之前&am…

小H靶场笔记:DC-7

DC-7 January 8, 2024 4:11 PM Tags&#xff1a;Drupal 8&#xff1b;Drush Owner&#xff1a;只惠摸鱼 信息收集 使用arp-scan和nmap扫描C段存活主机&#xff0c;探测到靶机ip&#xff1a;192.168.199.137&#xff0c;且开放80、22端口 探测22、80开放端口的服务、版本、操…

统计数据集句子长度信息

在文本分类任务做科研写论文的时候&#xff0c;我们有时候需要对对数据集的大小进行分析&#xff0c;如果你想统计CSV文件中某一列英语句子的单词个数&#xff08;不包含标题&#xff09;&#xff0c;可以使用Python的split()函数将句子拆分为单词&#xff0c;并计算单词的个数…

【EasyExcel】导出excel冻结表头和冻结指定列并支持筛选器

需求背景&#xff1a; 导出excel的同时冻结表头和前两列基础信息&#xff0c;方便导出后用户查看信息。 一、技术选型&#xff1a; easyExcel的自定义写策略处理&#xff1a;SheetWriteHandler 二、方案设计&#xff1a;&#xff08;基于实现 SheetWriteHandler 接口&#xf…

歌词滚动显示

歌词滚动显示 环境准备htmldata.js歌词css 解析歌词为对象数组查找指定时间点的歌词创建歌词元素li计算偏移量监听播放时间执行偏移计算 模仿音乐软件实现歌词随播放时间滚动显示 环境准备 html <!DOCTYPE html> <html lang"en"> <head><meta…

Redis基本原理和基础知识

目录 一、基本原理 &#xff08;一&#xff09;非关系型数据库 &#xff08;二&#xff09;关系型数据库与非关系型数据库的区别 &#xff08;三&#xff09;Redis简介 1.什么是Redis 2.数据存储结构 3.默认端口号 4.数据类型 &#xff08;1&#xff09;五大基础类型 …

test fuzz-02-模糊测试 JQF + Zest Semantic Fuzzing for Java

拓展阅读 开源 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) 开源 Junit performance rely on junit5 and jdk8.(java 性能测试框架。性能测试。压测。测试报告生成。) test fuzz-01-模糊测试&#xff08;Fuzz Testing&#xff09; test fuzz-…

软件概要设计(word)原件

1引言 1.1编写目的 1.2项目背景 1.3参考资料 2系统总体设计 2.1整体架构 2.2整体功能架构 2.3整体技术架构 2.4运行环境设计 2.5设计目标 3系统功能模块设计 3.1个人办公 4性能设计 4.1响应时间 4.2并发用户数 5接口设计 5.1接口设计原则 5.2接口实现方式 6运行设计 6.1运行模块…

Linux ls命令

目录 一. 配置项1.1 ls -l1.2 ls -a1.3 ls -lrt1.4 ls -ld .?* 二. 案例2.1 查看指定文件夹下文件的数量2.2 查看多个文件夹下文件信息 一. 配置项 1.1 ls -l ⏹ ls 列出当前文件夹下所有文件名称(不包含隐藏文件) jmw_num_00 jmw_num_02 jmw_num_04 jmw_num_06 jmw_n…

跨平台的文件传输协议@windows端服务器的配置@smb协议共享方案@ftp服务器设置

文章目录 abstractrefs ftp server下面是核心步骤FAQ smb server设置方法 共享文件夹的访问控制补充匿名访问问题协议相关信息参考android客户端推荐FAQ不同用户文件无法访问 比较和总结传输速率问题 abstract 文件传输协议是很常用的协议特别是跨平台的协议,往往更加受欢迎,应…

VS2022 | 调整适配虚幻5的设置

VS2022 | 调整适配虚幻5的设置