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 |
| 类别 | 后端删除接口(约) | 前端含「删除」页面(约) |
|---|---|---|
| CRM 业务 | 18 | 22 |
| PL 物流 | 8 | 9 |
| DB 地磅 | 3 | 2 |
| BX 报销 | 5 | 5 |
| 系统管理 | 11 | 11 |
| UI 本地删除(不改) | — | ~8 组件/页 |
| 合计(业务范围内) | 34 | 38 |
| # | 实体 | 后端接口 | 后端实现位置 | 前端 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 |
暂无独立列表删入口 | — | 预留后端 |
| # | 实体 | 后端接口 | 后端实现位置 | 前端 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 校验 |
| # | 实体 | 后端接口 | 后端实现位置 | 前端 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 |
| # | 实体 | 后端接口 | 后端实现位置 | 前端 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` |
| 页面 / 组件 | 说明 |
|---|---|
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 |
| # | 实体 | 后端接口 | 前端页面 |
|---|---|---|---|
| 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 |
后端
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 / idcanDeleteByCreator(row, currentUserId?) — 比对 row.createByfilterDeletableRows(rows) — 批量删前过滤CREATOR_DELETE_TIP = '仅创建人可删除'web-admin/src/components/business/creator-delete-button/index.vue — 统一 disabled + tooltip文档
openspec/changes/creator-only-delete/(permission + api conventions 各一条 Requirement)后端(每个 soft_delete 入口加 assert)
soft_delete_cluesoft_delete_customersoft_delete_opportunitiessoft_delete_quotessoft_delete_contractssoft_delete_contract_changessoft_delete_contract_templates前端
v-if="canDeleteByCreator(row)"ElMessage.warning 提示跳过条数createBy(缺则补 _row_to_dict)验收
后端
前端
CustomerDetailDialog 跟进删:realName → canDeleteByCreatorclue/detailDrawer canManageFollow 改用公共 utilInvoiceInfo、ContactList、product、product-category、RuleIndex后端
delete_carrier/vehicle/personnel/driver/plan + delete_draft_settlement 加 create_byvehicle-driver 删关联前端
db/inventory:保留现有日期规则,与创建人规则 AND 关系后端
BxCrudService.soft_delete 或各 Service 包装层加 create_by(注意 BX 部分历史 create_by 存 name 的脏数据,需迁移或兼容比对)前端
bx/reimburse/apply、bx/advance/apply、bx/expense-item、附件组件备注: B05 BX 工作流配置默认跳过;若客户要求纳入,单独开 Phase 4b。
| 场景 | 前端 | 后端 |
|---|---|---|
| 选中含他人数据 | 禁用批量删或只提交本人 id 并提示 | 对每条 assert;部分失败返回明细 |
| 选中全无 createBy | 普通用户禁用;超管可删 | 空 create_by 仅超管 |
| 混合选中 | 推荐:过滤后仅删本人,Message 汇总 | 事务:建议全失败或逐条(与 CRM 现有 delClue 部分失败风格对齐) |
| 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 用户(创建人 / 非创建人)。