spring-boot-4

SKILL.md

Spring Boot 4 开发规范

本项目使用 Spring Boot 4.0.1 + JPA + MySQL 8 技术栈。

技术栈

技术 版本 用途
Spring Boot 4.0.1 Web 框架
Spring Data JPA 3.4.x 数据访问
Hibernate 7.x ORM
MySQL 8.0 数据库
JSpecify 1.0.0 Null 安全
SpringDoc 2.8.x API 文档
Lombok - 简化代码

目录结构

backend/src/main/java/com/taichu/yingjiguanli/
├── YingjiGuanliApplication.java    # 启动类
├── common/                          # 通用类
│   ├── ApiResponse.java            # 统一响应
│   ├── BusinessException.java      # 业务异常
│   └── PageResult.java             # 分页结果
├── config/                          # 配置类
│   ├── CorsConfig.java
│   ├── GlobalExceptionHandler.java
│   └── JpaConfig.java
├── modules/                         # 业务模块
│   └── {module}/                   # 模块名
│       ├── entity/                 # 实体类
│       ├── repository/             # 数据访问
│       ├── service/                # 服务层
│       │   └── impl/
│       ├── controller/             # 控制器
│       ├── dto/                    # 数据传输对象
│       └── vo/                     # 视图对象
└── resources/
    ├── application.yml
    └── db/migration/               # Flyway 迁移

代码模板

Entity 实体类

package com.taichu.yingjiguanli.modules.{module}.entity;

import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.jspecify.annotations.Nullable;

import java.time.LocalDateTime;

/**
 * {实体描述}
 *
 * @author CX
 * @since {date}
 */
@Data
@Entity
@Table(name = "{table_name}")
@Comment("{表描述}")
public class {EntityName} {

    /**
     * 主键ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Comment("主键ID")
    private Long id;

    /**
     * {字段描述}
     */
    @Column(nullable = false, length = 100)
    @Comment("{字段描述}")
    private String name;

    /**
     * 创建时间
     */
    @Column(nullable = false, updatable = false)
    @Comment("创建时间")
    private LocalDateTime createdAt;

    /**
     * 更新时间
     */
    @Column(nullable = false)
    @Comment("更新时间")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}

Repository 数据访问

package com.taichu.yingjiguanli.modules.{module}.repository;

import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName};
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

import java.util.Optional;

/**
 * {实体}数据访问接口
 *
 * @author CX
 * @since {date}
 */
@Repository
public interface {EntityName}Repository extends JpaRepository<{EntityName}, Long>, JpaSpecificationExecutor<{EntityName}> {

    /**
     * 根据名称查询
     */
    Optional<{EntityName}> findByName(String name);
}

Service 服务层

package com.taichu.yingjiguanli.modules.{module}.service;

import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO;
import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName};
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

/**
 * {实体}服务接口
 *
 * @author CX
 * @since {date}
 */
public interface {EntityName}Service {

    /**
     * 创建
     */
    {EntityName} create({EntityName}DTO dto);

    /**
     * 更新
     */
    {EntityName} update(Long id, {EntityName}DTO dto);

    /**
     * 删除
     */
    void delete(Long id);

    /**
     * 根据ID查询
     */
    Optional<{EntityName}> findById(Long id);

    /**
     * 分页查询
     */
    Page<{EntityName}> findAll(Pageable pageable);
}

ServiceImpl 服务实现

package com.taichu.yingjiguanli.modules.{module}.service.impl;

import com.taichu.yingjiguanli.common.BusinessException;
import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO;
import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName};
import com.taichu.yingjiguanli.modules.{module}.repository.{EntityName}Repository;
import com.taichu.yingjiguanli.modules.{module}.service.{EntityName}Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

