192 lines
7.0 KiB
Markdown
192 lines
7.0 KiB
Markdown
:::tip 案例演示
|
||
案例演示模块是代码生成器的参考模板,展示了标准 CRUD 模块的完整实现。包含文件字段处理、枚举字典映射、状态管理等功能,可作为新模块开发的参考范例。
|
||
:::
|
||
|
||
## 模块结构
|
||
|
||
```
|
||
src/modules/example/
|
||
├── models.py # 案例模型
|
||
├── schema.py # 表单验证(WTForms)
|
||
├── repository.py # 数据访问层
|
||
├── service.py # 业务逻辑层(继承 BaseService)
|
||
└── router.py # 路由控制器
|
||
```
|
||
|
||
## 案例模型
|
||
|
||
```python
|
||
# src/modules/example/models.py
|
||
class Example(BaseModel, BaseDB):
|
||
"""
|
||
案例演示模型
|
||
|
||
包含文件字段(avatar)、枚举字段(type/status)等典型业务字段。
|
||
"""
|
||
__tablename__ = DB_PREFIX + "example"
|
||
__table_comment__ = "案例演示表"
|
||
|
||
# 案例名称
|
||
name = db.Column(db.String(255), nullable=False, index=True, comment="案例名称")
|
||
# 案例图片(存相对路径)
|
||
avatar = db.Column(db.String(255), nullable=False, comment="案例图片")
|
||
# 案例类型:1-类型1 2-类型2 3-类型3 4-类型4
|
||
type = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
|
||
comment="案例类型:1-类型1 2-类型2 3-类型3 4-类型4")
|
||
# 案例状态:1-在用 2-停用
|
||
status = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
|
||
index=True, comment="案例状态:1-在用 2-停用")
|
||
# 排序
|
||
sort = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
|
||
comment="案例排序")
|
||
```
|
||
|
||
## 服务层实现
|
||
|
||
```python
|
||
# src/modules/example/service.py
|
||
class ExampleService(BaseService):
|
||
"""
|
||
案例演示服务类
|
||
|
||
展示文件字段处理、枚举映射等标准 CRUD 模式。
|
||
"""
|
||
repository = ExampleRepository
|
||
form = ExampleForm
|
||
status_form = ExampleStatusForm
|
||
model_name = "案例演示"
|
||
|
||
@classmethod
|
||
def before_add(cls, form):
|
||
"""新增前钩子:处理文件字段"""
|
||
_handle_file_fields(form)
|
||
|
||
@classmethod
|
||
def before_update(cls, form, obj):
|
||
"""更新前钩子:处理文件字段"""
|
||
_handle_file_fields(form)
|
||
|
||
@classmethod
|
||
def _enrich_page_records(cls, records):
|
||
"""分页富化:文件字段拼接 URL"""
|
||
for data in records:
|
||
data["avatar"] = get_file_url(data.get("avatar"))
|
||
return records
|
||
|
||
@classmethod
|
||
def _detail_dict(cls, obj):
|
||
"""详情序列化:文件字段拼接 URL"""
|
||
data = model_to_dict(obj)
|
||
data["avatar"] = get_file_url(data.get("avatar"))
|
||
return data
|
||
|
||
|
||
def _handle_file_fields(form):
|
||
"""文件字段处理:临时文件迁移到正式目录"""
|
||
avatar = form.data.get("avatar")
|
||
if avatar:
|
||
form.avatar.data = save_file(avatar, "example")
|
||
```
|
||
|
||
### 文件字段处理模式
|
||
|
||
这是项目中处理文件字段的标准模式:
|
||
|
||
| 步骤 | 时机 | 处理 |
|
||
|:---|:---|:---|
|
||
| 1. 前端上传 | 用户选择文件 | 上传到临时目录,获得临时 URL |
|
||
| 2. 提交表单 | 前端提交 | 将临时 URL 传给后端 |
|
||
| 3. `before_add`/`before_update` | Service 钩子 | `save_file` 迁移到正式目录 |
|
||
| 4. 存储数据库 | Repository.create | 存相对路径 |
|
||
| 5. 查询返回 | `_detail_dict`/`_enrich_page_records` | `get_file_url` 拼接完整 URL |
|
||
|
||
### 枚举字段映射
|
||
|
||
通过 `serialize_maps` 声明枚举字段与字典编码的映射:
|
||
|
||
```python
|
||
class ExampleService(BaseService):
|
||
serialize_maps = {
|
||
'type': 'example_type', # 案例类型 → 数据字典
|
||
'status': 'example_status' # 案例状态 → 数据字典
|
||
}
|
||
```
|
||
|
||
查询结果自动包含 `typeText` 和 `statusText` 字段。
|
||
|
||
## 表单验证
|
||
|
||
```python
|
||
# src/modules/example/schema.py
|
||
class ExampleForm(BaseForm):
|
||
"""案例创建/编辑表单"""
|
||
name = StringField('案例名称', validators=[
|
||
DataRequired(message='案例名称不能为空'),
|
||
Length(max=255, message='案例名称长度不得超过255个字符')
|
||
])
|
||
avatar = StringField('案例图片', validators=[Optional(), Length(max=255)])
|
||
type = IntegerField('案例类型', validators=[
|
||
Optional(), NumberRange(min=1, max=4, message='案例类型值在1~4之间')
|
||
])
|
||
status = IntegerField('案例状态', validators=[
|
||
Optional(), NumberRange(min=1, max=2, message='案例状态值在1~2之间')
|
||
])
|
||
sort = IntegerField('案例排序', validators=[
|
||
Optional(), NumberRange(min=0, max=99999)
|
||
])
|
||
|
||
def validate_name(self, field):
|
||
"""案例名称唯一性校验"""
|
||
if not field.data:
|
||
return
|
||
current_id = self.id.data if self.id.data else None
|
||
if ExampleRepository.exists_by({'name': field.data}, exclude_id=current_id):
|
||
raise ValidationError("案例名称已存在")
|
||
|
||
|
||
class ExampleStatusForm(BaseForm):
|
||
"""案例状态更新表单"""
|
||
id = IntegerField('案例ID', validators=[
|
||
DataRequired(message='案例ID不能为空'),
|
||
NumberRange(min=1, message='案例ID必须大于0')
|
||
])
|
||
status = IntegerField('案例状态', validators=[
|
||
DataRequired(message='案例状态不能为空'),
|
||
NumberRange(min=1, max=2, message='案例状态值在1~2之间')
|
||
])
|
||
```
|
||
|
||
## API 接口
|
||
|
||
| 方法 | 路径 | 权限节点 | 说明 |
|
||
|:---|:---|:---|:---|
|
||
| GET | `/api/v1/example/page` | `sys:example:page` | 分页查询 |
|
||
| GET | `/api/v1/example/detail/<id>` | `sys:example:detail` | 详情查询 |
|
||
| GET | `/api/v1/example/list` | `sys:example:list` | 列表查询(无分页) |
|
||
| POST | `/api/v1/example/add` | `sys:example:add` | 新增案例 |
|
||
| PUT | `/api/v1/example/update` | `sys:example:update` | 编辑案例 |
|
||
| DELETE | `/api/v1/example/delete/<id>` | `sys:example:delete` | 删除案例 |
|
||
| DELETE | `/api/v1/example/batchDelete` | `sys:example:batchDelete` | 批量删除 |
|
||
| PUT | `/api/v1/example/status` | `sys:example:status` | 设置状态 |
|
||
|
||
## 数据字典
|
||
|
||
| 字典编码 | 用途 | 字典项示例 |
|
||
|:---|:---|:---|
|
||
| `example_type` | 案例类型名称 | 1=类型1, 2=类型2, 3=类型3, 4=类型4 |
|
||
| `example_status` | 案例状态名称 | 1=在用, 2=停用 |
|
||
|
||
## 作为代码生成器参考
|
||
|
||
案例演示模块是代码生成器的参考模板,新模块开发时可参考其模式:
|
||
|
||
1. **模型定义**:继承 `BaseModel + BaseDB`,字段注释完整
|
||
2. **表单验证**:继承 `BaseForm`,`validate_` 方法实现唯一性校验
|
||
3. **数据访问**:继承 `BaseRepository`,实现 `page()` / `list_enabled()`
|
||
4. **业务服务**:继承 `BaseService`,声明 `repository` / `form` / `model_name`
|
||
5. **路由控制**:Blueprint + `@check_permission` + `@check_demo` + `@operation_log`
|
||
|
||
## 总结
|
||
|
||
案例演示模块是标准 CRUD 模块的参考范例,展示了文件字段处理(`save_file`/`get_file_url`)、枚举字典映射(`serialize_maps`)、状态管理(`update_status`)等典型模式。新模块开发或代码生成器生成的代码均以此模块为参考。
|