关于 mapper-creator
mapper-creator 是 Spring Agent Toolkit 中用于生成实体与 DTO 映射器的技能,支持 MapStruct 接口、抽象类或自定义转换器。它依赖 Amplicode IntelliJ 插件提供的 Spring MCP,并要求实现严格依据示例文件。
主要功能
- 01生成实体与 DTO 映射器
- 02支持 MapStruct 或自定义转换器
- 03可配置部分更新与空值策略
- 04生成前检查 Spring MCP 工具
- 05仅按示例文件编写代码
适用场景
- 01为实体与 DTO 创建映射器
- 02需要添加部分更新映射方法
- 03在 Spring 项目中按现有示例生成代码
MCP 相关
通过 Amplicode IntelliJ 插件提供的 Spring MCP 服务获取项目摘要、依赖等上下文;若未连接,会要求先安装并配置 MCP。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ amplicode/ spring-skills --skill mapper-creator安装后在 Claude Code 中输入/mapper-creator确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/_fragments/after-mapping/java.mdkotlin.mdexamples/_fragments/aggregate-ref-from/java.mdkotlin.mdexamples/_fragments/aggregate-ref-to/java.mdkotlin.mdexamples/_fragments/context-class/java.mdkotlin.mdexamples/_fragments/custom-to-dto/java.mdkotlin.mdexamples/_fragments/custom-to-entity/java.mdkotlin.mdexamples/_fragments/custom-update-with-null/java.mdkotlin.mdexamples/_fragments/factory-field/java.mdkotlin.mdexamples/_fragments/flat-expression/java.mdkotlin.mdexamples/_fragments/full-update-method/java.mdkotlin.mdexamples/_fragments/mapper-annotation/java.mdkotlin.mdexamples/_fragments/parent-interface/java.mdkotlin.mdexamples/_fragments/partial-update-method/java.mdkotlin.mdexamples/_fragments/to-dto-method/java.mdkotlin.mdexamples/_fragments/to-entity-method/java.mdkotlin.md详细介绍
预检: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 客户端。"
Mapper 创建器
创建一个 mapper(MapStruct 接口/抽象类或自定义转换器),用于在实体和 DTO 之间进行转换。
重要:只能根据 examples/ 文件中的示例编写代码。如果没有匹配的示例——停止并询问用户。 重要:对于有固定选项的问题,优先使用
AskUserQuestion> 其同类工具 > 纯文本列表。没有交互式工具可用时,才将纯编号文本列表作为最后手段。 重要:运行步骤 1 之前,请先阅读对话上下文。步骤 2–3 中一半的问题可能已由用户的提示和先前轮次回答。重复询问已回答的问题,是此技能显得缓慢的首要原因。
默认值
| 选项 | 默认值 | 是否始终询问? | 备注 |
|---|---|---|---|
| entity | — | 是 | 要映射的实体 |
| dtoClass | — | 是 | 要映射到的 DTO |
| mapperType | MapStruct | 是 | 主要选项:MapStruct 或 Custom |
| className | {EntityName}Mapper | 否 | 提出建议并确认 |
| packageName | 与 DTO 同包 | 否 | 自动确定 |
| parentInterface | null | 否 | 扩展通用 mapper 接口 |
| language | 来自 get_project_summary | 否 | 自动确定 |
| componentModel | 如果依赖中包含 Spring,则为 SPRING;否则为 DEFAULT | 否 | 不会自动检测 CDI——用户必须明确指定 |
| partialUpdate | 否 | 否 | 添加 partialUpdate 方法 |
| partialUpdateNullStrategy | SET_TO_NULL | 否 | 如果 partialUpdate = 是:SET_TO_NULL / IGNORE / SET_TO_DEFAULT |
| updateWithNull | 否 | 否 | 添加 updateWithNull 方法 |
| dtoIsRecord | false | 否 | 是否为 DTO 使用 Java record(仅 Java;会影响自定义 toEntity 中的访问器) |
智能默认值: 如果用户说“使用默认值”“全部使用默认值”“默认设置”——跳过所有“是否始终询问?”为“否”的问题。只询问必需的问题。
智能识别回答: 用户直接提供某个值而非从列表中选择时——接受该值。例如:
- 问题“哪个实体?”→ 用户回答“Order”→ 这就是实体
- 问题“Mapper 类型?”→ 用户回答“mapstruct”→ 这就是所选项
- 如果用户在一条消息中给出了多个答案——全部接受,跳过已回答的问题
- 绝不要重复询问用户已经回答过的问题(包括间接回答的问题)
批量提问: 将相关问题分组到一次 AskUserQuestion 调用中(最多 4 个问题):
- 主要问题(mapper 类型)必须单独询问
- 不要将不同决策分支中的问题分组
- 优先使用
AskUserQuestion;仅当该工具不可用时才改用纯文本
步骤 0 —— 优先查看对话上下文(必需,不调用工具)
在调用任何 MCP 工具或提出任何问题之前,重新阅读用户的提示和本次对话的先前消息,并提取其中已说明的所有信息。此步骤不耗费任何成本,还能避免这项技能最常见的失败模式——询问用户已经提供过的信息。
在脑中列出输入清单,并勾选用户已明确或隐含提供的所有信息:
| 输入 | 在提示 / 上下文中查找 |
|---|---|
| 实体 | 类名(Order、Vet、ScheduleTemplate);“针对 X”;“从 X 到 Y”;IDE 中打开的文件;最近讨论过的实体 |
| DTO | 以 Dto / Response / Request 结尾的类名;“转换为 OrderDto”;“从 X 到 Y”;本次对话中刚由 dto-creator 生成的 DTO |
| mapperType | “MapStruct”、“mapstruct”、“@Mapper”、“custom”、“manually”、“static methods”、“extension function” → MapStruct 或 Custom |
| className | “命名为 OrderConverter”、“类 FooMapper” |
| package | “放在包 … 中”、“与 DTO 放在一起”、“与 controller 放在一起” |
| methods | “仅 toDto”、“包含 update”、“partial update”、“updateWithNull” |
| 智能默认值 | “使用默认值”、“全部使用默认值”、“默认设置”、“照常” |
| 项目既有信息 | 语言、JDK、依赖项——如果本次对话之前讨论过,就已知;不要重新获取 |
| 委托调用 | 如果 dto-creator 刚刚委托此技能处理,则实体、DTO、包和语言都已知——绝不要再次询问 |
对于每项已明确或强烈隐含回答的输入:将其标记为已确定,并跳过步骤 2–3 中相应的问题。如果用户写了“为 Order 创建一个映射器,将其映射为 OrderDto”,就不要问“哪个实体?”——实体和 DTO 都已明确。如果用户写了“创建一个 MapStruct 映射器”,就不要问“MapStruct 还是 Custom?”
对于每项尚未回答的输入:遵循下面的决策原则——先尝试从项目上下文中推断(步骤 1),然后再询问。
步骤 0 是思考过程,不是工具调用。不要向用户宣布此步骤。不要写“步骤 0 已完成”。开始步骤 1 前,只需在内部先理清用户已经提供的信息。
决策原则——先看上下文,再提问
在向用户提出任何问题之前,先尝试从已收集的上下文中推断答案:项目摘要、模块依赖项、实体详情、包中的现有文件、本次对话的先前消息,以及用户最初的提示。只有在上下文无法得出明确默认值,或选择确实取决于用户个人偏好(例如选择哪个实体、哪个 DTO)时,才提问。
决策优先级:
-
上下文明确无歧义 → 静默决定,不要提问。 示例:从
get_project_summary获取语言和模块信息;从list_module_dependencies判断是否存在 MapStruct;根据 DTO 的包确定映射器包;类名使用{Entity}Mapper;根据 Spring 是否存在确定 componentModel;用户明确说了“MapStruct”或“Custom”时,据此确定 mapperType。 -
上下文给出了强烈信号 → 用一行说明决定并列出其他选项,让用户覆盖选择或不作回应。 格式:
Will create `OrderMapper` (MapStruct, componentModel=spring, in the same package as `OrderDto`). Alternatives: Custom mapper. OK?用户可以回答“ok” / “yes”或不作回应,表示接受;也可以指定其他选项进行切换。
-
**上下文无法得出明确默认值 → 使用
AskUserQuestion(优先)或同类工具提问,并将推荐选项放在最前面,后缀加上“(推荐)”。**如果没有可用的交互工具,则改用纯文本提问。 -
关键输入的上下文完全为空 → 直接提问。 适用于:要使用哪个实体;要使用哪个 DTO(两者都未提及时);用户的真实意图。
如何提问 — 优先使用 AskUserQuestion
必须提问时,优先使用 AskUserQuestion 工具(或其同类工具),而不是在回复正文中编写编号列表。只有在没有交互式选项工具时,才改用纯文本。
本技能调用 AskUserQuestion 时的规则:
- 每次调用最多可包含 4 个彼此独立的问题(工具会将它们一起呈现)。可借此在一次往返中批量处理相关决策。
- 每个问题有 2–4 个选项。工具会自动添加供自由输入的“其他”选项,切勿手动添加。
- 将推荐选项放在首位,并在标签后加上
(Recommended)。 header是一个 12 字符的标签(例如“映射器”“方法”“软件包”)。- 每个选项都要有说明,解释该选项的含义。
以下情况不适合使用 AskUserQuestion:
- 没有可枚举选项的自由输入(例如任意类名)——使用纯文本提问。
- 原则 2 中的“单行确认”形式——这是简单的是/否问题,不是枚举选项。
下方步骤 2–3 中按场景列出的问题仅在情况 4 下作为备用方案。它们不是必须从头到尾执行的脚本。如果根据原则 1–3 已能确定某个问题的答案,就跳过该问题。
步骤 1 -- 收集最少的项目信息(自动执行,不提问)
只调用后续步骤实际会用到结果的 MCP 工具。不要为了“以备不时之需”而预先获取信息——这里的每个变量都必须有明确的后续用途。
| 工具 | 提取内容 | 变量 | 用途 |
|---|---|---|---|
get_project_summary | 语言、moduleName、buildFile | language、moduleName、buildFile | language → 步骤 4 的参考选择(Java 或 Kotlin);moduleName → 多模块消歧;buildFile → 步骤 5 注入依赖 |
list_module_dependencies(moduleName) | artifact ID | presentDeps | 步骤 5(是否已存在 MapStruct?是否需要添加?)以及下文的 componentModel 决策 |
这就是步骤 1 的全部内容。不要获取:
- Spring Boot 版本——没有任何分支取决于它
- application.properties 路径——步骤 5 不会写入 properties
mainPackage——步骤 4 根据 DTO 的软件包推导 mapper 软件包,而不是根据项目根目录推导get_entity_details/list_class_members——需要先确定 entity 和 DTO,这会在步骤 2 中进行。推迟到步骤 2。list_entity_mappers——仅在 MapStruct 参考的步骤 13 中解析uses = {...}时需要,而且只针对关联 entity。只有获取entityDetails后才能确定关联 entity。推迟到步骤 13,不要在步骤 1 中预先获取。
如果是多模块项目(get_project_summary 中包含多个模块):
询问要使用哪个模块。然后针对该模块重新调用 list_module_dependencies。
componentModel — 简化版
根据 presentDeps 确定 componentModel:
- Spring(任何
spring-boot-starter*或spring-context)→SPRING - 否则 →
DEFAULT
不会自动检测 CDI。如果用户使用 CDI 项目并希望设置 componentModel = "cdi",他们会明确说明;技能默认不应针对它分支处理。
第 2 步 -- 实体和 DTO
到第 0 步时,如果用户提到了实体和 DTO,你应该已经知道它们。最常见的情况:
- 用户写道“为 Order 创建一个映射器,将其映射到 OrderDto” → 两者都已知,跳过提问,直接进行下面的并行获取。
dto-creator刚刚委派了任务 → 实体和 DTO 由委派该技能的一方传入。绝不提问,也不要重新推导。- 用户写道“为 Order 创建一个映射器” → 实体是
Order。DTO 是本次对话中最近创建或讨论的、对应于该实体的 DTO;或者,如果有多个候选项,则调用list_entity_dtos(orderFqn)并选出唯一匹配项。只有在存在多个候选项且没有其他线索时才提问。
仅当上下文确实为空时才提问。提问时,优先使用纯文本(实体名和 DTO 名是自由格式输入——AskUserQuestion 并不适用于此处):
Which entity should I create a mapper for? And which DTO to map to?
确定实体 FQN 和 DTO FQN 后,并行调用:
| 工具 | 变量 | 用途 |
|---|---|---|
get_entity_details(entityFqn) | entityDetails | 第 4 步——构建 @Mapping 注解,检测带有 mappedBy 的非拥有方关联,以便用于 @AfterMapping |
list_class_members(dtoFqn) | dtoFields | 第 4 步——比较 DTO 字段与实体字段,以确定需要哪些 @Mapping(source, target) 行 |
这两次调用推迟到第 2 步,因为它们需要第 2 步的输入。 它们不属于第 1 步。
第 3 步 -- 映射器类型和变体设置
映射器类型——优先依据上下文
应用决策原则。上下文明确时,直接决定,不要告知用户:
| 上下文线索 | 决定 |
|---|---|
| 用户说了“MapStruct” / “@Mapper” | 使用 MapStruct,不提问 |
| 用户说了“Custom” / “手动” / “静态方法” / “扩展函数” | 使用 Custom,不提问 |
presentDeps 中已有 MapStruct,且用户没有给出线索 | 使用 MapStruct(根据原则 2,静默决定或用一句话确认) |
presentDeps 中没有 MapStruct,且项目规模较小或较简单 | MapStruct 仍是合适的默认选项——第 5 步会添加依赖。在一句话确认中说明这一点:“将创建一个 MapStruct 映射器,并在构建文件中添加依赖。也可选择无需依赖的 Custom。可以吗?” |
| 用户说“使用默认设置” | 使用 MapStruct |
仅当上表中的所有情况都不匹配时,才回退到 AskUserQuestion。使用以下选项:
| 问题 | 标题 | 选项(第一个为推荐项) |
|---|---|---|
{Entity} ↔ {Dto} 要使用哪种映射器类型? | 映射器 | MapStruct(推荐):使用 @Mapper/@Mapping 的接口 / Custom:包含静态方法的普通类 |
变体设置——通常使用默认值即可
对于 MapStruct 和 Custom 两种变体,默认值几乎总是合适的:
className={EntityName}MapperpackageName= 与 DTO 相同的包partialUpdate= 否updateWithNull= 否
除非用户明确要求进行配置(“配置方法”、“我需要部分更新”),或提出与默认值相矛盾的要求,否则不要一次性询问这些设置。
如果用户确实要求配置,则使用一次 AskUserQuestion 调用(multiSelect: true),并提供相关选项:
| 问题 | 标题 | 选项(第一个为推荐项) |
|---|---|---|
| 要向映射器添加哪些方法? | 方法 | toDto + fromDto(推荐):基本的双向转换 / + partialUpdate:从 DTO 更新实体 / + updateWithNull:使用 null 覆盖值进行 partialUpdate |
类名和包名是自由格式——仅当用户说“我想换个名称”或“放到其他包中”时,才使用纯文本提问。
步骤 4 —— 生成代码
根据映射器类型和语言确定参考文件:
- MapStruct + Java → 读取
references/mapstruct-java.md - MapStruct + Kotlin → 读取
references/mapstruct-kotlin.md - Custom + Java → 读取
references/custom-java.md - Custom + Kotlin → 读取
references/custom-kotlin.md
读取对应的参考文件,并严格遵循其中的生成顺序。
构建 @Mapping 注解
阅读 references/mapping-annotations.md,了解构建 @Mapping 注解的规则。
比较 entityDetails 中的实体字段与 dtoFields 中的 DTO 字段:
- 按名称匹配 DTO 字段和实体字段
- 对于名称不同的字段,添加
@Mapping(source, target) - 对于关联 ID 字段(例如,
customerId->customer.id),添加适当的映射 - 对于扁平字段(例如,
customerName->customer.name),添加表达式或 source.target 映射
读取骨架和片段
- 从
examples/_skeletons/{variant}-{language}.md读取骨架文件 - 应用变量替换
- 写入文件
- 对于生成顺序中的每个片段:
- 检查是否满足该片段的条件
- 从
examples/_fragments/{fragment-name}/{language}.md读取片段 - 应用变量替换
- 插入或编辑已创建的文件
变量替换规则
{packageName}-> 来自步骤 1 的上下文或用户回答{className}-> 来自用户回答,或默认为{EntityName}Mapper{entityClassFqn}-> 上下文中的实体 FQN{dtoClassFqn}-> 上下文中的 DTO FQN{entityParamName}-> 实体短名称首字母小写{dtoParamName}-> DTO 短名称首字母小写{methodName}-> 根据命名约定确定(参见references/method-naming.md)- 绝不要替换示例文件 Variables 部分中未列出的内容
- 绝不要添加示例中没有的导入、方法或代码
- **FQN 处理(关键):**示例中包含 FQN(例如,
org.mapstruct.Mapper、org.mapstruct.Mapping、org.mapstruct.ReportingPolicy、org.mapstruct.MappingConstants.ComponentModel.SPRING、实体/DTO FQN)。编写最终文件时,必须:- 将正文中的每个 FQN 替换为其短名称
(例如,
@org.mapstruct.Mapper(...)->@Mapper(...),org.mapstruct.ReportingPolicy.IGNORE->ReportingPolicy.IGNORE,{entityClassFqn}-> 实体短名称,{dtoClassFqn}-> DTO 短名称)。 - 收集所有已缩短的 FQN,并在
package语句后立即添加对应的import行,排序且不重复。 - 与映射器处于同一包中的类(实体、同包中的 DTO) 不得导入——只使用短名称。
java.lang中的类型不得导入。- Kotlin:遵循相同规则——在正文中缩短名称,并在顶部添加
import行。Kotlin 不需要导入同一包中的类。 - IDE 不会替你优化导入——文件将按原样保存。
- 将正文中的每个 FQN 替换为其短名称
(例如,
步骤 5 —— 添加 MapStruct 依赖项(自动,仅适用于 MapStruct 变体)
对于 Custom 映射器:完全跳过此步骤。Custom 映射器没有外部依赖项。
对于 MapStruct:检查 presentDeps,并将缺少的构件添加到项目的构建文件中。
必需构件
| 构件 ID | Group ID | Scope |
|---|---|---|
mapstruct | org.mapstruct | implementation |
mapstruct-processor | org.mapstruct | annotationProcessor(Java)/ kapt 或 ksp(Kotlin) |
如果 presentDeps 中两个构件都不缺少,则跳过此步骤其余部分——无需添加任何内容。
如何编辑构建文件
使用步骤 1 中记录的 buildFile 路径——这是技能必须编辑的准确文件。不要猜测;不要在项目中搜索构建文件。
根据文件扩展名选择编辑策略:
build.gradle.kts— 在现有的dependencies { … }块中添加implementation("org.mapstruct:mapstruct:{version}")。 对于处理器:- Java 项目 →
annotationProcessor("org.mapstruct:mapstruct-processor:{version}") - Kotlin 项目 →
kapt("org.mapstruct:mapstruct-processor:{version}")(如果尚未应用kotlin("kapt")插件,则应用该插件),或者在已配置 KSP 时使用ksp(...)
- Java 项目 →
build.gradle(Groovy)— 同上,使用 Groovy 的单引号语法pom.xml— 在<dependencies>中添加一个<dependency>条目,并根据用途设置<scope>(mapstruct使用compile;处理器通过maven-compiler-plugin的<annotationProcessorPaths>配置)
版本号:不要硬编码。如果项目的现有版本目录中有最新的稳定版 MapStruct 版本(例如 gradle/libs.versions.toml),请读取该版本;否则,如果使用 Maven,则采用与 Spring Boot BOM/项目父级相匹配的版本;否则,输出属性/变量占位符,并请用户确认。
使用 Edit 工具,并将 {buildFile} 作为 file_path。尽量少改动——在现有的依赖块中插入新行,不要重写文件。
无需添加属性
创建 Mapper 不会写入任何 application.properties 条目。
报告:“已在包 {packageName} 中创建 Mapper {className}。类型:{mapperType}。方法:{list of methods}。”
防幻觉检查清单
编写任何代码之前,请核实:
- 代码来自某个
examples/文件(注明具体文件) - 仅替换了已声明的变量
- 没有凭“已有知识”添加框架 API 调用
- 导入列表与示例完全一致
- 方法签名与示例完全一致
- 没有添加注释或便捷方法
- 示例中的 FQN 在代码主体中已缩写,并且在
package后添加了对应的import行(IDE 不会代为处理) -
@Mapping注解依据实体与 DTO 字段的比对结果,而非猜测 - Kotlin:多个
@Mapping使用@Mappings(value = [...])包装 - 只有当实体存在带有
mappedBy和子 DTO 的非所有者关联时,才添加@AfterMapping - 默认情况下,
toDto使用单独的@Mapping(source, target)对;仅当用户明确要求时才使用@InheritInverseConfiguration - 扁平集合辅助方法仅在
toDto方向生成;toEntity不会尝试通过 ID 加载实体(不注入仓库) - 扁平集合辅助方法遵循
{assocFieldName}To{capitalize(flatDtoFieldName)}命名规则(例如petsToPetIds,而非petsToId)