codex-code-review
SKILL.md
代码审查工作流
两阶段审查:本地检查(规范 + 逻辑) + Codex 深度审查(可选)。
Phase 1: 收集变更范围
git diff --name-only HEAD
git diff --cached --name-only
如果没有变更文件,提示"没有检测到代码变更"并终止。
将变更文件按类型分组:
- Java 文件:Controller / Business / Service / Mapper / Entity / VO / DTO
- XML 文件:Mapper XML
- SQL 文件:建表/变更脚本
- 其他:配置文件等
Phase 2: 本地检查(必做)
分两步执行:Step A 用 Grep 批量扫描规范问题(秒级),Step B 用 Read 逐文件审查代码逻辑。
Step A: 项目规范扫描(Grep 批量检查)
🔴 A1. 包名规范
Grep pattern: "package org\.dromara\." path: [目标目录] glob: "*.java"
- ❌
package org.dromara.xxx→ ✅package net.xnzn.core.xxx
🔴 A2. 禁止 RuoYi 工具类
Grep pattern: "MapstructUtils" path: [目标目录] glob: "*.java"
Grep pattern: "ServiceException" path: [目标目录] glob: "*.java"
Grep pattern: "import javax\.validation" path: [目标目录] glob: "*.java"
- ❌
MapstructUtils→ ✅BeanUtil.copyProperties() - ❌
ServiceException→ ✅LeException - ❌
javax.validation.*→ ✅jakarta.validation.*(JDK 21)
🔴 A3. 审计字段名称
Grep pattern: "private.*createBy|private.*updateBy|private.*createTime|private.*updateTime" path: [目标目录] glob: "*.java"
- ❌
createBy/updateBy/createTime/updateTime→ ✅crby/upby/crtime/uptime
🔴 A4. del_flag 值语义
Grep pattern: "delFlag.*=.*0|del_flag.*=.*0" path: [目标目录] glob: "*.java"
- ❌
delFlag = 0(RuoYi 正常值) → ✅delFlag = 2(leniu 正常值,1=删除)
🔴 A5. Entity 不含 tenant_id
Grep pattern: "tenantId|tenant_id" path: [目标目录] glob: "*.java"
- ❌ 双库物理隔离,无需
tenant_id字段
🔴 A6. 禁止 Map 传递业务数据
Grep pattern: "Map<String,\s*Object>" path: [目标目录] glob: "*.java"
- ❌
Map<String, Object>→ ✅ 使用 VO/DTO 类
🟡 A7. 事务注解缺少 rollbackFor
Grep pattern: "@Transactional\b" path: [目标目录] glob: "*.java"
# 对命中文件二次检查:是否缺少 rollbackFor
Grep pattern: "@Transactional\((?!.*rollbackFor)" path: [命中文件]
- ❌
@Transactional→ ✅@Transactional(rollbackFor = Exception.class)
🔴 A7b. selectOne 无唯一保障
Grep pattern: "selectOne(" path: [目标目录] glob: "*.java"
# 命中行检查:是否有 LIMIT 1 或注释说明唯一索引
- ❌ 无 LIMIT 且无唯一索引 → ✅ 加
.last("LIMIT 1")或确保有唯一索引
🟡 A7c. selectCount 用于存在性判断
Grep pattern: "selectCount" path: [目标目录] glob: "*.java"
# 检查命中行是否用于 > 0 判断(存在性),而非真正计数
- ❌
selectCount(w) > 0→ ✅mapper.exists(w)或selectList(w.last("LIMIT 1"))
🔴 A7d. Redis KEYS 命令
Grep pattern: "keysByPattern|\.keys\(" path: [目标目录] glob: "*.java"
- ❌
redisTemplate.keys()/keysByPattern()→ ✅ 使用 RedissondeleteByPattern()(内部 SCAN + UNLINK)
🟡 A7e. Optional.of 误用
Grep pattern: "Optional\.of\(" path: [目标目录] glob: "*.java"
# 排除 ofNullable 的命中
- ❌
Optional.of(可能为null)→ ✅Optional.ofNullable(value)
🟡 A7f. 布尔字段类型错误
Grep pattern: "private Integer (if|is|has)" path: [目标目录] glob: "*.java"
- ❌
private Integer isEnabled→ ✅private Boolean enabled
🟡 A7g. Wrapper 嵌套过深
# 人工审查:Read 文件时检查 Wrapper 嵌套是否超过 2 层
- ❌ Wrapper 嵌套 >2 层 → ✅ 迁移到 XML 原生 SQL
🟡 A8. 请求体封装
Grep pattern: "@RequestBody [^L]" path: [目标目录] glob: "*Controller.java"
- 建议 POST 请求使用
@RequestBody LeRequest<T>封装
🟡 A9. 金额类型错误
Grep pattern: "Double|Float|double|float" path: [目标目录] glob: "*.java"
# 在命中行中检查是否涉及金额字段(amount/price/money/fee/cost)
- ❌
Double/Float处理金额 → ✅Long(分)或BigDecimal
🟡 A10. BigDecimal 比较错误
Grep pattern: "BigDecimal.*==|==.*BigDecimal" path: [目标目录] glob: "*.java"
- ❌
bigDecimal1 == bigDecimal2→ ✅bigDecimal1.compareTo(bigDecimal2) == 0
🟡 A11. SELECT * 查询
Grep pattern: "SELECT \*|select \*" path: [目标目录] glob: "*.xml"
- ❌
SELECT *→ ✅ 明确列出需要的字段
🟡 A12. SQL 注入风险
Grep pattern: '\$\{' path: [目标目录] glob: "*.xml"
- ❌
${}拼接参数 → ✅#{}参数化查询(ORDER BY 等特殊场景除外)
🟡 A13. 国际化异常
Grep pattern: 'new LeException\("[^"]*[\u4e00-\u9fa5]' path: [目标目录] glob: "*.java"
- 建议使用
I18n.getMessage()替代硬编码中文
Step B: 代码逻辑审查(Read 逐文件检查)
对每个变更的 Java 文件执行 Read,按以下清单逐项审查。
Java 必检项(所有 Java 文件)
| # | 检查项 | 检查要点 | 严重级 |
|---|---|---|---|
| B1 | 空指针风险 | selectOne/getOne/selectById 返回值是否有 null 判断;Optional 是否正确处理 |
🔴 |
| B2 | 参数校验 | 入参是否校验非空和合法性(@NotNull/@NotEmpty/Objects.requireNonNull/手动 if 判断) |
🔴 |
| B3 | 并发安全 | 查询+新增/查询+更新的组合操作是否有竞态条件;是否需要分布式锁 | 🔴 |
| B4 | 事务边界 | 多表写操作是否加了 @Transactional(rollbackFor = Exception.class) |
🔴 |
| B5 | 资源关闭 | Stream/IO流/数据库连接是否用 try-with-resources 或 finally 关闭 | 🔴 |
| B6 | 集合并发修改 | forEach 内是否有 remove/add 操作;应使用 Iterator 或 removeIf |
🔴 |
| B7 | 分页泄漏 | PageMethod.startPage() 是否紧贴查询语句,中间不能有其他 SQL 查询 |
🔴 |
Java 安全项
| # | 检查项 | 检查要点 | 严重级 |
|---|---|---|---|
| B8 | 租户隔离(仅定时任务) | 定时任务中是否使用 Executors.doInTenant()/doInAllTenant() 切换到商户库;普通接口默认在商户库,无需额外处理 |
🔴 |
| B9 | SQL 注入 | 动态 SQL 是否使用参数化查询;拼接 SQL 是否转义 | 🔴 |
| B10 | 越权访问 | 删除/修改操作是否校验数据归属(检查 where 条件是否包含归属字段) | 🔴 |
| B11 | 敏感信息 | 日志中是否打印了密码、token、身份证、银行卡等敏感信息 | 🔴 |
| B12 | 批量操作限制 | 批量删除/更新是否限制了最大数量,防止误操作全表 | 🟡 |
Java 跨模块调用项
| # | 检查项 | 检查要点 | 严重级 |
|---|---|---|---|
| B13 | 返回值兜底 | 返回 List/Map 时是否有空集合兜底(Collections.emptyList()),避免调用方 NPE |
🟡 |
| B14 | 集合参数防御 | 集合入参(List/Set)是否判空,空集合的 IN() 会导致 SQL 异常 |
🔴 |
| B15 | 异常透传 | 是否吞掉异常不抛出(空 catch 块);跨模块调用需要明确的异常传递 | 🔴 |
| B16 | 日志追踪 | 关键操作或异常分支是否有日志(log.info/log.error),便于跨模块问题排查 |
🟡 |
Java 代码规范项
| # | 检查项 | 检查要点 | 严重级 |
|---|---|---|---|
| B17 | 魔法值 | 是否存在未定义的常量(状态码 1/2/3 必须用枚举,字符串必须定义常量) | 🟡 |
| B18 | 方法长度 | 方法是否过长(>50 行),需要拆分为子方法 | 🔵 |
| B19 | 注释完整性 | 公共 API(Controller/Business 方法)是否有 JavaDoc 注释 | 🔵 |
| B20 | 空 catch 块 | catch 块是否为空或仅打印日志而不处理/不抛出 | 🟡 |
| B21 | 过时 API | 是否使用了 @Deprecated 的方法或类 |
🔵 |
| B22 | 返回值一致性 | Controller 层是否统一返回 LeResponse<T>,不能裸返回 |
🟡 |
MyBatis XML 检查(仅 XML 文件)
| # | 检查项 | 检查要点 | 严重级 |
|---|---|---|---|
| X1 | SQL 注入 | 使用 ${} 而非 #{} 进行参数拼接(ORDER BY 等场景需白名单校验) |
🔴 |
| X2 | IN 查询防护 | IN 查询是否用 <foreach> 且考虑集合为空的情况(空 IN 会 SQL 异常) |
🔴 |
| X3 | 动态 SQL 语法 | <if>/<where>/<choose> 标签使用是否正确,是否会产生多余的 AND/OR |
🟡 |
| X4 | **SELECT *** | 是否使用 SELECT *,应明确列出需要的字段 |
🟡 |
| X5 | 缺少 WHERE | UPDATE/DELETE 是否缺少 WHERE 条件(全表操作风险) | 🔴 |
| X6 | 索引失效 | WHERE 条件是否对索引字段使用了函数(DATE(crtime))或隐式类型转换 |
🟡 |
| X7 | LIKE 前模糊 | LIKE '%xxx%' 或 LIKE CONCAT('%', #{}, '%') 前模糊导致全表扫描 |
🟡 |
| X8 | 大表无分页 | 大表查询是否遗漏分页,可能造成 OOM | 🟡 |
| X9 | namespace 匹配 | namespace 是否与 Mapper 接口全限定名完全匹配 | 🔴 |
| X10 | resultMap 映射 | 是否正确定义 resultMap,字段名和属性名是否对应 | 🟡 |
本地检查结果展示
# 代码审查报告
审查范围: [变更文件列表]
## 🔴 严重问题(X 项)
1. [B1 空指针风险]
文件: OrderServiceImpl.java:42
问题: selectById 返回值未做 null 判断
修复: if (ObjectUtil.isNull(entity)) throw new LeException("数据不存在");
2. [A2 禁止 RuoYi 工具类]
文件: UserServiceImpl.java:15
问题: 使用了 MapstructUtils
修复: 替换为 BeanUtil.copyProperties()
## 🟡 警告问题(X 项)
...
## 🔵 建议(X 项)
...
## ✅ 通过项
- [x] A1 包名规范 (net.xnzn.core.*)
- [x] A3 审计字段正确 (crby/crtime/upby/uptime)
- [x] A4 del_flag 语义正确 (2=正常)
- [x] B8 租户隔离正确
...
结论: ✅ 通过 / ⚠️ 需修复 X 项 / ❌ 不通过
如果全部通过 → 展示"本地检查通过",询问是否需要 Codex 深度审查。 如果有问题 → 先修复严重问题,修复后再询问是否需要 Codex 深度审查。
Phase 3: Codex 深度审查(可选)
依赖
collaborating-with-codexskill 的codex_bridge.py脚本。 Codex 擅长发现本地规则难以覆盖的逻辑 Bug、复杂并发问题、架构缺陷。
Phase 2 完成后询问用户:"是否需要 Codex 深度审查?"
- 用户同意 → 执行以下流程
- 用户拒绝 → 跳到 Phase 5
python3 .claude/skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd . \
--sandbox read-only \
--PROMPT "Review the following changed files for code quality issues:
FILES TO REVIEW:
{变更文件列表,含相对路径}
REVIEW FOCUS (beyond basic lint):
1. Logic bugs: race conditions, off-by-one, incorrect state transitions, edge cases
2. Security: privilege escalation, data leakage across tenants, missing auth checks
3. Architecture: Controller calling Mapper directly, Business layer bypassed, circular dependencies
4. Performance: N+1 queries, missing pagination on large tables, unnecessary DB calls in loops
5. Concurrency: check-then-act without locking, shared mutable state, CompletableFuture error handling
PROJECT CONTEXT:
- Package: net.xnzn.core.* (NOT org.dromara.*)
- 4-layer: Controller → Business → Service → Mapper
- Dual-database: tenant DB is default (by MERCHANT-ID header); only scheduled tasks need Executors.doInTenant()/doInAllTenant() to switch; Executors.doInSystem() for system DB access
- Audit fields: crby/crtime/upby/uptime
- del_flag: 1=deleted, 2=normal
- Exception: LeException (NOT ServiceException)
- Object copy: BeanUtil.copyProperties() (NOT MapstructUtils)
- Amount: stored as Long (fen/cents), NOT Double/Float
- Pagination: PageMethod.startPage() must be immediately before query
OUTPUT FORMAT:
For each issue:
- [SEVERITY] CRITICAL / WARNING / SUGGESTION
- [FILE] filepath:line_number
- [ISSUE] Description
- [FIX] Recommended fix
If no issues: ALL CLEAR
IMPORTANT: All comments in Chinese, code/paths in English."
关键约束:
- 始终使用
--sandbox read-only,Codex 不直接修改文件 - 变更文件过多时(>10 个),按模块分批审查
- 使用
run_in_background避免阻塞
Phase 4: 用户确认后修复
合并 Phase 2 和 Phase 3 的所有问题,等待用户确认:
- 全部修复:"修复所有问题" → 逐个修复所有 🔴 + 🟡
- 选择性修复:"只修复严重问题" → 仅修复 🔴
- 跳过:"不需要修复" → 终止
修复时:
- 按文件逐个修复,使用 Edit 工具
- 每修复一个文件,简要说明改动
- 🔵 建议级别默认跳过,除非用户明确要求
- 修复完成后运行
git diff展示所有变更
Phase 5: 最终确认
修复完成后询问:"是否需要再次审查确认?"
- 如果用户同意 → 回到 Phase 2 重新审查
- 如果用户拒绝 → 输出最终结论并终止
结论: ✅ 通过 / ⚠️ 需修复 / ❌ 不通过
前端审查(如涉及前端文件变更)
前端项目路径:/Users/xujiajun/Developer/frontProj/web
| # | 检查项 | 检查要点 |
|---|---|---|
| F1 | 响应码 | 成功码是 10000(不是 200) |
| F2 | Token | Admin-Token(localStorage) |
| F3 | 租户头 | MERCHANT-ID(请求头) |
| F4 | 金额显示 | 后端返回分,前端用 money() 转元 |
| F5 | 权限指令 | 按钮权限使用 v-hasPerm |
| F6 | 加密 | 敏感字段使用 SM4 加密 |
Weekly Installs
4
Repository
xu-cell/ai-engi…ing-initGitHub Stars
9
First Seen
9 days ago
Security Audits
Installed on
gemini-cli4
github-copilot4
codex4
kimi-cli4
cursor4
amp4