Compare commits

...

2 Commits

@ -1,10 +1,15 @@
from collections import namedtuple
from typing import TypeVar, Generic, Optional
from beartype.claw import beartype_this_package
from pydantic import BaseModel
beartype_this_package()
HttpCode = namedtuple('HttpResp', ['code', 'msg'])
T = TypeVar("T")
class HttpResp:
"""HTTP响应结果
"""
@ -24,4 +29,10 @@ class HttpResp:
REQUEST_404_ERROR = HttpCode(404, '请求接口不存在')
DATA_ALREADY_EXISTS = HttpCode(409, '数据已存在')
SYSTEM_ERROR = HttpCode(500, '系统错误')
SYSTEM_TIMEOUT_ERROR = HttpCode(504, '请求超时')
SYSTEM_TIMEOUT_ERROR = HttpCode(504, '请求超时')
class ApiResponse(BaseModel, Generic[T]):
code: int = HttpResp.SUCCESS.code
message: str = HttpResp.SUCCESS.msg
data: Optional[T] = None

@ -1,8 +1,9 @@
from typing import Optional, List, Any
from typing import Optional, List, Any, Generic, TypeVar
from typing import Union
from pydantic import BaseModel, Field
T = TypeVar('T')
class BaseTabelDto(BaseModel):
id: Optional[str] = None
@ -27,14 +28,14 @@ class BaseRenameReq(BaseModel):
name: str
class BasePageResp(BaseModel):
class BasePageResp(BaseModel, Generic[T]):
page_number: Optional[int]
page_size: Optional[int]
page_count: Optional[int]
sort: Optional[str]
orderby: Optional[str]
count: Optional[int]
data: Optional[List[Any]]
data: Optional[List[T]]
class Config:
arbitrary_types_allowed = True

@ -0,0 +1,58 @@
from typing import List
from pydantic import BaseModel
class CpuInfo(BaseModel):
cpu_num: int
total: float
sys: float
used: float
wait: float
free: float
class SystemInfo(BaseModel):
computerName: str
computerIp: str
userDir: str
osName: str
osArch: str
class DiskInfo(BaseModel):
dirName: str
sysTypeName: str
typeName: str
total: str # 格式化大小,如 "1.20GB"
free: str # 格式化大小
used: str # 格式化大小
usage: float # 百分比
class MemoryInfo(BaseModel):
total: float # GB
used: float # GB
free: float # GB
usage: float # 百分比
class PythonEnvInfo(BaseModel):
name: str
version: str
home: str
inputArgs: str
total: float # MB
max: float # MB
free: float # MB
usage: float # MB
runTime: str # 格式化时间,如 "1天2小时30分钟"
startTime: str # 格式化时间
class ServerInfo(BaseModel):
cpu: CpuInfo
memory: MemoryInfo
system: SystemInfo
disks: List[DiskInfo]
python: PythonEnvInfo

@ -1,8 +1,8 @@
import inspect
import logging
from functools import wraps
from typing import Union, Type, Callable, TypeVar
from datetime import datetime
from functools import wraps
from typing import Union, Type, Callable, TypeVar, get_type_hints
import pytz
from fastapi.encoders import jsonable_encoder
@ -10,15 +10,24 @@ from pydantic import BaseModel
from starlette.responses import JSONResponse
from config import get_settings
from entity.dto import HttpResp
from entity.dto import HttpResp, ApiResponse
from exceptions.base import AppException
from utils import get_uuid
RT = TypeVar('RT') # 返回类型
def unified_resp(func: Callable[..., RT]) -> Callable[..., RT]:
"""统一响应格式
接口正常返回时,统一响应结果格式
"""
# 获取原始函数的返回类型注解
hints = get_type_hints(func)
return_type = hints.get('return', None)
# 修改函数的返回类型注解
if return_type:
func.__annotations__['return'] = ApiResponse[return_type]
@wraps(func)
async def wrapper(*args, **kwargs) -> RT:

