Files
workphone-sdk/sdk/app/routers/qrcode.py

281 lines
7.7 KiB
Python
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.

"""
二维码生成路由
生成项目绑定二维码供手机APP扫描绑定
"""
from fastapi import APIRouter, Response
from pydantic import BaseModel
from typing import Optional
import json
import base64
import io
# 尝试导入qrcode库
try:
import qrcode
from qrcode.constants import ERROR_CORRECT_H
QRCODE_AVAILABLE = True
except ImportError:
QRCODE_AVAILABLE = False
router = APIRouter(prefix="/qrcode", tags=["二维码"])
class ProjectBindConfig(BaseModel):
"""项目绑定配置"""
project_id: str
project_name: Optional[str] = ""
server: Optional[str] = "ws://sdk.quwanzhi.com:8899/ws/device"
pwa: Optional[str] = ""
@router.post("/generate")
async def generate_qrcode(config: ProjectBindConfig):
"""
生成项目绑定二维码
请求体:
{
"project_id": "project_001",
"project_name": "测试项目",
"server": "ws://sdk.quwanzhi.com:8899/ws/device",
"pwa": ""
}
返回: Base64编码的PNG图片
"""
if not QRCODE_AVAILABLE:
return {
"success": False,
"message": "服务器未安装qrcode库请运行: pip install qrcode[pil]"
}
qr_data = {
"server": config.server,
"project": config.project_id,
"project_name": config.project_name,
}
if config.pwa:
qr_data["pwa"] = config.pwa
qr_content = json.dumps(qr_data, ensure_ascii=False)
# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(qr_content)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# 转换为Base64
buffer = io.BytesIO()
img.save(buffer, format='PNG')
img_base64 = base64.b64encode(buffer.getvalue()).decode()
return {
"success": True,
"project_id": config.project_id,
"project_name": config.project_name,
"qr_content": qr_content,
"image_base64": img_base64,
"image_data_url": f"data:image/png;base64,{img_base64}"
}
@router.get("/image/{project_id}")
async def get_qrcode_image(
project_id: str,
project_name: str = "",
server: str = "ws://sdk.quwanzhi.com:8899/ws/device",
pwa: str = ""
):
"""
直接获取二维码图片
示例: GET /api/v3/qrcode/image/project_001?project_name=测试项目
返回: PNG图片
"""
if not QRCODE_AVAILABLE:
return Response(
content="QRCode library not installed",
media_type="text/plain",
status_code=500
)
qr_data = {"server": server, "project": project_id, "project_name": project_name}
if pwa:
qr_data["pwa"] = pwa
qr_content = json.dumps(qr_data, ensure_ascii=False)
# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(qr_content)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# 返回PNG图片
buffer = io.BytesIO()
img.save(buffer, format='PNG')
buffer.seek(0)
return Response(
content=buffer.getvalue(),
media_type="image/png"
)
@router.get("/html/{project_id}")
async def get_qrcode_html(
project_id: str,
project_name: str = "",
server: str = "ws://sdk.quwanzhi.com:8899/ws/device",
pwa: str = ""
):
"""
获取带样式的二维码HTML页面
示例: GET /api/v3/qrcode/html/project_001?project_name=测试项目
返回: HTML页面可直接打印或分享
"""
if not QRCODE_AVAILABLE:
return Response(
content="<h1>QRCode library not installed</h1>",
media_type="text/html",
status_code=500
)
qr_data = {"server": server, "project": project_id, "project_name": project_name}
if pwa:
qr_data["pwa"] = pwa
qr_content = json.dumps(qr_data, ensure_ascii=False)
# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(qr_content)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# 转换为Base64
buffer = io.BytesIO()
img.save(buffer, format='PNG')
img_base64 = base64.b64encode(buffer.getvalue()).decode()
# 生成HTML页面
html = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目绑定二维码 - {project_name or project_id}</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}}
.card {{
background: white;
border-radius: 20px;
padding: 40px;
text-align: center;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
max-width: 400px;
}}
.logo {{
font-size: 48px;
margin-bottom: 20px;
}}
h1 {{
color: #333;
font-size: 24px;
margin-bottom: 10px;
}}
.project-name {{
color: #4CAF50;
font-size: 20px;
font-weight: bold;
margin-bottom: 30px;
}}
.qr-container {{
background: #f5f5f5;
padding: 20px;
border-radius: 16px;
margin-bottom: 30px;
}}
.qr-container img {{
max-width: 100%;
height: auto;
}}
.instructions {{
color: #666;
font-size: 14px;
line-height: 1.8;
text-align: left;
}}
.instructions li {{
margin-bottom: 8px;
}}
.project-id {{
background: #e8f5e9;
color: #2e7d32;
padding: 8px 16px;
border-radius: 20px;
display: inline-block;
font-size: 12px;
margin-top: 20px;
}}
@media print {{
body {{ background: white; }}
.card {{ box-shadow: none; }}
}}
</style>
</head>
<body>
<div class="card">
<div class="logo">📱</div>
<h1>工作手机Agent</h1>
<div class="project-name">{project_name or project_id}</div>
<div class="qr-container">
<img src="data:image/png;base64,{img_base64}" alt="绑定二维码">
</div>
<ul class="instructions">
<li>打开手机上的「工作手机Agent」APP</li>
<li>点击「📷 扫码绑定项目」按钮</li>
<li>扫描上方二维码完成绑定</li>
<li>点击「连接服务器」开始使用</li>
</ul>
<div class="project-id">项目ID: {project_id}</div>
</div>
</body>
</html>
"""
return Response(content=html, media_type="text/html")