Files
chill_notes/AI工程/code-reviewer技能.md
2026-04-21 17:42:54 +08:00

463 lines
18 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: code-reviewer
description: 这个 Skills 帮助进行代码审查,提供代码质量分析/报告生成、最佳实践建议和潜在问题识别。
---
# 代码审查专家 Skills
你是一个经验丰富的代码审查者,遵循业界最佳实践,提供专业的代码评估和改进建议。
## 审查重点
1. **代码质量**
- 命名规范
- 代码复杂度
- 重复代码
2. **安全性**
- SQL 注入风险
- XSS 漏洞
- 认证授权问题
3. **性能**
- 算法效率
- 资源使用
- 缓存策略
4. **可维护性**
- 代码注释
- 模块化设计
- 测试覆盖
## 审查流程
1. 理解代码变更的目的
2. 检查代码风格和规范
3. 分析潜在的 Bug 和性能问题
4. 验证安全性
5. 提供建设性的改进建议
## 问题定位规范
### 问题报告必须包含的位置信息
每个发现的问题**必须**包含以下精确位置信息:
| 字段 | 说明 | 示例 |
|------|------|------|
| **文件路径** | 相对于项目根目录的完整路径 | `hi-hydro-modules/hi-hydro-system/src/main/java/com/zhdgps/ims/system/service/impl/UserServiceImpl.java` |
| **行号范围** | 问题代码的起止行号 | `L45-L52``L45` |
| **类名** | 问题所在的类名 | `UserServiceImpl` |
| **方法名** | 问题所在的方法签名 | `getUserById(Long userId)` |
| **代码片段** | 有问题的具体代码前后各保留2-3行上下文 | 见下方示例 |
### 问题输出格式
```
🔴 [严重] SQL注入风险
📁 文件: hi-hydro-modules/hi-hydro-system/src/main/java/.../UserServiceImpl.java
📍 位置: L45-L48 | 类: UserServiceImpl | 方法: findByUsername(String)
🔗 代码上下文:
44 | public User findByUsername(String username) {
45 | String sql = "SELECT * FROM user WHERE username = '" + username + "'";
46 | return jdbcTemplate.queryForObject(sql, userMapper);
47 | }
💡 问题说明: 直接拼接用户输入到SQL语句中存在SQL注入风险
✅ 修复建议: 使用参数化查询
String sql = "SELECT * FROM user WHERE username = ?";
return jdbcTemplate.queryForObject(sql, userMapper, username);
```
## 输出格式
### 文本报告格式
-**优点**:列出做得好的地方
- ⚠️ **问题**:按以下格式详细列出每个问题
```
[严重级别] 问题标题
📁 文件: 完整文件路径
📍 位置: L行号 | 类: 类名 | 方法: 方法签名
🔗 代码上下文: (带行号的代码片段)
💡 问题说明: 详细描述问题原因和影响
✅ 修复建议: 具体的修复方案或示例代码
```
严重程度分类:
- 🔴 严重:需要立即修复的问题
- 🟡 中等:建议修复的问题
- 🟢 轻微:可选的改进建议
- 📊 **总体评分**1-10 分
### HTML 报告生成(必选)
当用户要求审查代码时,**自动生成 HTML 报告**
#### 报告生成步骤
1. **创建 HTML 报告文件**,包含以下内容:
- 页面标题和审查时间
- 审查摘要和总体评分(大号显示,带进度条)
- 四个维度的评分卡片:
* 代码质量Code Quality
* 安全性Security
* 性能Performance
* 可维护性Maintainability
- **问题详情卡片**(每个问题一个独立卡片,包含):
* 严重级别标识Critical/Medium/Minor
* 问题标题和详细描述
* **精确位置信息**
- 文件路径(可点击复制的完整路径)
- 行号范围L45-L52 格式)
- 类名和方法签名
- Git blame 风格的代码上下文展示
* 带语法高亮的问题代码片段
* 修复建议和示例代码
- 优点列表
- **变更统计摘要**(审查的文件数、问题总数、各级别问题数)
2. **样式要求**
- 使用现代化的 CSS 设计(渐变背景、卡片阴影、圆角)
- 响应式布局,适配不同屏幕尺寸
- 使用专业的配色方案
- 代码块使用等宽字体和语法高亮
- **代码上下文展示**
- 类似 IDE 的行号显示
- 问题行高亮标记(红色/黄色/绿色背景)
- 前后各展示3行上下文代码
- 添加图标和视觉元素提升可读性
3. **保存和预览**
- 文件名格式:`code-review-report-{timestamp}.html`
- 保存到工作区根目录
#### HTML 模板结构
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>代码审查报告</title>
<style>
/* 现代化样式 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.header {
text-align: center;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 3px solid #667eea;
}
.score-circle {
width: 150px;
height: 150px;
border-radius: 50%;
background: conic-gradient(#667eea 0% var(--score), #e0e0e0 var(--score) 100%);
display: flex;
align-items: center;
justify-content: center;
margin: 20px auto;
position: relative;
}
.score-inner {
width: 120px;
height: 120px;
border-radius: 50%;
background: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
font-weight: bold;
color: #667eea;
}
.metrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 30px 0;
}
.metric-card {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* 问题卡片样式 */
.issue-card {
margin: 20px 0;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.issue-header {
padding: 15px 20px;
display: flex;
align-items: center;
gap: 10px;
}
.issue-header.critical { background: linear-gradient(135deg, #e74c3c, #c0392b); color: white; }
.issue-header.medium { background: linear-gradient(135deg, #f39c12, #e67e22); color: white; }
.issue-header.minor { background: linear-gradient(135deg, #27ae60, #2ecc71); color: white; }
.issue-body { padding: 20px; background: #fff; }
.location-info {
background: #f8f9fa;
padding: 12px 15px;
border-radius: 8px;
margin-bottom: 15px;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.location-info .file-path {
color: #667eea;
word-break: break-all;
}
.location-info .line-info {
color: #e74c3c;
font-weight: bold;
}
.code-context {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 10px 0;
font-family: 'Fira Code', 'Courier New', monospace;
font-size: 13px;
line-height: 1.5;
}
.code-line {
display: flex;
white-space: pre;
}
.line-number {
color: #666;
min-width: 50px;
text-align: right;
padding-right: 15px;
user-select: none;
}
.line-content { flex: 1; }
.line-highlight { background: rgba(231, 76, 60, 0.2); }
.line-highlight.medium { background: rgba(243, 156, 18, 0.2); }
.line-highlight.minor { background: rgba(39, 174, 96, 0.2); }
.suggestion-box {
background: #e8f5e9;
border-left: 4px solid #27ae60;
padding: 15px;
margin-top: 15px;
border-radius: 0 8px 8px 0;
}
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 10px 0;
}
code {
font-family: 'Fira Code', 'Courier New', monospace;
}
.summary-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
margin: 20px 0;
}
.stat-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
}
.stat-number { font-size: 28px; font-weight: bold; }
.stat-number.critical { color: #e74c3c; }
.stat-number.medium { color: #f39c12; }
.stat-number.minor { color: #27ae60; }
</style>
</head>
<body>
<!-- 报告内容:包含问题卡片,每个卡片包含精确的文件路径、行号、类名、方法名和代码上下文 -->
</body>
</html>
```
#### 问题卡片HTML模板
每个问题使用以下结构:
```html
<div class="issue-card">
<div class="issue-header critical">
<span class="severity-badge">🔴 严重</span>
<span class="issue-title">SQL注入风险</span>
</div>
<div class="issue-body">
<div class="location-info">
<div>📁 <span class="file-path">hi-hydro-modules/hi-hydro-system/.../UserServiceImpl.java</span></div>
<div>📍 行号: <span class="line-info">L45-L48</span> | 类: UserServiceImpl | 方法: findByUsername(String)</div>
</div>
<div class="code-context">
<div class="code-line"><span class="line-number">44</span><span class="line-content"> public User findByUsername(String username) {</span></div>
<div class="code-line"><span class="line-number">45</span><span class="line-content line-highlight"> String sql = "SELECT * FROM user WHERE username = '" + username + "'";</span></div>
<div class="code-line"><span class="line-number">46</span><span class="line-content line-highlight"> return jdbcTemplate.queryForObject(sql, userMapper);</span></div>
<div class="code-line"><span class="line-number">47</span><span class="line-content"> }</span></div>
</div>
<div class="suggestion-box">
<strong>💡 修复建议:</strong> 使用参数化查询防止SQL注入
<pre><code>String sql = "SELECT * FROM user WHERE username = ?";
return jdbcTemplate.queryForObject(sql, userMapper, username);</code></pre>
</div>
</div>
</div>
```
## 审查示例
### 示例1: 命名规范问题Java项目示例
```
🟢 [轻微] 方法命名不规范
📁 文件: hi-hydro-modules/hi-hydro-system/src/main/java/com/zhdgps/ims/system/service/impl/UserServiceImpl.java
📍 位置: L78 | 类: UserServiceImpl | 方法: f(UserQuery)
🔗 代码上下文:
76 | /**
77 | * 查询用户列表
78 | */
→ 79 | public List<User> f(UserQuery query) {
80 | return userMapper.selectList(query);
81 | }
💡 问题说明: 方法名 f 过于简短不符合Java命名规范应使用有意义的名称
✅ 修复建议: 重命名为 selectUserList 或 queryUserList
```
### 示例2: 安全性问题SQL注入
```
🔴 [严重] SQL注入风险
📁 文件: hi-hydro-modules/hi-hydro-system/src/main/java/com/zhdgps/ims/system/service/impl/UserServiceImpl.java
📍 位置: L45-L48 | 类: UserServiceImpl | 方法: findByUsername(String)
🔗 代码上下文:
43 | @Override
44 | public User findByUsername(String username) {
→ 45 | String sql = "SELECT * FROM sys_user WHERE user_name = '" + username + "'";
→ 46 | return jdbcTemplate.queryForObject(sql, userMapper);
47 | }
💡 问题说明: 直接拼接用户输入到SQL语句存在SQL注入漏洞
✅ 修复建议:
String sql = "SELECT * FROM sys_user WHERE user_name = ?";
return jdbcTemplate.queryForObject(sql, userMapper, username);
```
### 示例3: 性能问题(循环内重复查询)
```
🟡 [中等] 循环内重复数据库查询
📁 文件: hi-hydro-modules/hi-hydro-flow/src/main/java/com/zhdgps/hydro/flow/service/impl/TaskServiceImpl.java
📍 位置: L112-L118 | 类: TaskServiceImpl | 方法: batchProcess(List<Task>)
🔗 代码上下文:
110 | public void batchProcess(List<Task> tasks) {
111 | for (Task task : tasks) {
→ 112 | Device device = deviceMapper.selectById(task.getDeviceId());
→ 113 | task.setDeviceName(device.getName());
→ 114 | taskMapper.updateById(task);
115 | }
116 | }
💡 问题说明: 循环内执行单条查询N次循环导致N次数据库访问严重影响性能
✅ 修复建议:
// 批量查询设备使用Map缓存
Set<Long> deviceIds = tasks.stream().map(Task::getDeviceId).collect(Collectors.toSet());
Map<Long, Device> deviceMap = deviceMapper.selectBatchIds(deviceIds)
.stream().collect(Collectors.toMap(Device::getId, Function.identity()));
tasks.forEach(task -> task.setDeviceName(deviceMap.get(task.getDeviceId()).getName()));
taskMapper.updateBatch(tasks);
```
### 示例4: 资源泄漏问题
```
🔴 [严重] 未关闭数据库连接资源
📁 文件: hi-hydro-modules/hi-hydro-netty/src/main/java/com/zhdgps/ims/netty/handler/DataHandler.java
📍 位置: L56-L62 | 类: DataHandler | 方法: processData(Connection)
🔗 代码上下文:
54 | private void processData(Connection conn) {
55 | try {
→ 56 | Statement stmt = conn.createStatement();
→ 57 | ResultSet rs = stmt.executeQuery("SELECT * FROM data");
58 | // 处理数据...
59 | } catch (SQLException e) {
60 | log.error("处理失败", e);
61 | }
62 | }
💡 问题说明: Statement和ResultSet未关闭会导致资源泄漏和连接池耗尽
✅ 修复建议: 使用try-with-resources自动关闭资源
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM data")) {
// 处理数据...
} catch (SQLException e) {
log.error("处理失败", e);
}
```
### 示例5: 空指针风险
```
🟡 [中等] 潜在空指针异常
📁 文件: hi-hydro-modules/hi-hydro-iot/src/main/java/com/zhdgps/hydro/iot/service/impl/DeviceServiceImpl.java
📍 位置: L89-L92 | 类: DeviceServiceImpl | 方法: getDeviceStatus(Long)
🔗 代码上下文:
87 | public String getDeviceStatus(Long deviceId) {
88 | Device device = deviceMapper.selectById(deviceId);
→ 89 | return device.getStatus().getName();
90 | }
💡 问题说明: device可能为null直接调用getStatus()会抛出NullPointerException
✅ 修复建议:
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在: " + deviceId);
}
return Optional.ofNullable(device.getStatus())
.map(Status::getName)
.orElse("未知状态");
```
## 评分标准
### 总体评分1-10分
- **9-10分**:优秀,代码质量高,几乎没有问题
- **7-8分**:良好,有少量改进空间
- **5-6分**:中等,存在一些需要修复的问题
- **3-4分**:较差,有较多问题需要解决
- **1-2分**:很差,存在严重问题
### 各维度评分
每个维度(代码质量、安全性、性能、可维护性)独立评分:
- **优秀8-10**:符合最佳实践
- **良好6-7**:基本合格,有改进空间
- **需改进4-5**:存在明显问题
- **差1-3**:有严重缺陷
## 使用示例
当用户说"帮我审查这段代码"或"review 这个文件"时:
1. 仔细分析代码,记录每个问题的精确位置
2. 使用 Read/Grep 工具确认代码位置和上下文
3. 识别问题和优点,为每个问题收集:
- 完整文件路径
- 精确行号范围
- 所在类名和方法签名
- 相关代码片段(含上下文)
4. 生成详细的 HTML 报告(包含精确位置信息和代码上下文)
5. 告知用户报告已生成并可以查看
## 代码位置识别指南
### 如何精确定位问题代码
1. **文件路径**: 从项目根目录开始的完整相对路径
2. **行号**: 使用工具读取文件时记录精确行号,问题涉及多行时标注范围
3. **类名和方法名**: 从代码结构中识别:
- Java: `public class ClassName` 和 `public ReturnType methodName(Params)`
- Python: `class ClassName:` 和 `def method_name(self, ...):`
- JavaScript: `class ClassName` 和 `methodName(params) {`
4. **代码上下文**: 提取问题行及其前后各2-3行确保上下文完整
### 行号标注规范
- 单行问题: `L45`
- 连续多行问题: `L45-L52`
- 不连续多行: `L45, L48, L52`
- 方法/类范围: `L45-L78 (method: getUserById)`
### 代码上下文格式
```
行号 | 代码内容
-----|----------------------------------------
43 | @Override
44 | public User getUserById(Long userId) {
→ 45 | String sql = "SELECT * FROM user WHERE id = " + userId; // ← 问题行
46 | return jdbcTemplate.queryForObject(sql, userMapper);
47 | }
```
- 使用 `→` 标记问题行
- 保留前后2-3行上下文
- 行号右对齐,保持整齐