Files
2026-10-10 12:20:37 +08:00

177 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 12.4 数据删除规范
## 软删除约定
:::tip 核心原则
所有业务数据统一使用软删除机制,通过 `is_delete` 字段标记记录状态,不物理删除数据。
:::
| is_delete 值 | 含义 |
|--------------|------|
| `0` | 正常记录(默认值) |
| `1` | 已删除记录 |
所有查询方法自动过滤 `is_delete = 0`,业务层无需关心已删除数据。
## 单条删除
### URL 格式
```text
DELETE /api/{module}/delete/{id}
```
### Controller 示例
```php
#[Log('案例演示-删除记录', Log::TYPE_DELETE, '删除案例演示ID:{id}')]
#[Permission('sys:example:delete', '删除案例演示')]
public function delete(int $id): Json
{
return parent::_remove($this->logic, $id);
}
```
### 内部流程
```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) 钩子
```
## 批量删除
### 请求格式
```text
DELETE /api/{module}/batchDelete
Content-Type: application/json
{ "ids": [1, 2, 3] }
```
### Controller 示例
```php
#[Log('案例演示-批量删除', Log::TYPE_DELETE)]
#[Permission('sys:example:delete', '删除案例演示')]
public function batchDelete(): Json
{
$ids = $this->getJsonBody()['ids'] ?? [];
return parent::_batchRemove($this->logic, $ids);
}
```
### 内部流程
```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
<!-- 方式一:Popconfirm 气泡确认 -->
<a-popconfirm title="确认删除该记录?" @confirm="handleDelete(record.id)">
<template #content>
<span>确认删除该记录?</span>
</template>
<a-button danger size="small">删除</a-button>
</a-popconfirm>
<!-- 方式二:Modal.confirm 对话框确认(项目通用做法) -->
<a-button danger size="small" @click="handleDelete(record.id)">删除</a-button>
<script setup>
import { Modal, message } from 'ant-design-vue';
const handleDelete = async (id) => {
Modal.confirm({
title: '提示',
content: '确定要删除该记录吗?',
onOk: async () => {
await exampleDelete(id);
message.success('删除成功');
reloadTable();
},
});
};
</script>
```
### 批量删除
```vue
<a-button
danger
:disabled="!selectedIds.length"
@click="handleBatchDelete"
>
批量删除 ({{ selectedIds.length }})
</a-button>
```