合并腾讯 qcloud 相关代码
This commit is contained in:
parent
fd28f612b4
commit
f2891a17c0
@ -1,8 +1,6 @@
|
|||||||
from fastapi import APIRouter, UploadFile, File, Depends
|
from fastapi import APIRouter, UploadFile, File, Depends
|
||||||
from typing import List
|
from typing import List
|
||||||
import uuid
|
from app.core.qcloud import qcloud_manager
|
||||||
from datetime import datetime
|
|
||||||
from app.core.cos import cos_manager
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.models.upload import UploadResponse, MultiUploadResponse
|
from app.models.upload import UploadResponse, MultiUploadResponse
|
||||||
from app.core.response import success_response, error_response, ResponseModel
|
from app.core.response import success_response, error_response, ResponseModel
|
||||||
@ -11,24 +9,6 @@ from app.models.user import UserDB
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
async def upload_to_cos(file: UploadFile) -> str:
|
|
||||||
"""上传文件到腾讯云COS"""
|
|
||||||
# 生成唯一文件名
|
|
||||||
ext = file.filename.split('.')[-1] if '.' in file.filename else ''
|
|
||||||
filename = f"{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4()}.{ext}"
|
|
||||||
|
|
||||||
# 上传文件
|
|
||||||
cos_manager.put_object(
|
|
||||||
Bucket=settings.COS_BUCKET,
|
|
||||||
Body=await file.read(),
|
|
||||||
Key=filename,
|
|
||||||
ContentType=file.content_type,
|
|
||||||
ACL="public-read"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 返回文件URL
|
|
||||||
return f"https://{settings.COS_BASE_URL}/{filename}"
|
|
||||||
|
|
||||||
@router.post("/image", response_model=ResponseModel)
|
@router.post("/image", response_model=ResponseModel)
|
||||||
async def upload_image(
|
async def upload_image(
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
@ -39,7 +19,7 @@ async def upload_image(
|
|||||||
return error_response(code=400, message="只能上传图片文件")
|
return error_response(code=400, message="只能上传图片文件")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = await upload_to_cos(file)
|
url = await qcloud_manager.upload_file(file)
|
||||||
return success_response(data=UploadResponse(url=url))
|
return success_response(data=UploadResponse(url=url))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(code=500, message=f"上传失败: {str(e)}")
|
return error_response(code=500, message=f"上传失败: {str(e)}")
|
||||||
@ -58,7 +38,7 @@ async def upload_images(
|
|||||||
for file in files:
|
for file in files:
|
||||||
if not file.content_type.startswith('image/'):
|
if not file.content_type.startswith('image/'):
|
||||||
return error_response(code=400, message="只能上传图片文件")
|
return error_response(code=400, message="只能上传图片文件")
|
||||||
url = await upload_to_cos(file)
|
url = await qcloud_manager.upload_file(file)
|
||||||
urls.append(url)
|
urls.append(url)
|
||||||
return success_response(data=MultiUploadResponse(urls=urls))
|
return success_response(data=MultiUploadResponse(urls=urls))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -72,7 +52,7 @@ async def get_presigned_url(
|
|||||||
"""获取预签名上传URL"""
|
"""获取预签名上传URL"""
|
||||||
key = f"uploads/{current_user.userid}/{filename}"
|
key = f"uploads/{current_user.userid}/{filename}"
|
||||||
try:
|
try:
|
||||||
url = cos_manager.get_upload_url(key)
|
url = qcloud_manager.get_upload_url(key)
|
||||||
return success_response(data={
|
return success_response(data={
|
||||||
"url": url,
|
"url": url,
|
||||||
"key": key
|
"key": key
|
||||||
|
|||||||
@ -38,32 +38,11 @@ redis_client = redis.Redis(
|
|||||||
async def send_verify_code(request: VerifyCodeRequest):
|
async def send_verify_code(request: VerifyCodeRequest):
|
||||||
"""发送验证码"""
|
"""发送验证码"""
|
||||||
phone = request.phone
|
phone = request.phone
|
||||||
code = ''.join(random.choices(string.digits, k=6))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 实例化认证对象
|
# 发送验证码
|
||||||
cred = credential.Credential(
|
code, request_id = await qcloud_manager.send_sms_code(phone)
|
||||||
settings.TENCENT_SECRET_ID,
|
|
||||||
settings.TENCENT_SECRET_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
# 实例化短信客户端
|
|
||||||
client = sms_client.SmsClient(cred, "ap-guangzhou")
|
|
||||||
|
|
||||||
# 组装请求参数
|
|
||||||
req = models.SendSmsRequest()
|
|
||||||
req.SmsSdkAppId = settings.SMS_SDK_APP_ID
|
|
||||||
req.SignName = settings.SMS_SIGN_NAME
|
|
||||||
req.TemplateId = settings.SMS_TEMPLATE_ID
|
|
||||||
req.TemplateParamSet = [code]
|
|
||||||
req.PhoneNumberSet = [f"+86{phone}"]
|
|
||||||
|
|
||||||
# 发送短信
|
|
||||||
resp = client.SendSms(req)
|
|
||||||
|
|
||||||
if resp.SendStatusSet[0].Code != "Ok":
|
|
||||||
return error_response(message=f"短信发送失败: {resp.SendStatusSet[0].Message}")
|
|
||||||
|
|
||||||
# 存储验证码到 Redis
|
# 存储验证码到 Redis
|
||||||
redis_client.setex(
|
redis_client.setex(
|
||||||
f"verify_code:{phone}",
|
f"verify_code:{phone}",
|
||||||
@ -73,7 +52,7 @@ async def send_verify_code(request: VerifyCodeRequest):
|
|||||||
|
|
||||||
return success_response(message="验证码已发送")
|
return success_response(message="验证码已发送")
|
||||||
|
|
||||||
except TencentCloudSDKException as e:
|
except Exception as e:
|
||||||
return error_response(message=f"发送验证码失败: {str(e)}")
|
return error_response(message=f"发送验证码失败: {str(e)}")
|
||||||
|
|
||||||
@router.post("/login")
|
@router.post("/login")
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
from qcloud_cos import CosConfig, CosS3Client
|
|
||||||
from app.core.config import settings
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
|
|
||||||
# 正常情况日志级别使用INFO,需要定位时可以修改为DEBUG,此时SDK会打印和服务端的通信信息
|
|
||||||
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
|
||||||
|
|
||||||
class COSManager:
|
|
||||||
def __init__(self):
|
|
||||||
config = CosConfig(
|
|
||||||
Region=settings.COS_REGION,
|
|
||||||
SecretId=settings.TENCENT_SECRET_ID,
|
|
||||||
SecretKey=settings.TENCENT_SECRET_KEY
|
|
||||||
)
|
|
||||||
self.client = CosS3Client(config)
|
|
||||||
|
|
||||||
def get_upload_url(self, key: str, expires: int = 3600) -> str:
|
|
||||||
"""
|
|
||||||
获取预签名上传URL
|
|
||||||
|
|
||||||
Args:
|
|
||||||
key: 对象键
|
|
||||||
expires: 签名有效期(秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
预签名URL
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
url = self.client.get_presigned_url(
|
|
||||||
Method='PUT',
|
|
||||||
Bucket=settings.COS_BUCKET,
|
|
||||||
Key=key,
|
|
||||||
Expired=expires
|
|
||||||
)
|
|
||||||
return url
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"获取上传URL失败: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def get_download_url(self, key: str, expires: int = 3600) -> str:
|
|
||||||
"""
|
|
||||||
获取预签名下载URL
|
|
||||||
|
|
||||||
Args:
|
|
||||||
key: 对象键
|
|
||||||
expires: 签名有效期(秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
预签名URL
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
url = self.client.get_presigned_url(
|
|
||||||
Method='GET',
|
|
||||||
Bucket=settings.COS_BUCKET,
|
|
||||||
Key=key,
|
|
||||||
Expired=expires
|
|
||||||
)
|
|
||||||
return url
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"获取下载URL失败: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def put_object(self, **kwargs):
|
|
||||||
"""上传对象的封装方法"""
|
|
||||||
return self.client.put_object(**kwargs)
|
|
||||||
|
|
||||||
# 创建全局 COS 客户端实例
|
|
||||||
cos_manager = COSManager()
|
|
||||||
@ -3,8 +3,15 @@ from tencentcloud.common.profile.client_profile import ClientProfile
|
|||||||
from tencentcloud.common.profile.http_profile import HttpProfile
|
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||||
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||||||
from tencentcloud.faceid.v20180301 import faceid_client, models
|
from tencentcloud.faceid.v20180301 import faceid_client, models
|
||||||
|
from tencentcloud.sms.v20210111 import sms_client, models as sms_models
|
||||||
|
from qcloud_cos import CosConfig, CosS3Client
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
import json
|
import json
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
from fastapi import UploadFile
|
||||||
|
|
||||||
class QCloudManager:
|
class QCloudManager:
|
||||||
"""腾讯云服务管理类"""
|
"""腾讯云服务管理类"""
|
||||||
@ -18,19 +25,88 @@ class QCloudManager:
|
|||||||
|
|
||||||
# 配置 HTTP
|
# 配置 HTTP
|
||||||
self.http_profile = HttpProfile()
|
self.http_profile = HttpProfile()
|
||||||
self.http_profile.endpoint = "faceid.tencentcloudapi.com"
|
|
||||||
|
|
||||||
# 配置 Client
|
# 配置 Client
|
||||||
self.client_profile = ClientProfile()
|
self.client_profile = ClientProfile()
|
||||||
self.client_profile.httpProfile = self.http_profile
|
self.client_profile.httpProfile = self.http_profile
|
||||||
|
|
||||||
# 初始化人脸识别客户端
|
# 初始化客户端
|
||||||
self.faceid_client = faceid_client.FaceidClient(
|
self.sms_client = None
|
||||||
self.cred,
|
self.faceid_client = None
|
||||||
settings.TENCENT_REGION,
|
self.cos_client = None
|
||||||
self.client_profile
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def _init_faceid_client(self):
|
||||||
|
"""初始化人脸识别客户端"""
|
||||||
|
if not self.faceid_client:
|
||||||
|
self.http_profile.endpoint = "faceid.tencentcloudapi.com"
|
||||||
|
self.faceid_client = faceid_client.FaceidClient(
|
||||||
|
self.cred,
|
||||||
|
settings.TENCENT_REGION,
|
||||||
|
self.client_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
def _init_sms_client(self):
|
||||||
|
"""初始化短信客户端"""
|
||||||
|
if not self.sms_client:
|
||||||
|
self.http_profile.endpoint = "sms.tencentcloudapi.com"
|
||||||
|
self.sms_client = sms_client.SmsClient(
|
||||||
|
self.cred,
|
||||||
|
settings.TENCENT_REGION
|
||||||
|
)
|
||||||
|
|
||||||
|
def _init_cos_client(self):
|
||||||
|
"""初始化 COS 客户端"""
|
||||||
|
if not self.cos_client:
|
||||||
|
config = CosConfig(
|
||||||
|
Region=settings.COS_REGION,
|
||||||
|
SecretId=settings.TENCENT_SECRET_ID,
|
||||||
|
SecretKey=settings.TENCENT_SECRET_KEY
|
||||||
|
)
|
||||||
|
self.cos_client = CosS3Client(config)
|
||||||
|
|
||||||
|
def generate_verify_code(self, length: int = 6) -> str:
|
||||||
|
"""生成验证码"""
|
||||||
|
return ''.join(random.choices(string.digits, k=length))
|
||||||
|
|
||||||
|
async def send_sms_code(self, phone: str) -> tuple[str, str]:
|
||||||
|
"""
|
||||||
|
发送短信验证码
|
||||||
|
|
||||||
|
Args:
|
||||||
|
phone: 手机号
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (验证码, 请求ID)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: 发送失败时抛出异常
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._init_sms_client()
|
||||||
|
|
||||||
|
# 生成验证码
|
||||||
|
code = self.generate_verify_code()
|
||||||
|
|
||||||
|
# 构建请求
|
||||||
|
req = sms_models.SendSmsRequest()
|
||||||
|
req.SmsSdkAppId = settings.SMS_SDK_APP_ID
|
||||||
|
req.SignName = settings.SMS_SIGN_NAME
|
||||||
|
req.TemplateId = settings.SMS_TEMPLATE_ID
|
||||||
|
req.TemplateParamSet = [code]
|
||||||
|
req.PhoneNumberSet = [f"+86{phone}"]
|
||||||
|
|
||||||
|
# 发送短信
|
||||||
|
response = self.sms_client.SendSms(req)
|
||||||
|
|
||||||
|
# 检查发送结果
|
||||||
|
if response.SendStatusSet[0].Code != "Ok":
|
||||||
|
raise Exception(response.SendStatusSet[0].Message)
|
||||||
|
|
||||||
|
return code, response.RequestId
|
||||||
|
|
||||||
|
except TencentCloudSDKException as e:
|
||||||
|
raise Exception(f"发送短信失败: {str(e)}")
|
||||||
|
|
||||||
async def verify_id_card(self, id_card: str, name: str) -> dict:
|
async def verify_id_card(self, id_card: str, name: str) -> dict:
|
||||||
"""
|
"""
|
||||||
身份证实名认证
|
身份证实名认证
|
||||||
@ -50,6 +126,8 @@ class QCloudManager:
|
|||||||
TencentCloudSDKException: 调用API失败
|
TencentCloudSDKException: 调用API失败
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
self._init_faceid_client()
|
||||||
|
|
||||||
# 构建请求
|
# 构建请求
|
||||||
req = models.IdCardVerificationRequest()
|
req = models.IdCardVerificationRequest()
|
||||||
params = {
|
params = {
|
||||||
@ -73,5 +151,64 @@ class QCloudManager:
|
|||||||
except TencentCloudSDKException as e:
|
except TencentCloudSDKException as e:
|
||||||
raise Exception(f"身份证实名认证失败: {str(e)}")
|
raise Exception(f"身份证实名认证失败: {str(e)}")
|
||||||
|
|
||||||
|
async def upload_file(self, file: UploadFile, folder: str = None) -> str:
|
||||||
|
"""
|
||||||
|
上传文件到 COS
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: 上传的文件
|
||||||
|
folder: 存储文件夹名称,默认使用日期
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 文件访问URL
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: 上传失败时抛出异常
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._init_cos_client()
|
||||||
|
|
||||||
|
# 生成存储路径
|
||||||
|
folder = folder or datetime.now().strftime('%Y%m%d')
|
||||||
|
ext = file.filename.split('.')[-1] if '.' in file.filename else ''
|
||||||
|
key = f"{folder}/{uuid.uuid4()}.{ext}"
|
||||||
|
|
||||||
|
# 上传文件
|
||||||
|
self.cos_client.put_object(
|
||||||
|
Bucket=settings.COS_BUCKET,
|
||||||
|
Body=await file.read(),
|
||||||
|
Key=key,
|
||||||
|
ContentType=file.content_type,
|
||||||
|
ACL="public-read"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 返回文件URL
|
||||||
|
return f"https://{settings.COS_BASE_URL}/{key}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"文件上传失败: {str(e)}")
|
||||||
|
|
||||||
|
def get_upload_url(self, key: str, expire: int = 300) -> str:
|
||||||
|
"""
|
||||||
|
获取预签名上传URL
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: 文件路径
|
||||||
|
expire: 链接有效期(秒)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 预签名URL
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._init_cos_client()
|
||||||
|
return self.cos_client.get_presigned_url(
|
||||||
|
Method='PUT',
|
||||||
|
Bucket=settings.COS_BUCKET,
|
||||||
|
Key=key,
|
||||||
|
Expired=expire
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"获取上传URL失败: {str(e)}")
|
||||||
|
|
||||||
# 创建全局实例
|
# 创建全局实例
|
||||||
qcloud_manager = QCloudManager()
|
qcloud_manager = QCloudManager()
|
||||||
Loading…
Reference in New Issue
Block a user