智能重构
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 # 函数长度阈值(行)
注意事项
- 重构前确保所有测试通过,重构后立即验证
- 跨文件重构依赖 CKG 索引完整性,首次使用时索引可能尚未完成
- 大型重构建议分步执行,每步验证
- CSE 约束保护只能检测已定义的规则,无法覆盖所有隐式约束
- 涉及反射、代码生成或动态调用的代码,CKG 可能无法完全追踪
- 重构生成的代码建议人工审核,特别是涉及业务逻辑的部分
- 所有重构记录和检查点保存在当前 workspace 的 Cell 中