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()