mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 20:55:14 +00:00
@@ -2,7 +2,7 @@
|
||||
|
||||
from typing import Optional, Union
|
||||
from datetime import datetime
|
||||
from pydantic import ConfigDict, Field, BaseModel, field_validator, model_validator
|
||||
from pydantic import ConfigDict, Field, BaseModel, model_validator
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -25,10 +25,10 @@ class JWTPayloadSchema(BaseModel):
|
||||
exp: Union[datetime, int] = Field(..., description='过期时间')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_fields(cls, data):
|
||||
if not data.sub or len(data.sub.strip()) == 0:
|
||||
def validate_fields(self):
|
||||
if not self.sub or len(self.sub.strip()) == 0:
|
||||
raise ValueError("会话编号不能为空")
|
||||
return data
|
||||
return self
|
||||
|
||||
|
||||
class JWTOutSchema(BaseModel):
|
||||
|
||||
@@ -28,10 +28,9 @@ class MenuCreateSchema(BaseModel):
|
||||
parent_id: Optional[int] = Field(default=None, ge=1, description="父菜单ID")
|
||||
description: Optional[str] = Field(default=None, max_length=500, description="备注说明")
|
||||
|
||||
@classmethod
|
||||
@model_validator(mode='after')
|
||||
def validate_fields(cls, data):
|
||||
return menu_request_validator(data)
|
||||
def validate_fields(self):
|
||||
return menu_request_validator(self)
|
||||
|
||||
|
||||
class MenuUpdateSchema(MenuCreateSchema):
|
||||
|
||||
@@ -26,11 +26,10 @@ class RolePermissionSettingSchema(BaseModel):
|
||||
menu_ids: List[int] = Field(default_factory=list, description='菜单ID列表')
|
||||
dept_ids: List[int] = Field(default_factory=list, description='部门ID列表')
|
||||
|
||||
@classmethod
|
||||
@model_validator(mode='after')
|
||||
def validate_fields(cls, data):
|
||||
def validate_fields(self):
|
||||
"""验证权限配置字段"""
|
||||
return role_permission_request_validator(data)
|
||||
return role_permission_request_validator(self)
|
||||
|
||||
|
||||
class RoleUpdateSchema(RoleCreateSchema):
|
||||
|
||||
@@ -78,7 +78,9 @@ class UserCreateSchema(CurrentUserUpdateSchema):
|
||||
|
||||
class UserUpdateSchema(UserCreateSchema):
|
||||
"""更新"""
|
||||
model_config = ConfigDict(from_attributes=True, exclude={"password"})
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
password: Optional[str] = Field(default=None, max_length=128, description="密码哈希值")
|
||||
|
||||
|
||||
class UserOutSchema(UserCreateSchema, BaseSchema):
|
||||
|
||||
@@ -85,19 +85,8 @@ class DemoEnvMiddleware(BaseHTTPMiddleware):
|
||||
return await call_next(request)
|
||||
|
||||
else:
|
||||
try:
|
||||
response = await call_next(request)
|
||||
|
||||
# 现在检查用户是否有权限执行操作
|
||||
user_username = request.scope.get("user_username")
|
||||
if user_username and user_username not in settings.DEMO_USER_WHITE_LIST:
|
||||
# 如果用户没有权限,返回错误响应
|
||||
return ErrorResponse(msg="演示环境,禁止操作")
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
# 处理可能的异常
|
||||
raise e
|
||||
# 直接返回错误响应,不继续执行后续操作
|
||||
return ErrorResponse(msg="演示环境,禁止操作")
|
||||
|
||||
return await call_next(request)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import uuid
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
from sqlalchemy import inspect, select, func
|
||||
from sqlalchemy import inspect, select, func, text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.base_model import MappedBase
|
||||
@@ -60,6 +60,7 @@ class InitializeData:
|
||||
RoleMenusModel,
|
||||
]
|
||||
self.created_tables = set()
|
||||
|
||||
async def __get_existing_tables(self, db: AsyncSession) -> List[str]:
|
||||
return await db.run_sync(
|
||||
lambda sync_db: inspect(sync_db.get_bind()).get_table_names()
|
||||
@@ -112,6 +113,37 @@ class InitializeData:
|
||||
logger.error(f"初始化 {table_name} 表数据失败: {str(e)}")
|
||||
raise
|
||||
|
||||
# 更新 PostgreSQL 序列值,确保自增 ID 正确
|
||||
if settings.DATABASE_TYPE == "postgresql":
|
||||
await self.__update_postgresql_sequences(db)
|
||||
|
||||
async def __update_postgresql_sequences(self, db: AsyncSession) -> None:
|
||||
"""更新 PostgreSQL 序列值,确保自增 ID 正确"""
|
||||
try:
|
||||
# 为每个有初始化数据的表更新序列值
|
||||
for model in self.prepare_init_models:
|
||||
table_name = model.__tablename__
|
||||
|
||||
# 检查表中是否有数据
|
||||
count_result = await db.execute(select(func.count()).select_from(model))
|
||||
existing_count = count_result.scalar()
|
||||
|
||||
if existing_count > 0:
|
||||
# 获取表中最大的 ID 值
|
||||
max_id_result = await db.execute(select(func.max(model.id)).select_from(model))
|
||||
max_id = max_id_result.scalar()
|
||||
|
||||
if max_id is not None:
|
||||
# 更新序列值
|
||||
sequence_name = f"{table_name}_id_seq"
|
||||
await db.execute(text(f"SELECT setval('{sequence_name}', {max_id}, true)"))
|
||||
logger.info(f"已更新 {table_name} 表的序列 {sequence_name} 值为 {max_id}")
|
||||
|
||||
await db.commit()
|
||||
except Exception as e:
|
||||
logger.error(f"更新 PostgreSQL 序列值失败: {str(e)}")
|
||||
raise
|
||||
|
||||
async def __get_data(self, filename: str) -> List[Dict]:
|
||||
"""读取初始化数据文件"""
|
||||
json_path = Path.joinpath(settings.SCRIPT_DIR, f'{filename}.json')
|
||||
@@ -133,4 +165,3 @@ class InitializeData:
|
||||
执行完整初始化流程
|
||||
"""
|
||||
await self.__init_model(db)
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# 项目介绍
|
||||
|
||||
基于 uni-app + Vue 3 + TypeScript 移动端跨平台开发模板,集成了 ESLint、Prettier、Stylelint、Husky 和 Commitlint 等工具,确保代码规范与质量。
|
||||
基于 uni-app + wot-design-uni + Vue 3 + TypeScript 移动端跨平台开发模板,集成了 ESLint、Prettier、Stylelint、Husky 和 Commitlint 等工具,确保代码规范与质量。
|
||||
|
||||
## 项目截图
|
||||
|
||||
|
||||
Vendored
-30
@@ -10,22 +10,17 @@ declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const Storage: typeof import('../utils/storage')['Storage']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const applyCSSVariables: typeof import('../utils/theme')['applyCSSVariables']
|
||||
const applyThemeOnPageShow: typeof import('../utils/theme')['applyThemeOnPageShow']
|
||||
const applyThemeToMiniProgram: typeof import('../utils/theme')['applyThemeToMiniProgram']
|
||||
const auth: typeof import('../api/auth')['default']
|
||||
const checkLogin: typeof import('../utils/auth')['checkLogin']
|
||||
const clearAll: typeof import('../utils/auth')['clearAll']
|
||||
const clearToken: typeof import('../utils/storage')['clearToken']
|
||||
const clearTokens: typeof import('../utils/auth')['clearTokens']
|
||||
const clearUserInfo: typeof import('../utils/auth')['clearUserInfo']
|
||||
const colorColumns: typeof import('../composables/useTheme')['colorColumns']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const config: typeof import('../api/config')['default']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const createRouter: typeof import('uni-mini-router')['createRouter']
|
||||
const currentThemeColor: typeof import('../composables/useTheme')['currentThemeColor']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const debounce: typeof import('../utils/index')['debounce']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
@@ -34,35 +29,22 @@ declare global {
|
||||
const dept: typeof import('../api/dept')['default']
|
||||
const dict: typeof import('../api/dict')['default']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const extendedColorOptions: typeof import('../composables/useTheme')['extendedColorOptions']
|
||||
const file: typeof import('../api/file')['default']
|
||||
const generateCSSVariables: typeof import('../utils/theme')['generateCSSVariables']
|
||||
const generateColorVariants: typeof import('../utils/theme')['generateColorVariants']
|
||||
const generateGradientBackground: typeof import('../utils/theme')['generateGradientBackground']
|
||||
const getAccessToken: typeof import('../utils/auth')['getAccessToken']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getContrastColor: typeof import('../utils/theme')['getContrastColor']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const getDarkerColor: typeof import('../utils/colorUtils')['getDarkerColor']
|
||||
const getLighterColor: typeof import('../utils/colorUtils')['getLighterColor']
|
||||
const getRefreshToken: typeof import('../utils/auth')['getRefreshToken']
|
||||
const getThemeClassName: typeof import('../utils/theme')['getThemeClassName']
|
||||
const getThemeColor: typeof import('../utils/theme')['getThemeColor']
|
||||
const getThemeColors: typeof import('../utils/theme')['getThemeColors']
|
||||
const getToken: typeof import('../utils/storage')['getToken']
|
||||
const getUserInfo: typeof import('../utils/auth')['getUserInfo']
|
||||
const guessSerializerType: typeof import('@uni-helper/uni-use')['guessSerializerType']
|
||||
const h: typeof import('vue')['h']
|
||||
const initTheme: typeof import('../composables/useTheme')['initTheme']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isDarkMode: typeof import('../utils/theme')['isDarkMode']
|
||||
const isLoggedIn: typeof import('../utils/auth')['isLoggedIn']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const isValidColor: typeof import('../utils/theme')['isValidColor']
|
||||
const log: typeof import('../api/log')['default']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
@@ -72,7 +54,6 @@ declare global {
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const menu: typeof import('../api/menu')['default']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const normalizeColor: typeof import('../utils/theme')['normalizeColor']
|
||||
const notice: typeof import('../api/notice')['default']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
|
||||
@@ -121,15 +102,12 @@ declare global {
|
||||
const ref: typeof import('vue')['ref']
|
||||
const request: typeof import('../utils/request')['default']
|
||||
const requireLogin: typeof import('../utils/auth')['requireLogin']
|
||||
const resetTheme: typeof import('../composables/useTheme')['resetTheme']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const role: typeof import('../api/role')['default']
|
||||
const setAccessToken: typeof import('../utils/auth')['setAccessToken']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const setRefreshToken: typeof import('../utils/auth')['setRefreshToken']
|
||||
const setThemeColor: typeof import('../utils/theme')['setThemeColor']
|
||||
const setThemeColorCache: typeof import('../utils/theme')['setThemeColorCache']
|
||||
const setToken: typeof import('../utils/storage')['setToken']
|
||||
const setUserInfo: typeof import('../utils/auth')['setUserInfo']
|
||||
const setupStore: typeof import('../store/index')['setupStore']
|
||||
@@ -138,14 +116,10 @@ declare global {
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const store: typeof import('../store/index')['store']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const theme: typeof import('../composables/useTheme')['theme']
|
||||
const themeColorOptions: typeof import('../composables/useTheme')['themeColorOptions']
|
||||
const themeVars: typeof import('../composables/useTheme')['themeVars']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const toggleTheme: typeof import('../composables/useTheme')['toggleTheme']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const tryOnBackPress: typeof import('@uni-helper/uni-use')['tryOnBackPress']
|
||||
const tryOnHide: typeof import('@uni-helper/uni-use')['tryOnHide']
|
||||
@@ -196,13 +170,9 @@ declare global {
|
||||
const useStorageSync: typeof import('@uni-helper/uni-use')['useStorageSync']
|
||||
const useTabbar: typeof import('../composables/useTabbar')['useTabbar']
|
||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||
const useTheme: typeof import('../composables/useTheme')['useTheme']
|
||||
const useThemeStore: typeof import('../store/modules/theme.store')['useThemeStore']
|
||||
const useToast: typeof import('wot-design-uni')['useToast']
|
||||
const useUploadFile: typeof import('@uni-helper/uni-use')['useUploadFile']
|
||||
const useUserStore: typeof import('../store/modules/user.store')['useUserStore']
|
||||
const useVisible: typeof import('@uni-helper/uni-use')['useVisible']
|
||||
const useWechat: typeof import('../composables/useWechat')['useWechat']
|
||||
const user: typeof import('../api/user')['default']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
|
||||
Reference in New Issue
Block a user