提交其他文件

This commit is contained in:
2026-03-14 16:20:49 +08:00
parent a227deaecd
commit 0a19b334f8
1385 changed files with 73568 additions and 0 deletions

View File

@ -0,0 +1,293 @@
<?php
namespace app\common\service\qrcode;
use think\facade\Request;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Color\Color;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\RoundBlockSizeMode;
use Exception;
class QrcodeService
{
/**
* 生成二维码并保存到文件
* @param string $content 二维码内容
* @param string $filename 文件名(可选,不包含路径)
* @param array $options 配置选项
* @return array 返回结果信息
*/
public function saveImage(string $content, string $filename = '', array $options = []): array
{
try {
// 验证内容
if (empty($content)) {
throw new Exception('二维码内容不能为空');
}
// 默认配置
$defaultOptions = [
'size' => 200, // 图片尺寸
'margin' => 10, // 边距
'foreground_color' => '#000000', // 前景色(黑色)
'background_color' => '#FFFFFF', // 背景色(白色)
'error_correction' => 'high', // 容错级别low, medium, quartile, high
'encoding' => 'UTF-8', // 编码
'round_block_size' => false, // 圆角模块
'validate_result' => false, // 验证结果
];
// 合并配置
$config = array_merge($defaultOptions, $options);
// 生成文件名
if (empty($filename)) {
$filename = $this->generateFilename($content);
}
// 确保是PNG格式
$filename = $this->ensurePngExtension($filename);
// 生成二维码
$imageData = $this->generateQrCode($content, $config);
// 保存文件
$saveInfo = $this->saveToFile($imageData, $filename);
return [
'file_url' => $saveInfo['path']
];
} catch (Exception $e) {
return [
'success' => false,
'message' => '二维码生成失败',
'error' => $e->getMessage(),
'content' => $content
];
}
}
/**
* 生成二维码图片数据
*/
private function generateQrCode(string $content, array $config): string
{
// 创建二维码对象
$qrCode = QrCode::create($content)
->setEncoding(new Encoding($config['encoding']))
->setErrorCorrectionLevel($this->getErrorCorrectionLevel($config['error_correction']))
->setSize($config['size'])
->setMargin($config['margin'])
->setRoundBlockSizeMode($config['round_block_size'] ?
RoundBlockSizeMode::Margin : RoundBlockSizeMode::None)
->setForegroundColor(new Color(
$this->hexToR($config['foreground_color']),
$this->hexToG($config['foreground_color']),
$this->hexToB($config['foreground_color'])
))
->setBackgroundColor(new Color(
$this->hexToR($config['background_color']),
$this->hexToG($config['background_color']),
$this->hexToB($config['background_color'])
));
// 创建写入器
$writer = new PngWriter();
// 生成二维码
$result = $writer->write($qrCode);
// 如果需要验证
if ($config['validate_result']) {
$reader = new \Endroid\QrCode\Reader\Reader();
try {
$reader->read($result->getString());
} catch (\Exception $e) {
throw new Exception('二维码验证失败: ' . $e->getMessage());
}
}
return $result->getString();
}
/**
* 保存图片到文件
*/
private function saveToFile(string $imageData, string $filename): array
{
// 创建存储目录(按日期分类)
$dateDir = date('Ymd');
$saveDir = 'uploads/qrcode/' . $dateDir . '/';
$relativePath = $saveDir . $filename;
// 获取完整路径
$rootPath = app()->getRootPath();
$fullPath = $rootPath . 'public/' . $relativePath;
// 确保目录存在
$dirPath = dirname($fullPath);
if (!is_dir($dirPath)) {
if (!mkdir($dirPath, 0755, true)) {
throw new Exception('无法创建目录: ' . $dirPath);
}
}
// 保存文件
if (file_put_contents($fullPath, $imageData) === false) {
throw new Exception('无法保存文件到: ' . $fullPath);
}
// 验证文件是否保存成功
if (!file_exists($fullPath) || filesize($fullPath) === 0) {
throw new Exception('文件保存失败或文件大小为0');
}
// 生成可访问的URL
$domain = Request::domain();
$fileUrl = $domain . '/' . $relativePath;
return [
'filename' => $filename,
'path' => $relativePath,
'full_path' => $fullPath,
'url' => $fileUrl
];
}
/**
* 生成唯一的文件名
*/
private function generateFilename(string $content): string
{
$timestamp = time();
$hash = substr(md5($content . $timestamp), 0, 8);
return 'qrcode_' . date('YmdHis') . '_' . $hash . '.png';
}
/**
* 确保文件扩展名为.png
*/
private function ensurePngExtension(string $filename): string
{
$pathinfo = pathinfo($filename);
// 如果没有扩展名或扩展名不是png则添加.png
if (!isset($pathinfo['extension']) || strtolower($pathinfo['extension']) !== 'png') {
$filename = ($pathinfo['filename'] ?? 'qrcode') . '.png';
}
return $filename;
}
/**
* 获取错误纠正级别
*/
private function getErrorCorrectionLevel(string $level): ErrorCorrectionLevel
{
$levels = [
'low' => ErrorCorrectionLevel::Low,
'medium' => ErrorCorrectionLevel::Medium,
'quartile' => ErrorCorrectionLevel::Quartile,
'high' => ErrorCorrectionLevel::High,
];
return $levels[$level] ?? ErrorCorrectionLevel::High;
}
/**
* 从十六进制颜色获取R值
*/
private function hexToR(string $hex): int
{
$hex = ltrim($hex, '#');
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
return hexdec(substr($hex, 0, 2));
}
/**
* 从十六进制颜色获取G值
*/
private function hexToG(string $hex): int
{
$hex = ltrim($hex, '#');
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
return hexdec(substr($hex, 2, 2));
}
/**
* 从十六进制颜色获取B值
*/
private function hexToB(string $hex): int
{
$hex = ltrim($hex, '#');
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
return hexdec(substr($hex, 4, 2));
}
/**
* 生成二维码并返回Base64编码
* @param string $content 二维码内容
* @param array $options 配置选项
* @return string Base64编码的图片
*/
public function generateBase64(string $content, array $options = []): string
{
try {
$defaultOptions = [
'size' => 300,
'margin' => 10,
'foreground_color' => '#000000',
'background_color' => '#FFFFFF',
'error_correction' => 'high',
];
$config = array_merge($defaultOptions, $options);
$imageData = $this->generateQrCode($content, $config);
return 'data:image/png;base64,' . base64_encode($imageData);
} catch (Exception $e) {
throw new Exception('生成Base64二维码失败: ' . $e->getMessage());
}
}
/**
* 直接生成并输出二维码图片
* @param string $content 二维码内容
* @param array $options 配置选项
* @return \think\Response
*/
public function generateResponse(string $content, array $options = [])
{
try {
$defaultOptions = [
'size' => 300,
'margin' => 10,
'foreground_color' => '#000000',
'background_color' => '#FFFFFF',
'error_correction' => 'high',
];
$config = array_merge($defaultOptions, $options);
$imageData = $this->generateQrCode($content, $config);
return response($imageData)
->contentType('image/png')
->header('Cache-Control', 'public, max-age=3600');
} catch (Exception $e) {
return response('二维码生成失败: ' . $e->getMessage())
->contentType('text/plain')
->code(500);
}
}
}