mcpField
U
unopim-code-review

unopim-code-review

github.com/unopim测试与质量140 次安装2 周前更新
代码仓库

关于 unopim-code-review

面向 UnoPim 代码变更审查的技能,依据 `unopim-standards` 检查新增或修改的行。清单覆盖安全、规模、可移植性和本地化等阻断风险,并规定了审查结论格式。

主要功能

  1. 01聚焦差异的审查范围
  2. 02十个维度的阻断项
  3. 03高优先级规范检查
  4. 04要求引用规范与示例
  5. 05结构化审查结论格式

适用场景

  1. 01审查 UnoPim 拉取请求
  2. 02检查新增路由是否符合 ACL 规则
  3. 03审计数据库查询的规模与可移植性

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/unopim/agent-skills --skill unopim-code-review

安装后在 Claude Code 中输入/unopim-code-review确认已加载。

详细介绍

UnoPim 代码审查

背景要求: 使用 unopim-standards——其参考文件(laravel13.md、security.md、performance.md、scale.md、extensibility.md、localization.md、comments.md)是规则手册。本技能是将这些规则应用于差异内容的检查清单。

范围——逐步收紧,而非考古

审查本次变更新增或修改的行。未改动行中的历史违规最多只能作为备注,绝不能作为阻止合并的问题——若阻止整个文件的修改,所有历史代码改动都会停滞。新引入的违规始终属于审查范围,即使它模仿了相邻的历史代码也不例外。

阻止性问题(命中任意一项 = 要求修改)

