From 808c6a5a6982488ba1fa5226524e305d38b028e9 Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Mon, 25 Aug 2025 14:32:07 +0800 Subject: [PATCH 1/9] =?UTF-8?q?refactor(job):=E4=BC=98=E5=8C=96=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/monitor/job_schema.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/backend/app/api/v1/schemas/monitor/job_schema.py b/backend/app/api/v1/schemas/monitor/job_schema.py index c393ccd8..515089c4 100644 --- a/backend/app/api/v1/schemas/monitor/job_schema.py +++ b/backend/app/api/v1/schemas/monitor/job_schema.py @@ -2,9 +2,6 @@ from pydantic import BaseModel, ConfigDict, Field from typing import Optional -from datetime import datetime - -from app.core.validator import DateTimeStr from app.core.base_schema import BaseSchema @@ -12,16 +9,15 @@ class JobCreateSchema(BaseModel): """ 定时任务调度表对应pydantic模型 """ - - name: Optional[str] = Field(default=None, max_length=64, description='任务名称') + name: Optional[str] = Field(..., max_length=64, description='任务名称') func: str = Field(..., description='任务函数') trigger: str = Field(..., description='触发器:控制此作业计划的 trigger 对象') args: Optional[str] = Field(default=None, description='位置参数') kwargs: Optional[str] = Field(default=None, description='关键字参数') - coalesce: Optional[bool] = Field(default=False, description='是否合并运行:是否在多个运行时间到期时仅运行作业一次') + coalesce: Optional[bool] = Field(..., description='是否合并运行:是否在多个运行时间到期时仅运行作业一次') max_instances: Optional[int] = Field(default=1, ge=1, description='最大实例数:允许的最大并发执行实例数') - jobstore: Optional[str] = Field(default='default', max_length=64, description='任务存储') - executor: Optional[str] = Field(default='default', max_length=64, description='任务执行器:将运行此作业的执行程序的名称') + jobstore: Optional[str] = Field(..., max_length=64, description='任务存储') + executor: Optional[str] = Field(..., max_length=64, description='任务执行器:将运行此作业的执行程序的名称') trigger_args: Optional[str] = Field(default=None, description='触发器参数') start_date: Optional[str] = Field(default=None, description='开始时间') end_date: Optional[str] = Field(default=None, description='结束时间') @@ -29,14 +25,13 @@ class JobCreateSchema(BaseModel): status: Optional[bool] = Field(default=False, description='任务状态:启动,停止') message: Optional[str] = Field(default=None, max_length=500, description='日志信息') + class JobUpdateSchema(JobCreateSchema): """定时任务更新模型""" - ... id: int = Field(..., gt=0, description="ID") class JobOutSchema(JobCreateSchema, BaseSchema): """定时任务响应模型""" model_config = ConfigDict(from_attributes=True) - ... \ No newline at end of file From b66fab05537d46ce5736d33f1e2b8e3854402b48 Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Tue, 26 Aug 2025 20:08:16 +0800 Subject: [PATCH 2/9] =?UTF-8?q?refactor(cache):=E4=BC=98=E5=8C=96=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9B=91=E6=8E=A7=E4=BF=A1=E6=81=AF=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/monitor/cache_schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/api/v1/schemas/monitor/cache_schema.py b/backend/app/api/v1/schemas/monitor/cache_schema.py index 571152d9..a74c2088 100644 --- a/backend/app/api/v1/schemas/monitor/cache_schema.py +++ b/backend/app/api/v1/schemas/monitor/cache_schema.py @@ -17,7 +17,7 @@ class CacheInfoSchema(BaseModel): """缓存对象信息模型""" model_config = ConfigDict(from_attributes=True) - cache_key: str = Field(default='', description='缓存键名') - cache_name: str = Field(default='', description='缓存名称') + cache_key: str = Field(..., description='缓存键名') + cache_name: str = Field(..., description='缓存名称') cache_value: Any = Field(default=None, description='缓存值') remark: Optional[str] = Field(default=None, description='备注说明') From 0781db32bee1f64d4f67fece41516a69514e0d20 Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Tue, 26 Aug 2025 20:09:20 +0800 Subject: [PATCH 3/9] =?UTF-8?q?refactor(online):=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=9C=A8=E7=BA=BF=E7=94=A8=E6=88=B7=E6=A8=A1=E5=9E=8B=E5=B1=9E?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/monitor/online_schema.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/api/v1/schemas/monitor/online_schema.py b/backend/app/api/v1/schemas/monitor/online_schema.py index b1faacb5..11a26d52 100644 --- a/backend/app/api/v1/schemas/monitor/online_schema.py +++ b/backend/app/api/v1/schemas/monitor/online_schema.py @@ -13,10 +13,10 @@ class OnlineOutSchema(BaseModel): model_config = ConfigDict(from_attributes=True) - name: Optional[str] = Field(default=None, description='用户名称') - session_id: Optional[str] = Field(default=None, description='会话编号') - user_id: Optional[int] = Field(default=None, description='用户ID') - user_name: Optional[str] = Field(default=None, description='用户名') + name: str = Field(..., description='用户名称') + session_id: str = Field(..., description='会话编号') + user_id: int = Field(..., description='用户ID') + user_name: str = Field(..., description='用户名') ipaddr: Optional[str] = Field(default=None, description='登陆IP地址') login_location: Optional[str] = Field(default=None, description='登录所属地') os: Optional[str] = Field(default=None, description='操作系统') From 2f1a3100179a6c65abdfe7e9601103db13cc7923 Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Tue, 26 Aug 2025 20:17:46 +0800 Subject: [PATCH 4/9] =?UTF-8?q?refactor(auth):=E4=BC=98=E5=8C=96=E6=9D=83?= =?UTF-8?q?=E9=99=90=E8=AE=A4=E8=AF=81=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/system/auth_schema.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/app/api/v1/schemas/system/auth_schema.py b/backend/app/api/v1/schemas/system/auth_schema.py index 8c92dc20..af0fc1c7 100644 --- a/backend/app/api/v1/schemas/system/auth_schema.py +++ b/backend/app/api/v1/schemas/system/auth_schema.py @@ -2,7 +2,7 @@ from typing import Optional, Union from datetime import datetime -from pydantic import ConfigDict, Field, BaseModel, model_validator +from pydantic import ConfigDict, Field, BaseModel, field_validator from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import Session @@ -15,7 +15,7 @@ class AuthSchema(BaseModel): user: Optional[UserOutSchema] = Field(default=None, description='用户信息') check_data_scope: bool = Field(default=True, description='是否检查数据权限') - db: AsyncSession | Session = Field(default=None, description='数据库会话') + db: AsyncSession | Session | None = Field(default=None, description='数据库会话') class JWTPayloadSchema(BaseModel): @@ -24,11 +24,12 @@ class JWTPayloadSchema(BaseModel): is_refresh: bool = Field(default=False, description='是否刷新token') 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: + @field_validator('sub') + @classmethod + def validate_sub(cls, value: str): + if not value or len(value.strip()) == 0: raise ValueError("会话编号不能为空") - return data + return value class JWTOutSchema(BaseModel): From 551c260e512148064eb600a648cd09694d7b75ba Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Tue, 26 Aug 2025 20:20:00 +0800 Subject: [PATCH 5/9] =?UTF-8?q?refactor(config):=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=85=8D=E7=BD=AE=E6=A8=A1=E5=9E=8B=E5=B1=9E?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/system/config_schema.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/app/api/v1/schemas/system/config_schema.py b/backend/app/api/v1/schemas/system/config_schema.py index bd671724..9e2850cd 100644 --- a/backend/app/api/v1/schemas/system/config_schema.py +++ b/backend/app/api/v1/schemas/system/config_schema.py @@ -17,23 +17,23 @@ class ConfigCreateSchema(BaseModel): class ConfigUpdateSchema(ConfigCreateSchema): """配置更新模型""" - id: int = Field(description="主键ID") + id: int = Field(..., description="主键ID") class ConfigOutSchema(ConfigCreateSchema, BaseSchema): """配置响应模型""" model_config = ConfigDict(from_attributes=True) - id: int = Field(description="主键ID") + id: int = Field(..., description="主键ID") -class UpdataSystemConfigSchema(BaseSchema): +class UpdataSystemConfigSchema(BaseModel): """更新系统配置""" sys_web_title: str = Field(description="网站标题") - sys_web_description: str = Field(description="网站描述") + sys_web_description: str = Field(description="网站描述") sys_web_favicon: str = Field(description="网站图标") sys_web_logo: str = Field(description="网站logo") - sys_login_background: str = Field(description="登录背景") - sys_web_copyright: str = Field(description="版权信息") + sys_login_background: str = Field(description="登录背景") + sys_web_copyright: str = Field(description="版权信息") sys_keep_record: str = Field(description="备案号") sys_help_doc: str = Field(description="帮助文档") sys_web_privacy: str = Field(description="隐私条款") From cdfeb6318ff049510542586d513b9c7163f98e7d Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Tue, 26 Aug 2025 20:27:08 +0800 Subject: [PATCH 6/9] =?UTF-8?q?refactor(dept):=E4=BC=98=E5=8C=96=E9=83=A8?= =?UTF-8?q?=E9=97=A8=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7=20refactor(menu):?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=8F=9C=E5=8D=95=E6=A8=A1=E5=9E=8B=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=20refactor(notice):=E4=BC=98=E5=8C=96=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E9=80=9A=E7=9F=A5=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=20refactor(position):=E4=BC=98=E5=8C=96=E5=B2=97=E4=BD=8D?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7=20refactor(role):=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=A7=92=E8=89=B2=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=20refactor(user):=E4=BC=98=E5=8C=96=E7=94=A8=E6=88=B7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/api/v1/schemas/system/dept_schema.py | 19 ++++++----- .../app/api/v1/schemas/system/menu_schema.py | 12 +++---- .../api/v1/schemas/system/notice_schema.py | 6 ++-- .../api/v1/schemas/system/position_schema.py | 10 +++--- .../app/api/v1/schemas/system/role_schema.py | 15 ++++----- .../app/api/v1/schemas/system/user_schema.py | 33 +++++++++++-------- 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/backend/app/api/v1/schemas/system/dept_schema.py b/backend/app/api/v1/schemas/system/dept_schema.py index ad13eaee..e02b3238 100644 --- a/backend/app/api/v1/schemas/system/dept_schema.py +++ b/backend/app/api/v1/schemas/system/dept_schema.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- from typing import Any, Optional -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator, validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from app.core.base_schema import BaseSchema -from app.core.validator import DateTimeStr -from app.core.logger import logger + class DeptCreateSchema(BaseModel): """部门创建模型""" @@ -15,20 +14,22 @@ class DeptCreateSchema(BaseModel): parent_id: Optional[int] = Field(default=None, ge=0, description="父部门ID") description: Optional[str] = Field(default=None, max_length=500, description="备注说明") - @model_validator(mode='after') - def validate_fields(self): - if not self.name or len(self.name.strip()) == 0: + @field_validator('name') + @classmethod + def validate_name(cls, value: str): + if not value or len(value.strip()) == 0: raise ValueError("部门名称不能为空") - self.name = self.name.replace(" ", "") - return self + value = value.replace(" ", "") + return value class DeptUpdateSchema(DeptCreateSchema): """部门更新模型""" - id: int = Field(..., gt=0, description="部门ID") + id: int = Field(..., ge=1, description="部门ID") class DeptOutSchema(DeptCreateSchema, BaseSchema): """部门响应模型""" model_config = ConfigDict(from_attributes=True) + parent_name: Optional[str] = Field(default=None, max_length=40, description="父部门名称") diff --git a/backend/app/api/v1/schemas/system/menu_schema.py b/backend/app/api/v1/schemas/system/menu_schema.py index f237ede4..f22a41e9 100644 --- a/backend/app/api/v1/schemas/system/menu_schema.py +++ b/backend/app/api/v1/schemas/system/menu_schema.py @@ -12,7 +12,7 @@ class MenuCreateSchema(BaseModel): name: str = Field(..., max_length=50, description="菜单名称") type: int = Field(..., ge=1, le=4, description="菜单类型(1:目录 2:菜单 3:按钮 4:外链)") icon: Optional[str] = Field(default=None, max_length=100, description="菜单图标") - order: int = Field(default=1, ge=0, description="显示顺序") + order: int = Field(..., ge=1, description="显示顺序") permission: Optional[str] = Field(default=None, max_length=100, description="权限标识") route_name: Optional[str] = Field(default=None, max_length=100, description="路由名称") route_path: Optional[str] = Field(default=None, max_length=200, description="路由地址") @@ -25,22 +25,22 @@ class MenuCreateSchema(BaseModel): title: Optional[str] = Field(default=None, max_length=50, description="菜单标题") params: Optional[list[dict[str, str]]] = Field(default=None, description="路由参数,格式为[{key: string, value: string}]") affix: bool = Field(default=False, description="是否固定标签页(True:是 False:否)") - parent_id: Optional[int] = Field(default=None, ge=0, description="父菜单ID") + 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): """菜单更新模型""" - id: int = Field(..., gt=0, description="菜单ID") + id: int = Field(..., ge=1, description="菜单ID") parent_name: Optional[str] = Field(default=None, max_length=50, description="父菜单名称") class MenuOutSchema(MenuCreateSchema, BaseSchema): """菜单响应模型""" model_config = ConfigDict(from_attributes=True) + parent_name: Optional[str] = Field(default=None, max_length=50, description="父菜单名称") \ No newline at end of file diff --git a/backend/app/api/v1/schemas/system/notice_schema.py b/backend/app/api/v1/schemas/system/notice_schema.py index d336bfd8..80db6a03 100644 --- a/backend/app/api/v1/schemas/system/notice_schema.py +++ b/backend/app/api/v1/schemas/system/notice_schema.py @@ -11,13 +11,13 @@ class NoticeCreateSchema(BaseModel): notice_title: str = Field(..., max_length=50, description='公告标题') notice_type: str = Field(..., description='公告类型(1通知 2公告)') notice_content: str = Field(..., description='公告内容') - status: bool = Field(True, description="是否启用(True:启用 False:禁用)") - description: Optional[str] = Field(None, max_length=255, description="公告描述") + status: bool = Field(default=True, description="是否启用(True:启用 False:禁用)") + description: Optional[str] = Field(default=None, max_length=255, description="公告描述") class NoticeUpdateSchema(NoticeCreateSchema): """公告通知更新模型""" - id: int = Field(..., gt=0, description="公告通知ID") + id: int = Field(..., ge=1, description="公告通知ID") class NoticeOutSchema(NoticeCreateSchema, BaseSchema): diff --git a/backend/app/api/v1/schemas/system/position_schema.py b/backend/app/api/v1/schemas/system/position_schema.py index 3cf04c31..66b73f47 100644 --- a/backend/app/api/v1/schemas/system/position_schema.py +++ b/backend/app/api/v1/schemas/system/position_schema.py @@ -11,12 +11,12 @@ class PositionCreateSchema(BaseModel): name: str = Field(..., max_length=40, description="岗位名称") order: Optional[int] = Field(default=1, ge=1, description='显示排序') status: bool = Field(default=True, description="是否启用(True:启用 False:禁用)") - description: Optional[str] = Field(None, description="备注说明") + description: Optional[str] = Field(default=None, description="备注说明") class PositionUpdateSchema(PositionCreateSchema): """岗位更新模型""" - id: int = Field(..., gt=0, description="岗位ID") + id: int = Field(..., ge=1, description="岗位ID") class PositionOutSchema(PositionCreateSchema, BaseSchema): @@ -27,6 +27,6 @@ class PositionOutSchema(PositionCreateSchema, BaseSchema): class PositionOptionsOut(PositionCreateSchema): model_config = ConfigDict(from_attributes=True) - id: int = Field(description="主键ID") - created_at: DateTimeStr = Field(description="创建时间") - updated_at: DateTimeStr = Field(description="更新时间") \ No newline at end of file + id: int = Field(..., description="主键ID") + created_at: DateTimeStr = Field(..., description="创建时间") + updated_at: DateTimeStr = Field(..., description="更新时间") diff --git a/backend/app/api/v1/schemas/system/role_schema.py b/backend/app/api/v1/schemas/system/role_schema.py index 7928fc16..dbe600ba 100644 --- a/backend/app/api/v1/schemas/system/role_schema.py +++ b/backend/app/api/v1/schemas/system/role_schema.py @@ -11,7 +11,7 @@ from app.core.validator import DateTimeStr class RoleCreateSchema(BaseModel): """角色创建模型""" - name: str = Field(default=None, max_length=15, description="角色名称") + name: str = Field(..., max_length=15, description="角色名称") order: Optional[int] = Field(default=1, ge=1, description='显示排序') data_scope: Optional[int] = Field(default=1, ge=1, le=5, description='数据权限范围') status: bool = Field(default=True, description="是否启用") @@ -25,16 +25,15 @@ 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): """角色更新模型""" - id: int = Field(..., gt=0, description="角色ID") + id: int = Field(..., ge=1, description="角色ID") class RoleOutSchema(RoleCreateSchema, BaseSchema): @@ -48,8 +47,8 @@ class RoleOutSchema(RoleCreateSchema, BaseSchema): class RoleOptionsOut(RoleCreateSchema): model_config = ConfigDict(from_attributes=True) - id: int = Field(description="主键ID") - created_at: DateTimeStr = Field(description="创建时间") - updated_at: DateTimeStr = Field(description="更新时间") + id: int = Field(..., description="主键ID") + created_at: DateTimeStr = Field(..., description="创建时间") + updated_at: DateTimeStr = Field(..., description="更新时间") menus: List[MenuOutSchema] = Field(default_factory=list, description='角色菜单列表') depts: List[DeptOutSchema] = Field(default_factory=list, description='角色部门列表') \ No newline at end of file diff --git a/backend/app/api/v1/schemas/system/user_schema.py b/backend/app/api/v1/schemas/system/user_schema.py index 3fbbd688..86177284 100644 --- a/backend/app/api/v1/schemas/system/user_schema.py +++ b/backend/app/api/v1/schemas/system/user_schema.py @@ -12,59 +12,63 @@ from app.core.base_schema import BaseSchema class CurrentUserUpdateSchema(BaseModel): """基础用户信息""" - name: str = Field(default=None, max_length=15, description="名称") + name: str = Field(..., max_length=15, description="名称") mobile: Optional[str] = Field(default=None, description="手机号") email: Optional[EmailStr] = Field(default=None, description="邮箱") gender: Optional[str] = Field(default=None, description="性别") avatar: Optional[str] = Field(default=None, description="头像") - @classmethod @field_validator("mobile") + @classmethod def validate_mobile(cls, value: Optional[str]): return mobile_validator(value) class UserRegisterSchema(BaseModel): """注册""" - name: str = Field(default=None, max_length=15, description="名称") + name: str = Field(..., max_length=15, description="名称") mobile: Optional[str] = Field(default=None, description="手机号") - username: str = Field(default=None, max_length=15, description="账号") - password: str = Field(default=None, max_length=128, description="密码哈希值") + username: str = Field(..., max_length=15, description="账号") + password: str = Field(..., max_length=128, description="密码哈希值") role_ids: Optional[List[int]] = Field(default=[2], description='角色ID') creator_id: Optional[int] = Field(default=1, description='创建人ID') description: Optional[str] = Field(default=f'注册用户{username}', max_length=250, description="备注") + @field_validator("mobile") + @classmethod + def validate_mobile(cls, value: Optional[str]): + return mobile_validator(value) class UserForgetPasswordSchema(BaseModel): """忘记密码""" - username: str = Field(default=None, max_length=15, description="用户名") + username: str = Field(..., max_length=15, description="用户名") mobile: Optional[str] = Field(default=None, description="手机号") - new_password: str = Field(default=None, max_length=128, description="新密码") + new_password: str = Field(..., max_length=128, description="新密码") - @classmethod @field_validator("mobile") + @classmethod def validate_mobile(cls, value: Optional[str]): return mobile_validator(value) class UserChangePasswordSchema(BaseModel): """修改密码""" - old_password: str = Field(default=None, max_length=128, description="旧密码") - new_password: str = Field(default=None, max_length=128, description="新密码") + old_password: str = Field(..., max_length=128, description="旧密码") + new_password: str = Field(..., max_length=128, description="新密码") class ResetPasswordSchema(BaseModel): """修改密码""" id: int = Field(..., description="主键ID") - password: str = Field(default=None, min_length=6, max_length=128, description="新密码") + password: str = Field(..., min_length=6, max_length=128, description="新密码") class UserCreateSchema(CurrentUserUpdateSchema): """新增""" model_config = ConfigDict(from_attributes=True) - username: str = Field(default=None, max_length=15, description="用户名") - password: Optional[str] = Field(default=None, max_length=128, description="密码哈希值") + username: str = Field(..., max_length=15, description="用户名") + password: str = Field(..., max_length=128, description="密码哈希值") status: bool = Field(default=True, description="是否可用") is_superuser: bool = Field(default=False, description="是否超管") description: Optional[str] = Field(None, max_length=255, description="备注") @@ -76,7 +80,8 @@ class UserCreateSchema(CurrentUserUpdateSchema): class UserUpdateSchema(UserCreateSchema): """更新""" - model_config = ConfigDict(from_attributes=True, exclude={"password"}) + model_config = ConfigDict(from_attributes=True) + id: int = Field(..., description="主键ID") From 028526b1102de083f967b11ca69ae19d7d5e53fe Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Thu, 28 Aug 2025 17:51:13 +0800 Subject: [PATCH 7/9] =?UTF-8?q?refactor(dict):=E4=BC=98=E5=8C=96=E5=AD=97?= =?UTF-8?q?=E5=85=B8=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/api/v1/models/system/dict_model.py | 4 +- .../app/api/v1/params/system/dict_param.py | 4 +- .../app/api/v1/schemas/system/dict_schema.py | 93 ++++++++----------- backend/sql/fastapi_vue_admin.sql | 4 +- 4 files changed, 46 insertions(+), 59 deletions(-) diff --git a/backend/app/api/v1/models/system/dict_model.py b/backend/app/api/v1/models/system/dict_model.py index 30daa0e3..7011a344 100644 --- a/backend/app/api/v1/models/system/dict_model.py +++ b/backend/app/api/v1/models/system/dict_model.py @@ -18,7 +18,7 @@ class DictTypeModel(ModelBase): id = Column(Integer, primary_key=True, autoincrement=True, comment='字典主键') dict_name = Column(String(100), nullable=True, unique=True, default='', comment='字典名称') dict_type = Column(String(100), nullable=True, unique=True, default='', comment='字典类型') - status = Column(Boolean, nullable=True, default='0', comment='状态(0正常 1停用)') + status = Column(Boolean, nullable=True, default='1', comment='状态(1正常 0停用)') # 审计字段 description = Column(Text, nullable=True, comment="备注说明") @@ -56,7 +56,7 @@ class DictDataModel(ModelBase): css_class = Column(String(100), nullable=True, default=None, comment='样式属性(其他样式扩展)') list_class = Column(String(100), nullable=True, default=None, comment='表格回显样式') is_default = Column(Boolean, nullable=True, default='N', comment='是否默认(Y是 N否)') - status = Column(Boolean, nullable=True, default='0', comment='状态(0正常 1停用)') + status = Column(Boolean, nullable=True, default='1', comment='状态(1正常 0停用)') # 审计字段 description = Column(Text, nullable=True, comment="备注说明") diff --git a/backend/app/api/v1/params/system/dict_param.py b/backend/app/api/v1/params/system/dict_param.py index 4f2f5e84..1b5909a0 100644 --- a/backend/app/api/v1/params/system/dict_param.py +++ b/backend/app/api/v1/params/system/dict_param.py @@ -14,7 +14,7 @@ class DictTypeQueryParams: self, dict_name: Optional[str] = Query(None, description="字典名称"), dict_type: Optional[str] = Query(None, description="字典类型"), - status: Optional[bool] = Query(None, description="状态(0正常 1停用)"), + status: Optional[bool] = Query(None, description="状态(1正常 0停用)"), creator: Optional[int] = Query(None, description="创建人"), start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"), end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"), @@ -43,7 +43,7 @@ class DictDataQueryParams: self, dict_label: Optional[str] = Query(None, description="字典标签"), dict_type: Optional[str] = Query(None, description="字典类型"), - status: Optional[bool] = Query(None, description="状态(0正常 1停用)"), + status: Optional[bool] = Query(None, description="状态(1正常 0停用)"), creator: Optional[int] = Query(None, description="创建人"), start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"), end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"), diff --git a/backend/app/api/v1/schemas/system/dict_schema.py b/backend/app/api/v1/schemas/system/dict_schema.py index 9ef367f5..042a473e 100644 --- a/backend/app/api/v1/schemas/system/dict_schema.py +++ b/backend/app/api/v1/schemas/system/dict_schema.py @@ -1,6 +1,6 @@ -from pydantic import BaseModel, ConfigDict, Field -from pydantic_validation_decorator import NotBlank, Pattern, Size -from typing import Literal, Optional +import re +from pydantic import BaseModel, ConfigDict, Field, field_validator +from typing import Optional from app.core.base_schema import BaseSchema @@ -10,29 +10,25 @@ class DictTypeCreateSchema(BaseModel): 字典类型表对应pydantic模型 """ - dict_name: Optional[str] = Field(default=None, description='字典名称') - dict_type: Optional[str] = Field(default=None, description='字典类型') - status: Optional[bool] = Field(default=None, description='状态(0正常 1停用)') + dict_name: str = Field(..., min_length=1, max_length=100, description='字典名称') + dict_type: str = Field(..., min_length=1, max_length=100, description='字典类型') + status: Optional[bool] = Field(default=None, description='状态(1正常 0停用)') description: Optional[str] = Field(None, max_length=255, description="描述") - @NotBlank(field_name='dict_name', message='字典名称不能为空') - @Size(field_name='dict_name', min_length=0, max_length=100, message='字典类型名称长度不能超过100个字符') - def get_dict_name(self): - return self.dict_name + @field_validator('dict_name') + def validate_dict_name(cls, value: str): + if not value or value.strip() == '': + raise ValueError('字典名称不能为空') + return value - @NotBlank(field_name='dict_type', message='字典类型不能为空') - @Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型类型长度不能超过100个字符') - @Pattern( - field_name='dict_type', - regexp='^[a-z][a-z0-9_]*$', - message='字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)', - ) - def get_dict_type(self): - return self.dict_type - - def validate_fields(self): - self.get_dict_name() - self.get_dict_type() + @field_validator('dict_type') + def validate_dict_type(cls, value: str): + if not value or value.strip() == '': + raise ValueError('字典类型不能为空') + regexp = r'^[a-z][a-z0-9_]*$' + if not re.match(regexp, value): + raise ValueError('字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)') + return value class DictTypeUpdateSchema(DictTypeCreateSchema): @@ -45,46 +41,37 @@ class DictTypeOutSchema(DictTypeCreateSchema, BaseSchema): model_config = ConfigDict(from_attributes=True) - class DictDataCreateSchema(BaseModel): """ 字典数据表对应pydantic模型 """ - - dict_sort: Optional[int] = Field(default=None, description='字典排序') - dict_label: Optional[str] = Field(default=None, description='字典标签') - dict_value: Optional[str] = Field(default=None, description='字典键值') - dict_type: Optional[str] = Field(default=None, description='字典类型') - css_class: Optional[str] = Field(default=None, description='样式属性(其他样式扩展)') + dict_sort: int = Field(..., gt=0, description='字典排序') + dict_label: str = Field(..., max_length=100, description='字典标签') + dict_value: str = Field(..., max_length=100, description='字典键值') + dict_type: str = Field(..., max_length=100, description='字典类型') + css_class: Optional[str] = Field(default=None, max_length=100, description='样式属性(其他样式扩展)') list_class: Optional[str] = Field(default=None, description='表格回显样式') is_default: Optional[bool] = Field(default=None, description='是否默认(Y是 N否)') - status: Optional[bool] = Field(default=None, description='状态(0正常 1停用)') + status: Optional[bool] = Field(default=None, description='状态(1正常 0停用)') description: Optional[str] = Field(default=None, max_length=255, description="描述") - @NotBlank(field_name='dict_label', message='字典标签不能为空') - @Size(field_name='dict_label', min_length=0, max_length=100, message='字典标签长度不能超过100个字符') - def get_dict_label(self): - return self.dict_label + @field_validator('dict_label') + def validate_dict_label(cls, value: str): + if not value or value.strip() == '': + raise ValueError('字典标签不能为空') + return value - @NotBlank(field_name='dict_value', message='字典键值不能为空') - @Size(field_name='dict_value', min_length=0, max_length=100, message='字典键值长度不能超过100个字符') - def get_dict_value(self): - return self.dict_value + @field_validator('dict_value') + def validate_dict_value(cls, value: str): + if not value or value.strip() == '': + raise ValueError('字典键值不能为空') + return value - @NotBlank(field_name='dict_type', message='字典类型不能为空') - @Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型长度不能超过100个字符') - def get_dict_type(self): - return self.dict_type - - @Size(field_name='css_class', min_length=0, max_length=100, message='样式属性长度不能超过100个字符') - def get_css_class(self): - return self.css_class - - def validate_fields(self): - self.get_dict_label() - self.get_dict_value() - self.get_dict_type() - self.get_css_class() + @field_validator('dict_type') + def validate_dict_type(cls, value: str): + if not value or value.strip() == '': + raise ValueError('字典类型不能为空') + return value class DictDataUpdateSchema(DictDataCreateSchema): diff --git a/backend/sql/fastapi_vue_admin.sql b/backend/sql/fastapi_vue_admin.sql index 769ac3e6..f6ce2316 100644 --- a/backend/sql/fastapi_vue_admin.sql +++ b/backend/sql/fastapi_vue_admin.sql @@ -166,7 +166,7 @@ CREATE TABLE `system_dict_data` ( `css_class` varchar(100) DEFAULT NULL COMMENT '样式属性(其他样式扩展)', `list_class` varchar(100) DEFAULT NULL COMMENT '表格回显样式', `is_default` tinyint(1) DEFAULT NULL COMMENT '是否默认(Y是 N否)', - `status` tinyint(1) DEFAULT NULL COMMENT '状态(0正常 1停用)', + `status` tinyint(1) DEFAULT NULL COMMENT '状态(1正常 0停用)', `description` text COMMENT '备注说明', `created_at` datetime DEFAULT NULL COMMENT '创建时间', `updated_at` datetime DEFAULT NULL COMMENT '更新时间', @@ -224,7 +224,7 @@ CREATE TABLE `system_dict_type` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '字典主键', `dict_name` varchar(100) DEFAULT NULL COMMENT '字典名称', `dict_type` varchar(100) DEFAULT NULL COMMENT '字典类型', - `status` tinyint(1) DEFAULT NULL COMMENT '状态(0正常 1停用)', + `status` tinyint(1) DEFAULT NULL COMMENT '状态(1正常 0停用)', `description` text COMMENT '备注说明', `created_at` datetime DEFAULT NULL COMMENT '创建时间', `updated_at` datetime DEFAULT NULL COMMENT '更新时间', From ffa1d4710241cfc8fa3274f3ebe89f029ff52048 Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Fri, 29 Aug 2025 11:21:59 +0800 Subject: [PATCH 8/9] =?UTF-8?q?refactor(user):=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/system/user_schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/app/api/v1/schemas/system/user_schema.py b/backend/app/api/v1/schemas/system/user_schema.py index 86177284..4fdee682 100644 --- a/backend/app/api/v1/schemas/system/user_schema.py +++ b/backend/app/api/v1/schemas/system/user_schema.py @@ -83,12 +83,14 @@ class UserUpdateSchema(UserCreateSchema): model_config = ConfigDict(from_attributes=True) id: int = Field(..., description="主键ID") + password: Optional[str] = Field(default=None, max_length=128, description="密码哈希值") class UserOutSchema(UserCreateSchema, BaseSchema): """响应""" model_config = ConfigDict(arbitrary_types_allowed=True, from_attributes=True) + password: str = Field(exclude=True) # password 不返回 last_login: Optional[DateTimeStr] = Field(default=None, description="最后登录时间") dept_name: Optional[str] = Field(default=None, description='部门名称') dept: Optional[DeptOutSchema] = Field(default=None, description='部门') From bf1c652c6c7eaa384ae9121978b57a504c7ed77a Mon Sep 17 00:00:00 2001 From: yuan <964343743@qq.com> Date: Fri, 29 Aug 2025 11:24:11 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix(menu):=E4=BF=AE=E5=A4=8D=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/schemas/system/menu_schema.py | 2 +- backend/app/core/validator.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/app/api/v1/schemas/system/menu_schema.py b/backend/app/api/v1/schemas/system/menu_schema.py index f22a41e9..b1a690e6 100644 --- a/backend/app/api/v1/schemas/system/menu_schema.py +++ b/backend/app/api/v1/schemas/system/menu_schema.py @@ -10,7 +10,7 @@ from app.core.base_schema import BaseSchema class MenuCreateSchema(BaseModel): """菜单创建模型""" name: str = Field(..., max_length=50, description="菜单名称") - type: int = Field(..., ge=1, le=4, description="菜单类型(1:目录 2:菜单 3:按钮 4:外链)") + type: Optional[int] = Field(default=None, ge=1, le=4, description="菜单类型(1:目录 2:菜单 3:按钮 4:外链)") icon: Optional[str] = Field(default=None, max_length=100, description="菜单图标") order: int = Field(..., ge=1, description="显示顺序") permission: Optional[str] = Field(default=None, max_length=100, description="权限标识") diff --git a/backend/app/core/validator.py b/backend/app/core/validator.py index e446193f..2489ab2b 100644 --- a/backend/app/core/validator.py +++ b/backend/app/core/validator.py @@ -99,8 +99,8 @@ def menu_request_validator(data): :return: 验证后的请求数据 :raises: CustomException 请求数据无效时抛出 """ - menu_types = {1: "目录", 2: "功能", 3: "权限"} - + menu_types = {1: "目录", 2: "功能", 3: "权限", 4: "外链"} + if data.type not in menu_types: raise CustomException(code=RET.BAD_REQUEST.code, msg=f"菜单类型必须为: {','.join(map(str, menu_types.keys()))}")