## 背景 - 当前不启用数据库外键约束(FK),删除由业务逻辑驱动。 - 现有删除实现多为单表删除,未做级联:如删除活动 `internal/service/activity/activity_delete.go:9`、删除期 `internal/service/activity/issue_delete.go:9`、删除用户 `internal/service/user/batch_user.go:32`。 - GORM 初始化未声明外键或级联:`internal/repository/mysql/mysql.go:97`。 ## 目标 - 在业务层实现“逻辑级联删除”,保证删除主实体时,同步清理其关联业务数据,且性能与可控性优于 FK 级联。 ## 级联清单 ### 删除活动(Activities.ID) - 删除活动期:`activity_issues` 按 `activity_id`(`internal/repository/mysql/model/activity_issues.gen.go:18`) - 期下配置与承诺: - 奖励配置 `activity_reward_settings` 按 `issue_id`(`internal/repository/mysql/model/activity_reward_settings.gen.go:18`) - 活动承诺字段:`activities.commitment_*`(活动维度,已统一使用活动级承诺,不存在期级承诺表) - 期下抽奖相关: - 抽奖日志 `activity_draw_logs` 按 `issue_id`(`internal/repository/mysql/model/activity_draw_logs.gen.go:18`) - 抽奖效果 `activity_draw_effects` 按 `draw_log_id`/`issue_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:17,29`) - 抽奖凭据 `activity_draw_receipts` 按 `draw_log_id`(`internal/repository/mysql/model/activity_draw_receipts.gen.go:17`) - 活动范围效果与道具: - 用户道具使用记录 `user_item_cards` 按 `used_activity_id` / `used_issue_id` / `used_draw_log_id`(`internal/repository/mysql/model/user_item_cards.gen.go:24,25,23`) - 抽奖效果快照 `activity_draw_effects` 按 `activity_id`/`issue_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:28,29`) - 资产与日志: - 用户资产 `user_inventory` 按 `activity_id`(`internal/repository/mysql/model/user_inventory.gen.go:21`) - 公会贡献日志 `guild_contribute_logs` 按 `activity_id` / `issues_id`(`internal/repository/mysql/model/guild_contribute_logs.gen.go:19,20`) - 系统模板: - 系统道具卡 `system_item_cards` 按 `activity_id` / `issue_id`(`internal/repository/mysql/model/system_item_cards.gen.go:23,24`) - 系统优惠券 `system_coupons` 按 `activity_id`(`internal/repository/mysql/model/system_coupons.gen.go:20`) - 最后删除活动主表:`activities`(`internal/repository/mysql/model/activities.gen.go:15`) ### 删除用户(Users.ID) - 用户身份与权益: - 头衔 `user_titles` 按 `user_id`(`internal/repository/mysql/model/user_titles.gen.go:16`) - 领取型权益限流 `user_title_effect_claims` 按 `user_id`(`internal/repository/mysql/model/user_title_effect_claims.gen.go:16`) - 用户账户与地址: - 地址 `user_addresses` 按 `user_id`(`internal/repository/mysql/model/user_addresses.gen.go:18`) - 积分余额 `user_points` 按 `user_id`(`internal/repository/mysql/model/user_points.gen.go:18`) - 积分流水 `user_points_ledger` 按 `user_id`(`internal/repository/mysql/model/user_points_ledger.gen.go:17`) - 用户订单与优惠: - 订单 `orders` 按 `user_id`(`internal/repository/mysql/model/orders.gen.go:18`) - 用户优惠券 `user_coupons` 按 `user_id`(`internal/repository/mysql/model/user_coupons.gen.go:18`) - 用户资产与道具: - 用户资产 `user_inventory` 按 `user_id`(`internal/repository/mysql/model/user_inventory.gen.go:18`) - 用户道具卡 `user_item_cards` 按 `user_id`(`internal/repository/mysql/model/user_item_cards.gen.go:18`) - 与抽奖相关: - 抽奖效果 `activity_draw_effects` 按 `user_id` 或关联 `draw_log_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:18,17`) - 抽奖凭据 `activity_draw_receipts` 关联 `draw_log_id`(`internal/repository/mysql/model/activity_draw_receipts.gen.go:17`) - 抽奖日志 `activity_draw_logs` 按 `user_id`(`internal/repository/mysql/model/activity_draw_logs.gen.go:17`) - 公会关联: - 公会成员 `guild_members` 按 `user_id`(`internal/repository/mysql/model/guild_members.gen.go:18`) - 公会贡献日志 `guild_contribute_logs` 按 `user_id`(`internal/repository/mysql/model/guild_contribute_logs.gen.go:18`) - 履约/发货: - 发货记录 `shipping_records` 按 `user_id`(`internal/repository/mysql/model/shipping_records.gen.go:18`) - 运营发货统计 `ops_shipping_stats` 按 `user_id`(`internal/repository/mysql/model/ops_shipping_stats.gen.go:21`) - 最后软删用户:`users.deleted_at`(`internal/repository/mysql/model/users.gen.go:20`) ## 删除顺序(事务内) - 统一采用“从叶到根”的顺序: 1) 以日志/效果/凭据等子表为先(`activity_draw_effects`、`activity_draw_receipts`、`activity_draw_logs`) 2) 再清理资产/权益/模板(`user_inventory`、`user_item_cards`、`user_titles`、`system_*`) 3) 清理期与期下配置(`activity_issues`、`activity_reward_settings`) 4) 删除根实体(`activities` 或软删 `users`) - 全过程包裹在单事务中,任何一步失败则回滚。 ## 实现策略 - 新增业务服务方法: - `DeleteActivityCascade(ctx, activityID)`:按“删除活动”清单顺序删除 - `DeleteUserCascade(ctx, userID)`:按“删除用户”清单顺序删除 - 技术要点: - 批量删除使用分批(如 5k/批)避免长事务与大锁;必要时按时间/ID 片段迭代 - 统一 `WHERE` 条件与索引列(`user_id`/`activity_id`/`issue_id`/`draw_log_id`)确保扫描性能 - 软删与硬删:保留用户软删(合规与审计),其余按当前表定义硬删;如需统一软删,可后续逐表补充 `gorm.DeletedAt` - 幂等性:每个子删除操作按条件删除,无记录时直接通过;重复调用不报错 - 审计:记录操作日志(操作者、对象ID、影响行数),便于回溯 ## 验收标准 - 删除活动时,任一期及其抽奖日志/效果/凭据、奖励配置、承诺、资产、贡献日志与相关模板均被清理;根活动删除成功。 - 删除用户时,地址/订单/优惠券/积分(余额+流水)/资产/道具/抽奖相关/公会关系/发货记录及统计均被清理;根用户软删成功。 - 全流程事务保障、失败回滚;批量删除性能稳定,无显著锁表或超时。 ## 需要改造的现有入口(参考) - 活动删除入口:`internal/service/activity/activity_delete.go:9` → 升级为调用 `DeleteActivityCascade` - 期删除入口:`internal/service/activity/issue_delete.go:9` → 被 `DeleteActivityCascade` 内部调用(或保留独立级联) - 用户删除入口:`internal/service/user/batch_user.go:32` → 升级为调用 `DeleteUserCascade` ## 后续动作 - 我将基于以上清单与顺序,补充两套事务级联删除实现,并为关键入口替换调用;同时补充单元测试覆盖正常/边界/异常三类用例,验证幂等与性能。