版本升级
1. 新增(kinit-api,kinit-admin):新增操作日志字段 2. 更新(kinit-api):更新接口依赖库 3. 更新(kinit-api):更新接口全局事件方法
This commit is contained in:
parent
ff56a184ca
commit
3de880524c
@ -45,11 +45,13 @@ export const columns = reactive<TableColumn[]>([
|
|||||||
{
|
{
|
||||||
field: 'last_login',
|
field: 'last_login',
|
||||||
label: '最近登录时间',
|
label: '最近登录时间',
|
||||||
show: true
|
show: true,
|
||||||
|
width: '190px'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'create_datetime',
|
field: 'create_datetime',
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
|
width: '190px',
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ export const columns = reactive<TableColumn[]>([
|
|||||||
{
|
{
|
||||||
field: 'category.name',
|
field: 'category.name',
|
||||||
label: '类别名称',
|
label: '类别名称',
|
||||||
|
width: '200px',
|
||||||
show: true,
|
show: true,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
span: 24
|
span: 24
|
||||||
@ -28,18 +29,21 @@ export const columns = reactive<TableColumn[]>([
|
|||||||
field: 'view_number',
|
field: 'view_number',
|
||||||
label: '查看次数',
|
label: '查看次数',
|
||||||
show: true,
|
show: true,
|
||||||
|
width: '100px',
|
||||||
span: 24
|
span: 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'is_active',
|
field: 'is_active',
|
||||||
label: '是否可见',
|
label: '是否可见',
|
||||||
show: true,
|
show: true,
|
||||||
|
width: '100px',
|
||||||
span: 24
|
span: 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'create_datetime',
|
field: 'create_datetime',
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
show: true,
|
show: true,
|
||||||
|
width: '200px',
|
||||||
span: 24,
|
span: 24,
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
@ -47,6 +51,7 @@ export const columns = reactive<TableColumn[]>([
|
|||||||
field: 'user.name',
|
field: 'user.name',
|
||||||
label: '创建人',
|
label: '创建人',
|
||||||
show: true,
|
show: true,
|
||||||
|
width: '100px',
|
||||||
span: 24
|
span: 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,22 @@ import { TableColumn } from '@/types/table'
|
|||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
export const columns = reactive<TableColumn[]>([
|
export const columns = reactive<TableColumn[]>([
|
||||||
|
{
|
||||||
|
field: 'user_id',
|
||||||
|
label: '操作人编号',
|
||||||
|
show: true,
|
||||||
|
disabled: true,
|
||||||
|
width: '100px',
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'user_name',
|
||||||
|
label: '操作人',
|
||||||
|
show: true,
|
||||||
|
disabled: true,
|
||||||
|
width: '100px',
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'telephone',
|
field: 'telephone',
|
||||||
label: '手机号',
|
label: '手机号',
|
||||||
@ -82,7 +98,7 @@ export const columns = reactive<TableColumn[]>([
|
|||||||
{
|
{
|
||||||
field: 'system',
|
field: 'system',
|
||||||
label: '系统',
|
label: '系统',
|
||||||
show: true,
|
show: false,
|
||||||
width: '150px',
|
width: '150px',
|
||||||
span: 24
|
span: 24
|
||||||
},
|
},
|
||||||
|
@ -43,7 +43,7 @@ Typer 官方文档:https://typer.tiangolo.com/
|
|||||||
|
|
||||||
开发语言:Python 3.10
|
开发语言:Python 3.10
|
||||||
|
|
||||||
开发框架:Fastapi 0.87.0
|
开发框架:Fastapi 0.94.1
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
|
|||||||
"""
|
"""
|
||||||
系统版本
|
系统版本
|
||||||
"""
|
"""
|
||||||
VERSION = "1.6.2"
|
VERSION = "1.6.3"
|
||||||
|
|
||||||
"""安全警告: 不要在生产中打开调试运行!"""
|
"""安全警告: 不要在生产中打开调试运行!"""
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@ -101,8 +101,8 @@ ALLOW_HEADERS = ["*"]
|
|||||||
全局事件配置
|
全局事件配置
|
||||||
"""
|
"""
|
||||||
EVENTS = [
|
EVENTS = [
|
||||||
"core.event.register_mongo" if MONGO_DB_ENABLE else None,
|
"core.event.connect_mongo" if MONGO_DB_ENABLE else None,
|
||||||
"core.event.register_redis" if REDIS_DB_ENABLE else None,
|
"core.event.connect_redis" if REDIS_DB_ENABLE else None,
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -76,6 +76,8 @@ class OpenAuth(AuthValidation):
|
|||||||
return Auth(db=db)
|
return Auth(db=db)
|
||||||
elif not user.is_active:
|
elif not user.is_active:
|
||||||
return Auth(db=db)
|
return Auth(db=db)
|
||||||
|
request.scope["user_id"] = user.id
|
||||||
|
request.scope["user_name"] = user.name
|
||||||
request.scope["telephone"] = user.telephone
|
request.scope["telephone"] = user.telephone
|
||||||
try:
|
try:
|
||||||
request.scope["body"] = await request.body()
|
request.scope["body"] = await request.body()
|
||||||
|
@ -70,6 +70,8 @@ class AuthValidation:
|
|||||||
raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
|
raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
|
||||||
elif not user.is_active:
|
elif not user.is_active:
|
||||||
raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code)
|
raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code)
|
||||||
|
request.scope["user_id"] = user.id
|
||||||
|
request.scope["user_name"] = user.name
|
||||||
request.scope["telephone"] = user.telephone
|
request.scope["telephone"] = user.telephone
|
||||||
try:
|
try:
|
||||||
request.scope["body"] = await request.body()
|
request.scope["body"] = await request.body()
|
||||||
|
@ -15,6 +15,8 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
class OpertionRecord(BaseModel):
|
class OpertionRecord(BaseModel):
|
||||||
telephone: Optional[str] = None
|
telephone: Optional[str] = None
|
||||||
|
user_id: Optional[str] = None
|
||||||
|
user_name: Optional[str] = None
|
||||||
status_code: Optional[int] = None
|
status_code: Optional[int] = None
|
||||||
request_ip: Optional[str] = None
|
request_ip: Optional[str] = None
|
||||||
request_method: Optional[str] = None
|
request_method: Optional[str] = None
|
||||||
|
@ -8,13 +8,25 @@
|
|||||||
|
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from aioredis import from_url
|
from application.settings import REDIS_DB_URL, MONGO_DB_URL, MONGO_DB_NAME, EVENTS
|
||||||
from application.settings import REDIS_DB_URL, MONGO_DB_URL, MONGO_DB_NAME
|
|
||||||
from core.mongo import db
|
from core.mongo import db
|
||||||
from utils.cache import Cache
|
from utils.cache import Cache
|
||||||
|
import aioredis
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from utils.tools import import_modules_async
|
||||||
|
|
||||||
|
|
||||||
def register_redis(app: FastAPI) -> None:
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
|
||||||
|
await import_modules_async(EVENTS, "全局事件", app=app, status=True)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
await import_modules_async(EVENTS, "全局事件", app=app, status=False)
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_redis(app: FastAPI, status: bool):
|
||||||
"""
|
"""
|
||||||
把 redis 挂载到 app 对象上面
|
把 redis 挂载到 app 对象上面
|
||||||
|
|
||||||
@ -46,30 +58,20 @@ def register_redis(app: FastAPI) -> None:
|
|||||||
该参数的默认值是 0,表示不进行健康检查。如果需要启用健康检查,则可以将该参数设置为一个正整数,表示检查间隔的秒数。
|
该参数的默认值是 0,表示不进行健康检查。如果需要启用健康检查,则可以将该参数设置为一个正整数,表示检查间隔的秒数。
|
||||||
例如,如果需要每隔 5 秒对 Redis 连接进行一次健康检查,则可以将 health_check_interval 设置为 5
|
例如,如果需要每隔 5 秒对 Redis 连接进行一次健康检查,则可以将 health_check_interval 设置为 5
|
||||||
:param app:
|
:param app:
|
||||||
|
:param status:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if status:
|
||||||
@app.on_event('startup')
|
|
||||||
async def startup_event():
|
|
||||||
"""
|
|
||||||
获取链接
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
print("Connecting to Redis")
|
print("Connecting to Redis")
|
||||||
app.state.redis = from_url(REDIS_DB_URL, decode_responses=True, health_check_interval=1)
|
assert isinstance(app, FastAPI)
|
||||||
|
app.state.redis = aioredis.from_url(REDIS_DB_URL, decode_responses=True, health_check_interval=1)
|
||||||
await Cache(app.state.redis).cache_tab_names()
|
await Cache(app.state.redis).cache_tab_names()
|
||||||
|
else:
|
||||||
@app.on_event('shutdown')
|
|
||||||
async def shutdown_event():
|
|
||||||
"""
|
|
||||||
关闭
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
print("Redis connection closed")
|
print("Redis connection closed")
|
||||||
await app.state.redis.close()
|
await app.state.redis.close()
|
||||||
|
|
||||||
|
|
||||||
def register_mongo(app: FastAPI) -> None:
|
async def connect_mongo(app: FastAPI, status: bool):
|
||||||
"""
|
"""
|
||||||
把 mongo 挂载到 app 对象上面
|
把 mongo 挂载到 app 对象上面
|
||||||
|
|
||||||
@ -77,21 +79,16 @@ def register_mongo(app: FastAPI) -> None:
|
|||||||
mongodb 官网:https://www.mongodb.com/docs/drivers/motor/
|
mongodb 官网:https://www.mongodb.com/docs/drivers/motor/
|
||||||
motor 文档:https://motor.readthedocs.io/en/stable/
|
motor 文档:https://motor.readthedocs.io/en/stable/
|
||||||
:param app:
|
:param app:
|
||||||
|
:param status:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if status:
|
||||||
@app.on_event('startup')
|
print("Connecting to Mongo")
|
||||||
async def startup_event():
|
|
||||||
"""
|
|
||||||
获取 mongodb 连接
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
await db.connect_to_database(path=MONGO_DB_URL, db_name=MONGO_DB_NAME)
|
await db.connect_to_database(path=MONGO_DB_URL, db_name=MONGO_DB_NAME)
|
||||||
|
else:
|
||||||
@app.on_event('shutdown')
|
print("Mongo connection closed")
|
||||||
async def shutdown_event():
|
|
||||||
"""
|
|
||||||
关闭
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
await db.close_database_connection()
|
await db.close_database_connection()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +69,8 @@ def register_operation_record_middleware(app: FastAPI):
|
|||||||
if not MONGO_DB_ENABLE:
|
if not MONGO_DB_ENABLE:
|
||||||
return response
|
return response
|
||||||
telephone = request.scope.get('telephone', None)
|
telephone = request.scope.get('telephone', None)
|
||||||
|
user_id = request.scope.get('user_id', None)
|
||||||
|
user_name = request.scope.get('user_name', None)
|
||||||
route = request.scope.get('route')
|
route = request.scope.get('route')
|
||||||
if not telephone:
|
if not telephone:
|
||||||
return response
|
return response
|
||||||
@ -98,6 +100,8 @@ def register_operation_record_middleware(app: FastAPI):
|
|||||||
document = {
|
document = {
|
||||||
"process_time": process_time,
|
"process_time": process_time,
|
||||||
"telephone": telephone,
|
"telephone": telephone,
|
||||||
|
"user_id": user_id,
|
||||||
|
"user_name": user_name,
|
||||||
"request_api": request.url.__str__(),
|
"request_api": request.url.__str__(),
|
||||||
"request_ip": request.client.host,
|
"request_ip": request.client.host,
|
||||||
"system": system,
|
"system": system,
|
||||||
|
@ -21,13 +21,11 @@ class MongoManage(DatabaseManage):
|
|||||||
db: AsyncIOMotorDatabase = None
|
db: AsyncIOMotorDatabase = None
|
||||||
|
|
||||||
async def connect_to_database(self, path: str, db_name: str):
|
async def connect_to_database(self, path: str, db_name: str):
|
||||||
print("Connecting to MongoDB")
|
|
||||||
self.client = AsyncIOMotorClient(path, maxPoolSize=10, minPoolSize=10)
|
self.client = AsyncIOMotorClient(path, maxPoolSize=10, minPoolSize=10)
|
||||||
self.db = self.client[db_name]
|
self.db = self.client[db_name]
|
||||||
|
|
||||||
async def close_database_connection(self):
|
async def close_database_connection(self):
|
||||||
self.client.close()
|
self.client.close()
|
||||||
print("MongoDB connection closed")
|
|
||||||
|
|
||||||
async def create_data(self, collection: str, data: dict) -> InsertOneResult:
|
async def create_data(self, collection: str, data: dict) -> InsertOneResult:
|
||||||
return await self.db[collection].insert_one(data)
|
return await self.db[collection].insert_one(data)
|
||||||
|
@ -17,19 +17,19 @@ from starlette.middleware.cors import CORSMiddleware
|
|||||||
from application import settings
|
from application import settings
|
||||||
from application import urls
|
from application import urls
|
||||||
from starlette.staticfiles import StaticFiles # 依赖安装:pip install aiofiles
|
from starlette.staticfiles import StaticFiles # 依赖安装:pip install aiofiles
|
||||||
import importlib
|
|
||||||
from core.logger import logger
|
|
||||||
from core.exception import register_exception
|
from core.exception import register_exception
|
||||||
import typer
|
import typer
|
||||||
from scripts.initialize.initialize import InitializeData, Environment
|
from scripts.initialize.initialize import InitializeData, Environment
|
||||||
import asyncio
|
import asyncio
|
||||||
from scripts.create_app.main import CreateApp
|
from scripts.create_app.main import CreateApp
|
||||||
|
from core.event import lifespan
|
||||||
|
from utils.tools import import_modules
|
||||||
|
|
||||||
|
|
||||||
shell_app = typer.Typer()
|
shell_app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
def init_app():
|
def create_app():
|
||||||
"""
|
"""
|
||||||
启动项目
|
启动项目
|
||||||
|
|
||||||
@ -40,24 +40,10 @@ def init_app():
|
|||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Kinit",
|
title="Kinit",
|
||||||
description="本项目基于Fastapi与Vue3+Typescript+Vite4+element-plus的基础项目 前端基于vue-element-plus-admin框架开发",
|
description="本项目基于Fastapi与Vue3+Typescript+Vite4+element-plus的基础项目 前端基于vue-element-plus-admin框架开发",
|
||||||
version="1.0.0"
|
version=settings.VERSION,
|
||||||
|
lifespan=lifespan
|
||||||
)
|
)
|
||||||
|
import_modules(settings.MIDDLEWARES, "中间件", app=app)
|
||||||
def import_module(modules: list, desc: str):
|
|
||||||
for module in modules:
|
|
||||||
if not module:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
# 动态导入模块
|
|
||||||
module_pag = importlib.import_module(module[0:module.rindex(".")])
|
|
||||||
getattr(module_pag, module[module.rindex(".") + 1:])(app)
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
logger.error(f"AttributeError:导入{desc}失败,未找到该模块:{module}")
|
|
||||||
except AttributeError:
|
|
||||||
logger.error(f"ModuleNotFoundError:导入{desc}失败,未找到该模块下的方法:{module}")
|
|
||||||
|
|
||||||
import_module(settings.MIDDLEWARES, "中间件")
|
|
||||||
import_module(settings.EVENTS, "全局事件")
|
|
||||||
# 全局异常捕捉处理
|
# 全局异常捕捉处理
|
||||||
register_exception(app)
|
register_exception(app)
|
||||||
# 跨域解决
|
# 跨域解决
|
||||||
@ -83,8 +69,11 @@ def init_app():
|
|||||||
def run():
|
def run():
|
||||||
"""
|
"""
|
||||||
启动项目
|
启动项目
|
||||||
|
|
||||||
|
factory: 在使用 uvicorn.run() 启动 ASGI 应用程序时,可以通过设置 factory 参数来指定应用程序工厂。
|
||||||
|
应用程序工厂是一个返回 ASGI 应用程序实例的可调用对象,它可以在启动时动态创建应用程序实例。
|
||||||
"""
|
"""
|
||||||
uvicorn.run(app='main:init_app', host="0.0.0.0", port=9000)
|
uvicorn.run(app='main:create_app', host="0.0.0.0", port=9000, lifespan="on", factory=True)
|
||||||
|
|
||||||
|
|
||||||
@shell_app.command()
|
@shell_app.command()
|
||||||
@ -111,7 +100,7 @@ def migrate(env: Environment = Environment.pro):
|
|||||||
|
|
||||||
|
|
||||||
@shell_app.command()
|
@shell_app.command()
|
||||||
def create_app(path: str):
|
def init_app(path: str):
|
||||||
"""
|
"""
|
||||||
自动创建初始化 APP 结构
|
自动创建初始化 APP 结构
|
||||||
|
|
||||||
|
Binary file not shown.
@ -11,6 +11,8 @@ import random
|
|||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
import importlib
|
||||||
|
from core.logger import logger
|
||||||
|
|
||||||
|
|
||||||
def test_password(password: str) -> Union[str, bool]:
|
def test_password(password: str) -> Union[str, bool]:
|
||||||
@ -73,6 +75,34 @@ def generate_string(length: int = 8) -> str:
|
|||||||
return ''.join(random.sample(string.ascii_letters + string.digits, length))
|
return ''.join(random.sample(string.ascii_letters + string.digits, length))
|
||||||
|
|
||||||
|
|
||||||
|
def import_modules(modules: list, desc: str, **kwargs):
|
||||||
|
for module in modules:
|
||||||
|
if not module:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
# 动态导入模块
|
||||||
|
module_pag = importlib.import_module(module[0:module.rindex(".")])
|
||||||
|
getattr(module_pag, module[module.rindex(".") + 1:])(**kwargs)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
logger.error(f"AttributeError:导入{desc}失败,未找到该模块:{module}")
|
||||||
|
except AttributeError:
|
||||||
|
logger.error(f"ModuleNotFoundError:导入{desc}失败,未找到该模块下的方法:{module}")
|
||||||
|
|
||||||
|
|
||||||
|
async def import_modules_async(modules: list, desc: str, **kwargs):
|
||||||
|
for module in modules:
|
||||||
|
if not module:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
# 动态导入模块
|
||||||
|
module_pag = importlib.import_module(module[0:module.rindex(".")])
|
||||||
|
await getattr(module_pag, module[module.rindex(".") + 1:])(**kwargs)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
logger.error(f"AttributeError:导入{desc}失败,未找到该模块:{module}")
|
||||||
|
except AttributeError:
|
||||||
|
logger.error(f"ModuleNotFoundError:导入{desc}失败,未找到该模块下的方法:{module}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# print(generate_invitation_code())
|
# print(generate_invitation_code())
|
||||||
# print(int(datetime.datetime.now().timestamp()))
|
# print(int(datetime.datetime.now().timestamp()))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user