SuperAPI AI Directory

refactor-safety

Coding & development

View repository

Updated 10-16 06:12

About this skill

Refactoring Safety Guidelines

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` - [章节]