myboot


Namemyboot JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
Summary类似 Spring Boot 的 Python 快速开发框架
upload_time2025-11-08 07:27:40
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords api config framework logging scheduler web
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # MyBoot - 类似 Spring Boot 的 Python 快速开发框架

[![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![PyPI Version](https://img.shields.io/pypi/v/myboot.svg)](https://pypi.org/project/myboot/)

MyBoot 是一个功能丰富的 Python Web 框架,提供类似 Spring Boot 的自动配置和快速开发功能。它集成了 Web API、定时任务、日志管理、配置管理等核心功能,让您能够快速构建现代化的 Python 应用程序。

## ✨ 主要特性

- 🚀 **快速启动**: 类似 Spring Boot 的自动配置和快速启动
- 🎯 **约定优于配置**: 遵循约定,减少配置工作,自动发现和注册组件
- 🌐 **Web API**: 基于 FastAPI 的高性能 Web API 开发
- ⚡ **高性能服务器**: 默认使用 Hypercorn 服务器,支持 HTTP/2 和多进程
- ⏰ **定时任务**: 强大的任务调度系统,支持 Cron 表达式和间隔任务
- 📝 **日志管理**: 基于 loguru 的强大日志系统,支持结构化日志和第三方库日志控制
- ⚙️ **配置管理**: 基于 Dynaconf 的强大配置系统,支持 YAML 配置、环境变量覆盖和远程配置
- 🔧 **中间件支持**: 丰富的中间件生态,包括 CORS、限流、安全等
- 📊 **健康检查**: 内置健康检查、就绪检查和存活检查
- 🎯 **依赖注入**: 简单的依赖注入和组件管理
- 🔄 **优雅关闭**: 支持优雅关闭和资源清理
- 📚 **自动文档**: 自动生成 API 文档和交互式界面

## 🚀 快速开始

### 安装

```bash
pip install myboot
```

### 命令行工具

MyBoot 提供了便捷的命令行工具用于初始化项目:

```bash
# 显示帮助信息
myboot --help

# 初始化新项目(交互式)
myboot init

# 使用指定模板初始化项目
myboot init --name my-app --template basic    # 基础模板
myboot init --name my-app --template api      # API 项目模板
myboot init --name my-app --template full     # 完整项目模板

# 显示框架信息
myboot info
```

### 创建应用

使用 `myboot init` 初始化项目后,在 `main.py` 中创建应用:

```python
"""main.py - 应用入口文件"""
from myboot.core.application import create_app

# 创建应用实例
app = create_app(name="我的应用")

# 运行应用
if __name__ == "__main__":
    app.run()
```

在 `app/api/` 目录中定义路由:

```python
"""app/api/routes.py"""
from myboot.core.decorators import get, post
from myboot.core.application import get_service

@get("/")
def hello():
    """Hello World 接口"""
    return {"message": "Hello, MyBoot!", "status": "success"}

@get("/users/{user_id}")
def get_user(user_id: int):
    """获取用户"""
    user_service = get_service('user_service')
    if user_service:
        return user_service.get_user(user_id)
    return {"user_id": user_id, "message": "用户服务未找到"}
```

### 运行应用

应用入口文件位于项目根目录的 `main.py`:

```bash
# 直接运行
python main.py

# 启用自动重载(开发环境)
python main.py --reload

# 指定端口和主机
python main.py --host 0.0.0.0 --port 8080
```

访问 http://localhost:8000 查看您的应用!

## 🎯 约定优于配置

MyBoot 框架的核心设计理念是"约定优于配置",让您能够快速开发而无需复杂的配置。

### 自动发现和注册

```python
from myboot.core.decorators import service, get, cron
from myboot.core.application import get_service

@service()
class UserService:
    """用户服务 - 自动注册为 'user_service'"""
    def get_user(self, user_id):
        return {"id": user_id, "name": f"用户{user_id}"}

@get('/users/{user_id}')
def get_user(user_id: int):
    """获取用户 - 自动注册路由"""
    # 使用全局函数获取服务(推荐方式)
    user_service = get_service('user_service')
    return user_service.get_user(user_id)

@cron('0 */5 * * * *')
def cleanup_task():
    """清理任务 - 自动注册定时任务"""
    print("执行清理任务")
```

### 零配置启动

```python
from myboot.core.application import Application

# 创建应用,自动发现和配置所有组件
app = Application(
    name="我的应用",
    auto_configuration=True,  # 启用自动配置
    auto_discover_package="app"  # 自动发现 app 包
)

# 直接运行,无需手动注册
app.run()
```

### 依赖注入和服务管理

MyBoot 提供了基于 `dependency_injector` 的强大依赖注入机制,支持自动依赖解析和注入,让您可以轻松管理服务之间的依赖关系。

#### 自动依赖注入

框架会自动检测服务的依赖关系并自动注入,无需手动获取:

```python
from myboot.core.decorators import service

@service()
class UserService:
    def __init__(self):
        self.users = {}

@service()
class EmailService:
    def send_email(self, to: str, subject: str):
        print(f"发送邮件到 {to}: {subject}")

@service()
class OrderService:
    # 自动注入 UserService 和 EmailService
    def __init__(self, user_service: UserService, email_service: EmailService):
        self.user_service = user_service
        self.email_service = email_service

    def create_order(self, user_id: int):
        user = self.user_service.get_user(user_id)
        self.email_service.send_email(user['email'], "订单创建", "您的订单已创建")
```

**特性:**

- ✅ 自动检测依赖关系
- ✅ 自动处理依赖顺序
- ✅ 支持多级依赖
- ✅ 支持可选依赖(`Optional[Type]`)
- ✅ 自动检测循环依赖
- ✅ 向后兼容,现有代码无需修改

**更多信息:** 查看 [依赖注入使用指南](docs/dependency-injection.md)

#### 获取服务 (get_service)

服务是通过 `@service()` 装饰器自动注册的,可以通过以下两种方式获取:

**方式一:通过全局函数(推荐)**

```python
from myboot.core.application import get_service

@get('/users/{user_id}')
def get_user(user_id: int):
    """获取用户"""
    # 使用全局函数获取服务(最简单的方式)
    user_service = get_service('user_service')
    return user_service.get_user(user_id)
```

**方式二:通过应用实例**

```python
from myboot.core.application import get_app

@get('/users/{user_id}')
def get_user(user_id: int):
    """获取用户"""
    # 通过应用实例获取服务
    app = get_app()
    user_service = app.get_service('user_service')
    return user_service.get_user(user_id)
```

#### 获取客户端 (get_client)

客户端是通过 `@client()` 装饰器自动注册的,可以通过以下两种方式获取:

**方式一:通过全局函数(推荐)**

```python
from myboot.core.application import get_client

@get('/api/products')
def get_products():
    """获取产品列表"""
    # 使用全局函数获取客户端
    redis_client = get_client('redis_client')
    if redis_client:
        cache_data = redis_client.get('products')
    return {"products": []}
```

**方式二:通过应用实例**

```python
from myboot.core.application import get_app

@get('/api/products')
def get_products():
    """获取产品列表"""
    # 通过应用实例获取客户端
    app = get_app()
    redis_client = app.get_client('redis_client')
    if redis_client:
        cache_data = redis_client.get('products')
    return {"products": []}
```

#### 完整示例

```python
from myboot.core.decorators import service, client, get, post
from myboot.core.application import get_service, get_client

# 定义服务
@service()
class UserService:
    """用户服务 - 自动注册为 'user_service'"""
    def get_user(self, user_id: int):
        return {"id": user_id, "name": f"用户{user_id}"}

# 定义客户端
@client('redis_client')
class RedisClient:
    """Redis 客户端 - 注册为 'redis_client'"""
    def get(self, key: str):
        return None  # 示例实现

# 在路由中使用
@get('/users/{user_id}')
def get_user(user_id: int):
    """获取用户"""
    # 获取服务
    user_service = get_service('user_service')
    user = user_service.get_user(user_id)

    # 获取客户端
    redis_client = get_client('redis_client')
    if redis_client:
        cache_key = f"user:{user_id}"
        cached = redis_client.get(cache_key)
        if cached:
            return cached

    return user

@post('/users')
def create_user(name: str, email: str):
    """创建用户"""
    # 可以同时获取多个服务
    user_service = get_service('user_service')
    email_service = get_service('email_service')

    user = user_service.create_user(name, email)
    email_service.send_email(email, "欢迎", f"欢迎 {name}")

    return {"message": "用户创建成功", "user": user}
```

#### 服务命名规则

- **默认命名**: 如果未指定名称,服务名会自动转换为类名的小写形式,并使用下划线分隔
  - `UserService` → `'user_service'`
  - `EmailService` → `'email_service'`
  - `DatabaseClient` → `'database_client'`
  - `RedisClient` → `'redis_client'`
- **自定义命名**: 可以通过装饰器参数指定名称
  - `@service('email_service')` → `'email_service'`
  - `@client('redis_client')` → `'redis_client'`

#### 注意事项

1. **服务必须已注册**: 在使用 `get_service()` 或 `get_client()` 之前,确保服务或客户端已经通过装饰器注册
2. **返回 None**: 如果服务或客户端不存在,函数会返回 `None`,使用前建议检查
3. **应用上下文**: 使用全局函数时,确保应用已经创建并初始化
4. **推荐使用全局函数**: 在路由处理函数中,推荐使用 `get_service()` 和 `get_client()` 全局函数,代码更简洁

### 约定规则

- **服务命名**: 类名自动转换为下划线分隔的小写形式作为服务名(如 `UserService` → `user_service`)
- **路由映射**: 函数名自动生成 RESTful 路径
- **任务调度**: 装饰器自动注册到调度器
- **组件扫描**: 自动扫描指定包中的所有组件

## ⚡ 高性能服务器

MyBoot 默认使用 Hypercorn 作为 ASGI 服务器,提供卓越的性能和特性:

### 服务器特性

- **高性能**: 基于 Hypercorn 的高性能 ASGI 服务器
- **HTTP/2 支持**: 支持现代 HTTP 协议
- **WebSocket 支持**: 支持实时通信
- **多进程支持**: 支持多工作进程,适合生产环境
- **自动重载**: 开发环境支持自动重载
- **优雅关闭**: 支持优雅关闭和资源清理

### 使用示例

```python
from myboot.core.application import Application

# 创建应用
app = Application(name="我的应用")

# 开发环境(单进程 + 自动重载)
app.run(host="0.0.0.0", port=8000, reload=True, workers=1)

# 生产环境(多进程)
app.run(host="0.0.0.0", port=8000, workers=4)

# 或者直接运行 main.py
# python main.py --reload  # 开发环境
# python main.py --workers 4  # 生产环境
```

## ⚙️ 配置管理

MyBoot 使用 Dynaconf 提供强大的配置管理功能:

### 基本使用

```python
from myboot.core.config import get_settings, get_config

# 直接使用 Dynaconf settings(自动查找配置文件)
settings = get_settings()
app_name = settings.app.name
server_port = settings.server.port

# 使用便捷函数
database_url = get_config('database.url', 'sqlite:///./app.db')
debug_mode = get_config('app.debug', False)

# 指定配置文件路径
settings = get_settings('custom_config.yaml')

# 通过环境变量指定配置文件
# export CONFIG_FILE=/path/to/config.yaml
# 或
# export CONFIG_FILE=https://example.com/config.yaml
```

### 环境变量覆盖

环境变量可以直接覆盖配置值(使用 `__` 作为分隔符),优先级高于所有配置文件:

```bash
# 使用环境变量覆盖配置值
export APP__NAME="MyApp"
export SERVER__PORT=9000
export LOGGING__LEVEL=DEBUG

# 嵌套配置使用双下划线分隔
export SERVER__CORS__ALLOW_ORIGINS='["http://localhost:3000"]'
```

**注意**:环境变量覆盖配置值的优先级最高,会覆盖所有配置文件中的对应值。

### 远程配置

```python
from myboot.core.config import get_settings

# 从远程 URL 加载配置
settings = get_settings('https://example.com/config.yaml')
```

### 配置优先级

MyBoot 按照以下优先级查找和加载配置文件:

1. **环境变量 `CONFIG_FILE`**(最高优先级)

   - 通过环境变量指定配置文件路径或 URL

   ```bash
   export CONFIG_FILE=/path/to/config.yaml
   # 或
   export CONFIG_FILE=https://example.com/config.yaml
   ```

2. **参数指定的配置文件**

   - 通过 `create_app()` 或 `get_settings()` 的 `config_file` 参数指定

   ```python
   app = create_app(name="我的应用", config_file="custom_config.yaml")
   ```

3. **项目根目录 `/conf` 目录下的配置文件**

   - `项目根目录/conf/config.yaml`
   - `项目根目录/conf/config.yml`

4. **项目根目录下的配置文件**

   - `项目根目录/config.yaml`
   - `项目根目录/config.yml`

5. **默认配置**
   - 内置的默认配置值

**注意**:环境变量还可以直接覆盖配置值(使用 `__` 作为分隔符),优先级高于所有配置文件:

```bash
export APP__NAME="MyApp"
export SERVER__PORT=9000
export LOGGING__LEVEL=DEBUG
```

## 📖 详细文档

- [📚 完整文档](docs/README.md) - 文档中心
- [⚡ REST API 异步任务](docs/rest-api-async-tasks.md) - REST API 中使用异步任务指南
- [🔧 依赖注入](docs/dependency-injection.md) - 依赖注入使用指南

### 1. Web API 开发

#### 基础路由

```python
from myboot.core.decorators import get, post, put, delete
from myboot.web.models import BaseResponse


@get("/users")
def get_users():
    """获取用户列表 - 自动注册为 GET /users"""
    # 约定优于配置:可以通过 get_service() 获取服务实例
    from myboot.core.application import get_service
    user_service = get_service('user_service')
    # 使用服务获取用户列表
    users = user_service.get_users() if user_service else []
    return BaseResponse(
        success=True,
        message="获取用户列表成功",
        data={"users": users}
    )


@post("/users")
def create_user(name: str, email: str):
    """创建用户 - 自动注册为 POST /users"""
    # 约定优于配置:服务自动注册,可以通过 get_service() 获取
    from myboot.core.application import get_service
    user_service = get_service('user_service')
    if user_service:
        user = user_service.create_user(name, email)
        return BaseResponse(
            success=True,
            message="用户创建成功",
            data=user
        )
    return BaseResponse(
        success=True,
        message="用户创建成功",
        data={"name": name, "email": email}
    )


@get("/users/{user_id}")
def get_user(user_id: int):
    """获取单个用户 - 自动注册为 GET /users/{user_id}"""
    # 约定优于配置:服务自动注册,可以通过 get_service() 获取
    from myboot.core.application import get_service
    user_service = get_service('user_service')
    if user_service:
        user = user_service.get_user(user_id)
        return BaseResponse(
            success=True,
            message="获取用户成功",
            data=user
        )
    return BaseResponse(
        success=True,
        message="获取用户成功",
        data={"user_id": user_id, "name": f"用户{user_id}", "email": f"user{user_id}@example.com"}
    )


@put("/users/{user_id}")
def update_user(user_id: int, name: str = None, email: str = None):
    """更新用户 - 自动注册为 PUT /users/{user_id}"""
    # 约定优于配置:服务自动注册,可以通过 get_service() 获取
    from myboot.core.application import get_service
    user_service = get_service('user_service')
    if user_service:
        update_data = {}
        if name:
            update_data['name'] = name
        if email:
            update_data['email'] = email
        user = user_service.update_user(user_id, **update_data)
        if user:
            return BaseResponse(
                success=True,
                message=f"用户 {user_id} 更新成功",
                data=user
            )
    return BaseResponse(
        success=True,
        message=f"用户 {user_id} 更新成功",
        data={"user_id": user_id, "name": name, "email": email}
    )


@delete("/users/{user_id}")
def delete_user(user_id: int):
    """删除用户 - 自动注册为 DELETE /users/{user_id}"""
    # 约定优于配置:服务自动注册,可以通过 get_service() 获取
    from myboot.core.application import get_service
    user_service = get_service('user_service')
    if user_service:
        user = user_service.delete_user(user_id)
        if user:
            return BaseResponse(
                success=True,
                message=f"用户 {user_id} 删除成功",
                data=user
            )
    return BaseResponse(
        success=True,
        message=f"用户 {user_id} 删除成功",
        data={"user_id": user_id}
    )


# 约定优于配置说明:
# 1. 使用 @get, @post, @put, @delete 装饰器自动注册路由
# 2. 函数名和路径自动映射
# 3. 框架自动发现和注册这些路由
# 4. 支持依赖注入,通过 get_service() 和 get_client() 获取服务和客户端
# 5. 统一的响应格式和错误处理
# 6. 无需手动在 main.py 中注册路由

# 更多关于依赖注入的说明,请参考"依赖注入和服务管理"章节
```

#### REST 控制器

使用 `@rest_controller` 装饰器可以创建 REST 控制器类,为类中的方法提供统一的基础路径。类中的方法需要显式使用 `@get`、`@post`、`@put`、`@delete`、`@patch` 等装饰器才会生成路由。

**基本用法:**

```python
from myboot.core.decorators import rest_controller, get, post, put, delete
from myboot.web.models import BaseResponse

@rest_controller('/api/users')
class UserController:
    """用户控制器"""

    def __init__(self):
        # 可以在这里初始化服务、客户端等
        pass

    @get('/')
    def list_users(self):
        """获取用户列表 - GET /api/users"""
        return BaseResponse(
            success=True,
            message="获取用户列表成功",
            data={"users": []}
        )

    @get('/{user_id}')
    def get_user(self, user_id: int):
        """获取单个用户 - GET /api/users/{user_id}"""
        return BaseResponse(
            success=True,
            message="获取用户成功",
            data={"user_id": user_id, "name": f"用户{user_id}"}
        )

    @post('/')
    def create_user(self, name: str, email: str):
        """创建用户 - POST /api/users"""
        return BaseResponse(
            success=True,
            message="用户创建成功",
            data={"name": name, "email": email}
        )

    @put('/{user_id}')
    def update_user(self, user_id: int, name: str = None, email: str = None):
        """更新用户 - PUT /api/users/{user_id}"""
        return BaseResponse(
            success=True,
            message=f"用户 {user_id} 更新成功",
            data={"user_id": user_id, "name": name, "email": email}
        )

    @delete('/{user_id}')
    def delete_user(self, user_id: int):
        """删除用户 - DELETE /api/users/{user_id}"""
        return BaseResponse(
            success=True,
            message=f"用户 {user_id} 删除成功",
            data={"user_id": user_id}
        )
```

**路径合并规则:**

- 方法路径以 `//` 开头:作为绝对路径使用(去掉一个 `/`)
- 方法路径以 `/` 开头:去掉开头的 `/` 后追加到基础路径
- 方法路径不以 `/` 开头:直接追加到基础路径

**示例:**

```python
@rest_controller('/api/reports')
class ReportController:
    """报告控制器"""

    @post('/generate')  # 最终路径: POST /api/reports/generate
    def create_report(self, report_type: str):
        return {"message": "报告生成任务已创建", "type": report_type}

    @get('/status/{job_id}')  # 最终路径: GET /api/reports/status/{job_id}
    def get_status(self, job_id: str):
        return {"status": "completed", "job_id": job_id}

    @get('//health')  # 最终路径: GET /health (绝对路径)
    def health_check(self):
        return {"status": "ok"}
```

**在控制器中使用服务:**

```python
from myboot.core.decorators import rest_controller, get, post
from myboot.core.application import get_service, get_client

@rest_controller('/api/products')
class ProductController:
    """产品控制器"""

    def __init__(self):
        # 在初始化时获取服务
        self.product_service = get_service('product_service')
        self.cache_client = get_client('redis_client')

    @get('/')
    def list_products(self):
        """获取产品列表"""
        # 使用服务
        if self.product_service:
            products = self.product_service.get_all()
            return BaseResponse(success=True, data={"products": products})
        return BaseResponse(success=True, data={"products": []})

    @post('/')
    def create_product(self, name: str, price: float):
        """创建产品"""
        # 在方法中也可以动态获取服务
        product_service = get_service('product_service')
        if product_service:
            product = product_service.create(name, price)
            # 使用客户端缓存
            if self.cache_client:
                self.cache_client.set(f"product:{product.id}", product)
            return BaseResponse(success=True, data={"product": product})
        return BaseResponse(success=False, message="服务不可用")
```

**注意事项:**

1. **显式装饰器**:类中的方法必须显式使用 `@get`、`@post` 等装饰器才会生成路由
2. **路径合并**:方法路径会自动与基础路径合并,形成最终的路由路径
3. **自动注册**:控制器类会被自动发现和注册,无需手动配置
4. **服务注入**:可以在 `__init__` 方法中初始化服务,或在方法中动态获取

#### 数据模型

```python
from pydantic import BaseModel
from typing import Optional

class User(BaseModel):
    """用户数据模型"""
    id: Optional[int] = None
    name: str
    email: str
    age: Optional[int] = None

from myboot.core.decorators import post
from myboot.web.models import BaseResponse

@post("/users")
def create_user(user: User):
    """创建用户"""
    return BaseResponse(
        success=True,
        message="用户创建成功",
        data=user.dict()
    )
```

#### 分页处理

```python
from myboot.core.decorators import get
from myboot.web.models import BaseResponse
from typing import Optional

@get("/users")
def get_users(
    page: int = 1,
    size: int = 10,
    search: Optional[str] = None
):
    """获取用户列表(分页)"""
    # 处理分页逻辑
    # users = get_users_from_db(page, size, search)
    # total_count = get_total_count(search)

    # 示例返回
    return BaseResponse(
        success=True,
        message="获取用户列表成功",
        data={
            "users": [],
            "total": 0,
            "page": page,
            "size": size
        }
    )
```

### 2. 定时任务

#### Cron 表达式任务

```python
from myboot.core.decorators import cron, interval, once
from myboot.core.config import get_config

# 直接指定 enabled 参数
@cron("0 0 * * * *", enabled=True)  # 每小时执行
def hourly_task():
    print("每小时任务")

# 从配置文件读取 enabled 状态
cleanup_enabled = get_config('jobs.cleanup_task.enabled', True)
@cron("0 0 2 * * *", enabled=cleanup_enabled)  # 每天凌晨2点执行
def daily_backup():
    print("每日备份")
```

#### 间隔任务

```python
# 直接启用
@interval(seconds=30, enabled=True)  # 每30秒执行
def heartbeat():
    print("心跳检测")

# 从配置文件读取
monitor_enabled = get_config('jobs.monitor.enabled', True)
@interval(minutes=5, enabled=monitor_enabled)  # 每5分钟执行
def monitor():
    print("系统监控")
```

#### 一次性任务

```python
# 一次性任务 - 过期后不再执行
@once("2024-12-31 23:59:59", enabled=True)
def new_year_task():
    print("新年任务")

# 如果任务时间已过期,将自动标记为过期,不再执行
```

### 3. 配置管理

#### 配置文件 (config.yaml)

```yaml
# 应用配置
app:
  name: "我的应用"
  version: "1.0.0"
  debug: true

# 服务器配置
server:
  host: "0.0.0.0"
  port: 8000
  reload: true

# 数据库配置
database:
  url: "sqlite:///./app.db"
  pool_size: 10

# 任务调度配置
scheduler:
  enabled: true # 是否启用调度器
  timezone: "Asia/Shanghai" # 时区设置(可选,需要安装 pytz)
  max_workers: 10 # 最大工作线程数

# 任务启用配置(可选)
jobs:
  heartbeat:
    enabled: true
  cleanup_task:
    enabled: false
  monitor:
    enabled: true

# 日志配置
logging:
  level: "INFO" # 日志级别: DEBUG, INFO, WARNING, ERROR, CRITICAL
  format: "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}"
  file: "logs/app.log" # 可选,如果配置会自动添加文件 handler
  # 第三方库日志级别配置
  third_party:
    urllib3: "WARNING"
    requests: "WARNING"
    hypercorn: "WARNING"
```

#### 配置使用

```python
from myboot.core.config import get_settings, get_config, get_config_bool, get_config_str

# 方式一:使用 get_settings() 获取完整配置对象
settings = get_settings()
port = settings.get("server.port", 8000)
debug = settings.get("app.debug", False)

# 方式二:使用便捷函数(推荐)
port = get_config("server.port", 8000)
debug = get_config_bool("app.debug", False)
db_url = get_config_str("database.url", "sqlite:///./app.db")

# 在应用实例中也可以直接使用
port = app.config.get("server.port", 8000)
```

#### 调度器配置

```yaml
# 任务调度配置
scheduler:
  enabled: true # 是否启用调度器
  timezone: "Asia/Shanghai" # 时区设置(需要安装 pytz)
  max_workers: 10 # 最大工作线程数
```

```python
# 获取调度器配置
config = app.scheduler.get_config()
print(config)  # {'enabled': True, 'timezone': 'Asia/Shanghai', ...}

# 列出所有任务
jobs = app.scheduler.list_all_jobs()
for job in jobs:
    print(job)

# 获取单个任务信息
job_info = app.scheduler.get_job_info('cron_heartbeat')
print(job_info)
```

#### 任务启用控制

任务装饰器支持 `enabled` 参数,可以控制任务是否启用:

```python
from myboot.core.decorators import cron, interval, once
from myboot.core.config import get_config

# 方式一:直接指定
@cron("0 */1 * * * *", enabled=True)  # 启用
def enabled_task():
    print("启用状态")

@interval(minutes=2, enabled=False)  # 禁用
def disabled_task():
    print("禁用状态")

# 方式二:从配置文件读取
task_enabled = get_config('jobs.my_task.enabled', True)
@once("2025-01-01 00:00:00", enabled=task_enabled)
def configurable_task():
    print("可配置任务")
```

**注意**:

- 如果 `enabled` 为 `None`,默认启用
- 一次性任务如果时间已过期,将自动标记为过期不再执行
- 已执行的一次性任务不会重复执行

### 4. 日志管理

MyBoot 使用 [loguru](https://github.com/Delgan/loguru) 作为日志系统,提供强大的日志功能和优雅的 API。

#### 基本使用

```python
# 方式一:直接使用 loguru(推荐)
from loguru import logger

logger.info("应用启动")
logger.error("发生错误")
logger.debug("调试信息")
logger.warning("警告信息")

# 方式二:使用框架导出的 logger
from myboot.core.logger import logger

logger.info("应用启动")
```

#### 日志配置

日志系统会在应用启动时自动根据配置文件初始化,无需手动配置。

**配置文件示例 (config.yaml):**

```yaml
# 日志配置
logging:
  # 日志级别: DEBUG, INFO, WARNING, ERROR, CRITICAL
  level: "INFO"

  # 日志格式(支持 loguru 格式或标准 logging 格式,会自动转换)
  # 如果设置了 json: true,此选项将被忽略
  format: "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}"

  # 是否使用 JSON 格式输出(适用于日志聚合和分析工具)
  # 设置为 true 时,日志以 JSON 格式输出,包含完整的结构化信息
  json: false

  # 日志文件路径(可选,如果配置会自动添加文件 handler,支持自动轮转)
  file: "logs/app.log"

  # 第三方库日志级别配置(用于控制第三方库的日志输出)
  third_party:
    urllib3: "WARNING" # 只显示 WARNING 及以上级别
    requests: "WARNING" # 只显示 WARNING 及以上级别
    hypercorn: "WARNING" # 只显示 WARNING 及以上级别
    hypercorn.error: "WARNING" # hypercorn.error logger
    asyncio: "INFO" # 显示 INFO 及以上级别
```

#### JSON 格式日志

启用 JSON 格式后,日志会以结构化 JSON 格式输出,便于日志聚合和分析工具(如 ELK、Loki、Grafana 等)处理:

```yaml
logging:
  level: "INFO"
  json: true # 启用 JSON 格式输出
  file: "logs/app.log"
```

JSON 格式日志包含以下字段:

- `text`: 格式化的日志文本
- `record`: 完整的日志记录对象
  - `time`: 时间戳
  - `level`: 日志级别
  - `message`: 日志消息
  - `name`: logger 名称
  - `module`: 模块名
  - `function`: 函数名
  - `file`: 文件名和路径
  - `line`: 行号
  - `process`: 进程信息
  - `thread`: 线程信息
  - `exception`: 异常信息(如果有)

#### 日志格式说明

**Loguru 格式(推荐):**

```python
format: "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}"
```

**标准 logging 格式(会自动转换):**

```python
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
```

#### 高级功能

```python
from loguru import logger

# 结构化日志
logger.info("用户登录", user_id=123, username="admin")

# 异常日志(自动包含堆栈跟踪)
try:
    1 / 0
except:
    logger.exception("发生错误")

# 绑定上下文信息
logger.bind(user_id=123).info("用户操作")

# 临时修改日志级别
with logger.contextualize(level="DEBUG"):
    logger.debug("这是调试信息")

# 添加自定义 handler(保留用户自定义 loguru 的能力)
from loguru import logger
logger.add("custom.log", rotation="100 MB", retention="30 days")
```

#### 手动初始化(可选)

如果需要手动初始化日志系统:

```python
from myboot.core.logger import setup_logging

# 使用默认配置初始化
setup_logging()

# 使用指定配置文件初始化
setup_logging("custom_config.yaml")
```

#### 配置参数说明

| 参数                            | 类型 | 说明                                                                 | 默认值          |
| ------------------------------- | ---- | -------------------------------------------------------------------- | --------------- |
| `logging.level`                 | str  | 日志级别 (DEBUG/INFO/WARNING/ERROR/CRITICAL)                         | INFO            |
| `logging.format`                | str  | 日志格式(支持 loguru 格式或标准 logging 格式,json 为 true 时忽略) | loguru 默认格式 |
| `logging.json`                  | bool | 是否使用 JSON 格式输出(适用于日志聚合和分析工具)                   | false           |
| `logging.file`                  | str  | 日志文件路径,如果配置会自动添加文件 handler                         | 无              |
| `logging.third_party.{library}` | str  | 第三方库日志级别,支持设置任意第三方库的日志级别                     | 无              |

#### 文件日志特性

如果配置了 `logging.file`,loguru 会自动提供:

- **自动轮转**: 当日志文件达到 10MB 时自动轮转
- **自动压缩**: 旧日志文件自动压缩为 zip
- **自动清理**: 保留 7 天的日志文件
- **异常信息**: 自动包含完整的堆栈跟踪

#### 第三方库日志控制

通过 `logging.third_party` 配置可以控制第三方库的日志输出级别:

```yaml
logging:
  third_party:
    urllib3: "WARNING" # 隐藏 urllib3 的 INFO 和 DEBUG 日志
    requests: "WARNING" # 隐藏 requests 的 INFO 和 DEBUG 日志
    hypercorn: "WARNING" # 隐藏 hypercorn 的 INFO 和 DEBUG 日志
    asyncio: "INFO" # 只显示 asyncio 的 INFO 及以上级别
```

这样可以有效减少第三方库的噪音日志,让日志更加清晰。

### 5. 中间件

MyBoot 支持通过装饰器定义中间件,中间件会自动注册:

```python
from myboot.core.decorators import middleware
from fastapi import Request

@middleware(order=1, path_filter='/api/*')
def api_middleware(request: Request, next_handler):
    """API 中间件 - 只处理 /api/* 路径"""
    # 前置处理
    print(f"处理请求: {request.method} {request.url}")

    # 调用下一个处理器
    response = next_handler(request)

    # 后置处理
    print(f"响应状态: {response.status_code}")
    return response

@middleware(order=2, methods=['POST', 'PUT'])
def post_middleware(request: Request, next_handler):
    """POST/PUT 中间件 - 只处理 POST 和 PUT 请求"""
    # 可以在这里添加请求验证、日志记录等
    return next_handler(request)
```

**中间件参数说明:**

- `order`: 执行顺序,数字越小越先执行(默认 0)
- `path_filter`: 路径过滤,支持字符串、字符串列表或正则表达式,如 `'/api/*'`, `['/api/*', '/admin/*']`
- `methods`: HTTP 方法过滤,如 `['GET', 'POST']`(默认 None,处理所有方法)
- `condition`: 条件函数,接收 request 对象,返回 bool 决定是否执行中间件

**注意**:CORS 中间件可以通过配置文件启用,无需手动添加。

### 6. 生命周期钩子

```python
from myboot.core.application import create_app

app = create_app(name="我的应用")

# 添加启动钩子
def startup_hook():
    """应用启动时执行"""
    print("应用启动")

app.add_startup_hook(startup_hook)

# 添加关闭钩子
def shutdown_hook():
    """应用关闭时执行"""
    print("应用关闭")

app.add_shutdown_hook(shutdown_hook)
```

## 📁 项目结构

### 标准项目结构(推荐)

使用 `myboot init` 命令创建的标准项目结构:

```
my-app/
├── main.py              # 应用入口(根目录)
├── pyproject.toml        # 项目配置文件
├── .gitignore           # Git 忽略文件
├── app/                  # 应用代码
│   ├── api/              # API 路由
│   ├── service/          # 业务逻辑层
│   ├── model/            # 数据模型
│   ├── jobs/             # 定时任务
│   └── client/           # 客户端(第三方API调用等)
├── conf/                 # 配置文件目录
│   └── config.yaml       # 主配置文件
└── tests/                # 测试代码
```

### 目录说明

- **main.py**: 应用入口文件,位于项目根目录
- **app/api/**: API 路由层,存放所有路由定义
- **app/service/**: 业务逻辑层,存放业务服务类
- **app/model/**: 数据模型层,存放 Pydantic 模型等
- **app/jobs/**: 定时任务,存放使用 `@cron`、`@interval` 等装饰器的任务
- **app/client/**: 客户端层,存放第三方服务客户端(如 Redis、HTTP 客户端等)
- **conf/**: 配置文件目录,存放 YAML 配置文件
- **tests/**: 测试代码目录

## 🔧 高级功能

### 1. 自定义中间件

使用装饰器定义中间件(推荐方式):

```python
from myboot.core.decorators import middleware
from fastapi import Request

@middleware(order=1)
def custom_middleware(request: Request, next_handler):
    """自定义中间件"""
    # 前置处理
    print(f"请求: {request.method} {request.url}")

    # 调用下一个处理器
    response = next_handler(request)

    # 后置处理
    print(f"响应: {response.status_code}")
    return response
```

或者使用 FastAPI 的 BaseHTTPMiddleware:

```python
from myboot.web.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request

class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # 中间件逻辑
        response = await call_next(request)
        return response

# 在应用初始化时添加
app.add_middleware(Middleware(CustomMiddleware))
```

### 2. 异步任务

在 REST API 中使用异步任务,避免阻塞请求响应。

#### 快速启动后台任务

```python
from myboot.core.decorators import post
from myboot.utils.async_utils import asyn_run
import time

def process_data(data: dict):
    """耗时的数据处理任务"""
    print(f"开始处理数据: {data}")
    time.sleep(5)  # 模拟耗时操作
    print(f"数据处理完成: {data}")
    return {"processed": True, "data": data}

@post('/api/tasks')
def create_task(data: dict):
    """创建异步任务 - 立即返回,任务在后台执行"""
    asyn_run(process_data, data, task_name="数据处理任务")
    return {"message": "任务已创建,正在后台处理"}
```

#### 使用任务管理器

对于需要跟踪任务状态的场景:

```python
from myboot.core.decorators import post, get, rest_controller
from myboot.jobs.manager import JobManager
from myboot.jobs.job import FunctionJob
import threading

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.job_manager = JobManager()

    @post('/generate')
    def create_report(self, report_type: str):
        """创建报告生成任务"""
        # 创建任务
        job = FunctionJob(
            func=generate_report,
            name=f"生成{report_type}报告",
            args=(report_type,),
            timeout=300  # 5分钟超时
        )

        # 添加到任务管理器并执行
        job_id = self.job_manager.add_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_info = self.job_manager.get_job_info(job_id)
        return job_info if job_info else {"error": "任务不存在"}
```

更多详细内容请参考 [REST API 异步任务文档](docs/rest-api-async-tasks.md)。

### 3. 任务管理

#### 调度器任务管理

```python
# 获取调度器配置
config = app.scheduler.get_config()
print(config)  # {'enabled': True, 'timezone': 'Asia/Shanghai', 'running': True, 'job_count': 3}

# 列出所有任务
jobs = app.scheduler.list_all_jobs()
for job in jobs:
    print(f"任务ID: {job['job_id']}, 类型: {job['type']}, 函数: {job['func_name']}")

# 获取单个任务信息
job_info = app.scheduler.get_job_info('cron_heartbeat')
if job_info:
    print(f"Cron表达式: {job_info.get('cron')}")
    print(f"是否已执行: {job_info.get('executed', False)}")
    print(f"是否已过期: {job_info.get('expired', False)}")

# 检查调度器是否启用
if app.scheduler.is_enabled():
    print("调度器已启用")
```

#### 任务管理器

```python
from myboot.jobs.manager import JobManager
from myboot.jobs.job import FunctionJob
import threading

# 获取任务管理器(单例模式)
job_manager = JobManager()

# 添加任务
def my_task(data: dict):
    """任务函数"""
    print(f"处理数据: {data}")
    return {"status": "completed", "data": data}

job = FunctionJob(func=my_task, name="my_task", args=({"key": "value"},))
job_id = job_manager.add_job(job)

# 执行任务(根据任务ID)
result = job_manager.execute_job(job_id, {"key": "value"})

# 或者根据名称执行任务
result = job_manager.execute_job_by_name("my_task", {"key": "value"})

# 获取任务状态(需要任务ID)
status = job_manager.get_job_status(job_id)

# 获取任务信息(需要任务ID)
job_info = job_manager.get_job_info(job_id)

# 列出所有任务信息
all_jobs = job_manager.get_all_job_info()

# 获取任务统计
statistics = job_manager.get_job_statistics()

# 在后台执行任务
thread = threading.Thread(target=job.execute)
thread.daemon = True
thread.start()
```

#### 任务特性

- **过期任务处理**: 一次性任务如果时间已过期,将自动标记为过期不再执行
- **已执行任务**: 一次性任务执行后不会重复执行
- **时区支持**: 支持配置时区(需要安装 pytz)
- **任务状态查询**: 可以查询任务的执行状态、是否过期等信息

## 📚 示例应用

- **基础示例** (`examples/convention_app.py`): 展示基本功能
- **依赖注入示例** (`examples/dependency_injection_example.py`): 展示依赖注入功能

## 🤝 贡献

欢迎贡献代码!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解如何参与。

## 📄 许可证

本项目采用 Apache-2.0 license 许可证。查看 [LICENSE](LICENSE) 文件了解详情。

## 🙏 致谢

感谢以下开源项目:

- [FastAPI](https://fastapi.tiangolo.com/) - 现代、快速的 Web 框架
- [APScheduler](https://apscheduler.readthedocs.io/) - Python 任务调度库
- [Pydantic](https://pydantic-docs.helpmanual.io/) - 数据验证库
- [Loguru](https://github.com/Delgan/loguru) - 现代、强大的日志库

## 📞 支持

如果您遇到问题或有建议,请:

1. 查看 [文档](https://github.com/TrumanDu/myboot)
2. 搜索 [Issues](https://github.com/TrumanDu/myboot/issues)
3. 创建新的 [Issue](https://github.com/TrumanDu/myboot/issues/new)

---

**MyBoot** - 让企业级应用开发更简单、更快速!
🚀

要解决的问题

1.  [x] 配置文件
2.  [x] 日志问题
3.  [x] web 快速开发框架
4.  [x] 自动注入
5.  [x] 异步任务
6.  [x] job 管理

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "myboot",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "api, config, framework, logging, scheduler, web",
    "author": null,
    "author_email": "TrumanDu <truman.p.du@qq.com>",
    "download_url": "https://files.pythonhosted.org/packages/15/a3/f778e7389351a96cdd6ce71e11b0afe90c0937bfa76b1f2b5a3e11fab761/myboot-0.1.1.tar.gz",
    "platform": null,
    "description": "# MyBoot - \u7c7b\u4f3c Spring Boot \u7684 Python \u5feb\u901f\u5f00\u53d1\u6846\u67b6\n\n[![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![PyPI Version](https://img.shields.io/pypi/v/myboot.svg)](https://pypi.org/project/myboot/)\n\nMyBoot \u662f\u4e00\u4e2a\u529f\u80fd\u4e30\u5bcc\u7684 Python Web \u6846\u67b6\uff0c\u63d0\u4f9b\u7c7b\u4f3c Spring Boot \u7684\u81ea\u52a8\u914d\u7f6e\u548c\u5feb\u901f\u5f00\u53d1\u529f\u80fd\u3002\u5b83\u96c6\u6210\u4e86 Web API\u3001\u5b9a\u65f6\u4efb\u52a1\u3001\u65e5\u5fd7\u7ba1\u7406\u3001\u914d\u7f6e\u7ba1\u7406\u7b49\u6838\u5fc3\u529f\u80fd\uff0c\u8ba9\u60a8\u80fd\u591f\u5feb\u901f\u6784\u5efa\u73b0\u4ee3\u5316\u7684 Python \u5e94\u7528\u7a0b\u5e8f\u3002\n\n## \u2728 \u4e3b\u8981\u7279\u6027\n\n- \ud83d\ude80 **\u5feb\u901f\u542f\u52a8**: \u7c7b\u4f3c Spring Boot \u7684\u81ea\u52a8\u914d\u7f6e\u548c\u5feb\u901f\u542f\u52a8\n- \ud83c\udfaf **\u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e**: \u9075\u5faa\u7ea6\u5b9a\uff0c\u51cf\u5c11\u914d\u7f6e\u5de5\u4f5c\uff0c\u81ea\u52a8\u53d1\u73b0\u548c\u6ce8\u518c\u7ec4\u4ef6\n- \ud83c\udf10 **Web API**: \u57fa\u4e8e FastAPI \u7684\u9ad8\u6027\u80fd Web API \u5f00\u53d1\n- \u26a1 **\u9ad8\u6027\u80fd\u670d\u52a1\u5668**: \u9ed8\u8ba4\u4f7f\u7528 Hypercorn \u670d\u52a1\u5668\uff0c\u652f\u6301 HTTP/2 \u548c\u591a\u8fdb\u7a0b\n- \u23f0 **\u5b9a\u65f6\u4efb\u52a1**: \u5f3a\u5927\u7684\u4efb\u52a1\u8c03\u5ea6\u7cfb\u7edf\uff0c\u652f\u6301 Cron \u8868\u8fbe\u5f0f\u548c\u95f4\u9694\u4efb\u52a1\n- \ud83d\udcdd **\u65e5\u5fd7\u7ba1\u7406**: \u57fa\u4e8e loguru \u7684\u5f3a\u5927\u65e5\u5fd7\u7cfb\u7edf\uff0c\u652f\u6301\u7ed3\u6784\u5316\u65e5\u5fd7\u548c\u7b2c\u4e09\u65b9\u5e93\u65e5\u5fd7\u63a7\u5236\n- \u2699\ufe0f **\u914d\u7f6e\u7ba1\u7406**: \u57fa\u4e8e Dynaconf \u7684\u5f3a\u5927\u914d\u7f6e\u7cfb\u7edf\uff0c\u652f\u6301 YAML \u914d\u7f6e\u3001\u73af\u5883\u53d8\u91cf\u8986\u76d6\u548c\u8fdc\u7a0b\u914d\u7f6e\n- \ud83d\udd27 **\u4e2d\u95f4\u4ef6\u652f\u6301**: \u4e30\u5bcc\u7684\u4e2d\u95f4\u4ef6\u751f\u6001\uff0c\u5305\u62ec CORS\u3001\u9650\u6d41\u3001\u5b89\u5168\u7b49\n- \ud83d\udcca **\u5065\u5eb7\u68c0\u67e5**: \u5185\u7f6e\u5065\u5eb7\u68c0\u67e5\u3001\u5c31\u7eea\u68c0\u67e5\u548c\u5b58\u6d3b\u68c0\u67e5\n- \ud83c\udfaf **\u4f9d\u8d56\u6ce8\u5165**: \u7b80\u5355\u7684\u4f9d\u8d56\u6ce8\u5165\u548c\u7ec4\u4ef6\u7ba1\u7406\n- \ud83d\udd04 **\u4f18\u96c5\u5173\u95ed**: \u652f\u6301\u4f18\u96c5\u5173\u95ed\u548c\u8d44\u6e90\u6e05\u7406\n- \ud83d\udcda **\u81ea\u52a8\u6587\u6863**: \u81ea\u52a8\u751f\u6210 API \u6587\u6863\u548c\u4ea4\u4e92\u5f0f\u754c\u9762\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n### \u5b89\u88c5\n\n```bash\npip install myboot\n```\n\n### \u547d\u4ee4\u884c\u5de5\u5177\n\nMyBoot \u63d0\u4f9b\u4e86\u4fbf\u6377\u7684\u547d\u4ee4\u884c\u5de5\u5177\u7528\u4e8e\u521d\u59cb\u5316\u9879\u76ee\uff1a\n\n```bash\n# \u663e\u793a\u5e2e\u52a9\u4fe1\u606f\nmyboot --help\n\n# \u521d\u59cb\u5316\u65b0\u9879\u76ee\uff08\u4ea4\u4e92\u5f0f\uff09\nmyboot init\n\n# \u4f7f\u7528\u6307\u5b9a\u6a21\u677f\u521d\u59cb\u5316\u9879\u76ee\nmyboot init --name my-app --template basic    # \u57fa\u7840\u6a21\u677f\nmyboot init --name my-app --template api      # API \u9879\u76ee\u6a21\u677f\nmyboot init --name my-app --template full     # \u5b8c\u6574\u9879\u76ee\u6a21\u677f\n\n# \u663e\u793a\u6846\u67b6\u4fe1\u606f\nmyboot info\n```\n\n### \u521b\u5efa\u5e94\u7528\n\n\u4f7f\u7528 `myboot init` \u521d\u59cb\u5316\u9879\u76ee\u540e\uff0c\u5728 `main.py` \u4e2d\u521b\u5efa\u5e94\u7528\uff1a\n\n```python\n\"\"\"main.py - \u5e94\u7528\u5165\u53e3\u6587\u4ef6\"\"\"\nfrom myboot.core.application import create_app\n\n# \u521b\u5efa\u5e94\u7528\u5b9e\u4f8b\napp = create_app(name=\"\u6211\u7684\u5e94\u7528\")\n\n# \u8fd0\u884c\u5e94\u7528\nif __name__ == \"__main__\":\n    app.run()\n```\n\n\u5728 `app/api/` \u76ee\u5f55\u4e2d\u5b9a\u4e49\u8def\u7531\uff1a\n\n```python\n\"\"\"app/api/routes.py\"\"\"\nfrom myboot.core.decorators import get, post\nfrom myboot.core.application import get_service\n\n@get(\"/\")\ndef hello():\n    \"\"\"Hello World \u63a5\u53e3\"\"\"\n    return {\"message\": \"Hello, MyBoot!\", \"status\": \"success\"}\n\n@get(\"/users/{user_id}\")\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u7528\u6237\"\"\"\n    user_service = get_service('user_service')\n    if user_service:\n        return user_service.get_user(user_id)\n    return {\"user_id\": user_id, \"message\": \"\u7528\u6237\u670d\u52a1\u672a\u627e\u5230\"}\n```\n\n### \u8fd0\u884c\u5e94\u7528\n\n\u5e94\u7528\u5165\u53e3\u6587\u4ef6\u4f4d\u4e8e\u9879\u76ee\u6839\u76ee\u5f55\u7684 `main.py`\uff1a\n\n```bash\n# \u76f4\u63a5\u8fd0\u884c\npython main.py\n\n# \u542f\u7528\u81ea\u52a8\u91cd\u8f7d\uff08\u5f00\u53d1\u73af\u5883\uff09\npython main.py --reload\n\n# \u6307\u5b9a\u7aef\u53e3\u548c\u4e3b\u673a\npython main.py --host 0.0.0.0 --port 8080\n```\n\n\u8bbf\u95ee http://localhost:8000 \u67e5\u770b\u60a8\u7684\u5e94\u7528\uff01\n\n## \ud83c\udfaf \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\n\nMyBoot \u6846\u67b6\u7684\u6838\u5fc3\u8bbe\u8ba1\u7406\u5ff5\u662f\"\u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\"\uff0c\u8ba9\u60a8\u80fd\u591f\u5feb\u901f\u5f00\u53d1\u800c\u65e0\u9700\u590d\u6742\u7684\u914d\u7f6e\u3002\n\n### \u81ea\u52a8\u53d1\u73b0\u548c\u6ce8\u518c\n\n```python\nfrom myboot.core.decorators import service, get, cron\nfrom myboot.core.application import get_service\n\n@service()\nclass UserService:\n    \"\"\"\u7528\u6237\u670d\u52a1 - \u81ea\u52a8\u6ce8\u518c\u4e3a 'user_service'\"\"\"\n    def get_user(self, user_id):\n        return {\"id\": user_id, \"name\": f\"\u7528\u6237{user_id}\"}\n\n@get('/users/{user_id}')\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u7528\u6237 - \u81ea\u52a8\u6ce8\u518c\u8def\u7531\"\"\"\n    # \u4f7f\u7528\u5168\u5c40\u51fd\u6570\u83b7\u53d6\u670d\u52a1\uff08\u63a8\u8350\u65b9\u5f0f\uff09\n    user_service = get_service('user_service')\n    return user_service.get_user(user_id)\n\n@cron('0 */5 * * * *')\ndef cleanup_task():\n    \"\"\"\u6e05\u7406\u4efb\u52a1 - \u81ea\u52a8\u6ce8\u518c\u5b9a\u65f6\u4efb\u52a1\"\"\"\n    print(\"\u6267\u884c\u6e05\u7406\u4efb\u52a1\")\n```\n\n### \u96f6\u914d\u7f6e\u542f\u52a8\n\n```python\nfrom myboot.core.application import Application\n\n# \u521b\u5efa\u5e94\u7528\uff0c\u81ea\u52a8\u53d1\u73b0\u548c\u914d\u7f6e\u6240\u6709\u7ec4\u4ef6\napp = Application(\n    name=\"\u6211\u7684\u5e94\u7528\",\n    auto_configuration=True,  # \u542f\u7528\u81ea\u52a8\u914d\u7f6e\n    auto_discover_package=\"app\"  # \u81ea\u52a8\u53d1\u73b0 app \u5305\n)\n\n# \u76f4\u63a5\u8fd0\u884c\uff0c\u65e0\u9700\u624b\u52a8\u6ce8\u518c\napp.run()\n```\n\n### \u4f9d\u8d56\u6ce8\u5165\u548c\u670d\u52a1\u7ba1\u7406\n\nMyBoot \u63d0\u4f9b\u4e86\u57fa\u4e8e `dependency_injector` \u7684\u5f3a\u5927\u4f9d\u8d56\u6ce8\u5165\u673a\u5236\uff0c\u652f\u6301\u81ea\u52a8\u4f9d\u8d56\u89e3\u6790\u548c\u6ce8\u5165\uff0c\u8ba9\u60a8\u53ef\u4ee5\u8f7b\u677e\u7ba1\u7406\u670d\u52a1\u4e4b\u95f4\u7684\u4f9d\u8d56\u5173\u7cfb\u3002\n\n#### \u81ea\u52a8\u4f9d\u8d56\u6ce8\u5165\n\n\u6846\u67b6\u4f1a\u81ea\u52a8\u68c0\u6d4b\u670d\u52a1\u7684\u4f9d\u8d56\u5173\u7cfb\u5e76\u81ea\u52a8\u6ce8\u5165\uff0c\u65e0\u9700\u624b\u52a8\u83b7\u53d6\uff1a\n\n```python\nfrom myboot.core.decorators import service\n\n@service()\nclass UserService:\n    def __init__(self):\n        self.users = {}\n\n@service()\nclass EmailService:\n    def send_email(self, to: str, subject: str):\n        print(f\"\u53d1\u9001\u90ae\u4ef6\u5230 {to}: {subject}\")\n\n@service()\nclass OrderService:\n    # \u81ea\u52a8\u6ce8\u5165 UserService \u548c EmailService\n    def __init__(self, user_service: UserService, email_service: EmailService):\n        self.user_service = user_service\n        self.email_service = email_service\n\n    def create_order(self, user_id: int):\n        user = self.user_service.get_user(user_id)\n        self.email_service.send_email(user['email'], \"\u8ba2\u5355\u521b\u5efa\", \"\u60a8\u7684\u8ba2\u5355\u5df2\u521b\u5efa\")\n```\n\n**\u7279\u6027\uff1a**\n\n- \u2705 \u81ea\u52a8\u68c0\u6d4b\u4f9d\u8d56\u5173\u7cfb\n- \u2705 \u81ea\u52a8\u5904\u7406\u4f9d\u8d56\u987a\u5e8f\n- \u2705 \u652f\u6301\u591a\u7ea7\u4f9d\u8d56\n- \u2705 \u652f\u6301\u53ef\u9009\u4f9d\u8d56\uff08`Optional[Type]`\uff09\n- \u2705 \u81ea\u52a8\u68c0\u6d4b\u5faa\u73af\u4f9d\u8d56\n- \u2705 \u5411\u540e\u517c\u5bb9\uff0c\u73b0\u6709\u4ee3\u7801\u65e0\u9700\u4fee\u6539\n\n**\u66f4\u591a\u4fe1\u606f\uff1a** \u67e5\u770b [\u4f9d\u8d56\u6ce8\u5165\u4f7f\u7528\u6307\u5357](docs/dependency-injection.md)\n\n#### \u83b7\u53d6\u670d\u52a1 (get_service)\n\n\u670d\u52a1\u662f\u901a\u8fc7 `@service()` \u88c5\u9970\u5668\u81ea\u52a8\u6ce8\u518c\u7684\uff0c\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u4e24\u79cd\u65b9\u5f0f\u83b7\u53d6\uff1a\n\n**\u65b9\u5f0f\u4e00\uff1a\u901a\u8fc7\u5168\u5c40\u51fd\u6570\uff08\u63a8\u8350\uff09**\n\n```python\nfrom myboot.core.application import get_service\n\n@get('/users/{user_id}')\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u7528\u6237\"\"\"\n    # \u4f7f\u7528\u5168\u5c40\u51fd\u6570\u83b7\u53d6\u670d\u52a1\uff08\u6700\u7b80\u5355\u7684\u65b9\u5f0f\uff09\n    user_service = get_service('user_service')\n    return user_service.get_user(user_id)\n```\n\n**\u65b9\u5f0f\u4e8c\uff1a\u901a\u8fc7\u5e94\u7528\u5b9e\u4f8b**\n\n```python\nfrom myboot.core.application import get_app\n\n@get('/users/{user_id}')\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u7528\u6237\"\"\"\n    # \u901a\u8fc7\u5e94\u7528\u5b9e\u4f8b\u83b7\u53d6\u670d\u52a1\n    app = get_app()\n    user_service = app.get_service('user_service')\n    return user_service.get_user(user_id)\n```\n\n#### \u83b7\u53d6\u5ba2\u6237\u7aef (get_client)\n\n\u5ba2\u6237\u7aef\u662f\u901a\u8fc7 `@client()` \u88c5\u9970\u5668\u81ea\u52a8\u6ce8\u518c\u7684\uff0c\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u4e24\u79cd\u65b9\u5f0f\u83b7\u53d6\uff1a\n\n**\u65b9\u5f0f\u4e00\uff1a\u901a\u8fc7\u5168\u5c40\u51fd\u6570\uff08\u63a8\u8350\uff09**\n\n```python\nfrom myboot.core.application import get_client\n\n@get('/api/products')\ndef get_products():\n    \"\"\"\u83b7\u53d6\u4ea7\u54c1\u5217\u8868\"\"\"\n    # \u4f7f\u7528\u5168\u5c40\u51fd\u6570\u83b7\u53d6\u5ba2\u6237\u7aef\n    redis_client = get_client('redis_client')\n    if redis_client:\n        cache_data = redis_client.get('products')\n    return {\"products\": []}\n```\n\n**\u65b9\u5f0f\u4e8c\uff1a\u901a\u8fc7\u5e94\u7528\u5b9e\u4f8b**\n\n```python\nfrom myboot.core.application import get_app\n\n@get('/api/products')\ndef get_products():\n    \"\"\"\u83b7\u53d6\u4ea7\u54c1\u5217\u8868\"\"\"\n    # \u901a\u8fc7\u5e94\u7528\u5b9e\u4f8b\u83b7\u53d6\u5ba2\u6237\u7aef\n    app = get_app()\n    redis_client = app.get_client('redis_client')\n    if redis_client:\n        cache_data = redis_client.get('products')\n    return {\"products\": []}\n```\n\n#### \u5b8c\u6574\u793a\u4f8b\n\n```python\nfrom myboot.core.decorators import service, client, get, post\nfrom myboot.core.application import get_service, get_client\n\n# \u5b9a\u4e49\u670d\u52a1\n@service()\nclass UserService:\n    \"\"\"\u7528\u6237\u670d\u52a1 - \u81ea\u52a8\u6ce8\u518c\u4e3a 'user_service'\"\"\"\n    def get_user(self, user_id: int):\n        return {\"id\": user_id, \"name\": f\"\u7528\u6237{user_id}\"}\n\n# \u5b9a\u4e49\u5ba2\u6237\u7aef\n@client('redis_client')\nclass RedisClient:\n    \"\"\"Redis \u5ba2\u6237\u7aef - \u6ce8\u518c\u4e3a 'redis_client'\"\"\"\n    def get(self, key: str):\n        return None  # \u793a\u4f8b\u5b9e\u73b0\n\n# \u5728\u8def\u7531\u4e2d\u4f7f\u7528\n@get('/users/{user_id}')\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u7528\u6237\"\"\"\n    # \u83b7\u53d6\u670d\u52a1\n    user_service = get_service('user_service')\n    user = user_service.get_user(user_id)\n\n    # \u83b7\u53d6\u5ba2\u6237\u7aef\n    redis_client = get_client('redis_client')\n    if redis_client:\n        cache_key = f\"user:{user_id}\"\n        cached = redis_client.get(cache_key)\n        if cached:\n            return cached\n\n    return user\n\n@post('/users')\ndef create_user(name: str, email: str):\n    \"\"\"\u521b\u5efa\u7528\u6237\"\"\"\n    # \u53ef\u4ee5\u540c\u65f6\u83b7\u53d6\u591a\u4e2a\u670d\u52a1\n    user_service = get_service('user_service')\n    email_service = get_service('email_service')\n\n    user = user_service.create_user(name, email)\n    email_service.send_email(email, \"\u6b22\u8fce\", f\"\u6b22\u8fce {name}\")\n\n    return {\"message\": \"\u7528\u6237\u521b\u5efa\u6210\u529f\", \"user\": user}\n```\n\n#### \u670d\u52a1\u547d\u540d\u89c4\u5219\n\n- **\u9ed8\u8ba4\u547d\u540d**: \u5982\u679c\u672a\u6307\u5b9a\u540d\u79f0\uff0c\u670d\u52a1\u540d\u4f1a\u81ea\u52a8\u8f6c\u6362\u4e3a\u7c7b\u540d\u7684\u5c0f\u5199\u5f62\u5f0f\uff0c\u5e76\u4f7f\u7528\u4e0b\u5212\u7ebf\u5206\u9694\n  - `UserService` \u2192 `'user_service'`\n  - `EmailService` \u2192 `'email_service'`\n  - `DatabaseClient` \u2192 `'database_client'`\n  - `RedisClient` \u2192 `'redis_client'`\n- **\u81ea\u5b9a\u4e49\u547d\u540d**: \u53ef\u4ee5\u901a\u8fc7\u88c5\u9970\u5668\u53c2\u6570\u6307\u5b9a\u540d\u79f0\n  - `@service('email_service')` \u2192 `'email_service'`\n  - `@client('redis_client')` \u2192 `'redis_client'`\n\n#### \u6ce8\u610f\u4e8b\u9879\n\n1. **\u670d\u52a1\u5fc5\u987b\u5df2\u6ce8\u518c**: \u5728\u4f7f\u7528 `get_service()` \u6216 `get_client()` \u4e4b\u524d\uff0c\u786e\u4fdd\u670d\u52a1\u6216\u5ba2\u6237\u7aef\u5df2\u7ecf\u901a\u8fc7\u88c5\u9970\u5668\u6ce8\u518c\n2. **\u8fd4\u56de None**: \u5982\u679c\u670d\u52a1\u6216\u5ba2\u6237\u7aef\u4e0d\u5b58\u5728\uff0c\u51fd\u6570\u4f1a\u8fd4\u56de `None`\uff0c\u4f7f\u7528\u524d\u5efa\u8bae\u68c0\u67e5\n3. **\u5e94\u7528\u4e0a\u4e0b\u6587**: \u4f7f\u7528\u5168\u5c40\u51fd\u6570\u65f6\uff0c\u786e\u4fdd\u5e94\u7528\u5df2\u7ecf\u521b\u5efa\u5e76\u521d\u59cb\u5316\n4. **\u63a8\u8350\u4f7f\u7528\u5168\u5c40\u51fd\u6570**: \u5728\u8def\u7531\u5904\u7406\u51fd\u6570\u4e2d\uff0c\u63a8\u8350\u4f7f\u7528 `get_service()` \u548c `get_client()` \u5168\u5c40\u51fd\u6570\uff0c\u4ee3\u7801\u66f4\u7b80\u6d01\n\n### \u7ea6\u5b9a\u89c4\u5219\n\n- **\u670d\u52a1\u547d\u540d**: \u7c7b\u540d\u81ea\u52a8\u8f6c\u6362\u4e3a\u4e0b\u5212\u7ebf\u5206\u9694\u7684\u5c0f\u5199\u5f62\u5f0f\u4f5c\u4e3a\u670d\u52a1\u540d\uff08\u5982 `UserService` \u2192 `user_service`\uff09\n- **\u8def\u7531\u6620\u5c04**: \u51fd\u6570\u540d\u81ea\u52a8\u751f\u6210 RESTful \u8def\u5f84\n- **\u4efb\u52a1\u8c03\u5ea6**: \u88c5\u9970\u5668\u81ea\u52a8\u6ce8\u518c\u5230\u8c03\u5ea6\u5668\n- **\u7ec4\u4ef6\u626b\u63cf**: \u81ea\u52a8\u626b\u63cf\u6307\u5b9a\u5305\u4e2d\u7684\u6240\u6709\u7ec4\u4ef6\n\n## \u26a1 \u9ad8\u6027\u80fd\u670d\u52a1\u5668\n\nMyBoot \u9ed8\u8ba4\u4f7f\u7528 Hypercorn \u4f5c\u4e3a ASGI \u670d\u52a1\u5668\uff0c\u63d0\u4f9b\u5353\u8d8a\u7684\u6027\u80fd\u548c\u7279\u6027\uff1a\n\n### \u670d\u52a1\u5668\u7279\u6027\n\n- **\u9ad8\u6027\u80fd**: \u57fa\u4e8e Hypercorn \u7684\u9ad8\u6027\u80fd ASGI \u670d\u52a1\u5668\n- **HTTP/2 \u652f\u6301**: \u652f\u6301\u73b0\u4ee3 HTTP \u534f\u8bae\n- **WebSocket \u652f\u6301**: \u652f\u6301\u5b9e\u65f6\u901a\u4fe1\n- **\u591a\u8fdb\u7a0b\u652f\u6301**: \u652f\u6301\u591a\u5de5\u4f5c\u8fdb\u7a0b\uff0c\u9002\u5408\u751f\u4ea7\u73af\u5883\n- **\u81ea\u52a8\u91cd\u8f7d**: \u5f00\u53d1\u73af\u5883\u652f\u6301\u81ea\u52a8\u91cd\u8f7d\n- **\u4f18\u96c5\u5173\u95ed**: \u652f\u6301\u4f18\u96c5\u5173\u95ed\u548c\u8d44\u6e90\u6e05\u7406\n\n### \u4f7f\u7528\u793a\u4f8b\n\n```python\nfrom myboot.core.application import Application\n\n# \u521b\u5efa\u5e94\u7528\napp = Application(name=\"\u6211\u7684\u5e94\u7528\")\n\n# \u5f00\u53d1\u73af\u5883\uff08\u5355\u8fdb\u7a0b + \u81ea\u52a8\u91cd\u8f7d\uff09\napp.run(host=\"0.0.0.0\", port=8000, reload=True, workers=1)\n\n# \u751f\u4ea7\u73af\u5883\uff08\u591a\u8fdb\u7a0b\uff09\napp.run(host=\"0.0.0.0\", port=8000, workers=4)\n\n# \u6216\u8005\u76f4\u63a5\u8fd0\u884c main.py\n# python main.py --reload  # \u5f00\u53d1\u73af\u5883\n# python main.py --workers 4  # \u751f\u4ea7\u73af\u5883\n```\n\n## \u2699\ufe0f \u914d\u7f6e\u7ba1\u7406\n\nMyBoot \u4f7f\u7528 Dynaconf \u63d0\u4f9b\u5f3a\u5927\u7684\u914d\u7f6e\u7ba1\u7406\u529f\u80fd\uff1a\n\n### \u57fa\u672c\u4f7f\u7528\n\n```python\nfrom myboot.core.config import get_settings, get_config\n\n# \u76f4\u63a5\u4f7f\u7528 Dynaconf settings\uff08\u81ea\u52a8\u67e5\u627e\u914d\u7f6e\u6587\u4ef6\uff09\nsettings = get_settings()\napp_name = settings.app.name\nserver_port = settings.server.port\n\n# \u4f7f\u7528\u4fbf\u6377\u51fd\u6570\ndatabase_url = get_config('database.url', 'sqlite:///./app.db')\ndebug_mode = get_config('app.debug', False)\n\n# \u6307\u5b9a\u914d\u7f6e\u6587\u4ef6\u8def\u5f84\nsettings = get_settings('custom_config.yaml')\n\n# \u901a\u8fc7\u73af\u5883\u53d8\u91cf\u6307\u5b9a\u914d\u7f6e\u6587\u4ef6\n# export CONFIG_FILE=/path/to/config.yaml\n# \u6216\n# export CONFIG_FILE=https://example.com/config.yaml\n```\n\n### \u73af\u5883\u53d8\u91cf\u8986\u76d6\n\n\u73af\u5883\u53d8\u91cf\u53ef\u4ee5\u76f4\u63a5\u8986\u76d6\u914d\u7f6e\u503c\uff08\u4f7f\u7528 `__` \u4f5c\u4e3a\u5206\u9694\u7b26\uff09\uff0c\u4f18\u5148\u7ea7\u9ad8\u4e8e\u6240\u6709\u914d\u7f6e\u6587\u4ef6\uff1a\n\n```bash\n# \u4f7f\u7528\u73af\u5883\u53d8\u91cf\u8986\u76d6\u914d\u7f6e\u503c\nexport APP__NAME=\"MyApp\"\nexport SERVER__PORT=9000\nexport LOGGING__LEVEL=DEBUG\n\n# \u5d4c\u5957\u914d\u7f6e\u4f7f\u7528\u53cc\u4e0b\u5212\u7ebf\u5206\u9694\nexport SERVER__CORS__ALLOW_ORIGINS='[\"http://localhost:3000\"]'\n```\n\n**\u6ce8\u610f**\uff1a\u73af\u5883\u53d8\u91cf\u8986\u76d6\u914d\u7f6e\u503c\u7684\u4f18\u5148\u7ea7\u6700\u9ad8\uff0c\u4f1a\u8986\u76d6\u6240\u6709\u914d\u7f6e\u6587\u4ef6\u4e2d\u7684\u5bf9\u5e94\u503c\u3002\n\n### \u8fdc\u7a0b\u914d\u7f6e\n\n```python\nfrom myboot.core.config import get_settings\n\n# \u4ece\u8fdc\u7a0b URL \u52a0\u8f7d\u914d\u7f6e\nsettings = get_settings('https://example.com/config.yaml')\n```\n\n### \u914d\u7f6e\u4f18\u5148\u7ea7\n\nMyBoot \u6309\u7167\u4ee5\u4e0b\u4f18\u5148\u7ea7\u67e5\u627e\u548c\u52a0\u8f7d\u914d\u7f6e\u6587\u4ef6\uff1a\n\n1. **\u73af\u5883\u53d8\u91cf `CONFIG_FILE`**\uff08\u6700\u9ad8\u4f18\u5148\u7ea7\uff09\n\n   - \u901a\u8fc7\u73af\u5883\u53d8\u91cf\u6307\u5b9a\u914d\u7f6e\u6587\u4ef6\u8def\u5f84\u6216 URL\n\n   ```bash\n   export CONFIG_FILE=/path/to/config.yaml\n   # \u6216\n   export CONFIG_FILE=https://example.com/config.yaml\n   ```\n\n2. **\u53c2\u6570\u6307\u5b9a\u7684\u914d\u7f6e\u6587\u4ef6**\n\n   - \u901a\u8fc7 `create_app()` \u6216 `get_settings()` \u7684 `config_file` \u53c2\u6570\u6307\u5b9a\n\n   ```python\n   app = create_app(name=\"\u6211\u7684\u5e94\u7528\", config_file=\"custom_config.yaml\")\n   ```\n\n3. **\u9879\u76ee\u6839\u76ee\u5f55 `/conf` \u76ee\u5f55\u4e0b\u7684\u914d\u7f6e\u6587\u4ef6**\n\n   - `\u9879\u76ee\u6839\u76ee\u5f55/conf/config.yaml`\n   - `\u9879\u76ee\u6839\u76ee\u5f55/conf/config.yml`\n\n4. **\u9879\u76ee\u6839\u76ee\u5f55\u4e0b\u7684\u914d\u7f6e\u6587\u4ef6**\n\n   - `\u9879\u76ee\u6839\u76ee\u5f55/config.yaml`\n   - `\u9879\u76ee\u6839\u76ee\u5f55/config.yml`\n\n5. **\u9ed8\u8ba4\u914d\u7f6e**\n   - \u5185\u7f6e\u7684\u9ed8\u8ba4\u914d\u7f6e\u503c\n\n**\u6ce8\u610f**\uff1a\u73af\u5883\u53d8\u91cf\u8fd8\u53ef\u4ee5\u76f4\u63a5\u8986\u76d6\u914d\u7f6e\u503c\uff08\u4f7f\u7528 `__` \u4f5c\u4e3a\u5206\u9694\u7b26\uff09\uff0c\u4f18\u5148\u7ea7\u9ad8\u4e8e\u6240\u6709\u914d\u7f6e\u6587\u4ef6\uff1a\n\n```bash\nexport APP__NAME=\"MyApp\"\nexport SERVER__PORT=9000\nexport LOGGING__LEVEL=DEBUG\n```\n\n## \ud83d\udcd6 \u8be6\u7ec6\u6587\u6863\n\n- [\ud83d\udcda \u5b8c\u6574\u6587\u6863](docs/README.md) - \u6587\u6863\u4e2d\u5fc3\n- [\u26a1 REST API \u5f02\u6b65\u4efb\u52a1](docs/rest-api-async-tasks.md) - REST API \u4e2d\u4f7f\u7528\u5f02\u6b65\u4efb\u52a1\u6307\u5357\n- [\ud83d\udd27 \u4f9d\u8d56\u6ce8\u5165](docs/dependency-injection.md) - \u4f9d\u8d56\u6ce8\u5165\u4f7f\u7528\u6307\u5357\n\n### 1. Web API \u5f00\u53d1\n\n#### \u57fa\u7840\u8def\u7531\n\n```python\nfrom myboot.core.decorators import get, post, put, delete\nfrom myboot.web.models import BaseResponse\n\n\n@get(\"/users\")\ndef get_users():\n    \"\"\"\u83b7\u53d6\u7528\u6237\u5217\u8868 - \u81ea\u52a8\u6ce8\u518c\u4e3a GET /users\"\"\"\n    # \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\uff1a\u53ef\u4ee5\u901a\u8fc7 get_service() \u83b7\u53d6\u670d\u52a1\u5b9e\u4f8b\n    from myboot.core.application import get_service\n    user_service = get_service('user_service')\n    # \u4f7f\u7528\u670d\u52a1\u83b7\u53d6\u7528\u6237\u5217\u8868\n    users = user_service.get_users() if user_service else []\n    return BaseResponse(\n        success=True,\n        message=\"\u83b7\u53d6\u7528\u6237\u5217\u8868\u6210\u529f\",\n        data={\"users\": users}\n    )\n\n\n@post(\"/users\")\ndef create_user(name: str, email: str):\n    \"\"\"\u521b\u5efa\u7528\u6237 - \u81ea\u52a8\u6ce8\u518c\u4e3a POST /users\"\"\"\n    # \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\uff1a\u670d\u52a1\u81ea\u52a8\u6ce8\u518c\uff0c\u53ef\u4ee5\u901a\u8fc7 get_service() \u83b7\u53d6\n    from myboot.core.application import get_service\n    user_service = get_service('user_service')\n    if user_service:\n        user = user_service.create_user(name, email)\n        return BaseResponse(\n            success=True,\n            message=\"\u7528\u6237\u521b\u5efa\u6210\u529f\",\n            data=user\n        )\n    return BaseResponse(\n        success=True,\n        message=\"\u7528\u6237\u521b\u5efa\u6210\u529f\",\n        data={\"name\": name, \"email\": email}\n    )\n\n\n@get(\"/users/{user_id}\")\ndef get_user(user_id: int):\n    \"\"\"\u83b7\u53d6\u5355\u4e2a\u7528\u6237 - \u81ea\u52a8\u6ce8\u518c\u4e3a GET /users/{user_id}\"\"\"\n    # \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\uff1a\u670d\u52a1\u81ea\u52a8\u6ce8\u518c\uff0c\u53ef\u4ee5\u901a\u8fc7 get_service() \u83b7\u53d6\n    from myboot.core.application import get_service\n    user_service = get_service('user_service')\n    if user_service:\n        user = user_service.get_user(user_id)\n        return BaseResponse(\n            success=True,\n            message=\"\u83b7\u53d6\u7528\u6237\u6210\u529f\",\n            data=user\n        )\n    return BaseResponse(\n        success=True,\n        message=\"\u83b7\u53d6\u7528\u6237\u6210\u529f\",\n        data={\"user_id\": user_id, \"name\": f\"\u7528\u6237{user_id}\", \"email\": f\"user{user_id}@example.com\"}\n    )\n\n\n@put(\"/users/{user_id}\")\ndef update_user(user_id: int, name: str = None, email: str = None):\n    \"\"\"\u66f4\u65b0\u7528\u6237 - \u81ea\u52a8\u6ce8\u518c\u4e3a PUT /users/{user_id}\"\"\"\n    # \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\uff1a\u670d\u52a1\u81ea\u52a8\u6ce8\u518c\uff0c\u53ef\u4ee5\u901a\u8fc7 get_service() \u83b7\u53d6\n    from myboot.core.application import get_service\n    user_service = get_service('user_service')\n    if user_service:\n        update_data = {}\n        if name:\n            update_data['name'] = name\n        if email:\n            update_data['email'] = email\n        user = user_service.update_user(user_id, **update_data)\n        if user:\n            return BaseResponse(\n                success=True,\n                message=f\"\u7528\u6237 {user_id} \u66f4\u65b0\u6210\u529f\",\n                data=user\n            )\n    return BaseResponse(\n        success=True,\n        message=f\"\u7528\u6237 {user_id} \u66f4\u65b0\u6210\u529f\",\n        data={\"user_id\": user_id, \"name\": name, \"email\": email}\n    )\n\n\n@delete(\"/users/{user_id}\")\ndef delete_user(user_id: int):\n    \"\"\"\u5220\u9664\u7528\u6237 - \u81ea\u52a8\u6ce8\u518c\u4e3a DELETE /users/{user_id}\"\"\"\n    # \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\uff1a\u670d\u52a1\u81ea\u52a8\u6ce8\u518c\uff0c\u53ef\u4ee5\u901a\u8fc7 get_service() \u83b7\u53d6\n    from myboot.core.application import get_service\n    user_service = get_service('user_service')\n    if user_service:\n        user = user_service.delete_user(user_id)\n        if user:\n            return BaseResponse(\n                success=True,\n                message=f\"\u7528\u6237 {user_id} \u5220\u9664\u6210\u529f\",\n                data=user\n            )\n    return BaseResponse(\n        success=True,\n        message=f\"\u7528\u6237 {user_id} \u5220\u9664\u6210\u529f\",\n        data={\"user_id\": user_id}\n    )\n\n\n# \u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\u8bf4\u660e\uff1a\n# 1. \u4f7f\u7528 @get, @post, @put, @delete \u88c5\u9970\u5668\u81ea\u52a8\u6ce8\u518c\u8def\u7531\n# 2. \u51fd\u6570\u540d\u548c\u8def\u5f84\u81ea\u52a8\u6620\u5c04\n# 3. \u6846\u67b6\u81ea\u52a8\u53d1\u73b0\u548c\u6ce8\u518c\u8fd9\u4e9b\u8def\u7531\n# 4. \u652f\u6301\u4f9d\u8d56\u6ce8\u5165\uff0c\u901a\u8fc7 get_service() \u548c get_client() \u83b7\u53d6\u670d\u52a1\u548c\u5ba2\u6237\u7aef\n# 5. \u7edf\u4e00\u7684\u54cd\u5e94\u683c\u5f0f\u548c\u9519\u8bef\u5904\u7406\n# 6. \u65e0\u9700\u624b\u52a8\u5728 main.py \u4e2d\u6ce8\u518c\u8def\u7531\n\n# \u66f4\u591a\u5173\u4e8e\u4f9d\u8d56\u6ce8\u5165\u7684\u8bf4\u660e\uff0c\u8bf7\u53c2\u8003\"\u4f9d\u8d56\u6ce8\u5165\u548c\u670d\u52a1\u7ba1\u7406\"\u7ae0\u8282\n```\n\n#### REST \u63a7\u5236\u5668\n\n\u4f7f\u7528 `@rest_controller` \u88c5\u9970\u5668\u53ef\u4ee5\u521b\u5efa REST \u63a7\u5236\u5668\u7c7b\uff0c\u4e3a\u7c7b\u4e2d\u7684\u65b9\u6cd5\u63d0\u4f9b\u7edf\u4e00\u7684\u57fa\u7840\u8def\u5f84\u3002\u7c7b\u4e2d\u7684\u65b9\u6cd5\u9700\u8981\u663e\u5f0f\u4f7f\u7528 `@get`\u3001`@post`\u3001`@put`\u3001`@delete`\u3001`@patch` \u7b49\u88c5\u9970\u5668\u624d\u4f1a\u751f\u6210\u8def\u7531\u3002\n\n**\u57fa\u672c\u7528\u6cd5\uff1a**\n\n```python\nfrom myboot.core.decorators import rest_controller, get, post, put, delete\nfrom myboot.web.models import BaseResponse\n\n@rest_controller('/api/users')\nclass UserController:\n    \"\"\"\u7528\u6237\u63a7\u5236\u5668\"\"\"\n\n    def __init__(self):\n        # \u53ef\u4ee5\u5728\u8fd9\u91cc\u521d\u59cb\u5316\u670d\u52a1\u3001\u5ba2\u6237\u7aef\u7b49\n        pass\n\n    @get('/')\n    def list_users(self):\n        \"\"\"\u83b7\u53d6\u7528\u6237\u5217\u8868 - GET /api/users\"\"\"\n        return BaseResponse(\n            success=True,\n            message=\"\u83b7\u53d6\u7528\u6237\u5217\u8868\u6210\u529f\",\n            data={\"users\": []}\n        )\n\n    @get('/{user_id}')\n    def get_user(self, user_id: int):\n        \"\"\"\u83b7\u53d6\u5355\u4e2a\u7528\u6237 - GET /api/users/{user_id}\"\"\"\n        return BaseResponse(\n            success=True,\n            message=\"\u83b7\u53d6\u7528\u6237\u6210\u529f\",\n            data={\"user_id\": user_id, \"name\": f\"\u7528\u6237{user_id}\"}\n        )\n\n    @post('/')\n    def create_user(self, name: str, email: str):\n        \"\"\"\u521b\u5efa\u7528\u6237 - POST /api/users\"\"\"\n        return BaseResponse(\n            success=True,\n            message=\"\u7528\u6237\u521b\u5efa\u6210\u529f\",\n            data={\"name\": name, \"email\": email}\n        )\n\n    @put('/{user_id}')\n    def update_user(self, user_id: int, name: str = None, email: str = None):\n        \"\"\"\u66f4\u65b0\u7528\u6237 - PUT /api/users/{user_id}\"\"\"\n        return BaseResponse(\n            success=True,\n            message=f\"\u7528\u6237 {user_id} \u66f4\u65b0\u6210\u529f\",\n            data={\"user_id\": user_id, \"name\": name, \"email\": email}\n        )\n\n    @delete('/{user_id}')\n    def delete_user(self, user_id: int):\n        \"\"\"\u5220\u9664\u7528\u6237 - DELETE /api/users/{user_id}\"\"\"\n        return BaseResponse(\n            success=True,\n            message=f\"\u7528\u6237 {user_id} \u5220\u9664\u6210\u529f\",\n            data={\"user_id\": user_id}\n        )\n```\n\n**\u8def\u5f84\u5408\u5e76\u89c4\u5219\uff1a**\n\n- \u65b9\u6cd5\u8def\u5f84\u4ee5 `//` \u5f00\u5934\uff1a\u4f5c\u4e3a\u7edd\u5bf9\u8def\u5f84\u4f7f\u7528\uff08\u53bb\u6389\u4e00\u4e2a `/`\uff09\n- \u65b9\u6cd5\u8def\u5f84\u4ee5 `/` \u5f00\u5934\uff1a\u53bb\u6389\u5f00\u5934\u7684 `/` \u540e\u8ffd\u52a0\u5230\u57fa\u7840\u8def\u5f84\n- \u65b9\u6cd5\u8def\u5f84\u4e0d\u4ee5 `/` \u5f00\u5934\uff1a\u76f4\u63a5\u8ffd\u52a0\u5230\u57fa\u7840\u8def\u5f84\n\n**\u793a\u4f8b\uff1a**\n\n```python\n@rest_controller('/api/reports')\nclass ReportController:\n    \"\"\"\u62a5\u544a\u63a7\u5236\u5668\"\"\"\n\n    @post('/generate')  # \u6700\u7ec8\u8def\u5f84: POST /api/reports/generate\n    def create_report(self, report_type: str):\n        return {\"message\": \"\u62a5\u544a\u751f\u6210\u4efb\u52a1\u5df2\u521b\u5efa\", \"type\": report_type}\n\n    @get('/status/{job_id}')  # \u6700\u7ec8\u8def\u5f84: GET /api/reports/status/{job_id}\n    def get_status(self, job_id: str):\n        return {\"status\": \"completed\", \"job_id\": job_id}\n\n    @get('//health')  # \u6700\u7ec8\u8def\u5f84: GET /health (\u7edd\u5bf9\u8def\u5f84)\n    def health_check(self):\n        return {\"status\": \"ok\"}\n```\n\n**\u5728\u63a7\u5236\u5668\u4e2d\u4f7f\u7528\u670d\u52a1\uff1a**\n\n```python\nfrom myboot.core.decorators import rest_controller, get, post\nfrom myboot.core.application import get_service, get_client\n\n@rest_controller('/api/products')\nclass ProductController:\n    \"\"\"\u4ea7\u54c1\u63a7\u5236\u5668\"\"\"\n\n    def __init__(self):\n        # \u5728\u521d\u59cb\u5316\u65f6\u83b7\u53d6\u670d\u52a1\n        self.product_service = get_service('product_service')\n        self.cache_client = get_client('redis_client')\n\n    @get('/')\n    def list_products(self):\n        \"\"\"\u83b7\u53d6\u4ea7\u54c1\u5217\u8868\"\"\"\n        # \u4f7f\u7528\u670d\u52a1\n        if self.product_service:\n            products = self.product_service.get_all()\n            return BaseResponse(success=True, data={\"products\": products})\n        return BaseResponse(success=True, data={\"products\": []})\n\n    @post('/')\n    def create_product(self, name: str, price: float):\n        \"\"\"\u521b\u5efa\u4ea7\u54c1\"\"\"\n        # \u5728\u65b9\u6cd5\u4e2d\u4e5f\u53ef\u4ee5\u52a8\u6001\u83b7\u53d6\u670d\u52a1\n        product_service = get_service('product_service')\n        if product_service:\n            product = product_service.create(name, price)\n            # \u4f7f\u7528\u5ba2\u6237\u7aef\u7f13\u5b58\n            if self.cache_client:\n                self.cache_client.set(f\"product:{product.id}\", product)\n            return BaseResponse(success=True, data={\"product\": product})\n        return BaseResponse(success=False, message=\"\u670d\u52a1\u4e0d\u53ef\u7528\")\n```\n\n**\u6ce8\u610f\u4e8b\u9879\uff1a**\n\n1. **\u663e\u5f0f\u88c5\u9970\u5668**\uff1a\u7c7b\u4e2d\u7684\u65b9\u6cd5\u5fc5\u987b\u663e\u5f0f\u4f7f\u7528 `@get`\u3001`@post` \u7b49\u88c5\u9970\u5668\u624d\u4f1a\u751f\u6210\u8def\u7531\n2. **\u8def\u5f84\u5408\u5e76**\uff1a\u65b9\u6cd5\u8def\u5f84\u4f1a\u81ea\u52a8\u4e0e\u57fa\u7840\u8def\u5f84\u5408\u5e76\uff0c\u5f62\u6210\u6700\u7ec8\u7684\u8def\u7531\u8def\u5f84\n3. **\u81ea\u52a8\u6ce8\u518c**\uff1a\u63a7\u5236\u5668\u7c7b\u4f1a\u88ab\u81ea\u52a8\u53d1\u73b0\u548c\u6ce8\u518c\uff0c\u65e0\u9700\u624b\u52a8\u914d\u7f6e\n4. **\u670d\u52a1\u6ce8\u5165**\uff1a\u53ef\u4ee5\u5728 `__init__` \u65b9\u6cd5\u4e2d\u521d\u59cb\u5316\u670d\u52a1\uff0c\u6216\u5728\u65b9\u6cd5\u4e2d\u52a8\u6001\u83b7\u53d6\n\n#### \u6570\u636e\u6a21\u578b\n\n```python\nfrom pydantic import BaseModel\nfrom typing import Optional\n\nclass User(BaseModel):\n    \"\"\"\u7528\u6237\u6570\u636e\u6a21\u578b\"\"\"\n    id: Optional[int] = None\n    name: str\n    email: str\n    age: Optional[int] = None\n\nfrom myboot.core.decorators import post\nfrom myboot.web.models import BaseResponse\n\n@post(\"/users\")\ndef create_user(user: User):\n    \"\"\"\u521b\u5efa\u7528\u6237\"\"\"\n    return BaseResponse(\n        success=True,\n        message=\"\u7528\u6237\u521b\u5efa\u6210\u529f\",\n        data=user.dict()\n    )\n```\n\n#### \u5206\u9875\u5904\u7406\n\n```python\nfrom myboot.core.decorators import get\nfrom myboot.web.models import BaseResponse\nfrom typing import Optional\n\n@get(\"/users\")\ndef get_users(\n    page: int = 1,\n    size: int = 10,\n    search: Optional[str] = None\n):\n    \"\"\"\u83b7\u53d6\u7528\u6237\u5217\u8868\uff08\u5206\u9875\uff09\"\"\"\n    # \u5904\u7406\u5206\u9875\u903b\u8f91\n    # users = get_users_from_db(page, size, search)\n    # total_count = get_total_count(search)\n\n    # \u793a\u4f8b\u8fd4\u56de\n    return BaseResponse(\n        success=True,\n        message=\"\u83b7\u53d6\u7528\u6237\u5217\u8868\u6210\u529f\",\n        data={\n            \"users\": [],\n            \"total\": 0,\n            \"page\": page,\n            \"size\": size\n        }\n    )\n```\n\n### 2. \u5b9a\u65f6\u4efb\u52a1\n\n#### Cron \u8868\u8fbe\u5f0f\u4efb\u52a1\n\n```python\nfrom myboot.core.decorators import cron, interval, once\nfrom myboot.core.config import get_config\n\n# \u76f4\u63a5\u6307\u5b9a enabled \u53c2\u6570\n@cron(\"0 0 * * * *\", enabled=True)  # \u6bcf\u5c0f\u65f6\u6267\u884c\ndef hourly_task():\n    print(\"\u6bcf\u5c0f\u65f6\u4efb\u52a1\")\n\n# \u4ece\u914d\u7f6e\u6587\u4ef6\u8bfb\u53d6 enabled \u72b6\u6001\ncleanup_enabled = get_config('jobs.cleanup_task.enabled', True)\n@cron(\"0 0 2 * * *\", enabled=cleanup_enabled)  # \u6bcf\u5929\u51cc\u66682\u70b9\u6267\u884c\ndef daily_backup():\n    print(\"\u6bcf\u65e5\u5907\u4efd\")\n```\n\n#### \u95f4\u9694\u4efb\u52a1\n\n```python\n# \u76f4\u63a5\u542f\u7528\n@interval(seconds=30, enabled=True)  # \u6bcf30\u79d2\u6267\u884c\ndef heartbeat():\n    print(\"\u5fc3\u8df3\u68c0\u6d4b\")\n\n# \u4ece\u914d\u7f6e\u6587\u4ef6\u8bfb\u53d6\nmonitor_enabled = get_config('jobs.monitor.enabled', True)\n@interval(minutes=5, enabled=monitor_enabled)  # \u6bcf5\u5206\u949f\u6267\u884c\ndef monitor():\n    print(\"\u7cfb\u7edf\u76d1\u63a7\")\n```\n\n#### \u4e00\u6b21\u6027\u4efb\u52a1\n\n```python\n# \u4e00\u6b21\u6027\u4efb\u52a1 - \u8fc7\u671f\u540e\u4e0d\u518d\u6267\u884c\n@once(\"2024-12-31 23:59:59\", enabled=True)\ndef new_year_task():\n    print(\"\u65b0\u5e74\u4efb\u52a1\")\n\n# \u5982\u679c\u4efb\u52a1\u65f6\u95f4\u5df2\u8fc7\u671f\uff0c\u5c06\u81ea\u52a8\u6807\u8bb0\u4e3a\u8fc7\u671f\uff0c\u4e0d\u518d\u6267\u884c\n```\n\n### 3. \u914d\u7f6e\u7ba1\u7406\n\n#### \u914d\u7f6e\u6587\u4ef6 (config.yaml)\n\n```yaml\n# \u5e94\u7528\u914d\u7f6e\napp:\n  name: \"\u6211\u7684\u5e94\u7528\"\n  version: \"1.0.0\"\n  debug: true\n\n# \u670d\u52a1\u5668\u914d\u7f6e\nserver:\n  host: \"0.0.0.0\"\n  port: 8000\n  reload: true\n\n# \u6570\u636e\u5e93\u914d\u7f6e\ndatabase:\n  url: \"sqlite:///./app.db\"\n  pool_size: 10\n\n# \u4efb\u52a1\u8c03\u5ea6\u914d\u7f6e\nscheduler:\n  enabled: true # \u662f\u5426\u542f\u7528\u8c03\u5ea6\u5668\n  timezone: \"Asia/Shanghai\" # \u65f6\u533a\u8bbe\u7f6e\uff08\u53ef\u9009\uff0c\u9700\u8981\u5b89\u88c5 pytz\uff09\n  max_workers: 10 # \u6700\u5927\u5de5\u4f5c\u7ebf\u7a0b\u6570\n\n# \u4efb\u52a1\u542f\u7528\u914d\u7f6e\uff08\u53ef\u9009\uff09\njobs:\n  heartbeat:\n    enabled: true\n  cleanup_task:\n    enabled: false\n  monitor:\n    enabled: true\n\n# \u65e5\u5fd7\u914d\u7f6e\nlogging:\n  level: \"INFO\" # \u65e5\u5fd7\u7ea7\u522b: DEBUG, INFO, WARNING, ERROR, CRITICAL\n  format: \"{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}\"\n  file: \"logs/app.log\" # \u53ef\u9009\uff0c\u5982\u679c\u914d\u7f6e\u4f1a\u81ea\u52a8\u6dfb\u52a0\u6587\u4ef6 handler\n  # \u7b2c\u4e09\u65b9\u5e93\u65e5\u5fd7\u7ea7\u522b\u914d\u7f6e\n  third_party:\n    urllib3: \"WARNING\"\n    requests: \"WARNING\"\n    hypercorn: \"WARNING\"\n```\n\n#### \u914d\u7f6e\u4f7f\u7528\n\n```python\nfrom myboot.core.config import get_settings, get_config, get_config_bool, get_config_str\n\n# \u65b9\u5f0f\u4e00\uff1a\u4f7f\u7528 get_settings() \u83b7\u53d6\u5b8c\u6574\u914d\u7f6e\u5bf9\u8c61\nsettings = get_settings()\nport = settings.get(\"server.port\", 8000)\ndebug = settings.get(\"app.debug\", False)\n\n# \u65b9\u5f0f\u4e8c\uff1a\u4f7f\u7528\u4fbf\u6377\u51fd\u6570\uff08\u63a8\u8350\uff09\nport = get_config(\"server.port\", 8000)\ndebug = get_config_bool(\"app.debug\", False)\ndb_url = get_config_str(\"database.url\", \"sqlite:///./app.db\")\n\n# \u5728\u5e94\u7528\u5b9e\u4f8b\u4e2d\u4e5f\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528\nport = app.config.get(\"server.port\", 8000)\n```\n\n#### \u8c03\u5ea6\u5668\u914d\u7f6e\n\n```yaml\n# \u4efb\u52a1\u8c03\u5ea6\u914d\u7f6e\nscheduler:\n  enabled: true # \u662f\u5426\u542f\u7528\u8c03\u5ea6\u5668\n  timezone: \"Asia/Shanghai\" # \u65f6\u533a\u8bbe\u7f6e\uff08\u9700\u8981\u5b89\u88c5 pytz\uff09\n  max_workers: 10 # \u6700\u5927\u5de5\u4f5c\u7ebf\u7a0b\u6570\n```\n\n```python\n# \u83b7\u53d6\u8c03\u5ea6\u5668\u914d\u7f6e\nconfig = app.scheduler.get_config()\nprint(config)  # {'enabled': True, 'timezone': 'Asia/Shanghai', ...}\n\n# \u5217\u51fa\u6240\u6709\u4efb\u52a1\njobs = app.scheduler.list_all_jobs()\nfor job in jobs:\n    print(job)\n\n# \u83b7\u53d6\u5355\u4e2a\u4efb\u52a1\u4fe1\u606f\njob_info = app.scheduler.get_job_info('cron_heartbeat')\nprint(job_info)\n```\n\n#### \u4efb\u52a1\u542f\u7528\u63a7\u5236\n\n\u4efb\u52a1\u88c5\u9970\u5668\u652f\u6301 `enabled` \u53c2\u6570\uff0c\u53ef\u4ee5\u63a7\u5236\u4efb\u52a1\u662f\u5426\u542f\u7528\uff1a\n\n```python\nfrom myboot.core.decorators import cron, interval, once\nfrom myboot.core.config import get_config\n\n# \u65b9\u5f0f\u4e00\uff1a\u76f4\u63a5\u6307\u5b9a\n@cron(\"0 */1 * * * *\", enabled=True)  # \u542f\u7528\ndef enabled_task():\n    print(\"\u542f\u7528\u72b6\u6001\")\n\n@interval(minutes=2, enabled=False)  # \u7981\u7528\ndef disabled_task():\n    print(\"\u7981\u7528\u72b6\u6001\")\n\n# \u65b9\u5f0f\u4e8c\uff1a\u4ece\u914d\u7f6e\u6587\u4ef6\u8bfb\u53d6\ntask_enabled = get_config('jobs.my_task.enabled', True)\n@once(\"2025-01-01 00:00:00\", enabled=task_enabled)\ndef configurable_task():\n    print(\"\u53ef\u914d\u7f6e\u4efb\u52a1\")\n```\n\n**\u6ce8\u610f**\uff1a\n\n- \u5982\u679c `enabled` \u4e3a `None`\uff0c\u9ed8\u8ba4\u542f\u7528\n- \u4e00\u6b21\u6027\u4efb\u52a1\u5982\u679c\u65f6\u95f4\u5df2\u8fc7\u671f\uff0c\u5c06\u81ea\u52a8\u6807\u8bb0\u4e3a\u8fc7\u671f\u4e0d\u518d\u6267\u884c\n- \u5df2\u6267\u884c\u7684\u4e00\u6b21\u6027\u4efb\u52a1\u4e0d\u4f1a\u91cd\u590d\u6267\u884c\n\n### 4. \u65e5\u5fd7\u7ba1\u7406\n\nMyBoot \u4f7f\u7528 [loguru](https://github.com/Delgan/loguru) \u4f5c\u4e3a\u65e5\u5fd7\u7cfb\u7edf\uff0c\u63d0\u4f9b\u5f3a\u5927\u7684\u65e5\u5fd7\u529f\u80fd\u548c\u4f18\u96c5\u7684 API\u3002\n\n#### \u57fa\u672c\u4f7f\u7528\n\n```python\n# \u65b9\u5f0f\u4e00\uff1a\u76f4\u63a5\u4f7f\u7528 loguru\uff08\u63a8\u8350\uff09\nfrom loguru import logger\n\nlogger.info(\"\u5e94\u7528\u542f\u52a8\")\nlogger.error(\"\u53d1\u751f\u9519\u8bef\")\nlogger.debug(\"\u8c03\u8bd5\u4fe1\u606f\")\nlogger.warning(\"\u8b66\u544a\u4fe1\u606f\")\n\n# \u65b9\u5f0f\u4e8c\uff1a\u4f7f\u7528\u6846\u67b6\u5bfc\u51fa\u7684 logger\nfrom myboot.core.logger import logger\n\nlogger.info(\"\u5e94\u7528\u542f\u52a8\")\n```\n\n#### \u65e5\u5fd7\u914d\u7f6e\n\n\u65e5\u5fd7\u7cfb\u7edf\u4f1a\u5728\u5e94\u7528\u542f\u52a8\u65f6\u81ea\u52a8\u6839\u636e\u914d\u7f6e\u6587\u4ef6\u521d\u59cb\u5316\uff0c\u65e0\u9700\u624b\u52a8\u914d\u7f6e\u3002\n\n**\u914d\u7f6e\u6587\u4ef6\u793a\u4f8b (config.yaml):**\n\n```yaml\n# \u65e5\u5fd7\u914d\u7f6e\nlogging:\n  # \u65e5\u5fd7\u7ea7\u522b: DEBUG, INFO, WARNING, ERROR, CRITICAL\n  level: \"INFO\"\n\n  # \u65e5\u5fd7\u683c\u5f0f\uff08\u652f\u6301 loguru \u683c\u5f0f\u6216\u6807\u51c6 logging \u683c\u5f0f\uff0c\u4f1a\u81ea\u52a8\u8f6c\u6362\uff09\n  # \u5982\u679c\u8bbe\u7f6e\u4e86 json: true\uff0c\u6b64\u9009\u9879\u5c06\u88ab\u5ffd\u7565\n  format: \"{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}\"\n\n  # \u662f\u5426\u4f7f\u7528 JSON \u683c\u5f0f\u8f93\u51fa\uff08\u9002\u7528\u4e8e\u65e5\u5fd7\u805a\u5408\u548c\u5206\u6790\u5de5\u5177\uff09\n  # \u8bbe\u7f6e\u4e3a true \u65f6\uff0c\u65e5\u5fd7\u4ee5 JSON \u683c\u5f0f\u8f93\u51fa\uff0c\u5305\u542b\u5b8c\u6574\u7684\u7ed3\u6784\u5316\u4fe1\u606f\n  json: false\n\n  # \u65e5\u5fd7\u6587\u4ef6\u8def\u5f84\uff08\u53ef\u9009\uff0c\u5982\u679c\u914d\u7f6e\u4f1a\u81ea\u52a8\u6dfb\u52a0\u6587\u4ef6 handler\uff0c\u652f\u6301\u81ea\u52a8\u8f6e\u8f6c\uff09\n  file: \"logs/app.log\"\n\n  # \u7b2c\u4e09\u65b9\u5e93\u65e5\u5fd7\u7ea7\u522b\u914d\u7f6e\uff08\u7528\u4e8e\u63a7\u5236\u7b2c\u4e09\u65b9\u5e93\u7684\u65e5\u5fd7\u8f93\u51fa\uff09\n  third_party:\n    urllib3: \"WARNING\" # \u53ea\u663e\u793a WARNING \u53ca\u4ee5\u4e0a\u7ea7\u522b\n    requests: \"WARNING\" # \u53ea\u663e\u793a WARNING \u53ca\u4ee5\u4e0a\u7ea7\u522b\n    hypercorn: \"WARNING\" # \u53ea\u663e\u793a WARNING \u53ca\u4ee5\u4e0a\u7ea7\u522b\n    hypercorn.error: \"WARNING\" # hypercorn.error logger\n    asyncio: \"INFO\" # \u663e\u793a INFO \u53ca\u4ee5\u4e0a\u7ea7\u522b\n```\n\n#### JSON \u683c\u5f0f\u65e5\u5fd7\n\n\u542f\u7528 JSON \u683c\u5f0f\u540e\uff0c\u65e5\u5fd7\u4f1a\u4ee5\u7ed3\u6784\u5316 JSON \u683c\u5f0f\u8f93\u51fa\uff0c\u4fbf\u4e8e\u65e5\u5fd7\u805a\u5408\u548c\u5206\u6790\u5de5\u5177\uff08\u5982 ELK\u3001Loki\u3001Grafana \u7b49\uff09\u5904\u7406\uff1a\n\n```yaml\nlogging:\n  level: \"INFO\"\n  json: true # \u542f\u7528 JSON \u683c\u5f0f\u8f93\u51fa\n  file: \"logs/app.log\"\n```\n\nJSON \u683c\u5f0f\u65e5\u5fd7\u5305\u542b\u4ee5\u4e0b\u5b57\u6bb5\uff1a\n\n- `text`: \u683c\u5f0f\u5316\u7684\u65e5\u5fd7\u6587\u672c\n- `record`: \u5b8c\u6574\u7684\u65e5\u5fd7\u8bb0\u5f55\u5bf9\u8c61\n  - `time`: \u65f6\u95f4\u6233\n  - `level`: \u65e5\u5fd7\u7ea7\u522b\n  - `message`: \u65e5\u5fd7\u6d88\u606f\n  - `name`: logger \u540d\u79f0\n  - `module`: \u6a21\u5757\u540d\n  - `function`: \u51fd\u6570\u540d\n  - `file`: \u6587\u4ef6\u540d\u548c\u8def\u5f84\n  - `line`: \u884c\u53f7\n  - `process`: \u8fdb\u7a0b\u4fe1\u606f\n  - `thread`: \u7ebf\u7a0b\u4fe1\u606f\n  - `exception`: \u5f02\u5e38\u4fe1\u606f\uff08\u5982\u679c\u6709\uff09\n\n#### \u65e5\u5fd7\u683c\u5f0f\u8bf4\u660e\n\n**Loguru \u683c\u5f0f\uff08\u63a8\u8350\uff09\uff1a**\n\n```python\nformat: \"{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}\"\n```\n\n**\u6807\u51c6 logging \u683c\u5f0f\uff08\u4f1a\u81ea\u52a8\u8f6c\u6362\uff09\uff1a**\n\n```python\nformat: \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n```\n\n#### \u9ad8\u7ea7\u529f\u80fd\n\n```python\nfrom loguru import logger\n\n# \u7ed3\u6784\u5316\u65e5\u5fd7\nlogger.info(\"\u7528\u6237\u767b\u5f55\", user_id=123, username=\"admin\")\n\n# \u5f02\u5e38\u65e5\u5fd7\uff08\u81ea\u52a8\u5305\u542b\u5806\u6808\u8ddf\u8e2a\uff09\ntry:\n    1 / 0\nexcept:\n    logger.exception(\"\u53d1\u751f\u9519\u8bef\")\n\n# \u7ed1\u5b9a\u4e0a\u4e0b\u6587\u4fe1\u606f\nlogger.bind(user_id=123).info(\"\u7528\u6237\u64cd\u4f5c\")\n\n# \u4e34\u65f6\u4fee\u6539\u65e5\u5fd7\u7ea7\u522b\nwith logger.contextualize(level=\"DEBUG\"):\n    logger.debug(\"\u8fd9\u662f\u8c03\u8bd5\u4fe1\u606f\")\n\n# \u6dfb\u52a0\u81ea\u5b9a\u4e49 handler\uff08\u4fdd\u7559\u7528\u6237\u81ea\u5b9a\u4e49 loguru \u7684\u80fd\u529b\uff09\nfrom loguru import logger\nlogger.add(\"custom.log\", rotation=\"100 MB\", retention=\"30 days\")\n```\n\n#### \u624b\u52a8\u521d\u59cb\u5316\uff08\u53ef\u9009\uff09\n\n\u5982\u679c\u9700\u8981\u624b\u52a8\u521d\u59cb\u5316\u65e5\u5fd7\u7cfb\u7edf\uff1a\n\n```python\nfrom myboot.core.logger import setup_logging\n\n# \u4f7f\u7528\u9ed8\u8ba4\u914d\u7f6e\u521d\u59cb\u5316\nsetup_logging()\n\n# \u4f7f\u7528\u6307\u5b9a\u914d\u7f6e\u6587\u4ef6\u521d\u59cb\u5316\nsetup_logging(\"custom_config.yaml\")\n```\n\n#### \u914d\u7f6e\u53c2\u6570\u8bf4\u660e\n\n| \u53c2\u6570                            | \u7c7b\u578b | \u8bf4\u660e                                                                 | \u9ed8\u8ba4\u503c          |\n| ------------------------------- | ---- | -------------------------------------------------------------------- | --------------- |\n| `logging.level`                 | str  | \u65e5\u5fd7\u7ea7\u522b (DEBUG/INFO/WARNING/ERROR/CRITICAL)                         | INFO            |\n| `logging.format`                | str  | \u65e5\u5fd7\u683c\u5f0f\uff08\u652f\u6301 loguru \u683c\u5f0f\u6216\u6807\u51c6 logging \u683c\u5f0f\uff0cjson \u4e3a true \u65f6\u5ffd\u7565\uff09 | loguru \u9ed8\u8ba4\u683c\u5f0f |\n| `logging.json`                  | bool | \u662f\u5426\u4f7f\u7528 JSON \u683c\u5f0f\u8f93\u51fa\uff08\u9002\u7528\u4e8e\u65e5\u5fd7\u805a\u5408\u548c\u5206\u6790\u5de5\u5177\uff09                   | false           |\n| `logging.file`                  | str  | \u65e5\u5fd7\u6587\u4ef6\u8def\u5f84\uff0c\u5982\u679c\u914d\u7f6e\u4f1a\u81ea\u52a8\u6dfb\u52a0\u6587\u4ef6 handler                         | \u65e0              |\n| `logging.third_party.{library}` | str  | \u7b2c\u4e09\u65b9\u5e93\u65e5\u5fd7\u7ea7\u522b\uff0c\u652f\u6301\u8bbe\u7f6e\u4efb\u610f\u7b2c\u4e09\u65b9\u5e93\u7684\u65e5\u5fd7\u7ea7\u522b                     | \u65e0              |\n\n#### \u6587\u4ef6\u65e5\u5fd7\u7279\u6027\n\n\u5982\u679c\u914d\u7f6e\u4e86 `logging.file`\uff0cloguru \u4f1a\u81ea\u52a8\u63d0\u4f9b\uff1a\n\n- **\u81ea\u52a8\u8f6e\u8f6c**: \u5f53\u65e5\u5fd7\u6587\u4ef6\u8fbe\u5230 10MB \u65f6\u81ea\u52a8\u8f6e\u8f6c\n- **\u81ea\u52a8\u538b\u7f29**: \u65e7\u65e5\u5fd7\u6587\u4ef6\u81ea\u52a8\u538b\u7f29\u4e3a zip\n- **\u81ea\u52a8\u6e05\u7406**: \u4fdd\u7559 7 \u5929\u7684\u65e5\u5fd7\u6587\u4ef6\n- **\u5f02\u5e38\u4fe1\u606f**: \u81ea\u52a8\u5305\u542b\u5b8c\u6574\u7684\u5806\u6808\u8ddf\u8e2a\n\n#### \u7b2c\u4e09\u65b9\u5e93\u65e5\u5fd7\u63a7\u5236\n\n\u901a\u8fc7 `logging.third_party` \u914d\u7f6e\u53ef\u4ee5\u63a7\u5236\u7b2c\u4e09\u65b9\u5e93\u7684\u65e5\u5fd7\u8f93\u51fa\u7ea7\u522b\uff1a\n\n```yaml\nlogging:\n  third_party:\n    urllib3: \"WARNING\" # \u9690\u85cf urllib3 \u7684 INFO \u548c DEBUG \u65e5\u5fd7\n    requests: \"WARNING\" # \u9690\u85cf requests \u7684 INFO \u548c DEBUG \u65e5\u5fd7\n    hypercorn: \"WARNING\" # \u9690\u85cf hypercorn \u7684 INFO \u548c DEBUG \u65e5\u5fd7\n    asyncio: \"INFO\" # \u53ea\u663e\u793a asyncio \u7684 INFO \u53ca\u4ee5\u4e0a\u7ea7\u522b\n```\n\n\u8fd9\u6837\u53ef\u4ee5\u6709\u6548\u51cf\u5c11\u7b2c\u4e09\u65b9\u5e93\u7684\u566a\u97f3\u65e5\u5fd7\uff0c\u8ba9\u65e5\u5fd7\u66f4\u52a0\u6e05\u6670\u3002\n\n### 5. \u4e2d\u95f4\u4ef6\n\nMyBoot \u652f\u6301\u901a\u8fc7\u88c5\u9970\u5668\u5b9a\u4e49\u4e2d\u95f4\u4ef6\uff0c\u4e2d\u95f4\u4ef6\u4f1a\u81ea\u52a8\u6ce8\u518c\uff1a\n\n```python\nfrom myboot.core.decorators import middleware\nfrom fastapi import Request\n\n@middleware(order=1, path_filter='/api/*')\ndef api_middleware(request: Request, next_handler):\n    \"\"\"API \u4e2d\u95f4\u4ef6 - \u53ea\u5904\u7406 /api/* \u8def\u5f84\"\"\"\n    # \u524d\u7f6e\u5904\u7406\n    print(f\"\u5904\u7406\u8bf7\u6c42: {request.method} {request.url}\")\n\n    # \u8c03\u7528\u4e0b\u4e00\u4e2a\u5904\u7406\u5668\n    response = next_handler(request)\n\n    # \u540e\u7f6e\u5904\u7406\n    print(f\"\u54cd\u5e94\u72b6\u6001: {response.status_code}\")\n    return response\n\n@middleware(order=2, methods=['POST', 'PUT'])\ndef post_middleware(request: Request, next_handler):\n    \"\"\"POST/PUT \u4e2d\u95f4\u4ef6 - \u53ea\u5904\u7406 POST \u548c PUT \u8bf7\u6c42\"\"\"\n    # \u53ef\u4ee5\u5728\u8fd9\u91cc\u6dfb\u52a0\u8bf7\u6c42\u9a8c\u8bc1\u3001\u65e5\u5fd7\u8bb0\u5f55\u7b49\n    return next_handler(request)\n```\n\n**\u4e2d\u95f4\u4ef6\u53c2\u6570\u8bf4\u660e\uff1a**\n\n- `order`: \u6267\u884c\u987a\u5e8f\uff0c\u6570\u5b57\u8d8a\u5c0f\u8d8a\u5148\u6267\u884c\uff08\u9ed8\u8ba4 0\uff09\n- `path_filter`: \u8def\u5f84\u8fc7\u6ee4\uff0c\u652f\u6301\u5b57\u7b26\u4e32\u3001\u5b57\u7b26\u4e32\u5217\u8868\u6216\u6b63\u5219\u8868\u8fbe\u5f0f\uff0c\u5982 `'/api/*'`, `['/api/*', '/admin/*']`\n- `methods`: HTTP \u65b9\u6cd5\u8fc7\u6ee4\uff0c\u5982 `['GET', 'POST']`\uff08\u9ed8\u8ba4 None\uff0c\u5904\u7406\u6240\u6709\u65b9\u6cd5\uff09\n- `condition`: \u6761\u4ef6\u51fd\u6570\uff0c\u63a5\u6536 request \u5bf9\u8c61\uff0c\u8fd4\u56de bool \u51b3\u5b9a\u662f\u5426\u6267\u884c\u4e2d\u95f4\u4ef6\n\n**\u6ce8\u610f**\uff1aCORS \u4e2d\u95f4\u4ef6\u53ef\u4ee5\u901a\u8fc7\u914d\u7f6e\u6587\u4ef6\u542f\u7528\uff0c\u65e0\u9700\u624b\u52a8\u6dfb\u52a0\u3002\n\n### 6. \u751f\u547d\u5468\u671f\u94a9\u5b50\n\n```python\nfrom myboot.core.application import create_app\n\napp = create_app(name=\"\u6211\u7684\u5e94\u7528\")\n\n# \u6dfb\u52a0\u542f\u52a8\u94a9\u5b50\ndef startup_hook():\n    \"\"\"\u5e94\u7528\u542f\u52a8\u65f6\u6267\u884c\"\"\"\n    print(\"\u5e94\u7528\u542f\u52a8\")\n\napp.add_startup_hook(startup_hook)\n\n# \u6dfb\u52a0\u5173\u95ed\u94a9\u5b50\ndef shutdown_hook():\n    \"\"\"\u5e94\u7528\u5173\u95ed\u65f6\u6267\u884c\"\"\"\n    print(\"\u5e94\u7528\u5173\u95ed\")\n\napp.add_shutdown_hook(shutdown_hook)\n```\n\n## \ud83d\udcc1 \u9879\u76ee\u7ed3\u6784\n\n### \u6807\u51c6\u9879\u76ee\u7ed3\u6784\uff08\u63a8\u8350\uff09\n\n\u4f7f\u7528 `myboot init` \u547d\u4ee4\u521b\u5efa\u7684\u6807\u51c6\u9879\u76ee\u7ed3\u6784\uff1a\n\n```\nmy-app/\n\u251c\u2500\u2500 main.py              # \u5e94\u7528\u5165\u53e3\uff08\u6839\u76ee\u5f55\uff09\n\u251c\u2500\u2500 pyproject.toml        # \u9879\u76ee\u914d\u7f6e\u6587\u4ef6\n\u251c\u2500\u2500 .gitignore           # Git \u5ffd\u7565\u6587\u4ef6\n\u251c\u2500\u2500 app/                  # \u5e94\u7528\u4ee3\u7801\n\u2502   \u251c\u2500\u2500 api/              # API \u8def\u7531\n\u2502   \u251c\u2500\u2500 service/          # \u4e1a\u52a1\u903b\u8f91\u5c42\n\u2502   \u251c\u2500\u2500 model/            # \u6570\u636e\u6a21\u578b\n\u2502   \u251c\u2500\u2500 jobs/             # \u5b9a\u65f6\u4efb\u52a1\n\u2502   \u2514\u2500\u2500 client/           # \u5ba2\u6237\u7aef\uff08\u7b2c\u4e09\u65b9API\u8c03\u7528\u7b49\uff09\n\u251c\u2500\u2500 conf/                 # \u914d\u7f6e\u6587\u4ef6\u76ee\u5f55\n\u2502   \u2514\u2500\u2500 config.yaml       # \u4e3b\u914d\u7f6e\u6587\u4ef6\n\u2514\u2500\u2500 tests/                # \u6d4b\u8bd5\u4ee3\u7801\n```\n\n### \u76ee\u5f55\u8bf4\u660e\n\n- **main.py**: \u5e94\u7528\u5165\u53e3\u6587\u4ef6\uff0c\u4f4d\u4e8e\u9879\u76ee\u6839\u76ee\u5f55\n- **app/api/**: API \u8def\u7531\u5c42\uff0c\u5b58\u653e\u6240\u6709\u8def\u7531\u5b9a\u4e49\n- **app/service/**: \u4e1a\u52a1\u903b\u8f91\u5c42\uff0c\u5b58\u653e\u4e1a\u52a1\u670d\u52a1\u7c7b\n- **app/model/**: \u6570\u636e\u6a21\u578b\u5c42\uff0c\u5b58\u653e Pydantic \u6a21\u578b\u7b49\n- **app/jobs/**: \u5b9a\u65f6\u4efb\u52a1\uff0c\u5b58\u653e\u4f7f\u7528 `@cron`\u3001`@interval` \u7b49\u88c5\u9970\u5668\u7684\u4efb\u52a1\n- **app/client/**: \u5ba2\u6237\u7aef\u5c42\uff0c\u5b58\u653e\u7b2c\u4e09\u65b9\u670d\u52a1\u5ba2\u6237\u7aef\uff08\u5982 Redis\u3001HTTP \u5ba2\u6237\u7aef\u7b49\uff09\n- **conf/**: \u914d\u7f6e\u6587\u4ef6\u76ee\u5f55\uff0c\u5b58\u653e YAML \u914d\u7f6e\u6587\u4ef6\n- **tests/**: \u6d4b\u8bd5\u4ee3\u7801\u76ee\u5f55\n\n## \ud83d\udd27 \u9ad8\u7ea7\u529f\u80fd\n\n### 1. \u81ea\u5b9a\u4e49\u4e2d\u95f4\u4ef6\n\n\u4f7f\u7528\u88c5\u9970\u5668\u5b9a\u4e49\u4e2d\u95f4\u4ef6\uff08\u63a8\u8350\u65b9\u5f0f\uff09\uff1a\n\n```python\nfrom myboot.core.decorators import middleware\nfrom fastapi import Request\n\n@middleware(order=1)\ndef custom_middleware(request: Request, next_handler):\n    \"\"\"\u81ea\u5b9a\u4e49\u4e2d\u95f4\u4ef6\"\"\"\n    # \u524d\u7f6e\u5904\u7406\n    print(f\"\u8bf7\u6c42: {request.method} {request.url}\")\n\n    # \u8c03\u7528\u4e0b\u4e00\u4e2a\u5904\u7406\u5668\n    response = next_handler(request)\n\n    # \u540e\u7f6e\u5904\u7406\n    print(f\"\u54cd\u5e94: {response.status_code}\")\n    return response\n```\n\n\u6216\u8005\u4f7f\u7528 FastAPI \u7684 BaseHTTPMiddleware\uff1a\n\n```python\nfrom myboot.web.middleware import Middleware\nfrom starlette.middleware.base import BaseHTTPMiddleware\nfrom fastapi import Request\n\nclass CustomMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request: Request, call_next):\n        # \u4e2d\u95f4\u4ef6\u903b\u8f91\n        response = await call_next(request)\n        return response\n\n# \u5728\u5e94\u7528\u521d\u59cb\u5316\u65f6\u6dfb\u52a0\napp.add_middleware(Middleware(CustomMiddleware))\n```\n\n### 2. \u5f02\u6b65\u4efb\u52a1\n\n\u5728 REST API \u4e2d\u4f7f\u7528\u5f02\u6b65\u4efb\u52a1\uff0c\u907f\u514d\u963b\u585e\u8bf7\u6c42\u54cd\u5e94\u3002\n\n#### \u5feb\u901f\u542f\u52a8\u540e\u53f0\u4efb\u52a1\n\n```python\nfrom myboot.core.decorators import post\nfrom myboot.utils.async_utils import asyn_run\nimport time\n\ndef process_data(data: dict):\n    \"\"\"\u8017\u65f6\u7684\u6570\u636e\u5904\u7406\u4efb\u52a1\"\"\"\n    print(f\"\u5f00\u59cb\u5904\u7406\u6570\u636e: {data}\")\n    time.sleep(5)  # \u6a21\u62df\u8017\u65f6\u64cd\u4f5c\n    print(f\"\u6570\u636e\u5904\u7406\u5b8c\u6210: {data}\")\n    return {\"processed\": True, \"data\": data}\n\n@post('/api/tasks')\ndef create_task(data: dict):\n    \"\"\"\u521b\u5efa\u5f02\u6b65\u4efb\u52a1 - \u7acb\u5373\u8fd4\u56de\uff0c\u4efb\u52a1\u5728\u540e\u53f0\u6267\u884c\"\"\"\n    asyn_run(process_data, data, task_name=\"\u6570\u636e\u5904\u7406\u4efb\u52a1\")\n    return {\"message\": \"\u4efb\u52a1\u5df2\u521b\u5efa\uff0c\u6b63\u5728\u540e\u53f0\u5904\u7406\"}\n```\n\n#### \u4f7f\u7528\u4efb\u52a1\u7ba1\u7406\u5668\n\n\u5bf9\u4e8e\u9700\u8981\u8ddf\u8e2a\u4efb\u52a1\u72b6\u6001\u7684\u573a\u666f\uff1a\n\n```python\nfrom myboot.core.decorators import post, get, rest_controller\nfrom myboot.jobs.manager import JobManager\nfrom myboot.jobs.job import FunctionJob\nimport threading\n\ndef generate_report(report_type: str):\n    \"\"\"\u751f\u6210\u62a5\u544a\u4efb\u52a1\"\"\"\n    import time\n    print(f\"\u5f00\u59cb\u751f\u6210 {report_type} \u62a5\u544a\")\n    time.sleep(10)  # \u6a21\u62df\u62a5\u544a\u751f\u6210\n    return {\"type\": report_type, \"status\": \"completed\"}\n\n@rest_controller('/api/reports')\nclass ReportController:\n    \"\"\"\u62a5\u544a\u63a7\u5236\u5668\"\"\"\n\n    def __init__(self):\n        self.job_manager = JobManager()\n\n    @post('/generate')\n    def create_report(self, report_type: str):\n        \"\"\"\u521b\u5efa\u62a5\u544a\u751f\u6210\u4efb\u52a1\"\"\"\n        # \u521b\u5efa\u4efb\u52a1\n        job = FunctionJob(\n            func=generate_report,\n            name=f\"\u751f\u6210{report_type}\u62a5\u544a\",\n            args=(report_type,),\n            timeout=300  # 5\u5206\u949f\u8d85\u65f6\n        )\n\n        # \u6dfb\u52a0\u5230\u4efb\u52a1\u7ba1\u7406\u5668\u5e76\u6267\u884c\n        job_id = self.job_manager.add_job(job)\n        thread = threading.Thread(target=job.execute)\n        thread.daemon = True\n        thread.start()\n\n        return {\"message\": \"\u62a5\u544a\u751f\u6210\u4efb\u52a1\u5df2\u521b\u5efa\", \"job_id\": job_id}\n\n    @get('/status/{job_id}')\n    def get_status(self, job_id: str):\n        \"\"\"\u67e5\u8be2\u4efb\u52a1\u72b6\u6001\"\"\"\n        job_info = self.job_manager.get_job_info(job_id)\n        return job_info if job_info else {\"error\": \"\u4efb\u52a1\u4e0d\u5b58\u5728\"}\n```\n\n\u66f4\u591a\u8be6\u7ec6\u5185\u5bb9\u8bf7\u53c2\u8003 [REST API \u5f02\u6b65\u4efb\u52a1\u6587\u6863](docs/rest-api-async-tasks.md)\u3002\n\n### 3. \u4efb\u52a1\u7ba1\u7406\n\n#### \u8c03\u5ea6\u5668\u4efb\u52a1\u7ba1\u7406\n\n```python\n# \u83b7\u53d6\u8c03\u5ea6\u5668\u914d\u7f6e\nconfig = app.scheduler.get_config()\nprint(config)  # {'enabled': True, 'timezone': 'Asia/Shanghai', 'running': True, 'job_count': 3}\n\n# \u5217\u51fa\u6240\u6709\u4efb\u52a1\njobs = app.scheduler.list_all_jobs()\nfor job in jobs:\n    print(f\"\u4efb\u52a1ID: {job['job_id']}, \u7c7b\u578b: {job['type']}, \u51fd\u6570: {job['func_name']}\")\n\n# \u83b7\u53d6\u5355\u4e2a\u4efb\u52a1\u4fe1\u606f\njob_info = app.scheduler.get_job_info('cron_heartbeat')\nif job_info:\n    print(f\"Cron\u8868\u8fbe\u5f0f: {job_info.get('cron')}\")\n    print(f\"\u662f\u5426\u5df2\u6267\u884c: {job_info.get('executed', False)}\")\n    print(f\"\u662f\u5426\u5df2\u8fc7\u671f: {job_info.get('expired', False)}\")\n\n# \u68c0\u67e5\u8c03\u5ea6\u5668\u662f\u5426\u542f\u7528\nif app.scheduler.is_enabled():\n    print(\"\u8c03\u5ea6\u5668\u5df2\u542f\u7528\")\n```\n\n#### \u4efb\u52a1\u7ba1\u7406\u5668\n\n```python\nfrom myboot.jobs.manager import JobManager\nfrom myboot.jobs.job import FunctionJob\nimport threading\n\n# \u83b7\u53d6\u4efb\u52a1\u7ba1\u7406\u5668\uff08\u5355\u4f8b\u6a21\u5f0f\uff09\njob_manager = JobManager()\n\n# \u6dfb\u52a0\u4efb\u52a1\ndef my_task(data: dict):\n    \"\"\"\u4efb\u52a1\u51fd\u6570\"\"\"\n    print(f\"\u5904\u7406\u6570\u636e: {data}\")\n    return {\"status\": \"completed\", \"data\": data}\n\njob = FunctionJob(func=my_task, name=\"my_task\", args=({\"key\": \"value\"},))\njob_id = job_manager.add_job(job)\n\n# \u6267\u884c\u4efb\u52a1\uff08\u6839\u636e\u4efb\u52a1ID\uff09\nresult = job_manager.execute_job(job_id, {\"key\": \"value\"})\n\n# \u6216\u8005\u6839\u636e\u540d\u79f0\u6267\u884c\u4efb\u52a1\nresult = job_manager.execute_job_by_name(\"my_task\", {\"key\": \"value\"})\n\n# \u83b7\u53d6\u4efb\u52a1\u72b6\u6001\uff08\u9700\u8981\u4efb\u52a1ID\uff09\nstatus = job_manager.get_job_status(job_id)\n\n# \u83b7\u53d6\u4efb\u52a1\u4fe1\u606f\uff08\u9700\u8981\u4efb\u52a1ID\uff09\njob_info = job_manager.get_job_info(job_id)\n\n# \u5217\u51fa\u6240\u6709\u4efb\u52a1\u4fe1\u606f\nall_jobs = job_manager.get_all_job_info()\n\n# \u83b7\u53d6\u4efb\u52a1\u7edf\u8ba1\nstatistics = job_manager.get_job_statistics()\n\n# \u5728\u540e\u53f0\u6267\u884c\u4efb\u52a1\nthread = threading.Thread(target=job.execute)\nthread.daemon = True\nthread.start()\n```\n\n#### \u4efb\u52a1\u7279\u6027\n\n- **\u8fc7\u671f\u4efb\u52a1\u5904\u7406**: \u4e00\u6b21\u6027\u4efb\u52a1\u5982\u679c\u65f6\u95f4\u5df2\u8fc7\u671f\uff0c\u5c06\u81ea\u52a8\u6807\u8bb0\u4e3a\u8fc7\u671f\u4e0d\u518d\u6267\u884c\n- **\u5df2\u6267\u884c\u4efb\u52a1**: \u4e00\u6b21\u6027\u4efb\u52a1\u6267\u884c\u540e\u4e0d\u4f1a\u91cd\u590d\u6267\u884c\n- **\u65f6\u533a\u652f\u6301**: \u652f\u6301\u914d\u7f6e\u65f6\u533a\uff08\u9700\u8981\u5b89\u88c5 pytz\uff09\n- **\u4efb\u52a1\u72b6\u6001\u67e5\u8be2**: \u53ef\u4ee5\u67e5\u8be2\u4efb\u52a1\u7684\u6267\u884c\u72b6\u6001\u3001\u662f\u5426\u8fc7\u671f\u7b49\u4fe1\u606f\n\n## \ud83d\udcda \u793a\u4f8b\u5e94\u7528\n\n- **\u57fa\u7840\u793a\u4f8b** (`examples/convention_app.py`): \u5c55\u793a\u57fa\u672c\u529f\u80fd\n- **\u4f9d\u8d56\u6ce8\u5165\u793a\u4f8b** (`examples/dependency_injection_example.py`): \u5c55\u793a\u4f9d\u8d56\u6ce8\u5165\u529f\u80fd\n\n## \ud83e\udd1d \u8d21\u732e\n\n\u6b22\u8fce\u8d21\u732e\u4ee3\u7801\uff01\u8bf7\u67e5\u770b [CONTRIBUTING.md](CONTRIBUTING.md) \u4e86\u89e3\u5982\u4f55\u53c2\u4e0e\u3002\n\n## \ud83d\udcc4 \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 Apache-2.0 license \u8bb8\u53ef\u8bc1\u3002\u67e5\u770b [LICENSE](LICENSE) \u6587\u4ef6\u4e86\u89e3\u8be6\u60c5\u3002\n\n## \ud83d\ude4f \u81f4\u8c22\n\n\u611f\u8c22\u4ee5\u4e0b\u5f00\u6e90\u9879\u76ee\uff1a\n\n- [FastAPI](https://fastapi.tiangolo.com/) - \u73b0\u4ee3\u3001\u5feb\u901f\u7684 Web \u6846\u67b6\n- [APScheduler](https://apscheduler.readthedocs.io/) - Python \u4efb\u52a1\u8c03\u5ea6\u5e93\n- [Pydantic](https://pydantic-docs.helpmanual.io/) - \u6570\u636e\u9a8c\u8bc1\u5e93\n- [Loguru](https://github.com/Delgan/loguru) - \u73b0\u4ee3\u3001\u5f3a\u5927\u7684\u65e5\u5fd7\u5e93\n\n## \ud83d\udcde \u652f\u6301\n\n\u5982\u679c\u60a8\u9047\u5230\u95ee\u9898\u6216\u6709\u5efa\u8bae\uff0c\u8bf7\uff1a\n\n1. \u67e5\u770b [\u6587\u6863](https://github.com/TrumanDu/myboot)\n2. \u641c\u7d22 [Issues](https://github.com/TrumanDu/myboot/issues)\n3. \u521b\u5efa\u65b0\u7684 [Issue](https://github.com/TrumanDu/myboot/issues/new)\n\n---\n\n**MyBoot** - \u8ba9\u4f01\u4e1a\u7ea7\u5e94\u7528\u5f00\u53d1\u66f4\u7b80\u5355\u3001\u66f4\u5feb\u901f\uff01\n\ud83d\ude80\n\n\u8981\u89e3\u51b3\u7684\u95ee\u9898\n\n1.  [x] \u914d\u7f6e\u6587\u4ef6\n2.  [x] \u65e5\u5fd7\u95ee\u9898\n3.  [x] web \u5feb\u901f\u5f00\u53d1\u6846\u67b6\n4.  [x] \u81ea\u52a8\u6ce8\u5165\n5.  [x] \u5f02\u6b65\u4efb\u52a1\n6.  [x] job \u7ba1\u7406\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "\u7c7b\u4f3c Spring Boot \u7684 Python \u5feb\u901f\u5f00\u53d1\u6846\u67b6",
    "version": "0.1.1",
    "project_urls": null,
    "split_keywords": [
        "api",
        " config",
        " framework",
        " logging",
        " scheduler",
        " web"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "23eceed2f167306c7ded27bfef699de37f99d62b905adc91a7b7ce59451848b8",
                "md5": "33743cd465dbf3404bbc40f10c900846",
                "sha256": "3a36917ae6e5213fe1e85800f4d067dc50e6f529b4b99f5bad29caa766a1e3f7"
            },
            "downloads": -1,
            "filename": "myboot-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "33743cd465dbf3404bbc40f10c900846",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 78303,
            "upload_time": "2025-11-08T07:27:38",
            "upload_time_iso_8601": "2025-11-08T07:27:38.752557Z",
            "url": "https://files.pythonhosted.org/packages/23/ec/eed2f167306c7ded27bfef699de37f99d62b905adc91a7b7ce59451848b8/myboot-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "15a3f778e7389351a96cdd6ce71e11b0afe90c0937bfa76b1f2b5a3e11fab761",
                "md5": "da15e5719182e8642feddea5d9a839b8",
                "sha256": "bc3930fb64841315899bd82871fd52ee164334ef5c71b49c47474a677a61196a"
            },
            "downloads": -1,
            "filename": "myboot-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "da15e5719182e8642feddea5d9a839b8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 75043,
            "upload_time": "2025-11-08T07:27:40",
            "upload_time_iso_8601": "2025-11-08T07:27:40.314259Z",
            "url": "https://files.pythonhosted.org/packages/15/a3/f778e7389351a96cdd6ce71e11b0afe90c0937bfa76b1f2b5a3e11fab761/myboot-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-08 07:27:40",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "myboot"
}
        
Elapsed time: 1.34725s