签入版本
This commit is contained in:
@@ -1,156 +1,54 @@
|
||||
# 12.4 数据删除规范
|
||||
# 12.6 数据删除规范
|
||||
|
||||
## 软删除约定
|
||||
|
||||
:::tip 核心原则
|
||||
所有业务数据统一使用软删除机制,通过 `is_delete` 字段标记记录状态,不物理删除数据。
|
||||
:::tip 建议遵守
|
||||
删除的默认形态是软删除;物理删除是受控例外。删除前做引用守卫,删除后可追溯。
|
||||
:::
|
||||
|
||||
| is_delete 值 | 含义 |
|
||||
|--------------|------|
|
||||
| `0` | 正常记录(默认值) |
|
||||
| `1` | 已删除记录 |
|
||||
## 默认:软删除
|
||||
|
||||
所有查询方法自动过滤 `is_delete = 0`,业务层无需关心已删除数据。
|
||||
```go
|
||||
// repository 标准入口
|
||||
repo.SoftDelete(ctx, "id", id)
|
||||
repo.SoftDeleteByID(ctx, id)
|
||||
|
||||
## 单条删除
|
||||
|
||||
### URL 格式
|
||||
|
||||
```text
|
||||
DELETE /api/{module}/delete/{id}
|
||||
// 批量:基座 BatchDelete(逐条软删)
|
||||
spec.BatchDelete(ctx, ids)
|
||||
```
|
||||
|
||||
### Controller 示例
|
||||
软删后对查询不可见(Ctx 注入 del_flag=0),数据保留可审计、可恢复(运维按需 UPDATE 回 0)。
|
||||
|
||||
```php
|
||||
#[Log('案例演示-删除记录', Log::TYPE_DELETE, '删除案例演示ID:{id}')]
|
||||
#[Permission('sys:example:delete', '删除案例演示')]
|
||||
public function delete(int $id): Json
|
||||
{
|
||||
return parent::_remove($this->logic, $id);
|
||||
}
|
||||
## 例外:物理删除
|
||||
|
||||
仅限**数据本身有保留期语义**的场景(超期日志、临时文件索引):
|
||||
|
||||
```go
|
||||
ctx = hooks.WithPhysicalDelete(ctx) // 显式放行
|
||||
```
|
||||
|
||||
### 内部流程
|
||||
放行要收敛在最小函数作用域,注释说明依据(如"超过 job.logRetentionDays")。
|
||||
|
||||
```text
|
||||
Controller::delete($id)
|
||||
│
|
||||
▼
|
||||
BaseController::_remove($logic, $id)
|
||||
│
|
||||
▼
|
||||
BaseLogic::delete($id)
|
||||
│
|
||||
├─ beforeDelete($id) 钩子(抛异常可拦截)
|
||||
│
|
||||
├─ $model->find($id)
|
||||
│ └─ 不存在 → return false
|
||||
│
|
||||
├─ $model->save(['is_delete' => 1])
|
||||
│
|
||||
└─ afterDelete($id) 钩子
|
||||
```
|
||||
## 删除守卫
|
||||
|
||||
## 批量删除
|
||||
删除被引用数据前做存在性检查,冲突时上抛守卫错误:
|
||||
|
||||
### 请求格式
|
||||
| 错误码 | 场景 |
|
||||
|---|---|
|
||||
| CodeHasChildren | 树形表存在下级(部门/菜单/分类) |
|
||||
| CodeInUse | 关键记录仍被引用(用户在角色中等) |
|
||||
|
||||
```text
|
||||
DELETE /api/{module}/batchDelete
|
||||
Content-Type: application/json
|
||||
守卫检查用 `ExistsByCond`(软删/租户已过滤)。
|
||||
|
||||
{ "ids": [1, 2, 3] }
|
||||
```
|
||||
## 前端交互
|
||||
|
||||
### Controller 示例
|
||||
- 危险删除(批量、不可逆)必须二次确认
|
||||
- 删除失败展示后端 msg(守卫原因)
|
||||
- 不做"假删除"(前端移除行但不调接口)
|
||||
|
||||
```php
|
||||
#[Log('案例演示-批量删除', Log::TYPE_DELETE)]
|
||||
#[Permission('sys:example:delete', '删除案例演示')]
|
||||
public function batchDelete(): Json
|
||||
{
|
||||
$ids = $this->getJsonBody()['ids'] ?? [];
|
||||
return parent::_batchRemove($this->logic, $ids);
|
||||
}
|
||||
```
|
||||
## 审计要求
|
||||
|
||||
### 内部流程
|
||||
删除操作经 RouteSpec Log/LogType=delete 留操作日志——谁删了什么可追溯。物理删除场景本身不落业务表,但任务日志留有执行记录。
|
||||
|
||||
```text
|
||||
Controller::batchDelete()
|
||||
│
|
||||
▼
|
||||
BaseController::_batchRemove($logic, $ids)
|
||||
│
|
||||
├─ ids 为空 → 返回失败 "请选择要删除的数据"
|
||||
│
|
||||
▼
|
||||
BaseLogic::batchDelete($ids)
|
||||
│
|
||||
├─ beforeBatchDelete($ids) 钩子(整体校验)
|
||||
│
|
||||
├─ 逐条 beforeDelete($id) 校验
|
||||
│
|
||||
├─ Model::batchSoftDelete($ids)
|
||||
│ └─ UPDATE SET is_delete = 1 WHERE id IN (...)
|
||||
│
|
||||
└─ afterBatchDelete($ids) 钩子
|
||||
```
|
||||
## 相关
|
||||
|
||||
## 删除前校验(beforeDelete 钩子)
|
||||
|
||||
在 Logic 中重写 `beforeDelete` 方法,抛异常可拦截删除:
|
||||
|
||||
```php
|
||||
class UserLogic extends BaseTenantLogic
|
||||
{
|
||||
protected function beforeDelete(int $id): void
|
||||
{
|
||||
// 检查是否有子级
|
||||
$children = Dept::where('parent_id', $id)->where('is_delete', 0)->count();
|
||||
if ($children > 0) {
|
||||
throw new \Exception('存在子部门,不可删除');
|
||||
}
|
||||
|
||||
// 检查是否被引用
|
||||
$userCount = User::where('dept_id', $id)->where('is_delete', 0)->count();
|
||||
if ($userCount > 0) {
|
||||
throw new \Exception('该部门下有用户,不可删除');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 何时可物理删除
|
||||
|
||||
| 场景 | 方式 | 说明 |
|
||||
|------|------|------|
|
||||
| 日志数据清理 | 定时任务 | CleanLogTask 清理 N 天前的日志 |
|
||||
| 临时数据 | 定时任务 | 清理过期的临时文件 |
|
||||
| 其它 | 审批 | 需经过管理员审批 |
|
||||
|
||||
## 前端删除规范
|
||||
|
||||
### 单条删除
|
||||
|
||||
```vue
|
||||
<el-popconfirm title="确认删除该记录?" @confirm="handleDelete(row.id)">
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
```
|
||||
|
||||
### 批量删除
|
||||
|
||||
```vue
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="!selectedIds.length"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
批量删除 ({{ selectedIds.length }})
|
||||
</el-button>
|
||||
```
|
||||
- [12.3 数据访问铁律](/zh/standards/data-access.html)
|
||||
- [4.8 横切机制](/zh/architecture/cross-cutting.html)
|
||||
|
||||
Reference in New Issue
Block a user