/**
 * {实体}服务实现
 *
 * @author CX
 * @since {date}
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class {EntityName}ServiceImpl implements {EntityName}Service {

    private final {EntityName}Repository repository;

    @Override
    @Transactional
    public {EntityName} create({EntityName}DTO dto) {
        log.info("创建{实体}: {}", dto);
        {EntityName} entity = new {EntityName}();
        // 设置属性...
        return repository.save(entity);
    }

    @Override
    @Transactional
    public {EntityName} update(Long id, {EntityName}DTO dto) {
        log.info("更新{实体} ID={}: {}", id, dto);
        {EntityName} entity = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "{实体}不存在"));
        // 更新属性...
        return repository.save(entity);
    }

    @Override
    @Transactional
    public void delete(Long id) {
        log.info("删除{实体} ID={}", id);
        if (!repository.existsById(id)) {
            throw new BusinessException(404, "{实体}不存在");
        }
        repository.deleteById(id);
    }

    @Override
    public Optional<{EntityName}> findById(Long id) {
        return repository.findById(id);
    }

    @Override
    public Page<{EntityName}> findAll(Pageable pageable) {
        return repository.findAll(pageable);
    }
}

Controller 控制器

package com.taichu.yingjiguanli.modules.{module}.controller;

import com.taichu.yingjiguanli.common.ApiResponse;
import com.taichu.yingjiguanli.common.BusinessException;
import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO;
import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName};
import com.taichu.yingjiguanli.modules.{module}.service.{EntityName}Service;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;

/**
 * {实体}控制器
 *
 * @author CX
 * @since {date}
 */
@RestController
@RequestMapping("/api/{module}")
@RequiredArgsConstructor
@Tag(name = "{模块名称}", description = "{模块描述}")
public class {EntityName}Controller {

    private final {EntityName}Service service;

    /**
     * 创建
     */
    @PostMapping
    @Operation(summary = "创建{实体}")
    public ApiResponse<{EntityName}> create(@Valid @RequestBody {EntityName}DTO dto) {
        return ApiResponse.success(service.create(dto));
    }

    /**
     * 更新
     */
    @PutMapping("/{id}")
    @Operation(summary = "更新{实体}")
    public ApiResponse<{EntityName}> update(@PathVariable Long id, @Valid @RequestBody {EntityName}DTO dto) {
        return ApiResponse.success(service.update(id, dto));
    }

    /**
     * 删除
     */
    @DeleteMapping("/{id}")
    @Operation(summary = "删除{实体}")
    public ApiResponse<Void> delete(@PathVariable Long id) {
        service.delete(id);
        return ApiResponse.success();
    }

    /**
     * 查询详情
     */
    @GetMapping("/{id}")
    @Operation(summary = "查询{实体}详情")
    public ApiResponse<{EntityName}> getById(@PathVariable Long id) {
        return ApiResponse.success(service.findById(id)
                .orElseThrow(() -> new BusinessException(404, "{实体}不存在")));
    }

    /**
     * 分页查询
     */
    @GetMapping
    @Operation(summary = "分页查询{实体}")
    public ApiResponse<Page<{EntityName}>> list(Pageable pageable) {
        return ApiResponse.success(service.findAll(pageable));
    }
}

DTO 数据传输对象

package com.taichu.yingjiguanli.modules.{module}.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

/**
 * {实体}数据传输对象
 *
 * @author CX
 * @since {date}
 */
@Data
public class {EntityName}DTO {

    /**
     * 名称
     */
    @NotBlank(message = "名称不能为空")
    @Size(max = 100, message = "名称长度不能超过100")
    private String name;

    // 其他字段...
}

Flyway 迁移脚本

-- V{n}__{description}.sql
-- 作者: CX
-- 日期: {date}
-- 描述: {description}

CREATE TABLE {table_name} (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    name VARCHAR(100) NOT NULL COMMENT '名称',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='{表描述}';

命名规范

位置 规范 示例
包名 小写 com.taichu.yingjiguanli.modules.user
类名 PascalCase UserController, UserService
方法名 camelCase findById, createUser
变量名 camelCase userName, createdAt
常量名 UPPER_SNAKE MAX_PAGE_SIZE
表名 snake_case sys_user, t_order
字段名 snake_case user_name, created_at

最佳实践

  1. 统一响应: 所有接口返回 ApiResponse<T>
  2. 业务异常: 使用 BusinessException 抛出业务错误
  3. 参数校验: 使用 @Valid + jakarta.validation
  4. 日志记录: 关键操作使用 @Slf4j 记录日志
  5. 事务管理: Service 层使用 @Transactional
  6. 中文注释: 所有类、方法、字段必须有中文注释

项目: 应急管理系统 (yingjiguanli) 技术栈: Spring Boot 4.0.1 + JPA + MySQL 8

Weekly Installs
18
GitHub Stars
1
First Seen
Jan 23, 2026
Installed on
opencode14
claude-code13
gemini-cli13
codex12
cursor12
trae11