197 lines
8.7 KiB
Markdown
197 lines
8.7 KiB
Markdown
# Repository 层
|
||
|
||
Repository 层是数据访问层,封装所有数据库查询操作。项目通过 `BaseRepository` 基类提供通用 CRUD 能力,子类只需声明 `model` 并按需实现 `page()` 和 `list_enabled()` 方法。
|
||
|
||
## 文件位置
|
||
|
||
```
|
||
src/modules/system/position/repository.py
|
||
```
|
||
|
||
## 完整代码
|
||
|
||
以岗位模块为例,`src/modules/system/position/repository.py` 完整内容:
|
||
|
||
```python
|
||
"""
|
||
岗位仓储模块
|
||
=============================================================
|
||
|
||
岗位数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
|
||
"""
|
||
|
||
from src.common.base_repository import BaseRepository
|
||
from src.common.constants.common import PAGE_SIZE
|
||
from src.modules.system.position.models import Position
|
||
|
||
|
||
class PositionRepository(BaseRepository):
|
||
"""岗位数据仓储,提供岗位相关的数据库访问操作"""
|
||
|
||
model = Position
|
||
|
||
@classmethod
|
||
def page(cls, params: dict):
|
||
"""
|
||
岗位分页查询
|
||
|
||
支持按名称模糊搜索、按状态精确过滤,按 sort 字段升序排列。
|
||
|
||
Args:
|
||
params: 查询参数字典
|
||
- pageNo: 当前页码
|
||
- pageSize: 每页记录数
|
||
- name: 岗位名称(模糊匹配)
|
||
- status: 状态(精确匹配)
|
||
|
||
Returns:
|
||
tuple: (items, total, pages)
|
||
- items: 当前页数据列表
|
||
- total: 总记录数
|
||
- pages: 总页数
|
||
"""
|
||
return cls.find_page(
|
||
page=int(params.get('pageNo', 1)),
|
||
page_size=int(params.get('pageSize', PAGE_SIZE)),
|
||
filters={'status': params.get('status')},
|
||
like_filters={'name': params.get('name')},
|
||
order_by='sort',
|
||
)
|
||
|
||
@classmethod
|
||
def list_enabled(cls, params=None):
|
||
"""
|
||
查询启用的岗位列表
|
||
|
||
用于下拉选择场景,仅返回启用状态的岗位(status=1 为硬性条件,不可传参覆盖)。
|
||
支持与分页一致的条件筛选(name 模糊匹配)。
|
||
|
||
Args:
|
||
params: 查询参数字典,可包含 name 字段用于模糊搜索
|
||
|
||
Returns:
|
||
list: 启用状态的岗位实例列表,按 sort 升序排列
|
||
"""
|
||
params = params or {}
|
||
return cls.find_list(
|
||
filters={'status': 1},
|
||
like_filters={'name': params.get('name')},
|
||
order_by='sort',
|
||
)
|
||
```
|
||
|
||
## 代码解析
|
||
|
||
### 基类继承
|
||
|
||
```python
|
||
class PositionRepository(BaseRepository):
|
||
model = Position
|
||
```
|
||
|
||
`BaseRepository` 是基类,子类只需声明 `model` 即可获得通用 CRUD 能力。所有方法均为 `@classmethod`,无需创建实例。
|
||
|
||
### 必须实现的方法
|
||
|
||
| 方法 | 说明 | 何时需要 |
|
||
|:---|:---|:---|
|
||
| `page(params)` | 分页查询 | 所有模块(Service 的 `get_page` 依赖此方法) |
|
||
| `list_enabled(params)` | 无分页列表查询 | 需要下拉选择的模块(Service 的 `get_list` 依赖此方法) |
|
||
|
||
`page()` 和 `list_enabled()` 内部调用基类的 `find_page()` 和 `find_list()` 方法,传入筛选条件和排序规则。
|
||
|
||
### BaseRepository 提供的通用方法
|
||
|
||
| 方法 | 说明 |
|
||
|:---|:---|
|
||
| `get_by_id(obj_id)` | 按主键查询单条未删除记录 |
|
||
| `get_by_ids(ids)` | 按主键列表批量查询未删除记录 |
|
||
| `first_by(filters)` | 按等值条件查询单条记录 |
|
||
| `exists_by(filters, exclude_id)` | 检查是否存在未删除记录(唯一性校验) |
|
||
| `count_by(filters)` | 按等值条件统计未删除记录数 |
|
||
| `exists(obj_id)` | 检查未删除记录是否存在 |
|
||
| `count_children(parent_id)` | 统计子记录数(树形结构删除前校验) |
|
||
| `find_page(...)` | 统一分页查询(支持等值/模糊/范围过滤) |
|
||
| `find_list(...)` | 统一不分页查询 |
|
||
| `create(**kwargs)` | 创建并保存新实例 |
|
||
| `update_by_id(obj_id, data)` | 按主键更新字段 |
|
||
| `update_status(obj_id, status)` | 单独更新状态字段 |
|
||
| `delete_by_id(obj_id)` | 软删除单条记录 |
|
||
| `batch_delete(ids)` | 批量软删除 |
|
||
| `batch_restore(ids)` | 批量恢复已删除记录 |
|
||
|
||
### 何时需要自定义方法
|
||
|
||
大多数模块只需实现 `page()` 和 `list_enabled()`。当需要以下场景时,才在子类中添加自定义方法:
|
||
|
||
- **复杂联表查询**:如 User 需要关联 Dept、Role 查询
|
||
- **特殊聚合查询**:如统计、分组
|
||
- **非标准查询条件**:如区间查询、模糊 + 精确组合
|
||
|
||
示例(UserRepository):
|
||
|
||
```python
|
||
# src/modules/system/user/repository.py
|
||
class UserRepository(BaseRepository):
|
||
"""用户数据仓储"""
|
||
model = User
|
||
|
||
@classmethod
|
||
def page(cls, params: dict):
|
||
"""用户分页查询"""
|
||
return cls.find_page(
|
||
page=int(params.get('pageNo', 1)),
|
||
page_size=int(params.get('pageSize', PAGE_SIZE)),
|
||
like_filters={'realname': params.get('realname')},
|
||
filters={'gender': params.get('gender'), 'status': params.get('status')},
|
||
order_desc='id',
|
||
)
|
||
```
|
||
|
||
## Repository 与 Service 的关系
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Router 层 │
|
||
│ - 定义路由(Blueprint) │
|
||
│ - 权限校验(@check_permission) │
|
||
│ - 参数解析(parse_request_body) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Service 层 │
|
||
│ - 业务逻辑(BaseService 模板方法 + 钩子) │
|
||
│ - 表单校验(WTForms) │
|
||
│ - 存在性校验(Repository.exists_by) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Repository 层 │
|
||
│ - 数据库查询(classmethod) │
|
||
│ - CRUD 操作 │
|
||
│ - 软删除过滤(is_delete == 0) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Model 层 │
|
||
│ - 表结构定义(BaseModel + BaseDB) │
|
||
│ - 字段映射 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 开发要点
|
||
|
||
1. **所有方法均为 classmethod**:无需创建实例,直接通过类调用
|
||
2. **必须实现 `page(params)`**:Service 的 `get_page` 依赖此方法
|
||
3. **按需实现 `list_enabled(params)`**:需要下拉选择的模块实现此方法
|
||
4. **`find_page` / `find_list` 自动过滤软删除**:且跳过空值查询条件
|
||
5. **`exists_by(filters, exclude_id)` 用于唯一性校验**:支持排除自身 ID(更新时校验)
|
||
6. **Repository 层不做响应封装**:返回 SQLAlchemy 查询结果,由 Service 层处理响应
|
||
|
||
## 总结
|
||
|
||
Repository 层继承 `BaseRepository` 获得通用 CRUD 能力,所有方法均为 `classmethod`。子类只需声明 `model` 并实现 `page()` / `list_enabled()` 方法。`find_page` / `find_list` 自动过滤软删除且跳过空值条件,`exists_by` 用于唯一性校验。Repository 层不做响应封装,由 Service 层处理。
|