""" 演示:使用Service层和中间件优化API 这个文件展示了如何使用Service层和中间件来重构和优化API """ from flask import request, jsonify, current_app from app import db from app.api import api_bp from app.services.license_service import LicenseService from app.services.product_service import ProductService from app.middleware.rate_limit import rate_limit, user_key, ip_key from app.utils.file_security import secure_file_upload from .decorators import require_login, require_admin import os # ==================== 优化后的License API ==================== @api_bp.route('/licenses', methods=['GET']) @require_login @rate_limit(limit=100, window=3600, key_func=user_key) # 用户每小时100次请求 def get_licenses_optimized(): """ 获取卡密列表(优化版) 使用Service层解耦业务逻辑 """ try: # 获取查询参数 page = request.args.get('page', 1, type=int) per_page = min(request.args.get('per_page', 20, type=int), 100) keyword = request.args.get('keyword', '').strip() license_type = request.args.get('type', type=int) status = request.args.get('status', type=int) product_id = request.args.get('product_id') # 使用Service层获取数据 licenses, total = LicenseService.get_licenses( page=page, per_page=per_page, keyword=keyword, license_type=license_type, status=status, product_id=product_id ) return jsonify({ 'success': True, 'data': { 'licenses': [license.to_dict() for license in licenses], 'pagination': { 'page': page, 'per_page': per_page, 'total': total, 'pages': (total + per_page - 1) // per_page } } }), 200 except Exception as e: current_app.logger.error(f"获取卡密列表失败: {str(e)}") return jsonify({ 'success': False, 'message': '服务器内部错误,请稍后重试' }), 500 @api_bp.route('/licenses/generate', methods=['POST']) @require_admin @rate_limit(limit=10, window=3600, key_func=user_key) # 超级管理员每小时10次生成请求 def generate_license_optimized(): """ 生成卡密(优化版) 使用Service层处理业务逻辑 """ try: data = request.get_json() if not data: return jsonify({ 'success': False, 'message': '请提供JSON数据' }), 400 # 验证必填参数 required_fields = ['product_id', 'valid_days', 'count'] for field in required_fields: if field not in data: return jsonify({ 'success': False, 'message': f'缺少必填参数: {field}' }), 400 product_id = data['product_id'] valid_days = data['valid_days'] count = data['count'] license_type = data.get('type', 0) max_bind_times = data.get('max_bind_times', 1) remark = data.get('remark', '') # 验证参数 if valid_days <= 0: return jsonify({ 'success': False, 'message': '有效天数必须大于0' }), 400 if count <= 0 or count > 1000: return jsonify({ 'success': False, 'message': '生成数量必须在1-1000之间' }), 400 # 使用Service层创建卡密 success, msg, licenses = LicenseService.batch_create_licenses( product_id=product_id, valid_days=valid_days, count=count, license_type=license_type, max_bind_times=max_bind_times, remark=remark ) if not success: return jsonify({ 'success': False, 'message': msg }), 400 return jsonify({ 'success': True, 'message': msg, 'data': { 'licenses': [license.to_dict() for license in licenses] } }), 201 except Exception as e: db.session.rollback() current_app.logger.error(f"生成卡密失败: {str(e)}") return jsonify({ 'success': False, 'message': f'服务器内部错误: {str(e)}' }), 500 @api_bp.route('/licenses/verify', methods=['GET']) @rate_limit(limit=1000, window=3600, key_func=ip_key) # 客户端每小时1000次验证请求 def verify_license_optimized(): """ 验证卡密(优化版) 使用Service层处理业务逻辑,添加频率限制 """ try: license_key = request.args.get('license_key') machine_code = request.args.get('machine_code') software_version = request.args.get('software_version', '') if not license_key or not machine_code: return jsonify({ 'success': False, 'message': '缺少必填参数: license_key, machine_code' }), 400 # 使用Service层验证卡密 success, msg, license = LicenseService.verify_license( license_key=license_key, machine_code=machine_code, software_version=software_version ) if not success: return jsonify({ 'success': False, 'message': msg }), 400 return jsonify({ 'success': True, 'message': msg, 'data': { 'license': license.to_dict() } }), 200 except Exception as e: current_app.logger.error(f"验证卡密失败: {str(e)}") return jsonify({ 'success': False, 'message': '服务器内部错误,请稍后重试' }), 500 @api_bp.route('/licenses/unbind', methods=['POST']) @require_login @rate_limit(limit=50, window=3600, key_func=user_key) # 用户每小时50次解绑请求 def unbind_license_optimized(): """ 解绑卡密(优化版) 使用Service层处理业务逻辑 """ try: data = request.get_json() if not data: return jsonify({ 'success': False, 'message': '请提供JSON数据' }), 400 license_key = data.get('license_key') if not license_key: return jsonify({ 'success': False, 'message': '缺少必填参数: license_key' }), 400 # 使用Service层解绑卡密 success, msg = LicenseService.unbind_license(license_key) if not success: return jsonify({ 'success': False, 'message': msg }), 400 return jsonify({ 'success': True, 'message': msg }), 200 except Exception as e: db.session.rollback() current_app.logger.error(f"解绑卡密失败: {str(e)}") return jsonify({ 'success': False, 'message': '服务器内部错误,请稍后重试' }), 500 # ==================== 优化后的产品API ==================== @api_bp.route('/products', methods=['GET']) @rate_limit(limit=200, window=3600, key_func=ip_key) # 客户端每小时200次请求 def get_products_optimized(): """ 获取产品列表(优化版) 使用Service层,添加频率限制 """ try: page = request.args.get('page', 1, type=int) per_page = min(request.args.get('per_page', 20, type=int), 100) keyword = request.args.get('keyword', '').strip() product_type = request.args.get('type') is_paid = request.args.get('is_paid', type=int) # 使用Service层获取产品 products, total = ProductService.get_products( page=page, per_page=per_page, keyword=keyword, product_type=product_type, is_paid=is_paid ) # 为每个产品添加最新版本信息 products_with_version = [] for product in products: product_dict = product.to_dict() latest_version = ProductService.get_latest_version(product.product_id) product_dict['latest_version'] = latest_version.version_num if latest_version else None products_with_version.append(product_dict) return jsonify({ 'success': True, 'data': { 'products': products_with_version, 'pagination': { 'page': page, 'per_page': per_page, 'total': total, 'pages': (total + per_page - 1) // per_page } } }), 200 except Exception as e: current_app.logger.error(f"获取产品列表失败: {str(e)}") return jsonify({ 'success': False, 'message': '服务器内部错误,请稍后重试' }), 500 # ==================== 优化后的文件上传API ==================== @api_bp.route('/upload', methods=['POST']) @require_login @rate_limit(limit=20, window=3600, key_func=user_key) # 用户每小时20次上传请求 def upload_file_optimized(): """ 文件上传(优化版) 使用安全的文件上传工具 """ try: if 'file' not in request.files: return jsonify({ 'success': False, 'message': '没有选择文件' }), 400 file = request.files['file'] if file.filename == '': return jsonify({ 'success': False, 'message': '没有选择文件' }), 400 # 获取上传目录 upload_folder = current_app.config.get('UPLOAD_FOLDER', 'static/uploads') # 定义允许的MIME类型 allowed_mimetypes = { 'image/png', 'image/jpeg', 'image/gif', 'image/webp', 'application/pdf', 'text/plain', 'application/zip', 'application/x-zip-compressed' } # 使用安全的文件上传 success, msg, file_path = secure_file_upload(file, upload_folder, allowed_mimetypes) if not success: return jsonify({ 'success': False, 'message': msg }), 400 # 返回相对路径 relative_path = os.path.relpath(file_path, current_app.root_path) return jsonify({ 'success': True, 'message': msg, 'data': { 'file_path': relative_path, 'filename': file.filename, 'size': os.path.getsize(file_path) } }), 201 except Exception as e: current_app.logger.error(f"文件上传失败: {str(e)}") return jsonify({ 'success': False, 'message': f'文件上传失败: {str(e)}' }), 500 # ==================== 使用示例 ==================== """ 总结:优化后的API具有以下优势: 1. **解耦业务逻辑**: - API层只处理HTTP相关逻辑(请求解析、响应格式化) - 业务逻辑转移到Service层 - 模型层只负责数据访问 2. **安全性增强**: - 频率限制防止API滥用 - 参数验证确保数据安全 - 安全文件上传防止恶意文件 3. **可维护性提升**: - 职责分离,每个组件职责单一 - 代码复用,Service层可以被多个API调用 - 易于测试,可以单独测试Service层 4. **性能优化**: - 避免N+1查询问题 - 缓存常用数据 - 数据库索引优化 5. **监控和可观测性**: - 健康检查端点 - 性能指标收集 - 日志记录 """