deliveryman-api/app/core/qcloud.py
2025-04-02 15:11:24 +08:00

338 lines
11 KiB
Python

from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.faceid.v20180301 import faceid_client, models
from tencentcloud.sms.v20210111 import sms_client, models as sms_models
from tencentcloud.faceid.v20180301 import models as faceid_models
from qcloud_cos import CosConfig, CosS3Client
from app.core.config import settings
import json
import random
import string
import uuid
from datetime import datetime
from fastapi import UploadFile
class QCloudManager:
"""腾讯云服务管理类"""
def __init__(self):
"""初始化认证信息"""
self.cred = credential.Credential(
settings.TENCENT_SECRET_ID,
settings.TENCENT_SECRET_KEY
)
# 配置 HTTP
self.http_profile = HttpProfile()
# 配置 Client
self.client_profile = ClientProfile()
self.client_profile.httpProfile = self.http_profile
# 初始化客户端
self.sms_client = None
self.faceid_client = None
self.cos_client = None
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_phone_code(self, length: int = 4) -> 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_phone_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 send_sms_order_complete(self, phone: str) -> tuple[str, str]:
"""
发送订单完成短信
"""
try:
self._init_sms_client()
# 构建请求
req = sms_models.SendSmsRequest()
req.SmsSdkAppId = settings.SMS_SDK_APP_ID
req.SignName = settings.SMS_SIGN_NAME
req.TemplateId = settings.SMS_TEMPLATE_ID_ORDER_COMPLETE
req.PhoneNumberSet = [f"+86{phone}"]
# 发送短信
response = self.sms_client.SendSms(req)
# 检查发送结果
if response.SendStatusSet[0].Code != "Ok":
raise Exception(response.SendStatusSet[0].Message)
return response.RequestId
except TencentCloudSDKException as e:
raise Exception(f"发送订单完成短信失败: {str(e)}")
async def send_sms_code_additional_fee(self, phone: str) -> tuple[str, str]:
"""
发送加价短信验证码
"""
try:
self._init_sms_client()
# 构建请求
req = sms_models.SendSmsRequest()
req.SmsSdkAppId = settings.SMS_SDK_APP_ID
req.SignName = settings.SMS_SIGN_NAME
req.TemplateId = settings.SMS_TEMPLATE_ID_ADDITIONAL_FEE
req.PhoneNumberSet = [f"+86{phone}"]
# 发送短信
response = self.sms_client.SendSms(req)
# 检查发送结果
if response.SendStatusSet[0].Code != "Ok":
raise Exception(response.SendStatusSet[0].Message)
return response.RequestId
except TencentCloudSDKException as e:
raise Exception(f"发送加价短信验证码失败: {str(e)}")
async def verify_id_card(self, id_card: str, name: str) -> dict:
"""
身份证实名认证
Args:
id_card: 身份证号
name: 姓名
Returns:
dict: {
"Result": "0"/"1"/"2", # 0:一致 1/2:不一致
"Description": str, # 结果描述
"RequestId": str # 请求ID
}
Raises:
TencentCloudSDKException: 调用API失败
"""
try:
self._init_faceid_client()
# 构建请求
req = faceid_models.IdCardVerificationRequest()
params = {
"IdCard": id_card,
"Name": name
}
req.from_json_string(json.dumps(params))
# 发送请求
response = self.faceid_client.IdCardVerification(req)
# 解析结果
result = json.loads(response.to_json_string())
return {
"Result": result.get("Result", "0"),
"Description": result.get("Description", "验证失败"),
"RequestId": result.get("RequestId", "")
}
except TencentCloudSDKException as e:
raise Exception(f"身份证实名认证失败: {str(e)}")
async def upload_file_bytes(self, file_bytes: bytes, key: str) -> str:
"""
上传文件字节流到 COS
"""
try:
self._init_cos_client()
# 上传文件
self.cos_client.put_object(
Bucket=settings.COS_BUCKET,
Body=file_bytes,
Key=key,
)
return f"https://{settings.COS_BASE_URL}/{key}"
except Exception as 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')
# 获取文件名
if file.filename:
ext = file.filename.split('.')[-1] if '.' in file.filename else ''
name = file.filename.split('.')[0] if '.' in file.filename else file.filename
key = f"{folder}/{name}_{uuid.uuid4()}.{ext}"
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)}")
async def verify_bank_card(self, name: str, id_card: str, card_number: str) -> dict:
"""
银行卡三要素核验
Args:
name: 姓名
id_card: 身份证号
card_number: 银行卡号
Returns:
dict: {
"Result": str, # "0":认证通过 "1":认证未通过 "2":认证不确定
"Description": str, # 结果描述
"RequestId": str # 请求ID
}
Raises:
Exception: 验证失败时抛出异常
"""
try:
self._init_faceid_client()
# 构建请求
req = faceid_models.BankCardVerificationRequest()
params = {
"Name": name,
"IdCard": id_card,
"BankCard": card_number
}
req.from_json_string(json.dumps(params))
# 发送请求
response = self.faceid_client.BankCardVerification(req)
# 解析结果
result = json.loads(response.to_json_string())
return {
"Result": result.get("Result", "2"),
"Description": result.get("Description", "验证失败"),
"RequestId": result.get("RequestId", "")
}
except TencentCloudSDKException as e:
raise Exception(f"银行卡三要素核验失败: {str(e)}")
# 创建全局实例
qcloud_manager = QCloudManager()