265 lines
14 KiB
Markdown
265 lines
14 KiB
Markdown
# 仅创建人可删除 — 删除接口 / 页面对照表与分阶段改造方案
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 业务数据删除时,仅允许 `create_by === 当前用户`(`super_admin` 可例外);后端强制校验,前端按创建人显隐/禁用删除按钮。
|
||
|
||
**Architecture:** 后端新增统一 `assert_row_deletable_by_creator()`,在各模块 `soft_delete` / `delete_*` 入口调用;前端新增 `canDeleteByCreator()` 工具与可选 `CreatorDeleteButton` 包装;列表批量删除过滤非本人数据并提示。系统管理类主数据默认**不纳入**本规则(见 §4)。
|
||
|
||
**Tech Stack:** FastAPI + SQLAlchemy(backend)、Vue 3 + Element Plus(web-admin)
|
||
|
||
**默认产品假设(实施前需客户签字确认):**
|
||
|
||
| 项 | 默认 |
|
||
|----|------|
|
||
| 超管 | `super_admin` 可删除任意业务数据 |
|
||
| 系统管理 | 租户/用户/角色/菜单/部门/字典/站点/职位/流程定义/请假 — **不适用**创建人规则 |
|
||
| 子表 | 跟进、联系人、开票信息、商机产品等 — **适用**创建人规则 |
|
||
| 历史空 `create_by` | 仅 `super_admin` 可删;普通用户不可删 |
|
||
| 比对字段 | 后端用 `user_id`(UUID);前端用 `row.createBy`,禁止用 `realName` |
|
||
|
||
---
|
||
|
||
## 1. 统计摘要
|
||
|
||
| 类别 | 后端删除接口(约) | 前端含「删除」页面(约) |
|
||
|------|-------------------|-------------------------|
|
||
| CRM 业务 | 18 | 22 |
|
||
| PL 物流 | 8 | 9 |
|
||
| DB 地磅 | 3 | 2 |
|
||
| BX 报销 | 5 | 5 |
|
||
| 系统管理 | 11 | 11 |
|
||
| **UI 本地删除(不改)** | — | ~8 组件/页 |
|
||
| **合计(业务范围内)** | **34** | **38** |
|
||
|
||
---
|
||
|
||
## 2. 需改造对照表(业务数据 — 纳入范围)
|
||
|
||
### 2.1 CRM
|
||
|
||
| # | 实体 | 后端接口 | 后端实现位置 | 前端 API | 前端页面 / 入口 | 批量删除 | 现状 |
|
||
|---|------|----------|--------------|----------|-----------------|----------|------|
|
||
| C01 | 线索 | `POST /web/crm/clue/delete` | `crm.py` → `soft_delete_clue` | `delClue` | `crm/clue/index.vue` 行删 | 否 | 仅 data_scope |
|
||
| C02 | 线索 | 同上 | 同上 | `delClue` | `crm/clue/detailDrawer.vue` 主删 | — | 无创建人校验 |
|
||
| C03 | 线索跟进 | `POST /web/crm/clue/follow/delete` | `delete_clue_follow` | `delClueFollow` | `detailDrawer.vue` 跟进删 | 否 | 前端 `canManageFollow`(createBy);后端 data_scope |
|
||
| C04 | 客户 | `POST /web/crm/customer/delete` | `soft_delete_customer` | `fetchDeleteCustomer` | `crm/customer/index.vue` | 否 | 无 |
|
||
| C05 | 客户 | 同上 | 同上 | 同上 | `CustomerDetailDialog.vue` 主删 | — | 无 |
|
||
| C06 | 客户跟进 | `POST /web/crm/customer/follow/delete` | `soft_delete_follow` | `fetchDeleteCustomerFollow` | `CustomerDetailDialog.vue` | 否 | 前端 realName+admin(脆弱) |
|
||
| C07 | 联系人 | `POST /web/crm/contact/delete` | `soft_delete_contact` | `fetchDeleteContact` | `CustomerDetailDialog.vue`、`ContactList.vue` | 否 | 无 |
|
||
| C08 | 开票信息 | `POST /web/crm/invoice-info/delete` | `soft_delete_invoice_info` | `delInvoiceInfo` | `InvoiceInfo.vue` | 否 | 无 |
|
||
| C09 | 商机 | `POST /web/crm/opportunity/delete` | `soft_delete_opportunities` | `delOpportunity` | `opportunity/index.vue` | 否 | 无 |
|
||
| C10 | 商机 | 同上 | 同上 | 同上 | `OpportunityDetailDrawer.vue` 主删 | — | 无 |
|
||
| C11 | 商机跟进 | `POST /web/crm/opportunity/follow/delete` | `soft_delete_opportunity_follow` | `delOpportunityFollow` | `OpportunityDetailDrawer.vue` | 否 | 无 |
|
||
| C12 | 商机产品 | `POST /web/crm/opportunity/product/delete` | `soft_delete_opportunity_products` | `delOpportunityProduct` | 表单内(若有持久化删) | 否 | 无 |
|
||
| C13 | 报价 | `POST /web/crm/quote/delete` | `soft_delete_quotes` | `delQuote` | `quote/index.vue` | 是 | 无 |
|
||
| C14 | 报价 | 同上 | 同上 | 同上 | `quote/detail.vue`、`QuoteDetailDialog.vue` | — | 无 |
|
||
| C15 | 报价产品 | `POST /web/crm/quote/product/delete` | `soft_delete_quote_products` | `delQuoteProduct` | 已保存明细删(若有) | 否 | 无;表单内行删见 §3 |
|
||
| C16 | 合同 | `DELETE /web/crm/contract/{ids}` | `soft_delete_contracts` | `delContract` | `contract/index.vue` | 是 | 无 |
|
||
| C17 | 合同 | 同上 | 同上 | 同上 | `contract/detail.vue`、`ContractDetailDialog.vue` | — | 无 |
|
||
| C18 | 合同变更 | `DELETE /web/crm/contract/change/{ids}` | `soft_delete_contract_changes` | `delChange` | `contract/change/index.vue` | 是 | 无 |
|
||
| C19 | 合同模板 | `DELETE /web/crm/contract/template/{ids}` | `soft_delete_contract_templates` | `delTemplate` | `contract/template/index.vue` | 是 | 无 |
|
||
| C20 | 产品 | `POST /web/crm/products/delete` | `soft_delete_product` | `fetchDeleteProduct` | `crm/product/index.vue` | 是 | 无 |
|
||
| C21 | 产品分类 | `POST /web/crm/product-categories/delete` | `soft_delete_category` | `fetchDeleteProductCategory` | `crm/product-category/index.vue` | 否 | 无 |
|
||
| C22 | 公海规则 | `POST /web/crm/customer/pool/rule/delete` | `delete_pool_rules` | `fetchDeletePoolRule` | `customer/pool/RuleIndex.vue` | 否 | 无 |
|
||
| C23 | 供应商信息 | `POST /web/crm/supplierInfo/delete` | `soft_delete_supplier_info` | `delSupplierInfo` | **暂无独立列表删入口** | — | 预留后端 |
|
||
|
||
### 2.2 PL 物流
|
||
|
||
| # | 实体 | 后端接口 | 后端实现位置 | 前端 API | 前端页面 | 批量 | 现状 |
|
||
|---|------|----------|--------------|----------|----------|------|------|
|
||
| P01 | 承运商 | `DELETE /transport/carrier/{id}` | `pl/base_data.py` | `carrierApi.delete` | `pl/carrier/index.vue` | 否 | data_scope |
|
||
| P02 | 车辆 | `DELETE /transport/vehicle/{id}` | 同上 | `vehicleApi.delete` | `pl/vehicle/index.vue` | 否 | data_scope |
|
||
| P03 | 自有车辆 | 同上 | 同上 | 同上 | `pl/self-vehicle/index.vue` | 否 | data_scope |
|
||
| P04 | 人员 | `DELETE /transport/personnel/{id}` | 同上 | `personnelApi.delete` | `pl/personnel/index.vue` | 否 | data_scope |
|
||
| P05 | 司机 | `DELETE /transport/driver/{id}` | 同上 | `driverApi.delete` | `pl/driver/index.vue` | 否 | data_scope |
|
||
| P06 | 车辆-司机关联 | `DELETE /transport/vehicle-driver/{id}` | 同上 | (内嵌于车辆/人员维护) | 车辆/人员表单内 | 否 | data_scope |
|
||
| P07 | 运输计划 | `DELETE /transport/plan/{id}` | `pl/plan.py` | `planApi.delete` | `pl/plan/index.vue` | 否 | data_scope |
|
||
| P08 | 运费结算单 | `DELETE /transport/freight/{id}` | `pl/freight.py` → `delete_draft_settlement` | `freightApi.delete` | `pl/freight/SettlementBillPanel.vue` | 否 | 仅草稿;无 create_by 校验 |
|
||
|
||
### 2.3 DB 地磅
|
||
|
||
| # | 实体 | 后端接口 | 后端实现位置 | 前端 API | 前端页面 | 现状 |
|
||
|---|------|----------|--------------|----------|----------|------|
|
||
| D01 | 库存记录 | `DELETE /inventory/{id}` | `db/inventory.py` | `inventoryApi.delete` | `db/inventory/index.vue` | 前端日期规则;后端 data_scope |
|
||
| D02 | 地磅计划 | `DELETE /plans/{id}` | `db/plans.py` | `plansApi.delete` | `db/plans/index.vue` | data_scope |
|
||
| D03 | 过磅记录 | `DELETE /weighing/{id}` | `db/weighing.py` | `weighingApi.delete` | 公开页若有 | data_scope |
|
||
|
||
### 2.4 BX 报销
|
||
|
||
| # | 实体 | 后端接口 | 后端实现位置 | 前端 API | 前端页面 | 现状 |
|
||
|---|------|----------|--------------|----------|----------|------|
|
||
| B01 | 报销申请 | `DELETE /reimburses/{id}` | `bx/reimburse.py` → `ReimburseService.soft_delete` | `reimburseApi.delete` | `bx/reimburse/apply/index.vue` | data_scope |
|
||
| B02 | 预支申请 | `DELETE /advances/{id}` | `bx/advance.py` | `advanceApi.delete` | `bx/advance/apply/index.vue` | data_scope |
|
||
| B03 | 费用项 | `DELETE /expense-items/{id}` | `bx/expense_item.py` | `expenseItemApi.delete` | `bx/expense-item/index.vue` | data_scope |
|
||
| B04 | 附件 | `DELETE /attachments/{id}` | `bx/attachment.py` | `attachmentApi.delete` | `BxAttachmentUpload` 等 | 随主单;删附件也需创建人 |
|
||
| B05 | BX 工作流配置 | `DELETE /workflows/{id}` | `bx/workflow.py` | `workflowApi.delete` | `bx/workflow/reimburse|advance/index.vue` | **建议归入系统配置,默认不改造** |
|
||
|
||
---
|
||
|
||
## 3. 不纳入改造(UI 本地 / 非持久化删除)
|
||
|
||
| 页面 / 组件 | 说明 |
|
||
|-------------|------|
|
||
| `QuoteFormPanel.vue` `handleDeleteProducts` / `handleRemoveProduct` | 编辑表单内未提交行,非 API 删库 |
|
||
| `contract/form.vue` 行项删除 | 同上 |
|
||
| `ContractAttachmentPanel.vue` | 表单附件列表本地 splice |
|
||
| `attachment-upload-field`、`BxAttachmentUpload` | 未保存附件卡片 |
|
||
| `FlowDesigner.vue` | 流程设计器节点/连线 |
|
||
| `system/user/detail.vue` 地址删除 | 本地 mock 数组 |
|
||
| `art-global-search` 搜索历史 | 本地 storage |
|
||
|
||
---
|
||
|
||
## 4. 默认排除(系统管理 — 待客户确认是否纳入)
|
||
|
||
| # | 实体 | 后端接口 | 前端页面 |
|
||
|---|------|----------|----------|
|
||
| S01 | 租户 | `DELETE /web/admin/tenants/{id}` | `system/tenant/index.vue` |
|
||
| S02 | 用户 | `DELETE /web/admin/users/{id}` | `system/user/index.vue` |
|
||
| S03 | 角色 | `DELETE /web/admin/role-management/{id}` | `system/role/index.vue` |
|
||
| S04 | 菜单 | `DELETE /web/admin/menus/{id}` | `system/menu/index.vue` |
|
||
| S05 | 部门 | `DELETE /web/admin/depts/{id}` | `system/dept/index.vue` |
|
||
| S06 | 字典类型/数据 | `DELETE .../dict-types|dict-data/{id}` | `system/dict/index.vue` |
|
||
| S07 | 站点 | `DELETE /web/admin/sites/{id}` | `system/site/index.vue` |
|
||
| S08 | 职位 | `DELETE /web/admin/posts/{id}` | `system/post/index.vue` |
|
||
| S09 | 流程定义 | `DELETE /web/admin/workflow/defs/{id}` | `system/workflow/process/index.vue` |
|
||
| S10 | 请假 | `DELETE /web/admin/leave/{id}` | `system/leave/index.vue` |
|
||
|
||
---
|
||
|
||
## 5. 分阶段改造方案
|
||
|
||
### Phase 0 — 基础设施(必须先做)
|
||
|
||
**后端**
|
||
|
||
- [ ] 新建 `backend/app/core/delete_guard.py`
|
||
- `assert_row_deletable_by_creator(row, user_info, *, message="仅创建人可删除")`
|
||
- `assert_rows_deletable_by_creator(rows, user_info)` — 批量时全量校验
|
||
- `super_admin` bypass;`create_by` 为空时仅超管可删
|
||
- [ ] 单元测试 `backend/tests/test_delete_guard.py`(创建人匹配 / 不匹配 / 超管 / 空 create_by)
|
||
|
||
**前端**
|
||
|
||
- [ ] 新建 `web-admin/src/utils/creator-delete.ts`
|
||
- `getCurrentUserId()` — 从 `useUserStore().info` 取 `userId` / `id`
|
||
- `canDeleteByCreator(row, currentUserId?)` — 比对 `row.createBy`
|
||
- `filterDeletableRows(rows)` — 批量删前过滤
|
||
- `CREATOR_DELETE_TIP = '仅创建人可删除'`
|
||
- [ ] 可选:`web-admin/src/components/business/creator-delete-button/index.vue` — 统一 disabled + tooltip
|
||
|
||
**文档**
|
||
|
||
- [ ] OpenSpec 变更提案:`openspec/changes/creator-only-delete/`(permission + api conventions 各一条 Requirement)
|
||
|
||
---
|
||
|
||
### Phase 1 — CRM 主实体(优先级最高)
|
||
|
||
**后端(每个 soft_delete 入口加 assert)**
|
||
|
||
- [ ] C01–C02 线索 — `soft_delete_clue`
|
||
- [ ] C04–C05 客户 — `soft_delete_customer`
|
||
- [ ] C09–C10 商机 — `soft_delete_opportunities`
|
||
- [ ] C13–C14 报价 — `soft_delete_quotes`
|
||
- [ ] C16–C17 合同 — `soft_delete_contracts`
|
||
- [ ] C18 合同变更 — `soft_delete_contract_changes`
|
||
- [ ] C19 合同模板 — `soft_delete_contract_templates`
|
||
|
||
**前端**
|
||
|
||
- [ ] 上表对应 10 个页面/抽屉:行删 / 工具栏删 `v-if="canDeleteByCreator(row)"`
|
||
- [ ] 批量删(报价/合同/变更/模板):选中时过滤 + `ElMessage.warning` 提示跳过条数
|
||
- [ ] 列表接口确认返回 `createBy`(缺则补 `_row_to_dict`)
|
||
|
||
**验收**
|
||
|
||
- [ ] 用户 A 创建、用户 B(同部门、有删权限)调用 API 返回 403
|
||
- [ ] 用户 A 可删自己的数据;超管可删任意
|
||
|
||
---
|
||
|
||
### Phase 2 — CRM 子实体 + 主数据
|
||
|
||
**后端**
|
||
|
||
- [ ] C03/C06–C08/C11–C12 跟进、联系人、开票、商机产品
|
||
- [ ] C20–C22 产品、分类、公海规则
|
||
- [ ] C23 供应商(仅后端,无 UI 也先加固)
|
||
|
||
**前端**
|
||
|
||
- [ ] 替换 `CustomerDetailDialog` 跟进删:`realName` → `canDeleteByCreator`
|
||
- [ ] 统一 `clue/detailDrawer` `canManageFollow` 改用公共 util
|
||
- [ ] `InvoiceInfo`、`ContactList`、`product`、`product-category`、`RuleIndex`
|
||
|
||
---
|
||
|
||
### Phase 3 — PL + DB
|
||
|
||
**后端**
|
||
|
||
- [ ] P01–P08:`delete_carrier/vehicle/personnel/driver/plan` + `delete_draft_settlement` 加 create_by
|
||
- [ ] P06 `vehicle-driver` 删关联
|
||
- [ ] D01–D03 库存/计划/过磅
|
||
|
||
**前端**
|
||
|
||
- [ ] PL 9 页 + DB 2 页按钮控制
|
||
- [ ] `db/inventory`:**保留**现有日期规则,与创建人规则 **AND** 关系
|
||
|
||
---
|
||
|
||
### Phase 4 — BX 报销
|
||
|
||
**后端**
|
||
|
||
- [ ] B01–B04:在 `BxCrudService.soft_delete` 或各 Service 包装层加 create_by(注意 BX 部分历史 `create_by` 存 name 的脏数据,需迁移或兼容比对)
|
||
|
||
**前端**
|
||
|
||
- [ ] `bx/reimburse/apply`、`bx/advance/apply`、`bx/expense-item`、附件组件
|
||
|
||
**备注:** B05 BX 工作流配置默认跳过;若客户要求纳入,单独开 Phase 4b。
|
||
|
||
---
|
||
|
||
### Phase 5 — 系统管理(可选,需客户确认)
|
||
|
||
- [ ] 若确认纳入:S01–S10 后端 + 11 个 system 页面
|
||
- [ ] 若不纳入:在帮助文档注明「系统管理不受创建人删除限制」
|
||
|
||
---
|
||
|
||
## 6. 批量删除统一行为
|
||
|
||
| 场景 | 前端 | 后端 |
|
||
|------|------|------|
|
||
| 选中含他人数据 | 禁用批量删或只提交本人 id 并提示 | 对每条 assert;部分失败返回明细 |
|
||
| 选中全无 createBy | 普通用户禁用;超管可删 | 空 create_by 仅超管 |
|
||
| 混合选中 | 推荐:过滤后仅删本人,Message 汇总 | 事务:建议**全失败**或**逐条**(与 CRM 现有 delClue 部分失败风格对齐) |
|
||
|
||
---
|
||
|
||
## 7. 实施顺序与估时
|
||
|
||
| Phase | 范围 | 后端 | 前端 | 估时 |
|
||
|-------|------|------|------|------|
|
||
| 0 | 基础设施 | 0.5d | 0.5d | 1d |
|
||
| 1 | CRM 主实体 | 1d | 1.5d | 2.5d |
|
||
| 2 | CRM 子实体 | 1d | 1d | 2d |
|
||
| 3 | PL + DB | 1d | 1d | 2d |
|
||
| 4 | BX | 1d | 0.5d | 1.5d |
|
||
| 5 | 系统管理(可选) | 1d | 1d | 2d |
|
||
|
||
**建议迭代:** 每 Phase 独立 PR,先后端再前端,每模块手工回归 2 用户(创建人 / 非创建人)。
|
||
|
||
---
|
||
|
||
## 8. 下一步
|
||
|
||
1. 客户确认 § 默认产品假设 + §4 系统管理是否纳入
|
||
2. 执行 **Phase 0** 并合入主分支
|
||
3. 按 Phase 1 → 4 逐步交付,每阶段更新本文件 checkbox
|