Skip to main content

java-crud-module

Use when scaffolding a new CRUD business module in a Spring Boot + MyBatis-Plus layered project. Covers the full 9-file flow: migration, entity, mapper, DTOs, converter, service, facade, and controller.

Source facts

Repository
jianyun8023/my-skills
Last source activity
July 18, 2026 at 12:02
Detected SKILL.md language
Chinese
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
java-crud-module
description
Use when scaffolding a new CRUD business module in a Spring Boot + MyBatis-Plus layered project. Covers the full 9-file flow: migration, entity, mapper, DTOs, converter, service, facade, and controller.
# CRUD 模块脚手架 为 Spring Boot + MyBatis-Plus 分层架构项目创建完整的业务模块。 **前置**: 遵守 `java-architecture-guide` 中的分层原则。 ## 适用场景 - 新建标准业务资源模块 - 为已有项目补齐一套完整 CRUD 骨架 - 需要快速生成符合项目约定的 9 个核心文件 ## 不适用 - 只改一个已有字段、接口或查询逻辑的局部需求 - 非 Spring Boot + MyBatis-Plus 分层项目 - 事件驱动、聚合根复杂编排或非典型 CRUD 场景 ## 模块创建 Checklist 按此顺序创建,每步依赖前一步的产出: ``` Step 1: Migration SQL → 建表,定义数据模型 Step 2: Entity → 映射表结构到 Java 类 Step 3: Mapper → 空接口,继承 BaseMapper Step 4: DTOs → 请求/响应对象 Step 5: Converter → MapStruct Entity ↔ DTO 转换 Step 6: Service 接口 → 定义业务方法签名 Step 7: Service 实现 → 实现业务逻辑 Step 8: Facade → 跨服务编排、事务管理 Step 9: Controller → HTTP 入口、参数校验 ``` 完成后更新 CHANGELOG.md。 ## 命名规范速查 以资源名 `AlertPolicy`(表名 `alert_policy`)为例: | 文件 | 命名 | 包路径 | |------|------|--------| | Migration | `YYYYMMDDHHMMSS_create_alert_policy.sql` | `migrations/{module}/scripts/` | | Entity | `AlertPolicy.java` | `{service-module}/.../entity/` | | Mapper | `AlertPolicyMapper.java` | `{service-module}/.../repository/` | | Create DTO | `PolicyCreateReq.java` | `{service-module}/.../dto/policy/` | | Update DTO | `PolicyUpdateReq.java` | `{service-module}/.../dto/policy/` | | Card DTO | `PolicyCard.java` | `{service-module}/.../dto/policy/` | | Detail DTO | `PolicyDetail.java` | `{service-module}/.../dto/policy/` | | Converter | `PolicyConverter.java` | `{service-module}/.../converter/` | | Service | `PolicyService.java` / `PolicyServiceImpl.java` | `{service-module}/.../service/` | | Facade | `PolicyFacade.java` | `{web-module}/.../facade/` | | Controller | `PolicyController.java` | `{web-module}/.../controller/` | | API 路径 | `/api/v1/policies` | — | **规则**: - 类名 PascalCase,表名 snake_case,API 路径 kebab-case 复数 - DTO 包按模块子目录组织(`dto/policy/`, `dto/task/`) - Facade 和 Controller 在 web 模块,其余在 service 模块 ## 典型目录结构 ``` project/ ├── {service-module}/src/main/java/com/example/service/ │ ├── entity/ # Step 2 │ ├── repository/ # Step 3 (Mapper) │ ├── dto/ │ │ └── {module}/ # Step 4 │ ├── converter/ # Step 5 │ └── service/ │ └── impl/ # Step 6-7 ├── {web-module}/src/main/java/com/example/web/ │ ├── facade/ # Step 8 │ └── controller/ # Step 9 └── migrations/{module}/scripts/ # Step 1 ``` ## 各步骤要点 ### Step 1: Migration SQL 参考 `java-db-migration` Skill。必备列:id, deleted, create_time, update_time。 ### Step 2: Entity ```java @Data @EqualsAndHashCode(callSuper = true) @TableName("alert_policy") public class AlertPolicy extends BizEntity { @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField("name") private String name; // 枚举字段 @TableField("status") private PolicyStatusEnum status; // JSON 字段 @TableField(value = "config_json", typeHandler = JacksonTypeHandler.class) private Map<String, Object> configJson; // 逻辑删除 @TableLogic private Boolean deleted; } ``` ### Step 3: Mapper ```java @Mapper public interface AlertPolicyMapper extends BaseMapper<AlertPolicy> { // 空接口 - 不添加任何方法 } ``` **禁止** Mapper XML 自定义 SQL,所有查询在 Service 层用 `LambdaQueryWrapper` 构建。 ### Step 4-5: DTOs + Converter 参考 `java-dto-converter` Skill。 ### Step 6-7: Service ```java // 接口 public interface PolicyService { AlertPolicy getById(Long id); Long create(PolicyCreateReq req); void update(Long id, PolicyUpdateReq req); void delete(Long id); PageResult<PolicyCard> getPage(int page, int size, String name); } // 实现 @Slf4j @Service @RequiredArgsConstructor public class PolicyServiceImpl implements PolicyService { private final AlertPolicyMapper policyMapper; private final PolicyConverter converter; @Override public AlertPolicy getById(Long id) { AlertPolicy entity = policyMapper.selectById(id); if (entity == null) { throw new BusinessException(ErrorCode.POLICY_NOT_FOUND, "策略不存在: " + id); } return entity; } @Override @Transactional(rollbackFor = Exception.class) public Long create(PolicyCreateReq req) { validateNameUnique(req.getName(), null); AlertPolicy entity = converter.toEntity(req); entity.setStatus(PolicyStatusEnum.DISABLED); policyMapper.insert(entity); return entity.getId(); } } ``` ### Step 8: Facade ```java @Component @Slf4j @RequiredArgsConstructor public class PolicyFacade { private final PolicyService policyService; private final TaskService taskService; // 可注入多个 Service @Transactional(rollbackFor = Exception.class) public Long create(PolicyCreateReq req) { return policyService.create(req); } // 读操作:查询 + 丰富关联数据 public PolicyDetail getDetail(Long id) { PolicyDetail detail = policyService.getDetail(id); // 丰富关联数据 enrichWithRelatedData(detail); return detail; } } ``` ### Step 9: Controller 参考 `java-api-endpoint` Skill。 ## 完成后检查 - [ ] 9 个文件全部创建 - [ ] 分层调用方向正确 (Controller → Facade → Service → Mapper) - [ ] Service 没有注入其他 Service - [ ] Mapper 是空接口 - [ ] 无 Mapper XML 自定义 SQL(查询均在 Service 构建) - [ ] 写操作有 @Transactional(rollbackFor = Exception.class) - [ ] DTO 有 @Schema 注解 - [ ] Controller 有 @Tag / @Operation 注解 - [ ] CHANGELOG.md 已更新 ## 完整代码参考 详细的 9 文件完整代码示例见 [reference.md](reference.md)。 ## 常见错误 | 错误做法 | 正确做法 | |----------|----------| | 跳过 Migration,先拍脑袋写 Entity | 先定表结构,再映射 Entity 和 DTO | | Controller 直接调 Service,省略 Facade | 保持 Controller → Facade → Service → Mapper | | Mapper 里写自定义 SQL 或 XML | 让查询逻辑留在 Service,用 `LambdaQueryWrapper` 构建 | | Create/Update/Detail 复用一个 DTO | 按创建、更新、卡片、详情拆分契约 | | 没确认项目是否真的维护 `CHANGELOG.md` 就硬更新 | 先遵循项目现有发布文档约定,再决定是否更新 |
View on GitHub