@ -2,6 +2,7 @@ import logging
from fastapi import APIRouter
from entity.dto.monitor_dto import ServerInfo
from router import unified_resp
from utils.server_info_utils import ServerInfoUtils
@ -10,14 +11,14 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix='/monitor', tags=["缓存监控服务"])
@router.get('/server',summary='服务监控')
@router.get('/server', summary='服务监控')
@unified_resp
def monitor_server():
def monitor_server() -> ServerInfo:
"""服务器信息监控"""
return {
'cpu': ServerInfoUtils.get_cpu_info(),
'mem': ServerInfoUtils.get_mem_info(),
'sys': ServerInfoUtils.get_sys_info(),
'disk': ServerInfoUtils.get_disk_info(),
'py': ServerInfoUtils.get_py_info(),
}
return ServerInfo(
cpu=ServerInfoUtils.get_cpu_info(),
memory=ServerInfoUtils.get_mem_info(),
system=ServerInfoUtils.get_sys_info(),
disks=ServerInfoUtils.get_disk_info(),
python=ServerInfoUtils.get_py_info()
)

@ -1,5 +1,9 @@
from typing import List
from fastapi import APIRouter, Query
from entity.db_models import User
from entity.dto.base import BasePageResp
from entity.dto.user_dto import UserQueryPageReq, UserQueryReq
from router import BaseController, unified_resp
from service.user_service import UserService
@ -10,10 +14,10 @@ base_app = BaseController(base_service)
@router.get("/page")
@unified_resp
async def get_page(req:UserQueryPageReq=Query(...)):
async def get_page(req:UserQueryPageReq=Query(...)) -> BasePageResp[User]:
return await base_service.get_by_page(req)
@router.get("/list")
@unified_resp
async def get_list(req:UserQueryReq=Query(...)):
async def get_list(req:UserQueryReq=Query(...))->List[User]:
return await base_service.get_list(req)

