202 lines
5.5 KiB
Python
202 lines
5.5 KiB
Python
"""二维码服务层 - 提供统一的业务逻辑接口"""
|
||
import base64
|
||
from typing import Optional
|
||
|
||
from .QrCode import QRCodeProcessor
|
||
|
||
|
||
class QrCodeService:
|
||
"""二维码服务 - 业务逻辑层"""
|
||
|
||
def __init__(self):
|
||
"""初始化服务"""
|
||
self.processor = QRCodeProcessor()
|
||
|
||
async def generate_qrcode(
|
||
self,
|
||
content: str,
|
||
size: int = 300,
|
||
error_correction: str = "H"
|
||
) -> dict:
|
||
"""
|
||
生成二维码
|
||
|
||
Args:
|
||
content: 二维码内容
|
||
size: 图片大小(像素)
|
||
error_correction: 纠错级别 (L/M/Q/H)
|
||
|
||
Returns:
|
||
{
|
||
"success": bool,
|
||
"image": str, # base64编码的图片
|
||
"content": str,
|
||
"size": int,
|
||
"error_correction": str,
|
||
"error": str # 仅失败时有
|
||
}
|
||
"""
|
||
try:
|
||
# 验证参数
|
||
if not content:
|
||
return {
|
||
"success": False,
|
||
"error": "内容不能为空"
|
||
}
|
||
|
||
if size < 100 or size > 2000:
|
||
return {
|
||
"success": False,
|
||
"error": "尺寸必须在100-2000之间"
|
||
}
|
||
|
||
if error_correction not in ["L", "M", "Q", "H"]:
|
||
return {
|
||
"success": False,
|
||
"error": "纠错级别必须是 L/M/Q/H 之一"
|
||
}
|
||
|
||
# 生成二维码
|
||
img_base64 = self.processor.generate(
|
||
content=content,
|
||
size=size,
|
||
error_correction=error_correction
|
||
)
|
||
|
||
return {
|
||
"success": True,
|
||
"image": img_base64,
|
||
"content": content,
|
||
"size": size,
|
||
"error_correction": error_correction
|
||
}
|
||
|
||
except Exception as e:
|
||
return {
|
||
"success": False,
|
||
"error": f"生成失败: {str(e)}"
|
||
}
|
||
|
||
async def parse_qrcode(
|
||
self,
|
||
image_source: str,
|
||
strategy: str = "auto"
|
||
) -> dict:
|
||
"""
|
||
解析二维码
|
||
|
||
Args:
|
||
image_source: 图片来源(URL/base64/本地路径)
|
||
strategy: 预处理策略 (basic/auto/enhanced/all)
|
||
|
||
Returns:
|
||
{
|
||
"success": bool,
|
||
"content": str or None,
|
||
"strategy_used": str,
|
||
"total_attempts": int,
|
||
"error": str # 仅失败时有
|
||
}
|
||
"""
|
||
try:
|
||
# 验证参数
|
||
if not image_source:
|
||
return {
|
||
"success": False,
|
||
"error": "图片来源不能为空"
|
||
}
|
||
|
||
if strategy not in ["basic", "auto", "enhanced", "all"]:
|
||
return {
|
||
"success": False,
|
||
"error": "策略必须是 basic/auto/enhanced/all 之一"
|
||
}
|
||
|
||
# 解析二维码
|
||
result = await self.processor.parse(image_source, strategy)
|
||
|
||
if result["success"]:
|
||
return result
|
||
else:
|
||
return {
|
||
"success": False,
|
||
"content": None,
|
||
"error": result.get("message", "解析失败"),
|
||
"total_attempts": result.get("total_attempts", 0)
|
||
}
|
||
|
||
except Exception as e:
|
||
return {
|
||
"success": False,
|
||
"content": None,
|
||
"error": f"解析失败: {str(e)}"
|
||
}
|
||
|
||
async def parse_qrcode_from_file(
|
||
self,
|
||
file_content: bytes,
|
||
file_type: str = "png",
|
||
strategy: str = "auto"
|
||
) -> dict:
|
||
"""
|
||
从文件内容解析二维码
|
||
|
||
Args:
|
||
file_content: 文件二进制内容
|
||
file_type: 文件类型 (png/jpg/jpeg等)
|
||
strategy: 预处理策略
|
||
|
||
Returns:
|
||
解析结果(格式同parse_qrcode)
|
||
"""
|
||
try:
|
||
# 转换为base64
|
||
img_base64 = base64.b64encode(file_content).decode()
|
||
image_source = f"data:image/{file_type};base64,{img_base64}"
|
||
|
||
# 调用解析方法
|
||
return await self.parse_qrcode(image_source, strategy)
|
||
|
||
except Exception as e:
|
||
return {
|
||
"success": False,
|
||
"content": None,
|
||
"error": f"文件解析失败: {str(e)}"
|
||
}
|
||
|
||
def validate_qrcode_content(self, content: str, max_length: int = 2953) -> dict:
|
||
"""
|
||
验证二维码内容是否合法
|
||
|
||
Args:
|
||
content: 要验证的内容
|
||
max_length: 最大长度(默认2953字节,version 40 with L级别)
|
||
|
||
Returns:
|
||
{
|
||
"valid": bool,
|
||
"length": int,
|
||
"error": str # 仅无效时有
|
||
}
|
||
"""
|
||
if not content:
|
||
return {
|
||
"valid": False,
|
||
"error": "内容不能为空"
|
||
}
|
||
|
||
content_bytes = content.encode("utf-8")
|
||
length = len(content_bytes)
|
||
|
||
if length > max_length:
|
||
return {
|
||
"valid": False,
|
||
"length": length,
|
||
"error": f"内容过长,当前{length}字节,最大支持{max_length}字节"
|
||
}
|
||
|
||
return {
|
||
"valid": True,
|
||
"length": length
|
||
}
|