智能重构

wescode 提供 AI 驱动的智能重构能力,结合 CKG(代码知识图谱)进行跨文件依赖分析,使用 CSE(约束满足引擎)保障重构安全性。支持重构检测、安全重构、跨文件重构、重构预览和约束保护五大能力。

重构检测

AI 识别重构时机

wescode 在日常编码过程中持续分析代码质量,主动提示重构机会:

代码异味检测:

AI 检测到以下重构机会:

🔵 [长函数] internal/order/service.go:CreateOrder
   当前 128 行,建议拆分为:
   - validateOrder():参数校验
   - calculatePrice():价格计算
   - persistOrder():持久化
   - notifyOrder():通知推送

🔵 [重复代码] internal/user/repo.go 与 internal/admin/repo.go
   FindByID / FindByEmail 查询逻辑重复率 85%
   建议抽取公共 BaseRepository

🟡 [过深嵌套] internal/payment/handler.go:ProcessPayment
   嵌套深度 6 层,建议使用 early return 模式

模式检测(CKG 驱动):

> 分析这个包的代码质量

CKG 分析 internal/auth/ 包:

结构问题:
- middleware.go 同时承担认证和授权,职责不单一
- handler.go 直接调用 repo 层,跳过 service 层
- 3 个文件存在循环引用风险

命名问题:
- CheckAuth() 与 VerifyToken() 命名不一致
  其他模块统一使用 Verify* 命名

复杂度:
- LoginHandler 圈复杂度 15(阈值 10)
- RefreshToken 圈复杂度 12(阈值 10)

重构建议触发方式

方式说明
自动检测文件保存时后台分析,在编辑器中以 hints 显示
Chat 对话> 这个函数需要重构吗?
命令触发wescode refactor --analyze internal/auth/
全局扫描> 扫描整个项目的重构机会

安全重构

重构安全保障

wescode 在执行重构时,确保行为不被破坏:

类型安全:

> 把 CreateUser 的参数从 3 个独立参数改为 struct

AI 分析影响:
- CreateUser(name, email, role) → CreateUser(req CreateUserReq)
- 调用方共 8 处,分布在 4 个文件中
- CKG 已找到所有调用点

安全检查:
✅ 所有调用方签名可自动更新
✅ 无反射调用(反射调用不能自动重构)
✅ 无接口方法覆盖(不影响接口契约)

行为保全:

> 重构 ProcessOrder 函数,拆分为三个子函数

AI 保障:
1. 拆分后的执行顺序与原函数完全一致
2. 错误传播路径不变
3. 副作用(数据库写入、通知发送)发生点不变
4. 上下文(context)正确传递

验证:
✅ 编译通过
✅ 现有测试全部通过(23/23)
✅ 行为等价分析通过

增量重构

对于大型重构,AI 支持分步执行,每步都可验证:

> 把所有 handler 从直接调用 repo 改为调用 service

AI 制定增量计划:
步骤 1/5:创建 UserService 接口和实现
  → 验证:编译通过 ✅

步骤 2/5:UserHandler 改用 UserService
  → 验证:编译通过 + 测试通过 ✅

步骤 3/5:OrderHandler 改用 OrderService
  → 验证:编译通过 + 测试通过 ✅

步骤 4/5:PaymentHandler 改用 PaymentService
  → 验证:编译通过 + 测试通过 ✅

步骤 5/5:移除 handler 对 repo 的直接依赖
  → 验证:编译通过 + 测试通过 + import 检查通过 ✅

回退机制

每次重构操作都创建可回退的检查点:

重构前自动创建检查点:refactor-checkpoint-20260914-2130
如果重构结果不满意:
> 回退到重构前的状态

git checkout refactor-checkpoint-20260914-2130

跨文件重构

CKG 驱动的全局重构

CKG 的调用图和依赖图使跨文件重构成为可能:

函数重命名:

> 把 GetUser 重命名为 FindUserByID

CKG 分析调用链:
internal/user/repo.go        → 定义处
internal/user/service.go     → 调用处 ×3
internal/api/user_handler.go → 调用处 ×2
internal/admin/handler.go    → 调用处 ×1
internal/auth/middleware.go   → 调用处 ×1
test/user_test.go            → 测试引用 ×4

共 5 个文件、11 处引用需要更新

包迁移:

> 把 internal/utils/string.go 中的函数移到 pkg/strutil/

AI 分析:
- 需要迁移 5 个函数
- 28 个文件引用了这些函数
- import 路径需要从 internal/utils 改为 pkg/strutil
- 2 个函数是 internal 包私有,需要改为 exported

执行计划:
1. 创建 pkg/strutil/ 包并复制函数
2. 更新 28 个文件的 import
3. 更新 2 个函数的可见性
4. 删除 internal/utils/string.go
5. 验证编译和测试

接口提取:

> 从 UserService 提取接口,方便测试 mock

AI 分析 UserService 的公开方法:
- CreateUser(ctx, req) (*User, error)
- GetUser(ctx, id) (*User, error)
- UpdateUser(ctx, id, req) (*User, error)
- DeleteUser(ctx, id) error
- ListUsers(ctx, opts) ([]*User, error)

生成接口:
type UserServicer interface {
    CreateUser(ctx context.Context, req CreateUserReq) (*User, error)
    GetUser(ctx context.Context, id string) (*User, error)
    // ...
}

同时更新 3 个调用方的类型声明

方法迁移:

> 把 Order.CalculateTotal() 移到 PriceCalculator

AI 分析:
- CalculateTotal 依赖 Order.Items 和 Order.Discount
- PriceCalculator 需要接收 Order 参数或相关字段
- 3 个调用方需要更新调用方式

