feat: 优化表单提交逻辑和添加表单验证

refactor: 重构后端模型验证逻辑

fix: 修复资源文件路径处理和下载服务

style: 调整前端样式和布局

perf: 优化AI错误处理和提示信息

docs: 更新上传组件提示信息

test: 添加表单提交测试用例

chore: 更新依赖和配置文件
This commit is contained in:
zhangtao
2025-10-28 23:32:10 +08:00
parent 96335b385f
commit a1f9475f3a
37 changed files with 723 additions and 64 deletions
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
from typing import Optional, List
from pydantic import BaseModel, ConfigDict, Field, EmailStr, field_validator
from pydantic import BaseModel, ConfigDict, Field, EmailStr, field_validator, model_validator
from app.core.validator import DateTimeStr, mobile_validator
from app.core.base_schema import BaseSchema, CommonSchema
from app.api.v1.module_system.role.schema import RoleOutSchema
from urllib.parse import urlparse
class CurrentUserUpdateSchema(BaseModel):
"""基础用户信息"""
@@ -20,6 +21,16 @@ class CurrentUserUpdateSchema(BaseModel):
def validate_mobile(cls, value: Optional[str]):
return mobile_validator(value)
@field_validator("avatar")
@classmethod
def validate_avatar(cls, value: Optional[str]):
if not value:
return value
parsed = urlparse(value)
if parsed.scheme in ("http", "https") and parsed.netloc:
return value
raise ValueError("头像地址需为有效的HTTP/HTTPS URL")
class UserRegisterSchema(BaseModel):
"""注册"""
@@ -36,6 +47,36 @@ class UserRegisterSchema(BaseModel):
def validate_mobile(cls, value: Optional[str]):
return mobile_validator(value)
@field_validator("username")
@classmethod
def validate_username(cls, value: str):
v = value.strip()
if not v:
raise ValueError("账号不能为空")
# 字母开头,允许字母数字_.-
import re
if not re.match(r"^[A-Za-z][A-Za-z0-9_.-]{2,31}$", v):
raise ValueError("账号需字母开头,3-32位,仅含字母/数字/_ . -")
return v
@model_validator(mode='before')
@classmethod
def _normalize(cls, values):
if isinstance(values, dict):
for k in ["name", "username", "password", "description"]:
if k in values and isinstance(values[k], str):
values[k] = values[k].strip() or values[k]
# role_ids 去重并转为 int
if "role_ids" in values and values["role_ids"] is not None:
try:
values["role_ids"] = list[int]({int(x) for x in values["role_ids"]})
except Exception:
pass
# mobile 空串转 None
if "mobile" in values and isinstance(values["mobile"], str) and values["mobile"].strip() == "":
values["mobile"] = None
return values
class UserForgetPasswordSchema(BaseModel):
"""忘记密码"""
@@ -75,6 +116,35 @@ class UserCreateSchema(CurrentUserUpdateSchema):
role_ids: Optional[List[int]] = Field(default=[], description='角色ID')
position_ids: Optional[List[int]] = Field(default=[], description='岗位ID')
@model_validator(mode='before')
@classmethod
def _normalize(cls, values):
if isinstance(values, dict):
# 字符串去空格和空串转 None
for k in ["username", "password", "description", "name"]:
if k in values and isinstance(values[k], str):
values[k] = values[k].strip() or None if values[k].strip() == "" else values[k].strip()
# bool 兼容
for k in ["status", "is_superuser"]:
if k in values:
v = values[k]
if isinstance(v, str):
values[k] = v.strip().lower() in {"true", "1", "yes", "y"}
# 列表转 int 去重
for k in ["role_ids", "position_ids"]:
if k in values and values[k] is not None:
try:
values[k] = list({int(x) for x in values[k]})
except Exception:
pass
return values
@model_validator(mode='after')
def _validate_after(self):
if self.status is False and (not self.description or not str(self.description).strip()):
raise ValueError("禁用状态下必须填写备注描述")
return self
class UserUpdateSchema(UserCreateSchema):
"""更新"""