441 lines
14 KiB
Python
441 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
约定优于配置示例应用
|
|
|
|
展示 MyBoot 框架的约定优于配置特性
|
|
"""
|
|
|
|
import sys
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
from myboot.core.application import Application
|
|
from myboot.core.config import get_config
|
|
from myboot.core.decorators import (
|
|
get, post, put, delete,
|
|
cron, interval, once,
|
|
service, client, middleware,
|
|
rest_controller, component
|
|
)
|
|
from myboot.jobs.scheduled_job import ScheduledJob
|
|
from myboot.core.scheduler import get_scheduler
|
|
|
|
# 添加项目根目录到 Python 路径
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
# 创建应用实例
|
|
app = Application(
|
|
name="约定优于配置示例",
|
|
auto_configuration=True, # 启用自动配置
|
|
auto_discover_package="examples.convention_app"
|
|
)
|
|
|
|
|
|
# ==================== 服务层 ====================
|
|
|
|
@service()
|
|
class UserService:
|
|
"""用户服务 - 自动注册为 'user_service'"""
|
|
|
|
def __init__(self):
|
|
self.users = {}
|
|
print("✅ UserService 已初始化")
|
|
|
|
def get_user(self, user_id: int):
|
|
"""获取用户"""
|
|
return self.users.get(user_id, {"id": user_id, "name": f"用户{user_id}"})
|
|
|
|
def create_user(self, name: str, email: str):
|
|
"""创建用户"""
|
|
user_id = len(self.users) + 1
|
|
user = {"id": user_id, "name": name, "email": email}
|
|
self.users[user_id] = user
|
|
return user
|
|
|
|
def update_user(self, user_id: int, **kwargs):
|
|
"""更新用户"""
|
|
if user_id in self.users:
|
|
self.users[user_id].update(kwargs)
|
|
return self.users[user_id]
|
|
return None
|
|
|
|
def delete_user(self, user_id: int):
|
|
"""删除用户"""
|
|
return self.users.pop(user_id, None)
|
|
|
|
|
|
@service('email_service')
|
|
class EmailService:
|
|
"""邮件服务 - 注册为 'email_service'"""
|
|
|
|
def __init__(self):
|
|
print("✅ EmailService 已初始化")
|
|
|
|
def send_email(self, to: str, subject: str, body: str):
|
|
"""发送邮件"""
|
|
print(f"📧 发送邮件到 {to}: {subject}")
|
|
return {"status": "sent", "to": to, "subject": subject}
|
|
|
|
|
|
# ==================== 客户端层 ====================
|
|
|
|
@client()
|
|
class DatabaseClient:
|
|
"""数据库客户端 - 自动注册为 'database_client'"""
|
|
|
|
def __init__(self):
|
|
print("✅ DatabaseClient 已初始化")
|
|
|
|
def connect(self):
|
|
"""连接数据库"""
|
|
print("🔗 连接数据库")
|
|
return True
|
|
|
|
def query(self, sql: str):
|
|
"""执行查询"""
|
|
print(f"📊 执行查询: {sql}")
|
|
return []
|
|
|
|
|
|
@client('redis_client')
|
|
class RedisClient:
|
|
"""Redis 客户端 - 注册为 'redis_client'"""
|
|
|
|
def __init__(self):
|
|
print("✅ RedisClient 已初始化")
|
|
self.value = None
|
|
|
|
def get(self, key: str):
|
|
"""获取缓存"""
|
|
print(f"📦 获取缓存: {key}")
|
|
return self.value
|
|
|
|
def set(self, key: str, value: str):
|
|
"""设置缓存"""
|
|
print(f"💾 设置缓存: {key} = {value}")
|
|
self.value = value
|
|
|
|
|
|
# ==================== 中间件 ====================
|
|
|
|
@middleware(order=1)
|
|
async def logging_middleware(request, next_handler):
|
|
"""日志中间件 - 自动注册,处理所有请求"""
|
|
print(f"📝 请求日志: {request.method} {request.url}")
|
|
response = await next_handler(request)
|
|
print(f"📝 响应日志: {response.status_code}")
|
|
return response
|
|
|
|
|
|
@middleware(order=2)
|
|
async def timing_middleware(request, next_handler):
|
|
"""计时中间件 - 自动注册,处理所有请求"""
|
|
import time
|
|
start_time = time.time()
|
|
response = await next_handler(request)
|
|
end_time = time.time()
|
|
elapsed = end_time - start_time
|
|
print(f"⏱️ 请求耗时: {elapsed:.3f}s - {request.method} {request.url.path}")
|
|
return response
|
|
|
|
|
|
@middleware(order=3, path_filter='/api/*')
|
|
async def api_middleware(request, next_handler):
|
|
"""API 中间件 - 只处理 /api/* 路径的请求"""
|
|
print(f"🔌 API 请求: {request.method} {request.url.path}")
|
|
# 可以在这里添加 API 特定的逻辑,如认证、限流等
|
|
response = await next_handler(request)
|
|
return response
|
|
|
|
|
|
@middleware(order=4, methods=['POST', 'PUT', 'PATCH'])
|
|
async def modify_middleware(request, next_handler):
|
|
"""修改操作中间件 - 只处理 POST、PUT、PATCH 请求"""
|
|
print(f"✏️ 修改操作: {request.method} {request.url.path}")
|
|
response = await next_handler(request)
|
|
return response
|
|
|
|
|
|
@middleware(
|
|
order=5,
|
|
path_filter=['/users/*', '/api/products/*'],
|
|
condition=lambda req: req.headers.get('user-agent', '').startswith('Mozilla')
|
|
)
|
|
async def browser_only_middleware(request, next_handler):
|
|
"""浏览器专用中间件 - 只处理浏览器请求且匹配特定路径"""
|
|
print(f"🌐 浏览器请求: {request.headers.get('user-agent', 'Unknown')}")
|
|
response = await next_handler(request)
|
|
return response
|
|
|
|
|
|
# ==================== 定时任务组件 ====================
|
|
# 注意:定时任务必须在 @component 装饰的类中定义,支持依赖注入
|
|
|
|
|
|
@component()
|
|
class ScheduledJobs:
|
|
"""定时任务组件 - 使用 @component 装饰器,支持依赖注入"""
|
|
|
|
def __init__(self):
|
|
print("✅ ScheduledJobs 已初始化")
|
|
|
|
@cron('0 */1 * * * *', enabled=True) # 每分钟执行
|
|
def heartbeat(self):
|
|
"""心跳任务"""
|
|
print("💓 心跳检测 - 系统运行正常")
|
|
|
|
@interval(minutes=10, enabled=get_config('jobs.cleanup_task.enabled', True))
|
|
def cleanup_task(self):
|
|
"""清理任务 - 从配置文件读取 enabled 状态"""
|
|
print("🧹 执行清理任务")
|
|
|
|
@once('2025-12-31 23:59:59')
|
|
def new_year_task(self):
|
|
"""新年任务"""
|
|
print("🎉 新年任务执行")
|
|
|
|
|
|
# ==================== REST 控制器 ====================
|
|
# 注意:路由必须在 @rest_controller 装饰的类中定义
|
|
|
|
@rest_controller('/')
|
|
class HomeController:
|
|
"""首页控制器"""
|
|
|
|
@get('/')
|
|
def home(self):
|
|
"""首页 - GET /"""
|
|
return {
|
|
"message": "欢迎使用 MyBoot 约定优于配置示例",
|
|
"features": [
|
|
"自动发现和注册组件",
|
|
"约定优于配置",
|
|
"零配置启动",
|
|
"REST 控制器",
|
|
"依赖注入",
|
|
"定时任务"
|
|
]
|
|
}
|
|
|
|
|
|
@rest_controller('/api/users')
|
|
class UserController:
|
|
"""用户控制器 - 使用依赖注入"""
|
|
|
|
def __init__(self, user_service: UserService, email_service: EmailService):
|
|
self.user_service = user_service
|
|
self.email_service = email_service
|
|
|
|
@get('/')
|
|
def list_users(self):
|
|
"""获取用户列表 - GET /api/users"""
|
|
return {"users": list(self.user_service.users.values())}
|
|
|
|
@get('/{user_id}')
|
|
def get_user(self, user_id: int):
|
|
"""获取单个用户 - GET /api/users/{user_id}"""
|
|
return self.user_service.get_user(user_id)
|
|
|
|
@post('/')
|
|
def create_user(self, name: str, email: str):
|
|
"""创建用户 - POST /api/users"""
|
|
user = self.user_service.create_user(name, email)
|
|
self.email_service.send_email(email, "欢迎注册", f"欢迎 {name} 注册我们的服务!")
|
|
return {"message": "用户创建成功", "user": user}
|
|
|
|
@put('/{user_id}')
|
|
def update_user(self, user_id: int, name: str = None, email: str = None):
|
|
"""更新用户 - PUT /api/users/{user_id}"""
|
|
update_data = {}
|
|
if name:
|
|
update_data['name'] = name
|
|
if email:
|
|
update_data['email'] = email
|
|
|
|
user = self.user_service.update_user(user_id, **update_data)
|
|
if user:
|
|
return {"message": "用户更新成功", "user": user}
|
|
return {"error": "用户不存在"}
|
|
|
|
@delete('/{user_id}')
|
|
def delete_user(self, user_id: int):
|
|
"""删除用户 - DELETE /api/users/{user_id}"""
|
|
user = self.user_service.delete_user(user_id)
|
|
if user:
|
|
return {"message": "用户删除成功", "user": user}
|
|
return {"error": "用户不存在"}
|
|
|
|
|
|
@rest_controller('/api/products')
|
|
class ProductController:
|
|
"""产品控制器 - 使用依赖注入"""
|
|
|
|
def __init__(self, redis_client: RedisClient):
|
|
self.redis_client = redis_client
|
|
self.products = {
|
|
1: {"id": 1, "name": "产品1", "price": 100},
|
|
2: {"id": 2, "name": "产品2", "price": 200}
|
|
}
|
|
|
|
@get('/')
|
|
def list_products(self):
|
|
"""获取产品列表 - GET /api/products"""
|
|
if self.redis_client:
|
|
print(self.redis_client.get('app_status'))
|
|
return {"products": list(self.products.values())}
|
|
|
|
@get('/{product_id}')
|
|
def get_product(self, product_id: int):
|
|
"""获取单个产品 - GET /api/products/{product_id}"""
|
|
return self.products.get(product_id, {"error": "产品不存在"})
|
|
|
|
@post('/')
|
|
def create_product(self, name: str, price: float):
|
|
"""创建产品 - POST /api/products"""
|
|
product_id = max(self.products.keys()) + 1
|
|
product = {"id": product_id, "name": name, "price": price}
|
|
self.products[product_id] = product
|
|
return {"message": "产品创建成功", "product": product}
|
|
|
|
@put('/{product_id}')
|
|
def update_product(self, product_id: int, name: str = None, price: float = None):
|
|
"""更新产品 - PUT /api/products/{product_id}"""
|
|
if product_id not in self.products:
|
|
return {"error": "产品不存在"}
|
|
|
|
if name:
|
|
self.products[product_id]['name'] = name
|
|
if price:
|
|
self.products[product_id]['price'] = price
|
|
|
|
return {"message": "产品更新成功", "product": self.products[product_id]}
|
|
|
|
@delete('/{product_id}')
|
|
def delete_product(self, product_id: int):
|
|
"""删除产品 - DELETE /api/products/{product_id}"""
|
|
if product_id in self.products:
|
|
product = self.products.pop(product_id)
|
|
return {"message": "产品删除成功", "product": product}
|
|
return {"error": "产品不存在"}
|
|
|
|
|
|
def generate_report(report_type: str):
|
|
"""生成报告任务"""
|
|
import time
|
|
print(f"开始生成 {report_type} 报告")
|
|
time.sleep(10) # 模拟报告生成
|
|
return {"type": report_type, "status": "completed"}
|
|
|
|
|
|
@rest_controller('/api/reports')
|
|
class ReportController:
|
|
"""报告控制器"""
|
|
|
|
def __init__(self):
|
|
self.scheduler = get_scheduler()
|
|
|
|
@post('/generate')
|
|
def create_report(self, report_type: str):
|
|
"""创建报告生成任务"""
|
|
# 创建自定义 ScheduledJob
|
|
class ReportJob(ScheduledJob):
|
|
def __init__(self, report_type: str):
|
|
super().__init__(
|
|
name=f"生成{report_type}报告",
|
|
timeout=300 # 5分钟超时
|
|
)
|
|
self.report_type = report_type
|
|
|
|
def run(self, *args, **kwargs):
|
|
return generate_report(self.report_type)
|
|
|
|
# 创建任务实例
|
|
job = ReportJob(report_type)
|
|
|
|
# 添加到调度器并执行
|
|
job_id = self.scheduler.add_scheduled_job(job)
|
|
thread = threading.Thread(target=job.execute)
|
|
thread.daemon = True
|
|
thread.start()
|
|
|
|
return {"message": "报告生成任务已创建", "job_id": job_id}
|
|
|
|
@get('/status/{job_id}')
|
|
def get_status(self, job_id: str):
|
|
"""查询任务状态"""
|
|
job = self.scheduler.get_scheduled_job(job_id)
|
|
if job:
|
|
return job.get_info()
|
|
return {"error": "任务不存在"}
|
|
|
|
|
|
# ==================== 启动钩子 ====================
|
|
@app.add_startup_hook
|
|
def startup_hook():
|
|
"""启动钩子"""
|
|
print("🚀 应用启动钩子执行")
|
|
|
|
from myboot.core.application import get_client
|
|
|
|
# 初始化数据库连接
|
|
db_client = get_client('database_client')
|
|
if db_client:
|
|
db_client.connect()
|
|
|
|
# 初始化 Redis 连接
|
|
redis_client = get_client('redis_client')
|
|
if redis_client:
|
|
redis_client.set('app_status', 'running')
|
|
|
|
|
|
@app.add_shutdown_hook
|
|
def shutdown_hook():
|
|
"""关闭钩子"""
|
|
print("🛑 应用关闭钩子执行")
|
|
|
|
|
|
# ==================== 主程序 ====================
|
|
|
|
if __name__ == "__main__":
|
|
print("=" * 60)
|
|
print("🎯 MyBoot 约定优于配置示例")
|
|
print("=" * 60)
|
|
print()
|
|
print("✨ 特性展示:")
|
|
print(" • 自动发现和注册组件")
|
|
print(" • 约定优于配置")
|
|
print(" • 零配置启动")
|
|
print(" • 自动服务注入")
|
|
print(" • 自动路由注册")
|
|
print(" • 自动任务调度")
|
|
print()
|
|
print("🌐 访问地址:")
|
|
print(" • 应用: http://localhost:8000")
|
|
print(" • API 文档: http://localhost:8000/docs")
|
|
print(" • 健康检查: http://localhost:8000/health")
|
|
print()
|
|
print("📚 API 端点(通过 @rest_controller 定义):")
|
|
print(" • GET / - 首页")
|
|
print(" • GET /api/users - 用户列表")
|
|
print(" • GET /api/users/{id} - 获取用户")
|
|
print(" • POST /api/users - 创建用户")
|
|
print(" • PUT /api/users/{id} - 更新用户")
|
|
print(" • DELETE /api/users/{id} - 删除用户")
|
|
print(" • GET /api/products - 产品列表")
|
|
print(" • GET /api/products/{id} - 获取产品")
|
|
print(" • POST /api/products - 创建产品")
|
|
print(" • PUT /api/products/{id} - 更新产品")
|
|
print(" • DELETE /api/products/{id} - 删除产品")
|
|
print()
|
|
print("⏰ 定时任务(通过 @component 组件定义):")
|
|
print(" • 心跳检测 (每分钟)")
|
|
print(" • 清理任务 (每10分钟)")
|
|
print(" • 新年任务 (2025-12-31 23:59:59)")
|
|
print()
|
|
print("=" * 60)
|
|
|
|
# 启动应用
|
|
app.run(host="0.0.0.0", port=8000, reload=False)
|