1. 注释噪音——开发者投诉的头号问题
  • 在方法体、对象/数组字面量、路由组或 Blade 标记中新增的任何注释都是阻止性问题——包括单行“原因”注释、分节线(// Views、// ----)和 {{-- --}} 说明。
  • 项目的基准是零注释:packages/Webkul/Product/src/Repositories/ProductRepository.php(18 个方法)和 packages/Webkul/Product/src/Type/AbstractType.php(988 行)均不包含行内注释。
  • 确实不明显的原因应写在类/方法 PHPDoc 或提交说明中。方法 PHPDoc:一行祈使句式摘要;仅在没有原生类型提示时使用 @param/@return。
  • 机械检查:对每条符合 ^\+\s*(//|/\*|\{\{--) 的 git diff -U0 新增行,均记为问题。
2. 硬编码颜色
  • 阻止性问题:十六进制/rgb 字面量、style="..."、<style> 块、任意值(bg-[#...])、默认意图色阶(violet-*、purple-*、red-*、green-*、blue-*),以及新代码中的旧版 cherry-*/sky-*/unopim-primary-*。
  • 必须使用语义令牌:仅限 primary(+50–900)、primary-hover、success、warning、danger、info——这些 CSS 变量定义于 packages/Webkul/Admin/src/Resources/assets/css/app.css(:root + .dark),并映射于 packages/Webkul/Admin/tailwind.config.js。字面量无法跟随 .dark 变量切换,因此会导致深色模式显示异常。
  • 中性色必须配有深色模式对应类,例如 text-primary-700 dark:text-primary-400。
3. 存在 x-admin:: 组件时使用原始 HTML
  • 管理界面视图中的原始 <input>、<select>、<textarea>、<label> 均为阻止性问题:应使用 x-admin::form.control-group 及 .label/.control/.error。
  • 手工构建的列表表格均为阻止性问题:应使用 x-admin::datagrid。自定义固定/遮罩层 div 均为阻止性问题:应使用 x-admin::modal(+ modal.confirm)或 x-admin::drawer。
  • packages/Webkul/Admin/src/Resources/views/components 下有约 140 个组件 Blade 文件——接受手工编写的标记前,先检查此目录(switcher、flat-picker、tinymce、tabs、accordion、dropdown、tree、badge、shimmer)。
  • 选择框使用 :options="json_encode(...)",并搭配 track-by/label-by——绝不要用 @foreach 遍历 <option>。
4. 重复代码
  • 同一代码块(逻辑或标记)出现 2 次或以上时,必须提取为方法、组件或局部模板。指出可复用的现有位置。
5. 扩展性——假设有数百万行数据
  • 对目录表无界调用 ->all()/->get()/->pluck() = 阻塞项。批量扫描使用 chunkById()/lazyById()(packages/Webkul/Completeness/src/Jobs/BulkProductCompletenessJob.php)。
  • **钩子无法覆盖此维度的大部分情况——请检查差异内容,不要假设退出代码能做到这一点。**只有 Model::all()、$someRepository->all() 和 select('*') 会被钩子拦截。以裸 ->get() 结尾的流式调用链、foreach 中的查询、缺少预加载、Blade 模板中的查询,以及控制器循环遍历目录而不是分发 ShouldQueue 作业的情况,全部都不会被门禁检测到,在此处均属于阻塞项。
  • 批量查找:使用 array_chunk($keys, 1000) + whereIn(packages/Webkul/DataTransfer/src/Helpers/Importers/Product/SKUStorage.php)——切勿在循环中逐行查询。
  • 导出数据源通过基于 ID 游标的键集分页进行流式处理(packages/Webkul/DataTransfer/src/Helpers/Sources/Export/ProductCursor.php);AbstractExporter 的 source->all() 默认实现仅适用于小型实体。
  • 多批次处理应使用 ShouldQueue 作业,绝不能在请求中内联执行。数据迁移也应采用流式处理(cursor/chunkById)。
  • 新增的 where/orderBy/可筛选 DataGrid 列需要建立索引。不要在循环或 Blade 中执行查询。遍历到的每个关联都要预加载。
6. 可移植性——MySQL + PostgreSQL、表前缀、可选 Elasticsearch
  • 原始 SQL 片段(DB::raw、selectRaw、whereRaw、orderByRaw)中如果指定表名,必须加上 DB::getTablePrefix() 前缀(packages/Webkul/Measurement/src/DataGrids/UnitDataGrid.php)。硬编码 wk_ = 阻塞项——不存在这样的前缀;DB_PREFIX 默认值为空。
  • 仅支持 MySQL 的 SQL(GROUP_CONCAT、反引号、DATE_FORMAT、CAST AS SIGNED)= 阻塞项。跨数据库兼容应使用 DB::rawQueryGrammar()(packages/Webkul/Core/src/Helpers/Database/Grammars/MySQLGrammar.php、packages/Webkul/Core/src/Helpers/Database/Grammars/PostgresGrammar.php),或使用 DB::getDriverName() === 'pgsql' 分支。
  • 未通过 config('elasticsearch.enabled') 判断是否启用且没有数据库回退的任何 Elasticsearch 调用,均属于阻塞项(packages/Webkul/Product/src/Factories/ProductQueryBuilderFactory.php)。
7. 安全
  • ACL 按路由名称采用失败开放策略(packages/Webkul/User/src/Http/Middleware/Bouncer.php):写入路由——store、update、destroy、mass_*(snake_case 键)、test-connection——若未出现在任何 acl.php 中,所有已登录管理员都可以访问 = 阻断项。通过 'also_authorizes' 绑定写入路由(packages/Webkul/Core/src/Tree.php)。
  • 每个端点都使用 FormRequest;内联 request()->validate() = 阻断项。仅当路由名称受 ACL 控制时,才使用 authorize(): true;否则应在 authorize() 中进行 bouncer 检查(packages/Webkul/Admin/src/Http/Requests/TinyMCEUploadRequest.php)。共享或无路由端点应在控制器中进行权限控制(packages/Webkul/Admin/src/Http/Controllers/MagicAI/MagicAIController.php)。
  • 上传:使用 mimes 允许列表 + 最大大小限制 + FileMimeExtensionMatch(packages/Webkul/Core/src/Rules/FileMimeExtensionMatch.php);接受 svg/html = 阻断项(脚本载体)。
  • 批量赋值:使用显式的 #[Fillable] 允许列表,且其中不得包含权限列;$guarded = [] = 阻断项。静态存储的机密信息必须同时使用 'encrypted' cast + #[Hidden] + $auditExclude(packages/Webkul/AiAgent/src/Models/Credential.php)——单独使用 $auditExclude 并不会加密。
  • 出站 URL 必须通过 SSRF 验证(packages/Webkul/Webhook/src/Validators/SafeWebhookUrl.php);CSV 导出必须转义公式运算符(packages/Webkul/DataTransfer/src/Helpers/Formatters/EscapeFormulaOperators.php)。不得将请求输入插入 SQL 或 DataGrid 闭包 HTML。
  • 上述阻断项中有两项没有钩子,必须由你亲自检查:ACL 路由覆盖(这是跨文件问题——必须同时阅读路由文件和每个 acl.php,单文件检查无法做到)以及 orderByRaw("$column $direction")(它与产品筛选器中合法的 whereRaw("$column IS NULL") 在同一行,外观完全相同)。 每次 PR 新增写入路由时,都要打开 acl.php 并与路由文件逐项核对;不要把“钩子检查通过”当作这两项已通过的证据。
8. 本地化
  • 任何硬编码的面向用户字符串 = 阻断项,包括占位符、工具提示、JS 提示,以及“Loading…”状态文本。先在 Resources/lang/en_US/ 中使用 trans()/@lang 并添加键,再覆盖全部 33 种语言环境,确保译文自然(不照搬英文),并保留 :placeholders。
  • 将键放在名为 en 的目录中 = 阻断项——这些键永远不会加载;回退语言环境为 en_US。
9. Octane 安全性
  • 持有请求或凭据状态的 singleton() 绑定 = 阻断项;应改用 scoped()(packages/Webkul/Core/src/Providers/CoreServiceProvider.php、packages/Webkul/Core/src/RequestMemo.php)。singleton() 仅用于无状态基础设施。
10. 验证证据
  • PR 必须证明这些检查确实运行过:vendor/bin/pint --test、composer test(Pest v5、MySQL 和 PgSQL)、composer phpstan、composer rector-dry、UI/管理员变更的 Playwright 测试、php artisan unopim:translations:check。只说“应该能通过”却没有输出 = 未验证——应要求提供运行结果。
  • 为了让检查通过而新增 ignoreErrors 或 withSkip 条目 = 阻断项。

高优先级(合并前修复,不会立即阻断)

  • 新模型没有 Concord 代理/契约,或在已有 repository 时直接使用 new Model();repository 未继承 Webkul\Core\Eloquent\Repository。
  • 未采用 Laravel 13 / PHP 8.4 惯用写法:缺少提升式构造函数属性、返回类型、枚举、casts() 方法,或模型上的 #[Fillable]/#[Table] 属性。
  • 迁移文件位于错误目录——核心包使用 src/Database/Migrations/(复数);只有插件式包使用根目录下的 Database/Migration/(单数)。
  • Provider 未加载路由、配置合并(菜单位于 'menu.admin'、ACL、导入器/导出器)、迁移,或翻译/视图命名空间。
  • 新行为没有测试;缺少 assertDatabaseHas/assertDatabaseMissing;未采用 Pest 的 it()/describe() 风格。
  • 事件名称不符合 {domain}.{entity}.{action}.{before|after} 格式;路由名称未使用点分隔。

判定格式

[Dimension] [Blocker|High|Medium]: one-sentence statement of the violation
Location: file:line (added/changed line)
Standard: unopim-standards/<reference file>   e.g. comments.md, security.md, scale.md
Pattern: a real exemplar path under packages/Webkul showing the correct form
Fix: the minimal change

每项判定都必须引用标准参考文件和真实示例路径。将相关问题归为一组;用一个分句说明标准存在的原因,不要写成一段。

评论

支持 Markdown