尝试提交
This commit is contained in:
commit
e8b2a9a99c
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
12
.idea/inspectionProfiles/Project_Default.xml
Normal file
12
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="pathlib.Path.__add__" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
4
.idea/misc.xml
Normal file
4
.idea/misc.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (wms-py)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/wms-py.iml" filepath="$PROJECT_DIR$/.idea/wms-py.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/wms-py.iml
Normal file
10
.idea/wms-py.iml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
258
README.md
Normal file
258
README.md
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
# WMS FastAPI 项目
|
||||
|
||||
这是一个基于 FastAPI 的仓库管理系统(WMS),采用标准的项目结构,支持多环境配置。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
wms-py/
|
||||
├── app/ # 应用核心代码
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # 主应用文件
|
||||
│ ├── config/ # 配置模块
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── settings.py # 配置管理
|
||||
│ ├── controllers/ # 控制器(路由)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── base_controller.py # 基础接口
|
||||
│ │ ├── database_controller.py # 数据库测试接口
|
||||
│ │ └── location_controller.py # 库位管理接口 🆕
|
||||
│ ├── services/ # 业务服务层 🆕
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── location_service.py # 库位业务逻辑
|
||||
│ ├── models/ # 数据模型 🆕
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── location.py # 库位数据模型
|
||||
│ ├── schemas/ # Pydantic模式 🆕
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── common.py # 通用响应模式
|
||||
│ │ └── location.py # 库位请求/响应模式
|
||||
│ └── utils/ # 工具类
|
||||
│ ├── __init__.py
|
||||
│ ├── database.py # 数据库工具类
|
||||
│ └── string_utils.py # 字符串工具 🆕
|
||||
├── config/ # 环境配置文件
|
||||
│ ├── development.yaml # 开发环境配置
|
||||
│ └── production.yaml # 生产环境配置
|
||||
├── requirements.txt # 依赖包列表
|
||||
├── run.py # 启动脚本
|
||||
├── create_tables.py # 数据库表创建脚本 🆕
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 多环境配置支持 (开发/生产)
|
||||
- ✅ YAML 配置文件
|
||||
- ✅ 数据库连接管理
|
||||
- ✅ 标准项目结构
|
||||
- ✅ API 文档自动生成
|
||||
- ✅ 数据库测试接口
|
||||
- ✅ 健康检查接口
|
||||
- ✅ **库位管理模块** 🆕
|
||||
- 批量生成库位
|
||||
- 查询已使用库位数量
|
||||
- 库位信息查询
|
||||
- 库位统计功能
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. 配置数据库
|
||||
|
||||
根据需要修改配置文件:
|
||||
|
||||
**开发环境** (`config/development.yaml`):
|
||||
```yaml
|
||||
database:
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
username: "root"
|
||||
password: "root"
|
||||
database: "wms_ntyc"
|
||||
```
|
||||
|
||||
### 3. 创建数据库表
|
||||
|
||||
```bash
|
||||
python create_tables.py
|
||||
```
|
||||
|
||||
### 4. 运行应用
|
||||
|
||||
```bash
|
||||
# 开发环境
|
||||
python run.py
|
||||
|
||||
# 生产环境
|
||||
set ENVIRONMENT=production && python run.py
|
||||
```
|
||||
|
||||
## API接口
|
||||
|
||||
### 基础接口
|
||||
- `GET /` - 系统信息
|
||||
- `GET /health` - 健康检查
|
||||
- `GET /config` - 配置信息
|
||||
|
||||
### 数据库测试接口
|
||||
- `GET /database/test` - 测试数据库连接
|
||||
- `GET /database/tables` - 获取所有表名
|
||||
- `GET /database/query?sql=SELECT * FROM table_name` - 执行SQL查询
|
||||
- `GET /database/table/{table_name}` - 查看指定表数据
|
||||
|
||||
### 库位管理接口 🆕
|
||||
|
||||
**库位生成**:
|
||||
- `POST /wms/location/genLocations` - 批量生成库位
|
||||
|
||||
请求示例:
|
||||
```json
|
||||
{
|
||||
"l_row": 5,
|
||||
"l_col": 8,
|
||||
"l_layer": 6,
|
||||
"l_depth": 2,
|
||||
"sub_area": "A",
|
||||
"area_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
**库位查询**:
|
||||
- `GET /wms/location/getUsedLocations?equipment_id=1&location_type=1` - 获取已使用库位数量
|
||||
- `GET /wms/location/location/{location_id}` - 根据ID查询库位信息
|
||||
- `GET /wms/location/locations/area/{area_id}` - 根据区域查询库位列表
|
||||
- `GET /wms/location/locations/stats` - 获取库位统计信息
|
||||
|
||||
### API 文档
|
||||
- `GET /docs` - Swagger UI 文档
|
||||
- `GET /redoc` - ReDoc 文档
|
||||
|
||||
## 库位管理功能详解
|
||||
|
||||
### 1. 库位生成逻辑
|
||||
|
||||
库位ID格式:`{子区域}{行号02d}-{列号02d}-{层号02d}-{深度02d}`
|
||||
|
||||
例如:
|
||||
- 子区域: A
|
||||
- 行: 1, 列: 2, 层: 3, 深度: 1
|
||||
- 生成ID: `A01-02-03-01`
|
||||
|
||||
### 2. 数据库表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE t_app_location (
|
||||
location_id VARCHAR(50) PRIMARY KEY COMMENT '库位ID',
|
||||
location_name VARCHAR(100) DEFAULT '' COMMENT '库位名称',
|
||||
location_type INT DEFAULT 1 COMMENT '库位类型',
|
||||
is_occupy INT DEFAULT 0 COMMENT '是否占用 0-未占用 1-已占用',
|
||||
is_enable INT DEFAULT 0 COMMENT '是否启用',
|
||||
equipment_id INT DEFAULT 0 COMMENT '设备ID',
|
||||
aisle_num_left INT COMMENT '左侧巷道号',
|
||||
aisle_num_right INT COMMENT '右侧巷道号',
|
||||
l_row INT COMMENT '行',
|
||||
l_col INT COMMENT '列',
|
||||
l_layer INT COMMENT '层',
|
||||
l_depth INT COMMENT '深度',
|
||||
remark TEXT DEFAULT '' COMMENT '备注',
|
||||
area_id INT DEFAULT 1 COMMENT '区域ID',
|
||||
sub_area VARCHAR(50) DEFAULT '' COMMENT '子区域'
|
||||
);
|
||||
```
|
||||
|
||||
### 3. 使用示例
|
||||
|
||||
**生成库位**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:12315/wms/location/genLocations" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"l_row": 2,
|
||||
"l_col": 3,
|
||||
"l_layer": 4,
|
||||
"l_depth": 1,
|
||||
"sub_area": "A",
|
||||
"area_id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
**查询统计**:
|
||||
```bash
|
||||
curl "http://localhost:12315/wms/location/locations/stats"
|
||||
```
|
||||
|
||||
## 项目架构说明
|
||||
|
||||
### 分层架构
|
||||
|
||||
1. **Controller层** (`app/controllers/`)
|
||||
- 处理HTTP请求
|
||||
- 参数验证
|
||||
- 路由定义
|
||||
|
||||
2. **Service层** (`app/services/`)
|
||||
- 业务逻辑处理
|
||||
- 事务管理
|
||||
- 数据操作
|
||||
|
||||
3. **Model层** (`app/models/`)
|
||||
- 数据库表映射
|
||||
- ORM模型定义
|
||||
|
||||
4. **Schema层** (`app/schemas/`)
|
||||
- 请求/响应数据验证
|
||||
- API文档生成
|
||||
|
||||
### 依赖注入
|
||||
|
||||
项目使用FastAPI的依赖注入系统:
|
||||
- 数据库会话注入
|
||||
- 服务类注入
|
||||
- 配置注入
|
||||
|
||||
### 对应关系
|
||||
|
||||
| Java概念 | Python/FastAPI概念 | 文件位置 |
|
||||
|---------|-------------------|----------|
|
||||
| @RestController | APIRouter | `app/controllers/` |
|
||||
| @Service | Service类 | `app/services/` |
|
||||
| @Entity | SQLAlchemy Model | `app/models/` |
|
||||
| DTO | Pydantic Schema | `app/schemas/` |
|
||||
| @Autowired | Depends() | 依赖注入 |
|
||||
|
||||
## IDE 配置
|
||||
|
||||
### PyCharm 配置
|
||||
|
||||
创建运行配置:
|
||||
- **Name**: `WMS Development`
|
||||
- **Script path**: `run.py`
|
||||
- **Environment variables**: `ENVIRONMENT=development`
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保数据库服务已启动
|
||||
2. 首次运行前执行 `python create_tables.py` 创建表
|
||||
3. 修改配置文件后需要重启应用
|
||||
4. 库位生成前请确认参数,避免重复数据
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **表不存在**
|
||||
- 运行 `python create_tables.py` 创建表
|
||||
|
||||
2. **库位ID重复**
|
||||
- 检查参数是否与已有数据冲突
|
||||
- 清理测试数据后重新生成
|
||||
|
||||
3. **数据库连接失败**
|
||||
- 检查配置文件中的数据库信息
|
||||
- 确认数据库服务状态
|
||||
3
app/__init__.py
Normal file
3
app/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .config import settings
|
||||
|
||||
# WMS FastAPI 应用包
|
||||
85
app/config.py
Normal file
85
app/config.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
from pydantic import BaseModel, computed_field, Field
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class DatabaseConfig(BaseModel):
|
||||
driver: str = "mysql+pymysql"
|
||||
host: str
|
||||
port: int = 3306
|
||||
name: str
|
||||
username: str
|
||||
password: str
|
||||
params: str = "characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&rewriteBatchedStatements=true"
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def url(self) -> str:
|
||||
"""生成数据库连接 URL"""
|
||||
return f"{self.driver}://{self.username}:{self.password}@{self.host}:{self.port}/{self.name}?{self.params}"
|
||||
|
||||
|
||||
class ServerConfig(BaseModel):
|
||||
port: int
|
||||
context_path: str = "/"
|
||||
|
||||
|
||||
class AppConfig(BaseModel):
|
||||
name: str = "wms_main"
|
||||
max_file_size: int = 100 * 1024 * 1024 # 100MB
|
||||
max_request_size: int = 1000 * 1024 * 1024 # 1000MB
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
"""全局配置模型"""
|
||||
app: AppConfig
|
||||
server: ServerConfig
|
||||
database: DatabaseConfig
|
||||
env: str = Field(default="development") # 当前环境名称
|
||||
|
||||
@classmethod
|
||||
def from_env(cls):
|
||||
"""从环境变量加载配置"""
|
||||
env_name = os.getenv("ENV", "development")
|
||||
|
||||
# 获取项目根目录
|
||||
base_dir = Path(__file__).resolve().parent.parent
|
||||
|
||||
# 加载基础配置
|
||||
base_path = base_dir / "config" / "base.yaml"
|
||||
base_data = {}
|
||||
if base_path.exists():
|
||||
with open(base_path, "r") as f:
|
||||
base_data = yaml.safe_load(f) or {}
|
||||
|
||||
# 加载环境特定配置
|
||||
env_path = base_dir / "config" / f"{env_name}.yaml"
|
||||
env_data = {}
|
||||
if env_path.exists():
|
||||
with open(env_path, "r") as f:
|
||||
env_data = yaml.safe_load(f) or {}
|
||||
|
||||
# 深度合并配置
|
||||
def deep_merge(base: dict, update: dict) -> dict:
|
||||
"""递归合并两个字典"""
|
||||
for key, value in update.items():
|
||||
if isinstance(value, dict) and key in base and isinstance(base[key], dict):
|
||||
base[key] = deep_merge(base[key], value)
|
||||
else:
|
||||
base[key] = value
|
||||
return base
|
||||
|
||||
# 合并配置
|
||||
merged_config = deep_merge(base_data, env_data)
|
||||
merged_config["env"] = env_name # 添加环境名称
|
||||
|
||||
return cls(**merged_config)
|
||||
|
||||
|
||||
# 创建全局配置对象
|
||||
settings = Settings.from_env()
|
||||
1
app/config/__init__.py
Normal file
1
app/config/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# 配置包
|
||||
71
app/config/settings.py
Normal file
71
app/config/settings.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import os
|
||||
import yaml
|
||||
from typing import Dict, Any
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class DatabaseConfig(BaseModel):
|
||||
"""数据库配置"""
|
||||
host: str
|
||||
port: int = 3306
|
||||
username: str
|
||||
password: str
|
||||
database: str
|
||||
charset: str = "utf8mb4"
|
||||
|
||||
|
||||
class AppConfig(BaseModel):
|
||||
"""应用配置"""
|
||||
name: str
|
||||
debug: bool = False
|
||||
host: str = "localhost"
|
||||
port: int = 12315
|
||||
database: DatabaseConfig
|
||||
|
||||
|
||||
class Settings:
|
||||
"""配置管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self._config: AppConfig = None
|
||||
self._env = os.getenv("ENVIRONMENT", "development")
|
||||
self.load_config()
|
||||
|
||||
def load_config(self):
|
||||
"""加载配置文件"""
|
||||
config_file = f"config/{self._env}.yaml"
|
||||
|
||||
if not os.path.exists(config_file):
|
||||
raise FileNotFoundError(f"配置文件不存在: {config_file}")
|
||||
|
||||
with open(config_file, 'r', encoding='utf-8') as f:
|
||||
config_data = yaml.safe_load(f)
|
||||
|
||||
# 解析数据库配置
|
||||
db_config = DatabaseConfig(**config_data['database'])
|
||||
|
||||
# 解析应用配置
|
||||
app_data = config_data['app']
|
||||
app_data['database'] = db_config
|
||||
|
||||
self._config = AppConfig(**app_data)
|
||||
|
||||
@property
|
||||
def config(self) -> AppConfig:
|
||||
"""获取配置"""
|
||||
return self._config
|
||||
|
||||
@property
|
||||
def environment(self) -> str:
|
||||
"""获取当前环境"""
|
||||
return self._env
|
||||
|
||||
@property
|
||||
def database_url(self) -> str:
|
||||
"""获取数据库连接URL"""
|
||||
db = self._config.database
|
||||
return f"mysql+pymysql://{db.username}:{db.password}@{db.host}:{db.port}/{db.database}?charset={db.charset}"
|
||||
|
||||
|
||||
# 全局配置实例
|
||||
settings = Settings()
|
||||
1
app/controllers/__init__.py
Normal file
1
app/controllers/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# 控制器包
|
||||
37
app/controllers/base_controller.py
Normal file
37
app/controllers/base_controller.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from fastapi import APIRouter
|
||||
from app.config.settings import settings
|
||||
|
||||
router = APIRouter(tags=["基础接口"])
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def root():
|
||||
"""根路径"""
|
||||
return {
|
||||
"message": "WMS FastAPI 系统",
|
||||
"app_name": settings.config.name,
|
||||
"environment": settings.environment,
|
||||
"debug": settings.config.debug,
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
async def get_config():
|
||||
"""获取配置信息"""
|
||||
return {
|
||||
"app": {
|
||||
"name": settings.config.name,
|
||||
"debug": settings.config.debug,
|
||||
"host": settings.config.host,
|
||||
"port": settings.config.port
|
||||
},
|
||||
"environment": settings.environment,
|
||||
"database": {
|
||||
"host": settings.config.database.host,
|
||||
"port": settings.config.database.port,
|
||||
"database": settings.config.database.database,
|
||||
"username": settings.config.database.username,
|
||||
"charset": settings.config.database.charset
|
||||
}
|
||||
}
|
||||
44
app/controllers/database_controller.py
Normal file
44
app/controllers/database_controller.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from app.utils.database import get_db, DatabaseUtils
|
||||
from typing import Optional
|
||||
|
||||
router = APIRouter(prefix="/database", tags=["数据库测试"])
|
||||
|
||||
|
||||
@router.get("/test")
|
||||
async def test_database_connection():
|
||||
"""测试数据库连接"""
|
||||
return DatabaseUtils.test_connection()
|
||||
|
||||
|
||||
@router.get("/tables")
|
||||
async def list_all_tables():
|
||||
"""获取所有表名"""
|
||||
return DatabaseUtils.get_all_tables()
|
||||
|
||||
|
||||
@router.get("/query")
|
||||
async def execute_query(
|
||||
sql: str = Query(..., description="要执行的SQL查询语句"),
|
||||
limit: int = Query(100, description="返回记录数限制", ge=1, le=1000)
|
||||
):
|
||||
"""执行SQL查询"""
|
||||
return DatabaseUtils.execute_query(sql, limit)
|
||||
|
||||
|
||||
@router.get("/table/{table_name}")
|
||||
async def list_table_data(
|
||||
table_name: str,
|
||||
limit: int = Query(100, description="返回记录数限制", ge=1, le=1000)
|
||||
):
|
||||
"""查看指定表的数据"""
|
||||
sql = f"SELECT * FROM {table_name}"
|
||||
return DatabaseUtils.execute_query(sql, limit)
|
||||
|
||||
|
||||
@router.get("/table/{table_name}/structure")
|
||||
async def get_table_structure(table_name: str):
|
||||
"""获取表结构"""
|
||||
sql = f"DESCRIBE {table_name}"
|
||||
return DatabaseUtils.execute_query(sql, 1000)
|
||||
31
app/controllers/location_controller.py
Normal file
31
app/controllers/location_controller.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from fastapi import APIRouter, Depends, Query, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
from app.utils.database import get_db
|
||||
from app.services.location_service import LocationService
|
||||
from app.schemas.location import LocationQueryRequest
|
||||
from app.schemas.common import BaseWmsApiResponse, WmsApiResponse
|
||||
from app.models.location import TAppLocation
|
||||
|
||||
router = APIRouter(prefix="/wms/location", tags=["库位管理"])
|
||||
|
||||
|
||||
def get_location_service(db: Session = Depends(get_db)) -> LocationService:
|
||||
return LocationService(db)
|
||||
|
||||
|
||||
@router.post("/genLocations", response_model=BaseWmsApiResponse)
|
||||
async def gen_locations(
|
||||
location_query: LocationQueryRequest,
|
||||
location_service: LocationService = Depends(get_location_service)
|
||||
) -> BaseWmsApiResponse:
|
||||
return location_service.gen_locations(location_query)
|
||||
|
||||
|
||||
@router.get("/getUsedLocations", response_model=WmsApiResponse)
|
||||
async def get_used_locations(
|
||||
equipment_id: int = Query(..., description="设备ID"),
|
||||
location_type: int = Query(..., description="库位类型"),
|
||||
location_service: LocationService = Depends(get_location_service)
|
||||
) -> WmsApiResponse:
|
||||
return location_service.get_used_locations(equipment_id, location_type)
|
||||
50
app/main.py
Normal file
50
app/main.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
from fastapi import FastAPI
|
||||
from app.config.settings import settings
|
||||
from app.utils.database import init_database, close_database
|
||||
from app.controllers import base_controller, database_controller, location_controller
|
||||
|
||||
# 创建FastAPI应用
|
||||
app = FastAPI(
|
||||
title=settings.config.name,
|
||||
description="仓库管理系统 API",
|
||||
version="1.0.0",
|
||||
debug=settings.config.debug
|
||||
)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""应用启动事件"""
|
||||
print("=" * 50)
|
||||
print(f"🚀 启动 {settings.config.name}")
|
||||
print(f"📦 环境: {settings.environment}")
|
||||
print(f"🔧 调试模式: {settings.config.debug}")
|
||||
print(f"🌐 地址: http://{settings.config.host}:{settings.config.port}")
|
||||
print("=" * 50)
|
||||
|
||||
# 初始化数据库
|
||||
init_database()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""应用关闭事件"""
|
||||
print("🔄 正在关闭应用...")
|
||||
close_database()
|
||||
print("✅ 应用已关闭")
|
||||
|
||||
|
||||
# 注册路由
|
||||
app.include_router(base_controller.router)
|
||||
app.include_router(database_controller.router)
|
||||
app.include_router(location_controller.router)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host=settings.config.host,
|
||||
port=settings.config.port,
|
||||
reload=settings.config.debug
|
||||
)
|
||||
1
app/models/__init__.py
Normal file
1
app/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# 数据模型包
|
||||
45
app/models/location.py
Normal file
45
app/models/location.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from sqlalchemy import Column, String, Integer, Text
|
||||
from app.utils.database import Base
|
||||
|
||||
|
||||
class TAppLocation(Base):
|
||||
__tablename__ = "t_app_location"
|
||||
|
||||
location_id = Column(String(50), primary_key=True, comment="库位ID")
|
||||
location_name = Column(String(100), default="", comment="库位名称")
|
||||
location_type = Column(Integer, default=1, comment="库位类型")
|
||||
is_occupy = Column(Integer, default=0, comment="是否占用 0-未占用 1-已占用")
|
||||
is_enable = Column(Integer, default=0, comment="是否启用")
|
||||
equipment_id = Column(Integer, default=0, comment="设备ID")
|
||||
aisle_num_left = Column(Integer, comment="左侧巷道号")
|
||||
aisle_num_right = Column(Integer, comment="右侧巷道号")
|
||||
l_row = Column(Integer, comment="行")
|
||||
l_col = Column(Integer, comment="列")
|
||||
l_layer = Column(Integer, comment="层")
|
||||
l_depth = Column(Integer, comment="深度")
|
||||
remark = Column(Text, default="", comment="备注")
|
||||
area_id = Column(Integer, default=1, comment="区域ID")
|
||||
sub_area = Column(String(50), default="", comment="子区域")
|
||||
|
||||
def __init__(self, location_id=None, location_name="", location_type=1,
|
||||
is_occupy=0, is_enable=0, equipment_id=0, aisle_num_left=None,
|
||||
aisle_num_right=None, l_row=None, l_col=None, l_layer=None,
|
||||
l_depth=None, remark="", area_id=1, sub_area=""):
|
||||
self.location_id = location_id
|
||||
self.location_name = location_name
|
||||
self.location_type = location_type
|
||||
self.is_occupy = is_occupy
|
||||
self.is_enable = is_enable
|
||||
self.equipment_id = equipment_id
|
||||
self.aisle_num_left = aisle_num_left
|
||||
self.aisle_num_right = aisle_num_right
|
||||
self.l_row = l_row
|
||||
self.l_col = l_col
|
||||
self.l_layer = l_layer
|
||||
self.l_depth = l_depth
|
||||
self.remark = remark
|
||||
self.area_id = area_id
|
||||
self.sub_area = sub_area
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TAppLocation(location_id='{self.location_id}', location_name='{self.location_name}')>"
|
||||
1
app/schemas/__init__.py
Normal file
1
app/schemas/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Pydantic模式包
|
||||
41
app/schemas/common.py
Normal file
41
app/schemas/common.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from pydantic import BaseModel
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class BaseWmsApiResponse(BaseModel):
|
||||
code: int
|
||||
message: str
|
||||
|
||||
@staticmethod
|
||||
def success(message: str = "操作成功"):
|
||||
return BaseWmsApiResponse(
|
||||
code=200,
|
||||
message=message
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def error(message: str = "操作失败", code: int = 500):
|
||||
return BaseWmsApiResponse(
|
||||
code=code,
|
||||
message=message
|
||||
)
|
||||
|
||||
|
||||
class WmsApiResponse(BaseWmsApiResponse):
|
||||
data: Optional[Any] = None
|
||||
|
||||
@staticmethod
|
||||
def success(data: Any = None, message: str = "操作成功"):
|
||||
return WmsApiResponse(
|
||||
code=200,
|
||||
message=message,
|
||||
data=data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def error(message: str = "操作失败", code: int = 500, data: Any = None):
|
||||
return WmsApiResponse(
|
||||
code=code,
|
||||
message=message,
|
||||
data=data
|
||||
)
|
||||
23
app/schemas/location.py
Normal file
23
app/schemas/location.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class LocationQueryRequest(BaseModel):
|
||||
l_row: int = Field(..., description="行数", ge=1, le=100)
|
||||
l_col: int = Field(..., description="列数", ge=1, le=100)
|
||||
l_layer: int = Field(..., description="层数", ge=1, le=50)
|
||||
l_depth: int = Field(..., description="深度", ge=1, le=50)
|
||||
sub_area: str = Field(..., description="子区域标识", min_length=1, max_length=10)
|
||||
area_id: Optional[int] = Field(1, description="区域ID")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"l_row": 5,
|
||||
"l_col": 8,
|
||||
"l_layer": 6,
|
||||
"l_depth": 2,
|
||||
"sub_area": "A",
|
||||
"area_id": 1
|
||||
}
|
||||
}
|
||||
1
app/services/__init__.py
Normal file
1
app/services/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# 业务服务包
|
||||
89
app/services/location_service.py
Normal file
89
app/services/location_service.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from typing import List
|
||||
import logging
|
||||
|
||||
from app.models.location import TAppLocation
|
||||
from app.schemas.location import LocationQueryRequest
|
||||
from app.schemas.common import BaseWmsApiResponse, WmsApiResponse
|
||||
from app.utils.string_utils import pad_left
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocationService:
|
||||
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
|
||||
def gen_locations(self, location_query: LocationQueryRequest) -> BaseWmsApiResponse:
|
||||
try:
|
||||
new_location_list: List[TAppLocation] = []
|
||||
|
||||
for row in range(1, location_query.l_row + 1):
|
||||
for col in range(1, location_query.l_col + 1):
|
||||
for layer in range(1, location_query.l_layer + 1):
|
||||
for depth in range(1, location_query.l_depth + 1):
|
||||
# 生成库位ID,格式:子区域 + 行(2位) + "-" + 列(2位) + "-" + 层(2位) + "-" + 深度(2位)
|
||||
location_id = (
|
||||
f"{location_query.sub_area}"
|
||||
f"{pad_left(str(row), '0', 2)}-"
|
||||
f"{pad_left(str(col), '0', 2)}-"
|
||||
f"{pad_left(str(layer), '0', 2)}-"
|
||||
f"{pad_left(str(depth), '0', 2)}"
|
||||
)
|
||||
|
||||
new_location = TAppLocation(
|
||||
location_id=location_id,
|
||||
location_name="",
|
||||
location_type=1,
|
||||
is_occupy=0,
|
||||
is_enable=0,
|
||||
equipment_id=0,
|
||||
aisle_num_left=(row + 1) // 2,
|
||||
aisle_num_right=(row + 1) // 2,
|
||||
l_row=row,
|
||||
l_col=col,
|
||||
l_layer=layer,
|
||||
l_depth=depth,
|
||||
remark="",
|
||||
area_id=location_query.area_id if location_query.area_id is not None else 1,
|
||||
sub_area=location_query.sub_area
|
||||
)
|
||||
new_location_list.append(new_location)
|
||||
|
||||
self.db.add_all(new_location_list)
|
||||
self.db.commit()
|
||||
|
||||
logger.info(f"成功创建了{len(new_location_list)}个库位")
|
||||
return BaseWmsApiResponse.success(f"成功创建了{len(new_location_list)}个库位。")
|
||||
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"创建库位失败: {str(e)}")
|
||||
return BaseWmsApiResponse.error(f"创建库位失败: {str(e)}")
|
||||
|
||||
def get_used_locations(self, equipment_id: int, location_type: int) -> WmsApiResponse:
|
||||
try:
|
||||
count = self.db.query(func.count(TAppLocation.location_id)).filter(
|
||||
TAppLocation.equipment_id == equipment_id,
|
||||
TAppLocation.location_type == location_type,
|
||||
TAppLocation.is_occupy == 1
|
||||
).scalar()
|
||||
|
||||
# 确保count不为None
|
||||
if count is None:
|
||||
count = 0
|
||||
|
||||
return WmsApiResponse.success(data=count, message="查询成功")
|
||||
|
||||
except Exception as e:
|
||||
# 打印完整异常信息(包含堆栈)
|
||||
logger.exception("查询已使用库位时发生异常")
|
||||
|
||||
# 返回错误响应 - 也使用直接实例化
|
||||
return WmsApiResponse.error(
|
||||
code=500,
|
||||
message=f"查询失败: {type(e).__name__}",
|
||||
data=None
|
||||
)
|
||||
1
app/utils/__init__.py
Normal file
1
app/utils/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# 工具包
|
||||
123
app/utils/database.py
Normal file
123
app/utils/database.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
from sqlalchemy.pool import QueuePool
|
||||
from app.config.settings import settings
|
||||
from typing import Generator
|
||||
|
||||
# 数据库引擎
|
||||
engine = create_engine(
|
||||
settings.database_url,
|
||||
poolclass=QueuePool,
|
||||
pool_size=10,
|
||||
max_overflow=20,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=3600,
|
||||
echo=settings.config.debug # 在调试模式下打印SQL
|
||||
)
|
||||
|
||||
# 会话工厂
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# 基础模型类
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
class DatabaseUtils:
|
||||
"""数据库工具类"""
|
||||
|
||||
@staticmethod
|
||||
def test_connection() -> dict:
|
||||
"""测试数据库连接"""
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
result = conn.execute(text("SELECT 1 as test"))
|
||||
row = result.fetchone()
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "数据库连接正常",
|
||||
"test_result": row[0] if row else None,
|
||||
"database_url": settings.database_url.replace(settings.config.database.password, "****")
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"数据库连接失败: {str(e)}"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_all_tables() -> dict:
|
||||
"""获取所有表名"""
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
result = conn.execute(text("SHOW TABLES"))
|
||||
tables = [row[0] for row in result.fetchall()]
|
||||
return {
|
||||
"status": "success",
|
||||
"tables": tables,
|
||||
"count": len(tables)
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"获取表名失败: {str(e)}"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def execute_query(query: str, limit: int = 100) -> dict:
|
||||
"""执行查询语句"""
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
# 添加LIMIT限制
|
||||
if "LIMIT" not in query.upper():
|
||||
query = f"{query} LIMIT {limit}"
|
||||
|
||||
result = conn.execute(text(query))
|
||||
|
||||
# 获取列名
|
||||
columns = list(result.keys()) if result.keys() else []
|
||||
|
||||
# 获取数据
|
||||
rows = result.fetchall()
|
||||
data = [dict(zip(columns, row)) for row in rows]
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"columns": columns,
|
||||
"data": data,
|
||||
"count": len(data)
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"查询执行失败: {str(e)}"
|
||||
}
|
||||
|
||||
|
||||
# 初始化数据库连接
|
||||
def init_database():
|
||||
"""初始化数据库"""
|
||||
try:
|
||||
# 测试连接
|
||||
test_result = DatabaseUtils.test_connection()
|
||||
if test_result["status"] == "success":
|
||||
print(f"✓ 数据库连接成功 - 环境: {settings.environment}")
|
||||
print(f"✓ 数据库: {settings.config.database.host}:{settings.config.database.port}/{settings.config.database.database}")
|
||||
else:
|
||||
print(f"✗ 数据库连接失败: {test_result['message']}")
|
||||
except Exception as e:
|
||||
print(f"✗ 数据库初始化错误: {str(e)}")
|
||||
|
||||
|
||||
def close_database():
|
||||
"""关闭数据库连接"""
|
||||
engine.dispose()
|
||||
print("数据库连接已关闭")
|
||||
14
app/utils/string_utils.py
Normal file
14
app/utils/string_utils.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
def pad_left(text: str, pad_char: str = "0", length: int = 2) -> str:
|
||||
return text.rjust(length, pad_char)
|
||||
|
||||
|
||||
def pad_right(text: str, pad_char: str = " ", length: int = 10) -> str:
|
||||
return text.ljust(length, pad_char)
|
||||
|
||||
|
||||
def is_empty(text: str) -> bool:
|
||||
return text is None or text.strip() == ""
|
||||
|
||||
|
||||
def is_not_empty(text: str) -> bool:
|
||||
return not is_empty(text)
|
||||
12
config/base.yaml
Normal file
12
config/base.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
app:
|
||||
name: wms_main
|
||||
max_file_size: 104857600 # 100MB
|
||||
max_request_size: 1048576000 # 1000MB
|
||||
|
||||
server:
|
||||
context_path: "/"
|
||||
|
||||
database:
|
||||
driver: mysql+pymysql
|
||||
port: 3306
|
||||
params: characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&rewriteBatchedStatements=true
|
||||
13
config/development.yaml
Normal file
13
config/development.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
app:
|
||||
name: "wms_main"
|
||||
debug: true
|
||||
host: "localhost"
|
||||
port: 12315
|
||||
|
||||
database:
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
username: "root"
|
||||
password: "root"
|
||||
database: "wms_ntyc"
|
||||
charset: "utf8mb4"
|
||||
13
config/production.yaml
Normal file
13
config/production.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
app:
|
||||
name: "wms_main"
|
||||
debug: false
|
||||
host: "localhost"
|
||||
port: 12315
|
||||
|
||||
database:
|
||||
host: "10.18.58.21"
|
||||
port: 3306
|
||||
username: "user"
|
||||
password: "user"
|
||||
database: "wms_yachi_nantong"
|
||||
charset: "utf8mb4"
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fastapi
|
||||
uvicorn
|
||||
pydantic
|
||||
pyyaml
|
||||
python-dotenv
|
||||
sqlalchemy
|
||||
pymysql
|
||||
python-multipart
|
||||
59
run.py
Normal file
59
run.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
WMS FastAPI 应用启动脚本
|
||||
支持通过环境变量切换开发/生产环境
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import uvicorn
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 获取环境变量,默认为开发环境
|
||||
env = os.getenv("ENVIRONMENT", "development")
|
||||
|
||||
print(f"🔧 当前环境: {env}")
|
||||
print(f"📁 项目根目录: {project_root}")
|
||||
|
||||
# 设置环境变量
|
||||
os.environ["ENVIRONMENT"] = env
|
||||
|
||||
try:
|
||||
from app.config.settings import settings
|
||||
|
||||
print(f"🚀 启动FastAPI应用...")
|
||||
print(f"📦 应用名称: {settings.config.name}")
|
||||
print(f"🌐 访问地址: http://{settings.config.host}:{settings.config.port}")
|
||||
print(f"📚 API文档: http://{settings.config.host}:{settings.config.port}/docs")
|
||||
|
||||
# 根据环境设置不同的启动参数
|
||||
if env == "development":
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host=settings.config.host,
|
||||
port=settings.config.port,
|
||||
reload=True,
|
||||
log_level="debug"
|
||||
)
|
||||
else:
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host=settings.config.host,
|
||||
port=settings.config.port,
|
||||
reload=False,
|
||||
workers=4
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
test_main.http
Normal file
11
test_main.http
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Test your FastAPI endpoints
|
||||
|
||||
GET http://127.0.0.1:8000/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
|
||||
GET http://127.0.0.1:8000/hello/User
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
128
test_setup.py
Normal file
128
test_setup.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
快速测试脚本,验证项目配置是否正确
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
|
||||
def test_config():
|
||||
"""测试配置加载"""
|
||||
print("🔧 测试配置加载...")
|
||||
|
||||
# 测试开发环境配置
|
||||
os.environ["ENVIRONMENT"] = "development"
|
||||
try:
|
||||
from app.config.settings import settings
|
||||
print(f"✅ 开发环境配置加载成功")
|
||||
print(f" 应用名称: {settings.config.name}")
|
||||
print(
|
||||
f" 数据库: {settings.config.database.host}:{settings.config.database.port}/{settings.config.database.database}")
|
||||
print(f" 调试模式: {settings.config.debug}")
|
||||
except Exception as e:
|
||||
print(f"❌ 开发环境配置加载失败: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_database():
|
||||
"""测试数据库连接"""
|
||||
print("\n🔍 测试数据库连接...")
|
||||
|
||||
try:
|
||||
from app.utils.database import DatabaseUtils
|
||||
result = DatabaseUtils.test_connection()
|
||||
|
||||
if result["status"] == "success":
|
||||
print(f"✅ 数据库连接成功")
|
||||
print(f" 测试结果: {result['test_result']}")
|
||||
|
||||
# 测试获取表名
|
||||
tables_result = DatabaseUtils.get_all_tables()
|
||||
if tables_result["status"] == "success":
|
||||
print(f"✅ 获取表名成功,共 {tables_result['count']} 张表")
|
||||
if tables_result["tables"]:
|
||||
print(f" 前5张表: {tables_result['tables'][:5]}")
|
||||
else:
|
||||
print(f"⚠️ 获取表名失败: {tables_result['message']}")
|
||||
|
||||
else:
|
||||
print(f"❌ 数据库连接失败: {result['message']}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 数据库测试失败: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_app_start():
|
||||
"""测试应用是否可以正常启动"""
|
||||
print("\n🚀 测试应用启动...")
|
||||
|
||||
try:
|
||||
from app.main import app
|
||||
print(f"✅ FastAPI应用创建成功")
|
||||
print(f" 应用标题: {app.title}")
|
||||
print(f" 路由数量: {len(app.routes)}")
|
||||
|
||||
# 检查路由
|
||||
routes = [route.path for route in app.routes if hasattr(route, 'path')]
|
||||
print(f" 主要路由: {routes}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 应用启动测试失败: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("=" * 50)
|
||||
print("🧪 WMS FastAPI 项目配置测试")
|
||||
print("=" * 50)
|
||||
|
||||
tests = [
|
||||
("配置加载", test_config),
|
||||
("数据库连接", test_database),
|
||||
("应用启动", test_app_start)
|
||||
]
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for test_name, test_func in tests:
|
||||
try:
|
||||
if test_func():
|
||||
passed += 1
|
||||
else:
|
||||
failed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ {test_name}测试异常: {e}")
|
||||
failed += 1
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print(f"📊 测试结果: 通过 {passed}, 失败 {failed}")
|
||||
|
||||
if failed == 0:
|
||||
print("🎉 所有测试通过!项目配置正确,可以启动应用。")
|
||||
print("\n💡 启动命令:")
|
||||
print(" python run.py")
|
||||
print("\n📚 访问文档:")
|
||||
print(" http://localhost:12315/docs")
|
||||
else:
|
||||
print("⚠️ 部分测试失败,请检查配置。")
|
||||
|
||||
print("=" * 50)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user