方案 A:PriceCalculator.CalculateTotal(order *Order)
方案 B:PriceCalculator.CalculateTotal(items []Item, discount float64)

推荐方案 B:降低耦合,PriceCalculator 不需要知道完整 Order 结构

跨模块依赖调整

> 分析 internal/order/ 和 internal/payment/ 的循环依赖

CKG 依赖图:
order → payment(OrderService 调用 PaymentService.Charge)
payment → order(PaymentCallback 调用 OrderService.UpdateStatus)

建议:
提取 internal/event/ 包,使用事件驱动解耦:
order → event(发布 OrderCreated)
payment → event(订阅 OrderCreated,发布 PaymentCompleted)
order → event(订阅 PaymentCompleted,更新订单状态)

重构预览

变更预览

在实际执行重构前,wescode 提供完整的变更预览:

> 预览:把所有错误处理改为 wrapped error

预览结果:
═══════════════════════════════════════
  重构预览:Error Wrapping
  影响文件:12
  影响行数:47
═══════════════════════════════════════

文件 1: internal/user/repo.go
  行 34:  - return err
          + return fmt.Errorf("find user by id %s: %w", id, err)
  行 56:  - return err
          + return fmt.Errorf("create user: %w", err)

文件 2: internal/order/service.go
  行 23:  - return nil, err
          + return nil, fmt.Errorf("create order: %w", err)
  ...

是否应用这些变更?[y/n/逐个确认]

Diff 视图

重构预览支持编辑器内的 Diff 视图:

> 预览重构并在编辑器中显示 diff

打开 Diff 编辑器:
左侧:原始代码
右侧:重构后代码

可以在 Diff 视图中:
- 逐个文件查看变更
- 接受或拒绝单个变更
- 编辑重构结果后再应用

影响范围可视化

利用 CKG 可视化重构影响范围:

> 显示这次重构的影响范围

影响调用图:
UserHandler.Create()
  └→ UserService.CreateUser()  ← 重构目标
       ├→ UserRepo.Insert()
       ├→ EmailService.Send()
       └→ AuditLog.Record()

直接影响:1 个函数签名变更
间接影响:3 个调用方需要适配
测试影响:5 个测试需要更新

逐步确认模式

对于大范围重构,支持逐个确认每处变更:

变更 1/47: internal/user/repo.go:34
  - return err
  + return fmt.Errorf("find user: %w", err)
  [接受/跳过/编辑] → 接受

变更 2/47: internal/user/repo.go:56
  - return err
  + return fmt.Errorf("create user: %w", err)
  [接受/跳过/编辑] → 接受

...

已应用 45/47 处变更(跳过 2 处)

约束保护

CSE 约束满足引擎

CSE 在重构过程中持续验证项目约束不被违反:

架构约束:

CSE 检查:重构后是否违反分层架构?

✅ handler → service → repo:正确
❌ handler → repo:违反!
   internal/api/admin.go:45 直接引用了 internal/user/repo
   重构前该约束已存在,建议一并修复

✅ 无循环依赖
✅ internal/ 包不被外部引用

业务约束:

CSE 检查:重构后业务规则是否完整?

✅ 订单创建前必须校验库存:规则仍在 CreateOrder 中
✅ 支付前必须锁定订单:规则仍在 ProcessPayment 中
⚠️  用户删除前必须清理关联数据:
    重构后 cleanupRelatedData() 调用在新函数中,
    但执行顺序从"删除前"变为"删除后"
    → 这是不安全的,请确认执行顺序

编码约束:

CSE 检查:重构后编码规范是否满足?

✅ 所有 exported 函数有文档注释
✅ error 返回值在最后一个位置
✅ context.Context 在第一个参数位置
⚠️  新函数 calculateDiscount 未添加单元测试
    → 项目约定:所有 exported 函数必须有测试

自定义约束规则

团队可以在 .wescode/constraints.yaml 中定义项目约束:

constraints:
  architecture:
    - name: "分层约束"
      rule: "handler 不得直接引用 repo"
      check: "no_direct_import(api/*, repo/*)"
    
    - name: "接口隔离"
      rule: "service 层通过接口依赖 repo"
      check: "depends_on_interface(service/*, repo/*)"

  naming:
    - name: "接口命名"
      rule: "接口名以 er 结尾"
      check: "interface_name_suffix(*er)"

  testing:
    - name: "测试覆盖"
      rule: "所有 exported 函数必须有测试"
      check: "exported_functions_have_tests()"

  security:
    - name: "SQL 注入防护"
      rule: "禁止字符串拼接 SQL"
      check: "no_string_concat_sql()"

约束违反报告

当重构违反约束时,AI 会阻止并报告:

⛔ 重构被 CSE 约束阻止:

约束:handler 不得直接引用 repo
位置:internal/api/user_handler.go:15
原因:重构后 handler 直接导入了 internal/user/repo

修复建议:
1. 通过 UserService 间接访问(推荐)
2. 添加约束豁免(不推荐)

是否按建议修复?[y/n]

配置选项

refactoring:
  auto_detect: true              # 自动检测重构机会
  preview_before_apply: true     # 应用前总是预览
  create_checkpoint: true        # 重构前创建检查点
  verify_after_apply: true       # 重构后自动验证
  cse_enabled: true              # 启用 CSE 约束检查
  cse_block_on_violation: true   # 约束违反时阻止重构
  max_files_per_refactor: 50     # 单次重构最多影响文件数
  complexity_threshold: 10       # 圈复杂度阈值
  function_length_threshold: 80  # 函数长度阈值(行)

注意事项