bindbox-game/.trae/documents/实现回收站与软删除机制.md
邹方成 2a89a1ab9d
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(admin): 更新前端资源文件及修复相关功能
refactor(service): 修改banner和guild删除逻辑为软删除
fix(service): 修复删除操作使用软删除而非物理删除

build: 添加SQLite测试仓库实现
docs: 新增奖励管理字段拆分和批量抽奖UI改造文档

ci: 更新CI忽略文件
style: 清理无用资源文件
2025-11-19 01:35:55 +08:00

6.3 KiB
Raw Blame History

总体方案

  • 引入统一软删除机制:所有支持回收站的业务表增加 deleted_at DATETIME NULLdeleted_by BIGINT NULL 字段,并在 ORM 层启用 GORM Soft Delete 以默认过滤已删除记录。
  • 提供中心化回收站接口与界面:集中列出各类型的软删除数据,支持恢复与彻底删除(二次确认)。
  • 权限与菜单对齐:仅管理员可访问回收站;侧边栏在“系统管理”下新增“回收站”菜单,配置角色可见与按钮权限。

数据库改造

  • 目标表(根据现有模型与端点清单):activitiesactivity_issuesactivity_reward_settingsproductsproduct_categoriesbannerguildsystem_titlessystem_title_effectssystem_item_cardssystem_couponsmenusmenu_actionsrolesrole_users
  • 变更内容:
    • 添加列:deleted_at DATETIME NULL(建立索引以提升查询效率)、deleted_by BIGINT NULL
    • 后续通过 cmd/gormgen 重新生成 internal/repository/mysql/{model,dao}/*.gen.go,使模型包含 gorm.DeletedAtDeletedBy 字段。
  • 迁移策略:当前仓库未集成迁移工具,计划新增 migrations/ 与 SQL 脚本(或集成 goose),在测试/生产库执行列新增与索引建立,完成后重新生成 ORM 代码。

后端改造

  • 技术栈:gin + gorm(已存在),读写库封装见 internal/repository/mysql/mysql.go,路由集中在 internal/router/router.go
  • 删除逻辑切换为软删除:
    • 将所有 DELETE /api/... 对应的处理器由物理删除改为 gorm 软删除:db.Delete(&model),自动写入 deleted_at;同时通过上下文注入当前用户 ID 写入 deleted_by(更新列)。
    • 涉及端点示例(集中定义于 internal/router/router.go 对应 handler活动、期数、奖励、工会、商品、分类、轮播图、称号/特效、道具卡、优惠券、系统菜单/动作、角色、角色成员等。
  • 查询默认过滤:
    • 使用含 gorm.DeletedAt 的模型,gorm 默认生成 WHERE deleted_at IS NULL;确保服务与 DAO 层不使用 Unscoped(),避免误返回已删除数据。
  • 回收站接口:新增 internal/api/admin/system_recycle.go 与服务层 internal/service/recycle/recycle_service.go
    • GET /api/admin/recycle:参数 type枚举activity、issue、reward、product...)、分页;实现为 Unscoped().Where("deleted_at IS NOT NULL") 列表,返回原始字段与 deleted_atdeleted_by
    • POST /api/admin/recycle/restore:参数 type,idUnscoped() 更新目标记录 deleted_at=NULL, deleted_by=NULL,恢复数据。
    • DELETE /api/admin/recycle:参数 type,idUnscoped().Delete(&model) 执行物理删除。
  • 关联完整性:
    • 软删除不级联子表;外键仍保持,恢复后关联自然可用。
    • 若业务场景需要父子一致软删除(可选扩展),在服务层增加按类型的联动策略,但默认不做级联以降低风险。
  • 权限拦截:
    • 以上回收站接口全部走管理员认证中间件 internal/router/interceptor/admin_auth.go::AdminTokenAuthVerify
    • 彻底删除在服务层进行额外二次确认校验(配合前端确认对话框),后端也校验 roleaction 权限。

前端改造web/admin

  • 框架:vue3 + vue-router + pinia + element-plus,侧边栏位于 web/admin/src/components/core/layouts/art-menus/art-sidebar-menu,菜单数据由路由模块/后端菜单驱动。
  • 路由与页面:
    • 新增路由模块 web/admin/src/router/modules/system/recycle.tspath:'/system/recycle'meta.roles:['admin']meta.authList:['recycle:list','recycle:restore','recycle:forceDelete']
    • 新增页面 web/admin/src/views/system/recycle/index.vue
      • 顶部类型筛选(下拉:活动、商品、工会…)。
      • 表格列出删除数据(含关键原字段、deleted_atdeleted_by),分页。
      • 行操作:恢复(调用 POST /api/admin/recycle/restore)、彻底删除ElMessageBox.confirm 二次确认后调用 DELETE /api/admin/recycle)。
  • API 封装:web/admin/src/api/system/recycle.ts(使用现有 Axios 封装 web/admin/src/utils/http/index.tslistDeleted(type,page,size)restore(type,id)forceDelete(type,id)
  • 菜单项:
    • 后端在 menus 表增加“系统管理/回收站”,path:'/system/recycle',并在角色分配接口 POST /api/role/:role_id/menus 仅赋予管理员;前端 ListSimpleMenus 拉取后自动渲染。
  • 权限指令与守卫:
    • 按按钮权限 meta.authList 与角色 meta.roles 控制显示;普通用户不显示菜单,无法访问路由或调用接口。

权限控制细则

  • 仅管理员角色拥有“回收站”菜单与相关按钮权限;普通用户删除操作也走软删除,但无法访问回收站接口。
  • 彻底删除在前端强制二次确认,并在后端再次校验权限与保护条件(如必须管理员)。

测试方案

  • 后端Go testing
    • internal/api/admin/system_recycle_test.go
      • 删除后记录存在于回收站接口,原查询不可见(验证 deleted_at 与默认过滤)。
      • 恢复后记录重新可见,deleted_at/deleted_by 为空。
      • 权限:非管理员访问回收站接口返回 401/403;管理员正常。
      • 关联完整性:恢复后关联查询正常(父子外键未破坏)。
  • 前端Vitest
    • web/admin/src/tests/system/recycle/recycle.test.ts
      • 菜单与路由权限:管理员可见“回收站”,普通用户不可见。
      • 列表加载与分页、恢复按钮触发成功提示与刷新、彻底删除二次确认弹窗与成功流程。
      • 401/403 拦截按现有拦截器工作(参考 src/tests/auth/401-error-handling.test.ts)。

交付与验证

  • 执行数据库迁移并生成 ORM 代码 → 修改删除逻辑到软删除 → 新增回收站接口与前端页面 → 配置菜单与角色 → 编写并运行测试。
  • 验收标准:
    • 所有默认查询不返回已删除记录。
    • 回收站能列出各类删除记录,恢复/彻底删除工作正常。
    • 权限严格生效;普通用户删除进入回收站但不可访问回收站界面。
    • 恢复后关联数据完整、功能正常。