主要修改在asr-monitor-test 修改小程序手机号码登录 小程序的TTS生成(查一查、AI) 增加和支付相关的功能

This commit is contained in:
qcloud
2025-07-10 22:04:44 +08:00
parent 0665eb2c2d
commit 74899acab9
23 changed files with 4467 additions and 459 deletions

View File

@@ -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)):
"""