Files
ai_wht_B/ver_2512212119/auth_routes.py

299 lines
12 KiB
Python
Raw Normal View History

2026-01-06 14:18:39 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
认证接口
"""
from flask import Blueprint, request, jsonify
import logging
from datetime import datetime
from auth_utils import AuthUtils
from database_config import get_db_manager, format_datetime_fields
from log_utils import log_create, log_update, log_delete, log_error, log_operation
logger = logging.getLogger('article_server')
# 创建蓝图
auth_bp = Blueprint('auth', __name__, url_prefix='/api/auth')
@auth_bp.route('/login', methods=['POST'])
def login():
"""统一登录接口(支持企业主和员工)"""
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR', '未知'))
logger.info(f"[用户登录] 开始处理登录请求, IP: {client_ip}")
try:
data = request.get_json()
if not data:
logger.warning(f"[用户登录] 请求参数为空, IP: {client_ip}")
return jsonify({
'code': 400,
'message': '请求参数错误',
'data': None
}), 400
# 支持手机号或用户名登录
phone = data.get('phone')
username = data.get('username')
password = data.get('password')
login_account = phone or username
logger.info(f"[用户登录] 收到登录请求, 账号: {login_account}, IP: {client_ip}")
if not login_account or not password:
logger.warning(f"[用户登录] 账号或密码为空, 账号: {login_account}, IP: {client_ip}")
return jsonify({
'code': 400,
'message': '账号和密码不能为空',
'data': None
}), 400
# 第一步:先在 ai_users 表查询用户
# 从ai_authors表获取xhs_account字段以优化性能
logger.info(f"[用户登录] 开始在ai_users表查询用户, 账号: {login_account}")
db_manager = get_db_manager()
# 支持手机号或用户名登录
sql = """
SELECT u.id, u.enterprise_id, u.enterprise_name, u.username, u.phone, u.password,
u.real_name, u.role, u.status, u.is_bound_xhs, a.xhs_account
FROM ai_users u
LEFT JOIN ai_authors a ON u.id = a.created_user_id AND a.status = 'active'
WHERE (u.phone = %s OR u.username = %s) AND u.status = 'active'
"""
result = db_manager.execute_query(sql, (login_account, login_account))
if not result:
logger.warning(f"[用户登录失败] 用户不存在或已禁用: {login_account}, IP: {client_ip}")
return jsonify({
'code': 401,
'message': '账号或密码错误',
'data': None
}), 401
user = result[0]
logger.info(f"[用户登录] 查询到用户信息, 用户名: {user['username']}, 角色: {user['role']}, 企业: {user['enterprise_name']}, 企业ID: {user['enterprise_id']}")
# 验证密码
logger.info(f"[用户登录] 开始验证密码, 账号: {login_account}")
if not AuthUtils.verify_password(password, user['password']):
logger.warning(f"[用户登录失败] 密码错误: {login_account}, 用户名: {user['username']}, IP: {client_ip}")
return jsonify({
'code': 401,
'message': '账号或密码错误',
'data': None
}), 401
logger.info(f"[用户登录] 密码验证成功, 用户名: {user['username']}, 角色: {user['role']}")
# 如果是企业角色,获取企业详细信息
enterprise_info = None
if user['role'] == 'enterprise' and user['enterprise_id']:
logger.info(f"[用户登录] 检测到企业角色, 开始获取企业详细信息, 企业ID: {user['enterprise_id']}")
enterprise_sql = """
SELECT id, enterprise_ID, name, short_name, phone, email, status,
users_total, products_total, articles_total, published_total
FROM ai_enterprises
WHERE id = %s AND status = 'active'
"""
enterprise_result = db_manager.execute_query(enterprise_sql, (user['enterprise_id'],))
if enterprise_result:
enterprise_info = enterprise_result[0]
logger.info(f"[用户登录] 获取企业信息成功, 企业名称: {enterprise_info['name']}, 企业编号: {enterprise_info['enterprise_ID']}")
else:
logger.warning(f"[用户登录] 企业信息不存在或已禁用, 企业ID: {user['enterprise_id']}")
# 生成token
logger.info(f"[用户登录] 开始生成token, 用户ID: {user['id']}, 角色: {user['role']}")
token = AuthUtils.generate_token(
user['id'],
user['phone'] or user['username'],
user['role'],
user['enterprise_id']
)
# 构造返回数据
user_info = {
'id': user['id'],
'name': user['real_name'] or user['username'],
'username': user['username'],
'phone': user['phone'],
'role': user['role'],
'enterpriseId': user['enterprise_id'],
'enterpriseName': user['enterprise_name'],
'isBoundXHS': bool(user.get('is_bound_xhs', 0)),
'xhsAccount': user.get('xhs_account', '')
}
# 如果是企业角色且有企业信息,添加企业详细信息
if enterprise_info:
user_info.update({
'enterpriseCode': enterprise_info['enterprise_ID'],
'enterpriseShortName': enterprise_info['short_name'],
'enterpriseEmail': enterprise_info['email'],
'enterprisePhone': enterprise_info['phone'],
'usersTotal': enterprise_info['users_total'],
'productsTotal': enterprise_info['products_total'],
'articlesTotal': enterprise_info['articles_total'],
'publishedTotal': enterprise_info['published_total']
})
logger.info(f"[用户登录成功] Token生成成功, 用户: {user['username']}, 角色: {user['role']}, 企业: {user['enterprise_name']}, IP: {client_ip}")
# 返回用户信息和token
return jsonify({
'code': 200,
'message': '登录成功',
'data': {
'token': token,
'userInfo': user_info
},
'timestamp': int(datetime.now().timestamp() * 1000)
})
except Exception as e:
logger.error(f"[用户登录处理] 处理请求时发生错误: {str(e)}", exc_info=True)
return jsonify({
'code': 500,
'message': '服务器内部错误',
'data': None
}), 500
@auth_bp.route('/employee/login', methods=['POST'])
def employee_login():
"""员工登录"""
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR', '未知'))
logger.info(f"[员工登录] 开始处理员工登录请求, IP: {client_ip}")
try:
data = request.get_json()
if not data:
logger.warning(f"[员工登录] 请求参数为空, IP: {client_ip}")
return jsonify({
'code': 400,
'message': '请求参数错误',
'data': None
}), 400
phone = data.get('phone')
password = data.get('password')
logger.info(f"[员工登录] 收到登录请求, 手机号: {phone}, IP: {client_ip}")
if not phone or not password:
logger.warning(f"[员工登录] 手机号或密码为空, 手机号: {phone}, IP: {client_ip}")
return jsonify({
'code': 400,
'message': '手机号和密码不能为空',
'data': None
}), 400
# 查询员工用户
# 从ai_authors表获取xhs_account字段以优化性能
logger.info(f"[员工登录] 开始查询员工信息, 手机号: {phone}")
db_manager = get_db_manager()
sql = """
SELECT u.id, u.enterprise_id, u.real_name, u.phone, u.password, u.role, u.status,
u.is_bound_xhs, a.xhs_account,
e.name as enterprise_name
FROM ai_users u
LEFT JOIN ai_enterprises e ON u.enterprise_id = e.id
LEFT JOIN ai_authors a ON u.id = a.created_user_id AND a.status = 'active'
WHERE u.phone = %s
"""
result = db_manager.execute_query(sql, (phone,))
if not result:
logger.warning(f"[员工登录失败] 用户不存在: {phone}, IP: {client_ip}")
return jsonify({
'code': 401,
'message': '手机号或密码错误',
'data': None
}), 401
user = result[0]
logger.info(f"[员工登录] 查询到员工信息, 姓名: {user['real_name']}, 角色: {user['role']}, 企业: {user['enterprise_name']}, 状态: {user['status']}, 绑定小红书: {user.get('is_bound_xhs', 0)}")
# 检查用户状态
if user['status'] != 'active':
logger.warning(f"[员工登录失败] 用户状态异常: {phone}, 状态: {user['status']}, 姓名: {user['real_name']}, IP: {client_ip}")
return jsonify({
'code': 403,
'message': '用户已被禁用',
'data': None
}), 403
# 验证密码
logger.info(f"[员工登录] 开始验证密码, 手机号: {phone}, 姓名: {user['real_name']}")
if not AuthUtils.verify_password(password, user['password']):
logger.warning(f"[员工登录失败] 密码错误: {phone}, 姓名: {user['real_name']}, IP: {client_ip}")
return jsonify({
'code': 401,
'message': '手机号或密码错误',
'data': None
}), 401
# 生成token
logger.info(f"[员工登录] 密码验证成功, 开始生成token, 姓名: {user['real_name']}, 角色: {user['role']}, 企业: {user['enterprise_name']}")
token = AuthUtils.generate_token(user['id'], phone, user['role'], user['enterprise_id'])
logger.info(f"[员工登录成功] Token生成成功, 姓名: {user['real_name']}, 手机号: {phone}, 角色: {user['role']}, 企业: {user['enterprise_name']}, IP: {client_ip}")
# 返回用户信息和token
return jsonify({
'code': 200,
'message': '登录成功',
'data': {
'token': token,
'userInfo': {
'id': user['id'],
'name': user['real_name'],
'phone': user['phone'],
'role': user['role'],
'enterpriseId': user['enterprise_id'],
'enterpriseName': user['enterprise_name'],
'isBoundXHS': bool(user.get('is_bound_xhs', 0)),
'xhsAccount': user.get('xhs_account', '')
}
},
'timestamp': int(datetime.now().timestamp() * 1000)
})
except Exception as e:
logger.error(f"[员工登录处理] 处理请求时发生错误: {str(e)}", exc_info=True)
return jsonify({
'code': 500,
'message': '服务器内部错误',
'data': None
}), 500
@auth_bp.route('/logout', methods=['POST'])
def logout():
"""用户登出"""
try:
# 记录登出日志
auth_header = request.headers.get('Authorization')
if auth_header:
parts = auth_header.split()
if len(parts) == 2:
token = parts[1]
payload = AuthUtils.verify_token(token)
if payload:
logger.info(f"[登出成功] 用户ID: {payload.get('user_id')}")
return jsonify({
'code': 200,
'message': '退出成功',
'data': None,
'timestamp': int(datetime.now().timestamp() * 1000)
})
except Exception as e:
logger.error(f"[登出处理] 处理请求时发生错误: {str(e)}", exc_info=True)
return jsonify({
'code': 500,
'message': '服务器内部错误',
'data': None
}), 500