Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
refactor(service): 修改banner和guild删除逻辑为软删除 fix(service): 修复删除操作使用软删除而非物理删除 build: 添加SQLite测试仓库实现 docs: 新增奖励管理字段拆分和批量抽奖UI改造文档 ci: 更新CI忽略文件 style: 清理无用资源文件
6.3 KiB
6.3 KiB
总体方案
- 引入统一软删除机制:所有支持回收站的业务表增加
deleted_at DATETIME NULL、deleted_by BIGINT NULL字段,并在 ORM 层启用 GORM Soft Delete 以默认过滤已删除记录。 - 提供中心化回收站接口与界面:集中列出各类型的软删除数据,支持恢复与彻底删除(二次确认)。
- 权限与菜单对齐:仅管理员可访问回收站;侧边栏在“系统管理”下新增“回收站”菜单,配置角色可见与按钮权限。
数据库改造
- 目标表(根据现有模型与端点清单):
activities、activity_issues、activity_reward_settings、products、product_categories、banner、guild、system_titles、system_title_effects、system_item_cards、system_coupons、menus、menu_actions、roles、role_users。 - 变更内容:
- 添加列:
deleted_at DATETIME NULL(建立索引以提升查询效率)、deleted_by BIGINT NULL。 - 后续通过
cmd/gormgen重新生成internal/repository/mysql/{model,dao}/*.gen.go,使模型包含gorm.DeletedAt与DeletedBy字段。
- 添加列:
- 迁移策略:当前仓库未集成迁移工具,计划新增
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_at、deleted_by。POST /api/admin/recycle/restore:参数type,id;Unscoped()更新目标记录deleted_at=NULL, deleted_by=NULL,恢复数据。DELETE /api/admin/recycle:参数type,id;Unscoped().Delete(&model)执行物理删除。
- 关联完整性:
- 软删除不级联子表;外键仍保持,恢复后关联自然可用。
- 若业务场景需要父子一致软删除(可选扩展),在服务层增加按类型的联动策略,但默认不做级联以降低风险。
- 权限拦截:
- 以上回收站接口全部走管理员认证中间件
internal/router/interceptor/admin_auth.go::AdminTokenAuthVerify。 - 彻底删除在服务层进行额外二次确认校验(配合前端确认对话框),后端也校验
role或action权限。
- 以上回收站接口全部走管理员认证中间件
前端改造(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.ts,path:'/system/recycle',meta.roles:['admin'],meta.authList:['recycle:list','recycle:restore','recycle:forceDelete']。 - 新增页面
web/admin/src/views/system/recycle/index.vue:- 顶部类型筛选(下拉:活动、商品、工会…)。
- 表格列出删除数据(含关键原字段、
deleted_at、deleted_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.ts):listDeleted(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 代码 → 修改删除逻辑到软删除 → 新增回收站接口与前端页面 → 配置菜单与角色 → 编写并运行测试。
- 验收标准:
- 所有默认查询不返回已删除记录。
- 回收站能列出各类删除记录,恢复/彻底删除工作正常。
- 权限严格生效;普通用户删除进入回收站但不可访问回收站界面。
- 恢复后关联数据完整、功能正常。