- name
- code-reviewer
- description
- Lingman-Starter 多端代码审查助手。当用户需要:(1) 检查后端或前端代码是否符合公司规范 (2) 审查 CRUD 分层、命名、权限、异常与事务 (3) 审查管理后台 Web 页面 (4) 审查 uni-app 页面、组件、API 接入、Pinia、跨端兼容和资源生命周期 (5) 发现空指针、SQL 注入、越权和敏感信息风险 时触发。审查前必须先识别项目类型并加载对应规范。不要在以下场景触发:生成代码、生成 SQL、单纯运行时错误排查。
# 代码审查指南
## 审查入口与项目分流
审查前先识别目标,不能看到 `.vue` 就默认按管理后台审查:
| 类型 | 主要证据 | 使用规范 |
|---|---|---|
| Java 后端 | `pom.xml`、Spring/MyBatis、`src/main/java` | [framework.md](../lingman-core/framework.md) |
| 管理后台 Web | `src/views`、Element Plus、`@lingman/yd` | [frontend-spec.md](../lingman-core/frontend/frontend-spec.md) |
| uni-app | `@dcloudio/*`、pages/manifest、`uni.scss`、小程序/APP | [uni-app-spec.md](../lingman-core/uni-app/uni-app-spec.md) |
| p708 或相似项目 | uni-app 且命中对应请求、TabBar、视频架构 | [p708-verified-patterns.md](../lingman-core/uni-app/p708-verified-patterns.md) |
混合仓库按变更文件路径分别审查,禁止共用错误模板。完整检查项见 [review-checklist.md](references/review-checklist.md)。
## 审查维度
| 维度 | 检查项 | 严重程度 |
|------|--------|---------|
| **规范合规** | 包名、类名、URL 是否符合约定 | 高 |
| **分层正确** | Controller/Service/Mapper 是否越层调用 | 高 |
| **参数校验** | 入参是否有 `@Valid` / `@NotNull` | 高 |
| **权限控制** | 管理端接口是否有 `@PreAuthorize` | 高 |
| **异常处理** | 是否使用 `throw exception(ErrorCode)` | 中 |
| **事务配置** | 写操作是否有 `@Transactional` | 中 |
| **空值安全** | 数据库查询结果是否判空 | 中 |
| **SQL 安全** | 是否使用 `${}` 拼接 SQL | 高 |
| **返回格式** | 是否使用 `CommonResult<T>` 包装 | 中 |
| **VO 隔离** | Controller 是否直接返回 DO | 高 |
| **日志规范** | 是否用 `@Slf4j`、套用 `[线程id][操作人][模块][动作] key={}` 公式、含唯一 ID、异常末位、未打大对象、未拼字符串、未泄露敏感数据 | 中 |
> **管理后台 Web 审查维度**:仅在项目类型确认后使用,详见 [frontend-spec.md](../lingman-core/frontend/frontend-spec.md)
> - API 层:是否从 `api/auto/` 导入自动生成的 API 对象(`import { ApiAppXxxAppAdminApiAuto }`),禁止直接手写 URL
> - **API 自动生成文件保护**:`src/api/auto/` 下的文件是否为命令自动生成,是否存在手动修改的痕迹(如手动增删接口、调整参数类型等)
> - 列表页:是否使用 `useTable` hook 管理列表状态,而非手动维护 `loading`/`total`/`list`
> - 列表页搜索表单:字段 ≥ 2 个时是否使用 `<Search>` 组件;仅 1 个搜索字段或需自定义样式时是否合理回退 `el-form`(详见 frontend-spec.md「搜索表单」说明)
> - 表单校验:`FormRules` 是否与后端 `@Valid` 规则一致(必填、格式、长度)
> - 错误处理:是否使用 `useMessage()` 或 `ElMessage`/`ElMessageBox`,异常捕获是否完整(`try/finally`)
> - 字典使用:表格中字典列是否使用 `<DictTag>`,下拉框是否使用 `getDictOptions` / `getIntDictOptions`
> - **组件/工具优先使用 `@lingman/yd`**:
> - 列表表格是否优先使用 `<Table>` 组件,而非直接使用 `<el-table>`
> - 弹窗是否优先使用 `<Dialog>` 组件(或 `el-dialog`),避免自己封装弹窗
> - 日期格式化是否优先使用 `formatDate` / `dateFormatter`,避免手写日期转换
> - 文件下载是否优先使用 `download.excel` / `download.word` 等,避免自己封装下载
> - 字典查询是否优先使用 `getDictOptions` / `getDictLabel`,避免自己写字典查询逻辑
> - 表单校验是否优先使用 `useValidator()` 的 `required()` / `lengthRange()`,避免自己写通用校验规则
> - UUID/随机字符串是否优先使用 `generateUUID()` / `generateRandomStr()`,避免自己写生成逻辑
> - 树形操作是否优先使用 `eachTree` / `findNode` / `treeToList`,避免自己写树遍历
> - Token 操作是否优先使用 `getAccessToken()` / `setToken()`,避免自己写 Token 读写
> - 样式规范:是否优先使用 UnoCSS 原子类,组件样式是否加 `scoped`;数值表示间距、尺寸、宽高、`gap`、定位或平移偏移时是否显式带 CSS 单位,语义关键字、命名令牌、比例/分数等排除项按 [frontend-spec.md](../lingman-core/frontend/frontend-spec.md)「11.1 优先使用 UnoCSS 原子类」执行
> - 权限控制:按钮是否使用 `v-hasPermi`,JS 逻辑中是否使用 `checkPermi`
>
> **uni-app 审查维度**:仅在确认是 uni-app 后使用,详见 [uni-app-spec.md](../lingman-core/uni-app/uni-app-spec.md) 和 [review-checklist.md](references/review-checklist.md)
> - 自动生成目录与 pages/manifest 配置源是否正确,未手改受保护文件
> - TabBar 导航、页面生命周期、Pinia 状态和请求桥接是否复用项目能力
> - APP/H5/小程序条件编译是否隔离,浏览器/平台 API 未越界
> - Wot/项目 UI 库、UnoCSS/rpx、主题变量、安全区和动态图标 safelist 是否正确
> - 视频、WebRTC、MediaStream、定时器和监听器是否在隐藏/卸载时释放
> - APP 优先任务是否只验证了 H5,未验证项是否如实说明
## 审查清单
### Controller 层
- [ ] 类上有 `@Tag` + `@RestController` + `@RequestMapping`
- [ ] 管理端 URL 前缀正确:`/admin/{biz}`(管理端)、`/app/{biz}`(用户端)
- [ ] 类上有 `@Validated`
- [ ] 管理端方法有 `@PreAuthorize("@ss.hasPermission('xxx')")`
- [ ] 入参有 `@Valid`(POST/PUT)
- [ ] 接口路径全局唯一,无"同路径不同请求方式"冲突
- [ ] 非 GET 请求(POST/PUT/DELETE/PATCH)一律使用 `@RequestBody`,未误用 `@RequestParam` / `@PathVariable`
- [ ] 单 ID 删除/操作统一使用公共类 `AdminDeleteReqVO`(`common_vo/AdminDeleteReqVO.java`),字段名为 `id`,未自行定义 `{Name}DeleteReqVO`
- [ ] 返回类型为 `CommonResult<T>`
- [ ] Swagger 注解完整:`@Operation(summary = "...")`
### Service 层
- [ ] 接口定义在 `service/admin/` 或 `service/app/`
- [ ] 实现类在 `service/admin/impl/` 或 `service/app/impl/`
- [ ] 实现类有 `@Service`
- [ ] 写操作有 `@Transactional(rollbackFor = Exception.class)`
- [ ] 使用 `throw exception(ErrorCodeConstants.XXX)` 抛业务异常
- [ ] 查询结果判空后再使用
### 日志
- [ ] 统一使用 Lombok `@Slf4j`(字段名 `log`),未手写 `LoggerFactory.getLogger(...)`
- [ ] **套用结构化公式** `[线程id] [操作人] [业务模块] [业务动作], key={}, ...`,带四要素前缀
- [ ] 每条日志**包含唯一标识符(ID)**,可追溯具体记录
- [ ] 用 `{}` 占位符,未用字符串拼接
- [ ] 异常对象作为最后一个参数,未拼进消息、未吞堆栈(不只 `e.getMessage()`)
- [ ] 未打印完整大对象(DO/VO/List/Map),只打关键 ID 或摘要
- [ ] 未写自然语言长句
- [ ] 昂贵操作用 `isDebugEnabled()` / `isInfoEnabled()` 守卫
- [ ] 日志级别合理(ERROR/WARN/INFO/DEBUG)
- [ ] 未记录密码、token、身份证、手机号、银行卡等敏感数据
- [ ] 循环/高频路径未打 `INFO` 及以上日志
### VO 层
- [ ] 有 `@Schema(description = "...")`
- [ ] 分页 VO 继承 `PageParam`
- [ ] 时间字段有 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")`
- [ ] 校验注解使用正确:`@NotBlank`、`@NotNull`、`@Size`
### Mapper/DO 层
- [ ] DO 继承 `BaseDO`
- [ ] `@TableName` 命名正确:`t_xxx`
- [ ] Mapper 继承 `BaseMapperX<T>` 且有 `@Mapper`
- [ ] 字段用 `@TableField` 映射
## 常见问题及修复
### 问题:Controller 直接返回 DO
```java
// 错误
public CommonResult<AnnouncementDO> get(Long id) {
return CommonResult.success(announcementMapper.selectById(id));
}
// 正确
public CommonResult<AnnouncementRespVO> get(Long id) {
return CommonResult.success(announcementService.get(id));
}
```
### 问题:缺少权限注解
```java
// 错误
@GetMapping("/delete")
public CommonResult<Boolean> delete(...) { }
// 正确
@GetMapping("/delete")
@PreAuthorize("@ss.hasPermission('app:announcement:delete')")
public CommonResult<Boolean> delete(...) { }
```
### 问题:未判空直接使用
```java
// 错误
AdminUserDO user = adminUserMapper.selectById(userId);
return user.getNickname();
// 正确
AdminUserDO user = adminUserMapper.selectById(userId);
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
return user.getNickname();
```
### 问题:需要幂等但未加注解
```java
// 以下场景需要考虑幂等:
// - 用户可能重复点击的提交操作(创建、支付、审批提交)
// - 网络重试可能导致重复请求的场景
// - 表单提交、按钮点击类接口
// 错误:重复点击可能产生重复数据
@PostMapping("/submit")
public CommonResult<Long> submit(@RequestBody XxxReqVO reqVO) { }
// 正确:添加幂等保护
@Idempotent(timeout = 10, timeUnit = TimeUnit.SECONDS, keyResolver = UserIdempotentKeyResolver.class)
@PostMapping("/submit")
public CommonResult<Long> submit(@RequestBody XxxReqVO reqVO) { }
// 无需幂等的场景:查询、列表、获取详情等幂等操作
@PostMapping("/page")
public CommonResult<PageResult<XxxRespVO>> page(@RequestBody XxxPageReqVO reqVO) { }
```
### 问题:VO 缺少数据翻译
```java
// 错误:字典值直接返回数字,前端无法展示文本
@Data
public class XxxRespVO {
private Integer status; // 前端看到 0/1,不知道含义
}
// 正确:使用 @Trans 自动翻译
@Data
public class XxxRespVO implements VO {
@Trans(type = TransType.SIMPLE, target = DictDataDO.class, fields = "label", ref = "statusName")
private Integer status;
private String statusName; // 自动填充为"启用"/"禁用"
}
```
### 问题:忘记清除审计字段
```java
// 错误:更新时未清除 BaseDO 的审计字段
XxxDO updateObj = xxxConvert.convert(reqVO);
xxxMapper.updateById(updateObj);
// 正确:先 clean() 再更新
XxxDO updateObj = xxxConvert.convert(reqVO);
updateObj.clean();
xxxMapper.updateById(updateObj);
```
### 问题:未注册数据权限
```java
// 错误:新实体没有在 AppDataPermissionConfiguration 中注册
// 导致数据权限拦截器无法过滤该实体的查询
// 正确:在 AppDataPermissionConfiguration 中添加
@Bean
public DeptDataPermissionRuleCustomizer appDeptDataPermissionRuleCustomizer() {
return rule -> {
rule.addUserColumn(XxxDO.class, "user_id");
rule.addDeptColumn(XxxDO.class, "dept_id");
};
}
```
### 问题:时间字段缺少格式化
```java
// 错误:时间返回为数组格式 [2026, 1, 1, 12, 0, 0]
private LocalDateTime createTime;
// 正确:添加 @JsonFormat
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
```
### 问题:日志不符合结构化公式(缺四要素 / 长句 / 拼接 / 打印大对象)
```java
// ❌ 错误:自然语言长句 + 字符串拼接 + 吞堆栈 + 打印大对象,无法追溯来源
@Service
public class XxxServiceImpl implements XxxService {
public void doSomething(Long id) {
log.info("处理完成了,id=" + id + ",对象是" + xxxDO);
log.error("处理失败: " + e.getMessage());
}
}
// ✅ 正确:[线程id] [操作人] [业务模块] [业务动作] + 唯一 ID + 异常末位
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class XxxServiceImpl implements XxxService {
public void doSomething(Long id) {
log.info("[{}] [{}] [XXX模块] [处理XXX], id={}",
Thread.currentThread().threadId(), SecurityFrameworkUtils.getLoginUserId(), id);
log.error("[{}] [{}] [XXX模块] [处理失败], id={}",
Thread.currentThread().threadId(), SecurityFrameworkUtils.getLoginUserId(), id, e);
}
}
```
> 五要素(线程id + 操作人 + 模块 + 动作 + 唯一ID)齐全,才能让人 grep 追溯到具体来源。
## 参考文档
| 场景 | 参考文档 |
|------|----------|
| 审查检查项清单 | [review-checklist.md](references/review-checklist.md) |
| 后端框架规范 | [framework.md](../lingman-core/framework.md) |
| 管理后台 Web 规范 | [frontend-spec.md](../lingman-core/frontend/frontend-spec.md) |
| uni-app 通用规范 | [uni-app-spec.md](../lingman-core/uni-app/uni-app-spec.md) |
| p708 已验证模式 | [p708-verified-patterns.md](../lingman-core/uni-app/p708-verified-patterns.md) |
| 代码模板 | [code-template.md](../crud-generator/references/code-template.md) |
Ver en GitHub