fastadmin实现海报批量生成、邮件批量发送

devtools/2025/2/22 8:48:20/

记录一个海报批量生成、邮件批量发送功能开发,业务场景如下:

国外客户做观展预登记,工作人员通过后台,批量给这些观众生成入场证件并发送到观众登记的邮箱,以方便观众入场时快速进场。证件信息包含入场二维码、姓名;需要批量生成证件和批量发送邮件功能。

实现步骤大概如下:

index页面增加三个按钮,三个按钮的html如下:

 <a class="btn btn-info btn-change btn-start" data-params="" data-url="miniform/guojihaibao/getdata" href="javascript:;"><i class="fa fa-play"></i> 批量获取登记数据</a><a class="btn btn-success btn-disabled disabled btn-selected" href="javascript:;"><i class="fa fa-magic"></i> 批量生成海报</a> <a class="btn btn-warning btn-disabled disabled btn-sendemail" href="javascript:;"><i class="fa fa-leaf"></i> 批量发送邮件</a>

1、批量获取登记数据;

后端

    public function getdata(){$row = $this->model->query("SELECT a.name,a.email,a.qrcode 
FROM fa_miniform_di53jieguojimingjiajudongguanzhanlanhui a LEFT JOIN fa_haibao_guoji b
ON a.email=b.email
WHERE b.email IS NULL AND lang='en';");// dump($row);exit;if(!$row) $this->error('没有登记数据');$insert = $this->model->insertAll($row);if($insert){$this->success('同步了'.count($row).'条数据');}}

2、批量生成海报;

前端JS

 // 批量生成海报$(document).on("click", ".btn-selected", function () {let ids = Table.api.selectedids(table) //获取选中的条目ID集合ids.forEach(function(value,index) {let row = Table.api.getrowbyid(table, value) //根据主键ID获取行数据                if(row.url_image){Toastr.error(row.name+'已生成海报');return false;}$.ajax({type: "GET",url: "miniform/guojihaibao/get_poster" + '/ids/' + value, cache: false,success: function(data) {Toastr.info(data.msg);}});});table.bootstrapTable('refresh',{});});

后端

    //生成海报public function get_poster($ids=null){if(!$ids) $this->error('ids参数缺失');$row = $this->model->get($ids);$fileUrl = '/uploads/qrcode/haibao/'. $row->qrcode.'.jpg';$filename = ROOT_PATH .'public'. $fileUrl;//生成用户二维码$qrInfo = Haibao::buildQrcode($row->qrcode,'');$config = array('image'=>array(array('url'=>$qrInfo,     //二维码地址'is_yuan'=>false,          //true图片圆形处理'stream'=>0,'top'=>1140,'right'=>0,'width'=>500,             //图像宽'height'=>500,            //图像高'opacity'=>100            //透明度),),'text'=>array(array(// 'text'=>$userInfo['invite_code'],            //文字内容'text'=>$row->name,'left'=>-1,                              //小于0为水平居中      'top'=>1750,'fontSize'=>38,                         //字号'fontColor'=>'88, 133, 44',                //字体颜色'angle'=>0,'fontPath'=>ROOT_PATH.'/public/assets/fonts/SourceHanSansK-Regular.ttf',     //字体文件)),'background'=>cdnurl($this->background,true),          //背景图);Haibao::createPoster($config,$filename);$url = cdnurl($fileUrl,true);if($url){$update = $this->model->save(['url_image'=>$fileUrl],['id'=>$ids]);if($update) $this->success('生成成功',$url);}}

其中生成二维码和生成海报引入了另外一个类文件Haibao

<?phpnamespace app\admin\model\call;use think\Model;
use think\Response;
use traits\model\SoftDelete;class Haibao extends Model
{use SoftDelete;// 表名protected $name = 'haibao';// 自动写入时间戳字段protected $autoWriteTimestamp = 'integer';// 定义时间戳字段名protected $createTime = 'createtime';protected $updateTime = 'updatetime';protected $deleteTime = 'deletetime';// 追加属性protected $append = [];public static function init(){self::afterWrite(function ($row) {});self::afterDelete(function ($row) {});self::afterInsert(function ($row) {// dump($row['text1']);exit;});self::afterUpdate(function ($row) {});}// 生成二维码public static function buildQrcode($text,$label){$params = ['text'           => $text,'size'           => 350,    //大小'padding'        => 15,    //内边距'errorlevel'     => 'medium',   //容错级别:low-低   medium-中等   quartile-高   high-超高'foreground'     => "#000000",     //前景色'background'     => "#ffffff",  //背景色'logo'           => 0,    //Logo:1-显示,0-不显示'logosize'       => '',  //Logo大小'label'          => $label, //标签'labelfontsize'  => 14, //标签大小'labelalignment' => 'center',    //标签水平位置:left-左  center-中   right-右];$qrCode = \addons\qrcode\library\Service::qrcode($params);$response = Response::create()->header("Content-Type", "image/png");// 直接显示二维码header('Content-Type: ' . $qrCode->getContentType());$response->content($qrCode->writeString());// 写入到文件$fileUrl = '/uploads/qrcode/haibao/' . md5(implode('', $params)) . '.png';$filePath = ROOT_PATH .'public'. $fileUrl;if (!file_exists(ROOT_PATH .'public/uploads/qrcode/')) mkdir (ROOT_PATH .'public/uploads/qrcode/',0777,true); if (!file_exists(ROOT_PATH .'public/uploads/qrcode/haibao/')) mkdir (ROOT_PATH .'public/uploads/qrcode/haibao/',0777,true); $qrCode->writeFile($filePath);return $filePath;}/*** 生成宣传海报* @param array  参数,包括图片和文字* @param string  $filename 生成海报文件名,不传此参数则不生成文件,直接输出图片* @return [type] [description]*/public static function createPoster($config = array() , $filename = "") {//如果要看报什么错,可以先注释调这个header//if(empty($filename)) header("content-type: image/png");if (empty($filename)) header("content-type: image/png");$imageDefault = array('left' => 0,'top' => 0,'right' => 0,'bottom' => 0,'width' => 100,'height' => 100,'opacity' => 100);$textDefault = array('text' => '','left' => 0,'top' => 0,'fontSize' => 32, //字号'fontColor' => '255,255,255', //字体颜色'angle' => 0,);$background = $config['background']; //海报最底层得背景//背景方法$backgroundInfo = getimagesize($background);$backgroundFun = 'imagecreatefrom' . image_type_to_extension($backgroundInfo[2], false);$background = $backgroundFun($background);$backgroundWidth = imagesx($background); //背景宽度$backgroundHeight = imagesy($background); //背景高度$imageRes = imageCreatetruecolor($backgroundWidth, $backgroundHeight);$color = imagecolorallocate($imageRes, 0, 0, 0);imagefill($imageRes, 0, 0, $color);imagecopyresampled($imageRes, $background, 0, 0, 0, 0, imagesx($background) , imagesy($background) , imagesx($background) , imagesy($background));//处理了图片if (!empty($config['image'])) {foreach ($config['image'] as $key => $val) {$val = array_merge($imageDefault, $val);$info = getimagesize($val['url']);$function = 'imagecreatefrom' . image_type_to_extension($info[2], false);if ($val['stream']) { //如果传的是字符串图像流$info = getimagesizefromstring($val['url']);$function = 'imagecreatefromstring';}$res = $function($val['url']);$resWidth = $info[0];$resHeight = $info[1];//建立画板 ,缩放图片至指定尺寸$canvas = imagecreatetruecolor($val['width'], $val['height']);imagefill($canvas, 0, 0, $color);//如果是透明的gif或png做透明处理$ext = pathinfo($val['url']);if (array_key_exists('extension',$ext)) {if ($ext['extension'] == 'gif' || $ext['extension'] == 'png') {// imageColorTransparent($canvas, $color); //颜色透明                     }}//关键函数,参数(目标资源,源,目标资源的开始坐标x,y, 源资源的开始坐标x,y,目标资源的宽高w,h,源资源的宽高w,h)imagecopyresampled($canvas, $res, 0, 0, 0, 0, $val['width'], $val['height'], $resWidth, $resHeight);//$val['left'] = $val['left']<0?$backgroundWidth- abs($val['left']) - $val['width']:$val['left'];//如果left小于-1我这做成了计算让其水平居中if ($val['left'] < 0) {$val['left'] = ceil($backgroundWidth - $val['width']) / 2;}$val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) - $val['height'] : $val['top'];//放置图像imagecopymerge($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height'], $val['opacity']); //左,上,右,下,宽度,高度,透明度}}//处理文字if (!empty($config['text'])) {foreach ($config['text'] as $key => $val) {$val = array_merge($textDefault, $val);list($R, $G, $B) = explode(',', $val['fontColor']);$fontColor = imagecolorallocate($imageRes, $R, $G, $B);//$val['left'] = $val['left']<0?$backgroundWidth- abs($val['left']):$val['left'];//如果left小于-1我这做成了计算让其水平居中if ($val['left'] < 0) {$fontBox = imagettfbbox($val['fontSize'], 0, $val['fontPath'], $val['text']); //文字水平居中实质$val['left'] = ceil(($backgroundWidth - $fontBox[2]) / 2); //计算文字的水平位置}$val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) : $val['top'];imagettftext($imageRes, $val['fontSize'], $val['angle'], $val['left'], $val['top'], $fontColor, $val['fontPath'], $val['text']);}}//生成图片if (!empty($filename)) {$res = imagejpeg($imageRes, $filename, 90); //保存到本地imagedestroy($imageRes);if (!$res) return false;return $filename;} else {header("Content-type:image/png");imagejpeg($imageRes); //在浏览器上显示imagedestroy($imageRes);}}}

3、批量发送邮件;

前端JS

         // 批量发送邮件$(document).on("click", ".btn-sendemail", function () {let ids = Table.api.selectedids(table) //获取选中的条目ID集合ids.forEach(function(id,index) {let row = Table.api.getrowbyid(table, id) //根据主键ID获取行数据                if(row.send_email){Toastr.error(row.name+'有发送记录');return false;}if(!row.url_image){Toastr.error(row.name+'无海报,请先生成');return false;}$.ajax({type: "GET",url: 'miniform/guojihaibao/email_api?image='+row.url_image+'&email='+row.email+"&ids="+row.id,cache: false,success: function(data) {Toastr.info(data.msg);}});});table.bootstrapTable('refresh',{});});

后端

    /**英文--提交email*/public function email_api($ids=null,$email=null,$image=null){if (!preg_match('/^[^\s@]+@[^\s@]+\.[^\s@]+$/', $email)) $this->error('邮箱正则不通过');if(!$image) $this->error('无海报');$url = 'xxx';$title = 'VIP badge to participate the 53rd International Famous Furniture Fair (Dongguan)';$fsr='FURNITRUE FAIR (DONGGUAN)';// dump($image);$neirong = '<img src="'.cdnurl($image,true).'">';// $email = 'zhanpeng.wang@qq.com';$params = ['title'=>$title,'fsr'=>$fsr,'neirong'=>$neirong,'youxiang'=>$email];// dump($params);exit;$result = \fast\Http::post($url, $params);if($result){$result = json_decode($result,true);if($result['code']==200){$this->model->save(['send_email'=>time()],['id'=>$ids]);// $row = $this->model->get($ids);// dump();exit;// $row->send_email = time();// $row->save();$this->success($result['message']);}else{$this->error($result['message']);}}else{$this->error('API接口错误');}}

最终实现后台管理效果如下:

生成海报的效果

客户收到邮件的效果(每个邮件平台不一样,仅作参考)


http://www.ppmy.cn/devtools/160907.html

相关文章

亲测Windows部署Ollama+WebUI可视化

一. Ollama下载 登录Ollama官网(Ollama)点击Download进行下载 如果下载很慢可用以下地址下载&#xff1a; https://github.com/ollama/ollama/releases/download/v0.5.7/OllamaSetup.exe 在DeepSeek官网上&#xff0c;你可以直接点击【model】 到达这个界面之后&#xff0c;…

Deepseek整合SpringAI

在现代应用开发中&#xff0c;问答系统是一个常见的需求&#xff0c;尤其是在客服、教育和技术支持领域。本文将介绍如何使用 Spring Boot、Deepseek 和 Spring AI 构建一个简单的问答系统&#xff0c;并通过 Postman 调用 API 接口实现问答功能。通过本文&#xff0c;你将学习…

STL —— 洛谷字符串(string库)入门题(蓝桥杯题目训练)(二)

目录 一、B2121 最长最短单词 - 洛谷 算法代码&#xff1a; 代码分析 变量定义 输入处理 单词长度计算 更新最长和最短单词的长度 输出最长单词 输出最短单词 评测记录&#xff1a;​编辑 二、B2122 单词翻转 - 洛谷 算法代码&#xff1a; 代码分析 引入头文件和定…

今日行情明日机会——20250221

明日投资机会分析 根据2月21日市场数据&#xff0c;市场情绪活跃&#xff08;涨停117家&#xff0c;跌停4家&#xff09;&#xff0c;资金聚焦机器人、AI、算力、芯片等科技主线&#xff0c;结合板块梯队与连板强度&#xff0c;建议重点关注以下方向&#xff1a; 1. 机器人概念…

基于Spring Boot的农产品智慧物流系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

AI客服-接入deepseek大模型到微信(本地部署deepseek集成微信自动收发消息)

1.本地部署 1.1 ollama Ollama软件通过其高度优化的推理引擎和先进的内存管理机制&#xff0c;显著提升了大型语言模型在本地设备上的运行效率。其核心采用了量化技术&#xff08;Quantization&#xff09;以降低模型的计算复杂度和存储需求&#xff0c;同时结合张量并行计算&…

基于ffmpeg+openGL ES实现的视频编辑工具-opengl相关逻辑(五)

在我们的项目中,OpenGL ES 扮演着至关重要的角色,其主要功能是获取图像数据,经过一系列修饰后将处理结果展示到屏幕上,以此实现各种丰富多样的视觉效果。为了让大家更好地理解后续知识,本文将详细介绍 OpenGL 相关代码。需要注意的是,当前方案将对 OpenGL 的所有操作都集…

Redis 设置密码无效问题解决

一、验证密码有没有生效 运行cmd&#xff0c;cd到redis的目录下 输入“redis-cli.exe” 回车 输入“auth 123456” 回车 若错误&#xff0c;说明没有设置密码或者设置的密码没有生效 输入“exit” 回车就立即退出redis 二、解决方案是&#xff1a;直接修改后缀是 .conf 的…