14 KiB
14 KiB
仅创建人可删除 — 删除接口 / 页面对照表与分阶段改造方案
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}` |
| 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.pyassert_row_deletable_by_creator(row, user_info, *, message="仅创建人可删除")assert_rows_deletable_by_creator(rows, user_info)— 批量时全量校验super_adminbypass;create_by为空时仅超管可删
- 单元测试
backend/tests/test_delete_guard.py(创建人匹配 / 不匹配 / 超管 / 空 create_by)
前端
- 新建
web-admin/src/utils/creator-delete.tsgetCurrentUserId()— 从useUserStore().info取userId/idcanDeleteByCreator(row, currentUserId?)— 比对row.createByfilterDeletableRows(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/detailDrawercanManageFollow改用公共 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. 下一步
- 客户确认 § 默认产品假设 + §4 系统管理是否纳入
- 执行 Phase 0 并合入主分支
- 按 Phase 1 → 4 逐步交付,每阶段更新本文件 checkbox