提交文件
This commit is contained in:
403
.qoder/repowiki/zh/content/数据模型详解/模型验证规则.md
Normal file
403
.qoder/repowiki/zh/content/数据模型详解/模型验证规则.md
Normal file
@@ -0,0 +1,403 @@
|
||||
# 模型验证规则
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py)
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py)
|
||||
- [backend/app/api/v1/assessments.py](file://backend/app/api/v1/assessments.py)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py)
|
||||
- [backend/app/services/indicator_service.py](file://backend/app/services/indicator_service.py)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py)
|
||||
- [backend/app/main.py](file://backend/app/main.py)
|
||||
- [backend/requirements.txt](file://backend/requirements.txt)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概览](#架构概览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文件系统性梳理了医院绩效系统中的模型验证规则,涵盖数据模型的验证机制,包括字段类型验证、范围限制、格式检查和业务规则验证。文档重点解释了以下三个层面的验证实现:
|
||||
- Pydantic 模型验证:通过 Pydantic 的 Field 参数与类型注解实现请求体与响应体的数据校验。
|
||||
- SQLAlchemy 约束验证:通过数据库层的列类型、索引与 CheckConstraint 实现数据完整性约束。
|
||||
- 业务逻辑验证:通过服务层与 API 层的业务规则检查,确保数据在业务语义上的正确性。
|
||||
|
||||
同时,文档阐述了验证错误的处理方式与用户友好的错误信息设计,并提供验证规则的扩展方法与自定义验证器的实现建议。
|
||||
|
||||
## 项目结构
|
||||
后端采用 FastAPI + SQLAlchemy 异步 ORM 的架构,数据模型位于 models 目录,Pydantic 数据模式位于 schemas 目录,API 路由位于 api/v1 目录,业务逻辑位于 services 目录,异常处理与全局配置位于 main.py。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "API 层"
|
||||
A1["staff.py"]
|
||||
A2["indicators.py"]
|
||||
A3["assessments.py"]
|
||||
end
|
||||
subgraph "服务层"
|
||||
S1["staff_service.py"]
|
||||
S2["indicator_service.py"]
|
||||
S3["assessment_service.py"]
|
||||
end
|
||||
subgraph "模式层"
|
||||
P1["schemas.py"]
|
||||
end
|
||||
subgraph "模型层"
|
||||
M1["models.py"]
|
||||
M2["finance.py"]
|
||||
end
|
||||
subgraph "核心"
|
||||
C1["main.py"]
|
||||
C2["requirements.txt"]
|
||||
end
|
||||
A1 --> S1
|
||||
A2 --> S2
|
||||
A3 --> S3
|
||||
S1 --> M1
|
||||
S2 --> M1
|
||||
S3 --> M1
|
||||
A1 --> P1
|
||||
A2 --> P1
|
||||
A3 --> P1
|
||||
M1 --> C2
|
||||
M2 --> C2
|
||||
P1 --> C2
|
||||
C1 --> A1
|
||||
C1 --> A2
|
||||
C1 --> A3
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L1-L124)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py#L1-L142)
|
||||
- [backend/app/api/v1/assessments.py](file://backend/app/api/v1/assessments.py#L1-L166)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py#L1-L112)
|
||||
- [backend/app/services/indicator_service.py](file://backend/app/services/indicator_service.py#L1-L197)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py#L114-L262)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L1-L743)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L1-L438)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py#L1-L79)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L50-L91)
|
||||
- [backend/requirements.txt](file://backend/requirements.txt#L1-L16)
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L1-L124)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py#L1-L142)
|
||||
- [backend/app/api/v1/assessments.py](file://backend/app/api/v1/assessments.py#L1-L166)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py#L1-L112)
|
||||
- [backend/app/services/indicator_service.py](file://backend/app/services/indicator_service.py#L1-L197)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py#L114-L262)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L1-L743)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L1-L438)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py#L1-L79)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L50-L91)
|
||||
- [backend/requirements.txt](file://backend/requirements.txt#L1-L16)
|
||||
|
||||
## 核心组件
|
||||
- Pydantic 数据模式:通过 Field 的类型、长度、数值范围、正则表达式等参数实现字段级验证;通过枚举类型保证取值域的正确性;通过 model_config 的 from_attributes 控制 ORM 对象到 Pydantic 模式的转换。
|
||||
- SQLAlchemy 模型:通过列类型(String、Integer、Numeric、Boolean、DateTime、Enum)、索引与 CheckConstraint 实现数据库层约束;通过外键关系与级联策略保证参照完整性。
|
||||
- API 层:在路由函数中接收 Pydantic 模式作为请求体,自动触发 Pydantic 验证;在业务逻辑允许范围内进行额外的业务规则检查,并抛出 HTTP 异常。
|
||||
- 服务层:封装 CRUD 与业务逻辑,负责数据一致性与业务规则的执行;在必要时进行跨实体的约束检查。
|
||||
- 异常处理:全局注册异常处理器,捕获并记录验证错误与业务异常,返回统一的错误信息。
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L49-L60)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L62-L85)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py#L45-L74)
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L68-L81)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py#L69-L91)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L58-L74)
|
||||
|
||||
## 架构概览
|
||||
下图展示了从 API 请求到数据库写入的完整验证链路,包括 Pydantic 验证、业务规则检查与数据库约束验证。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant API as "API 路由"
|
||||
participant Pyd as "Pydantic 模式"
|
||||
participant Svc as "服务层"
|
||||
participant DB as "SQLAlchemy 模型"
|
||||
participant SQL as "数据库"
|
||||
Client->>API : "POST /staff"
|
||||
API->>Pyd : "StaffCreate 验证"
|
||||
Pyd-->>API : "验证通过/错误"
|
||||
API->>Svc : "调用业务逻辑"
|
||||
Svc->>DB : "构造模型实例"
|
||||
DB->>SQL : "执行插入/更新"
|
||||
SQL-->>DB : "约束检查结果"
|
||||
DB-->>Svc : "返回持久化结果"
|
||||
Svc-->>API : "返回业务结果"
|
||||
API-->>Client : "统一响应"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L68-L81)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L107-L124)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py#L69-L76)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L88-L114)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### Pydantic 模型验证
|
||||
- 字段类型与长度验证:通过 Field 的类型注解与 max_length/min_length 限制字符串长度;例如员工工号、姓名、职位、电话、邮箱等字段均设置了长度限制。
|
||||
- 数值范围验证:通过 ge(大于等于)、le(小于等于)、gt(大于)等参数限制数值范围;例如基本工资、绩效系数、权重、最高分值、年份、月份等。
|
||||
- 枚举验证:通过 str + Enum 的组合确保取值域的正确性;例如科室类型、员工状态、指标类型、考核状态等。
|
||||
- 正则表达式验证:通过 pattern 参数对字符串格式进行约束;例如用户角色字段使用正则限制合法取值。
|
||||
- 响应模式转换:通过 model_config(from_attributes=True) 将 ORM 对象直接转换为 Pydantic 模式,避免重复映射。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class StaffBase {
|
||||
+employee_id : str
|
||||
+name : str
|
||||
+department_id : int
|
||||
+position : str
|
||||
+title : Optional[str]
|
||||
+phone : Optional[str]
|
||||
+email : Optional[str]
|
||||
+base_salary : float
|
||||
+performance_ratio : float
|
||||
}
|
||||
class StaffCreate {
|
||||
+status : StaffStatus
|
||||
+hire_date : Optional[datetime]
|
||||
}
|
||||
class StaffResponse {
|
||||
+id : int
|
||||
+status : StaffStatus
|
||||
+hire_date : Optional[datetime]
|
||||
+created_at : datetime
|
||||
+updated_at : datetime
|
||||
+department_name : Optional[str]
|
||||
}
|
||||
StaffCreate --|> StaffBase
|
||||
StaffResponse --|> StaffBase
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L107-L150)
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L64-L98)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L107-L150)
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L315-L345)
|
||||
|
||||
### SQLAlchemy 约束验证
|
||||
- 列类型与默认值:通过 String、Integer、Numeric、Boolean、DateTime、Enum 等类型定义字段属性与默认值;例如 Numeric(10,2) 用于金额与分数的精确存储。
|
||||
- 索引优化:为常用查询字段添加索引,提升查询性能;例如科室类型、状态、年月组合等。
|
||||
- CheckConstraint:通过 CheckConstraint 在数据库层强制业务约束;例如指标权重必须大于 0,财务金额必须大于等于 0。
|
||||
- 外键关系:通过 ForeignKey 建立实体间的参照关系,配合级联策略保证数据一致性。
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
STAFF {
|
||||
int id PK
|
||||
string employee_id UK
|
||||
string name
|
||||
int department_id FK
|
||||
string position
|
||||
string title
|
||||
string phone
|
||||
string email
|
||||
numeric base_salary
|
||||
numeric performance_ratio
|
||||
enum status
|
||||
date hire_date
|
||||
}
|
||||
DEPARTMENTS {
|
||||
int id PK
|
||||
string name
|
||||
string code UK
|
||||
enum dept_type
|
||||
int parent_id FK
|
||||
int level
|
||||
int sort_order
|
||||
boolean is_active
|
||||
}
|
||||
INDICATORS {
|
||||
int id PK
|
||||
string name
|
||||
string code UK
|
||||
enum indicator_type
|
||||
enum bs_dimension
|
||||
numeric weight
|
||||
numeric max_score
|
||||
numeric target_value
|
||||
string target_unit
|
||||
text calculation_method
|
||||
text assessment_method
|
||||
text deduction_standard
|
||||
text data_source
|
||||
string applicable_dept_types
|
||||
boolean is_veto
|
||||
boolean is_active
|
||||
}
|
||||
ASSESSMENTS {
|
||||
int id PK
|
||||
int staff_id FK
|
||||
int period_year
|
||||
int period_month
|
||||
string period_type
|
||||
numeric total_score
|
||||
numeric weighted_score
|
||||
enum status
|
||||
int assessor_id FK
|
||||
int reviewer_id FK
|
||||
datetime submit_time
|
||||
datetime review_time
|
||||
text remark
|
||||
}
|
||||
ASSESSMENT_DETAILS {
|
||||
int id PK
|
||||
int assessment_id FK
|
||||
int indicator_id FK
|
||||
numeric actual_value
|
||||
numeric score
|
||||
text evidence
|
||||
text remark
|
||||
}
|
||||
SALARY_RECORDS {
|
||||
int id PK
|
||||
int staff_id FK
|
||||
int period_year
|
||||
int period_month
|
||||
numeric base_salary
|
||||
numeric performance_score
|
||||
numeric performance_bonus
|
||||
numeric deduction
|
||||
numeric allowance
|
||||
numeric total_salary
|
||||
string status
|
||||
text remark
|
||||
}
|
||||
DEPARTMENTS ||--o{ STAFF : "拥有"
|
||||
STAFF ||--o{ ASSESSMENTS : "被考核"
|
||||
ASSESSMENTS ||--o{ ASSESSMENT_DETAILS : "包含"
|
||||
INDICATORS ||--o{ ASSESSMENT_DETAILS : "被评分"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L62-L114)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L117-L146)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L149-L178)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L181-L202)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L205-L230)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py#L45-L74)
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L143-L146)
|
||||
- [backend/app/models/finance.py](file://backend/app/models/finance.py#L68-L74)
|
||||
|
||||
### 业务逻辑验证
|
||||
- API 层业务规则:在创建与更新接口中,先进行 Pydantic 验证,再进行业务规则检查;例如创建员工前检查工号唯一性,创建指标前检查编码唯一性。
|
||||
- 服务层业务规则:在服务层进行更复杂的业务一致性检查;例如更新考核记录时,仅允许在草稿或已驳回状态下进行修改,并重新计算总分与加权分。
|
||||
- 统一错误处理:通过 HTTPException 抛出业务错误,结合全局异常处理器记录日志并返回统一的错误响应。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["开始"]) --> ValidatePyd["Pydantic 验证"]
|
||||
ValidatePyd --> PydanticOK{"验证通过?"}
|
||||
PydanticOK --> |否| ReturnError["返回验证错误"]
|
||||
PydanticOK --> |是| BusinessCheck["业务规则检查"]
|
||||
BusinessCheck --> BusinessOK{"业务规则通过?"}
|
||||
BusinessOK --> |否| RaiseHTTP["抛出 HTTP 异常"]
|
||||
BusinessOK --> |是| Persist["持久化到数据库"]
|
||||
Persist --> Done(["结束"])
|
||||
ReturnError --> Done
|
||||
RaiseHTTP --> Done
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L75-L78)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py#L78-L81)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py#L114-L151)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L58-L74)
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L68-L95)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py#L71-L98)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py#L114-L151)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L58-L74)
|
||||
|
||||
### 验证错误处理与用户友好信息
|
||||
- HTTP 异常:在业务规则不满足时,使用 HTTPException 返回明确的状态码与错误信息,如“员工不存在”、“工号已存在”、“指标不存在”等。
|
||||
- 统一响应结构:API 返回统一的响应结构,包含状态码、消息、数据、分页信息等,便于前端展示与调试。
|
||||
- 全局异常处理:注册 RequestValidationError 与 StarletteHTTPException 的处理器,记录错误日志并向上抛出,确保错误信息一致且可追踪。
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L60-L61)
|
||||
- [backend/app/api/v1/indicators.py](file://backend/app/api/v1/indicators.py#L66-L67)
|
||||
- [backend/app/api/v1/assessments.py](file://backend/app/api/v1/assessments.py#L99-L101)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L58-L74)
|
||||
|
||||
### 验证规则扩展与自定义验证器
|
||||
- 扩展 Pydantic 验证:可在现有 Field 参数基础上增加更多约束,如自定义正则表达式、长度范围、数值边界等;对于复杂字段(如 JSON),可使用 Json 类型与自定义解析器。
|
||||
- 自定义验证器:可通过 Pydantic 的 field_validator 或 root_validator 实现跨字段验证与复杂业务规则;例如在创建考核时,验证明细中的指标权重与总分计算的一致性。
|
||||
- 数据库约束增强:在模型层新增 CheckConstraint 或复合索引,以强化数据完整性;例如为“员工工号+部门”的唯一性约束添加复合索引。
|
||||
- 业务规则扩展:在服务层增加新的业务规则检查点,如在创建考核时检查指标是否适用于当前科室类型、是否启用等。
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/schemas/schemas.py](file://backend/app/schemas/schemas.py#L153-L192)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L143-L146)
|
||||
- [backend/app/services/assessment_service.py](file://backend/app/services/assessment_service.py#L114-L151)
|
||||
|
||||
## 依赖关系分析
|
||||
- 版本要求:项目使用 FastAPI、SQLAlchemy、Pydantic 等核心依赖,版本在 requirements.txt 中声明,确保验证与 ORM 功能的稳定性。
|
||||
- 模块间耦合:API 层依赖 Pydantic 模式与服务层;服务层依赖 SQLAlchemy 模型;模型层依赖数据库驱动与 Alembic 迁移工具。
|
||||
- 异常处理:全局异常处理器统一处理验证与业务异常,减少重复代码并提升用户体验。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Req["requirements.txt"] --> FastAPI["FastAPI"]
|
||||
Req --> SQLAlchemy["SQLAlchemy"]
|
||||
Req --> Pydantic["Pydantic"]
|
||||
API["API 路由"] --> PydanticSchema["Pydantic 模式"]
|
||||
API --> Service["服务层"]
|
||||
Service --> Model["SQLAlchemy 模型"]
|
||||
Model --> DB["数据库"]
|
||||
Main["main.py"] --> API
|
||||
Main --> Service
|
||||
Main --> Model
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [backend/requirements.txt](file://backend/requirements.txt#L1-L16)
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L10-L15)
|
||||
- [backend/app/services/staff_service.py](file://backend/app/services/staff_service.py#L9-L10)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L13-L13)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L50-L77)
|
||||
|
||||
**章节来源**
|
||||
- [backend/requirements.txt](file://backend/requirements.txt#L1-L16)
|
||||
- [backend/app/main.py](file://backend/app/main.py#L50-L77)
|
||||
|
||||
## 性能考虑
|
||||
- 索引优化:为高频查询字段(如科室类型、状态、年月组合)建立索引,降低查询成本。
|
||||
- 数值精度:使用 Numeric 类型存储金额与分数,避免浮点误差;合理设置精度与小数位数。
|
||||
- 查询优化:在服务层使用 selectinload 等策略预加载关联对象,减少 N+1 查询问题。
|
||||
- 异步 I/O:利用 SQLAlchemy 异步引擎与 FastAPI 的异步特性,提升并发处理能力。
|
||||
|
||||
## 故障排除指南
|
||||
- Pydantic 验证失败:检查请求体字段类型、长度与范围是否符合模式定义;查看响应中的错误字段与原因。
|
||||
- 业务规则失败:确认业务状态是否允许当前操作(如仅草稿或已驳回状态可更新);检查唯一性约束冲突(如工号、指标编码)。
|
||||
- 数据库约束失败:检查 CheckConstraint 与索引是否满足;确认外键关系是否正确。
|
||||
- 异常处理:查看全局异常处理器的日志输出,定位具体错误位置与堆栈信息。
|
||||
|
||||
**章节来源**
|
||||
- [backend/app/main.py](file://backend/app/main.py#L58-L74)
|
||||
- [backend/app/api/v1/staff.py](file://backend/app/api/v1/staff.py#L75-L78)
|
||||
- [backend/app/models/models.py](file://backend/app/models/models.py#L143-L146)
|
||||
|
||||
## 结论
|
||||
本系统通过“Pydantic 模式验证 + SQLAlchemy 数据库约束 + 服务层业务规则”的三层验证机制,确保了数据在进入业务流程前后的完整性与一致性。API 层负责输入验证与业务规则检查,服务层负责复杂业务逻辑与数据一致性,模型层负责数据结构与约束定义。配合统一的异常处理与日志记录,系统在保证功能正确的同时,也具备良好的可维护性与可扩展性。未来可在 Pydantic 中引入自定义验证器与复杂字段解析,在模型层增加更多数据库约束,并在服务层扩展业务规则检查点,进一步完善验证体系。
|
||||
Reference in New Issue
Block a user