@ -1,11 +1,10 @@
from typing import Union, Type, List, Any, TypeVar, Generic, Callable, Coroutine, Optional
from typing import Union, Type, List, Any, TypeVar, Generic
from fastapi_pagination import Params
from fastapi_pagination.ext.sqlalchemy import paginate
from pydantic import BaseModel
from sqlalchemy import func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import SQLModel
from core.global_context import current_session
from entity import DbBaseModel
@ -22,6 +21,7 @@ session.scalar: 直接明确获取一条数据,可以直接返回,无需额
"""
T = TypeVar('T', bound=DbBaseModel)
class BaseService(Generic[T]):
model: Type[T] # 子类必须指定模型
@ -66,7 +66,7 @@ class BaseService(Generic[T]):
pass
@classmethod
async def get_by_page(cls, query_params: Union[dict, BasePageQueryReq])->BasePageResp:
async def get_by_page(cls, query_params: Union[dict, BasePageQueryReq]) -> BasePageResp[T]:
if not isinstance(query_params, dict):
query_params = query_params.model_dump()
query_params = {k: v for k, v in query_params.items() if v is not None}
@ -75,7 +75,7 @@ class BaseService(Generic[T]):
@classmethod
async def auto_page(cls, query_stmt, query_params: Union[dict, BasePageQueryReq] = None,
dto_model_class: Type[BaseModel] = None)->BasePageResp:
dto_model_class: Type[BaseModel] = None) -> BasePageResp[T]:
if not query_params:
query_params = {}
if not isinstance(query_params, dict):
@ -116,7 +116,7 @@ class BaseService(Generic[T]):
})
@classmethod
async def get_list(cls, query_params: Union[dict, BaseQueryReq])->List[T]:
async def get_list(cls, query_params: Union[dict, BaseQueryReq]) -> List[T]:
if not isinstance(query_params, dict):
query_params = query_params.model_dump()
query_params = {k: v for k, v in query_params.items() if v is not None}
@ -150,7 +150,7 @@ class BaseService(Generic[T]):
return [item["id"] for item in exec_result.scalars().all()]
@classmethod
async def save(cls, **kwargs)->T:
async def save(cls, **kwargs) -> T:
sample_obj = cls.model(**kwargs)
session = cls.get_db()
session.add(sample_obj)
@ -158,7 +158,7 @@ class BaseService(Generic[T]):
return sample_obj
@classmethod
async def insert_many(cls, data_list, batch_size=100)->None:
async def insert_many(cls, data_list, batch_size=100) -> None:
async with cls.get_db() as session:
for d in data_list:
if not d.get("id", None):
@ -168,27 +168,27 @@ class BaseService(Generic[T]):
session.add_all(data_list[i: i + batch_size])
@classmethod
async def update_by_id(cls, pid, data)-> int:
async def update_by_id(cls, pid, data) -> int:
update_stmt = cls.model.update().where(cls.model.id == pid).values(**data)
session = cls.get_db()
result = await session.execute(update_stmt)
return result.rowcount
@classmethod
async def update_many_by_id(cls, data_list)->None:
async def update_many_by_id(cls, data_list) -> None:
async with cls.get_db() as session:
for data in data_list:
stmt = cls.model.update().where(cls.model.id == data["id"]).values(**data)
await session.execute(stmt)
@classmethod
async def get_by_id(cls, pid)->T:
async def get_by_id(cls, pid) -> T:
stmt = cls.model.select().where(cls.model.id == pid)
session = cls.get_db()
return await session.scalar(stmt)
@classmethod
async def get_by_ids(cls, pids, cols=None)->List[T]:
async def get_by_ids(cls, pids, cols=None) -> List[T]:
if cols:
objs = cls.model.select(*cols)
else:
@ -199,14 +199,14 @@ class BaseService(Generic[T]):
return list(result.all())
@classmethod
async def delete_by_id(cls, pid)-> int:
async def delete_by_id(cls, pid) -> int:
del_stmt = cls.model.delete().where(cls.model.id == pid)
session = cls.get_db()
exec_result = await session.execute(del_stmt)
return exec_result.rowcount
@classmethod
async def delete_by_ids(cls, pids)-> int:
async def delete_by_ids(cls, pids) -> int:
session = cls.get_db()
del_stmt = cls.model.delete().where(cls.model.id.in_(pids))
result = await session.execute(del_stmt)

@ -7,6 +7,7 @@ from typing import List, Any
import psutil
from config import get_settings
from entity.dto.monitor_dto import CpuInfo, MemoryInfo, SystemInfo, DiskInfo, PythonEnvInfo
from utils.ip_utils import IpUtil
@ -50,7 +51,7 @@ class ServerInfoUtils:
return res
@staticmethod
def get_cpu_info() -> dict:
def get_cpu_info() -> CpuInfo:
"""获取CPU信息"""
res = {'cpu_num': psutil.cpu_count(logical=True)}
cpu_times = psutil.cpu_times()
@ -62,35 +63,35 @@ class ServerInfoUtils:
res['used'] = round(cpu_times.user / total, 2)
res['wait'] = round(get_attr(cpu_times, 'iowait', 0.0) / total, 2)
res['free'] = round(cpu_times.idle / total, 2)
return res
return CpuInfo(**res)
@staticmethod
def get_mem_info() -> dict:
def get_mem_info() -> MemoryInfo:
"""获取内存信息"""
number = 1024 ** 3
return {
return MemoryInfo(**{
'total': round(psutil.virtual_memory().total / number, 2),
'used': round(psutil.virtual_memory().used / number, 2),
'free': round(psutil.virtual_memory().available / number, 2),
'usage': round(psutil.virtual_memory().percent, 2)}
'usage': round(psutil.virtual_memory().percent, 2)})
@staticmethod
def get_sys_info() -> dict:
def get_sys_info() -> SystemInfo:
"""获取服务器信息"""
return {
return SystemInfo(**{
'computerName': IpUtil.get_host_name(),
'computerIp': IpUtil.get_host_ip(),
'userDir': os.path.dirname(os.path.abspath(os.path.join(__file__, '../..'))),
'osName': platform.system(),
'osArch': platform.machine()}
'osArch': platform.machine()})
@staticmethod
def get_disk_info() -> List[dict]:
def get_disk_info() -> List[DiskInfo]:
"""获取磁盘信息"""
disk_info = []
for disk in psutil.disk_partitions():
usage = psutil.disk_usage(disk.mountpoint)
disk_info.append({
disk_info.append(DiskInfo(**{
'dirName': disk.mountpoint,
'sysTypeName': disk.fstype,
'typeName': disk.device,
@ -98,7 +99,7 @@ class ServerInfoUtils:
'free': ServerInfoUtils.get_size(usage.free),
'used': ServerInfoUtils.get_size(usage.used),
'usage': round(usage.percent, 2),
})
}))
return disk_info
@staticmethod
@ -108,7 +109,7 @@ class ServerInfoUtils:
cur_proc = psutil.Process(os.getpid())
mem_info = cur_proc.memory_info()
start_dt = datetime.fromtimestamp(cur_proc.create_time())
return {
return PythonEnvInfo(**{
'name': 'Python',
'version': platform.python_version(),
'home': sys.executable,
@ -119,4 +120,4 @@ class ServerInfoUtils:
'usage': round(mem_info.rss / number, 2),
'runTime': ServerInfoUtils.fmt_timedelta(datetime.now() - start_dt),
'startTime': start_dt.strftime(ServerInfoUtils.datetime_fmt),
}
})

Loading…
Cancel
Save