主要修改在asr-monitor-test 修改小程序手机号码登录 小程序的TTS生成(查一查、AI) 增加和支付相关的功能
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
from fastapi import WebSocket, APIRouter,WebSocketDisconnect,Request,Body,Query
|
||||
from fastapi import FastAPI, UploadFile, File, Form, Header,Depends
|
||||
from fastapi import FastAPI, UploadFile, File, Form, Header, Depends
|
||||
from fastapi.responses import StreamingResponse,JSONResponse
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
import logging
|
||||
from fastapi import HTTPException
|
||||
from Crypto.Cipher import AES
|
||||
import base64,uuid
|
||||
import base64,uuid,asyncio
|
||||
import requests
|
||||
from datetime import datetime,timedelta
|
||||
from database import *
|
||||
@@ -17,10 +17,10 @@ logger = logging.getLogger("login")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # tokenUrl 对应登录接口路径
|
||||
|
||||
# 需要配置的参数(从环境变量获取)
|
||||
WX_APPID = "wxed388cef83f109a3" # 小程序appid
|
||||
WX_SECRET = "f687afd2c8fae49b4aed2e4a8dd76e6e" # 小程序密钥
|
||||
WX_APPID = "wx446813bfb3a6985a" #"wxed388cef83f109a3" # 小程序appid
|
||||
WX_SECRET = "a7455fca777ad59ce96cc154d62f795f" #"f687afd2c8fae49b4aed2e4a8dd76e6e" # 小程序密钥
|
||||
WX_API_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
JWT_SECRET_KEY="3e5b8d7f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d"
|
||||
JWT_SECRET_KEY = "3e5b8d7f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d"
|
||||
ALGORITHM = "HS256"
|
||||
|
||||
# 伪数据库
|
||||
@@ -45,7 +45,7 @@ def create_jwt(user_id: str) -> str:
|
||||
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=ALGORITHM)
|
||||
|
||||
|
||||
def decrypt_phone(encrypted_data: str, session_key: str, iv: str) -> dict:
|
||||
def decrypt_data(encrypted_data: str, session_key: str, iv: str) -> dict:
|
||||
try:
|
||||
|
||||
# Base64解码
|
||||
@@ -65,15 +65,17 @@ def decrypt_phone(encrypted_data: str, session_key: str, iv: str) -> dict:
|
||||
|
||||
# 解析JSON
|
||||
result = json.loads(decrypted.decode('utf-8'))
|
||||
if 'purePhoneNumber' not in result:
|
||||
raise ValueError("解密数据不包含手机号")
|
||||
|
||||
return result['purePhoneNumber']
|
||||
logging.info(f"解密数据: {result}")
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
# 特定错误类型识别
|
||||
logging.info(f"解密过程失败: {str(e)}")
|
||||
raise ValueError("SESSION_KEY_MISMATCH")
|
||||
except Exception as e:
|
||||
raise ValueError(f"解密过程失败: {str(e)}")
|
||||
except Exception as e:
|
||||
raise ValueError(f"解密失败: {str(e)}")
|
||||
|
||||
logging.info(f"解密过程失败: {str(e)}")
|
||||
if "padding" in str(e).lower():
|
||||
raise ValueError("SESSION_KEY_EXPIRED")
|
||||
raise
|
||||
|
||||
async def get_wx_session(code: str):
|
||||
"""
|
||||
@@ -124,7 +126,6 @@ async def get_wx_session(code: str):
|
||||
status_code=401,
|
||||
detail="微信认证失败:缺少openid"
|
||||
)
|
||||
logging.info(f"wx_data {wx_data}")
|
||||
return wx_data
|
||||
|
||||
|
||||
@@ -140,24 +141,61 @@ async def wechat_login(request: Request):
|
||||
if not all(k in data for k in required_fields):
|
||||
raise HTTPException(400, "Missing required fields")
|
||||
code = data.get('code')
|
||||
# 调用微信接口获取session信息
|
||||
try:
|
||||
wx_data = await get_wx_session(code)
|
||||
logging.info(f"wechat login wx_data={wx_data}")
|
||||
except HTTPException as e:
|
||||
raise e # 直接抛出已处理过的异常
|
||||
encrypted_data = data['encryptedData']
|
||||
iv = data['iv']
|
||||
|
||||
# 伪解密手机号
|
||||
try:
|
||||
#phone_number = "138" + str(hash(data["encryptedData"]))[-8:]
|
||||
phone_number=decrypt_phone(data["encryptedData"],wx_data['session_key'],data["iv"])
|
||||
except:
|
||||
raise HTTPException(400, "Decrypt failed")
|
||||
logging.info(f"wechat login data={data}")
|
||||
# 关键修改:增加重试机制
|
||||
max_retries = 2
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
# 每次尝试都重新获取 session_key
|
||||
# 微信小程序登录 code 具有一次性特征(有效期约5分钟)
|
||||
# 添加冷启动保护延迟
|
||||
await asyncio.sleep(0.5) # 关键延迟
|
||||
|
||||
logging.info(f"decrypt_phone return {phone_number}")
|
||||
wx_data = await get_wx_session(code)
|
||||
session_key = wx_data["session_key"]
|
||||
openid = wx_data["openid"]
|
||||
logging.info(f"get_wx_session return {wx_data}")
|
||||
# 解密数据
|
||||
try:
|
||||
result = decrypt_data(encrypted_data, session_key, iv)
|
||||
except ValueError as e:
|
||||
# 特定错误处理
|
||||
if "SESSION_KEY_" in str(e):
|
||||
raise HTTPException(418, "SESSION_KEY_INVALID")
|
||||
raise
|
||||
|
||||
if 'purePhoneNumber' not in result:
|
||||
logging.warning("解密数据不包含手机号")
|
||||
phone_number = None
|
||||
else:
|
||||
phone_number = result['purePhoneNumber']
|
||||
break # 成功则跳出循环
|
||||
|
||||
except HTTPException as e:
|
||||
if attempt < max_retries - 1:
|
||||
# 特定错误时刷新 code
|
||||
if "invalid session_key" in str(e).lower():
|
||||
logging.warning("Session key 过期,尝试刷新")
|
||||
# 触发前端重新获取 code
|
||||
raise HTTPException(401, "SESSION_KEY_EXPIRED")
|
||||
else:
|
||||
logging.error(f"尝试 {attempt + 1} 失败: {str(e)}")
|
||||
await asyncio.sleep(1) # 短暂等待后重试
|
||||
else:
|
||||
logging.error(f"最终解密失败: {str(e)}")
|
||||
raise HTTPException(400, "Decrypt failed")
|
||||
except Exception as e:
|
||||
logging.error(f"解密异常: {str(e)}")
|
||||
if attempt == max_retries - 1:
|
||||
raise HTTPException(400, "Decrypt failed")
|
||||
|
||||
logging.info(f"decrypt_data return {phone_number}")
|
||||
# ========== 数据库操作开始 ==========
|
||||
# 使用数据库查询替代内存查询
|
||||
db_users = get_users(phone=phone_number)
|
||||
db_users = get_users(openid=openid)
|
||||
user = db_users[0] if db_users else None
|
||||
|
||||
# 用户不存在时创建新用户
|
||||
@@ -184,20 +222,8 @@ async def wechat_login(request: Request):
|
||||
}
|
||||
updated_user = update_user(user["user_id"], update_data)
|
||||
# ========== 数据库操作结束 ==========
|
||||
"""
|
||||
# 查找或创建用户
|
||||
user = next((u for u in fake_db["users"] if u["phone"] == phone_number), None)
|
||||
if not user:
|
||||
user = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"openid": wx_data["openid"],
|
||||
"phone": phone_number,
|
||||
"museums": [1, 2, 3] # 伪权限数据
|
||||
}
|
||||
fake_db["users"].append(user)
|
||||
logging.info(f"login return {user}")
|
||||
"""
|
||||
|
||||
logging.info(f"login return {user}")
|
||||
# 生成token
|
||||
return JSONResponse({
|
||||
"token": create_jwt(user["user_id"]),
|
||||
@@ -302,32 +328,6 @@ async def optional_current_user(token: str = Depends(oauth2_scheme)):
|
||||
except (JWTError, StopIteration):
|
||||
return None
|
||||
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)) :
|
||||
# 身份验证核心逻辑
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="无法验证凭证",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
try:
|
||||
# 1. 解码并验证JWT
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
user_id: str = payload.get("sub")
|
||||
if user_id is None:
|
||||
raise credentials_exception
|
||||
|
||||
# 2. 从数据库获取完整用户信息
|
||||
user = get_user_by_id(user_id)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
|
||||
return user
|
||||
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
@login_router.get("/verify")
|
||||
async def verify_token(user: dict = Depends(optional_current_user)):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user