版本升级

1. 新增(kinit-api,kinit-admin):新增操作日志字段
2. 更新(kinit-api):更新接口依赖库
3. 更新(kinit-api):更新接口全局事件方法
This commit is contained in:
ktianc 2023-03-15 10:58:36 +08:00
parent ff56a184ca
commit 3de880524c
14 changed files with 110 additions and 63 deletions

View File

@ -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
}, },
{ {

View File

@ -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
}, },
{ {

View File

@ -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
}, },

View File

@ -43,7 +43,7 @@ Typer 官方文档https://typer.tiangolo.com/
开发语言Python 3.10 开发语言Python 3.10
开发框架Fastapi 0.87.0 开发框架Fastapi 0.94.1
## 使用 ## 使用

View File

@ -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,
] ]
""" """

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

@ -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)

View File

@ -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.

View File

@ -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()))