Trigger Scenarios
- The user says "refactor", "extract", "merge", "optimize structure", "simplify code"
- Involves modifications to table columns, data structures, configuration items
- Merging conditional branches (if/else/switch)
Refactoring Process
1. Read the original code (mandatory)
Do not refactor from memory or speculation, you must read the original code in full:
# 读取完整文件
Read: file_path="src/views/Order.vue"
If it is a conditional branch, read the code of all branches
Grep: pattern="if.*Health Cloud" -A 50 -B 5 Grep: pattern="else" -A 50 -B 5
**常见错误**:
- ❌ 只读一个分支,推测其他分支
- ❌ 凭记忆重构表格列
- ❌ 假设两个条件分支结构相似
---
### 2. 制作对比清单
#### 表格重构示例
**原始代码(健康云租户)**:
Column 1: Order Number Column 2: Order Date Column 3: Material Name Column 4: Material Quantity Column 5: Unit Price Column 6: Amount
**原始代码(非健康云租户)**:
Column 1: Order Number Column 2: Creation Time Column 3: Material Name Column 4: Unit Price Column 5: Amount
**对比清单**:
| 健康云 | 非健康云 | 状态 |
|--------|---------|------|
| 订单编号 | 订单编号 | ✅ 一致 |
| 订单日期 | 创建时间 | ⚠️ 名称不同 |
| 材料名称 | 材料名称 | ✅ 一致 |
| 材料数量 | - | ❌ 非健康云无此列 |
| 单价 | 单价 | ✅ 一致 |
| 金额 | 金额 | ✅ 一致 |
#### 数据结构重构示例
**原始接口**:
```typescript
interface Order {
id: string
date: string
items: Item[]
total: number
}
Post-refactoring Interface:
interface Order {
id: string
createdAt: string // 重命名:date → createdAt
items: Item[]
total: number
}
Comparison Checklist:
- ✅ Same number of fields (4)
- ⚠️ Field renamed: date → createdAt
- ✅ Same field order
- ✅ Same field types
3. Verification Checkpoints
After refactoring is complete, you must check item by item:
Table Refactoring Check
- Same number of columns (original 6 columns → 6 columns after refactoring)
- Same column order (column 1 is Order Number, column 2 is Date...)
- Same column names (or a clear mapping relationship)
- No missing columns
- No extra columns
Conditional Branch Check
- All if/else/switch branches have been handled
- The logic of each branch is consistent with the original code
- No assumption that one branch is similar to another branch
Data Structure Check
- Same number of fields
- Same field types
- Same field order (if order matters)
- No missing fields
4. Output Format
After refactoring is complete, output a comparison checklist:
## 重构对比
### 原始结构(健康云租户)
- 列1: 订单编号
- 列2: 订单日期
- 列3: 材料名称
- 列4: 材料数量
- 列5: 单价
- 列6: 金额
### 原始结构(非健康云租户)
- 列1: 订单编号
- 列2: 创建时间
- 列3: 材料名称
- 列4: 单价
- 列5: 金额
### 重构后结构
- 列1: 订单编号
- 列2: 日期(健康云显示"订单日期",非健康云显示"创建时间")
- 列3: 材料名称
- 列4: 材料数量(仅健康云显示)
- 列5: 单价
- 列6: 金额
### 变更说明
- 合并:订单日期 + 创建时间 → 日期(条件显示)
- 条件显示:材料数量仅在健康云租户显示
- 删除:无
- 新增:无
Common Pitfalls
1. Assumption-driven refactoring
Incorrect Example:
// 只读了健康云租户的代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
}
// ❌ 假设非健康云租户只是列名不同
else {
columns = ['订单编号', '创建时间', '材料名称', '材料数量', '单价', '金额']
}
Correct Approach:
// 读取两个分支的原始代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
} else {
// 非健康云租户没有"材料数量"列
columns = ['订单编号', '创建时间', '材料名称', '单价', '金额']
}
2. Memory-based refactoring
Incorrect Example:
// ❌ 凭记忆重构,没有读取原始代码
const columns = ['订单编号', '材料名称', '单价', '金额', '订单日期']
Correct Approach:
// ✅ 读取原始代码,确认列顺序
Read: file_path="src/views/Order.vue"
// 原始代码:['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
const columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
3. Skipping verification
Incorrect Example:
// 重构完成,直接提交
// ❌ 没有验证列数、列顺序、列名
Correct Approach:
## 验证清单
- [x] 列数一致:原始 6 列 → 重构后 6 列
- [x] 列顺序一致:订单编号、订单日期、材料名称、材料数量、单价、金额
- [x] 列名一致:完全一致
- [x] 没有遗漏列
- [x] 没有多余列
5. Extracting submodules causes circular dependencies
Scenario: When splitting submodules from a large service/component, the submodule needs to call back methods of the parent module
⚠️ Pre-check process (must be executed before splitting)
First extract shared methods, then split subservices:
拆分前:
1. 扫描父服务,识别被多个子服务调用的工具方法
2. 将这些方法提取到独立 Helper/Utils 类
3. 然后再拆分子服务(此时子服务依赖 Helper,不依赖父服务)
❌ 错误顺序:拆分子服务 → 发现循环依赖 → 修复
✅ 正确顺序:提取共享方法 → 拆分子服务 → 无循环依赖
⚠️ Repeated Pattern Warning
If a circular dependency has already appeared when extracting the first subservice, all subsequent subservice extractions must first check for the same problem:
真实案例:
- ReportService → 提取 ReportInvoiceService → 循环依赖(resolveTenantIds)
- ReportService → 提取 ReportPaymentService → 同样的循环依赖!
根因:resolveTenantIds() 留在 ReportService,所有子服务都需要它
修复:应在第一次发现时就提取 TenantHelper,避免后续重复犯错
Incorrect Example:
// ❌ ReportService 拆分出 ReportInvoiceService
// 但 ReportInvoiceService 又需要调用 ReportService.resolveTenantIds()
@Service
@RequiredArgsConstructor
public class ReportInvoiceService {
private final ReportService reportService; // 循环依赖!
}
Correct Approach (by priority):
// ✅ 方案1:提取公共方法到独立工具类(推荐)
@Component
public class TenantHelper {
public List<Long> resolveTenantIds(Long tenantId) { ... }
}
// ✅ 方案2:@Lazy 字段注入(应急)
@Service
public class ReportInvoiceService {
@Autowired @Lazy
private ReportService reportService;
}
// ✅ 方案3:函数式回调
public void process(Function<Long, List<Long>> tenantResolver) { ... }
Checklist:
- Pre-check: Are there utility methods in the parent service that are called by multiple subservices? Yes → extract them into an independent class first
- Duplicate Check: Does this split have the same callback dependencies as previous splits?
- Draw dependency graph: after splitting, is there a cycle A → B → A?
- Is the dependency direction unidirectional: parent → child (child → parent prohibited)
- Spring Boot 3.x prohibits constructor circular dependencies by default
Not limited to Spring:
- Go: package-level circular imports (compile error) → extract a common package
- Vue: component circular references → async components or extract common logic into composables
- TypeScript: module circular references → extract a common module
6. Initialization conflicts when extracting state management
Scenario: when extracting state from a component into a custom Hook/composable/Service, the encapsulated open method adds initialization logic, overwriting the state preset by the caller
Real case:
// 重构前:调用方直接控制状态,时序正确
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
setMarkPaidModalVisible(true) // 2. 打开弹窗 ✅
// 重构后:提取到 useOrderModals hook
const openMarkPaidModal = () => {
setMarkPaidText('') // ❌ 清空了调用方刚设置的值!
setMarkPaidModalVisible(true)
}
// 调用方:
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
openMarkPaidModal() // 2. 内部又清空了 → 弹窗空白
Correct approach:
// ✅ 方案1:open 方法接受参数
const openMarkPaidModal = (initialText?: string) => {
if (initialText !== undefined) setMarkPaidText(initialText)
setMarkPaidModalVisible(true)
}
// ✅ 方案2:初始化放在 close 而非 open
const closeMarkPaidModal = () => {
setMarkPaidText('') // 关闭时清空是安全的
setMarkPaidModalVisible(false)
}
Checklist:
- When extracting the open/show method, check whether the caller sets state before open
- Will the initialization logic in the open method overwrite the value preset by the caller
- Initialization/clearing logic should be placed in close rather than open
- If open needs initialization, it should be passed in via parameters rather than hardcoded internally
Applicable scope:
- React:useState → useXxxModal hook
- Vue:ref → useXxxDialog composable
- Java: Service methods adding default value logic
7. Loss of layout structure during UI component refactoring
Scenario: when refactoring container components such as Modal/Dialog/Drawer, simplifying the complex title/header/footer, causing button layout and functionality to be lost
Real case:
// 重构前:title 包含条件渲染的按钮
<Modal
title={
<div className="flex justify-between">
<span>发票详情</span>
{!editMode && <Button icon={<EditOutlined />}>编辑发票</Button>}
{editMode && (
<Space>
<Button icon={<CloseOutlined />}>取消</Button>
<Button type="primary" icon={<SaveOutlined />}>保存</Button>
</Space>
)}
</div>
}
footer={[
<Button key="download" type="primary" icon={<DownloadOutlined />}>
下载原始PDF
</Button>,
<Button key="close">关闭</Button>,
]}
/>
// ❌ 重构后:title 简化为纯文本,footer 按钮被替换
<Modal
title={`发票详情 - ${invoice?.invoiceNumber}`} // 丢失编辑/取消/保存按钮
footer={[
<button key="edit">编辑</button>, // 降级为原生 button + 丢失"下载PDF"
<button key="close">关闭</button>,
]}
/>
Checklist:
- Is the JSX structure of title/header/footer consistent with the original code
- Are conditionally rendered buttons (
{condition && <Button>}) all preserved - Is the number of buttons consistent (N before refactoring → N after refactoring)
- Are component library components preserved (Ant Design
<Button>cannot be downgraded to<button>) - Are functional buttons missing (such as "Download", "Export", etc.)
Applicable scope:
- React: title/footer of Modal, Dialog, Drawer
- Vue: header/footer slots of el-dialog, el-drawer
- Any UI container component containing conditionally rendered buttons
8. Complex rendering logic simplified and replaced
Scenario: when refactoring table columns or components, replacing complex render functions (JSON parsing, conditional formatting, diff comparison) with simple text display, causing functionality degradation
Real case:
// 重构前:~80 行的复杂 render,从 beforeValue/afterValue JSON 解析渲染 diff
{
title: '变更详情',
key: 'changeDetails',
render: (_, record) => {
const oldValues = JSON.parse(record.beforeValue)
const newValues = JSON.parse(record.afterValue)
// ... 字段对比、颜色标记、条件渲染(新增/删除/更新)
return <div>{changedKeys.map(key => (
<div>{translateFieldName(key)}: {oldValue} → {newValue}</div>
))}</div>
}
}
// ❌ 重构后:简化为读取一个数据库中为空的字段
{
title: '变更摘要',
dataIndex: 'changeSummary', // 数据库中此字段为空!
render: (val) => val || '-' // 全部显示 -
}
Checklist:
- Is the original render function longer than 20 lines or does it contain JSON parsing? → cannot be simplified to plain text
- Does the field used after refactoring (changeSummary) have data in the database?
- Is the rendering effect equivalent (color markers, diff comparison, conditional display)
- Is the data source consistent (beforeValue/afterValue vs changeSummary)
Applicable scope:
- Refactoring of render functions for table columns
- Refactoring of custom rendering for list items
- Any rendering logic containing JSON parsing or conditional formatting
4. Field name inference refactoring (extremely hidden)
Scenario: there are multiple similar fields in the type definition, and the wrong one was chosen during refactoring
Incorrect example:
// 类型定义
interface InvoiceHistory {
changedAt?: string // 可选字段
createdAt: string // 必填字段
changeType?: string // 错误字段
operationType: string // 正确字段
}
// ❌ 根据类型定义推测,选择了可选字段
dataIndex: 'changedAt' // 可能为 undefined → Invalid Date
dataIndex: 'changeType' // 字段不存在 → 显示为空
// ❌ 枚举映射不完整
typeMap: {
CREATE: { text: '创建', color: 'green' },
UPDATE: { text: '更新', color: 'blue' },
DELETE: { text: '删除', color: 'red' },
// 遗漏了 UPLOAD、OCR_START、OCR_SUCCESS 等 7 个类型
}
Correct approach:
// ✅ 查看原始代码的实际使用
git show HEAD~1:src/components/InvoiceHistoryTab.tsx
// 原始代码使用 createdAt(必填)和 operationType(正确)
dataIndex: 'createdAt'
dataIndex: 'operationType'
// 原始代码有 10 个枚举类型
typeMap: {
UPLOAD: { text: '上传发票', color: 'blue' },
OCR_START: { text: '开始OCR', color: 'cyan' },
OCR_SUCCESS: { text: 'OCR成功', color: 'green' },
OCR_FAILED: { text: 'OCR失败', color: 'red' },
OCR_RETRY: { text: '重试OCR', color: 'orange' },
MANUAL_EDIT: { text: '手动编辑', color: 'orange' },
LINK_ORDER: { text: '关联订单', color: 'purple' },
UNLINK_ORDER: { text: '取消关联', color: 'magenta' },
FIELD_CONFIRM: { text: '确认字段', color: 'green' },
DELETE: { text: '删除发票', color: 'red' },
}
// ✅ 防御性编程
render: (val: string) => {
if (!val) return '-'
try {
return new Date(val).toLocaleString('zh-CN')
} catch {
return val
}
}
Checklist:
- Check the actual field names in the original code (don't just look at the type definitions)
- Prefer required fields, avoid optional fields
- Enum mappings must be complete (check against the original code one by one)
- Runtime testing (TypeScript cannot detect field name errors)
- Defensive programming (if (!val) return '-')
Why TypeScript cannot detect it:
// ❌ TypeScript 不会报错(changedAt 在类型定义中存在)
dataIndex: 'changedAt' // 类型检查通过
render: (val: string) => new Date(val).toLocaleString()
// 运行时:val 为 undefined → Invalid Date
Rule traceability
> 📋 本回复遵循:`refactor-safety` - [章节]