2026-04-10 14:29:41 +08:00

160 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.IO;
using QRCoder;
using Furion.DependencyInjection;
using YD_Event.Core.Domain.Certificate.Models;
namespace YD_Event.Core.Domain.Certificate
{
/// <summary>
/// 二维码服务
/// </summary>
public class QRCodeService : ITransient
{
/// <summary>
/// 生成二维码图片
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="size">二维码尺寸(像素)</param>
/// <param name="logoPath">Logo图片路径(可选)</param>
/// <returns>二维码图片流</returns>
public Stream GenerateQRCode(string content, int size = 200, string logoPath = null)
{
using (var qrGenerator = new QRCodeGenerator())
{
var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.M);
// 使用PngByteQRCode来避免Bitmap依赖
var qrCode = new PngByteQRCode(qrCodeData);
var qrCodeBytes = qrCode.GetGraphic(
pixelsPerModule: CalculatePixelsPerModule(size));
var outputStream = new MemoryStream(qrCodeBytes);
outputStream.Position = 0;
return outputStream;
}
}
/// <summary>
/// 生成证书二维码URL
/// </summary>
/// <param name="certificateId">证书ID</param>
/// <param name="baseUrl">基础URL</param>
/// <returns>证书查看URL</returns>
public string GenerateCertificateUrl(long certificateId, string baseUrl)
{
if (string.IsNullOrWhiteSpace(baseUrl))
throw new ArgumentException("Base URL cannot be null or empty", nameof(baseUrl));
var normalizedBaseUrl = baseUrl.TrimEnd('/');
return $"{normalizedBaseUrl}/certificate/view/{certificateId}";
}
/// <summary>
/// 生成证书PDF下载URL
/// </summary>
/// <param name="certificateId">证书ID</param>
/// <param name="pdfUrl">PDF文件URL</param>
/// <returns>PDF下载URL</returns>
public string GeneratePdfUrl(long certificateId, string pdfUrl)
{
if (string.IsNullOrWhiteSpace(pdfUrl))
throw new ArgumentException("PDF URL cannot be null or empty", nameof(pdfUrl));
return pdfUrl;
}
/// <summary>
/// 生成带验证信息的二维码内容
/// </summary>
/// <param name="certificateId">证书ID</param>
/// <param name="verificationCode">验证码</param>
/// <param name="baseUrl">基础URL</param>
/// <returns>验证URL</returns>
public string GenerateVerificationUrl(long certificateId, string verificationCode, string baseUrl)
{
if (string.IsNullOrWhiteSpace(baseUrl))
throw new ArgumentException("Base URL cannot be null or empty", nameof(baseUrl));
if (string.IsNullOrWhiteSpace(verificationCode))
throw new ArgumentException("Verification code cannot be null or empty", nameof(verificationCode));
var normalizedBaseUrl = baseUrl.TrimEnd('/');
return $"{normalizedBaseUrl}/certificate/verify/{certificateId}?code={verificationCode}";
}
/// <summary>
/// 计算每个模块的像素数
/// </summary>
/// <param name="desiredSize">期望的二维码尺寸</param>
/// <returns>每个模块的像素数</returns>
private int CalculatePixelsPerModule(int desiredSize)
{
// 标准二维码是25x25或21x21模块,这里按25计算
const int standardModules = 25;
return Math.Max(1, desiredSize / standardModules);
}
/// <summary>
/// 验证二维码内容
/// </summary>
/// <param name="content">二维码内容</param>
/// <returns>是否有效</returns>
public bool ValidateQRContent(string content)
{
if (string.IsNullOrWhiteSpace(content))
return false;
// 检查内容长度(二维码有容量限制)
if (content.Length > 2953) // 数字模式的最大容量
return false;
// 检查是否为有效URL或文本
return Uri.TryCreate(content, UriKind.Absolute, out _) || !content.Contains(" ");
}
/// <summary>
/// 生成带样式的二维码
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="options">二维码选项</param>
/// <returns>二维码图片流</returns>
public Stream GenerateStyledQRCode(string content, QRCodeOptions options)
{
using (var qrGenerator = new QRCodeGenerator())
{
var eccLevel = GetECCLevel(options.ErrorCorrectionLevel);
var qrCodeData = qrGenerator.CreateQrCode(content, eccLevel);
// 使用PngByteQRCode来避免Bitmap依赖
var qrCode = new PngByteQRCode(qrCodeData);
var pixelsPerModule = options.Size > 0 ? CalculatePixelsPerModule(options.Size) : 10;
var qrCodeBytes = qrCode.GetGraphic(pixelsPerModule: pixelsPerModule);
var outputStream = new MemoryStream(qrCodeBytes);
outputStream.Position = 0;
return outputStream;
}
}
/// <summary>
/// 获取错误纠正级别
/// </summary>
/// <param name="level">级别字符串</param>
/// <returns>ECC级别</returns>
private QRCodeGenerator.ECCLevel GetECCLevel(string level)
{
return level?.ToUpperInvariant() switch
{
"L" => QRCodeGenerator.ECCLevel.L,
"M" => QRCodeGenerator.ECCLevel.M,
"Q" => QRCodeGenerator.ECCLevel.Q,
"H" => QRCodeGenerator.ECCLevel.H,
_ => QRCodeGenerator.ECCLevel.M
};
}
}
}