table_game/backend/app/services/user_login_service.py

278 lines
8.8 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.

from fastapi import HTTPException
from ..db import get_connection
import hashlib
import random
from ..utils.sms_sender import send_sms # 需要实现阿里云短信发送
import redis
from configparser import ConfigParser
import os
import requests
redis_client = redis.Redis(host='localhost', port=6379, db=0)
from ..schemas.user_auth import (
UserLoginRequest,
CodeLoginRequest,
ResetPasswordRequest,
SendCodeRequest,
CheckUserExistenceRequest
)
from ..utils import get_accesstoken
async def authenticate_user(phone_number: str, password: str):
"""手机号密码登录验证"""
conn = get_connection()
try:
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM users WHERE phone_number = %s", (phone_number,))
user = cursor.fetchone()
if not user:
raise HTTPException(404, "用户不存在")
if user["password"] != hashlib.md5(password.encode()).hexdigest():
raise HTTPException(401, "密码错误")
return user
finally:
cursor.close()
conn.close()
async def send_verification_code(phone_number: str):
"""发送4位验证码"""
code = f"{random.randint(0,9999):04}"
print(code)
# 存储验证码到Redis需要配置Redis连接
redis_client.setex(f"sms_code:{phone_number}", 300, code)
# 调用阿里云短信接口
await send_sms(
phone_number=phone_number,
template_code="SMS_480005109", # 实际模板ID
template_param={"code": code}
)
return {"message": "验证码已发送"}
async def verify_code_login(phone_number: str, code: str):
"""验证码登录"""
stored_code = redis_client.get(f"sms_code:{phone_number}")
if stored_code:
stored_code = stored_code.decode('utf-8')
if not stored_code or stored_code != code:
raise HTTPException(400, "验证码错误")
conn = get_connection()
try:
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM users WHERE phone_number = %s", (phone_number,))
user = cursor.fetchone()
if not user:
raise HTTPException(404, "用户不存在")
return user
finally:
cursor.close()
conn.close()
async def reset_password(request: ResetPasswordRequest):
"""重置密码"""
# 先验证验证码
await verify_code_login(request.phone_number, request.code)
# 更新密码
hashed_pwd = hashlib.md5(request.new_password.encode()).hexdigest()
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute("UPDATE users SET password = %s WHERE phone_number = %s",
(hashed_pwd, request.phone_number))
conn.commit()
return {"message": "密码重置成功"}
finally:
cursor.close()
conn.close()
async def register_user(phone_number: str, code: str, username: str, password: str):
"""注册新用户"""
# 先验证验证码
stored_code = redis_client.get(f"sms_code:{phone_number}")
if not stored_code or stored_code.decode() != code:
raise HTTPException(400, "验证码错误")
# 检查手机号是否已注册
conn = get_connection()
try:
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM users WHERE phone_number = %s", (phone_number,))
user = cursor.fetchone()
if user:
raise HTTPException(400, "该手机号已注册")
# 检查用户名是否已存在
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
user = cursor.fetchone()
if user:
raise HTTPException(400, "该用户名已被使用")
# 注册新用户
hashed_pwd = hashlib.md5(password.encode()).hexdigest()
cursor.execute("INSERT INTO users (phone_number, username, password) VALUES (%s, %s, %s)",
(phone_number, username, hashed_pwd))
conn.commit()
return {"message": "注册成功"}
finally:
cursor.close()
conn.close()
async def check_user_existence(phone_number: str):
"""检查用户是否存在"""
conn = get_connection()
try:
cursor = conn.cursor()
cursor.execute(
"SELECT user_id FROM users WHERE phone_number = %s",
(phone_number,)
)
exists = cursor.fetchone() is not None
return {"exists": exists}
except Exception as e:
raise HTTPException(500, f"查询失败: {str(e)}")
finally:
cursor.close()
conn.close()
async def check_wx_bind_status(token: str):
"""检查微信绑定状态"""
from ..utils.jwt_handler import verify_token
try:
# 验证JWT令牌
payload = verify_token(token)
phone_number = payload["sub"]
# 获取数据库连接
connection = get_connection()
cursor = connection.cursor(dictionary=True)
# 查询微信OpenID
cursor.execute(
"SELECT wx_openid FROM users WHERE phone_number = %s",
(phone_number,)
)
user = cursor.fetchone()
if not user:
raise HTTPException(404, "用户不存在")
return {"is_binded": user['wx_openid'] is not None}
except ValueError as e:
raise HTTPException(401, str(e))
except Exception as e:
raise HTTPException(500, str(e))
finally:
cursor.close()
connection.close()
async def bind_wechat_openid(token: str, code: str):
"""通过微信code绑定openid"""
from ..utils.jwt_handler import verify_token
import requests
try:
# 验证JWT令牌
payload = verify_token(token)
phone_number = payload["sub"]
config = ConfigParser()
config.read(os.path.join(os.path.dirname(__file__), '../../config.conf'))
# 从微信获取openid需要配置微信应用信息
wechat_url = "https://api.weixin.qq.com/sns/jscode2session"
params = {
"appid": config.get("wechat", "appid"),
"secret": config.get("wechat", "secret"),
"js_code": code,
"grant_type": "authorization_code"
}
response = requests.get(wechat_url, params=params)
wechat_data = response.json()
if 'openid' not in wechat_data:
raise HTTPException(400, "微信授权失败")
openid = wechat_data['openid']
# 存储到数据库
connection = get_connection()
cursor = connection.cursor()
cursor.execute(
"UPDATE users SET wx_openid = %s WHERE phone_number = %s",
(openid, phone_number)
)
connection.commit()
return {"message": "微信绑定成功"}
except ValueError as e:
raise HTTPException(401, str(e))
except requests.RequestException:
raise HTTPException(503, "无法连接微信服务器")
except Exception as e:
connection.rollback()
raise HTTPException(500, str(e))
finally:
cursor.close()
connection.close()
async def use_wx_phoneNumber(code: str):
"""通过微信code获取用户手机号并检查注册状态"""
try:
access_token = get_accesstoken.get_access_token()
wx_url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
params = {"access_token": access_token}
response = requests.post(wx_url, params=params, json={"code": code})
wx_data = response.json()
if wx_data.get("errcode") != 0:
raise HTTPException(400, f"微信接口错误: {wx_data.get('errmsg')}")
phone_number = wx_data["phone_info"]["phoneNumber"]
conn = get_connection()
cursor = conn.cursor(dictionary=True)
# 检查用户是否存在
cursor.execute("SELECT * FROM users WHERE phone_number = %s", (phone_number,))
user = cursor.fetchone()
if not user:
# 自动注册新用户
cursor.execute(
"INSERT INTO users (phone_number, username, password) VALUES (%s, %s, %s)",
(phone_number, "用户_临时", "no_set")
)
# 获取新用户ID并更新用户名
user_id = cursor.lastrowid
cursor.execute(
"UPDATE users SET username = %s WHERE user_id = %s",
(f"用户_{user_id}", user_id)
)
conn.commit()
# 重新查询新用户信息
cursor.execute("SELECT * FROM users WHERE user_id = %s", (user_id,))
user = cursor.fetchone()
return {"user": user}
except requests.RequestException:
raise HTTPException(503, "无法连接微信服务器")
except KeyError:
raise HTTPException(400, "无效的微信响应格式")
finally:
# 确保关闭数据库连接
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
conn.close()