160 lines
6.1 KiB
C#
Raw Normal View History

2026-03-04 12:13:49 +08:00
using System.IO;
using QRCoder;
using Furion.DependencyInjection;
2026-04-10 14:29:41 +08:00
using YD_Event.Core.Domain.Certificate.Models;
2026-03-04 12:13:49 +08:00
namespace YD_Event.Core.Domain.Certificate
2026-03-04 12:13:49 +08:00
{
/// <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
};
}
}
}