初始化提交
This commit is contained in:
113
NBATransfer-backend/routes/admin.py
Normal file
113
NBATransfer-backend/routes/admin.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""管理员相关路由"""
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models import User
|
||||
from services.admin_service import AdminService
|
||||
|
||||
admin_bp = Blueprint('admin', __name__)
|
||||
|
||||
def admin_required():
|
||||
"""管理员权限验证装饰器"""
|
||||
current_user_id = get_jwt_identity()
|
||||
user = User.query.get(current_user_id)
|
||||
|
||||
if not user or not user.is_admin:
|
||||
return None
|
||||
|
||||
return user
|
||||
|
||||
@admin_bp.route('/users', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_users():
|
||||
"""获取用户列表"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
# 分页参数
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
search = request.args.get('search', '')
|
||||
|
||||
result, status_code = AdminService.get_users(page, per_page, search)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/users/<int:user_id>', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_user_detail(user_id):
|
||||
"""获取用户详情"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
result, status_code = AdminService.get_user_detail(user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/users/<int:user_id>/toggle-status', methods=['POST'])
|
||||
@jwt_required()
|
||||
def toggle_user_status(user_id):
|
||||
"""启用/禁用用户"""
|
||||
admin = admin_required()
|
||||
if not admin:
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
result, status_code = AdminService.toggle_user_status(admin.id, user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/users/<int:user_id>/adjust-balance', methods=['POST'])
|
||||
@jwt_required()
|
||||
def adjust_balance(user_id):
|
||||
"""调整用户余额"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
data = request.get_json()
|
||||
result, status_code = AdminService.adjust_balance(user_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/orders', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_all_orders():
|
||||
"""获取所有订单"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
status = request.args.get('status')
|
||||
|
||||
result, status_code = AdminService.get_all_orders(page, per_page, status)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/api-calls', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_all_api_calls():
|
||||
"""获取所有API调用记录"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
status = request.args.get('status')
|
||||
|
||||
result, status_code = AdminService.get_all_api_calls(page, per_page, status)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/stats/overview', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_overview_stats():
|
||||
"""获取总览统计"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
result, status_code = AdminService.get_overview_stats()
|
||||
return jsonify(result), status_code
|
||||
|
||||
@admin_bp.route('/stats/chart', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_chart_data():
|
||||
"""获取图表数据(最近7天)"""
|
||||
if not admin_required():
|
||||
return jsonify({'error': '需要管理员权限'}), 403
|
||||
|
||||
days = request.args.get('days', 7, type=int)
|
||||
result, status_code = AdminService.get_chart_data(days)
|
||||
return jsonify(result), status_code
|
||||
45
NBATransfer-backend/routes/api_service.py
Normal file
45
NBATransfer-backend/routes/api_service.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""API服务相关路由 - 支持 Nano Banana API 代理"""
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from services.api_proxy_service import ApiProxyService
|
||||
|
||||
api_bp = Blueprint('api_service', __name__)
|
||||
|
||||
@api_bp.route('/text-to-image', methods=['POST'])
|
||||
@jwt_required()
|
||||
def text_to_image():
|
||||
"""
|
||||
通用 API 代理 (支持文生图和对话)
|
||||
"""
|
||||
current_user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
|
||||
result, status_code = ApiProxyService.handle_api_request(current_user_id, data)
|
||||
|
||||
if status_code == 200 and data.get('stream', False):
|
||||
return current_app.response_class(result, mimetype='text/event-stream')
|
||||
|
||||
return jsonify(result), status_code
|
||||
|
||||
@api_bp.route('/models', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_models():
|
||||
"""
|
||||
获取可用的模型列表
|
||||
"""
|
||||
result, status_code = ApiProxyService.get_models()
|
||||
return jsonify(result), status_code
|
||||
|
||||
@api_bp.route('/pricing', methods=['GET'])
|
||||
def get_pricing():
|
||||
"""获取价格信息(公开接口)"""
|
||||
result, status_code = ApiProxyService.get_pricing()
|
||||
return jsonify(result), status_code
|
||||
|
||||
@api_bp.route('/call/<int:call_id>', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_api_call(call_id):
|
||||
"""获取API调用详情"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = ApiProxyService.get_api_call(current_user_id, call_id)
|
||||
return jsonify(result), status_code
|
||||
56
NBATransfer-backend/routes/apikey.py
Normal file
56
NBATransfer-backend/routes/apikey.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""用户 API Key 管理相关路由"""
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from services.apikey_service import APIKeyService
|
||||
|
||||
apikey_bp = Blueprint('apikey', __name__)
|
||||
|
||||
@apikey_bp.route('/keys', methods=['GET'])
|
||||
@jwt_required()
|
||||
def list_api_keys():
|
||||
"""获取用户的所有 API Key"""
|
||||
user_id = get_jwt_identity()
|
||||
result, status_code = APIKeyService.list_api_keys(user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@apikey_bp.route('/keys', methods=['POST'])
|
||||
@jwt_required()
|
||||
def create_api_key():
|
||||
"""创建新的 API Key"""
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
result, status_code = APIKeyService.create_api_key(user_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@apikey_bp.route('/keys/<int:key_id>', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_api_key(key_id):
|
||||
"""获取单个 API Key 详情"""
|
||||
user_id = get_jwt_identity()
|
||||
result, status_code = APIKeyService.get_api_key(user_id, key_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@apikey_bp.route('/keys/<int:key_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
def update_api_key(key_id):
|
||||
"""更新 API Key 名称或状态"""
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
result, status_code = APIKeyService.update_api_key(user_id, key_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@apikey_bp.route('/keys/<int:key_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
def delete_api_key(key_id):
|
||||
"""删除 API Key"""
|
||||
user_id = get_jwt_identity()
|
||||
result, status_code = APIKeyService.delete_api_key(user_id, key_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@apikey_bp.route('/keys/<int:key_id>/regenerate', methods=['POST'])
|
||||
@jwt_required()
|
||||
def regenerate_api_key(key_id):
|
||||
"""重置/轮换 API Key"""
|
||||
user_id = get_jwt_identity()
|
||||
result, status_code = APIKeyService.regenerate_api_key(user_id, key_id)
|
||||
return jsonify(result), status_code
|
||||
70
NBATransfer-backend/routes/auth.py
Normal file
70
NBATransfer-backend/routes/auth.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""认证相关路由"""
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity, create_access_token
|
||||
from services.auth_service import AuthService
|
||||
from services.user_service import UserService
|
||||
|
||||
auth_bp = Blueprint('auth', __name__)
|
||||
|
||||
@auth_bp.route('/send-verification-code', methods=['POST'])
|
||||
def send_verification_code():
|
||||
"""发送验证码"""
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.send_verification_code(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/register', methods=['POST'])
|
||||
def register():
|
||||
"""用户注册 - 需要验证码"""
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.register(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/login', methods=['POST'])
|
||||
def login():
|
||||
"""用户登录"""
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.login(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/me', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_current_user():
|
||||
"""获取当前用户信息"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = UserService.get_profile(current_user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/change-password', methods=['POST'])
|
||||
@jwt_required()
|
||||
def change_password():
|
||||
"""修改密码"""
|
||||
current_user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.change_password(current_user_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/reset-password', methods=['POST'])
|
||||
def reset_password():
|
||||
"""重置密码 - 需要验证码"""
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.reset_password(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/verify-code', methods=['POST'])
|
||||
def verify_code():
|
||||
"""验证验证码是否有效(不标记为已使用)"""
|
||||
data = request.get_json()
|
||||
result, status_code = AuthService.verify_code(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@auth_bp.route('/refresh', methods=['POST'])
|
||||
@jwt_required(refresh=True)
|
||||
def refresh():
|
||||
"""刷新访问令牌"""
|
||||
current_user_id = get_jwt_identity()
|
||||
access_token = create_access_token(identity=current_user_id)
|
||||
|
||||
return jsonify({
|
||||
'access_token': access_token
|
||||
}), 200
|
||||
57
NBATransfer-backend/routes/order.py
Normal file
57
NBATransfer-backend/routes/order.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""订单相关路由"""
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from services.order_service import OrderService
|
||||
|
||||
order_bp = Blueprint('order', __name__)
|
||||
|
||||
@order_bp.route('/create', methods=['POST'])
|
||||
@jwt_required()
|
||||
def create_order():
|
||||
"""创建充值订单"""
|
||||
current_user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
result, status_code = OrderService.create_order(current_user_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@order_bp.route('/list', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_orders():
|
||||
"""获取订单列表"""
|
||||
current_user_id = get_jwt_identity()
|
||||
|
||||
# 分页参数
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
status = request.args.get('status')
|
||||
|
||||
result, status_code = OrderService.get_orders(current_user_id, page, per_page, status)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@order_bp.route('/<int:order_id>', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_order(order_id):
|
||||
"""获取订单详情"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = OrderService.get_order(current_user_id, order_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@order_bp.route('/callback/alipay', methods=['POST'])
|
||||
def alipay_callback():
|
||||
"""支付宝支付回调(预留接口)"""
|
||||
data = request.get_json() or request.form.to_dict()
|
||||
result, status_code = OrderService.alipay_callback(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@order_bp.route('/callback/wechat', methods=['POST'])
|
||||
def wechat_callback():
|
||||
"""微信支付回调(预留接口)"""
|
||||
data = request.get_json() or request.form.to_dict()
|
||||
result, status_code = OrderService.wechat_callback(data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
@order_bp.route('/notify/<order_no>', methods=['POST'])
|
||||
def payment_notify(order_no):
|
||||
"""模拟支付通知(仅用于测试)"""
|
||||
result, status_code = OrderService.payment_notify(order_no)
|
||||
return jsonify(result), status_code
|
||||
65
NBATransfer-backend/routes/user.py
Normal file
65
NBATransfer-backend/routes/user.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""用户相关路由"""
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from services.user_service import UserService
|
||||
|
||||
user_bp = Blueprint('user', __name__)
|
||||
|
||||
|
||||
@user_bp.route('/profile', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_profile():
|
||||
"""获取用户资料"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = UserService.get_profile(current_user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
|
||||
@user_bp.route('/profile', methods=['PUT'])
|
||||
@jwt_required()
|
||||
def update_profile():
|
||||
"""更新用户资料"""
|
||||
current_user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
result, status_code = UserService.update_profile(current_user_id, data)
|
||||
return jsonify(result), status_code
|
||||
|
||||
|
||||
@user_bp.route('/balance', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_balance():
|
||||
"""获取账户余额"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = UserService.get_balance(current_user_id)
|
||||
return jsonify(result), status_code
|
||||
|
||||
|
||||
@user_bp.route('/transactions', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_transactions():
|
||||
"""获取交易记录"""
|
||||
current_user_id = get_jwt_identity()
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
result, status_code = UserService.get_transactions(current_user_id, page, per_page)
|
||||
return jsonify(result), status_code
|
||||
|
||||
|
||||
@user_bp.route('/api-calls', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_api_calls():
|
||||
"""获取API调用记录"""
|
||||
current_user_id = get_jwt_identity()
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
result, status_code = UserService.get_api_calls(current_user_id, page, per_page)
|
||||
return jsonify(result), status_code
|
||||
|
||||
|
||||
@user_bp.route('/stats', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_stats():
|
||||
"""获取用户统计信息"""
|
||||
current_user_id = get_jwt_identity()
|
||||
result, status_code = UserService.get_stats(current_user_id)
|
||||
return jsonify(result), status_code
|
||||
36
NBATransfer-backend/routes/v1_api.py
Normal file
36
NBATransfer-backend/routes/v1_api.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""对外公开的 API 路由 (v1) - 支持 API Key 认证"""
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from services.apikey_service import APIKeyService
|
||||
from services.v1_service import V1Service
|
||||
|
||||
v1_bp = Blueprint('v1_api', __name__)
|
||||
|
||||
@v1_bp.route('/chat/completions', methods=['POST'])
|
||||
def chat_completions():
|
||||
"""
|
||||
OpenAI 兼容的 Chat Completions 接口
|
||||
支持多模型,通过 modelapiservice 分发
|
||||
"""
|
||||
# 1. 认证
|
||||
auth_header = request.headers.get('Authorization')
|
||||
user, error = APIKeyService.authenticate_api_key(auth_header)
|
||||
|
||||
if error:
|
||||
return jsonify({'error': {'message': error, 'type': 'auth_error', 'code': 401}}), 401
|
||||
|
||||
# 2. 获取请求数据
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': {'message': '无效的 JSON 请求体', 'type': 'invalid_request_error', 'code': 400}}), 400
|
||||
|
||||
result, status_code = V1Service.chat_completions(user, data)
|
||||
|
||||
if status_code == 200 and data.get('stream', False):
|
||||
return current_app.response_class(result, mimetype='text/event-stream')
|
||||
|
||||
return jsonify(result), status_code
|
||||
|
||||
@v1_bp.route('/text-to-image', methods=['POST'])
|
||||
def text_to_image_alias():
|
||||
"""兼容旧接口路径"""
|
||||
return chat_completions()
|
||||
Reference in New Issue
Block a user