# 删除规范 ## 概述 统一使用软删除机制,通过 `is_delete` 字段标记记录状态,不物理删除数据。删除操作分为单条删除和批量删除两种方式。 :::tip 软删除约定 - `is_delete = 0`:正常记录(默认值) - `is_delete = 1`:已删除记录 - 所有查询方法自动过滤 `is_delete = 0`,业务层无需关心 ::: ## 单条删除 ### URL 格式 ``` DELETE /api/v1/{module}/delete/{id} ``` 支持逗号分隔的多 ID 删除: ``` DELETE /api/v1/position/delete/1,2,3 ``` ### Handler 示例 ```python class PositionDeleteHandler(BaseHandler): @permission_required("sys:position:delete") @check_demo @operation_log("岗位管理", "删除") async def delete(self, id): """根据ID删除单个岗位""" return await position_service.delete(self, id) ``` :::tip 装饰器顺序 删除接口的装饰器顺序:`@permission_required` → `@check_demo` → `@operation_log` ::: ## 批量删除 ### 请求格式 ``` POST /api/v1/{module}/batchDelete Content-Type: application/json [1, 2, 3] ``` ### Handler 示例 ```python class PositionBatchDeleteHandler(BaseHandler): @permission_required("sys:position:delete") @check_demo @operation_log("岗位管理", "批量删除") async def post(self): """批量删除岗位""" return await position_service.batch_delete(self) ``` ## 删除流程 ### 原理 Repository 层的 `batch_delete` 只做数据原语(软删除 + 返回条数),Service 层负责 R 响应封装和文案统一。 ```python async def delete(self, handler, ids) -> R: try: id_list = list(dict.fromkeys(parse_id_list(str(ids)))) if not id_list: return R.failed(handler, "记录ID不存在") if len(self.repo.get_by_ids(id_list)) != len(id_list): return R.failed(handler, "记录不存在") err = self._before_delete(ids) if err: return R.failed(handler, err) count = self.repo.batch_delete(ids) return R.ok(handler, msg="本次共删除{0}条数据".format(count)) finally: self.repo.close() ``` ### 自定义 Service 扩展 ```python class PositionService(BaseService): def _before_delete(self, ids) -> Optional[str]: """删除前校验:存在用户引用时禁止删除""" id_list = parse_id_list(str(ids)) if id_list and user_repo.filter( User.position_id.in_(id_list), User.is_delete == 0 ).first(): return "存在用户引用该岗位,请先调整用户岗位" return None ``` :::tip _before_delete 钩子 - 返回 `None`:放行,继续删除 - 返回字符串:拦截,返回 `R.failed(字符串)` - 用于检查子级引用、业务约束等 ::: ## 删除流程 ``` 1. Endpoint 接收请求 ↓ 2. Service.delete(ids) 或 Service.batch_delete(request) ↓ 3. _before_delete(ids) 钩子校验 ↓(拦截则返回 R.failed) 4. delete(handler, ids) ↓ 5. repo.batch_delete(ids) 软删除 ↓ 6. 返回 R.ok(msg="本次共删除N条数据") ``` ## Repository 层实现 ```python def batch_delete(self, ids_str) -> int: """按ID软删除,返回实际删除条数""" id_list = parse_id_list(str(ids_str)) if not id_list: return 0 records = self.db.query(self.model).filter( self.model.id.in_(id_list), self._soft_col() == 0 ).all() for record in records: setattr(record, self.soft_delete_col, 1) return len(records) ``` :::tip 返回值说明 `batch_delete` 返回实际软删除的条数。如果传入的 ID 中包含已删除或不存在的记录,返回值会小于入参数量,此时 `delete` 会返回失败提示。 ::: ## 权限标识 | 操作 | 权限标识 | 说明 | |------|---------|------| | 单条删除 | `sys:{module}:delete` | 删除单条记录 | | 批量删除 | `sys:{module}:batchDelete` | 批量删除记录 | ## 常见错误提示 | 提示 | 原因 | |------|------| | "记录ID不存在" | 入参为空或格式错误 | | "记录不存在" | 部分或全部 ID 对应的记录已删除/不存在 | | "存在用户引用该岗位" | _before_delete 钩子拦截 | ## 总结 删除规范统一使用软删除(`is_delete` 标记),支持单条删除(逗号分隔 ID)和批量删除(JSON 数组)。Repository 层做数据原语,Service 层通过 `delete` 统一封装响应。自定义 Service 通过 `_before_delete` 钩子添加业务校验。批量删除端点必须为 `async def`。