mcpField
M
mapper-creator

mapper-creator

github.com/amplicode开发134435 次安装7 周前更新
代码仓库

关于 mapper-creator

mapper-creator 是 Spring Agent Toolkit 中用于生成实体与 DTO 映射器的技能,支持 MapStruct 接口、抽象类或自定义转换器。它依赖 Amplicode IntelliJ 插件提供的 Spring MCP,并要求实现严格依据示例文件。

主要功能

  1. 01生成实体与 DTO 映射器
  2. 02支持 MapStruct 或自定义转换器
  3. 03可配置部分更新与空值策略
  4. 04生成前检查 Spring MCP 工具
  5. 05仅按示例文件编写代码

适用场景

  1. 01为实体与 DTO 创建映射器
  2. 02需要添加部分更新映射方法
  3. 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.md
kotlin.md
examples/_fragments/aggregate-ref-from/
java.md
kotlin.md
examples/_fragments/aggregate-ref-to/
java.md
kotlin.md
examples/_fragments/context-class/
java.md
kotlin.md
examples/_fragments/custom-to-dto/
java.md
kotlin.md
examples/_fragments/custom-to-entity/
java.md
kotlin.md
examples/_fragments/custom-update-with-null/
java.md
kotlin.md
examples/_fragments/factory-field/
java.md
kotlin.md
examples/_fragments/flat-expression/
java.md
kotlin.md
examples/_fragments/full-update-method/
java.md
kotlin.md
examples/_fragments/mapper-annotation/
java.md
kotlin.md
examples/_fragments/parent-interface/
java.md
kotlin.md
examples/_fragments/partial-update-method/
java.md
kotlin.md
examples/_fragments/to-dto-method/
java.md
kotlin.md
examples/_fragments/to-entity-method/
java.md
kotlin.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
mapperTypeMapStruct是主要选项:MapStruct 或 Custom
className{EntityName}Mapper否提出建议并确认
packageName与 DTO 同包否自动确定
parentInterfacenull否扩展通用 mapper 接口
language来自 get_project_summary否自动确定
componentModel如果依赖中包含 Spring,则为 SPRING;否则为 DEFAULT否不会自动检测 CDI——用户必须明确指定
partialUpdate否否添加 partialUpdate 方法
partialUpdateNullStrategySET_TO_NULL否如果 partialUpdate = 是:SET_TO_NULL / IGNORE / SET_TO_DEFAULT
updateWithNull否否添加 updateWithNull 方法
dtoIsRecordfalse否是否为 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)时,才提问。

决策优先级:

  1. 上下文明确无歧义 → 静默决定,不要提问。 示例:从 get_project_summary 获取语言和模块信息;从 list_module_dependencies 判断是否存在 MapStruct;根据 DTO 的包确定映射器包;类名使用 {Entity}Mapper;根据 Spring 是否存在确定 componentModel;用户明确说了“MapStruct”或“Custom”时,据此确定 mapperType。

  2. 上下文给出了强烈信号 → 用一行说明决定并列出其他选项,让用户覆盖选择或不作回应。 格式:

    Will create `OrderMapper` (MapStruct, componentModel=spring, in the same package as `OrderDto`).
    Alternatives: Custom mapper. OK?
    

    用户可以回答“ok” / “yes”或不作回应,表示接受;也可以指定其他选项进行切换。

  3. **上下文无法得出明确默认值 → 使用 AskUserQuestion(优先)或同类工具提问,并将推荐选项放在最前面,后缀加上“(推荐)”。**如果没有可用的交互工具,则改用纯文本提问。

  4. 关键输入的上下文完全为空 → 直接提问。 适用于:要使用哪个实体;要使用哪个 DTO(两者都未提及时);用户的真实意图。

如何提问 — 优先使用 AskUserQuestion

必须提问时,优先使用 AskUserQuestion 工具(或其同类工具),而不是在回复正文中编写编号列表。只有在没有交互式选项工具时,才改用纯文本。

本技能调用 AskUserQuestion 时的规则:

  • 每次调用最多可包含 4 个彼此独立的问题(工具会将它们一起呈现)。可借此在一次往返中批量处理相关决策。
  • 每个问题有 2–4 个选项。工具会自动添加供自由输入的“其他”选项,切勿手动添加。
  • 将推荐选项放在首位,并在标签后加上 (Recommended)。
  • header 是一个 12 字符的标签(例如“映射器”“方法”“软件包”)。
  • 每个选项都要有说明,解释该选项的含义。

以下情况不适合使用 AskUserQuestion:

  • 没有可枚举选项的自由输入(例如任意类名)——使用纯文本提问。
  • 原则 2 中的“单行确认”形式——这是简单的是/否问题,不是枚举选项。

下方步骤 2–3 中按场景列出的问题仅在情况 4 下作为备用方案。它们不是必须从头到尾执行的脚本。如果根据原则 1–3 已能确定某个问题的答案,就跳过该问题。


步骤 1 -- 收集最少的项目信息(自动执行,不提问)

只调用后续步骤实际会用到结果的 MCP 工具。不要为了“以备不时之需”而预先获取信息——这里的每个变量都必须有明确的后续用途。

工具提取内容变量用途
get_project_summary语言、moduleName、buildFilelanguage、moduleName、buildFilelanguage → 步骤 4 的参考选择(Java 或 Kotlin);moduleName → 多模块消歧;buildFile → 步骤 5 注入依赖
list_module_dependencies(moduleName)artifact IDpresentDeps步骤 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}Mapper
  • packageName = 与 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 字段:

  1. 按名称匹配 DTO 字段和实体字段
  2. 对于名称不同的字段,添加 @Mapping(source, target)
  3. 对于关联 ID 字段(例如,customerId -> customer.id),添加适当的映射
  4. 对于扁平字段(例如,customerName -> customer.name),添加表达式或 source.target 映射
读取骨架和片段
  1. 从 examples/_skeletons/{variant}-{language}.md 读取骨架文件
  2. 应用变量替换
  3. 写入文件
  4. 对于生成顺序中的每个片段:
    • 检查是否满足该片段的条件
    • 从 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)。编写最终文件时,必须:
    1. 将正文中的每个 FQN 替换为其短名称 (例如,@org.mapstruct.Mapper(...) -> @Mapper(...), org.mapstruct.ReportingPolicy.IGNORE -> ReportingPolicy.IGNORE, {entityClassFqn} -> 实体短名称,{dtoClassFqn} -> DTO 短名称)。
    2. 收集所有已缩短的 FQN,并在 package 语句后立即添加对应的 import 行,排序且不重复。
    3. 与映射器处于同一包中的类(实体、同包中的 DTO) 不得导入——只使用短名称。
    4. java.lang 中的类型不得导入。
    5. Kotlin:遵循相同规则——在正文中缩短名称,并在顶部添加 import 行。Kotlin 不需要导入同一包中的类。
    6. IDE 不会替你优化导入——文件将按原样保存。

步骤 5 —— 添加 MapStruct 依赖项(自动,仅适用于 MapStruct 变体)

对于 Custom 映射器:完全跳过此步骤。Custom 映射器没有外部依赖项。

对于 MapStruct:检查 presentDeps,并将缺少的构件添加到项目的构建文件中。

必需构件
构件 IDGroup IDScope
mapstructorg.mapstructimplementation
mapstruct-processororg.mapstructannotationProcessor(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(...)
  • 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)

评论

支持 Markdown