Merge pull request #160 from 1014TaoTao/v2.0.0

V2.0.0
This commit is contained in:
fastapiadmin
2025-09-08 21:03:56 +08:00
committed by GitHub
8 changed files with 48 additions and 58 deletions
@@ -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):
+2 -13
View File
@@ -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)
+33 -2
View File
@@ -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
View File
@@ -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 等工具,确保代码规范与质量。
## 项目截图
-30
View File
@@ -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']