import{_ as i,o as a,c as t,a2 as n}from"./chunks/framework.B-QBKwc1.js";const o=JSON.parse('{"title":"数据删除规范","description":"本页规定业务数据的软删除约定与物理删除禁区,开发涉及删除功能时按本规范实现以保证数据可追溯。","frontmatter":{},"headers":[],"relativePath":"zh/standards/deletion.md","filePath":"zh/standards/deletion.md"}'),l={name:"zh/standards/deletion.md"};function h(e,s,k,p,d,r){return a(),t("div",null,[...s[0]||(s[0]=[n(`
概述
本页规定业务数据的软删除约定与物理删除禁区,开发涉及删除功能时按本规范实现以保证数据可追溯。
核心原则
所有业务数据统一使用软删除机制,通过 is_delete 字段标记记录状态,不物理删除数据(SoftDeletesByFlag Trait + SoftDeleteFlagScope 全局范围,不使用 deleted_at)。
| is_delete 值 | 含义 |
|---|---|
0 | 正常记录(默认值) |
1 | 已删除记录 |
所有查询方法自动过滤 is_delete = 0,业务层无需关心已删除数据。
DELETE /api/{module}/delete/{id}#[OperationLog('案例演示-删除', OperationLog::TYPE_DELETE, '删除案例ID:{id}')]
#[Permission('sys:example:delete', '删除案例')]
public function delete(int $id): JsonResponse
{
return $this->_delete($this->exampleService, $id);
}Controller::delete($id)
│
▼
Controller 基类::_delete($service, $id)
│
▼
Service::delete($id) → HasCrudOperations::doDelete($id)
│ (DB::transaction 包裹)
├─ beforeDelete($id) 钩子(抛异常可拦截,事务回滚)
│
├─ $model->find($id)
│ └─ 不存在 → return false
│
├─ $model->softDelete() → UPDATE SET is_delete = 1
│
└─ afterDelete($id) 钩子DELETE /api/{module}/batchDelete
Content-Type: application/json
{ "ids": [1, 2, 3] }#[OperationLog('案例演示-批量删除', OperationLog::TYPE_DELETE)]
#[Permission('sys:example:batchDelete', '批量删除案例')]
public function batchDelete(): JsonResponse
{
$ids = $this->getJsonBody()['ids'] ?? [];
return $this->_batchDelete($this->exampleService, $ids);
}Controller::batchDelete()
│
▼
Controller 基类::_batchDelete($service, $ids)
│
├─ ids 为空 → 返回失败 "请选择要删除的数据"
│
▼
Service::batchDelete($ids) → HasCrudOperations::doBatchDelete($ids)
│ (DB::transaction 包裹)
├─ beforeBatchDelete($ids) 钩子(整体校验)
│
├─ 逐条 beforeDelete($id) 校验
│
├─ Model::batchSoftDelete($ids)
│ └─ UPDATE SET is_delete = 1 WHERE id IN (...)
│
└─ afterBatchDelete($ids) 钩子权限码区别
单条删除权限为 sys:{模块}:delete,批量删除权限为 sys:{模块}:batchDelete(代码生成器创建菜单时分别生成),前端按钮需分别控制显隐。
在 Service 中重写 beforeDelete 方法,抛异常可拦截删除(事务自动回滚):
class DeptService extends BaseTenantService
{
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('该部门下有用户,不可删除');
}
}
}可用钩子(均在 HasCrudOperations 中声明):beforeDelete($id)、afterDelete($id)、beforeBatchDelete($ids)、afterBatchDelete($ids)。
| 场景 | 方式 | 说明 |
|---|---|---|
| 日志数据清理 | 定时任务 | 通过「定时任务」模块(job 表)配置清理 N 天前的日志 |
| 临时数据 | 定时任务 | 清理过期的临时文件 |
| 其它 | 审批 | 需经过管理员审批 |
<el-popconfirm title="确认删除该记录?" @confirm="handleDelete(row.id)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
</template>
</el-popconfirm><el-button
type="danger"
:disabled="!selectedIds.length"
@click="handleBatchDelete"
>
批量删除 ({{ selectedIds.length }})
</el-button>