关于 crud-rest-controller
crud-rest-controller 是 Spring Agent Toolkit 中用于生成实体 CRUD REST 控制器的技能,可结合 Spring Data repository、DTO 映射、分页、过滤和 patch。它通过 Amplicode IntelliJ 插件的 Spring MCP 获取项目上下文,并要求代码严格遵循示例文件。
主要功能
- 01生成标准 CRUD REST 端点
- 02支持 Spring Data repository
- 03可选 DTO 映射与分页
- 04支持过滤和 patch
- 05仅按示例文件编写代码
适用场景
- 01为实体生成 CRUD REST 控制器
- 02为列表端点配置分页或过滤
- 03使用 DTO 处理实体请求与响应
MCP 相关
依赖 Amplicode IntelliJ 插件提供的 Spring MCP 服务获取项目摘要、依赖和端点信息。若 MCP 工具不可用,技能会要求先安装并配置插件。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ amplicode/ spring-skills --skill crud-rest-controller安装后在 Claude Code 中输入/crud-rest-controller确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/_beans/injection/java.mdkotlin.mdexamples/_beans/repo-enhance/java.mdkotlin.mdexamples/_dependencies/dependencies.mdexamples/_methods/create/java.mdkotlin.mdexamples/_methods/delete-many/java.mdkotlin.mdexamples/_methods/delete/java.mdkotlin.mdexamples/_methods/get-list/java.mdkotlin.mdexamples/_methods/get-many/java.mdkotlin.mdexamples/_methods/get-one/java.mdkotlin.mdexamples/_methods/patch-many/java.mdkotlin.mdexamples/_methods/patch/java.mdkotlin.mdexamples/_skeletons/java.mdkotlin.md详细介绍
Preflight:Spring MCP
此技能属于 Spring Agent Toolkit,旨在与 Spring MCP 服务(由 Amplicode IntelliJ 插件提供)配合使用。开始任何操作前,请检查工具列表中是否有 Spring MCP 工具——这些工具会显示在 amplicode MCP 服务下(例如 get_project_summary、list_module_dependencies、get_entity_details);会将 MCP 工具扁平化到工具列表中的运行环境,会在相同名称前使用 mcp__amplicode__ 前缀。
- 如果至少有一个 Amplicode 工具可用——MCP 已连接。继续使用以下技能。
- 如果没有可用工具——停止并调用
amplicode-install技能(随 Spring Agent Toolkit 一同提供)。该技能会安装 Amplicode 插件,并引导用户点击欢迎屏幕上的 «Настроить Spring Agent» 按钮并重启 MCP 客户端。完成后,MCP 工具即可使用——继续此技能。 - 如果技能列表中未注册
amplicode-install,请用用户的语言告知用户:“此技能需要 Amplicode IntelliJ 插件及其 MCP 服务。请从 https://amplicode.ru/marketplace 将其安装到 IntelliJ IDEA Ultimate/Community 或 GigaIDE,打开任意项目,点击 Amplicode 欢迎屏幕上的 «Настроить Spring Agent》,然后重启 MCP 客户端。”
CRUD REST Controller
为实体生成一个 @RestController 类,其中包含标准 CRUD 端点,并使用 Spring Data 仓库,支持可选的 DTO 映射、分页、筛选和补丁操作。
关键:只能依据 examples/ 文件中的示例编写代码。如果没有匹配的示例——停止并询问用户。 关键:对于有固定选项的问题,优先使用
AskUserQuestion> 其类似工具 > 纯文本列表。只有在没有交互工具可用时,才将纯编号文本列表作为最后选择。 关键:运行步骤 1 前,先阅读对话上下文。步骤 2–4 中一半的问题可能已由用户的提示和此前的对话回答。重复询问已说明的信息,是此技能让人觉得迟缓的首要原因。
默认值
以下选项按主题分组。language 和 bootVersion
会自动检测;其他所有选项均按照决策原则(根据上下文推断 → 确认 → 询问)确定。
第 1 组 — 实体与仓库
| 选项 | 默认值 | 说明 |
|---|---|---|
| entity | -- | 要为哪个实体创建 Controller |
| repository | 该实体对应的第一个现有仓库 | 要使用哪个仓库 |
第 2 组 — DTO 模式
| 选项 | 默认值 | 说明 |
|---|---|---|
| DTO mode | DTO(推荐) | 如果该实体没有现成的 DTO,则委托给 dto-creator;用户也可以选择不使用 DTO,直接使用实体 |
第 3 组 — Controller 命名与路径
| 选项 | 默认值 | 说明 |
|---|---|---|
| controllerName | {EntityName}Controller | 根据项目的命名约定提供建议 |
| controllerPackage | 与现有 Controller 相同,或使用 mainPackage | 从项目中自动检测 |
| basePath | /rest | 持久基础路径 |
| resourcePath | /{pluralizedEntityName} | 根据实体名称自动生成 |
第 4 组 — 分页与筛选
| 选项 | 默认值 | 说明 |
|---|---|---|
| pagination | true | 为 GET_LIST 启用分页 |
| paginationType | PAGE | PAGE 或 WINDOW(WINDOW 需要选择筛选;可避免计数查询开销) |
| filter | 无 | 用于 GET_LIST 的 JPA Specification 筛选 |
自动检测(无需提问)
| 选项 | 来源 |
|---|---|
| patchStrategy | ObjectMapper |
| language | get_project_summary |
| bootVersion | get_project_summary |
智能默认值: 如果用户说“使用默认值”“全部使用默认值”“默认设置”或类似内容,则跳过所有“是否始终询问?”为“否”的问题。只询问必答问题。
智能识别回答: 当用户直接提供值,而不是从编号列表中选择时,直接接受该值。例如:
- 问题“哪个实体?”--> 用户回答“Product” --> 这就是实体,不要再次询问
- 问题“哪个 Repository?”--> 用户回答“ProductRepository” --> 这就是选择
- 如果用户在一条消息中提供多个答案 --> 全部接受,跳过已回答的问题
- 绝不要询问用户已经回答过的问题(包括隐含回答)
批量提问: 当必须提出多个问题时(即无法根据决策原则第 1–2 条得出答案),如果这些问题:
- 属于同一逻辑部分
- 不依赖彼此的答案
则将它们合并到一次 AskUserQuestion 调用中(每次最多 4 个问题)。
规则:
- 每次
AskUserQuestion调用最多 3–4 个问题 - 在推荐选项旁标注“(推荐)”,并将其放在首位
- 绝不要将不同决策分支的问题放在一起
- 主要分支问题(实体选择)始终单独询问
- 选择问题时优先使用
AskUserQuestion;只有在无法使用该工具时,才改用纯文本列表
决策原则——先看上下文,再提问
在向用户提出任何问题之前,先尝试从已收集的上下文中推导答案:项目摘要、模块依赖项、实体详情、包中的现有文件、此前的对话轮次,以及用户最初的提示。只有当上下文无法得出明确默认值,或选择确实取决于用户个人偏好(例如选择哪个实体、哪个 Repository)时,才提问。
决策优先级:
-
上下文明确无误 → 静默决定,不要提问。 示例:从
get_project_summary获取语言和 JDK;实体只有一个 Repository 时选择该 Repository;根据现有控制器确定控制器包;根据现有端点确定 basePath;根据list_module_dependencies确定 Jackson 版本;项目已有统一使用的分页类型时采用该类型。 -
上下文有明显倾向 → 用一行说明决定和其他选项,让用户选择修改或保持默认。 格式:
Will create `ProductController` at `/rest/products` with Page pagination, no DTO. Alternatives: use DTO, Window pagination, custom paths. OK?用户可以回答“好”/“是”/不回复,即表示接受;也可以指出其他选项,即切换选项。这与编号问题格式不同——这里只需一行确认。
-
上下文无法得出明确默认值 → 使用
AskUserQuestion(首选)或类似方式提问,并将推荐选项放在首位。 如果可以使用AskUserQuestion,请使用该工具,并将推荐选项标注为“(推荐)”后置于首位。如果没有可用的交互式选择工具,则改用纯文本列表。 绝不要逐步提问(“哪个实体?”→ 用户选择 → “哪个 Repository?”→ …),而应在一次批量调用中提出所有问题。 -
关键输入的上下文完全为空 → 直接提问。 适用于:未提及要选择哪个实体,以及用户本身的意图。
如何提问 — 优先使用 AskUserQuestion
必须提问时,优先使用 AskUserQuestion 工具(或其同类工具),而不是在回复正文中编写编号列表。只有在没有可用的交互式选项工具时,才改用纯文本。
此技能中调用 AskUserQuestion 的规则:
- 每次调用最多可包含 4 个彼此独立的问题(工具会将它们一并呈现)。可用此方式在一次往返中批量处理相关决策。
- 每个问题有 2–4 个选项。工具会自动添加一个供自由输入的“其他”选项,切勿手动添加。
- 将推荐选项放在首位,并在标签后附加
(Recommended)。 header是一个 12 字符的标签(例如“Entity”、“DTO mode”、“Paths”)。- 每个选项都应有一段说明,解释该选项的含义。
以下情况不适合使用 AskUserQuestion:
- 需要自由输入,且没有可枚举的选项(例如任意类名、任意路径)——用纯文本提问。
- 原则 2 中的“单行确认”形式——这是一个有明显是/否答案的普通问题,不是枚举选项。
下面步骤 2–4 中由屏幕内容驱动的问题列表,是针对情况 4 的备用方案,不是要从头到尾依次执行的脚本。如果根据原则 1–3 已经确定某个问题的答案,跳过该问题。
步骤 0 —— 首先查看对话上下文(必需,不调用工具)
在调用任何 MCP 工具、提出任何问题之前,重新阅读用户的提示和本次对话之前的内容,提取其中已经说明的信息。这一步无需耗费任何成本,还能避免此技能最常见的错误——询问用户已经提供的信息。
在脑中列出输入项,并逐项勾选用户已经明确或隐含提供的内容:
| 输入项 | 在提示/上下文中查找 |
|---|---|
| 实体 | 类名(Product、Order、Vet);“针对 X”;IDE 中打开的文件;文件路径;本次对话中最近讨论过的实体 |
| 仓库 | “使用 ProductRepository”、“配合 OrderRepo”;或可推断的信息——如果该实体只有一个仓库 |
| DTO 模式 | “使用 DTO”、“不使用 DTO”、“直接使用实体”、“映射到 ProductDto” |
| 控制器名称 | “命名为 ProductResource”、“类名为 FooController” |
| 路径 | “使用 /api/products”、“基础路径 /rest”、“资源路径 /items” |
| 分页 | “使用分页”、“不分页”、“使用 Window”、“使用 Page” |
| 筛选器 | “使用筛选器”、“不使用筛选器”、“使用 JPA Specification” |
| 方法 | “所有 CRUD 操作”、“仅读取”、“只读”、“不包含删除”、“GET + CREATE”、“完整 CRUD” |
| 智能默认值 | “使用默认值”、“全部使用默认值”、“默认设置”、“照常” |
| 先前已知的项目信息 | 编程语言、JDK、依赖项——如果本次对话之前已讨论过,则视为已知;不要重新获取 |
对于每个已明确说明或有充分依据可推断的输入:
将其标记为已确定,并跳过步骤 2–4 中相应的问题。用户写了“为 Product 创建 CRUD 控制器”时,不要问“实体是什么?”——答案就是 Product。如果该实体只有一个仓库,不要问“使用哪个仓库?”
对于每个尚未回答的输入:遵循上文的决策原则——先尝试根据项目上下文推断(步骤 1),然后再提问。
步骤 0 是在脑中完成的,不是工具调用。不要向用户宣布此步骤。不要写“步骤 0 已完成”。开始步骤 1 之前,只需先在脑中梳理用户已经说明的内容。
步骤 1 —— 收集最少的项目上下文(自动执行,不提问)
只调用结果会被后续步骤实际使用的 MCP 工具。不要“以防万一”提前获取数据——这里的每个变量都必须有明确的后续用途。
| 工具 | 变量 | 用途 |
|---|---|---|
get_project_summary | language、bootMajor、moduleName、buildFile、mainPackage | language → 选择参考文件(java 或 kotlin);bootMajor → jakarta 或 javax、Jackson 版本;moduleName → 消除多模块歧义;buildFile → 步骤 6 中注入依赖;mainPackage → 控制器包名回退值 |
list_module_dependencies(moduleName) | presentDeps | 用于推导下方标志——SpringDoc/Validation/Jackson 功能门控、步骤 6 依赖检查 |
list_project_endpoints | endpoints | 步骤 4 中检测路径冲突,以及检测现有 basePath/命名约定 |
步骤 1 到此为止。不要获取:
list_all_domain_entities—— 仅当用户未在提示词中指定实体时才需要。延后到步骤 2,作为惰性回退。list_entity_repositories—— 需要先知道实体,这将在步骤 2 中确定。延后到步骤 3,作为惰性回退。get_entity_details—— 需要先知道实体。延后到步骤 2。list_entity_dtos/list_entity_mappers—— 需要先知道实体以及 DTO 决策。延后到步骤 4。
调用 get_project_summary 后,确定:
language:Java 或 KotlinbootMajor:2、3 或 4(用于 jakarta 或 javax、Jackson 版本)- 检查
presentDeps中是否包含org.springdoc:springdoc-openapi-->hasSpringDoc - 检查
presentDeps中是否包含org.hibernate.validator:hibernate-validator或org.springframework.boot:spring-boot-starter-validation-->hasValidation - **Jackson 版本检测:**检查
presentDeps中是否包含tools.jackson:jackson-databind或tools.jackson:jackson-core-->jacksonMajor = 3。否则,如果包含com.fasterxml.jackson:jackson-databind-->jacksonMajor = 2。Spring Boot 4.x 使用 Jackson 3.x(tools.jackson包),Spring Boot 2.x/3.x 使用 Jackson 2.x(com.fasterxml.jackson包)。- 如果
jacksonMajor = 3:JsonNodeFqn=tools.jackson.databind.JsonNode,ObjectMapperFqn=tools.jackson.databind.ObjectMapper - 如果
jacksonMajor = 2:JsonNodeFqn=com.fasterxml.jackson.databind.JsonNode,ObjectMapperFqn=com.fasterxml.jackson.databind.ObjectMapper
- 如果
- PagedModel 检测:
PagedModel(org.springframework.data.web.PagedModel)在 Spring Data 3.2+ 中可用。如果bootMajor >= 3(Spring Boot 3.2+)或bootMajor = 4,则对带 Page 分页的 GET_LIST 使用PagedModel包装器。否则直接返回Page<Entity>。
如果是多模块项目(get_project_summary 中包含多个模块):
询问要使用哪个模块。然后使用该模块重新调用特定模块的 MCP 工具。
步骤 2 -- 选择实体
到步骤 0 时,如果用户提到了实体,你应该已经知道该实体。
最常见的情况:用户写了“为 Product 创建 CRUD 控制器” →
实体是 Product,跳过提问,直接执行下面的获取操作。
延迟回退 — 仅当实体未知时: 调用
list_all_domain_entities(moduleName) → entities,然后通过
AskUserQuestion 提问(选项 = 列表中的实体名称,最多 4 个;如果
实体超过 4 个,根据上下文选择最可能的 4 个候选项,并
补充说明用户可以通过“Other”输入其他名称)。
这是唯一应该调用 list_all_domain_entities 的地方。如果
用户在提示词中指定了实体,不要调用它。
确定实体 FQN 后,调用 get_entity_details(entityFqn) 获取:
IdType-- 实体 ID 字段类型(例如java.lang.Long、java.util.UUID)entityVar-- 首字母小写的实体名称(例如product)entityVarPlural-- 首字母小写的复数实体名称(例如products)EntityName-- 类的简单名称(例如Product)EntityNamePlural-- 简单名称的复数形式(例如Products)- 实体是否具有验证注解 -->
entityHasValidation
步骤 3 -- 选择仓库
到步骤 0 时,如果用户提到了仓库,你可能已经知道该仓库。
调用 list_entity_repositories(entityFqn) → repos。然后应用
决策原则:
- 如果恰好存在一个仓库 → 静默选择它(原则 1)。
- 如果用户指定了某个仓库 → 直接使用,跳过提问。
- 如果存在多个仓库 → 通过
AskUserQuestion提问,选项来自repos。 - 如果不存在仓库 → 告知用户,并建议手动创建或使用单独的技能。
选择后:
repoFieldName-- 首字母小写的仓库类名称(例如productRepository)RepoFqn-- 仓库类的 FQN
步骤 4 -- DTO 和自定义问题
到步骤 0 时,如果用户提到了 DTO 模式(例如“使用 DTO”“不使用 DTO”“直接使用实体”“映射到 ProductDto”),你可能已经知道该模式。 如果已知,跳过提问并继续。
如果未知,通过 AskUserQuestion 提问:
| 问题 | 标题 | 选项(第一个 = 推荐) |
|---|---|---|
| 使用 DTO 进行映射? | DTO 模式 | 是,使用 DTO(推荐):选择现有 DTO 或通过 dto-creator 创建 / 否,直接使用实体 |
如果选择 DTO:
调用 list_entity_dtos(entityFqn) 和 list_entity_mappers(entityFqn)。
如果找到现有 DTO 和映射器: 请用户选择。提取:
DtoFqn-- DTO 类的 FQNdtoVar-- 首字母小写的 DTO 名称mapperFieldName-- 首字母小写的映射器类名称toDtoMethodName-- 映射器方法,实体 --> DTO(例如toDto)toEntityMethodName-- 映射器方法,DTO --> 实体(例如toEntity)updateMethodName-- 用于更新的映射器方法(例如updateWithNullValues)
警告:CREATE 和 PATCH 操作要求映射器具有 toEntity 和 updateWithNull 方法。
如果该实体不存在 DTO: 委托 dto-creator 创建
一个。dto-creator 技能会处理 DTO 生成,并会
自动委托 mapper-creator 创建映射器(因为 DTO 用于 REST 控制器 ——
转换不可避免)。二者创建完成后,返回此处并继续步骤 4 的路径设置。
如果 DTO 存在但没有映射器: 委托 mapper-creator 创建
一个。映射器创建完成后,返回此处并继续。
路径、分页与筛选设置
应用 决策原则。这些设置几乎总是能根据上下文推导出合适的默认值——除非用户明确要求自定义,否则使用原则 2(单行确认):
Will create `{EntityName}Controller` in `{controllerPackage}` at `{basePath}/{entityVarPlural}`, Page pagination, no filter. OK?
用户可以回答“好”/“是”或保持沉默 → 接受所有默认值;也可以覆盖特定值 → 仅应用这些覆盖项。
只有在用户明确要求精细控制(“自定义路径”“配置分页”),或上下文无法得出明确的默认值时,才改为逐项提问。
如果用户选择了筛选器:请用户选择现有的筛选器类。提取 FilterFqn 和 toSpecificationMethodName。如果仓库没有扩展 JpaSpecificationExecutor,则添加它(WA-repo-enhance)。
方法选择
到步骤 0 时,你可能已经知道用户需要哪些方法(例如“只读”“仅 GET 和 CREATE”“完整 CRUD”)。如果已知,则跳过提问。
如果未知,则通过 AskUserQuestion 提问:
| 问题 | 标题 | 选项(第一项为推荐选项) |
|---|---|---|
| 要生成哪些 CRUD 方法? | 方法 | 完整 CRUD(推荐):GET_LIST、GET_ONE、GET_MANY、CREATE、PATCH、PATCH_MANY、DELETE、DELETE_MANY / 标准 CRUD:GET_LIST、GET_ONE、CREATE、PATCH、DELETE / 只读:GET_LIST、GET_ONE / 自定义:逐项选择方法 |
将所选方法集存储为 selectedMethods。步骤 5.3 仅生成这些方法,跳过其余方法。
步骤 5 -- 生成代码
5.1 创建控制器类(WA1)
根据 language 读取 examples/_skeletons/java.md 或 examples/_skeletons/kotlin.md。
应用变量替换:
{packageName}--> controllerPackage{className}--> controllerName{requestPath}--> basePath + resourcePath
使用 Write 工具创建 src/main/{java,kotlin}/{package-path}/{controllerName}.{java,kt}。
5.2 添加 Bean 注入(WA2)
读取 examples/_beans/injection/java.md 或 kotlin.md。
为仓库添加构造函数参数。如果使用 DTO 和 Mapper:还要注入 Mapper Bean。
注入 ${ObjectMapperFqn} 字段(在步骤 1 中解析:Jackson 3.x 使用 tools.jackson.databind.ObjectMapper,Jackson 2.x 使用 com.fasterxml.jackson.databind.ObjectMapper)。
使用 Edit 工具修改控制器类。
5.3 添加 CRUD 方法(WA3-WA11)
对于 selectedMethods 中的每个方法(来自步骤 4 的方法选择):
-
确定示例文件路径:
examples/_methods/{method-name}/{language}.md -
读取示例文件
-
根据以下条件选择正确的代码变体:
- DTO 模式(不使用 DTO 或使用 DTO)
- 对于 GET_LIST:分页(PAGE/WINDOW/无)和筛选(有/无)
- 对于 PATCH/PATCH_MANY:ObjectMapper 或 ObjectPatcher,以及 DTO 或不使用 DTO(4 种组合)
- 对于 CREATE:带或不带 @Valid
-
应用变量替换(仅限 Variables 部分中声明的变量)
-
FQN 处理(至关重要): 示例中包含 FQN(例如
org.springframework.web.bind.annotation.GetMapping、org.springframework.data.domain.Pageable、实体/DTO/仓库 FQN)。编写最终文件时,必须:- 将正文中的每个 FQN 替换为其短名称
- 收集所有缩短的 FQN,并添加对应的
import行 紧跟在package语句之后,按顺序排列,不得重复 - 与控制器属于同一包的类不得导入
java.lang中的类型不得导入- Kotlin:遵循相同规则——在正文中缩短名称,并在顶部添加
import行
-
使用 Edit 工具将方法插入控制器类的主体中
5.4 如有需要,添加 JpaSpecificationExecutor(WA-repo-enhance)
如果已选择筛选器,且仓库尚未扩展 JpaSpecificationExecutor:
读取 examples/_beans/repo-enhance/java.md 或 kotlin.md。
使用 Edit 工具,将 org.springframework.data.jpa.repository.JpaSpecificationExecutor<{EntityFqn}> 添加到仓库的 extends 列表中。
步骤 6 —— 依赖项和属性(自动)
- 读取
examples/_dependencies/dependencies.md - 对于每个不在
presentDeps中的工件:- 使用步骤 1 中的
buildFile - 编辑构建文件以添加依赖项
- 使用步骤 1 中的
- 调用
refresh_build_system_model - 不为此技能编写任何属性(控制器没有
application.properties条目) - 报告:“已创建控制器 {controllerName},包含 {EntityName} 的 CRUD 端点。已添加依赖项:[列表]。”
防止幻觉检查清单
编写任何代码之前,请确认:
- 代码来自某个 examples/ 文件(注明具体文件)
- 只替换了已声明的变量
- 没有“凭知识”添加任何框架 API 调用
- 导入列表涵盖正文中所有缩写的 FQN(没有遗漏导入,也没有多余导入)
- 方法签名与示例完全一致
- 没有添加注释或便捷方法
- 正文中缩写了示例中的 FQN,且在
package后添加了对应的import行(IDE 不会替你完成这一步) - 仅当
hasSpringDoc = true时才添加@ParameterObject注解 - 仅当
hasValidation = true且实体具有验证注解时才添加@Valid注解 - PATCH 和 PATCH_MANY 方法根据项目依赖使用 ObjectMapper(后备方案)或 ObjectPatcher
- PATCH_MANY 中的 getId() 与实体实际的 ID 访问器一致
- Jackson FQN 在 Jackson 3.x(Spring Boot 4.x)中使用
tools.jackson.databind.*,在 Jackson 2.x(Spring Boot 2.x/3.x)中使用com.fasterxml.jackson.databind.*。绝不能将其中一种写死——始终从步骤 1 解析${JsonNodeFqn}和${ObjectMapperFqn}。 - 使用 Page 分页的 GET_LIST 应采用
PagedModel<Entity>包装器(Spring Data 3.2+ / Boot 3.2+/4.x),或直接返回Page<Entity>(较旧版本)。选择正确的示例变体。